findutils/find/parser.c
2005-08-01 07:20:49 +00:00

2571 lines
75 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* parser.c -- convert the command line args into an expression tree.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
2004, 2005 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA.
*/
#include "defs.h"
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <fnmatch.h>
#include "modechange.h"
#include "modetype.h"
#include "xstrtol.h"
#include "xalloc.h"
#include "quote.h"
#include "quotearg.h"
#include "buildcmd.h"
#include "nextelem.h"
#include "stdio-safer.h"
#include "regextype.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
/* The presence of unistd.h is assumed by gnulib these days, so we
* might as well assume it too.
*/
/* We need <unistd.h> for isatty(). */
#include <unistd.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(Text) gettext (Text)
#else
# define _(Text) Text
#endif
#ifdef gettext_noop
# define N_(String) gettext_noop (String)
#else
/* See locate.c for explanation as to why not use (String) */
# define N_(String) String
#endif
#if !defined (isascii) || defined (STDC_HEADERS)
#ifdef isascii
#undef isascii
#endif
#define isascii(c) 1
#endif
#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
#ifndef HAVE_ENDGRENT
#define endgrent()
#endif
#ifndef HAVE_ENDPWENT
#define endpwent()
#endif
static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_atime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ctime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_mtime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
boolean parse_open PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr));
boolean parse_close PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr));
static boolean insert_type PARAMS((char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred));
static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry, int regex_options));
static boolean insert_fprintf PARAMS((FILE *fp, const struct parser_table *entry, PRED_FUNC func, char *argv[], int *arg_ptr));
static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, char *argv[], int *arg_ptr));
static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
static boolean insert_time PARAMS((char **argv, int *arg_ptr, const struct parser_table* entry, PRED_FUNC pred));
static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
static boolean insert_num PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry));
static FILE *open_output_file PARAMS((char *path));
static boolean stream_is_tty(FILE *fp);
#ifdef DEBUG
char *find_pred_name PARAMS((PRED_FUNC pred_func));
#endif /* DEBUG */
#define PASTE(x,y) x##y
#define STRINGIFY(s) #s
#define PARSE_OPTION(what,suffix) \
{ (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
#define PARSE_POSOPT(what,suffix) \
{ (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
#define PARSE_TEST(what,suffix) \
{ (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
#define PARSE_TEST_NP(what,suffix) \
{ (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
#define PARSE_ACTION(what,suffix) \
{ (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
#define PARSE_ACTION_NP(what,suffix) \
{ (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
#define PARSE_PUNCTUATION(what,suffix) \
{ (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
If they are in some Unix versions of find, they are marked `Unix'. */
static struct parser_table const parse_table[] =
{
PARSE_PUNCTUATION("!", negate),
PARSE_PUNCTUATION("not", negate), /* GNU */
PARSE_PUNCTUATION("(", open),
PARSE_PUNCTUATION(")", close),
PARSE_PUNCTUATION(",", comma), /* GNU */
PARSE_PUNCTUATION("a", and),
PARSE_TEST ("amin", amin), /* GNU */
PARSE_PUNCTUATION("and", and), /* GNU */
PARSE_TEST ("anewer", anewer), /* GNU */
PARSE_TEST ("atime", atime),
PARSE_TEST ("cmin", cmin), /* GNU */
PARSE_TEST ("cnewer", cnewer), /* GNU */
PARSE_TEST ("ctime", ctime),
PARSE_POSOPT ("daystart", daystart), /* GNU */
PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
PARSE_OPTION ("depth", depth),
PARSE_TEST ("empty", empty), /* GNU */
PARSE_ACTION ("exec", exec),
PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
PARSE_ACTION ("fls", fls), /* GNU */
PARSE_POSOPT ("follow", follow), /* GNU, Unix */
PARSE_ACTION ("fprint", fprint), /* GNU */
PARSE_ACTION ("fprint0", fprint0), /* GNU */
PARSE_ACTION ("fprintf", fprintf), /* GNU */
PARSE_TEST ("fstype", fstype), /* GNU, Unix */
PARSE_TEST ("gid", gid), /* GNU */
PARSE_TEST ("group", group),
PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
PARSE_TEST ("ilname", ilname), /* GNU */
PARSE_TEST ("iname", iname), /* GNU */
PARSE_TEST ("inum", inum), /* GNU, Unix */
PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
PARSE_TEST_NP ("iregex", iregex), /* GNU */
PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
PARSE_TEST ("links", links),
PARSE_TEST ("lname", lname), /* GNU */
PARSE_ACTION ("ls", ls), /* GNU, Unix */
PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
PARSE_OPTION ("mindepth", mindepth), /* GNU */
PARSE_TEST ("mmin", mmin), /* GNU */
PARSE_OPTION ("mount", xdev), /* Unix */
PARSE_TEST ("mtime", mtime),
PARSE_TEST ("name", name),
#ifdef UNIMPLEMENTED_UNIX
PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
#endif
PARSE_TEST ("newer", newer),
PARSE_OPTION ("noleaf", noleaf), /* GNU */
PARSE_TEST ("nogroup", nogroup),
PARSE_TEST ("nouser", nouser),
PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
PARSE_OPTION ("nowarn", nowarn), /* GNU */
PARSE_PUNCTUATION("o", or),
PARSE_PUNCTUATION("or", or), /* GNU */
PARSE_ACTION ("ok", ok),
PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
PARSE_TEST ("path", path), /* GNU, HP-UX, GNU prefers wholename */
PARSE_TEST ("perm", perm),
PARSE_ACTION ("print", print),
PARSE_ACTION ("print0", print0), /* GNU */
PARSE_ACTION_NP ("printf", printf), /* GNU */
PARSE_ACTION ("prune", prune),
PARSE_ACTION ("quit", quit), /* GNU */
PARSE_TEST ("regex", regex), /* GNU */
PARSE_OPTION ("regextype", regextype), /* GNU */
PARSE_TEST ("samefile", samefile), /* GNU */
PARSE_TEST ("size", size),
PARSE_TEST ("type", type),
PARSE_TEST ("uid", uid), /* GNU */
PARSE_TEST ("used", used), /* GNU */
PARSE_TEST ("user", user),
PARSE_OPTION ("warn", warn), /* GNU */
PARSE_TEST_NP ("wholename", wholename), /* GNU, replaces -path */
PARSE_OPTION ("xdev", xdev),
PARSE_TEST ("xtype", xtype), /* GNU */
#ifdef UNIMPLEMENTED_UNIX
/* It's pretty ugly for find to know about archive formats.
Plus what it could do with cpio archives is very limited.
Better to leave it out. */
PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
#endif
/* gnulib's stdbool.h might have made true and false into macros,
* so we can't leave named 'true' and 'false' tokens, so we have
* to expeant the relevant entries longhand.
*/
{ARG_TEST, "false", parse_false, pred_false}, /* GNU */
{ARG_TEST, "true", parse_true, pred_true }, /* GNU */
/* Various other cases that don't fit neatly into our macro scheme. */
{ARG_TEST, "help", parse_help, NULL}, /* GNU */
{ARG_TEST, "-help", parse_help, NULL}, /* GNU */
{ARG_TEST, "version", parse_version, NULL}, /* GNU */
{ARG_TEST, "-version", parse_version, NULL}, /* GNU */
{0, 0, 0, 0}
};
static const char *first_nonoption_arg = NULL;
void
set_follow_state(enum SymlinkOption opt)
{
switch (opt)
{
case SYMLINK_ALWAYS_DEREF: /* -L */
options.xstat = optionl_stat;
options.no_leaf_check = true;
break;
case SYMLINK_NEVER_DEREF: /* -P (default) */
options.xstat = optionp_stat;
/* Can't turn no_leaf_check off because the user might have specified
* -noleaf anyway
*/
break;
case SYMLINK_DEREF_ARGSONLY: /* -H */
options.xstat = optionh_stat;
options.no_leaf_check = true;
}
options.symlink_handling = opt;
/* For DEBUG_STAT, the choice is made at runtime within debug_stat()
* by checking the contents of the symlink_handling variable.
*/
#if defined(DEBUG_STAT)
options.xstat = debug_stat;
#endif /* !DEBUG_STAT */
}
void
parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
{
(void) args;
(void) argno;
(void) last;
(void) predicates;
first_nonoption_arg = NULL;
}
void
parse_end_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
{
/* does nothing */
(void) args;
(void) argno;
(void) last;
(void) predicates;
}
/* Return a pointer to the parser function to invoke for predicate
SEARCH_NAME.
Return NULL if SEARCH_NAME is not a valid predicate name. */
const struct parser_table*
find_parser (char *search_name)
{
int i;
const char *original_arg = search_name;
if (*search_name == '-')
search_name++;
for (i = 0; parse_table[i].parser_name != 0; i++)
{
if (strcmp (parse_table[i].parser_name, search_name) == 0)
{
/* If this is an option, but we have already had a
* non-option argument, the user may be under the
* impression that the behaviour of the option
* argument is conditional on some preceding
* tests. This might typically be the case with,
* for example, -maxdepth.
*
* The options -daystart and -follow are exempt
* from this treatment, since their positioning
* in the command line does have an effect on
* subsequent tests but not previous ones. That
* might be intentional on the part of the user.
*/
if (parse_table[i].type != ARG_POSITIONAL_OPTION)
{
/* Something other than -follow/-daystart.
* If this is an option, check if it followed
* a non-option and if so, issue a warning.
*/
if (parse_table[i].type == ARG_OPTION)
{
if ((first_nonoption_arg != NULL)
&& options.warnings )
{
/* option which follows a non-option */
error (0, 0,
_("warning: you have specified the %s "
"option after a non-option argument %s, "
"but options are not positional (%s affects "
"tests specified before it as well as those "
"specified after it). Please specify options "
"before other arguments.\n"),
original_arg,
first_nonoption_arg,
original_arg);
}
}
else
{
/* Not an option or a positional option,
* so remember we've seen it in order to
* use it in a possible future warning message.
*/
if (first_nonoption_arg == NULL)
{
first_nonoption_arg = original_arg;
}
}
}
return &parse_table[i];
}
}
return NULL;
}
/* The parsers are responsible to continue scanning ARGV for
their arguments. Each parser knows what is and isn't
allowed for itself.
ARGV is the argument array.
*ARG_PTR is the index to start at in ARGV,
updated to point beyond the last element consumed.
The predicate structure is updated with the new information. */
static boolean
parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
(*arg_ptr)++;
return true;
}
static boolean
parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = get_new_pred (entry);
our_pred->pred_func = pred_and;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_and);
#endif /* DEBUG */
our_pred->p_type = BI_OP;
our_pred->p_prec = AND_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
struct stat stat_newer;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
(*arg_ptr)++;
return true;
}
static boolean
parse_atime (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_time (argv, arg_ptr, entry, pred_atime);
}
boolean
parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = get_new_pred (entry);
our_pred->pred_func = pred_close;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_close);
#endif /* DEBUG */
our_pred->p_type = CLOSE_PAREN;
our_pred->p_prec = NO_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
(*arg_ptr)++;
return true;
}
static boolean
parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
struct stat stat_newer;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
(*arg_ptr)++;
return true;
}
static boolean
parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = get_new_pred (entry);
our_pred->pred_func = pred_comma;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_comma);
#endif /* DEBUG */
our_pred->p_type = BI_OP;
our_pred->p_prec = COMMA_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_ctime (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_time (argv, arg_ptr, entry, pred_ctime);
}
static boolean
parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct tm *local;
(void) entry;
(void) argv;
(void) arg_ptr;
if (options.full_days == false)
{
options.cur_day_start += DAYSECS;
local = localtime (&options.cur_day_start);
options.cur_day_start -= (local
? (local->tm_sec + local->tm_min * 60
+ local->tm_hour * 3600)
: options.cur_day_start % DAYSECS);
options.full_days = true;
}
return true;
}
static boolean
parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
our_pred->side_effects = our_pred->no_default_print = true;
/* -delete implies -depth */
options.do_dir_first = false;
return true;
}
static boolean
parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) entry;
(void) argv;
(void) arg_ptr;
options.do_dir_first = false;
return true;
}
static boolean
parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
if (options.warnings)
{
error (0, 0,
_("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
}
return parse_depth(entry, argv, arg_ptr);
}
static boolean
parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
insert_primary (entry);
return true;
}
static boolean
parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_exec_ok ("-exec", entry, argv, arg_ptr);
}
static boolean
parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_exec_ok ("-execdir", entry, argv, arg_ptr);
}
static boolean
parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
our_pred->need_stat = our_pred->need_type = false;
our_pred->side_effects = our_pred->no_default_print = false;
return true;
}
static boolean
parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary (entry);
our_pred->args.stream = open_output_file (argv[*arg_ptr]);
our_pred->side_effects = our_pred->no_default_print = true;
(*arg_ptr)++;
return true;
}
static boolean
parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
{
FILE *fp;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (argv[*arg_ptr + 1] == NULL)
{
/* Ensure we get "missing arg" message, not "invalid arg". */
(*arg_ptr)++;
return false;
}
fp = open_output_file (argv[*arg_ptr]);
(*arg_ptr)++;
return insert_fprintf (fp, entry, pred_fprintf, argv, arg_ptr);
}
static boolean
parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) entry;
(void) argv;
(void) arg_ptr;
set_follow_state(SYMLINK_ALWAYS_DEREF);
return true;
}
static boolean
parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary (entry);
our_pred->args.printf_vec.segment = NULL;
our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
our_pred->side_effects = our_pred->no_default_print = true;
our_pred->need_stat = our_pred->need_type = false;
(*arg_ptr)++;
return true;
}
static boolean
parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary (entry);
our_pred->args.stream = open_output_file (argv[*arg_ptr]);
our_pred->side_effects = our_pred->no_default_print = true;
our_pred->need_stat = our_pred->need_type = false;
(*arg_ptr)++;
return true;
}
static boolean
parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary (entry);
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_num (argv, arg_ptr, entry);
}
static boolean
parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct group *cur_gr;
struct predicate *our_pred;
gid_t gid;
int gid_len;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
cur_gr = getgrnam (argv[*arg_ptr]);
endgrent ();
if (cur_gr != NULL)
gid = cur_gr->gr_gid;
else
{
gid_len = strspn (argv[*arg_ptr], "0123456789");
if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
return false;
gid = atoi (argv[*arg_ptr]);
}
our_pred = insert_primary (entry);
our_pred->args.gid = gid;
(*arg_ptr)++;
return true;
}
static boolean
parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) entry;
(void) argv;
(void) arg_ptr;
printf (_("\
Usage: %s [path...] [expression]\n"), program_name);
puts (_("\n\
default path is the current directory; default expression is -print\n\
expression may consist of: operators, options, tests, and actions:\n"));
puts (_("\
operators (decreasing precedence; -and is implicit where no others are given):\n\
( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
puts (_("\
positional options (always true): -daystart -follow -regextype\n\n\
normal options (always true, specified before other expressions):\n\
-depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
--version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
puts (_("\
tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
-cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
-ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
-links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
puts (_("\
-nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
-wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
-used N -user NAME -xtype [bcdpfls]\n"));
puts (_("\
actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
-fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
-exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
-execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
"));
puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
email to <bug-findutils@gnu.org>."));
exit (0);
}
static boolean
parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary (entry);
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
/* sanity check the fnmatch() function to make sure
* it really is the GNU version.
*/
static boolean
fnmatch_sanitycheck(void)
{
/* fprintf(stderr, "Performing find sanity check..."); */
if (0 != fnmatch("foo", "foo", 0)
|| 0 == fnmatch("Foo", "foo", 0)
|| 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
{
error (1, 0, _("sanity check of the fnmatch() library function failed."));
/* fprintf(stderr, "FAILED\n"); */
return false;
}
/* fprintf(stderr, "OK\n"); */
return true;
}
static boolean
check_name_arg(const char *pred, const char *arg)
{
if (strchr(arg, '/'))
{
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'."),
pred, arg, arg);
}
return true; /* allow it anyway */
}
static boolean
parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!check_name_arg("-iname", argv[*arg_ptr]))
return false;
fnmatch_sanitycheck();
our_pred = insert_primary (entry);
our_pred->need_stat = our_pred->need_type = false;
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_num (argv, arg_ptr, entry);
}
/* -ipath is deprecated (at RMS's request) in favour of
* -iwholename. See the node "GNU Manuals" in standards.texi
* for the rationale for this (basically, GNU prefers the use
* of the phrase "file name" to "path name"
*/
static boolean
parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
{
error (0, 0,
_("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
return parse_iwholename(entry, argv, arg_ptr);
}
static boolean
parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
fnmatch_sanitycheck();
our_pred = insert_primary_withpred (entry, pred_ipath);
our_pred->need_stat = our_pred->need_type = false;
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
}
static boolean
parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_num (argv, arg_ptr, entry);
}
static boolean
parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
fnmatch_sanitycheck();
our_pred = insert_primary (entry);
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) &argv;
(void) &arg_ptr;
our_pred = insert_primary (entry);
our_pred->side_effects = our_pred->no_default_print = true;
return true;
}
static boolean
parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
{
int depth_len;
(void) entry;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
depth_len = strspn (argv[*arg_ptr], "0123456789");
if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
return false;
options.maxdepth = atoi (argv[*arg_ptr]);
if (options.maxdepth < 0)
return false;
(*arg_ptr)++;
return true;
}
static boolean
parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
{
int depth_len;
(void) entry;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
depth_len = strspn (argv[*arg_ptr], "0123456789");
if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
return false;
options.mindepth = atoi (argv[*arg_ptr]);
if (options.mindepth < 0)
return false;
(*arg_ptr)++;
return true;
}
static boolean
parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
(*arg_ptr)++;
return true;
}
static boolean
parse_mtime (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_time (argv, arg_ptr, entry, pred_mtime);
}
static boolean
parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!check_name_arg("-name", argv[*arg_ptr]))
return false;
fnmatch_sanitycheck();
our_pred = insert_primary (entry);
our_pred->need_stat = our_pred->need_type = false;
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) &argv;
(void) &arg_ptr;
our_pred = get_new_pred_chk_op (entry);
our_pred->pred_func = pred_negate;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_negate);
#endif /* DEBUG */
our_pred->p_type = UNI_OP;
our_pred->p_prec = NEGATE_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
struct stat stat_newer;
(void) argv;
(void) arg_ptr;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
(*arg_ptr)++;
return true;
}
static boolean
parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) &argv;
(void) &arg_ptr;
(void) entry;
options.no_leaf_check = true;
return true;
}
#ifdef CACHE_IDS
/* Arbitrary amount by which to increase size
of `uid_unused' and `gid_unused'. */
#define ALLOC_STEP 2048
/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
char *uid_unused = NULL;
/* Number of elements in `uid_unused'. */
unsigned uid_allocated;
/* Similar for GIDs and group entries. */
char *gid_unused = NULL;
unsigned gid_allocated;
#endif
static boolean
parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) &argv;
(void) &arg_ptr;
our_pred = insert_primary (entry);
#ifdef CACHE_IDS
if (gid_unused == NULL)
{
struct group *gr;
gid_allocated = ALLOC_STEP;
gid_unused = xmalloc (gid_allocated);
memset (gid_unused, 1, gid_allocated);
setgrent ();
while ((gr = getgrent ()) != NULL)
{
if ((unsigned) gr->gr_gid >= gid_allocated)
{
unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
gid_unused = xrealloc (gid_unused, new_allocated);
memset (gid_unused + gid_allocated, 1,
new_allocated - gid_allocated);
gid_allocated = new_allocated;
}
gid_unused[(unsigned) gr->gr_gid] = 0;
}
endgrent ();
}
#endif
return true;
}
static boolean
parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
#ifdef CACHE_IDS
if (uid_unused == NULL)
{
struct passwd *pw;
uid_allocated = ALLOC_STEP;
uid_unused = xmalloc (uid_allocated);
memset (uid_unused, 1, uid_allocated);
setpwent ();
while ((pw = getpwent ()) != NULL)
{
if ((unsigned) pw->pw_uid >= uid_allocated)
{
unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
uid_unused = xrealloc (uid_unused, new_allocated);
memset (uid_unused + uid_allocated, 1,
new_allocated - uid_allocated);
uid_allocated = new_allocated;
}
uid_unused[(unsigned) pw->pw_uid] = 0;
}
endpwent ();
}
#endif
return true;
}
static boolean
parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
(void) entry;
options.warnings = false;
return true;;
}
static boolean
parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_exec_ok ("-ok", entry, argv, arg_ptr);
}
static boolean
parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_exec_ok ("-okdir", entry, argv, arg_ptr);
}
boolean
parse_open (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = get_new_pred_chk_op (entry);
our_pred->pred_func = pred_open;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_open);
#endif /* DEBUG */
our_pred->p_type = OPEN_PAREN;
our_pred->p_prec = NO_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = get_new_pred (entry);
our_pred->pred_func = pred_or;
#ifdef DEBUG
our_pred->p_name = find_pred_name (pred_or);
#endif /* DEBUG */
our_pred->p_type = BI_OP;
our_pred->p_prec = OR_PREC;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
/* -path is deprecated (at RMS's request) in favour of
* -iwholename. See the node "GNU Manuals" in standards.texi
* for the rationale for this (basically, GNU prefers the use
* of the phrase "file name" to "path name".
*
* We do not issue a warning that this usage is deprecated
* since HPUX find supports this predicate also.
*/
static boolean
parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return parse_wholename(entry, argv, arg_ptr);
}
static boolean
parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary_withpred (entry, pred_path);
our_pred->need_stat = our_pred->need_type = false;
our_pred->args.str = argv[*arg_ptr];
(*arg_ptr)++;
return true;
}
static boolean
parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
{
mode_t perm_val;
int mode_start = 0;
boolean havekind = false;
enum permissions_type kind = PERM_EXACT;
struct mode_change *change = NULL;
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
switch (argv[*arg_ptr][0])
{
case '-':
mode_start = 1;
kind = PERM_AT_LEAST;
havekind = true;
break;
case '+':
change = mode_compile (argv[*arg_ptr]);
if (NULL == change)
{
/* Most likely the caller is an old script that is still
* using the obsolete GNU syntax '-perm +MODE'. This old
* syntax was withdrawn in favor of '-perm /MODE' because
* it is incompatible with POSIX in some cases, but we
* still support uses of it that are not incompatible with
* POSIX.
*/
mode_start = 1;
kind = PERM_ANY;
}
else
{
/* This is a POSIX-compatible usage */
mode_start = 0;
kind = PERM_EXACT;
}
havekind = true;
break;
case '/': /* GNU extension */
mode_start = 1;
kind = PERM_ANY;
havekind = true;
break;
default:
/* For example, '-perm 0644', which is valid and matches
* only files whose mode is exactly 0644.
*
* We do nothing here, because mode_start and kind are already
* correctly set.
*/
break;
}
if (NULL == change)
{
change = mode_compile (argv[*arg_ptr] + mode_start);
if (NULL == change)
error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
}
perm_val = mode_adjust (0, change, 0);
free (change);
our_pred = insert_primary (entry);
if (havekind)
{
our_pred->args.perm.kind = kind;
}
else
{
switch (argv[*arg_ptr][0])
{
case '-':
our_pred->args.perm.kind = PERM_AT_LEAST;
break;
case '+':
our_pred->args.perm.kind = PERM_ANY;
break;
default:
our_pred->args.perm.kind = PERM_EXACT;
break;
}
}
our_pred->args.perm.val = perm_val & MODE_ALL;
(*arg_ptr)++;
return true;
}
boolean
parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
/* -print has the side effect of printing. This prevents us
from doing undesired multiple printing when the user has
already specified -print. */
our_pred->side_effects = our_pred->no_default_print = true;
our_pred->need_stat = our_pred->need_type = false;
our_pred->args.printf_vec.segment = NULL;
our_pred->args.printf_vec.stream = stdout;
our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
return true;
}
static boolean
parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
/* -print0 has the side effect of printing. This prevents us
from doing undesired multiple printing when the user has
already specified -print0. */
our_pred->side_effects = our_pred->no_default_print = true;
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
{
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
return insert_fprintf (stdout, entry, pred_fprintf, argv, arg_ptr);
}
static boolean
parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
our_pred->need_stat = our_pred->need_type = false;
/* -prune has a side effect that it does not descend into
the current directory. */
our_pred->side_effects = true;
our_pred->no_default_print = false;
return true;
}
static boolean
parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred = insert_primary (entry);
(void) argv;
(void) arg_ptr;
our_pred->need_stat = our_pred->need_type = false;
our_pred->side_effects = true; /* Exiting is a side effect... */
our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
return true;
}
static boolean
parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
{
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
/* collect the regex type name */
options.regex_options = get_regex_type(argv[*arg_ptr]);
(*arg_ptr)++;
return true;
}
static boolean
parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_regex (argv, arg_ptr, entry, options.regex_options);
}
static boolean
insert_regex (char **argv, int *arg_ptr, const struct parser_table *entry, int regex_options)
{
struct predicate *our_pred;
struct re_pattern_buffer *re;
const char *error_message;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary_withpred (entry, pred_regex);
our_pred->need_stat = our_pred->need_type = false;
re = (struct re_pattern_buffer *)
xmalloc (sizeof (struct re_pattern_buffer));
our_pred->args.regex = re;
re->allocated = 100;
re->buffer = (unsigned char *) xmalloc (re->allocated);
re->fastmap = NULL;
re_set_syntax(regex_options);
re->syntax = regex_options;
re->translate = NULL;
error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
re);
if (error_message)
error (1, 0, "%s", error_message);
(*arg_ptr)++;
return true;
}
static boolean
parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
int blksize = 512;
int len;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
len = strlen (argv[*arg_ptr]);
if (len == 0)
error (1, 0, _("invalid null argument to -size"));
switch (argv[*arg_ptr][len - 1])
{
case 'b':
blksize = 512;
argv[*arg_ptr][len - 1] = '\0';
break;
case 'c':
blksize = 1;
argv[*arg_ptr][len - 1] = '\0';
break;
case 'k':
blksize = 1024;
argv[*arg_ptr][len - 1] = '\0';
break;
case 'M': /* Megabytes */
blksize = 1024*1024;
argv[*arg_ptr][len - 1] = '\0';
break;
case 'G': /* Gigabytes */
blksize = 1024*1024*1024;
argv[*arg_ptr][len - 1] = '\0';
break;
case 'w':
blksize = 2;
argv[*arg_ptr][len - 1] = '\0';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
}
if (!get_num (argv[*arg_ptr], &num, &c_type))
return false;
our_pred = insert_primary (entry);
our_pred->args.size.kind = c_type;
our_pred->args.size.blocksize = blksize;
our_pred->args.size.size = num;
(*arg_ptr)++;
return true;
}
static boolean
parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
struct stat st;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if ((*options.xstat) (argv[*arg_ptr], &st))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.fileid.ino = st.st_ino;
our_pred->args.fileid.dev = st.st_dev;
our_pred->need_type = false;
our_pred->need_stat = true;
(*arg_ptr)++;
return true;
}
static boolean
parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
(void) argv;
(void) arg_ptr;
our_pred = insert_primary (entry);
our_pred->need_stat = our_pred->need_type = false;
return true;
}
static boolean
parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_type (argv, arg_ptr, entry, pred_type);
}
static boolean
parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_num (argv, arg_ptr, entry);
}
static boolean
parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num_days;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num (argv[*arg_ptr], &num_days, &c_type))
return false;
t = num_days * DAYSECS;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
(*arg_ptr)++;
return true;
}
static boolean
parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct passwd *cur_pwd;
struct predicate *our_pred;
uid_t uid;
int uid_len;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
cur_pwd = getpwnam (argv[*arg_ptr]);
endpwent ();
if (cur_pwd != NULL)
uid = cur_pwd->pw_uid;
else
{
uid_len = strspn (argv[*arg_ptr], "0123456789");
if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
return false;
uid = atoi (argv[*arg_ptr]);
}
our_pred = insert_primary (entry);
our_pred->args.uid = uid;
(*arg_ptr)++;
return true;
}
static boolean
parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
{
extern char *version_string;
int features = 0;
(void) argv;
(void) arg_ptr;
(void) entry;
fflush (stderr);
printf (_("GNU find version %s\n"), version_string);
printf (_("Features enabled: "));
#if CACHE_IDS
printf("CACHE_IDS ");
++features;
#endif
#if DEBUG
printf("DEBUG ");
++features;
#endif
#if DEBUG_STAT
printf("DEBUG_STAT ");
++features;
#endif
#if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
printf("D_TYPE ");
++features;
#endif
#if defined(O_NOFOLLOW)
printf("O_NOFOLLOW(%s) ",
(options.open_nofollow_available ? "enabled" : "disabled"));
++features;
#endif
#if defined(LEAF_OPTIMISATION)
printf("LEAF_OPTIMISATION ");
++features;
#endif
if (is_fts_enabled())
{
printf("FTS ");
++features;
}
if (0 == features)
{
/* For the moment, leave this as English in case someone wants
to parse these strings. */
printf("none");
}
printf("\n");
exit (0);
}
static boolean
parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
(void) entry;
options.stay_on_filesystem = true;
return true;
}
static boolean
parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
(void) entry;
options.ignore_readdir_race = true;
return true;
}
static boolean
parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
(void) entry;
options.ignore_readdir_race = false;
return true;
}
static boolean
parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
(void) entry;
options.warnings = true;
return true;
}
static boolean
parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) argv;
(void) arg_ptr;
return insert_type (argv, arg_ptr, entry, pred_xtype);
}
static boolean
insert_type (char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred)
{
mode_t type_cell;
struct predicate *our_pred;
if ((argv == NULL) || (argv[*arg_ptr] == NULL)
|| (strlen (argv[*arg_ptr]) != 1))
return false;
switch (argv[*arg_ptr][0])
{
case 'b': /* block special */
type_cell = S_IFBLK;
break;
case 'c': /* character special */
type_cell = S_IFCHR;
break;
case 'd': /* directory */
type_cell = S_IFDIR;
break;
case 'f': /* regular file */
type_cell = S_IFREG;
break;
#ifdef S_IFLNK
case 'l': /* symbolic link */
type_cell = S_IFLNK;
break;
#endif
#ifdef S_IFIFO
case 'p': /* pipe */
type_cell = S_IFIFO;
break;
#endif
#ifdef S_IFSOCK
case 's': /* socket */
type_cell = S_IFSOCK;
break;
#endif
#ifdef S_IFDOOR
case 'D': /* Solaris door */
type_cell = S_IFDOOR;
break;
#endif
default: /* None of the above ... nuke 'em. */
return false;
}
our_pred = insert_primary_withpred (entry, which_pred);
/* Figure out if we will need to stat the file, because if we don't
* need to follow symlinks, we can avoid a stat call by using
* struct dirent.d_type.
*/
if (which_pred == pred_xtype)
{
our_pred->need_stat = true;
our_pred->need_type = false;
}
else
{
our_pred->need_stat = false; /* struct dirent is enough */
our_pred->need_type = true;
}
our_pred->args.type = type_cell;
(*arg_ptr)++; /* Move on to next argument. */
return true;
}
/* Return true if the file accessed via FP is a terminal.
*/
static boolean
stream_is_tty(FILE *fp)
{
int fd = fileno(fp);
if (-1 == fd)
{
return false; /* not a valid stream */
}
else
{
return isatty(fd) ? true : false;
}
}
/* If true, we've determined that the current fprintf predicate
uses stat information. */
static boolean fprintf_stat_needed;
/* XXX: do we need to pass FUNC to this function? */
static boolean
insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char **argv, int *arg_ptr)
{
char *format; /* Beginning of unprocessed format string. */
register char *scan; /* Current address in scanning `format'. */
register char *scan2; /* Address inside of element being scanned. */
struct segment **segmentp; /* Address of current segment. */
struct predicate *our_pred;
format = argv[(*arg_ptr)++];
fprintf_stat_needed = false; /* Might be overridden later. */
our_pred = insert_primary_withpred (entry, func);
our_pred->side_effects = our_pred->no_default_print = true;
our_pred->args.printf_vec.stream = fp;
our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
segmentp = &our_pred->args.printf_vec.segment;
*segmentp = NULL;
for (scan = format; *scan; scan++)
{
if (*scan == '\\')
{
scan2 = scan + 1;
if (*scan2 >= '0' && *scan2 <= '7')
{
register int n, i;
for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
i++, scan2++)
n = 8 * n + *scan2 - '0';
scan2--;
*scan = n;
}
else
{
switch (*scan2)
{
case 'a':
*scan = 7;
break;
case 'b':
*scan = '\b';
break;
case 'c':
make_segment (segmentp, format, scan - format, KIND_STOP);
our_pred->need_stat = fprintf_stat_needed;
return true;
case 'f':
*scan = '\f';
break;
case 'n':
*scan = '\n';
break;
case 'r':
*scan = '\r';
break;
case 't':
*scan = '\t';
break;
case 'v':
*scan = '\v';
break;
case '\\':
/* *scan = '\\'; * it already is */
break;
default:
error (0, 0,
_("warning: unrecognized escape `\\%c'"), *scan2);
scan++;
continue;
}
}
segmentp = make_segment (segmentp, format, scan - format + 1,
KIND_PLAIN);
format = scan2 + 1; /* Move past the escape. */
scan = scan2; /* Incremented immediately by `for'. */
}
else if (*scan == '%')
{
if (scan[1] == '%')
{
segmentp = make_segment (segmentp, format, scan - format + 1,
KIND_PLAIN);
scan++;
format = scan + 1;
continue;
}
/* Scan past flags, width and precision, to verify kind. */
for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
/* Do nothing. */ ;
while (ISDIGIT (*scan2))
scan2++;
if (*scan2 == '.')
for (scan2++; ISDIGIT (*scan2); scan2++)
/* Do nothing. */ ;
if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
{
segmentp = make_segment (segmentp, format, scan2 - format,
(int) *scan2);
scan = scan2;
format = scan + 1;
}
else if (strchr ("ACT", *scan2) && scan2[1])
{
segmentp = make_segment (segmentp, format, scan2 - format,
*scan2 | (scan2[1] << 8));
scan = scan2 + 1;
format = scan + 1;
continue;
}
else
{
/* An unrecognized % escape. Print the char after the %. */
error (0, 0, _("warning: unrecognized format directive `%%%c'"),
*scan2);
segmentp = make_segment (segmentp, format, scan - format,
KIND_PLAIN);
format = scan + 1;
continue;
}
}
}
if (scan > format)
make_segment (segmentp, format, scan - format, KIND_PLAIN);
our_pred->need_type = false;
our_pred->need_stat = fprintf_stat_needed;
return true;
}
/* Create a new fprintf segment in *SEGMENT, with type KIND,
from the text in FORMAT, which has length LEN.
Return the address of the `next' pointer of the new segment. */
static struct segment **
make_segment (struct segment **segment, char *format, int len, int kind)
{
char *fmt;
*segment = (struct segment *) xmalloc (sizeof (struct segment));
(*segment)->kind = kind;
(*segment)->next = NULL;
(*segment)->text_len = len;
fmt = (*segment)->text = xmalloc (len + sizeof "d");
strncpy (fmt, format, len);
fmt += len;
switch (kind & 0xff)
{
case KIND_PLAIN: /* Plain text string, no % conversion. */
case KIND_STOP: /* Terminate argument, no newline. */
break;
case 'a': /* atime in `ctime' format */
case 'A': /* atime in user-specified strftime format */
case 'c': /* ctime in `ctime' format */
case 'C': /* ctime in user-specified strftime format */
case 'F': /* filesystem type */
case 'g': /* group name */
case 'i': /* inode number */
case 'l': /* object of symlink */
case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
case 's': /* size in bytes */
case 't': /* mtime in `ctime' format */
case 'T': /* mtime in user-specified strftime format */
case 'u': /* user name */
case 'y': /* file type */
case 'Y': /* symlink pointed file type */
fprintf_stat_needed = true;
/* FALLTHROUGH */
case 'f': /* basename of path */
case 'h': /* leading directories part of path */
case 'H': /* ARGV element file was found under */
case 'p': /* pathname */
case 'P': /* pathname with ARGV element stripped */
*fmt++ = 's';
break;
/* Numeric items that one might expect to honour
* #, 0, + flags but which do not.
*/
case 'G': /* GID number */
case 'U': /* UID number */
case 'b': /* size in 512-byte blocks */
case 'D': /* Filesystem device on which the file exits */
case 'k': /* size in 1K blocks */
case 'n': /* number of links */
fprintf_stat_needed = true;
*fmt++ = 's';
break;
/* Numeric items that DO honour #, 0, + flags.
*/
case 'd': /* depth in search tree (0 = ARGV element) */
*fmt++ = 'd';
break;
case 'm': /* mode as octal number (perms only) */
*fmt++ = 'o';
fprintf_stat_needed = true;
break;
}
*fmt = '\0';
return &(*segment)->next;
}
static void
check_path_safety(const char *action)
{
const char *path = getenv("PATH");
char *s;
s = next_element(path, 1);
while ((s = next_element ((char *) NULL, 1)) != NULL)
{
if (0 == strcmp(s, "."))
{
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)"),
action);
}
}
}
/* handles both exec and ok predicate */
#if defined(NEW_EXEC)
/* handles both exec and ok predicate */
static boolean
new_insert_exec_ok (const char *action,
const struct parser_table *entry,
char **argv,
int *arg_ptr)
{
int start, end; /* Indexes in ARGV of start & end of cmd. */
int i; /* Index into cmd args */
int saw_braces; /* True if previous arg was '{}'. */
boolean allow_plus; /* True if + is a valid terminator */
int brace_count; /* Number of instances of {}. */
PRED_FUNC func = entry->pred_func;
struct predicate *our_pred;
struct exec_val *execp; /* Pointer for efficiency. */
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
our_pred = insert_primary_withpred (entry, func);
our_pred->side_effects = our_pred->no_default_print = true;
execp = &our_pred->args.exec_vec;
if ((func != pred_okdir) && (func != pred_ok))
{
allow_plus = true;
execp->close_stdin = false;
}
else
{
allow_plus = false;
/* If find reads stdin (i.e. for -ok and similar), close stdin
* in the child to prevent some script from consiming the output
* intended for find.
*/
execp->close_stdin = true;
}
if ((func == pred_execdir) || (func == pred_okdir))
{
options.ignore_readdir_race = false;
check_path_safety(action);
execp->use_current_dir = true;
}
else
{
execp->use_current_dir = false;
}
our_pred->args.exec_vec.multiple = 0;
/* Count the number of args with path replacements, up until the ';'.
* Also figure out if the command is terminated by ";" or by "+".
*/
start = *arg_ptr;
for (end = start, saw_braces=0, brace_count=0;
(argv[end] != NULL)
&& ((argv[end][0] != ';') || (argv[end][1] != '\0'));
end++)
{
/* For -exec and -execdir, "{} +" can terminate the command. */
if ( allow_plus
&& argv[end][0] == '+' && argv[end][1] == 0
&& saw_braces)
{
our_pred->args.exec_vec.multiple = 1;
break;
}
saw_braces = 0;
if (strstr (argv[end], "{}"))
{
saw_braces = 1;
++brace_count;
if (0 == end && (func == pred_execdir || func == pred_okdir))
{
/* The POSIX standard says that {} replacement should
* occur even in the utility name. This is insecure
* since it means we will be executing a command whose
* name is chosen according to whatever find finds in
* the filesystem. That can be influenced by an
* attacker. Hence for -execdir and -okdir this is not
* allowed. We can specify this as those options are
* not defined by POSIX.
*/
error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
}
}
}
/* Fail if no command given or no semicolon found. */
if ((end == start) || (argv[end] == NULL))
{
*arg_ptr = end;
free(our_pred);
return false;
}
if (our_pred->args.exec_vec.multiple && brace_count > 1)
{
const char *suffix;
if (func == pred_execdir)
suffix = "dir";
else
suffix = "";
error(1, 0,
_("Only one instance of {} is supported with -exec%s ... +"),
suffix);
}
/* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
bc_init_controlinfo(&execp->ctl);
execp->ctl.exec_callback = launch;
if (our_pred->args.exec_vec.multiple)
{
/* "+" terminator, so we can just append our arguments after the
* command and initial arguments.
*/
execp->replace_vec = NULL;
execp->ctl.replace_pat = NULL;
execp->ctl.rplen = 0;
execp->ctl.lines_per_exec = 0; /* no limit */
execp->ctl.args_per_exec = 0; /* no limit */
/* remember how many arguments there are */
execp->ctl.initial_argc = (end-start) - 1;
/* execp->state = xmalloc(sizeof struct buildcmd_state); */
bc_init_state(&execp->ctl, &execp->state, execp);
/* Gather the initial arguments. Skip the {}. */
for (i=start; i<end-1; ++i)
{
bc_push_arg(&execp->ctl, &execp->state,
argv[i], strlen(argv[i])+1,
NULL, 0,
1);
}
}
else
{
/* Semicolon terminator - more than one {} is supported, so we
* have to do brace-replacement.
*/
execp->num_args = end - start;
execp->ctl.replace_pat = "{}";
execp->ctl.rplen = strlen(execp->ctl.replace_pat);
execp->ctl.lines_per_exec = 0; /* no limit */
execp->ctl.args_per_exec = 0; /* no limit */
execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
/* execp->state = xmalloc(sizeof(*(execp->state))); */
bc_init_state(&execp->ctl, &execp->state, execp);
/* Remember the (pre-replacement) arguments for later. */
for (i=0; i<execp->num_args; ++i)
{
execp->replace_vec[i] = argv[i+start];
}
}
if (argv[end] == NULL)
*arg_ptr = end;
else
*arg_ptr = end + 1;
return true;
}
#else
/* handles both exec and ok predicate */
static boolean
old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
{
int start, end; /* Indexes in ARGV of start & end of cmd. */
int num_paths; /* Number of args with path replacements. */
int path_pos; /* Index in array of path replacements. */
int vec_pos; /* Index in array of args. */
struct predicate *our_pred;
struct exec_val *execp; /* Pointer for efficiency. */
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
/* Count the number of args with path replacements, up until the ';'. */
start = *arg_ptr;
for (end = start, num_paths = 0;
(argv[end] != NULL)
&& ((argv[end][0] != ';') || (argv[end][1] != '\0'));
end++)
if (strstr (argv[end], "{}"))
num_paths++;
/* Fail if no command given or no semicolon found. */
if ((end == start) || (argv[end] == NULL))
{
*arg_ptr = end;
return false;
}
our_pred = insert_primary (func);
our_pred->side_effects = our_pred->no_default_print = true;
execp = &our_pred->args.exec_vec;
execp->usercontext = our_pred;
execp->use_current_dir = false;
execp->paths =
(struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
/* Record the positions of all args, and the args with path replacements. */
for (end = start, path_pos = vec_pos = 0;
(argv[end] != NULL)
&& ((argv[end][0] != ';') || (argv[end][1] != '\0'));
end++)
{
register char *p;
execp->paths[path_pos].count = 0;
for (p = argv[end]; *p; ++p)
if (p[0] == '{' && p[1] == '}')
{
execp->paths[path_pos].count++;
++p;
}
if (execp->paths[path_pos].count)
{
execp->paths[path_pos].offset = vec_pos;
execp->paths[path_pos].origarg = argv[end];
path_pos++;
}
execp->vec[vec_pos++] = argv[end];
}
execp->paths[path_pos].offset = -1;
execp->vec[vec_pos] = NULL;
if (argv[end] == NULL)
*arg_ptr = end;
else
*arg_ptr = end + 1;
return true;
}
#endif
static boolean
insert_exec_ok (const char *action, const struct parser_table *entry, char **argv, int *arg_ptr)
{
#if defined(NEW_EXEC)
return new_insert_exec_ok(action, entry, argv, arg_ptr);
#else
return old_insert_exec_ok(func, argv, arg_ptr);
#endif
}
/* Get a number of days and comparison type.
STR is the ASCII representation.
Set *NUM_DAYS to the number of days, taken as being from
the current moment (or possibly midnight). Thus the sense of the
comparison type appears to be reversed.
Set *COMP_TYPE to the kind of comparison that is requested.
Return true if all okay, false if input error.
Used by -atime, -ctime and -mtime (parsers) to
get the appropriate information for a time predicate processor. */
static boolean
get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
{
boolean r = get_num (str, num_days, comp_type);
if (r)
switch (*comp_type)
{
case COMP_LT: *comp_type = COMP_GT; break;
case COMP_GT: *comp_type = COMP_LT; break;
default: break;
}
return r;
}
/* Insert a time predicate PRED.
ARGV is a pointer to the argument array.
ARG_PTR is a pointer to an index into the array, incremented if
all went well.
Return true if input is valid, false if not.
A new predicate node is assigned, along with an argument node
obtained with malloc.
Used by -atime, -ctime, and -mtime parsers. */
static boolean
insert_time (char **argv, int *arg_ptr, const struct parser_table* entry, PRED_FUNC pred)
{
struct predicate *our_pred;
uintmax_t num_days;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
return false;
/* Figure out the timestamp value we are looking for. */
t = ( options.cur_day_start - num_days * DAYSECS
+ ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
if (1)
{
/* We introduce a scope in which 'val' can be declared, for the
* benefit of compilers that are really C89 compilers
* which support intmax_t because config.h #defines it
*/
intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
+ ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
t = val;
/* Check for possibility of an overflow */
if ( (intmax_t)t != val )
{
error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
}
}
our_pred = insert_primary_withpred (entry, pred);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
(*arg_ptr)++;
#ifdef DEBUG
fprintf (stderr, "inserting %s\n", our_pred->p_name);
fprintf (stderr, " type: %s %s ",
(c_type == COMP_GT) ? "gt" :
((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
(c_type == COMP_GT) ? " >" :
((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
t = our_pred->args.info.l_val;
fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
if (c_type == COMP_EQ)
{
t = our_pred->args.info.l_val += DAYSECS;
fprintf (stderr, " < %ju %s",
(uintmax_t) our_pred->args.info.l_val, ctime (&t));
our_pred->args.info.l_val -= DAYSECS;
}
#endif /* DEBUG */
return true;
}
/* Get a number with comparison information.
The sense of the comparison information is 'normal'; that is,
'+' looks for a count > than the number and '-' less than.
STR is the ASCII representation of the number.
Set *NUM to the number.
Set *COMP_TYPE to the kind of comparison that is requested.
Return true if all okay, false if input error. */
static boolean
get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
{
if (str == NULL)
return false;
switch (str[0])
{
case '+':
*comp_type = COMP_GT;
str++;
break;
case '-':
*comp_type = COMP_LT;
str++;
break;
default:
*comp_type = COMP_EQ;
break;
}
return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
}
/* Insert a number predicate.
ARGV is a pointer to the argument array.
*ARG_PTR is an index into ARGV, incremented if all went well.
*PRED is the predicate processor to insert.
Return true if input is valid, false if error.
A new predicate node is assigned, along with an argument node
obtained with malloc.
Used by -inum and -links parsers. */
static boolean
insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num (argv[*arg_ptr], &num, &c_type))
return false;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.l_val = num;
(*arg_ptr)++;
#ifdef DEBUG
fprintf (stderr, "inserting %s\n", our_pred->p_name);
fprintf (stderr, " type: %s %s ",
(c_type == COMP_GT) ? "gt" :
((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
(c_type == COMP_GT) ? " >" :
((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
#endif /* DEBUG */
return true;
}
static FILE *
open_output_file (char *path)
{
FILE *f;
if (!strcmp (path, "/dev/stderr"))
return stderr;
else if (!strcmp (path, "/dev/stdout"))
return stdout;
f = fopen_safer (path, "w");
if (f == NULL)
error (1, errno, "%s", path);
return f;
}