diff options
Diffstat (limited to 'find/parser.c')
-rw-r--r-- | find/parser.c | 3864 |
1 files changed, 1117 insertions, 2747 deletions
diff --git a/find/parser.c b/find/parser.c index b7ef88f8..3d85a9a3 100644 --- a/find/parser.c +++ b/find/parser.c @@ -1,11 +1,10 @@ /* parser.c -- convert the command line args into an expression tree. - Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003, - 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc. - This program is free software: you can redistribute it and/or modify + 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 3 of the License, or - (at your option) any later version. + 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 @@ -13,58 +12,19 @@ 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, see <http://www.gnu.org/licenses/>. -*/ + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <config.h> - -#include "defs.h" +#include <sys/types.h> +#include <sys/stat.h> #include <ctype.h> -#include <math.h> -#include <assert.h> +#include <stdio.h> #include <pwd.h> -#include <errno.h> #include <grp.h> -#include <fnmatch.h> #include "modechange.h" +#include "defs.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" -#include "stat-time.h" -#include "xstrtod.h" -#include "fts_.h" -#include "getdate.h" -#include "error.h" -#include "findutils-version.h" - -#include <fcntl.h> - - -/* 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> -#include <sys/stat.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 @@ -73,594 +33,225 @@ #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)) +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#define ISUPPER(c) (isascii (c) && isupper (c)) -#ifndef HAVE_ENDGRENT -#define endgrent() +#ifndef _POSIX_VERSION +/* POSIX.1 header files should declare these. */ +struct group *getgrnam (); +struct passwd *getpwnam (); +#endif + +#ifdef CACHE_IDS +/* These two aren't specified by POSIX.1. */ +struct group *getgrent (); +struct passwd *getpwent (); +#endif + +#ifndef S_IFLNK +#define lstat stat +#endif + +char *strstr (); +int lstat (); +int stat (); +#ifndef atol /* for Linux */ +long atol (); #endif -#ifndef HAVE_ENDPWENT +struct tm *localtime (); + +#ifdef _POSIX_SOURCE +#define endgrent() #define endpwent() +#else +void endgrent (); +void endpwent (); #endif -static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr)); -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_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_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_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_newerXY 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)); -#if 0 -static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); -#endif -static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); -static boolean parse_time 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)); - - -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 (struct format_val *vec, - const struct parser_table *entry, - PRED_FUNC func, - const char *format); - -static struct segment **make_segment PARAMS((struct segment **segment, - char *format, int len, - int kind, char format_char, - char aux_format_char, - struct predicate *pred)); -static boolean insert_exec_ok PARAMS((const char *action, - const struct parser_table *entry, - int dirfd, - char *argv[], - int *arg_ptr)); -static boolean get_comp_type PARAMS((const char **str, - enum comparison_type *comp_type)); -static boolean get_relative_timestamp PARAMS((const char *str, - struct time_val *tval, - struct timespec origin, - double sec_per_unit, - const char *overflowmessage)); -static boolean get_num PARAMS((const char *str, - uintmax_t *num, - enum comparison_type *comp_type)); -static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, - const struct parser_table *entry)); -static void open_output_file (const char *path, struct format_val *p); -static void open_stdout (struct format_val *p); -static boolean stream_is_tty(FILE *fp); -static boolean parse_noop PARAMS((const struct parser_table* entry, - char **argv, int *arg_ptr)); - -#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) } - - -/* Predicates we cannot handle in the usual way. If you add an entry - * to this table, double-check the switch statement in - * pred_sanity_check() to make sure that the new case is being - * correctly handled. - */ -static struct parser_table const parse_entry_newerXY = - { - ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */ - }; +static boolean parse_amin P_((char *argv[], int *arg_ptr)); +static boolean parse_and P_((char *argv[], int *arg_ptr)); +static boolean parse_anewer P_((char *argv[], int *arg_ptr)); +static boolean parse_atime P_((char *argv[], int *arg_ptr)); +boolean parse_close P_((char *argv[], int *arg_ptr)); +static boolean parse_cmin P_((char *argv[], int *arg_ptr)); +static boolean parse_cnewer P_((char *argv[], int *arg_ptr)); +static boolean parse_comma P_((char *argv[], int *arg_ptr)); +static boolean parse_ctime P_((char *argv[], int *arg_ptr)); +static boolean parse_daystart P_((char *argv[], int *arg_ptr)); +static boolean parse_depth P_((char *argv[], int *arg_ptr)); +static boolean parse_empty P_((char *argv[], int *arg_ptr)); +static boolean parse_exec P_((char *argv[], int *arg_ptr)); +static boolean parse_false P_((char *argv[], int *arg_ptr)); +static boolean parse_fls P_((char *argv[], int *arg_ptr)); +static boolean parse_fprintf P_((char *argv[], int *arg_ptr)); +static boolean parse_follow P_((char *argv[], int *arg_ptr)); +static boolean parse_fprint P_((char *argv[], int *arg_ptr)); +static boolean parse_fprint0 P_((char *argv[], int *arg_ptr)); +static boolean parse_fstype P_((char *argv[], int *arg_ptr)); +static boolean parse_gid P_((char *argv[], int *arg_ptr)); +static boolean parse_group P_((char *argv[], int *arg_ptr)); +static boolean parse_help P_((char *argv[], int *arg_ptr)); +static boolean parse_ilname P_((char *argv[], int *arg_ptr)); +static boolean parse_iname P_((char *argv[], int *arg_ptr)); +static boolean parse_inum P_((char *argv[], int *arg_ptr)); +static boolean parse_ipath P_((char *argv[], int *arg_ptr)); +static boolean parse_iregex P_((char *argv[], int *arg_ptr)); +static boolean parse_links P_((char *argv[], int *arg_ptr)); +static boolean parse_lname P_((char *argv[], int *arg_ptr)); +static boolean parse_ls P_((char *argv[], int *arg_ptr)); +static boolean parse_maxdepth P_((char *argv[], int *arg_ptr)); +static boolean parse_mindepth P_((char *argv[], int *arg_ptr)); +static boolean parse_mmin P_((char *argv[], int *arg_ptr)); +static boolean parse_mtime P_((char *argv[], int *arg_ptr)); +static boolean parse_name P_((char *argv[], int *arg_ptr)); +static boolean parse_negate P_((char *argv[], int *arg_ptr)); +static boolean parse_newer P_((char *argv[], int *arg_ptr)); +static boolean parse_noleaf P_((char *argv[], int *arg_ptr)); +static boolean parse_nogroup P_((char *argv[], int *arg_ptr)); +static boolean parse_nouser P_((char *argv[], int *arg_ptr)); +static boolean parse_ok P_((char *argv[], int *arg_ptr)); +boolean parse_open P_((char *argv[], int *arg_ptr)); +static boolean parse_or P_((char *argv[], int *arg_ptr)); +static boolean parse_path P_((char *argv[], int *arg_ptr)); +static boolean parse_perm P_((char *argv[], int *arg_ptr)); +boolean parse_print P_((char *argv[], int *arg_ptr)); +static boolean parse_print0 P_((char *argv[], int *arg_ptr)); +static boolean parse_printf P_((char *argv[], int *arg_ptr)); +static boolean parse_prune P_((char *argv[], int *arg_ptr)); +static boolean parse_regex P_((char *argv[], int *arg_ptr)); +static boolean insert_regex P_((char *argv[], int *arg_ptr, boolean ignore_case)); +static boolean parse_size P_((char *argv[], int *arg_ptr)); +static boolean parse_true P_((char *argv[], int *arg_ptr)); +static boolean parse_type P_((char *argv[], int *arg_ptr)); +static boolean parse_uid P_((char *argv[], int *arg_ptr)); +static boolean parse_used P_((char *argv[], int *arg_ptr)); +static boolean parse_user P_((char *argv[], int *arg_ptr)); +static boolean parse_version P_((char *argv[], int *arg_ptr)); +static boolean parse_xdev P_((char *argv[], int *arg_ptr)); +static boolean parse_xtype P_((char *argv[], int *arg_ptr)); + +static boolean insert_regex P_((char *argv[], int *arg_ptr, boolean ignore_case)); +static boolean insert_type P_((char *argv[], int *arg_ptr, boolean (*which_pred )())); +static boolean insert_fprintf P_((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr)); +static struct segment **make_segment P_((struct segment **segment, char *format, int len, int kind)); +static boolean insert_exec_ok P_((boolean (*func )(), char *argv[], int *arg_ptr)); +static boolean get_num_days P_((char *str, unsigned long *num_days, enum comparison_type *comp_type)); +static boolean insert_time P_((char *argv[], int *arg_ptr, PFB pred)); +static boolean get_num P_((char *str, unsigned long *num, enum comparison_type *comp_type)); +static boolean insert_num P_((char *argv[], int *arg_ptr, PFB pred)); +static FILE *open_output_file P_((char *path)); + +#ifdef DEBUG +char *find_pred_name _P((PFB pred_func)); +#endif /* DEBUG */ + +struct parser_table +{ + char *parser_name; + PFB parser_func; +}; /* 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), /* POSIX */ - PARSE_PUNCTUATION("not", negate), /* GNU */ - PARSE_PUNCTUATION("(", openparen), /* POSIX */ - PARSE_PUNCTUATION(")", closeparen), /* POSIX */ - PARSE_PUNCTUATION(",", comma), /* GNU */ - PARSE_PUNCTUATION("a", and), /* POSIX */ - PARSE_TEST ("amin", amin), /* GNU */ - PARSE_PUNCTUATION("and", and), /* GNU */ - PARSE_TEST ("anewer", anewer), /* GNU */ - {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ - PARSE_TEST ("cmin", cmin), /* GNU */ - PARSE_TEST ("cnewer", cnewer), /* GNU */ - {ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */ - 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), /* POSIX */ - PARSE_TEST ("empty", empty), /* GNU */ - {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */ - {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */ - 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 */ - {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */ - PARSE_TEST ("fstype", fstype), /* GNU, Unix */ - PARSE_TEST ("gid", gid), /* GNU */ - PARSE_TEST ("group", group), /* POSIX */ - 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), /* POSIX */ - 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 */ - {ARG_TEST, "mtime", parse_time, pred_mtime}, /* POSIX */ - PARSE_TEST ("name", name), -#ifdef UNIMPLEMENTED_UNIX - PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */ -#endif - PARSE_TEST ("newer", newer), /* POSIX */ - {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ - PARSE_OPTION ("noleaf", noleaf), /* GNU */ - PARSE_TEST ("nogroup", nogroup), /* POSIX */ - PARSE_TEST ("nouser", nouser), /* POSIX */ - PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */ - PARSE_POSOPT ("nowarn", nowarn), /* GNU */ - PARSE_PUNCTUATION("o", or), /* POSIX */ - PARSE_PUNCTUATION("or", or), /* GNU */ - PARSE_ACTION ("ok", ok), /* POSIX */ - PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */ - PARSE_TEST ("path", path), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */ - PARSE_TEST ("perm", perm), /* POSIX */ - PARSE_ACTION ("print", print), /* POSIX */ - PARSE_ACTION ("print0", print0), /* GNU */ - {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */ - PARSE_ACTION ("prune", prune), /* POSIX */ - PARSE_ACTION ("quit", quit), /* GNU */ - {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */ - PARSE_TEST ("regex", regex), /* GNU */ - PARSE_OPTION ("regextype", regextype), /* GNU */ - PARSE_TEST ("samefile", samefile), /* GNU */ -#if 0 - PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */ -#endif - PARSE_TEST ("size", size), /* POSIX */ - PARSE_TEST ("type", type), /* POSIX */ - PARSE_TEST ("uid", uid), /* GNU */ - PARSE_TEST ("used", used), /* GNU */ - PARSE_TEST ("user", user), /* POSIX */ - PARSE_OPTION ("warn", warn), /* GNU */ - PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */ - {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */ - PARSE_OPTION ("xdev", xdev), /* POSIX */ - PARSE_TEST ("xtype", xtype), /* GNU */ + {"!", parse_negate}, + {"not", parse_negate}, /* GNU */ + {"(", parse_open}, + {")", parse_close}, + {",", parse_comma}, /* GNU */ + {"a", parse_and}, + {"amin", parse_amin}, /* GNU */ + {"and", parse_and}, /* GNU */ + {"anewer", parse_anewer}, /* GNU */ + {"atime", parse_atime}, + {"cmin", parse_cmin}, /* GNU */ + {"cnewer", parse_cnewer}, /* 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 */ + {"cpio", parse_cpio}, /* Unix */ +#endif + {"ctime", parse_ctime}, + {"daystart", parse_daystart}, /* GNU */ + {"depth", parse_depth}, + {"empty", parse_empty}, /* GNU */ + {"exec", parse_exec}, + {"false", parse_false}, /* GNU */ + {"fls", parse_fls}, /* GNU */ + {"follow", parse_follow}, /* GNU, Unix */ + {"fprint", parse_fprint}, /* GNU */ + {"fprint0", parse_fprint0}, /* GNU */ + {"fprintf", parse_fprintf}, /* GNU */ + {"fstype", parse_fstype}, /* GNU, Unix */ + {"gid", parse_gid}, /* GNU */ + {"group", parse_group}, + {"help", parse_help}, /* GNU */ + {"-help", parse_help}, /* GNU */ + {"ilname", parse_ilname}, /* GNU */ + {"iname", parse_iname}, /* GNU */ + {"inum", parse_inum}, /* GNU, Unix */ + {"ipath", parse_ipath}, /* GNU */ + {"iregex", parse_iregex}, /* GNU */ + {"links", parse_links}, + {"lname", parse_lname}, /* GNU */ + {"ls", parse_ls}, /* GNU, Unix */ + {"maxdepth", parse_maxdepth}, /* GNU */ + {"mindepth", parse_mindepth}, /* GNU */ + {"mmin", parse_mmin}, /* GNU */ + {"mount", parse_xdev}, /* Unix */ + {"mtime", parse_mtime}, + {"name", parse_name}, +#ifdef UNIMPLEMENTED_UNIX + {"ncpio", parse_ncpio}, /* 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 */ - {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */ - - /* 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} + {"newer", parse_newer}, + {"noleaf", parse_noleaf}, /* GNU */ + {"nogroup", parse_nogroup}, + {"nouser", parse_nouser}, + {"o", parse_or}, + {"or", parse_or}, /* GNU */ + {"ok", parse_ok}, + {"path", parse_path}, /* GNU, HP-UX */ + {"perm", parse_perm}, + {"print", parse_print}, + {"print0", parse_print0}, /* GNU */ + {"printf", parse_printf}, /* GNU */ + {"prune", parse_prune}, + {"regex", parse_regex}, /* GNU */ + {"size", parse_size}, + {"true", parse_true}, /* GNU */ + {"type", parse_type}, + {"uid", parse_uid}, /* GNU */ + {"used", parse_used}, /* GNU */ + {"user", parse_user}, + {"version", parse_version}, /* GNU */ + {"-version", parse_version}, /* GNU */ + {"xdev", parse_xdev}, + {"xtype", parse_xtype}, /* GNU */ + {0, 0} }; - -static const char *first_nonoption_arg = NULL; -static const struct parser_table *noop = NULL; - - -void -check_option_combinations(const struct predicate *p) -{ - enum { seen_delete=1u, seen_prune=2u }; - unsigned int predicates = 0u; - - while (p) - { - if (p->pred_func == pred_delete) - predicates |= seen_delete; - else if (p->pred_func == pred_prune) - predicates |= seen_prune; - p = p->pred_next; - } - - if ((predicates & seen_prune) && (predicates & seen_delete)) - { - /* The user specified both -delete and -prune. One might test - * this by first doing - * find dirs .... -prune ..... -print - * to fnd out what's going to get deleted, and then switch to - * find dirs .... -prune ..... -delete - * once we are happy. Unfortunately, the -delete action also - * implicitly turns on -depth, which will affect the behaviour - * of -prune (in fact, it makes it a no-op). In this case we - * would like to prevent unfortunate accidents, so we require - * the user to have explicitly used -depth. - * - * We only get away with this because the -delete predicate is not - * in POSIX. If it was, we couldn't issue a fatal error here. - */ - if (!options.explicit_depth) - { - /* This fixes Savannah bug #20865. */ - error (1, 0, _("The -delete action atomatically turns on -depth, " - "but -prune does nothing when -depth is in effect. " - "If you want to carry on anyway, just explicitly use " - "the -depth option.")); - } - } -} - - -static const struct parser_table* -get_noop(void) -{ - int i; - if (NULL == noop) - { - for (i = 0; parse_table[i].parser_name != 0; i++) - { - if (ARG_NOOP ==parse_table[i].type) - { - noop = &(parse_table[i]); - break; - } - } - } - return noop; -} - -static int -get_stat_Ytime(const struct stat *p, - char what, - struct timespec *ret) -{ - switch (what) - { - case 'a': - *ret = get_stat_atime(p); - return 1; - case 'B': - *ret = get_stat_birthtime(p); - return (ret->tv_nsec >= 0); - case 'c': - *ret = get_stat_ctime(p); - return 1; - case 'm': - *ret = get_stat_mtime(p); - return 1; - default: - assert (0); - abort(); - } -} - -void -set_follow_state(enum SymlinkOption opt) -{ - if (options.debug_options & DebugStat) - { - /* For DebugStat, the choice is made at runtime within debug_stat() - * by checking the contents of the symlink_handling variable. - */ - options.xstat = debug_stat; - } - else - { - 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; -} - - -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; -} - - -/* Check that it is legal to fid the given primary in its - * position and return it. - */ -const struct parser_table* -found_parser(const char *original_arg, const struct parser_table *entry) -{ - /* 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 (entry->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 (entry->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 entry; -} - - /* 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) +PFB +find_parser (search_name) + char *search_name; { int i; - const char *original_arg = search_name; - - /* Ugh. Special case -newerXY. */ - if (0 == strncmp("-newer", search_name, 6) - && (8 == strlen(search_name))) - { - return found_parser(original_arg, &parse_entry_newerXY); - } - + 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) - { - return found_parser(original_arg, &parse_table[i]); - } - } - return NULL; -} - -static float -estimate_file_age_success_rate(float num_days) -{ - if (num_days < 0.1) - { - /* Assume 1% of files have timestamps in the future */ - return 0.01f; - } - else if (num_days < 1) - { - /* Assume 30% of files have timestamps today */ - return 0.3f; - } - else if (num_days > 100) - { - /* Assume 30% of files are very old */ - return 0.3f; - } - else - { - /* Assume 39% of files are between 1 and 100 days old. */ - return 0.39f; - } + if (strcmp (parse_table[i].parser_name, search_name) == 0) + return (parse_table[i].parser_func); + return (NULL); } -static float -estimate_timestamp_success_rate(time_t when) -{ - /* This calculation ignores the nanoseconds field of the - * origin, but I don't think that makes much difference - * to our estimate. - */ - int num_days = (options.cur_day_start.tv_sec - when) / 86400; - return estimate_file_age_success_rate(num_days); -} - -/* Collect an argument from the argument list, or - * return false. - */ -static boolean -collect_arg(char **argv, int *arg_ptr, const char **collected_arg) -{ - if ((argv == NULL) || (argv[*arg_ptr] == NULL)) - { - *collected_arg = NULL; - return false; - } - else - { - *collected_arg = argv[*arg_ptr]; - (*arg_ptr)++; - return true; - } -} - -static boolean -collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p) -{ - const char *filename; - if (collect_arg(argv, arg_ptr, &filename)) - { - if (0 == (options.xstat)(filename, p)) - { - return true; - } - else - { - fatal_file_error(filename); - } - } - else - { - return false; - } -} - /* The parsers are responsible to continue scanning ARGV for their arguments. Each parser knows what is and isn't allowed for itself. @@ -671,925 +262,595 @@ collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p) The predicate structure is updated with the new information. */ - static boolean -parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_amin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_amin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_and (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) argv; - (void) arg_ptr; - - our_pred = get_new_pred (entry); + our_pred = get_new_pred (); 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; + our_pred->need_stat = false; + return (true); } static boolean -parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_anewer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { + struct predicate *our_pred; struct stat stat_newer; - set_stat_placeholders(&stat_newer); - if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.reftime.xval = XVAL_ATIME; - our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); - our_pred->args.reftime.kind = COMP_GT; - our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); - return true; - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_anewer); + our_pred->args.time = stat_newer.st_mtime; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_atime (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_time (argv, arg_ptr, pred_atime)); } boolean -parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_close (argv, arg_ptr) + 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_closeparen; + our_pred = get_new_pred (); + 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; + our_pred->need_stat = false; + return (true); } static boolean -parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_cmin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct stat stat_newer; + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; - set_stat_placeholders(&stat_newer); - if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */ - our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); - our_pred->args.reftime.kind = COMP_GT; - our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); - return true; - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_cmin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); } static boolean -parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_cnewer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; + struct stat stat_newer; - (void) argv; - (void) arg_ptr; - - our_pred = get_new_pred (entry); - our_pred->pred_func = pred_comma; - our_pred->p_type = BI_OP; - our_pred->p_prec = COMMA_PREC; - our_pred->need_stat = our_pred->need_type = false; - our_pred->est_success_rate = 1.0f; - return true; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_cnewer); + our_pred->args.time = stat_newer.st_mtime; + (*arg_ptr)++; + return (true); } static boolean -parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_comma (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct tm *local; - - (void) entry; - (void) argv; - (void) arg_ptr; + struct predicate *our_pred; - if (options.full_days == false) - { - options.cur_day_start.tv_sec += DAYSECS; - options.cur_day_start.tv_nsec = 0; - local = localtime (&options.cur_day_start.tv_sec); - options.cur_day_start.tv_sec -= (local - ? (local->tm_sec + local->tm_min * 60 - + local->tm_hour * 3600) - : options.cur_day_start.tv_sec % DAYSECS); - options.full_days = true; - } - return true; + our_pred = get_new_pred (); + 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 = false; + return (true); } static boolean -parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr) +parse_ctime (argv, arg_ptr) + 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; - - /* We do not need stat information because we check for the case - * (errno==EISDIR) in pred_delete. - */ - our_pred->need_stat = our_pred->need_type = false; - - our_pred->est_success_rate = 1.0f; - return true; + return (insert_time (argv, arg_ptr, pred_ctime)); } static boolean -parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_daystart (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - (void) entry; - (void) argv; + struct tm *local; - options.do_dir_first = false; - options.explicit_depth = true; - return parse_noop(entry, argv, arg_ptr); -} - -static boolean -parse_d (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - if (options.warnings) + if (full_days == false) { - error (0, 0, - _("warning: the -d option is deprecated; please use " - "-depth instead, because the latter is a " - "POSIX-compliant feature.")); + cur_day_start += DAYSECS; + local = localtime (&cur_day_start); + cur_day_start -= local->tm_sec + local->tm_min * 60 + + local->tm_hour * 3600; + full_days = true; } - return parse_depth(entry, argv, arg_ptr); + return (true); } - + static boolean -parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_depth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *our_pred; - (void) argv; - (void) arg_ptr; - - our_pred = insert_primary (entry); - our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */ - return true; + do_dir_first = false; + return (true); } - + static boolean -parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_empty (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr); + insert_primary (pred_empty); + return (true); } static boolean -parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_exec (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr); + return (insert_exec_ok (pred_exec, argv, arg_ptr)); } static boolean -parse_false (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_false (argv, arg_ptr) + 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; - our_pred->est_success_rate = 0.0f; - return true; -} -static boolean -insert_fls (const struct parser_table* entry, const char *filename) -{ - struct predicate *our_pred = insert_primary (entry); - if (filename) - open_output_file (filename, &our_pred->args.printf_vec); - else - open_stdout (&our_pred->args.printf_vec); - our_pred->side_effects = our_pred->no_default_print = true; - our_pred->est_success_rate = 1.0f; - return true; + our_pred = insert_primary (pred_false); + our_pred->need_stat = false; + return (true); } - static boolean -parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_fls (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *filename; - return collect_arg(argv, arg_ptr, &filename) - && insert_fls(entry, filename); -} + struct predicate *our_pred; -static boolean -parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - set_follow_state(SYMLINK_ALWAYS_DEREF); - return parse_noop(entry, argv, arg_ptr); + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fls); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + (*arg_ptr)++; + return (true); } -static boolean -parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr) +static boolean +parse_fprintf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *our_pred; - const char *filename; - if (collect_arg(argv, arg_ptr, &filename)) - { - our_pred = insert_primary (entry); - open_output_file (filename, &our_pred->args.printf_vec); - our_pred->side_effects = our_pred->no_default_print = true; - our_pred->need_stat = our_pred->need_type = false; - our_pred->est_success_rate = 1.0f; - return true; - } - else + FILE *fp; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (argv[*arg_ptr + 1] == NULL) { - return false; + /* 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, pred_fprintf, argv, arg_ptr)); } -static boolean -insert_fprint(const struct parser_table* entry, const char *filename) +static boolean +parse_follow (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *our_pred = insert_primary (entry); - if (filename) - open_output_file (filename, &our_pred->args.printf_vec); - else - open_stdout (&our_pred->args.printf_vec); - our_pred->side_effects = our_pred->no_default_print = true; - our_pred->need_stat = our_pred->need_type = false; - our_pred->est_success_rate = 1.0f; - return true; + dereference = true; + xstat = stat; + no_leaf_check = true; + return (true); } - static boolean -parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_fprint (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *filename; - if (collect_arg(argv, arg_ptr, &filename)) - return insert_fprint(entry, filename); - else - return false; -} + struct predicate *our_pred; -static float estimate_fstype_success_rate(const char *fsname) -{ - struct stat dir_stat; - const char *dir = "/"; - if (0 == stat(dir, &dir_stat)) - { - const char *fstype = filesystem_type(&dir_stat, dir); - /* Assume most files are on the same file system type as the root fs. */ - if (0 == strcmp(fsname, fstype)) - return 0.7f; - else - return 0.3f; - } - return 1.0f; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fprint); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + our_pred->need_stat = false; + (*arg_ptr)++; + return (true); } - static boolean -parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_fprint0 (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *typename; - if (collect_arg(argv, arg_ptr, &typename)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.str = typename; - - /* This is an expensive operation, so although there are - * circumstances where it is selective, we ignore this fact - * because we probably don't want to promote this test to the - * front anyway. - */ - our_pred->est_success_rate = estimate_fstype_success_rate(typename); - return true; - } - else - { - return false; - } + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fprint0); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + our_pred->need_stat = false; + (*arg_ptr)++; + return (true); } static boolean -parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_fstype (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *p = insert_num (argv, arg_ptr, entry); - if (p) - { - p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; - return true; - } - else - { - return false; - } -} + struct predicate *our_pred; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fstype); + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} -static int -safe_atoi (const char *s) +static boolean +parse_gid (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - long lval; - char *end; - - errno = 0; - lval = strtol(s, &end, 10); - if ( (LONG_MAX == lval) || (LONG_MIN == lval) ) - { - /* max/min possible value, or an error. */ - if (errno == ERANGE) - { - /* too big, or too small. */ - error(1, errno, "%s", s); - } - else - { - /* not a valid number */ - error(1, errno, "%s", s); - } - /* Otherwise, we do a range chack against INT_MAX and INT_MIN - * below. - */ - } - - if (lval > INT_MAX || lval < INT_MIN) - { - /* The number was in range for long, but not int. */ - errno = ERANGE; - error(1, errno, "%s", s); - } - else if (*end) - { - error(1, errno, "Unexpected suffix %s on %s", - quotearg_n_style(0, options.err_quoting_style, end), - quotearg_n_style(1, options.err_quoting_style, s)); - } - else if (end == s) - { - error(1, errno, "Expected an integer: %s", - quotearg_n_style(0, options.err_quoting_style, s)); - } - return (int)lval; + return (insert_num (argv, arg_ptr, pred_gid)); } - static boolean -parse_group (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_group (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *groupname; + struct group *cur_gr; + struct predicate *our_pred; + gid_t gid; + int gid_len; - if (collect_arg(argv, arg_ptr, &groupname)) + 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_t gid; - struct predicate *our_pred; - struct group *cur_gr = getgrnam(groupname); - endgrent(); - if (cur_gr) - { - gid = cur_gr->gr_gid; - } - else - { - const int gid_len = strspn (groupname, "0123456789"); - if (gid_len) - { - if (groupname[gid_len] == 0) - { - gid = safe_atoi (groupname); - } - else - { - /* XXX: no test in test suite for this */ - error(1, 0, _("%s is not the name of an existing group and" - " it does not look like a numeric group ID " - "because it has the unexpected suffix %s"), - quotearg_n_style(0, options.err_quoting_style, groupname), - quotearg_n_style(1, options.err_quoting_style, groupname+gid_len)); - return false; - } - } - else - { - if (*groupname) - { - /* XXX: no test in test suite for this */ - error(1, 0, _("%s is not the name of an existing group"), - quotearg_n_style(0, options.err_quoting_style, groupname)); - } - else - { - error(1, 0, _("argument to -group is empty, but should be a group name")); - } - return false; - } - } - our_pred = insert_primary (entry); - our_pred->args.gid = gid; - our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2; - return true; + gid_len = strspn (argv[*arg_ptr], "0123456789"); + if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0')) + return (false); + gid = atoi (argv[*arg_ptr]); } - return false; + our_pred = insert_primary (pred_group); + our_pred->args.gid = gid; + (*arg_ptr)++; + return (true); } static boolean -parse_help (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_help (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - (void) entry; - (void) argv; - (void) arg_ptr; - - usage(stdout, 0, NULL); - puts (_("\n\ + printf ("\ +Usage: %s [path...] [expression]\n", program_name); + printf ("\ default path is the current directory; default expression is -print\n\ -expression may consist of: operators, options, tests, and actions:\n")); - puts (_("\ +expression may consist of:\n\ 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\ + ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n"); + printf ("\ + EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n\ +options (always true): -daystart -depth -follow --help\n\ + -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev\n\ +tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n"); + printf ("\ -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 (_("\ + -ilname PATTERN -iname PATTERN -inum N -ipath PATTERN -iregex PATTERN\n\ + -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE\n"); + printf ("\ -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\ - -readable -writable -executable\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>.")); + -size N[bckw] -true -type [bcdpfls] -uid N -used N -user NAME\n\ + -xtype [bcdpfls]\n"); + printf ("\ +actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\ + -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls\n"); exit (0); } -static float -estimate_pattern_match_rate(const char *pattern, int is_regex) -{ - if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, "."))) - { - /* A wildcard; assume the pattern matches most files. */ - return 0.8f; - } - else - { - return 0.1f; - } -} - static boolean -parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_ilname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *name; - if (collect_arg(argv, arg_ptr, &name)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.str = name; - /* Use the generic glob pattern estimator to figure out how many - * links will match, but bear in mind that most files won't be links. - */ - our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); - return true; - } - else - { - return false; - } -} - + struct predicate *our_pred; -/* sanity check the fnmatch() function to make sure that case folding - * is supported (as opposed to just having the flag ignored). - */ -static boolean -fnmatch_sanitycheck(void) -{ - static boolean checked = false; - if (!checked) - { - 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.")); - return false; - } - checked = true; - } - return checked; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_ilname); + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); } - static boolean -check_name_arg(const char *pred, const char *arg) +parse_iname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - if (options.warnings && 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, - safely_quote_err_filename(0, arg), - safely_quote_err_filename(1, arg)); - } - return true; /* allow it anyway */ -} - - + struct predicate *our_pred; -static boolean -parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *name; - fnmatch_sanitycheck(); - if (collect_arg(argv, arg_ptr, &name)) - { - if (check_name_arg("-iname", name)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->need_stat = our_pred->need_type = false; - our_pred->args.str = name; - our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); - return true; - } - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_iname); + our_pred->need_stat = 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) +parse_inum (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *p = insert_num (argv, arg_ptr, entry); - if (p) - { - /* inode number is exact match only, so very low proportions of - * files match - */ - p->est_success_rate = 1e-6; - return true; - } - else - { - return false; - } + return (insert_num (argv, arg_ptr, pred_inum)); } -/* -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) +parse_ipath (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *name; + struct predicate *our_pred; - fnmatch_sanitycheck (); - if (collect_arg (argv, arg_ptr, &name)) - { - struct predicate *our_pred = insert_primary_withpred (entry, pred_ipath); - our_pred->need_stat = our_pred->need_type = false; - our_pred->args.str = name; - our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); - return true; - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_ipath); + our_pred->need_stat = false; + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); } static boolean -parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_iregex (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return parse_ipath (entry, argv, arg_ptr); + return insert_regex (argv, arg_ptr, true); } static boolean -parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_links (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options); + return (insert_num (argv, arg_ptr, pred_links)); } static boolean -parse_links (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_lname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - struct predicate *p = insert_num (argv, arg_ptr, entry); - if (p) - { - if (p->args.numinfo.l_val == 1) - p->est_success_rate = 0.99; - else if (p->args.numinfo.l_val == 2) - p->est_success_rate = 0.01; - else - p->est_success_rate = 1e-3; - return true; - } - else - { - return false; - } -} + struct predicate *our_pred; -static boolean -parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *name; - fnmatch_sanitycheck(); - if (collect_arg(argv, arg_ptr, &name)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.str = name; - our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); - return true; - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_lname); + 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) +parse_ls (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - (void) &argv; - (void) &arg_ptr; - return insert_fls(entry, NULL); + struct predicate *our_pred; + + our_pred = insert_primary (pred_ls); + our_pred->side_effects = true; + return (true); } static boolean -insert_depthspec(const struct parser_table* entry, char **argv, int *arg_ptr, - int *limitptr) +parse_maxdepth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *depthstr; int depth_len; - const char *predicate = argv[(*arg_ptr)-1]; - if (collect_arg(argv, arg_ptr, &depthstr)) - { - depth_len = strspn (depthstr, "0123456789"); - if ((depth_len > 0) && (depthstr[depth_len] == 0)) - { - (*limitptr) = safe_atoi (depthstr); - if (*limitptr >= 0) - { - return parse_noop(entry, argv, arg_ptr); - } - } - error(1, 0, _("Expected a positive decimal integer argument to %s, but got %s"), - predicate, - quotearg_n_style(0, options.err_quoting_style, depthstr)); - return false; - } - /* missing argument */ - return false; -} - -static boolean -parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - return insert_depthspec(entry, argv, arg_ptr, &options.maxdepth); + 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); + maxdepth = atoi (argv[*arg_ptr]); + if (maxdepth < 0) + return (false); + (*arg_ptr)++; + return (true); } static boolean -parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_mindepth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_depthspec(entry, argv, arg_ptr, &options.mindepth); + int depth_len; + + 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); + mindepth = atoi (argv[*arg_ptr]); + if (mindepth < 0) + return (false); + (*arg_ptr)++; + return (true); } - static boolean -do_parse_xmin (const struct parser_table* entry, - char **argv, - int *arg_ptr, - enum xval xv) +parse_mmin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *minutes; + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; - if (collect_arg(argv, arg_ptr, &minutes)) - { - struct time_val tval; - tval.xval = xv; - struct timespec origin = options.cur_day_start; - origin.tv_sec += DAYSECS; - if (get_relative_timestamp(minutes, &tval, origin, 60, - "arithmetic overflow while converting %s " - "minutes to a number of seconds")) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.reftime = tval; - our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); - return true; - } - } - return false; -} -static boolean -parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME); -} - -static boolean -parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME); + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_mmin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); } - static boolean -parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_mtime (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME); + return (insert_time (argv, arg_ptr, pred_mtime)); } static boolean -parse_name (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_name (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *name; - if (collect_arg(argv, arg_ptr, &name)) - { - fnmatch_sanitycheck(); - if (check_name_arg("-name", name)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->need_stat = our_pred->need_type = false; - our_pred->args.str = name; - our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); - return true; - } - } - return false; + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_name); + our_pred->need_stat = 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) +parse_negate (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) &argv; - (void) &arg_ptr; - - our_pred = get_new_pred_chk_op (entry); + our_pred = get_new_pred_chk_op (); 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; + our_pred->need_stat = false; + return (true); } static boolean -parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_newer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; struct stat stat_newer; - set_stat_placeholders(&stat_newer); - if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) - { - our_pred = insert_primary (entry); - our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); - our_pred->args.reftime.xval = XVAL_MTIME; - our_pred->args.reftime.kind = COMP_GT; - our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); - return true; - } - return false; -} - - -static boolean -parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - (void) argv; - (void) arg_ptr; - if ((argv == NULL) || (argv[*arg_ptr] == NULL)) - { - return false; - } - else if (8u != strlen(argv[*arg_ptr])) - { - return false; - } - else - { - char x, y; - const char validchars[] = "aBcmt"; - - assert (0 == strncmp("-newer", argv[*arg_ptr], 6)); - x = argv[*arg_ptr][6]; - y = argv[*arg_ptr][7]; - - -#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) - if ('B' == x || 'B' == y) - { - error(0, 0, - _("This system does not provide a way to find the birth time of a file.")); - return 0; - } -#endif - - /* -newertY (for any Y) is invalid. */ - if (x == 't' - || 0 == strchr(validchars, x) - || 0 == strchr( validchars, y)) - { - return false; - } - else - { - struct predicate *our_pred; - - /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr - * past the test name (for most other tests, this is already done) - */ - (*arg_ptr)++; - - our_pred = insert_primary (entry); - - - switch (x) - { - case 'a': - our_pred->args.reftime.xval = XVAL_ATIME; - break; - case 'B': - our_pred->args.reftime.xval = XVAL_BIRTHTIME; - break; - case 'c': - our_pred->args.reftime.xval = XVAL_CTIME; - break; - case 'm': - our_pred->args.reftime.xval = XVAL_MTIME; - break; - default: - assert (strchr(validchars, x)); - assert (0); - } - - if ('t' == y) - { - if (!get_date(&our_pred->args.reftime.ts, - argv[*arg_ptr], - &options.start_time)) - { - error(1, 0, - _("I cannot figure out how to interpret %s as a date or time"), - quotearg_n_style(0, options.err_quoting_style, argv[*arg_ptr])); - } - } - else - { - struct stat stat_newer; - - /* Stat the named file. */ - set_stat_placeholders(&stat_newer); - if ((*options.xstat) (argv[*arg_ptr], &stat_newer)) - fatal_file_error(argv[*arg_ptr]); - - if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts)) - { - /* We cannot extract a timestamp from the struct stat. */ - error(1, 0, _("Cannot obtain birth time of file %s"), - safely_quote_err_filename(0, argv[*arg_ptr])); - } - } - our_pred->args.reftime.kind = COMP_GT; - our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec); - (*arg_ptr)++; - - assert (our_pred->pred_func != NULL); - assert (our_pred->pred_func == pred_newerXY); - assert (our_pred->need_stat); - return true; - } - } + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_newer); + 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) +parse_noleaf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - options.no_leaf_check = true; - return parse_noop(entry, argv, arg_ptr); + no_leaf_check = true; + return true; } #ifdef CACHE_IDS @@ -1609,15 +870,13 @@ unsigned gid_allocated; #endif static boolean -parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_nogroup (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) &argv; - (void) &arg_ptr; - - our_pred = insert_primary (entry); - our_pred->est_success_rate = 1e-4; + our_pred = insert_primary (pred_nogroup); #ifdef CACHE_IDS if (gid_unused == NULL) { @@ -1642,19 +901,17 @@ parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr) endgrent (); } #endif - return true; + return (true); } static boolean -parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_nouser (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) argv; - (void) arg_ptr; - - our_pred = insert_primary (entry); - our_pred->est_success_rate = 1e-3; + our_pred = insert_primary (pred_nouser); #ifdef CACHE_IDS if (uid_unused == NULL) { @@ -1679,399 +936,242 @@ parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr) endpwent (); } #endif - return true; -} - -static boolean -parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - options.warnings = false; - return parse_noop(entry, argv, arg_ptr); + return (true); } static boolean -parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_ok (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr); -} - -static boolean -parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr); + return (insert_exec_ok (pred_ok, argv, arg_ptr)); } boolean -parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_open (argv, arg_ptr) + 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_openparen; + our_pred = get_new_pred_chk_op (); + 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; + our_pred->need_stat = false; + return (true); } static boolean -parse_or (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_or (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) argv; - (void) arg_ptr; - - our_pred = get_new_pred (entry); + our_pred = get_new_pred (); 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; -} - -/* For some time, -path was 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 - * (a) HPUX find supports this predicate also and - * (b) it will soon be in POSIX anyway. - */ -static boolean -parse_path (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *name; - if (collect_arg(argv, arg_ptr, &name)) - { - struct predicate *our_pred = insert_primary_withpred (entry, pred_path); - our_pred->need_stat = our_pred->need_type = false; - our_pred->args.str = name; - our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); - return true; - } - return false; + our_pred->need_stat = false; + return (true); } static boolean -parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_path (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return parse_path (entry, argv, arg_ptr); -} + struct predicate *our_pred; -static void -non_posix_mode(const char *mode) -{ - if (options.posixly_correct) - { - error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."), - quotearg_n_style(0, options.err_quoting_style, mode)); - } + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_path); + our_pred->need_stat = 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) +parse_perm (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - mode_t perm_val[2]; - float rate; + unsigned long perm_val; int mode_start = 0; - boolean havekind = false; - enum permissions_type kind = PERM_EXACT; - struct mode_change *change = NULL; + struct mode_change *change; struct predicate *our_pred; - const char *perm_expr; - if (!collect_arg(argv, arg_ptr, &perm_expr)) - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); - switch (perm_expr[0]) + switch (argv[*arg_ptr][0]) { case '-': + case '+': mode_start = 1; - kind = PERM_AT_LEAST; - havekind = true; - rate = 0.2; - break; - - case '+': - change = mode_compile (perm_expr); - 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. - * - * Example: POSIXLY_CORRECT=y find -perm +a+x - */ - non_posix_mode(perm_expr); - - /* support the previous behaviour. */ - mode_start = 1; - kind = PERM_ANY; - rate = 0.3; - } - else - { - /* This is a POSIX-compatible usage */ - mode_start = 0; - kind = PERM_EXACT; - rate = 0.1; - } - havekind = true; - break; - - case '/': /* GNU extension */ - non_posix_mode(perm_expr); - mode_start = 1; - kind = PERM_ANY; - havekind = true; - rate = 0.3; break; - default: - /* For example, '-perm 0644', which is valid and matches - * only files whose mode is exactly 0644. - */ - mode_start = 0; - kind = PERM_EXACT; - havekind = true; - rate = 0.01; + /* empty */ break; } - if (NULL == change) - { - change = mode_compile (perm_expr + mode_start); - if (NULL == change) - error (1, 0, _("invalid mode %s"), - quotearg_n_style(0, options.err_quoting_style, perm_expr)); - } - perm_val[0] = mode_adjust (0, false, 0, change, NULL); - perm_val[1] = mode_adjust (0, true, 0, change, NULL); - free (change); - - if (('/' == perm_expr[0]) && (0 == perm_val[0]) && (0 == perm_val[1])) - { - /* The meaning of -perm /000 will change in the future. It - * currently matches no files, but like -perm -000 it should - * match all files. - * - * Starting in 2005, we used to issue a warning message - * informing the user that the behaviour would change in the - * future. We have now changed the behaviour and issue a - * warning message that the behaviour recently changed. - */ - error (0, 0, - _("warning: you have specified a mode pattern %s (which is " - "equivalent to /000). The meaning of -perm /000 has now been " - "changed to be consistent with -perm -000; that is, while it " - "used to match no files, it now matches all files."), - perm_expr); - - kind = PERM_AT_LEAST; - havekind = true; - - /* The "magic" number below is just the fraction of files on my - * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks). - * Actual totals are 1472 and 1073833. - */ - rate = 0.9986; /* probably matches anything but a broken symlink */ - } - - our_pred = insert_primary (entry); - our_pred->est_success_rate = rate; - if (havekind) - { - our_pred->args.perm.kind = kind; - } - else + change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS); + if (change == MODE_INVALID) + error (1, 0, "invalid mode `%s'", argv[*arg_ptr]); + else if (change == MODE_MEMORY_EXHAUSTED) + error (1, 0, "virtual memory exhausted"); + perm_val = mode_adjust (0, change); + mode_free (change); + + our_pred = insert_primary (pred_perm); + + switch (argv[*arg_ptr][0]) { - - switch (perm_expr[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; - } + case '-': + /* Set magic flag to indicate true if at least the given bits are set. */ + our_pred->args.perm = (perm_val & 07777) | 010000; + break; + case '+': + /* Set magic flag to indicate true if any of the given bits are set. */ + our_pred->args.perm = (perm_val & 07777) | 020000; + break; + default: + /* True if exactly the given bits are set. */ + our_pred->args.perm = (perm_val & 07777); + break; } - memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val); - return true; + (*arg_ptr)++; + return (true); } boolean -parse_print (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_print (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - (void) argv; - (void) arg_ptr; - - our_pred = insert_primary (entry); + our_pred = insert_primary (pred_print); /* -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; - open_stdout(&our_pred->args.printf_vec); - return true; + our_pred->side_effects = true; + our_pred->need_stat = false; + return (true); } static boolean -parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_print0 (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_fprint(entry, NULL); -} + struct predicate *our_pred; -static boolean -parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *format; - if (collect_arg(argv, arg_ptr, &format)) - { - struct format_val fmt; - open_stdout(&fmt); - return insert_fprintf (&fmt, entry, pred_fprintf, format); - } - return false; + our_pred = insert_primary (pred_print0); + /* -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 = true; + our_pred->need_stat = false; + return (true); } -static boolean -parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr) +static boolean +parse_printf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *format, *filename; - if (collect_arg(argv, arg_ptr, &filename)) - { - if (collect_arg(argv, arg_ptr, &format)) - { - struct format_val fmt; - open_output_file (filename, &fmt); - return insert_fprintf (&fmt, entry, pred_fprintf, format); - } - } - return false; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr)); } static boolean -parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_prune (argv, arg_ptr) + 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. */ - our_pred->est_success_rate = 1.0f; - return true; -} - - -static boolean -parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *type_name; - if (collect_arg(argv, arg_ptr, &type_name)) - { - /* collect the regex type name */ - options.regex_options = get_regex_type(type_name); - return parse_noop(entry, argv, arg_ptr); - } - return false; + our_pred = insert_primary (pred_prune); + our_pred->need_stat = false; + return (true); } - static boolean -parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_regex (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_regex (argv, arg_ptr, entry, options.regex_options); + return insert_regex (argv, arg_ptr, false); } static boolean -insert_regex (char **argv, - int *arg_ptr, - const struct parser_table *entry, - int regex_options) +insert_regex (argv, arg_ptr, ignore_case) + char *argv[]; + int *arg_ptr; + boolean ignore_case; { - const char *rx; - if (collect_arg(argv, arg_ptr, &rx)) + 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 (pred_regex); + our_pred->need_stat = 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; + + if (ignore_case) { - struct re_pattern_buffer *re; - const char *error_message; - struct predicate *our_pred = insert_primary_withpred (entry, pred_regex); - our_pred->need_stat = our_pred->need_type = false; - re = xmalloc (sizeof (struct re_pattern_buffer)); - our_pred->args.regex = re; - re->allocated = 100; - re->buffer = xmalloc (re->allocated); - re->fastmap = NULL; + unsigned i; - re_set_syntax(regex_options); - re->syntax = regex_options; - re->translate = NULL; - - error_message = re_compile_pattern (rx, strlen(rx), re); - if (error_message) - error (1, 0, "%s", error_message); - our_pred->est_success_rate = estimate_pattern_match_rate(rx, 1); - return true; + re->translate = xmalloc (256); + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < 256; i++) + re->translate[i] = ISUPPER (i) ? tolower (i) : i; } - return false; + else + 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) +parse_size (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { struct predicate *our_pred; - uintmax_t num; - char suffix; + unsigned long num; enum comparison_type c_type; - int blksize = 512; int len; - /* XXX: cannot (yet) convert to ue collect_arg() as this - * function modifies the args in-place. - */ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) - return false; - + return (false); len = strlen (argv[*arg_ptr]); if (len == 0) - error (1, 0, _("invalid null argument to -size")); - - suffix = argv[*arg_ptr][len - 1]; - switch (suffix) + error (1, 0, "invalid null argument to -size"); + switch (argv[*arg_ptr][len - 1]) { case 'b': blksize = 512; @@ -2088,16 +1188,6 @@ parse_size (const struct parser_table* entry, char **argv, int *arg_ptr) 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'; @@ -2116,577 +1206,198 @@ parse_size (const struct parser_table* entry, char **argv, int *arg_ptr) break; default: - error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]); + error (1, 0, "invalid -size type `%c'", argv[*arg_ptr][len - 1]); } - /* TODO: accept fractional megabytes etc. ? */ if (!get_num (argv[*arg_ptr], &num, &c_type)) - { - error(1, 0, - _("Invalid argument `%s%c' to -size"), - argv[*arg_ptr], (int)suffix); - return false; - } - our_pred = insert_primary (entry); + return (false); + our_pred = insert_primary (pred_size); our_pred->args.size.kind = c_type; our_pred->args.size.blocksize = blksize; our_pred->args.size.size = num; - our_pred->need_stat = true; - our_pred->need_type = false; - - if (COMP_GT == c_type) - our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9; - else if (COMP_LT == c_type) - our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1; - else - our_pred->est_success_rate = 0.01; - (*arg_ptr)++; - return true; + return (true); } - static boolean -parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_true (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - /* General idea: stat the file, remember device and inode numbers. - * If a candidate file matches those, it's the same file. - */ struct predicate *our_pred; - struct stat st, fst; - int fd, openflags; - - set_stat_placeholders(&st); - if (!collect_arg_stat_info(argv, arg_ptr, &st)) - return false; - - set_stat_placeholders(&fst); - /* POSIX systems are free to re-use the inode number of a deleted - * file. To ensure that we are not fooled by inode reuse, we hold - * the file open if we can. This would prevent the system reusing - * the file. - */ - fd = -3; /* means, uninitialised */ - openflags = O_RDONLY; - - if (options.symlink_handling == SYMLINK_NEVER_DEREF) - { - if (options.open_nofollow_available) - { - assert (O_NOFOLLOW != 0); - openflags |= O_NOFOLLOW; - fd = -1; /* safe to open it. */ - } - else - { - if (S_ISLNK(st.st_mode)) - { - /* no way to ensure that a symlink will not be followed - * by open(2), so fall back on using lstat(). Accept - * the risk that the named file will be deleted and - * replaced with another having the same inode. - * - * Avoid opening the file. - */ - fd = -2; /* Do not open it */ - } - else - { - fd = -1; - /* Race condition here: the file might become a symlink here. */ - } - } - } - else - { - /* We want to dereference the symlink anyway */ - fd = -1; /* safe to open it without O_NOFOLLOW */ - } - assert (fd != -3); /* check we made a decision */ - if (fd == -1) - { - /* Race condition here. The file might become a - * symbolic link in between out call to stat and - * the call to open. - */ - fd = open(argv[*arg_ptr], openflags); - - if (fd >= 0) - { - /* We stat the file again here to prevent a race condition - * between the first stat and the call to open(2). - */ - if (0 != fstat(fd, &fst)) - { - fatal_file_error(argv[*arg_ptr]); - } - else - { - /* Worry about the race condition. If the file became a - * symlink after our first stat and before our call to - * open, fst may contain the stat information for the - * destination of the link, not the link itself. - */ - if ((*options.xstat) (argv[*arg_ptr], &st)) - fatal_file_error(argv[*arg_ptr]); - - if ((options.symlink_handling == SYMLINK_NEVER_DEREF) - && (!options.open_nofollow_available)) - { - if (S_ISLNK(st.st_mode)) - { - /* We lost the race. Leave the data in st. The - * file descriptor points to the wrong thing. - */ - close(fd); - fd = -1; - } - else - { - /* Several possibilities here: - * 1. There was no race - * 2. The file changed into a symlink after the stat and - * before the open, and then back into a non-symlink - * before the second stat. - * - * In case (1) there is no problem. In case (2), - * the stat() and fstat() calls will have returned - * different data. O_NOFOLLOW was not available, - * so the open() call may have followed a symlink - * even if the -P option is in effect. - */ - if ((st.st_dev == fst.st_dev) - && (st.st_ino == fst.st_ino)) - { - /* No race. No need to copy fst to st, - * since they should be identical (modulo - * differences in padding bytes). - */ - } - else - { - /* We lost the race. Leave the data in st. The - * file descriptor points to the wrong thing. - */ - close(fd); - fd = -1; - } - } - } - else - { - st = fst; - } - } - } - } - - our_pred = insert_primary (entry); - our_pred->args.samefileid.ino = st.st_ino; - our_pred->args.samefileid.dev = st.st_dev; - our_pred->args.samefileid.fd = fd; - our_pred->need_type = false; - our_pred->need_stat = true; - our_pred->est_success_rate = 0.01f; - return true; + our_pred = insert_primary (pred_true); + our_pred->need_stat = false; + return (true); } -#if 0 -/* This function is commented out partly because support for it is - * uneven. - */ static boolean -parse_show_control_chars (const struct parser_table* entry, - char **argv, - int *arg_ptr) +parse_type (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - const char *arg; - const char *errmsg = _("The -show-control-chars option takes " - "a single argument which " - "must be 'literal' or 'safe'"); - - if ((argv == NULL) || (argv[*arg_ptr] == NULL)) - { - error (1, errno, "%s", errmsg); - return false; - } - else - { - arg = argv[*arg_ptr]; - - if (0 == strcmp("literal", arg)) - { - options.literal_control_chars = true; - } - else if (0 == strcmp("safe", arg)) - { - options.literal_control_chars = false; - } - else - { - error (1, errno, "%s", errmsg); - return false; - } - (*arg_ptr)++; /* consume the argument. */ - return true; - } + return insert_type (argv, arg_ptr, pred_type); } -#endif - static boolean -parse_true (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_uid (argv, arg_ptr) + 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->est_success_rate = 1.0f; - return true; + return (insert_num (argv, arg_ptr, pred_uid)); } static boolean -parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - (void) entry; - return parse_true(get_noop(), argv, arg_ptr); -} +parse_used (argv, arg_ptr) + char *argv[]; + int *arg_ptr; -static boolean -parse_accesscheck (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; - if (pred_is(our_pred, pred_executable)) - our_pred->est_success_rate = 0.2; - else - our_pred->est_success_rate = 0.9; - 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); -} + unsigned long num_days; + enum comparison_type c_type; -static boolean -parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - struct predicate *p = insert_num (argv, arg_ptr, entry); - if (p) - { - p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; - return true; - } - else - { - return false; - } + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num (argv[*arg_ptr], &num_days, &c_type)) + return (false); + our_pred = insert_primary (pred_used); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = num_days * DAYSECS; + (*arg_ptr)++; + return (true); } static boolean -parse_used (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_user (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { + struct passwd *cur_pwd; struct predicate *our_pred; - struct time_val tval; - const char *offset_str; - const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds"; + uid_t uid; + int uid_len; - if (collect_arg(argv, arg_ptr, &offset_str)) - { - /* The timespec is actually a delta value, so we use an origin of 0. */ - struct timespec zero = {0,0}; - if (get_relative_timestamp(offset_str, &tval, zero, DAYSECS, errmsg)) - { - our_pred = insert_primary (entry); - our_pred->args.reftime = tval; - our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS); - return true; - } - else - { - error(1, 0, _("Invalid argument %s to -used"), offset_str); - return false; - } - } + 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 { - return false; /* missing argument */ + 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 (pred_user); + our_pred->args.uid = uid; + (*arg_ptr)++; + return (true); } static boolean -parse_user (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - const char *username; - - if (collect_arg(argv, arg_ptr, &username)) - { - struct predicate *our_pred; - uid_t uid; - struct passwd *cur_pwd = getpwnam(username); - endpwent(); - if (cur_pwd != NULL) - { - uid = cur_pwd->pw_uid; - } - else - { - int uid_len = strspn (username, "0123456789"); - if (uid_len && (username[uid_len]==0)) - uid = safe_atoi (username); - else - return false; - } - our_pred = insert_primary (entry); - our_pred->args.uid = uid; - our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2; - return true; - } - return false; -} - -static boolean -parse_version (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_version (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - int features = 0; - int flags; - - (void) argv; - (void) arg_ptr; - (void) entry; - - display_findutils_version("find"); - 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 + extern char *version_string; - flags = 0; - if (is_fts_enabled(&flags)) - { - int nflags = 0; - printf("FTS("); - ++features; - - if (flags & FTS_CWDFD) - { - if (nflags) - { - printf(","); - } - printf("FTS_CWDFD"); - ++nflags; - } - printf(") "); - } - - printf("CBO(level=%d) ", (int)(options.optimisation_level)); - ++features; - - if (0 == features) - { - /* For the moment, leave this as English in case someone wants - to parse these strings. */ - printf("none"); - } - printf("\n"); - + fflush (stderr); + printf ("GNU find version %s\n", version_string); exit (0); } static boolean -parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_xdev (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - options.stay_on_filesystem = true; - return parse_noop(entry, argv, arg_ptr); -} - -static boolean -parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - options.ignore_readdir_race = true; - return parse_noop(entry, argv, arg_ptr); -} - -static boolean -parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - options.ignore_readdir_race = false; - return parse_noop(entry, argv, arg_ptr); -} - -static boolean -parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr) -{ - options.warnings = true; - return parse_noop(entry, argv, arg_ptr); + stay_on_filesystem = true; + return true; } static boolean -parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_xtype (argv, arg_ptr) + char *argv[]; + int *arg_ptr; { - return insert_type (argv, arg_ptr, entry, pred_xtype); + return insert_type (argv, arg_ptr, pred_xtype); } static boolean -insert_type (char **argv, int *arg_ptr, - const struct parser_table *entry, - PRED_FUNC which_pred) +insert_type (argv, arg_ptr, which_pred) + char *argv[]; + int *arg_ptr; + boolean (*which_pred) (); { - mode_t type_cell; + unsigned long type_cell; struct predicate *our_pred; - float rate = 0.5; - const char *typeletter; - if (collect_arg(argv, arg_ptr, &typeletter)) + if ((argv == NULL) || (argv[*arg_ptr] == NULL) + || (strlen (argv[*arg_ptr]) != 1)) + return (false); + switch (argv[*arg_ptr][0]) { - if (strlen(typeletter) != 1u) - { - error(1, 0, _("Arguments to -type should contain only one letter")); - return false; - } - - switch (typeletter[0]) - { - case 'b': /* block special */ - type_cell = S_IFBLK; - rate = 0.01f; - break; - case 'c': /* character special */ - type_cell = S_IFCHR; - rate = 0.01f; - break; - case 'd': /* directory */ - type_cell = S_IFDIR; - rate = 0.4f; - break; - case 'f': /* regular file */ - type_cell = S_IFREG; - rate = 0.95f; - break; + 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; - rate = 0.1f; - break; + case 'l': /* symbolic link */ + type_cell = S_IFLNK; + break; #endif #ifdef S_IFIFO - case 'p': /* pipe */ - type_cell = S_IFIFO; - rate = 0.01f; - break; + case 'p': /* pipe */ + type_cell = S_IFIFO; + break; #endif #ifdef S_IFSOCK - case 's': /* socket */ - type_cell = S_IFSOCK; - rate = 0.01f; - break; -#endif -#ifdef S_IFDOOR - case 'D': /* Solaris door */ - type_cell = S_IFDOOR; - rate = 0.01f; - break; + case 's': /* socket */ + type_cell = S_IFSOCK; + break; #endif - default: /* None of the above ... nuke 'em. */ - error(1, 0, _("Unknown argument to -type: %c"), (*typeletter)); - return false; - } - our_pred = insert_primary_withpred (entry, which_pred); - our_pred->est_success_rate = rate; - - /* 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; - return true; + default: /* None of the above ... nuke 'em. */ + return (false); } - return false; + our_pred = insert_primary (which_pred); + our_pred->args.type = type_cell; + (*arg_ptr)++; /* Move on to next argument. */ + return (true); } +/* If true, we've determined that the current fprintf predicate + uses stat information. */ +static boolean fprintf_stat_needed; -/* 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; - } - -} - - - - -/* XXX: do we need to pass FUNC to this function? */ static boolean -insert_fprintf (struct format_val *vec, - const struct parser_table *entry, PRED_FUNC func, - const char *format_const) +insert_fprintf (fp, func, argv, arg_ptr) + FILE *fp; + boolean (*func) (); + char *argv[]; + int *arg_ptr; { - char *format = (char*)format_const; /* XXX: casting away constness */ + 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; - our_pred = insert_primary_withpred (entry, func); - our_pred->side_effects = our_pred->no_default_print = true; - our_pred->args.printf_vec = *vec; - our_pred->need_type = false; - our_pred->need_stat = false; - our_pred->p_cost = NeedsNothing; + format = argv[(*arg_ptr)++]; + fprintf_stat_needed = false; /* Might be overridden later. */ + our_pred = insert_primary (func); + our_pred->side_effects = true; + our_pred->args.printf_vec.stream = fp; segmentp = &our_pred->args.printf_vec.segment; *segmentp = NULL; @@ -2716,12 +1427,9 @@ insert_fprintf (struct format_val *vec, *scan = '\b'; break; case 'c': - make_segment (segmentp, format, scan - format, - KIND_STOP, 0, 0, - our_pred); - if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo)) - our_pred->p_cost = NeedsStatInfo; - return true; + make_segment (segmentp, format, scan - format, KIND_STOP); + our_pred->need_stat = fprintf_stat_needed; + return (true); case 'f': *scan = '\f'; break; @@ -2741,30 +1449,22 @@ insert_fprintf (struct format_val *vec, /* *scan = '\\'; * it already is */ break; default: - error (0, 0, - _("warning: unrecognized escape `\\%c'"), *scan2); + error (0, 0, "warning: unrecognized escape `\\%c'", *scan2); scan++; continue; } } segmentp = make_segment (segmentp, format, scan - format + 1, - KIND_PLAIN, 0, 0, - our_pred); + KIND_PLAIN); format = scan2 + 1; /* Move past the escape. */ scan = scan2; /* Incremented immediately by `for'. */ } else if (*scan == '%') { - if (scan[1] == 0) - { - /* Trailing %. We don't like those. */ - error (1, 0, _("error: %s at end of format string"), scan); - } - else if (scan[1] == '%') + if (scan[1] == '%') { segmentp = make_segment (segmentp, format, scan - format + 1, - KIND_PLAIN, 0, 0, - our_pred); + KIND_PLAIN); scan++; format = scan + 1; continue; @@ -2777,19 +1477,17 @@ insert_fprintf (struct format_val *vec, if (*scan2 == '.') for (scan2++; ISDIGIT (*scan2); scan2++) /* Do nothing. */ ; - if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) + if (strchr ("abcdfFgGhHiklmnpPstuU", *scan2)) { segmentp = make_segment (segmentp, format, scan2 - format, - KIND_FORMAT, *scan2, 0, - our_pred); + (int) *scan2); scan = scan2; format = scan + 1; } - else if (strchr ("ABCT", *scan2) && scan2[1]) + else if (strchr ("ACT", *scan2) && scan2[1]) { segmentp = make_segment (segmentp, format, scan2 - format, - KIND_FORMAT, scan2[0], scan2[1], - our_pred); + *scan2 | (scan2[1] << 8)); scan = scan2 + 1; format = scan + 1; continue; @@ -2797,11 +1495,10 @@ insert_fprintf (struct format_val *vec, else { /* An unrecognized % escape. Print the char after the %. */ - error (0, 0, _("warning: unrecognized format directive `%%%c'"), + error (0, 0, "warning: unrecognized format directive `%%%c'", *scan2); segmentp = make_segment (segmentp, format, scan - format, - KIND_PLAIN, 0, 0, - our_pred); + KIND_PLAIN); format = scan + 1; continue; } @@ -2809,9 +1506,9 @@ insert_fprintf (struct format_val *vec, } if (scan > format) - make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0, - our_pred); - return true; + make_segment (segmentp, format, scan - format, KIND_PLAIN); + our_pred->need_stat = fprintf_stat_needed; + return (true); } /* Create a new fprintf segment in *SEGMENT, with type KIND, @@ -2819,476 +1516,208 @@ insert_fprintf (struct format_val *vec, 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 format_char, - char aux_format_char, - struct predicate *pred) -{ - enum EvaluationCost mycost = NeedsNothing; +make_segment (segment, format, len, kind) + struct segment **segment; + char *format; + int len, kind; +{ char *fmt; - *segment = xmalloc (sizeof (struct segment)); + *segment = (struct segment *) xmalloc (sizeof (struct segment)); - (*segment)->segkind = kind; - (*segment)->format_char[0] = format_char; - (*segment)->format_char[1] = aux_format_char; + (*segment)->kind = kind; (*segment)->next = NULL; (*segment)->text_len = len; - fmt = (*segment)->text = xmalloc (len + sizeof "d"); + fmt = (*segment)->text = xmalloc (len + 3); /* room for "ld\0" */ strncpy (fmt, format, len); fmt += len; - switch (kind) + switch (kind & 0xff) { case KIND_PLAIN: /* Plain text string, no % conversion. */ case KIND_STOP: /* Terminate argument, no newline. */ - assert (0 == format_char); - assert (0 == aux_format_char); - *fmt = '\0'; - if (mycost > pred->p_cost) - pred->p_cost = NeedsNothing; - return &(*segment)->next; break; - } - assert (kind == KIND_FORMAT); - switch (format_char) - { - case 'l': /* object of symlink */ - pred->need_stat = true; - mycost = NeedsLinkName; - *fmt++ = 's'; - break; - - case 'y': /* file type */ - pred->need_type = true; - mycost = NeedsType; - *fmt++ = 's'; - break; - case 'a': /* atime in `ctime' format */ - case 'A': /* atime in user-specified strftime format */ - case 'B': /* birth time in user-specified strftime format */ case 'c': /* ctime in `ctime' format */ - case 'C': /* ctime in user-specified strftime format */ - case 'F': /* file system type */ + case 'F': /* filesystem type */ case 'g': /* group name */ - case 'i': /* inode number */ - case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */ - case 's': /* size in bytes */ + case 'l': /* object of symlink */ case 't': /* mtime in `ctime' format */ - case 'T': /* mtime in user-specified strftime format */ case 'u': /* user name */ - pred->need_stat = true; - mycost = NeedsStatInfo; - *fmt++ = 's'; - break; - - case 'S': /* sparseness */ - pred->need_stat = true; - mycost = NeedsStatInfo; - *fmt++ = 'g'; - break; - - case 'Y': /* symlink pointed file type */ - pred->need_stat = true; - mycost = NeedsType; /* true for amortised effect */ - *fmt++ = 's'; - break; - + case 'A': /* atime in user-specified strftime format */ + case 'C': /* ctime in user-specified strftime format */ + case 'T': /* mtime in user-specified strftime format */ + 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; - case 'H': /* ARGV element file was found under */ - *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 (NOT birthtime in ctime fmt)*/ - case 'D': /* Filesystem device on which the file exits */ + case 'b': /* size in 512-byte blocks */ case 'k': /* size in 1K blocks */ + case 's': /* size in bytes */ + *fmt++ = 'l'; + /*FALLTHROUGH*/ case 'n': /* number of links */ - pred->need_stat = true; - mycost = NeedsStatInfo; - *fmt++ = 's'; - break; - - /* Numeric items that DO honour #, 0, + flags. - */ + fprintf_stat_needed = true; + /* FALLTHROUGH */ case 'd': /* depth in search tree (0 = ARGV element) */ *fmt++ = 'd'; break; - case 'm': /* mode as octal number (perms only) */ - *fmt++ = 'o'; - pred->need_stat = true; - mycost = NeedsStatInfo; + case 'i': /* inode number */ + *fmt++ = 'l'; + /*FALLTHROUGH*/ + case 'G': /* GID number */ + case 'U': /* UID number */ + *fmt++ = 'u'; + fprintf_stat_needed = true; break; - case '{': - case '[': - case '(': - error (1, 0, - _("error: the format directive `%%%c' is reserved for future use"), - (int)kind); - /*NOTREACHED*/ + case 'm': /* mode as octal number (perms only) */ + *fmt++ = 'o'; + fprintf_stat_needed = true; break; } *fmt = '\0'; - if (mycost > pred->p_cost) - pred->p_cost = mycost; - return &(*segment)->next; -} - -static void -check_path_safety(const char *action, char **argv) -{ - char *s; - const char *path = getenv("PATH"); - if (NULL == path) - { - /* $PATH is not set. Assume the OS default is safe. - * That may not be true on Windows, but I'm not aware - * of a way to get Windows to avoid searching the - * current directory anyway. - */ - return; - } - - (void)argv; - - 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); - } - else if ('/' != s[0]) - { - /* Relative paths are also dangerous in $PATH. */ - error(1, 0, _("The relative path %s is included in the PATH " - "environment variable, which is insecure in " - "combination with the %s action of find. " - "Please remove that entry from $PATH"), - safely_quote_err_filename(0, s), - action); - } - } + return (&(*segment)->next); } - -/* handles both exec and ok predicate */ static boolean -new_insert_exec_ok (const char *action, - const struct parser_table *entry, - int dirfd, - char **argv, - int *arg_ptr) +insert_exec_ok (func, argv, arg_ptr) + boolean (*func) (); + 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; - enum BC_INIT_STATUS bcstatus; - + 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; + return (false); - our_pred = insert_primary_withpred (entry, func); - our_pred->side_effects = our_pred->no_default_print = true; - our_pred->need_type = our_pred->need_stat = false; - - 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, argv); - 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 "+". - */ + /* Count the number of args with path replacements, up until the ';'. */ start = *arg_ptr; - for (end = start, saw_braces=0, brace_count=0; + for (end = start, num_paths = 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 (mbsstr (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 file system. 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.")); - } - } - } - + if (strstr (argv[end], "{}")) + num_paths++; /* 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); - } - - /* We use a switch statement here so that the compiler warns us when - * we forget to handle a newly invented enum value. - * - * Like xargs, we allow 2KiB of headroom for the launched utility to - * export its own environment variables before calling something - * else. - */ - bcstatus = bc_init_controlinfo(&execp->ctl, 2048u); - switch (bcstatus) - { - case BC_INIT_ENV_TOO_BIG: - case BC_INIT_CANNOT_ACCOMODATE_HEADROOM: - error(1, 0, - _("The environment is too large for exec().")); - break; - case BC_INIT_OK: - /* Good news. Carry on. */ - break; + return (false); } - bc_use_sensible_arg_max(&execp->ctl); - - execp->ctl.exec_callback = launch; - - if (our_pred->args.exec_vec.multiple) + our_pred = insert_primary (func); + our_pred->side_effects = true; + execp = &our_pred->args.exec_vec; + 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++) { - /* "+" 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 */ + register char *p; - /* 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) + 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) { - bc_push_arg(&execp->ctl, &execp->state, - argv[i], strlen(argv[i])+1, - NULL, 0, - 1); + execp->paths[path_pos].offset = vec_pos; + execp->paths[path_pos].origarg = argv[end]; + path_pos++; } + execp->vec[vec_pos++] = argv[end]; } - 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->paths[path_pos].offset = -1; + execp->vec[vec_pos] = NULL; - - /* 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; -} - - - -static boolean -insert_exec_ok (const char *action, - const struct parser_table *entry, - int dirfd, - char **argv, - int *arg_ptr) -{ - return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr); + return (true); } - - -/* Get a timestamp and comparison type. - +/* Get a number of days and comparison type. STR is the ASCII representation. - Set *NUM_DAYS to the number of days/minutes/whatever, taken as being - relative to ORIGIN (usually the current moment or midnight). - Thus the sense of the comparison type appears to be reversed. + 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. - Issue OVERFLOWMESSAGE if overflow occurs. + 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_relative_timestamp (const char *str, - struct time_val *result, - struct timespec origin, - double sec_per_unit, - const char *overflowmessage) +get_num_days (str, num_days, comp_type) + char *str; + unsigned long *num_days; + enum comparison_type *comp_type; { - uintmax_t checkval; - double offset, seconds, nanosec; - - if (get_comp_type(&str, &result->kind)) - { - /* Invert the sense of the comparison */ - switch (result->kind) - { - case COMP_LT: result->kind = COMP_GT; break; - case COMP_GT: result->kind = COMP_LT; break; - default: break; - } + int len_days; /* length of field */ - /* Convert the ASCII number into floating-point. */ - if (xstrtod(str, NULL, &offset, strtod)) - { - /* Separate the floating point number the user specified - * (which is a number of days, or minutes, etc) into an - * integral number of seconds (SECONDS) and a fraction (NANOSEC). - */ - nanosec = modf(offset * sec_per_unit, &seconds); - nanosec *= 1.0e9; /* convert from fractional seconds to ns. */ - - result->ts.tv_sec = origin.tv_sec - seconds; - result->ts.tv_nsec = origin.tv_nsec - nanosec; - checkval = (uintmax_t)origin.tv_sec - seconds; - - if (origin.tv_nsec < nanosec) - { - /* Perform a carry operation */ - result->ts.tv_nsec += 1000000000; - result->ts.tv_sec -= 1; - checkval -= 1; - } - /* Check for overflow. */ - if (checkval != result->ts.tv_sec) - { - /* an overflow has occurred. */ - error (1, 0, overflowmessage, str); - } - return true; - } - else - { - /* Conversion from ASCII to double failed. */ - return false; - } - } - else + if (str == NULL) + return (false); + switch (str[0]) { - return false; + case '+': + *comp_type = COMP_LT; + str++; + break; + case '-': + *comp_type = COMP_GT; + str++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *comp_type = COMP_EQ; + break; + default: + return (false); } + + /* We know the first char has been reasonable. Find the + number of days to play with. */ + len_days = strspn (str, "0123456789"); + if ((len_days == 0) || (str[len_days] != '\0')) + return (false); + *num_days = (unsigned long) atol (str); + return (true); } -/* Insert a time predicate based on the information in ENTRY. +/* 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. @@ -3300,137 +1729,100 @@ get_relative_timestamp (const char *str, Used by -atime, -ctime, and -mtime parsers. */ -static boolean -parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) +static boolean +insert_time (argv, arg_ptr, pred) + char *argv[]; + int *arg_ptr; + PFB pred; { struct predicate *our_pred; - struct time_val tval; - enum comparison_type comp; - const char *timearg, *orig_timearg; - const char *errmsg = "arithmetic overflow while converting %s " - "days to a number of seconds"; - struct timespec origin; - - if (!collect_arg(argv, arg_ptr, &timearg)) - return false; - orig_timearg = timearg; - - /* Decide the origin by previewing the comparison type. */ - origin = options.cur_day_start; - - if (get_comp_type(&timearg, &comp)) - { - /* Remember, we invert the sense of the comparison, so this tests - * against COMP_LT instead of COMP_GT... - */ - if (COMP_LT == comp) - { - uintmax_t expected = origin.tv_sec + (DAYSECS-1); - origin.tv_sec += (DAYSECS-1); - if (origin.tv_sec != expected) - { - error(1, 0, - _("arithmetic overflow when trying to calculate the end of today")); - } - } - } - /* We discard the value of comp here, as get_relative_timestamp - * will set tval.kind. For that to work, we have to restore - * timearg so that it points to the +/- prefix, if any. get_comp_type() - * will have advanced timearg, so we restore it. - */ - timearg = orig_timearg; - - if (!get_relative_timestamp(timearg, &tval, origin, DAYSECS, errmsg)) - return false; - - our_pred = insert_primary (entry); - our_pred->args.reftime = tval; - our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); + unsigned long num_days; + enum comparison_type c_type; - if (options.debug_options & DebugExpressionTree) - { - time_t t; - - fprintf (stderr, "inserting %s\n", our_pred->p_name); - fprintf (stderr, " type: %s %s ", - (tval.kind == COMP_GT) ? "gt" : - ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")), - (tval.kind == COMP_GT) ? " >" : - ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?"))); - t = our_pred->args.reftime.ts.tv_sec; - fprintf (stderr, "%ju %s", - (uintmax_t) our_pred->args.reftime.ts.tv_sec, - ctime (&t)); - if (tval.kind == COMP_EQ) - { - t = our_pred->args.reftime.ts.tv_sec + DAYSECS; - fprintf (stderr, " < %ju %s", - (uintmax_t) t, ctime (&t)); - } - } - - return true; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num_days, &c_type)) + return (false); + our_pred = insert_primary (pred); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start - num_days * DAYSECS + + ((c_type == COMP_GT) ? DAYSECS - 1 : 0); + (*arg_ptr)++; +#ifdef DEBUG + printf ("inserting %s\n", our_pred->p_name); + printf (" 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) ? ">=" : " ?"))); + printf ("%ld %s", our_pred->args.info.l_val, + ctime (&our_pred->args.info.l_val)); + if (c_type == COMP_EQ) + { + our_pred->args.info.l_val += DAYSECS; + printf (" < %ld %s", our_pred->args.info.l_val, + ctime (&our_pred->args.info.l_val)); + our_pred->args.info.l_val -= DAYSECS; + } +#endif /* DEBUG */ + return (true); } -/* Get the comparison type prefix (if any) from a number argument. - The prefix is at *STR. +/* Get a number with comparision information. + The sense of the comparision information is 'normal'; that is, + '+' looks for inums or links > 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. - Advance *STR beyond any initial comparison prefix. + + Return true if all okay, false if input error. + + Used by the -inum and -links predicate parsers. */ - Return true if all okay, false if input error. */ static boolean -get_comp_type(const char **str, enum comparison_type *comp_type) +get_num (str, num, comp_type) + char *str; + unsigned long *num; + enum comparison_type *comp_type; { - switch (**str) + int len_num; /* Length of field. */ + + if (str == NULL) + return (false); + switch (str[0]) { case '+': *comp_type = COMP_GT; - (*str)++; + str++; break; case '-': *comp_type = COMP_LT; - (*str)++; + str++; break; - default: + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': *comp_type = COMP_EQ; break; + default: + return (false); } - 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 (const char *str, - uintmax_t *num, - enum comparison_type *comp_type) -{ - char *pend; - - if (str == NULL) - return false; - /* Figure out the comparison type if the caller accepts one. */ - if (comp_type) - { - if (!get_comp_type(&str, comp_type)) - return false; - } - - return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK; + /* We know the first char has been reasonable. Find the number of + days to play with. */ + len_num = strspn (str, "0123456789"); + if ((len_num == 0) || (str[len_num] != '\0')) + return (false); + *num = (unsigned long) atol (str); + return (true); } /* Insert a number predicate. @@ -3445,70 +1837,48 @@ get_num (const char *str, Used by -inum and -links parsers. */ -static struct predicate * -insert_num (char **argv, int *arg_ptr, const struct parser_table *entry) +static boolean +insert_num (argv, arg_ptr, pred) + char *argv[]; + int *arg_ptr; + PFB pred; { - const char *numstr; - - if (collect_arg(argv, arg_ptr, &numstr)) - { - uintmax_t num; - enum comparison_type c_type; + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; - if (get_num (numstr, &num, &c_type)) - { - struct predicate *our_pred = insert_primary (entry); - our_pred->args.numinfo.kind = c_type; - our_pred->args.numinfo.l_val = num; - - if (options.debug_options & DebugExpressionTree) - { - 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.numinfo.l_val); - } - return our_pred; - } - } - return NULL; + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = num; + (*arg_ptr)++; +#ifdef DEBUG + printf ("inserting %s\n", our_pred->p_name); + printf (" 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) ? " =" : " ?"))); + printf ("%ld\n", our_pred->args.info.l_val); +#endif /* DEBUG */ + return (true); } -static void -open_output_file (const char *path, struct format_val *p) +static FILE * +open_output_file (path) + char *path; { - p->segment = NULL; - p->quote_opts = clone_quoting_options (NULL); - + FILE *f; + if (!strcmp (path, "/dev/stderr")) - { - p->stream = stderr; - p->filename = _("standard error"); - } + return (stderr); else if (!strcmp (path, "/dev/stdout")) - { - p->stream = stdout; - p->filename = _("standard output"); - } - else - { - p->stream = fopen_safer (path, "w"); - p->filename = path; - - if (p->stream == NULL) - { - fatal_file_error(path); - } - } - - p->dest_is_tty = stream_is_tty(p->stream); -} - -static void -open_stdout (struct format_val *p) -{ - open_output_file("/dev/stdout", p); + return (stdout); + f = fopen (path, "w"); + if (f == NULL) + error (1, errno, "%s", path); + return (f); } |