diff options
Diffstat (limited to 'find')
-rw-r--r-- | find/Makefile.am | 52 | ||||
-rw-r--r-- | find/Makefile.in | 160 | ||||
-rw-r--r-- | find/defs.h | 681 | ||||
-rw-r--r-- | find/find.1 | 1792 | ||||
-rw-r--r-- | find/find.c | 1626 | ||||
-rw-r--r-- | find/fstype.c | 465 | ||||
-rw-r--r-- | find/parser.c | 3864 | ||||
-rw-r--r-- | find/pred.c | 2513 | ||||
-rw-r--r-- | find/tree.c | 1492 | ||||
-rw-r--r-- | find/util.c | 1101 | ||||
-rw-r--r-- | find/version.c | 1 |
11 files changed, 3226 insertions, 10521 deletions
diff --git a/find/Makefile.am b/find/Makefile.am index b0015098..d8096cde 100644 --- a/find/Makefile.am +++ b/find/Makefile.am @@ -1,38 +1,14 @@ -AUTOMAKE_OPTIONS = std-options -localedir = $(datadir)/locale -# noinst_PROGRAMS = regexprops -# regexprops_SOURCES = regexprops.c - -noinst_LIBRARIES = libfindtools.a -libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c - - -# We always build two versions of find, one with fts, one without. -# Their names depend on whether the user specified --with-fts. -# -# --with-fts find extra binary -# yes with fts 'oldfind', without fts -# no without fts 'ftsfind', with fts -# -if WITH_FTS -bin_PROGRAMS = find oldfind -find_SOURCES = ftsfind.c -oldfind_SOURCES = find.c -else -bin_PROGRAMS = find ftsfind -find_SOURCES = find.c -ftsfind_SOURCES = ftsfind.c -endif - -EXTRA_DIST = defs.h $(man_MANS) -INCLUDES = -I../gnulib/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCALEDIR=\"$(localedir)\" -LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -man_MANS = find.1 -SUBDIRS = . testsuite - -#$(PROGRAMS): ../lib/libfind.a - -dist-hook: findutils-check-manpages - -findutils-check-manpages: - $(top_srcdir)/build-aux/man-lint.sh $(srcdir) $(man_MANS) +PROGRAMS = find +find_SOURCES = find.c fstype.c parser.c pred.c tree.c util.c version.c +DIST_OTHER = defs.h +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../lib/libfind.a +MANS = find.1 +CONFIG_HEADER = ../config.h + +$(PROGRAMS): ../lib/libfind.a + +parser.o: ../lib/modechange.h +find.o fstype.o parser.o pred.o: ../lib/modetype.h +find.o fstype.o parser.o pred.o tree.o util.o: defs.h +pred.o: ../lib/wait.h diff --git a/find/Makefile.in b/find/Makefile.in new file mode 100644 index 00000000..1f658cb0 --- /dev/null +++ b/find/Makefile.in @@ -0,0 +1,160 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 1994 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ +ANSI2KNR = ./ansi2knr + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +find_OBJECTS = find.o fstype.o parser.o pred.o tree.o util.o version.o +NROFF = nroff + +SOURCES = ${find_SOURCES} +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +PROGRAMS = find +find_SOURCES = find.c fstype.c parser.c pred.c tree.c util.c version.c +DIST_OTHER = defs.h +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../lib/libfind.a +MANS = find.1 +CONFIG_HEADER = ../config.h + +all:: ${ALL} + +.c.o: + $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $< + +$(find_OBJECTS): ../config.h +install:: install-programs + +install-programs: $(PROGRAMS) $(SCRIPTS) + $(top_srcdir)/mkinstalldirs $(bindir) + for p in $(PROGRAMS) $(SCRIPTS); do \ + $(INSTALL_PROGRAM) $$p $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +uninstall:: uninstall-programs + +uninstall-programs: + for p in $(PROGRAMS) $(SCRIPTS); do \ + rm -f $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +find: $(find_OBJECTS) + $(CC) -o $@ $(find_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +install:: install-man + +install-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\.\([0-9][a-z]*\)$$%\1%'`; \ + inst=`basename $$man $$sect|sed '$(transform)'`$$sect; \ + mdir=$(mandir)/man$$sect; \ + $(top_srcdir)/mkinstalldirs $$mdir; \ + echo installing $$man as $$mdir/$$inst; \ + $(INSTALL_DATA) $(srcdir)/$$man $$mdir/$$inst; \ + cdir=$(mandir)/cat$$sect; \ + if test -d $$cdir; then \ + echo formatting $$man as $$cdir/$$inst; \ + $(NROFF) -man $(srcdir)/$$man > $$cdir/$$inst; \ + fi; \ + done + +uninstall:: uninstall-man + +uninstall-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\(\.[0-9][a-z]*\)$$%\1%'; \ + inst=`basename $$man $sect|sed '$(transform)'`.$$sect; \ + mdir=$(mandir)/man$$sect; \ + cdir=$(mandir)/cat$$sect; \ + rm -f $$mdir/$$inst $$cdir/$$inst; \ + done + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +$(PROGRAMS): ../lib/libfind.a + +parser.o: ../lib/modechange.h +find.o fstype.o parser.o pred.o: ../lib/modetype.h +find.o fstype.o parser.o pred.o tree.o util.o: defs.h +pred.o: ../lib/wait.h diff --git a/find/defs.h b/find/defs.h index d076aa9b..ec029de1 100644 --- a/find/defs.h +++ b/find/defs.h @@ -1,101 +1,62 @@ /* defs.h -- data types and declarations. - Copyright (C) 1990, 91, 92, 93, 94, 2000, 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - - -#ifndef INC_DEFS_H -#define INC_DEFS_H 1 - -#if !defined(ALREADY_INCLUDED_CONFIG_H) -/* - * Savannah bug #20128: if we include some system header and it - * includes some othersecond system header, the second system header - * may in fact turn out to be a file provided by gnulib. For that - * situation, we need to have already included <config.h> so that the - * Gnulib files have access to the information probed by their - * configure script fragments. So <config.h> should be the first - * thing included. - */ -#error "<config.h> should be #included before defs.h, and indeed before any other header" -Please stop compiling the program now -#endif + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <sys/types.h> - -/* XXX: some of these includes probably don't belong in a common header file */ -#include <sys/stat.h> -#include <stdio.h> /* for FILE* */ +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) #include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <time.h> -#include <limits.h> /* for CHAR_BIT */ -#include <stdbool.h> /* for bool/boolean */ -#include <stdint.h> /* for uintmax_t */ -#include <sys/stat.h> /* S_ISUID etc. */ - - - -#ifndef CHAR_BIT -# define CHAR_BIT 8 +#else +#include <strings.h> +#ifndef strchr +#define strchr index +#endif +#ifndef strrchr +#define strrchr rindex #endif - -#if HAVE_INTTYPES_H -# include <inttypes.h> #endif -typedef bool boolean; -#include "regex.h" -#include "timespec.h" -#include "buildcmd.h" -#include "quotearg.h" - -/* These days we will assume ANSI/ISO C protootypes work on our compiler. */ -#define PARAMS(Args) Args - -#ifndef ATTRIBUTE_NORETURN -# if HAVE_ATTRIBUTE_NORETURN -# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) -# else -# define ATTRIBUTE_NORETURN /* nothing */ -# endif +#include <errno.h> +#ifndef errno +extern int errno; #endif -int optionl_stat PARAMS((const char *name, struct stat *p)); -int optionp_stat PARAMS((const char *name, struct stat *p)); -int optionh_stat PARAMS((const char *name, struct stat *p)); -int debug_stat PARAMS((const char *file, struct stat *bufp)); +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif -void set_stat_placeholders PARAMS((struct stat *p)); -int get_statinfo PARAMS((const char *pathname, const char *name, struct stat *p)); +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <time.h> -#define MODE_WXUSR (S_IWUSR | S_IXUSR) -#define MODE_R (S_IRUSR | S_IRGRP | S_IROTH) -#define MODE_RW (S_IWUSR | S_IWGRP | S_IWOTH | MODE_R) -#define MODE_RWX (S_IXUSR | S_IXGRP | S_IXOTH | MODE_RW) -#define MODE_ALL (S_ISUID | S_ISGID | S_ISVTX | MODE_RWX) +#include "regex.h" +#if __STDC__ +# define P_(s) s +#else +# define P_(s) () +#endif -struct predicate; -struct options; +/* Not char because of type promotion; NeXT gcc can't handle it. */ +typedef int boolean; +#define true 1 +#define false 0 -/* Pointer to a predicate function. */ -typedef boolean (*PRED_FUNC)(const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr); +/* Pointer to function returning boolean. */ +typedef boolean (*PFB)(); /* The number of seconds in a day. */ #define DAYSECS 86400 @@ -109,13 +70,6 @@ enum comparison_type COMP_EQ }; -enum permissions_type -{ - PERM_AT_LEAST, - PERM_ANY, - PERM_EXACT -}; - enum predicate_type { NO_TYPE, @@ -139,64 +93,27 @@ enum predicate_precedence struct long_val { enum comparison_type kind; - boolean negative; /* Defined only when representing time_t. */ - uintmax_t l_val; -}; - -struct perm_val -{ - enum permissions_type kind; - mode_t val[2]; -}; - -/* dir_id is used to support loop detection in find.c - */ -struct dir_id -{ - ino_t ino; - dev_t dev; -}; - -/* samefile_file_id is used to support the -samefile test. - */ -struct samefile_file_id -{ - ino_t ino; - dev_t dev; - int fd; + unsigned long l_val; }; struct size_val { enum comparison_type kind; int blocksize; - uintmax_t size; + unsigned long size; }; - -enum xval - { - XVAL_ATIME, XVAL_BIRTHTIME, XVAL_CTIME, XVAL_MTIME, XVAL_TIME - }; - -struct time_val +struct path_arg { - enum xval xval; - enum comparison_type kind; - struct timespec ts; + short offset; /* Offset in `vec' of this arg. */ + short count; /* Number of path replacements in this arg. */ + char *origarg; /* Arg with "{}" intact. */ }; - struct exec_val { - boolean multiple; /* -exec {} \+ denotes multiple argument. */ - struct buildcmd_control ctl; - struct buildcmd_state state; - char **replace_vec; /* Command arguments (for ";" style) */ - int num_args; - boolean use_current_dir; /* If nonzero, don't chdir to start dir */ - boolean close_stdin; /* If true, close stdin in the child. */ - int dirfd; /* The directory to do the exec in. */ + struct path_arg *paths; /* Array of args with path replacements. */ + char **vec; /* Array of args to pass to program. */ }; /* The format string for a -printf or -fprintf is chopped into one or @@ -205,17 +122,12 @@ struct exec_val each \c and `%' conversion is a segment. */ /* Special values for the `kind' field of `struct segment'. */ -enum SegmentKind - { - KIND_PLAIN=0, /* Segment containing just plain text. */ - KIND_STOP=1, /* \c -- stop printing and flush output. */ - KIND_FORMAT, /* Regular format */ - }; +#define KIND_PLAIN 0 /* Segment containing just plain text. */ +#define KIND_STOP 1 /* \c -- stop printing and flush output. */ struct segment { - enum SegmentKind segkind; /* KIND_FORMAT, KIND_PLAIN, KIND_STOP */ - char format_char[2]; /* Format chars if kind is KIND_FORMAT */ + int kind; /* Format chars or KIND_{PLAIN,STOP}. */ char *text; /* Plain text or `%' format string. */ int text_len; /* Length of `text'. */ struct segment *next; /* Next segment for this predicate. */ @@ -225,38 +137,12 @@ struct format_val { struct segment *segment; /* Linked list of segments. */ FILE *stream; /* Output stream to print on. */ - const char *filename; /* We need the filename for error messages. */ - boolean dest_is_tty; /* True if the destination is a terminal. */ - struct quoting_options *quote_opts; -}; - -/* Profiling information for a predicate */ -struct predicate_performance_info -{ - unsigned long visits; - unsigned long successes; }; -/* evaluation cost of a predicate */ -enum EvaluationCost -{ - NeedsNothing, - NeedsType, - NeedsStatInfo, - NeedsLinkName, - NeedsAccessInfo, - NeedsSyncDiskHit, - NeedsEventualExec, - NeedsImmediateExec, - NeedsUserInteraction, - NeedsUnknown, - NumEvaluationCosts -}; - struct predicate { /* Pointer to the function that implements this predicate. */ - PRED_FUNC pred_func; + PFB pred_func; /* Only used for debugging, but defined unconditionally so individual modules can be compiled with -DDEBUG. */ @@ -270,54 +156,32 @@ struct predicate /* The precedence of this node. Only has meaning for operators. */ enum predicate_precedence p_prec; - /* True if this predicate node produces side effects. - If side_effects are produced - then optimization will not be performed */ + /* True if this predicate node produces side effects. */ boolean side_effects; - /* True if this predicate node requires default print be turned off. */ - boolean no_default_print; - /* True if this predicate node requires a stat system call to execute. */ boolean need_stat; - /* True if this predicate node requires knowledge of the file type. */ - boolean need_type; - - enum EvaluationCost p_cost; - - /* est_success_rate is a number between 0.0 and 1.0 */ - float est_success_rate; - - /* True if this predicate should display control characters literally */ - boolean literal_control_chars; - - /* True if this predicate didn't originate from the user. */ - boolean artificial; - - /* The raw text of the argument of this predicate. */ - char *arg_text; - /* Information needed by the predicate processor. Next to each member are listed the predicates that use it. */ union { - const char *str; /* fstype [i]lname [i]name [i]path */ + char *str; /* fstype [i]lname [i]name [i]path */ struct re_pattern_buffer *regex; /* regex */ struct exec_val exec_vec; /* exec ok */ - struct long_val numinfo; /* gid inum links uid */ + struct long_val info; /* atime ctime mtime inum links */ struct size_val size; /* size */ uid_t uid; /* user */ gid_t gid; /* group */ - struct time_val reftime; /* newer newerXY anewer cnewer mtime atime ctime mmin amin cmin */ - struct perm_val perm; /* perm */ - struct samefile_file_id samefileid; /* samefile */ - mode_t type; /* type */ - struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */ + time_t time; /* newer */ + unsigned long perm; /* perm */ + unsigned long type; /* type */ + FILE *stream; /* fprint fprint0 */ + struct format_val printf_vec; /* printf fprintf */ } args; /* The next predicate in the user input sequence, - which represents the order in which the user supplied the + which repesents the order in which the user supplied the predicates on the command line. */ struct predicate *pred_next; @@ -326,334 +190,143 @@ struct predicate processed. */ struct predicate *pred_left; struct predicate *pred_right; - - struct predicate_performance_info perf; - - const struct parser_table* parser_entry; }; -/* find.c, ftsfind.c */ -boolean is_fts_enabled(int *ftsoptions); -int get_start_dirfd(void); -int get_current_dirfd(void); - /* find library function declarations. */ -/* find global function declarations. */ - -/* find.c */ -/* SymlinkOption represents the choice of - * -P, -L or -P (default) on the command line. - */ -enum SymlinkOption - { - SYMLINK_NEVER_DEREF, /* Option -P */ - SYMLINK_ALWAYS_DEREF, /* Option -L */ - SYMLINK_DEREF_ARGSONLY /* Option -H */ - }; -extern enum SymlinkOption symlink_handling; /* defined in find.c. */ - -void set_follow_state PARAMS((enum SymlinkOption opt)); -void cleanup(void); - -/* fstype.c */ -char *filesystem_type PARAMS((const struct stat *statp, const char *path)); -char * get_mounted_filesystems (void); -dev_t * get_mounted_devices PARAMS((size_t *)); +/* dirname.c */ +char *dirname P_((char *path)); +/* error.c */ +void error P_((int status, int errnum, char *message, ...)); +/* listfile.c */ +void list_file P_((char *name, char *relname, struct stat *statp, FILE *stream)); +char *get_link_name P_((char *name, char *relname)); -enum arg_type - { - ARG_OPTION, /* regular options like -maxdepth */ - ARG_NOOP, /* does nothing, returns true, internal use only */ - ARG_POSITIONAL_OPTION, /* options whose position is important (-follow) */ - ARG_TEST, /* a like -name */ - ARG_SPECIAL_PARSE, /* complex to parse, don't eat the test name before calling parse_xx(). */ - ARG_PUNCTUATION, /* like -o or ( */ - ARG_ACTION /* like -print */ - }; - - -struct parser_table; -/* Pointer to a parser function. */ -typedef boolean (*PARSE_FUNC)(const struct parser_table *p, - char *argv[], int *arg_ptr); -struct parser_table -{ - enum arg_type type; - char *parser_name; - PARSE_FUNC parser_func; - PRED_FUNC pred_func; -}; - -/* parser.c */ -const struct parser_table* find_parser PARAMS((char *search_name)); -boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); -void pred_sanity_check PARAMS((const struct predicate *predicates)); -void check_option_combinations (const struct predicate *p); -void parse_begin_user_args PARAMS((char **args, int argno, const struct predicate *last, const struct predicate *predicates)); -void parse_end_user_args PARAMS((char **args, int argno, const struct predicate *last, const struct predicate *predicates)); -boolean parse_openparen PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr)); -boolean parse_closeparen PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr)); - -/* pred.c */ - -typedef boolean PREDICATEFUNCTION(const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr); -PREDICATEFUNCTION pred_amin; -PREDICATEFUNCTION pred_and; -PREDICATEFUNCTION pred_anewer; -PREDICATEFUNCTION pred_atime; -PREDICATEFUNCTION pred_closeparen; -PREDICATEFUNCTION pred_cmin; -PREDICATEFUNCTION pred_cnewer; -PREDICATEFUNCTION pred_comma; -PREDICATEFUNCTION pred_ctime; -PREDICATEFUNCTION pred_delete; -PREDICATEFUNCTION pred_empty; -PREDICATEFUNCTION pred_exec; -PREDICATEFUNCTION pred_execdir; -PREDICATEFUNCTION pred_executable; -PREDICATEFUNCTION pred_false; -PREDICATEFUNCTION pred_fls; -PREDICATEFUNCTION pred_fprint; -PREDICATEFUNCTION pred_fprint0; -PREDICATEFUNCTION pred_fprintf; -PREDICATEFUNCTION pred_fstype; -PREDICATEFUNCTION pred_gid; -PREDICATEFUNCTION pred_group; -PREDICATEFUNCTION pred_ilname; -PREDICATEFUNCTION pred_iname; -PREDICATEFUNCTION pred_inum; -PREDICATEFUNCTION pred_ipath; -PREDICATEFUNCTION pred_links; -PREDICATEFUNCTION pred_lname; -PREDICATEFUNCTION pred_ls; -PREDICATEFUNCTION pred_mmin; -PREDICATEFUNCTION pred_mtime; -PREDICATEFUNCTION pred_name; -PREDICATEFUNCTION pred_negate; -PREDICATEFUNCTION pred_newer; -PREDICATEFUNCTION pred_newerXY; -PREDICATEFUNCTION pred_nogroup; -PREDICATEFUNCTION pred_nouser; -PREDICATEFUNCTION pred_ok; -PREDICATEFUNCTION pred_okdir; -PREDICATEFUNCTION pred_openparen; -PREDICATEFUNCTION pred_or; -PREDICATEFUNCTION pred_path; -PREDICATEFUNCTION pred_perm; -PREDICATEFUNCTION pred_print; -PREDICATEFUNCTION pred_print0; -PREDICATEFUNCTION pred_prune; -PREDICATEFUNCTION pred_quit; -PREDICATEFUNCTION pred_readable; -PREDICATEFUNCTION pred_regex; -PREDICATEFUNCTION pred_samefile; -PREDICATEFUNCTION pred_size; -PREDICATEFUNCTION pred_true; -PREDICATEFUNCTION pred_type; -PREDICATEFUNCTION pred_uid; -PREDICATEFUNCTION pred_used; -PREDICATEFUNCTION pred_user; -PREDICATEFUNCTION pred_writable; -PREDICATEFUNCTION pred_xtype; - - - -int launch PARAMS((const struct buildcmd_control *ctl, - struct buildcmd_state *buildstate)); - - -char *find_pred_name PARAMS((PRED_FUNC pred_func)); - - - -void print_predicate PARAMS((FILE *fp, const struct predicate *p)); -void print_tree PARAMS((FILE*, struct predicate *node, int indent)); -void print_list PARAMS((FILE*, struct predicate *node)); -void print_optlist PARAMS((FILE *fp, const struct predicate *node)); -void show_success_rates(const struct predicate *node); +/* savedir.c */ +char *savedir P_((char *dir, unsigned name_size)); +/* stpcpy.c */ +#if !HAVE_STPCPY +char *stpcpy P_((char *dest, const char *src)); +#endif -/* tree.c */ -struct predicate * build_expression_tree PARAMS((int argc, char *argv[], int end_of_leading_options)); -struct predicate * get_eval_tree PARAMS((void)); -struct predicate *get_new_pred PARAMS((const struct parser_table *entry)); -struct predicate *get_new_pred_chk_op PARAMS((const struct parser_table *entry)); -float calculate_derived_rates PARAMS((struct predicate *p)); +/* xgetcwd.c */ +char *xgetcwd P_((void)); -/* util.c */ -struct predicate *insert_primary PARAMS((const struct parser_table *entry)); -struct predicate *insert_primary_withpred PARAMS((const struct parser_table *entry, PRED_FUNC fptr)); -void usage PARAMS((FILE *fp, int status, char *msg)); -extern boolean check_nofollow(void); -void complete_pending_execs(struct predicate *p); -void complete_pending_execdirs(int dirfd); /* Passing dirfd is an unpleasant CodeSmell. */ -const char *safely_quote_err_filename (int n, char const *arg); -void fatal_file_error(const char *name) ATTRIBUTE_NORETURN; -void nonfatal_file_error(const char *name); - -int process_leading_options PARAMS((int argc, char *argv[])); -void set_option_defaults PARAMS((struct options *p)); - -#if 0 -#define apply_predicate(pathname, stat_buf_ptr, node) \ - (*(node)->pred_func)((pathname), (stat_buf_ptr), (node)) +/* xmalloc.c */ +#if __STDC__ +#define VOID void #else -boolean apply_predicate(const char *pathname, struct stat *stat_buf, struct predicate *p); +#define VOID char #endif -#define pred_is(node, fn) ( ((node)->pred_func) == (fn) ) - +VOID *xmalloc P_((size_t n)); +VOID *xrealloc P_((VOID *p, size_t n)); -/* find.c. */ -int get_info PARAMS((const char *pathname, struct stat *p, struct predicate *pred_ptr)); -int following_links PARAMS((void)); -int digest_mode PARAMS((mode_t mode, const char *pathname, const char *name, struct stat *pstat, boolean leaf)); -boolean default_prints PARAMS((struct predicate *pred)); -boolean looks_like_expression PARAMS((const char *arg, boolean leading)); +/* xstrdup.c */ +char *xstrdup P_((char *string)); +/* find global function declarations. */ -enum DebugOption - { - DebugNone = 0, - DebugExpressionTree = 1, - DebugStat = 2, - DebugSearch = 4, - DebugTreeOpt = 8, - DebugHelp = 16, - DebugExec = 32, - DebugSuccessRates = 64 - }; - -struct options -{ - /* If true, process directory before contents. True unless -depth given. */ - boolean do_dir_first; - /* If true, -depth was EXPLICITLY set (as opposed to having been turned - * on by -delete, for example). - */ - boolean explicit_depth; - - /* If >=0, don't descend more than this many levels of subdirectories. */ - int maxdepth; - - /* If >=0, don't process files above this level. */ - int mindepth; - - /* If true, do not assume that files in directories with nlink == 2 - are non-directories. */ - boolean no_leaf_check; - - /* If true, don't cross filesystem boundaries. */ - boolean stay_on_filesystem; - - /* If true, we ignore the problem where we find that a directory entry - * no longer exists by the time we get around to processing it. - */ - boolean ignore_readdir_race; - - /* If true, pass control characters through. If false, escape them - * or turn them into harmless things. - */ - boolean literal_control_chars; - - /* If true, we issue warning messages - */ - boolean warnings; - - /* If true, avoid POSIX-incompatible behaviours - * (this functionality is currently incomplete - * and at the moment affects mainly warning messages). - */ - boolean posixly_correct; - - struct timespec start_time; /* Time at start of execution. */ - - /* Either one day before now (the default), or the start of today (if -daystart is given). */ - struct timespec cur_day_start; - - /* If true, cur_day_start has been adjusted to the start of the day. */ - boolean full_days; - - int output_block_size; /* Output block size. */ - - /* bitmask for debug options */ - unsigned long debug_options; - - enum SymlinkOption symlink_handling; - - - /* Pointer to the function used to stat files. */ - int (*xstat) (const char *name, struct stat *statbuf); - - - /* Indicate if we can implement safely_chdir() using the O_NOFOLLOW - * flag to open(2). - */ - boolean open_nofollow_available; - - /* The variety of regular expression that we support. - * The default is POSIX Basic Regular Expressions, but this - * can be changed with the positional option, -regextype. - */ - int regex_options; - - /* Optimisation level. One is the default. - */ - unsigned short optimisation_level; - - - /* How should we quote filenames in error messages and so forth? - */ - enum quoting_style err_quoting_style; -}; -extern struct options options; +/* fstype.c */ +char *filesystem_type P_((char *path, char *relpath, struct stat *statp)); +/* parser.c */ +PFB find_parser P_((char *search_name)); +boolean parse_close P_((char *argv[], int *arg_ptr)); +boolean parse_open P_((char *argv[], int *arg_ptr)); +boolean parse_print P_((char *argv[], int *arg_ptr)); -struct state -{ - /* Current depth; 0 means current path is a command line arg. */ - int curdepth; - - /* If true, we have called stat on the current path. */ - boolean have_stat; - - /* If true, we know the type of the current path. */ - boolean have_type; - mode_t type; /* this is the actual type */ - - /* The file being operated on, relative to the current directory. - Used for stat, readlink, remove, and opendir. */ - char *rel_pathname; - /* The directory fd to which rel_pathname is relative. Thsi is relevant - * when we're navigating the hierarchy with fts() and using FTS_CWDFD. - */ - int cwd_dir_fd; - - /* Length of starting path. */ - int starting_path_length; - - /* If true, don't descend past current directory. - Can be set by -prune, -maxdepth, and -xdev/-mount. */ - boolean stop_at_current_level; - - /* Status value to return to system. */ - int exit_status; - - /* True if there are any execdirs. This saves us a pair of fchdir() - * calls for every directory we leave if it is false. This is just - * an optimisation. Set to true if you want to be conservative. - */ - boolean execdirs_outstanding; -}; +/* pred.c */ +boolean pred_amin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_and P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_anewer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_atime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_close P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_cmin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_cnewer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_comma P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ctime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_empty P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_exec P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_false P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fls P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprint P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprint0 P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprintf P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fstype P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_gid P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_group P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ilname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_iname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_inum P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ipath P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_links P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_lname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ls P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_mmin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_mtime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_name P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_negate P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_newer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_nogroup P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_nouser P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ok P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_open P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_or P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_path P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_perm P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_print P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_print0 P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_prune P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_regex P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_size P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_true P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_type P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_uid P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_used P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_user P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_xtype P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +char *find_pred_name P_((PFB pred_func)); +#ifdef DEBUG +void print_tree P_((struct predicate *node, int indent)); +void print_list P_((struct predicate *node)); +#endif /* DEBUG */ -/* finddata.c */ -extern struct state state; -extern char const *starting_dir; -extern int starting_desc; -extern char *program_name; +/* tree.c */ +struct predicate *get_expr P_((struct predicate **input, int prev_prec)); +boolean opt_expr P_((struct predicate **eval_treep)); +boolean mark_stat P_((struct predicate *tree)); +/* util.c */ +char *basename P_((char *fname)); +struct predicate *get_new_pred P_((void)); +struct predicate *get_new_pred_chk_op P_((void)); +struct predicate *insert_primary P_((boolean (*pred_func )())); +void usage P_((char *msg)); +extern char *program_name; +extern struct predicate *predicates; +extern struct predicate *last_pred; +extern boolean do_dir_first; +extern int maxdepth; +extern int mindepth; +extern int curdepth; +extern time_t cur_day_start; +extern boolean full_days; +extern boolean no_leaf_check; +extern boolean stay_on_filesystem; +extern boolean stop_at_current_level; +extern boolean have_stat; +extern char *rel_pathname; +#ifndef HAVE_FCHDIR +extern char *starting_dir; +#else +extern int starting_desc; #endif +extern int exit_status; +extern int path_length; +extern int (*xstat) (); +extern boolean dereference; diff --git a/find/find.1 b/find/find.1 index d7f40cf9..d6280d40 100644 --- a/find/find.1 +++ b/find/find.1 @@ -1,14 +1,13 @@ -.TH FIND 1 \" -*- nroff -*- +.TH FIND 1L \" -*- nroff -*- .SH NAME find \- search for files in a directory hierarchy .SH SYNOPSIS -.B find -[\-H] [\-L] [\-P] [\-D debugopts] [\-Olevel] [path...] [expression] +.B find +[path...] [expression] .SH DESCRIPTION This manual page documents the GNU version of .BR find . -GNU .B find searches the directory tree rooted at each given file name by evaluating the given expression from left to right, according to the @@ -18,400 +17,53 @@ known (the left hand side is false for \fIand\fR operations, true for .B find moves on to the next file name. .PP -If you are using -.B find -in an environment where security is important (for example if you are -using it to seach directories that are writable by other users), you -should read the "Security Considerations" chapter of the findutils -documentation, which is called \fBFinding Files\fP and comes with -findutils. That document also includes a lot more detail -and discussion than this manual page, so you may find it a more useful -source of information. -.SH OPTIONS -The -.BR \-H , -.B \-L -and -.B \-P -options control the treatment of symbolic -links. Command-line arguments following these are taken to be names -of files or directories to be examined, up to the first argument that -begins with `\-', or the argument `(' or `!'. That argument and any -following arguments are taken to be the expression describing what is -to be searched for. If no paths are given, the current directory is -used. If no expression is given, the expression -.B \-print -is used -(but you should probably consider using -.B \-print0 -instead, anyway). +The first argument that begins with `\-', `(', `)', `,', or `!' is taken +to be the beginning of the expression; any arguments before it are +paths to search, and any arguments after it are the rest of the +expression. If no paths are given, the current directory is used. If +no expression is given, the expression `\-print' is used. .PP -This manual page talks about `options' within the expression list. -These options control the behaviour of -.B find -but are specified immediately after the last path name. The five -`real' options -.BR \-H , -.BR \-L , -.BR \-P , -.B \-D -and -.B \-O -must appear before -the first path name, if at all. A double dash -.B \-\- -can also be used -to signal that any remaining arguments are not options (though -ensuring that all start points begin with either `./' or `/' is -generally safer if you use wildcards in the list of start points). -.IP \-P -Never follow symbolic links. This is the default behaviour. When -.B find -examines or prints information a file, and the file is a symbolic -link, the information used shall be taken from the properties of the -symbolic link itself. - -.IP \-L -Follow symbolic links. When .B find -examines or prints information about files, the information used shall -be taken from the properties of the file to which the link points, not -from the link itself (unless it is a broken symbolic link or -.B find -is unable to examine the file to which the link points). Use of this -option implies -.BR \-noleaf . -If you later use the -.B \-P -option, -.B \-noleaf -will still be in effect. If -.B \-L -is in effect and -.B find -discovers a symbolic link to a subdirectory during its search, -the subdirectory pointed to by the symbolic link will be searched. -.IP -When the -.B \-L -option is in effect, the -.B \-type -predicate will always -match against the type of the file that a symbolic link points to -rather than the link itself (unless the symbolic link is broken). -Using -.B \-L -causes the -.B \-lname -and -.B \-ilname -predicates always to return -false. - -.IP \-H -Do not follow symbolic links, except while processing the command -line arguments. When -.B find -examines or prints information about files, the information used -shall be taken from the properties of the symbolic link itself. The -only exception to this behaviour is when a file specified on the -command line is a symbolic link, and the link can be resolved. For -that situation, the information used is taken from whatever the link -points to (that is, the link is followed). The information about the -link itself is used as a fallback if the file pointed to by the -symbolic link cannot be examined. If -.B \-H -is in effect and one of the -paths specified on the command line is a symbolic link to a directory, -the contents of that directory will be examined (though of course -\-maxdepth 0 would prevent this). -.P -If more than one of -.BR \-H , -.B \-L -and -.B \-P -is specified, each overrides the -others; the last one appearing on the command line takes effect. -Since it is the default, the -.B \-P -option should be considered to be in -effect unless either -.B \-H -or -.B \-L -is specified. - -GNU -.B find -frequently stats files during the processing of the command line -itself, before any searching has begun. These options also affect how -those arguments are processed. Specifically, there are a number of -tests that compare files listed on the command line against a file we -are currently considering. In each case, the file specified on the -command line will have been examined and some of its properties will -have been saved. If the named file is in fact a symbolic link, and -the -.B \-P -option is in effect (or if neither -.B \-H -nor -.B \-L -were specified), the information used for the comparison will be taken from -the properties of the symbolic link. Otherwise, it will be taken from -the properties of the file the link points to. If -.B find -cannot follow the link (for example because it has insufficient -privileges or the link points to a nonexistent file) the properties of -the link itself will be used. -.P -When the -.B \-H -or -.B \-L options are in effect, any symbolic links listed -as the argument of -.B \-newer -will be dereferenced, and the timestamp -will be taken from the file to which the symbolic link points. The -same consideration applies to -.BR \-newerXY , -.B \-anewer -and -.BR \-cnewer . - -The -.B \-follow -option has a similar effect to -.BR \-L , -though it takes -effect at the point where it appears (that is, if -.B \-L -is not used but -.B \-follow -is, any symbolic links appearing after -.B \-follow -on the -command line will be dereferenced, and those before it will not). - -.IP "\-D debugoptions" -Print diagnostic information; this can be helpful to diagnose problems -with why -.B find -is not doing what you want. The list of debug options should be comma -separated. Compatibility of the debug options is not guaranteed -between releases of findutils. For a complete list of valid debug -options, see the output of -.B find \-D -.BR help . -Valid debug options include -.RS -.IP help -Explain the debugging options -.IP tree -Show the expression tree in its original and optimised form. -.IP stat -Print messages as files are examined with the -.B stat -and -.B lstat -system calls. The -.B find -program tries to minimise such calls. -.IP opt -Prints diagnostic information relating to the optimisation of the -expression tree; see the \-O option. -.IP rates -Prints a summary indicating how often each predicate succeeded or -failed. -.RE -.IP \-Olevel -Enables query optimisation. The -.B find -program reorders tests to speed up execution while preserving the -overall effect; that is, predicates with side effects are not -reordered relative to each other. The optimisations performed at each -optimisation level are as follows. -.RS -.IP 0 -Equivalent to optimisation level 1. -.IP 1 -This is the default optimisation level and corresponds to the -traditional behaviour. Expressions are reordered so that tests based -only on the names of files (for example -.B \-name -and -.BR \-regex ) -are performed first. -.IP 2 -Any -.B \-type -or -.B \-xtype -tests are performed after any tests based only on the names of files, -but before any tests that require information from the inode. On many -modern versions of Unix, file types are returned by -.B readdir() -and so these predicates are faster to evaluate than predicates which -need to stat the file first. -.IP 3 -At this optimisation level, the full cost-based query optimiser is -enabled. The order of tests is modified so that cheap (i.e. fast) -tests are performed first and more expensive ones are performed later, -if necessary. Within each cost band, predicates are evaluated earlier -or later according to whether they are likely to succeed or not. For -.BR \-o , -predicates which are likely to succeed are evaluated earlier, and for -.BR \-a , -predicates which are likely to fail are evaluated earlier. -.RE -.IP -The cost-based optimiser has a fixed idea of how likely any given test -is to succeed. In some cases the probability takes account of the -specific nature of the test (for example, -.B \-type f -is assumed to be more likely to succeed than -.BR "\-type c" ). -The cost-based optimiser is currently being evaluated. If it does -not actually improve the performance of -.BR find , -it will be removed again. Conversely, optimisations that prove to be -reliable, robust and effective may be enabled at lower optimisation -levels over time. However, the default behaviour (i.e. optimisation -level 1) will not be changed in the 4.3.x release series. The -findutils test suite runs all the tests on -.B find -at each optimisation level and ensures that the result is the same. -.P +exits with status 0 if all files are processed successfully, greater +than 0 if errors occur. .SH EXPRESSIONS +.P The expression is made up of options (which affect overall operation -rather than the processing of a specific file, and always return -true), tests (which return a true or false value), and actions (which -have side effects and return a true or false value), all separated by -operators. -.B \-and -is assumed where the operator is omitted. - -If the expression contains no actions other than -.BR \-prune , -.B \-print -is -performed on all files for which the expression is true. - +rather than the processing of a specific file, and always return true), +tests (which return a true or false value), and actions (which have side +effects and return a true or false value), all separated by operators. +\-and is assumed where the operator is omitted. If the expression contains +no actions other than \-prune, \-print is performed on all files +for which the expression is true. .SS OPTIONS .P -All options always return true. Except for -.BR \-daystart , -.B \-follow -and -.BR \-regextype , -the options affect all tests, including tests specified -before the option. This is because the options are processed when the -command line is parsed, while the tests don't do anything until files -are examined. The -.BR \-daystart , -.B \-follow -and -.B \-regextype -options are different in this respect, and have an effect only on tests which -appear later in the command line. Therefore, for clarity, it is best -to place them at the beginning of the expression. A warning is issued -if you don't do this. - -.IP \-d -A synonym for \-depth, for compatibility with FreeBSD, NetBSD, MacOS X and OpenBSD. - +All options always return true. They always take effect, rather than +being processed only when their place in the expression is reached. +Therefore, for clarity, it is best to place them at the beginning of +the expression. .IP \-daystart -Measure times (for -.BR \-amin , -.BR \-atime , -.BR \-cmin , -.BR \-ctime , -.BR \-mmin , -and -.BR \-mtime ) -from the beginning of today rather than from 24 hours ago. This -option only affects tests which appear later on the command line. - +Measure times (for \-amin, \-atime, \-cmin, \-ctime, \-mmin, and \-mtime) +from the beginning of today rather than from 24 hours ago. .IP \-depth -Process each directory's contents before the directory itself. The -\-delete action also implies -.BR \-depth . - +Process each directory's contents before the directory itself. .IP \-follow -Deprecated; use the -.B \-L -option instead. Dereference symbolic links. -Implies -.BR \-noleaf . -The -.B \-follow -option affects only those tests which -appear after it on the command line. Unless the -.B \-H -or -.B \-L -option has -been specified, the position of the -.B \-follow -option changes the behaviour of the -.B \-newer -predicate; any files listed as the argument -of -.B \-newer -will be dereferenced if they are symbolic links. The same -consideration applies to -.BR \-newerXY , -.B \-anewer -and -.BR \-cnewer . -Similarly, the -.B \-type -predicate will always match against the type of the file -that a symbolic link points to rather than the link itself. Using -.B \-follow -causes the -.B \-lname and -.B \-ilname -predicates always to return false. - +Dereference symbolic links. Implies \-noleaf. .IP "\-help, \-\-help" Print a summary of the command-line usage of .B find and exit. - -.IP \-ignore_readdir_race -Normally, \fBfind\fR will emit an error message when it fails to stat a file. -If you give this option and a file is deleted between the time \fBfind\fR -reads the name of the file from the directory and the time it tries to stat -the file, no error message will be issued. This also applies to files -or directories whose names are given on the command line. This option takes -effect at the time the command line is read, which means that you cannot search -one part of the filesystem with this option on and part of it with this option -off (if you need to do that, you will need to issue two \fBfind\fR commands -instead, one with the option and one without it). - .IP "\-maxdepth \fIlevels\fR" Descend at most \fIlevels\fR (a non-negative integer) levels of -directories below the command line arguments. -.B \-maxdepth 0 - means only apply the tests and actions to the command line arguments. - +directories below the command line arguments. `\-maxdepth 0' means +only apply the tests and actions to the command line arguments. .IP "\-mindepth \fIlevels\fR" Do not apply any tests or actions at levels less than \fIlevels\fR (a -non-negative integer). -.B \-mindepth 1 -means process all files except the command line arguments. - +non-negative integer). `\-mindepth 1' means process all files except +the command line arguments. .IP \-mount Don't descend directories on other filesystems. An alternate name for -.BR \-xdev , -for compatibility with some other versions of +\-xdev, for compatibility with some other versions of .BR find . - -.IP \-noignore_readdir_race -Turns off the effect of -.BR \-ignore_readdir_race . - .IP "\-noleaf" Do not optimize by assuming that directories contain 2 fewer subdirectories than their hard link count. This option is needed when @@ -427,55 +79,11 @@ than the directory's link count, it knows that the rest of the entries in the directory are non-directories (`leaf' files in the directory tree). If only the files' names need to be examined, there is no need to stat them; this gives a significant increase in search speed. - -.IP "\-regextype \fItype\fR" -Changes the regular expression syntax understood by -.B \-regex -and -.B \-iregex -tests which occur later on the command line. Currently-implemented -types are emacs (this is the default), posix-awk, posix-basic, -posix-egrep and posix-extended. - .IP "\-version, \-\-version" Print the \fBfind\fR version number and exit. - -.IP "\-warn, \-nowarn" -Turn warning messages on or off. These warnings apply only to the -command line usage, not to any conditions that -.B find -might encounter when it searches directories. The default behaviour -corresponds to -.B \-warn -if standard input is a tty, and to -.B \-nowarn -otherwise. - .IP \-xdev Don't descend directories on other filesystems. - .SS TESTS -Some tests, for example -.B \-newerXY -and -.BR -samefile , -allow comparison between the file currently being examined and some -reference file specified on the command line. When these tests are -used, the interpretation of the reference file is determined by the -options -.BR \-H , -.B \-L -and -.B \-P -and any previous -.BR \-follow , -but the reference file is only examined once, at the time the command -line is parsed. If the reference file cannot be examined (for -example, the -.BR stat (2) -system call fails for it), an error message is issued, and -.B find -exits with a nonzero status. .P Numeric arguments can be specified as .IP \fI+n\fP @@ -487,383 +95,109 @@ for less than .IP \fIn\fP for exactly .IR n . -.P - .IP "\-amin \fIn\fR" File was last accessed \fIn\fR minutes ago. - .IP "\-anewer \fIfile\fR" -File was last accessed more recently than \fIfile\fR was modified. If -\fIfile\fR is a symbolic link and the -.B \-H -option or the -.B \-L -option is in effect, the access time of the file it points to is -always used. - +File was last accessed more recently than \fIfile\fR was modified. +\-anewer is affected by \-follow only if \-follow comes before +\-anewer on the command line. .IP "\-atime \fIn\fR" -File was last accessed \fIn\fR*24 hours ago. -When find figures out how many 24-hour periods ago the file -was last accessed, any fractional part is ignored, so to match -.B \-atime -.BR +1 , -a file has to have been accessed at least -.I two -days ago. - +File was last accessed \fIn\fR*24 hours ago. .IP "\-cmin \fIn\fR" File's status was last changed \fIn\fR minutes ago. - .IP "\-cnewer \fIfile\fR" -File's status was last changed more recently than \fIfile\fR was -modified. If \fIfile\fR is a symbolic link and the -.B \-H -option or the -.B \-L -option is in effect, the status-change time of the file it points -to is always used. - +File's status was last changed more recently than \fIfile\fR was modified. +\-cnewer is affected by \-follow only if \-follow comes before +\-cnewer on the command line. .IP "\-ctime \fIn\fR" File's status was last changed \fIn\fR*24 hours ago. -See the comments for -.B \-atime -to understand how rounding affects the interpretation of file status -change times. - .IP \-empty File is empty and is either a regular file or a directory. - -.IP \-executable -Matches files which are executable and directories which are -searchable (in a file name resolution sense). This takes into account -access control lists and other permissions artefacts which the -.B \-perm -test ignores. This test makes use of the -.BR access (2) -system call, and so can be fooled by NFS servers which do UID -mapping (or root-squashing), since many systems implement -.BR access (2) -in the client's kernel and so cannot make use of the UID mapping -information held on the server. Because this test is based only on -the result of the -.BR access (2) -system call, there is no guarantee that a file for which this test -succeeds can actually be executed. - .IP \-false Always false. - .IP "\-fstype \fItype\fR" File is on a filesystem of type \fItype\fR. The valid filesystem types vary among different versions of Unix; an incomplete list of filesystem types that are accepted on some version of Unix or another -is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use -.B \-printf +is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use \-printf with the %F directive to see the types of your filesystems. - .IP "\-gid \fIn\fR" File's numeric group ID is \fIn\fR. - .IP "\-group \fIgname\fR" File belongs to group \fIgname\fR (numeric group ID allowed). - .IP "\-ilname \fIpattern\fR" -Like -.BR \-lname , -but the match is case insensitive. -If the -.B \-L -option or the -.B \-follow -option is in effect, this test returns false unless the symbolic link -is broken. - +Like \-lname, but the match is case insensitive. .IP "\-iname \fIpattern\fR" -Like -.BR \-name , -but the match is case insensitive. For example, the +Like \-name, but the match is case insensitive. For example, the patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo', -`fOo', etc. In these patterns, unlike filename expansion by the -shell, an initial '.' can be matched by `*'. That is, -.B find \-name *bar -will match the file `.foobar'. Please note that you should quote -patterns as a matter of course, otherwise the shell will expand any -wildcard characters in them. - +`fOo', etc. .IP "\-inum \fIn\fR" -File has inode number \fIn\fR. It is normally easier to use the -.B \-samefile -test instead. - +File has inode number \fIn\fR. .IP "\-ipath \fIpattern\fR" -Behaves in the same way as -.BR \-iwholename . -This option is deprecated, so please do not use it. - +Like \-path, but the match is case insensitive. .IP "\-iregex \fIpattern\fR" -Like -.BR \-regex , -but the match is case insensitive. - -.IP "\-iwholename \fIpattern\fR" -Like -.BR \-wholename , -but the match is case insensitive. - +Like \-regex, but the match is case insensitive. .IP "\-links \fIn\fR" File has \fIn\fR links. - .IP "\-lname \fIpattern\fR" File is a symbolic link whose contents match shell pattern \fIpattern\fR. The metacharacters do not treat `/' or `.' specially. -If the -.B \-L -option or the -.B \-follow -option is in effect, this test returns false unless the symbolic link -is broken. - .IP "\-mmin \fIn\fR" File's data was last modified \fIn\fR minutes ago. - .IP "\-mtime \fIn\fR" File's data was last modified \fIn\fR*24 hours ago. -See the comments for -.B \-atime -to understand how rounding affects the interpretation of file -modification times. - .IP "\-name \fIpattern\fR" Base of file name (the path with the leading directories removed) matches shell pattern \fIpattern\fR. The metacharacters (`*', `?', -and `[]') match a `.' at the start of the base name (this is a change -in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a -directory and the files under it, use -.BR \-prune ; -see an example in the -description of -.BR \-path . -Braces are not recognised as being -special, despite the fact that some shells including Bash imbue braces -with a special meaning in shell patterns. The filename matching is -performed with the use of the -.BR fnmatch (3) -library function. Don't forget to enclose the pattern in quotes -in order to protect it from expansion by the shell. - +and `[]') do not match a `.' at the start of the base name. To ignore +a directory and the files under it, use \-prune; see an example in the +description of \-path. .IP "\-newer \fIfile\fR" -File was modified more recently than \fIfile\fR. If \fIfile\fR is a -symbolic link and the -.B \-H -option or the -.B \-L -option is in effect, the -modification time of the file it points to is always used. - -.IP "\-newerXY \fIreference\fR" -Compares the timestamp of the current file with \fIreference\fR. -The -.I reference -argument is normally the name of a file (and one of its timestamps is -used for the comparison) but it may also be a string describing an -absolute time. -.I X -and -.I Y -are placeholders for other letters, and these letters select which -time belonging to -how -.I reference -is used for the comparison. -.TS -ll -ll -ll -ll -llw(2i). -a The access time of the file \fIreference\fR -B The birth time of the file \fIreference\fR -c The inode status change time of \fIreference\fR -m The modification time of the file \fIreference\fR -t \fIreference\fR is interpreted directly as a time -.TE - -Some combinations are invalid; for example, it is invalid for -.I X -to be -.IR t . -Some combinations are not implemented on all systems; for example -.I B -is not supported on all systems. If an invalid or unsupported -combination of -.I XY -is specified, a fatal error results. Time specifications are -interpreted as for the argument to the -.B \-d -option of GNU -.BR date . -If you try to use the birth time of a reference file, and the birth -time cannot be determined, a fatal error message results. If you -specify a test which refers to the birth time of files being examined, -this test will fail for any files where the birth time is unknown. - -.IP \-nogroup -No group corresponds to file's numeric group ID. - +File was modified more recently than \fIfile\fR. +\-newer is affected by \-follow only if \-follow comes before +\-newer on the command line. .IP \-nouser No user corresponds to file's numeric user ID. - +.IP \-nogroup +No group corresponds to file's numeric group ID. .IP "\-path \fIpattern\fR" File name matches shell pattern \fIpattern\fR. The metacharacters do not treat `/' or `.' specially; so, for example, .br .in +1i -find . \-path "./sr*sc" +find . \-path './sr*sc' .br .in -1i -will print an entry for a directory called `./src/misc' (if one -exists). To ignore a whole directory tree, use -.B \-prune -rather than +will print an entry for a directory called './src/misc' (if one +exists). To ignore a whole directory tree, use \-prune rather than checking every file in the tree. For example, to skip the directory `src/emacs' and all files and directories under it, and print the names of the other files found, do something like this: .br .in +1i -find . \-path ./src/emacs \-prune \-o \-print -.br -.in -1i -Note that the pattern match test applies to the whole file name, -starting from one of the start points named on the command line. It -would only make sense to use an absolute path name here if the -relevant start point is also an absolute path. This means that this -command will never match anything: -.br -.in +1i -find bar \-path /foo/bar/myfile \-print +find . \-path './src/emacs' -prune -o -print .br .in -1i -The predicate -.B \-path -is also supported by HP-UX -.B find -and will be in a forthcoming version of the POSIX standard. - .IP "\-perm \fImode\fR" File's permission bits are exactly \fImode\fR (octal or symbolic). -Since an exact match is required, if you want to use this form for -symbolic modes, you may have to specify a rather complex mode string. -For example -.B \-perm g=w -will only match files which have mode 0020 -(that is, ones for which group write permission is the only permission -set). It is more likely that you will want to use the `/' or `-' -forms, for example -.BR "\-perm \-g=w" , -which matches any file with group write permission. See the -.B EXAMPLES -section for some illustrative examples. - +Symbolic modes use mode 0 as a point of departure. .IP "\-perm \-\fImode\fR" All of the permission bits \fImode\fR are set for the file. -Symbolic modes are accepted in this form, and this is usually the way -in which would want to use them. You must specify `u', `g' or `o' if -you use a symbolic mode. See the -.B EXAMPLES -section for some illustrative examples. - -.IP "\-perm /\fImode\fR" -Any of the permission bits \fImode\fR are set for the file. Symbolic -modes are accepted in this form. You must specify `u', `g' or `o' if -you use a symbolic mode. See the -.B EXAMPLES -section for some illustrative examples. If no permission bits in -.I mode -are set, this test currently matches no files. However, it will soon -be changed to match any file (the idea is to be more consistent with -the behaviour of -.B \-perm -.BR \-000 ). - .IP "\-perm +\fImode\fR" -Deprecated, old way of searching for files with any of the permission -bits in \fImode\fR set. You should use -.B \-perm \fI/mode\fR -instead. Trying to use the `+' syntax with symbolic modes will yield -surprising results. For example, `+u+x' is a valid symbolic mode -(equivalent to +u,+x, i.e. 0111) and will therefore not be evaluated -as -.B \-perm +\fImode\fR -but instead as the exact mode specifier -.B \-perm \fImode\fR -and so it matches files with exact permissions 0111 instead of files with any -execute bit set. If you found this paragraph confusing, you're not -alone - just use -.B \-perm /\fImode\fR. -This form of the -.B \-perm -test is deprecated because the POSIX specification requires the -interpretation of a leading `+' as being part of a symbolic mode, and -so we switched to using `/' instead. - -.IP \-readable -Matches files which are readable. This takes into account access -control lists and other permissions artefacts which the -.B \-perm -test ignores. This test makes use of the -.BR access (2) -system call, and so can be fooled by NFS servers which do UID -mapping (or root-squashing), since many systems implement -.BR access (2) -in the client's kernel and so cannot make use of the UID mapping -information held on the server. - +Any of the permission bits \fImode\fR are set for the file. .IP "\-regex \fIpattern\fR" File name matches regular expression \fIpattern\fR. This is a match on the whole path, not a search. For example, to match a file named `./fubar3', you can use the regular expression `.*bar.' or `.*b.*3', -but not `f.*r3'. The regular expressions understood by -.B find -are by default Emacs Regular Expressions, but this can be -changed with the -.B \-regextype -option. - -.IP "\-samefile \fIname\fR" -File refers to the same inode as \fIname\fR. When -.B \-L -is in effect, this can include symbolic links. - -.IP "\-size \fIn\fR[cwbkMG]" -File uses \fIn\fP units of space. The following suffixes -can be used: -.RS -.IP `b' -for 512-byte blocks (this is the default if no suffix is used) -.IP `c' -for bytes -.IP `w' -for two-byte words -.IP `k' -for Kilobytes (units of 1024 bytes) -.IP `M' -for Megabytes (units of 1048576 bytes) -.IP `G' -for Gigabytes (units of 1073741824 bytes) -.RE -.IP +but not `b.*r3'. +.IP "\-size \fIn\fR[bckw]" +File uses \fIn\fP units of space. The units are 512-byte blocks by +default or if `b' follows \fIn\fP, bytes if `c' follows \fIn\fP, +kilobytes if `k' follows \fIn\fP, or 2-byte words if `w' follows \fIn\fP. The size does not count indirect blocks, but it does count blocks in -sparse files that are not actually allocated. Bear in mind that the -`%k' and `%b' format specifiers of -.B \-printf -handle sparse files -differently. The `b' suffix always denotes 512-byte blocks and never -1 Kilobyte blocks, which is different to the behaviour of -.BR \-ls . - +sparse files that are not actually allocated. .IP \-true Always true. - .IP "\-type \fIc\fR" File is of type \fIc\fR: .RS @@ -878,102 +212,23 @@ named pipe (FIFO) .IP f regular file .IP l -symbolic link; this is never true if the -.B \-L -option or the -.B \-follow -option is in effect, unless the symbolic link is broken. If you want -to search for symbolic links when -.B \-L -is in effect, use -.BR \-xtype . +symbolic link .IP s socket -.IP D -door (Solaris) .RE .IP "\-uid \fIn\fR" File's numeric user ID is \fIn\fR. - .IP "\-used \fIn\fR" File was last accessed \fIn\fR days after its status was last changed. - .IP "\-user \fIuname\fR" File is owned by user \fIuname\fR (numeric user ID allowed). - -.IP "\-wholename \fIpattern\fR" -See \-path. This alternative is less portable than -.BR \-path . - -.IP "\-writable" -Matches files which are writable. This takes into account access -control lists and other permissions artefacts which the -.B \-perm -test ignores. This test makes use of the -.BR access (2) -system call, and so can be fooled by NFS servers which do UID -mapping (or root-squashing), since many systems implement -.BR access (2) -in the client's kernel and so cannot make use of the UID mapping -information held on the server. - .IP "\-xtype \fIc\fR" -The same as -.B \-type -unless the file is a symbolic link. For symbolic -links: if the -.B \-H -or -.B \-P -option was specified, true if the file is a -link to a file of type \fIc\fR; if the -.B \-L -option has been given, true -if \fIc\fR is `l'. In other words, for symbolic links, -.B \-xtype -checks the type of the file that -.B \-type -does not check. - +The same as \-type unless the file is a symbolic link. For symbolic +links: if \-follow has not been given, true if the file is a link to a +file of type \fIc\fR; if \-follow has been given, true if \fIc\fR is +`l'. In other words, for symbolic links, \-xtype checks the type of +the file that \-type does not check. .SS ACTIONS -.IP "\-delete\fR" -Delete files; true if removal succeeded. If the removal failed, an -error message is issued. -If -.B \-delete -fails, -.BR find 's -exit status will be nonzero -(when it eventually exits). -Use of -.B \-delete -automatically turns on the -.B \-depth -option. - -.BR Warnings : -Don't forget that the find command line is -evaluated as an expression, so putting -.B \-delete -first will make -.B find -try to delete everything below the starting points you specified. -When testing a -.B find -command line that you later intend to use with -.BR \-delete , -you should explicitly specify -.B \-depth -in order to avoid later surprises. Because -.B \-delete -implies -.BR \-depth , -you cannot usefully use -.B \-prune -and -.B \-delete -together. - .IP "\-exec \fIcommand\fR ;" Execute \fIcommand\fR; true if 0 status is returned. All following arguments to @@ -985,168 +240,36 @@ command, not just in arguments where it is alone, as in some versions of .BR find . Both of these constructions might need to be escaped (with a `\e') or -quoted to protect them from expansion by the shell. See the -.B EXAMPLES -section for examples of the use of the -.B \-exec -option. The specified -command is run once for each matched file. -The command is executed in the starting directory. There are -unavoidable security problems surrounding use of the -.B \-exec -action; -you should use the -.B \-execdir -option instead. - -.IP "\-exec \fIcommand\fR {} +" -This variant of the -.B \-exec -action runs the specified command on the -selected files, but the command line is built by appending each -selected file name at the end; the total number of invocations of the -command will be much less than the number of matched files. The -command line is built in much the same way that -.B xargs -builds its command lines. Only one instance of `{}' is allowed within -the command. The command is executed in the starting directory. - -.IP "\-execdir \fIcommand\fR ;" -.IP "\-execdir \fIcommand\fR {} +" -Like -.BR \-exec , -but the specified command is run from the subdirectory -containing the matched file, which is not normally the directory in -which you started -.BR find . -This a much more secure method for invoking commands, as it avoids -race conditions during resolution of the paths to the matched files. -As with the -.B \-exec -action, the `+' form of -.B \-execdir -will build a -command line to process more than one matched file, but any given -invocation of -.I command -will only list files that exist in the same subdirectory. If you use -this option, you must ensure that your -.B $PATH -environment variable does not reference `.'; -otherwise, an attacker can run any commands they like by leaving an -appropriately-named file in a directory in which you will run -.BR \-execdir . -The same applies to having entries in -.B $PATH -which are empty or which are not absolute directory names. - +quoted to protect them from expansion by the shell. The command is +executed in the starting directory. .IP "\-fls \fIfile\fR" -True; like -.B \-ls -but write to \fIfile\fR like -.BR \-fprint . -The output file is always created, even if the predicate is never -matched. -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - +True; like \-ls but write to \fIfile\fR like \-fprint. .IP "\-fprint \fIfile\fR" True; print the full file name into file \fIfile\fR. If \fIfile\fR does not exist when \fBfind\fR is run, it is created; if it does exist, it is truncated. The file names ``/dev/stdout'' and ``/dev/stderr'' are handled specially; they refer to the standard output and standard error output, respectively. -The output file is always created, even if the predicate is never matched. -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - .IP "\-fprint0 \fIfile\fR" -True; like -.B \-print0 -but write to \fIfile\fR like -.BR \-fprint . -The output file is always created, even if the predicate is never matched. -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - +True; like \-print0 but write to \fIfile\fR like \-fprint. .IP "\-fprintf \fIfile\fR \fIformat\fR" -True; like -.B \-printf -but write to \fIfile\fR like -.BR \-fprint . -The output file is always created, even if the predicate is never matched. -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - -.IP \-ls -True; list current file in -.B ls \-dils -format on standard output. -The block counts are of 1K blocks, unless the environment variable -POSIXLY_CORRECT is set, in which case 512-byte blocks are used. -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - +True; like \-printf but write to \fIfile\fR like \-fprint. .IP "\-ok \fIcommand\fR ;" -Like -.B \-exec -but ask the user first (on the standard input); if the +Like \-exec but ask the user first (on the standard input); if the response does not start with `y' or `Y', do not run the command, and -return false. If the command is run, its standard input is redirected -from -.BR /dev/null . - -.IP "\-okdir \fIcommand\fR ;" -Like -.B \-execdir -but ask the user first (on the standard input); if the -response does not start with `y' or `Y', do not run the command, and -return false. If the command is run, its standard input is redirected -from -.BR /dev/null . - +return false. .IP \-print -True; print the full file name on the standard output, followed by a -newline. If you are piping the output of -.B find -into another program and there is the faintest possibility that the files -which you are searching for might contain a newline, then you should -seriously consider using the -.B \-print0 -option instead of -.BR \-print . -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - +True; print the full file name on the standard output, followed by a newline. .IP \-print0 True; print the full file name on the standard output, followed by a -null character (instead of the newline character that -.B \-print -uses). -This allows file names that contain newlines or other types of white -space to be correctly interpreted by programs that process the -\fBfind\fR output. This option corresponds to the -.B \-0 -option of -.BR xargs . - +null character. This allows file names that contain newlines to be +correctly interpreted by programs that process the \fBfind\fR output. .IP "\-printf \fIformat\fR" True; print \fIformat\fR on the standard output, interpreting `\e' escapes and `%' directives. Field widths and precisions can be -specified as with the `printf' C function. Please note that many of -the fields are printed as %s rather than %d, and this may mean that -flags don't work as you might expect. This also means that the `\-' -flag does work (it forces fields to be left-aligned). Unlike -.BR \-print , -.B \-printf -does not add a newline at the end of the string. The escapes -and directives are: +specified as with the `printf' C function. Unlike \-print, \-printf +does not add a newline at the end of the string. The escapes and +directives are: .RS .IP \ea Alarm bell. @@ -1164,12 +287,8 @@ Carriage return. Horizontal tab. .IP \ev Vertical tab. -.IP \e\0 -ASCII NUL. .IP \e\e A literal backslash (`\e'). -.IP \eNNN -The character whose ASCII code is NNN (octal). .PP A `\e' character followed by any other character is treated as an ordinary character, so they both are printed. @@ -1182,10 +301,10 @@ File's last access time in the format specified by \fIk\fR, which is either `@' or a directive for the C `strftime' function. The possible values for \fIk\fR are listed below; some of them might not be available on all systems, due to differences in `strftime' between -systems. +systems. .RS .IP @ -seconds since Jan. 1, 1970, 00:00 GMT, with fractional part. +seconds since Jan. 1, 1970, 00:00 GMT. .PP Time fields: .IP H @@ -1203,14 +322,9 @@ locale's AM or PM .IP r time, 12-hour (hh:mm:ss [AP]M) .IP S -Second (00.00 .. 61.00). There is a fractional part. +second (00..61) .IP T time, 24-hour (hh:mm:ss) -.IP + -Date and time, separated by `+', for example -`2004\-04\-28+22:22:05.0'. This is a GNU extension. The time is -given in the current timezone (which may be affected by setting the TZ -environment variable). The seconds field includes a fractional part. .IP X locale's time representation (H:M:S) .IP Z @@ -1226,11 +340,7 @@ locale's abbreviated month name (Jan..Dec) .IP B locale's full month name, variable length (January..December) .IP c -locale's date and time (Sat Nov 04 12:02:33 EST 1989). The format is -the same as for -.BR ctime (3) -and so to preserve compatibility with that format, there is no fractional part -in the seconds field. +locale's date and time (Sat Nov 04 12:02:33 EST 1989) .IP d day of month (01..31) .IP D @@ -1255,9 +365,7 @@ last two digits of year (00..99) year (1970...) .RE .IP %b -The amount of disk space used for this file in 512-byte blocks. Since disk -space is allocated in multiples of the filesystem block size this is usually -greater than %s/512, but it can also be smaller if the file is a sparse file. +File's size in 512-byte blocks (rounded up). .IP %c File's last status change time in the format returned by the C `ctime' function. @@ -1267,9 +375,6 @@ which is the same as for %A. .IP %d File's depth in the directory tree; 0 means the file is a command line argument. -.IP %D -The device number on which the file exists (the st_dev field of struct -stat), in decimal. .IP %f File's name with any leading directories removed (only the last element). .IP %F @@ -1281,31 +386,16 @@ File's group name, or numeric group ID if the group has no name. File's numeric group ID. .IP %h Leading directories of file's name (all but the last element). -If the file name contains no slashes (since it is in the current -directory) the %h specifier expands to ".". .IP %H Command line argument under which file was found. .IP %i File's inode number (in decimal). .IP %k -The amount of disk space used for this file in 1K blocks. Since disk space is -allocated in multiples of the filesystem block size this is usually greater -than %s/1024, but it can also be smaller if the file is a sparse file. +File's size in 1K blocks (rounded up). .IP %l Object of symbolic link (empty string if file is not a symbolic link). .IP %m -File's permission bits (in octal). This option uses the `traditional' -numbers which most Unix implementations use, but if your particular -implementation uses an unusual ordering of octal permissions bits, you -will see a difference between the actual value of the file's mode and -the output of %m. Normally you will want to have a leading -zero on this number, and to do this, you should use the -.B # -flag (as in, for example, `%#m'). -.IP %M -File's permissions (in symbolic form, as for -.BR ls ). -This directive is supported in findutils 4.2.5 and later. +File's permission bits (in octal). .IP %n Number of hard links to file. .IP %p @@ -1315,15 +405,6 @@ File's name with the name of the command line argument under which it was found removed. .IP %s File's size in bytes. -.IP %S -File's sparseness. This is calculated as (BLOCKSIZE*st_blocks / -st_size). The exact value you will get for an ordinary file of a -certain length is system-dependent. However, normally sparse files -will have values less than 1.0, and files which use indirect blocks -may have a value which is greater than 1.0. The value used for -BLOCKSIZE is system-dependent, but is usually 512 bytes. If the file -size is zero, the value printed is undefined. On systems which lack -support for st_blocks, a file's sparseness is assumed to be 1.0. .IP %t File's last modification time in the format returned by the C `ctime' function. @@ -1334,694 +415,41 @@ which is the same as for %A. File's user name, or numeric user ID if the user has no name. .IP %U File's numeric user ID. -.IP %y -File's type (like in -.BR "ls \-l" ), -U=unknown type (shouldn't happen) -.IP %Y -File's type (like %y), plus follow symlinks: L=loop, N=nonexistent -.PP -A `%' character followed by any other character is discarded, but the -other character is printed (don't rely on this, as further format -characters may be introduced). A `%' at the end of the format -argument causes undefined behaviour since there is no following -character. In some locales, it may hide your door keys, while in -others it may remove the final page from the novel you are reading. - -The %m and %d directives support the -.B # -, -.B 0 -and -.B + -flags, but the other directives do not, even if they -print numbers. Numeric directives that do not support these flags -include -.BR G , -.BR U , -.BR b , -.BR D , -.B k -and -.BR n . -The `\-' format flag is supported and changes the alignment of a field -from right-justified (which is the default) to left-justified. .PP -See the -.B UNUSUAL FILENAMES -section for information about how unusual characters in filenames are handled. - - +A `%' character followed by any other character is discarded (but the +other character is printed). .RE .IP \-prune -True; if the file is a directory, do not descend into it. If -.B \-depth -is given, false; no effect. Because -.B \-delete -implies -.BR \-depth , -you cannot usefully use -.B \-prune -and -.B \-delete together. - -.IP "\-quit" -Exit immediately. No child processes will be left running, but no more -paths specified on the command line will be processed. For example, -.B find /tmp/foo /tmp/bar \-print \-quit -will print only -.BR /tmp/foo . -Any command lines which have been built up with -.B \-execdir ... {} + -will be invoked before -.B find -exits. The exit status may or may not be zero, depending on whether -an error has already occurred. - -.SS UNUSUAL FILENAMES -Many of the actions of -.B find -result in the printing of data which is under the control of other -users. This includes file names, sizes, modification times and so -forth. File names are a potential problem since they can contain any -character except `\e0' and `/'. Unusual characters in file names can -do unexpected and often undesirable things to your terminal (for -example, changing the settings of your function keys on some -terminals). Unusual characters are handled differently by various -actions, as described below. - -.IP "\-print0, \-fprint0\" -Always print the exact filename, unchanged, even if the output is -going to a terminal. - -.IP "\-ls, \-fls" -Unusual characters are always escaped. White space, backslash, and -double quote characters are printed using C-style escaping (for -example `\ef', `\e"'). Other unusual characters are printed using an -octal escape. Other printable characters (for -.B \-ls -and -.B \-fls -these are the characters between octal 041 and 0176) are printed as-is. - -.IP "\-printf, \-fprintf" -If the output is not going to a terminal, it is printed as-is. -Otherwise, the result depends on which directive is in use. The -directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are -not under control of files' owners, and so are printed as-is. The -directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have -values which are under the control of files' owners but which cannot -be used to send arbitrary data to the terminal, and so these are -printed as-is. The directives %f, %h, %l, %p and %P are quoted. This -quoting is performed in the same way as for GNU -.BR ls . -This is not the same quoting mechanism as the one used for -.B \-ls -and -.BR \-fls . -If you are able to decide what format to use for the output of -.B find -then it is normally better to use `\e0' as a terminator -than to use newline, as file names can contain white space and newline -characters. - -.IP "\-print, \-fprint" -Quoting is handled in the same way as for -.B \-printf -and -.BR \-fprintf . -If you are using -.B find -in a script or in a situation where the matched files might have -arbitrary names, you should consider using -.B \-print0 -instead of -.BR \-print . -.P -The -.B \-ok -and -.B \-okdir -actions print the current filename as-is. This may change in a future release. +If \-depth is not given, true; do not descend the current directory. +.br +If \-depth is given, false; no effect. +.IP \-ls +True; list current file in `ls \-dils' format on standard output. +The block counts are of 1K blocks, unless the environment variable +POSIXLY_CORRECT is set, in which case 512-byte blocks are used. .SS OPERATORS .P Listed in order of decreasing precedence: - .IP "( \fIexpr\fR )" -Force precedence. Since parentheses are special to the shell, you -will normally need to quote them. Many of the examples in this manual -page use backslashes for this purpose: `\e(...\e)' instead of `(...)'. - +Force precedence. .IP "! \fIexpr\fR" -True if \fIexpr\fR is false. This character will also usually need -protection from interpretation by the shell. - +True if \fIexpr\fR is false. .IP "\-not \fIexpr\fR" -Same as ! \fIexpr\fR, but not POSIX compliant. - +Same as ! \fIexpr\fR. .IP "\fIexpr1 expr2\fR" -Two expressions in a row are taken to be joined with an -implied "and"; \fIexpr2\fR is not evaluated if \fIexpr1\fR is false. - +And (implied); \fIexpr2\fR is not evaluated if \fIexpr1\fR is false. .IP "\fIexpr1\fR \-a \fIexpr2\fR" Same as \fIexpr1 expr2\fR. - .IP "\fIexpr1\fR \-and \fIexpr2\fR" -Same as \fIexpr1 expr2\fR, but not POSIX compliant. - +Same as \fIexpr1 expr2\fR. .IP "\fIexpr1\fR \-o \fIexpr2\fR" Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true. - .IP "\fIexpr1\fR \-or \fIexpr2\fR" -Same as \fIexpr1\fR -.B \-o -\fIexpr2\fR, but not POSIX compliant. - +Same as \fIexpr1\fR \-o \fIexpr2\fR. .IP "\fIexpr1\fR , \fIexpr2\fR" -List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. The -value of \fIexpr1\fR is discarded; the value of the list is the value -of \fIexpr2\fR. The comma operator can be useful for searching for -several different types of thing, but traversing the filesystem -hierarchy only once. The -.B \-fprintf -action can be used to list the various matched items into several -different output files. - - -.SH "STANDARDS CONFORMANCE" -For closest compliance to the POSIX standard, you should set the -POSIXLY_CORRECT environment variable. The following options are -specified in the POSIX standard (IEEE Std 1003.1, 2003 Edition): - -.IP \fB\-H\fR -This option is supported. - -.IP \fB\-L\fR -This option is supported. - -.IP \fB\-name\fR -This option is supported, but POSIX conformance depends on the -POSIX conformance of the system's -.BR fnmatch (3) -library function. As of findutils-4.2.2, shell metacharacters -(`*', `?' or `[]' for example) will match a leading `.', because -IEEE PASC interpretation 126 requires this. This is a change from -previous versions of findutils. - -.IP \fB\-type\fR -Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'. -GNU find also supports `D', representing a Door, where the OS provides these. - -.IP \fB\-ok\fR -Supported. Interpretation of the response is not locale-dependent -(see ENVIRONMENT VARIABLES). - -.IP \fB\-newer\fR -Supported. If the file specified is a symbolic link, it is always -dereferenced. This is a change from previous behaviour, which used to -take the relevant time from the symbolic link; see the HISTORY section -below. - -.IP \fB\-perm\fR -Supported. If the POSIXLY_CORRECT environment variable is not set, -some mode arguments (for example +a+x) which are not valid in POSIX -are supported for backward-compatibility. - -.IP "Other predicates" -The predicates -.BR \-atime , -.BR \-ctime , -.BR \-depth , -.BR \-group , -.BR \-links , -.BR \-mtime , -.BR \-nogroup , -.BR \-nouser , -.BR \-print , -.BR \-prune , -.BR \-size , -.BR \-user -and -.B \-xdev -are all supported. - -.P -The POSIX standard specifies parentheses `(', `)', negation `!' and the -`and' and `or' operators ( -.BR \-a , -.BR \-o ). -.P -All other options, predicates, expressions and so forth are extensions -beyond the POSIX standard. Many of these extensions are not unique to -GNU find, however. -.P -The POSIX standard requires that -.B find -detects loops: -.IP -The -.B find -utility shall detect infinite loops; that is, entering a -previously visited directory that is an ancestor of the last file -encountered. When it detects an infinite loop, find shall write a -diagnostic message to standard error and shall either recover its -position in the hierarchy or terminate. -.P -GNU -.B find -complies with these requirements. The link count of -directories which contain entries which are hard links to an ancestor -will often be lower than they otherwise should be. This can mean that -GNU find will sometimes optimise away the visiting of a subdirectory -which is actually a link to an ancestor. Since -.B find -does not actually enter such a subdirectory, it is allowed to avoid -emitting a diagnostic message. Although this behaviour may be -somewhat confusing, it is unlikely that anybody actually depends on -this behaviour. If the leaf optimisation has been turned off with -.BR \-noleaf , -the directory entry will always be examined and the diagnostic message -will be issued where it is appropriate. Symbolic links cannot be used -to create filesystem cycles as such, but if the -.B \-L -option or the -.B \-follow -option is in use, a diagnostic message is issued when -.B find -encounters a loop of symbolic links. As with loops containing hard -links, the leaf optimisation will often mean that -.B find -knows that it doesn't need to call -.I stat() -or -.I chdir() -on the symbolic link, so this diagnostic is frequently not necessary. -.P -The -.B \-d -option is supported for compatibility with various BSD systems, -but you should use the POSIX-compliant option -.B \-depth -instead. -.P -The POSIXLY_CORRECT environment variable does not affect the behaviour -of the -.B \-regex -or -.B \-iregex -tests because those tests aren't specified in the POSIX standard. -.SH "ENVIRONMENT VARIABLES" - -.IP LANG -Provides a default value for the internationalization variables that -are unset or null. - -.IP LC_ALL -If set to a non-empty string value, override the values of all the -other internationalization variables. - -.IP LC_COLLATE -The POSIX standard specifies that this variable affects the pattern -matching to be used for the -.B \-name -option. GNU find uses the -.BR fnmatch (3) -library function, and so support for `LC_COLLATE' depends on the -system library. - -.IP -POSIX also specifies that the `LC_COLLATE' environment -variable affects the interpretation of the user's response to the -query issued by -.BR \-ok' , -but this is not the case for GNU find. - -.IP LC_CTYPE -This variable affects the treatment of character classes used with -the -.B \-name -test, if the system's -.BR fnmatch (3) -library function supports this. It has no effect on the behaviour -of the -.B \-ok -expression. - -.IP LC_MESSAGES -Determines the locale to be used for internationalised messages. - -.IP NLSPATH -Determines the location of the internationalisation message catalogues. - -.IP PATH -Affects the directories which are searched to find the executables -invoked by -.BR \-exec , -.BR \-execdir , -.B \-ok -and -.BR \-okdir . - -.IP POSIXLY_CORRECT -Determines the block size used by -.B \-ls -and -.BR \-fls . -If -.B POSIXLY_CORRECT -is set, blocks are units of 512 bytes. Otherwise -they are units of 1024 bytes. -.IP -Setting this variable also turns off -warning messages (that is, implies -.BR \-nowarn ) -by default, because POSIX requires that apart from -the output for -.BR \-ok , -all messages printed on stderr are diagnositcs and must result in a -non-zero exit status. -.IP -When POSIXLY_CORRECT is not set, -.B \-perm -+zzz -is treated just like -.B \-perm -/zzz -if -+zzz is not a valid symbolic mode. When POSIXLY_CORRECT is set, such -constructs are treated as an error. - -.IP TZ -Affects the time zone used for some of the time-related format -directives of -.B \-printf -and -.BR \-fprintf . -.SH "EXAMPLES" -.nf -.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f - -.fi -Find files named -.B core -in or below the directory -.B /tmp -and delete them. Note that this will work incorrectly if there are -any filenames containing newlines, single or double quotes, or spaces. -.P -.B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f - -.fi -Find files named -.B core -in or below the directory -.B /tmp -and delete them, processing filenames in such a way that file or -directory names containing single or double quotes, spaces or newlines -are correctly handled. The -.B \-name -test comes before the -.B \-type -test in order to avoid having to call -.B stat(2) -on every file. - -.P -.nf -.B find . \-type f \-exec file \(aq{}\(aq \e\; - -.fi -Runs `file' on every file in or below the current directory. Notice -that the braces are enclosed in single quote marks to protect them -from interpretation as shell script punctuation. The semicolon is -similarly protected by the use of a backslash, though single quotes -could have been used in that case also. - -.P -.nf -.B find / \e -.B \e( \-perm \-4000 \-fprintf /root/suid.txt "%#m %u %p\en" \e) , \e -.B \e( \-size +100M \-fprintf /root/big.txt "%\-10s %p\en" \e) - -.fi -Traverse the filesystem just once, listing setuid files and -directories into -.B /root/suid.txt -and large files into -.BR /root/big.txt . - -.P -.nf -.B find $HOME \-mtime 0 - -.fi -Search for files in your home directory which have been modified in -the last twenty-four hours. This command works this way because the -time since each file was last modified is divided by 24 hours and any -remainder is discarded. That means that to match -.B \-mtime -.BR 0 , -a file will have to have a modification in the past which is less than -24 hours ago. - -.P -.nf -.B find /sbin /usr/sbin -executable \e! -readable \-print - -.fi -Search for files which are executable but not readable. - -.P -.nf -.B find . \-perm 664 - -.fi -Search for files which have read and write permission for their owner, -and group, but which other users can read but not write to. Files -which meet these criteria but have other permissions bits set (for -example if someone can execute the file) will not be matched. - -.P -.nf -.B find . \-perm \-664 - -.fi -Search for files which have read and write permission for their owner -and group, and which other users can read, without regard to the -presence of any extra permission bits (for example the executable -bit). This will match a file which has mode 0777, for example. - -.P -.nf -.B find . \-perm /222 - -.fi -Search for files which are writable by somebody (their owner, or -their group, or anybody else). - -.P -.nf -.B find . \-perm /220 -.B find . \-perm /u+w,g+w -.B find . \-perm /u=w,g=w - -.fi -All three of these commands do the same thing, but the first one uses -the octal representation of the file mode, and the other two use the -symbolic form. These commands all search for files which are -writable by either their owner or their group. The files don't have -to be writable by both the owner and group to be matched; either will -do. - -.P -.nf -.B find . \-perm \-220 -.B find . \-perm \-g+w,u+w - -.fi -Both these commands do the same thing; search for files which are -writable by both their owner and their group. - -.P -.nf -.B find . \-perm \-444 \-perm /222 ! \-perm /111 -.B find . \-perm \-a+r \-perm /a+w ! \-perm /a+x - -.fi -These two commands both search for files that are readable for -everybody ( -.B \-perm \-444 -or -.BR "\-perm \-a+r" ), -have at least one write bit -set ( -.B \-perm /222 -or -.BR "\-perm /a+w" ) -but are not executable for anybody ( -.B ! \-perm /111 -and -.B ! \-perm /a+x -respectively). - -.P -.nf -.B cd /source-dir -.B find . \-name .snapshot \-prune \-o \e( \e! \-name "*~" \-print0 \e)| -.B cpio \-pmd0 /dest-dir - -.fi -This command copies the contents of -.B /source-dir -to -.BR /dest-dir , -but omits files and directories named -.B .snapshot -(and anything in them). It also omits files or directories whose name -ends in -.BR ~ , -but not their contents. The construct -.B \-prune \-o \e( ... \-print0 \e) -is quite common. The idea here is that the expression before -.B \-prune -matches things which are to be pruned. However, the -.B \-prune -action itself returns true, so the following -.B \-o -ensures that the right hand side is evaluated only for those -directories which didn't get pruned (the contents of the pruned -directories are not even visited, so their contents are irrelevant). -The expression on the right hand side of the -.B \-o -is in parentheses only for clarity. It emphasises that the -.B \-print0 -action takes place only for things that didn't have -.B \-prune -applied to them. Because the default `and' condition between tests -binds more tightly than -.BR \-o , -this is the default anyway, but the parentheses help to show -what is going on. - -.SH EXIT STATUS -.PP -.B find -exits with status 0 if all files are processed successfully, greater -than 0 if errors occur. This is deliberately a very broad -description, but if the return value is non-zero, you should not rely -on the correctness of the results of -.BR find . - +List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. +The value of \fIexpr1\fR is discarded; the value of the list is the +value of \fIexpr2\fR. .SH "SEE ALSO" -\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1), -\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2), -\fBlstat\fP(2), \fBls\fP(1), \fBprintf\fP(3), \fBstrftime\fP(3), -\fBctime\fP(3), \fBFinding Files\fP (on-line in Info, or printed). -.SH "HISTORY" -As of findutils-4.2.2, shell metacharacters (`*', `?' or `[]' for -example) used in filename patterns will match a leading `.', because -IEEE POSIX interpretation 126 requires this. -.P -The syntax -\.B \-perm +MODE -was deprecated in findutils-4.2.21, in favour of -\.B \-perm -.BR /MODE . -As of findutils-4.3.3, -.B \-perm /000 -now matches all files instead of none. -.P -Nanosecond-resolution -timestamps were implemented in findutils-4.3.3. -.P -As of findutils-4.3.11, the -.B \-delete -action sets -.BR find 's -exit status to a nonzero value when it fails. -However, -.B find -will not exit immediately. Previously, -.BR find 's -exit status was unaffected by the failure of -.BR \-delete . -.TS -l l l . -Feature Added in Also occurs in -\-newerXY 4.3.3 BSD -\-D 4.3.1 -\-O 4.3.1 -\-readable 4.3.0 -\-writable 4.3.0 -\-executable 4.3.0 -\-regextype 4.2.24 -\-exec ... + 4.2.12 POSIX -\-execdir 4.2.12 BSD -\-okdir 4.2.12 -\-samefile 4.2.11 -\-H 4.2.5 POSIX -\-L 4.2.5 POSIX -\-P 4.2.5 BSD -\-delete 4.2.3 -\-quit 4.2.3 -\-d 4.2.3 BSD -\-wholename 4.2.0 -\-iwholename 4.2.0 -\-ignore_readdir_race 4.2.0 -\-fls 4.0 -\-ilname 3.8 -\-iname 3.8 -\-ipath 3.8 -\-iregex 3.8 -.TE -.SH "NON-BUGS" -.nf -.B $ find . \-name *.c \-print -find: paths must precede expression -Usage: find [\-H] [\-L] [\-P] [\-Olevel] [\-D help|tree|search|stat|rates|opt|exec] [path...] [expression] -.fi -.P -This happens because -.I *.c -has been expanded by the shell -resulting in -.B find -actually receiving a command line like this: -.nf - -.B find . \-name bigram.c code.c frcode.c locate.c \-print - -.fi -That command is of course not going to work. Instead of doing things -this way, you should enclose the pattern in quotes or escape the wildcard: -.nf -.B $ find . \-name \e*.c \-print -.fi - -.SH "BUGS" -.P -There are security problems inherent in the behaviour that the POSIX -standard specifies for -.BR find , -which therefore cannot be fixed. For example, the -.B \-exec -action is -inherently insecure, and -.B \-execdir -should be used instead. -Please see \fBFinding Files\fP for more information. -.P -The environment variable -.B LC_COLLATE -has no effect on the -.B \-ok -action. -.P -The best way to report a bug is to use the form at -http://savannah.gnu.org/bugs/?group=findutils. -The reason for this is that you will then be able to track progress in -fixing the problem. Other comments about \fBfind\fP(1) and about -the findutils package in general can be sent to the -.I bug\-findutils -mailing list. To join the list, send email to -.IR bug\-findutils\-request@gnu.org . +\fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBupdatedb\fP(1L), \fBxargs\fP(1L) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/find/find.c b/find/find.c index 66ee07e7..361f2c41 100644 --- a/find/find.c +++ b/find/find.c @@ -1,1078 +1,304 @@ /* find -- search for files in a directory hierarchy - Copyright (C) 1990, 91, 92, 93, 94, 2000, - 2003, 2004, 2005, 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, 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. */ + /* GNU find was written by Eric Decker <cire@cisco.com>, - with enhancements by David MacKenzie <djm@gnu.org>, + with enhancements by David MacKenzie <djm@gnu.ai.mit.edu>, Jay Plett <jay@silence.princeton.nj.us>, and Tim Wood <axolotl!tim@toad.com>. The idea for -print0 and xargs -0 came from - Dan Bernstein <brnstnd@kramden.acf.nyu.edu>. - Improvements have been made by James Youngman <jay@gnu.org>. -*/ - + Dan Bernstein <brnstnd@kramden.acf.nyu.edu>. */ #include <config.h> -#include "defs.h" - -#define USE_SAFE_CHDIR 1 -#undef STAT_MOUNTPOINTS - - -#include <errno.h> -#include <assert.h> - +#include <sys/types.h> #include <sys/stat.h> +#include <stdio.h> +#ifdef HAVE_FCNTL_H #include <fcntl.h> -#include <openat.h> - -#include "xalloc.h" -#include "human.h" -#include "canonicalize.h" -#include <modetype.h> - -#include "closein.h" -#include "savedirinfo.h" -#include "buildcmd.h" -#include "dirname.h" -#include "quote.h" -#include "quotearg.h" -#include "xgetcwd.h" -#include "error.h" - -#ifdef HAVE_LOCALE_H -#include <locale.h> -#endif - -#if ENABLE_NLS -# include <libintl.h> -# define _(Text) gettext (Text) -#else -# define _(Text) Text -#define textdomain(Domain) -#define bindtextdomain(Package, Directory) -#define ngettext(singular,plural,n) ((1==n) ? singular : plural) -#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 +#include <sys/file.h> #endif +#include "defs.h" +#include "modetype.h" -#ifdef STAT_MOUNTPOINTS -static void init_mounted_dev_list(int mandatory); +#ifndef S_IFLNK +#define lstat stat #endif -static void process_top_path PARAMS((char *pathname, mode_t mode)); -static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent, mode_t type)); -static void process_dir PARAMS((char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)); +int lstat (); +int stat (); +#define apply_predicate(pathname, stat_buf_ptr, node) \ + (*(node)->pred_func)((pathname), (stat_buf_ptr), (node)) +static void process_top_path P_((char *pathname)); +static int process_path P_((char *pathname, char *name, boolean leaf, char *parent)); +static void process_dir P_((char *pathname, char *name, int pathlen, struct stat *statp, char *parent)); +static boolean no_side_effects P_((struct predicate *pred)); /* Name this program was run with. */ -extern char *program_name; - -/* A file descriptor open to the initial working directory. - Doing it this way allows us to work when the i.w.d. has - unreadable parents. */ -extern int starting_desc; - -/* The stat buffer of the initial working directory. */ -static struct stat starting_stat_buf; +char *program_name; -enum ChdirSymlinkHandling - { - SymlinkHandleDefault, /* Normally the right choice */ - SymlinkFollowOk /* see comment in process_top_path() */ - }; +/* All predicates for each path to process. */ +struct predicate *predicates; +/* The last predicate allocated. */ +struct predicate *last_pred; -enum TraversalDirection - { - TraversingUp, - TraversingDown - }; +/* The root of the evaluation tree. */ +static struct predicate *eval_tree; -enum WdSanityCheckFatality - { - FATAL_IF_SANITY_CHECK_FAILS, - RETRY_IF_SANITY_CHECK_FAILS, - NON_FATAL_IF_SANITY_CHECK_FAILS - }; +/* If true, process directory before contents. True unless -depth given. */ +boolean do_dir_first; +/* If >=0, don't descend more than this many levels of subdirectories. */ +int maxdepth; -int get_current_dirfd(void) -{ - return AT_FDCWD; -} - - -int -main (int argc, char **argv) -{ - int i; - int end_of_leading_options = 0; /* First arg after any -H/-L etc. */ - struct predicate *eval_tree; +/* If >=0, don't process files above this level. */ +int mindepth; - program_name = argv[0]; - state.exit_status = 0; +/* Current depth; 0 means current path is a command line arg. */ +int curdepth; - /* Set the option defaults before we do the locale - * initialisation as check_nofollow() needs to be executed in the - * POSIX locale. - */ - set_option_defaults(&options); +/* Seconds between 00:00 1/1/70 and either one day before now + (the default), or the start of today (if -daystart is given). */ +time_t cur_day_start; -#ifdef HAVE_SETLOCALE - setlocale (LC_ALL, ""); -#endif - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); - atexit (close_stdin); +/* If true, cur_day_start has been adjusted to the start of the day. */ +boolean full_days; - /* Check for -P, -H or -L options. */ - end_of_leading_options = process_leading_options(argc, argv); +/* If true, do not assume that files in directories with nlink == 2 + are non-directories. */ +boolean no_leaf_check; - if (options.debug_options & DebugStat) - options.xstat = debug_stat; +/* If true, don't cross filesystem boundaries. */ +boolean stay_on_filesystem; -#ifdef DEBUG - fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start)); -#endif /* DEBUG */ +/* If true, don't descend past current directory. + Can be set by -prune, -maxdepth, and -xdev/-mount. */ +boolean stop_at_current_level; - /* state.cwd_dir_fd has to be initialised before we call build_expression_tree() - * because command-line parsing may lead us to stat some files. - */ - state.cwd_dir_fd = AT_FDCWD; - - /* We are now processing the part of the "find" command line - * after the -H/-L options (if any). - */ - eval_tree = build_expression_tree(argc, argv, end_of_leading_options); - - - /* safely_chdir() needs to check that it has ended up in the right place. - * To avoid bailing out when something gets automounted, it checks if - * the target directory appears to have had a directory mounted on it as - * we chdir()ed. The problem with this is that in order to notice that - * a file system was mounted, we would need to lstat() all the mount points. - * That strategy loses if our machine is a client of a dead NFS server. - * - * Hence if safely_chdir() and wd_sanity_check() can manage without needing - * to know the mounted device list, we do that. - */ - if (!options.open_nofollow_available) - { -#ifdef STAT_MOUNTPOINTS - init_mounted_dev_list(0); -#endif - } - - - starting_desc = open (".", O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE +#ifndef HAVE_FCHDIR +/* The full path of the initial working directory. */ +char *starting_dir; +#else +/* A file descriptor open to the initial working directory. + Doing it this way allows us to work when the i.w.d. has + unreadable parents. */ +int starting_desc; #endif - ); - if (0 <= starting_desc && fchdir (starting_desc) != 0) - { - close (starting_desc); - starting_desc = -1; - } - - if (starting_desc < 0) - { - starting_dir = xgetcwd (); - if (! starting_dir) - error (1, errno, _("cannot get current directory")); - } - set_stat_placeholders(&starting_stat_buf); - if ((*options.xstat) (".", &starting_stat_buf) != 0) - error (1, errno, _("cannot stat current directory")); - - /* If no paths are given, default to ".". */ - for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++) - { - process_top_path (argv[i], 0); - } - - /* If there were no path arguments, default to ".". */ - if (i == end_of_leading_options) - { - /* - * We use a temporary variable here because some actions modify - * the path temporarily. Hence if we use a string constant, - * we get a coredump. The best example of this is if we say - * "find -printf %H" (note, not "find . -printf %H"). - */ - char defaultpath[2] = "."; - process_top_path (defaultpath, 0); - } - - /* If "-exec ... {} +" has been used, there may be some - * partially-full command lines which have been built, - * but which are not yet complete. Execute those now. - */ - show_success_rates(eval_tree); - cleanup(); - return state.exit_status; -} - -boolean is_fts_enabled(int *ftsoptions) -{ - /* this version of find (i.e. this main()) does not use fts. */ - *ftsoptions = 0; - return false; -} - - -static char * -specific_dirname(const char *dir) -{ - char dirbuf[1024]; - - if (0 == strcmp(".", dir)) - { - /* OK, what's '.'? */ - if (NULL != getcwd(dirbuf, sizeof(dirbuf))) - { - return strdup(dirbuf); - } - else - { - return strdup(dir); - } - } - else - { - char *result = canonicalize_filename_mode(dir, CAN_EXISTING); - if (NULL == result) - return strdup(dir); - else - return result; - } -} - - - -/* Return non-zero if FS is the name of a file system that is likely to - * be automounted - */ -static int -fs_likely_to_be_automounted(const char *fs) -{ - return ( (0==strcmp(fs, "nfs")) || (0==strcmp(fs, "autofs")) || (0==strcmp(fs, "subfs"))); -} +/* If true, we have called stat on the current path. */ +boolean have_stat; +/* The file being operated on, relative to the current directory. + Used for stat, readlink, and opendir. */ +char *rel_pathname; -#ifdef STAT_MOUNTPOINTS -static dev_t *mounted_devices = NULL; -static size_t num_mounted_devices = 0u; +/* Length of current path. */ +int path_length; +/* true if following symlinks. Should be consistent with xstat. */ +boolean dereference; -static void -init_mounted_dev_list(int mandatory) -{ - assert (NULL == mounted_devices); - assert (0 == num_mounted_devices); - mounted_devices = get_mounted_devices(&num_mounted_devices); - if (mandatory && (NULL == mounted_devices)) - { - error(1, 0, "Cannot read list of mounted devices."); - } -} - -static void -refresh_mounted_dev_list(void) -{ - if (mounted_devices) - { - free(mounted_devices); - mounted_devices = 0; - } - num_mounted_devices = 0u; - init_mounted_dev_list(1); -} +/* Pointer to the function used to stat files. */ +int (*xstat) (); +/* Status value to return to system. */ +int exit_status; -/* Search for device DEV in the array LIST, which is of size N. */ +#ifdef DEBUG_STAT static int -dev_present(dev_t dev, const dev_t *list, size_t n) -{ - if (list) - { - while (n-- > 0u) - { - if ( (*list++) == dev ) - return 1; - } - } - return 0; -} - -enum MountPointStateChange - { - MountPointRecentlyMounted, - MountPointRecentlyUnmounted, - MountPointStateUnchanged - }; - - - -static enum MountPointStateChange -get_mount_state(dev_t newdev) +debug_stat (file, bufp) + char *file; + struct stat *bufp; { - int new_is_present, new_was_present; - - new_was_present = dev_present(newdev, mounted_devices, num_mounted_devices); - refresh_mounted_dev_list(); - new_is_present = dev_present(newdev, mounted_devices, num_mounted_devices); - - if (new_was_present == new_is_present) - return MountPointStateUnchanged; - else if (new_is_present) - return MountPointRecentlyMounted; - else - return MountPointRecentlyUnmounted; + fprintf (stderr, "debug_stat (%s)\n", file); + return lstat (file, bufp); } - - - -/* We stat()ed a directory, chdir()ed into it (we know this - * since direction is TraversingDown), stat()ed it again, - * and noticed that the device numbers are different. Check - * if the file system was recently mounted. - * - * If it was, it looks like chdir()ing into the directory - * caused a file system to be mounted. Maybe automount is - * running. Anyway, that's probably OK - but it happens - * only when we are moving downward. - * - * We also allow for the possibility that a similar thing - * has happened with the unmounting of a file system. This - * is much rarer, as it relies on an automounter timeout - * occurring at exactly the wrong moment. - */ -static enum WdSanityCheckFatality -dirchange_is_fatal(const char *specific_what, - enum WdSanityCheckFatality isfatal, - int silent, - struct stat *newinfo) +#endif /* DEBUG_STAT */ + +void +main (argc, argv) + int argc; + char *argv[]; { - enum MountPointStateChange transition = get_mount_state(newinfo->st_dev); - switch (transition) - { - case MountPointRecentlyUnmounted: - isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; - if (!silent) - { - error (0, 0, - _("Warning: file system %s has recently been unmounted."), - safely_quote_err_filename(0, specific_what)); - } - break; - - case MountPointRecentlyMounted: - isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; - if (!silent) - { - error (0, 0, - _("Warning: file system %s has recently been mounted."), - safely_quote_err_filename(0, specific_what)); - } - break; - - case MountPointStateUnchanged: - /* leave isfatal as it is */ - break; - } - - return isfatal; -} - - -#endif + int i; + PFB parse_function; /* Pointer to who is to do the parsing. */ + struct predicate *cur_pred; + char *predicate_name; /* Name of predicate being parsed. */ + program_name = argv[0]; + predicates = NULL; + last_pred = NULL; + do_dir_first = true; + maxdepth = mindepth = -1; + cur_day_start = time ((time_t *) 0) - DAYSECS; + full_days = false; + no_leaf_check = false; + stay_on_filesystem = false; + exit_status = 0; + dereference = false; +#ifdef DEBUG_STAT + xstat = debug_stat; +#else /* !DEBUG_STAT */ + xstat = lstat; +#endif /* !DEBUG_STAT */ -/* Examine the results of the stat() of a directory from before we - * entered or left it, with the results of stat()ing it afterward. If - * these are different, the file system tree has been modified while we - * were traversing it. That might be an attempt to use a race - * condition to persuade find to do something it didn't intend - * (e.g. an attempt by an ordinary user to exploit the fact that root - * sometimes runs find on the whole file system). However, this can - * also happen if automount is running (certainly on Solaris). With - * automount, moving into a directory can cause a file system to be - * mounted there. - * - * To cope sensibly with this, we will raise an error if we see the - * device number change unless we are chdir()ing into a subdirectory, - * and the directory we moved into has been mounted or unmounted "recently". - * Here "recently" means since we started "find" or we last re-read - * the /etc/mnttab file. - * - * If the device number does not change but the inode does, that is a - * problem. - * - * If the device number and inode are both the same, we are happy. - * - * If a file system is (un)mounted as we chdir() into the directory, that - * may mean that we're now examining a section of the file system that might - * have been excluded from consideration (via -prune or -quit for example). - * Hence we print a warning message to indicate that the output of find - * might be inconsistent due to the change in the file system. - */ -static boolean -wd_sanity_check(const char *thing_to_stat, - const char *progname, - const char *what, - dev_t old_dev, - ino_t old_ino, - struct stat *newinfo, - int parent, - int line_no, - enum TraversalDirection direction, - enum WdSanityCheckFatality isfatal, - boolean *changed) /* output parameter */ -{ - const char *fstype; - char *specific_what = NULL; - int silent = 0; - const char *current_dir = "."; - - *changed = false; - - set_stat_placeholders(newinfo); - if ((*options.xstat) (current_dir, newinfo) != 0) - fatal_file_error(thing_to_stat); - - if (old_dev != newinfo->st_dev) - { - *changed = true; - specific_what = specific_dirname(what); - fstype = filesystem_type(newinfo, current_dir); - silent = fs_likely_to_be_automounted(fstype); - - /* This condition is rare, so once we are here it is - * reasonable to perform an expensive computation to - * determine if we should continue or fail. - */ - if (TraversingDown == direction) - { -#ifdef STAT_MOUNTPOINTS - isfatal = dirchange_is_fatal(specific_what,isfatal,silent,newinfo); -#else - isfatal = RETRY_IF_SANITY_CHECK_FAILS; -#endif - } +#ifdef DEBUG + printf ("cur_day_start = %s", ctime (&cur_day_start)); +#endif /* DEBUG */ - switch (isfatal) + /* Find where in ARGV the predicates begin. */ + for (i = 1; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + /* Do nothing. */ ; + + /* Enclose the expression in `( ... )' so a default -print will + apply to the whole expression. */ + parse_open (argv, &argc); + /* Build the input order list. */ + while (i < argc) + { + if (strchr ("-!(),", argv[i][0]) == NULL) + usage ("paths must precede expression"); + predicate_name = argv[i]; + parse_function = find_parser (predicate_name); + if (parse_function == NULL) + error (1, 0, "invalid predicate `%s'", predicate_name); + i++; + if (!(*parse_function) (argv, &i)) { - case FATAL_IF_SANITY_CHECK_FAILS: - { - fstype = filesystem_type(newinfo, current_dir); - error (1, 0, - _("%s%s changed during execution of %s (old device number %ld, new device number %ld, file system type is %s) [ref %ld]"), - safely_quote_err_filename(0, specific_what), - parent ? "/.." : "", - safely_quote_err_filename(1, progname), - (long) old_dev, - (long) newinfo->st_dev, - fstype, - (long)line_no); - /*NOTREACHED*/ - return false; - } - - case NON_FATAL_IF_SANITY_CHECK_FAILS: - { - /* Since the device has changed under us, the inode number - * will almost certainly also be different. However, we have - * already decided that this is not a problem. Hence we return - * without checking the inode number. - */ - free(specific_what); - return true; - } - - case RETRY_IF_SANITY_CHECK_FAILS: - return false; + if (argv[i] == NULL) + error (1, 0, "missing argument to `%s'", predicate_name); + else + error (1, 0, "invalid argument `%s' to `%s'", + argv[i], predicate_name); } } - - /* Device number was the same, check if the inode has changed. */ - if (old_ino != newinfo->st_ino) + if (predicates->pred_next == NULL) { - *changed = true; - specific_what = specific_dirname(what); - fstype = filesystem_type(newinfo, current_dir); - - error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0, - 0, /* no relevant errno value */ - _("%s%s changed during execution of %s " - "(old inode number %ld, new inode number %ld, file system type is %s) [ref %ld]"), - safely_quote_err_filename(0, specific_what), - parent ? "/.." : "", - safely_quote_err_filename(1, progname), - (long) old_ino, - (long) newinfo->st_ino, - fstype, - (long)line_no); - free(specific_what); - return false; + /* No predicates that do something other than set a global variable + were given; remove the unneeded initial `(' and add `-print'. */ + cur_pred = predicates; + predicates = last_pred = predicates->pred_next; + free ((char *) cur_pred); + parse_print (argv, &argc); } - - return true; -} - -enum SafeChdirStatus - { - SafeChdirOK, - SafeChdirFailSymlink, - SafeChdirFailNotDir, - SafeChdirFailStat, - SafeChdirFailWouldBeUnableToReturn, - SafeChdirFailChdirFailed, - SafeChdirFailNonexistent, - SafeChdirFailDestUnreadable - }; - -/* Safely perform a change in directory. We do this by calling - * lstat() on the subdirectory, using chdir() to move into it, and - * then lstat()ing ".". We compare the results of the two stat calls - * to see if they are consistent. If not, we sound the alarm. - * - * If following_links() is true, we do follow symbolic links. - */ -static enum SafeChdirStatus -safely_chdir_lstat(const char *dest, - enum TraversalDirection direction, - struct stat *statbuf_dest, - enum ChdirSymlinkHandling symlink_follow_option, - boolean *did_stat) -{ - struct stat statbuf_arrived; - int rv, dotfd=-1; - int saved_errno; /* specific_dirname() changes errno. */ - boolean rv_set = false; - boolean statflag = false; - int tries = 0; - enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS; - - saved_errno = errno = 0; - - dotfd = open(".", O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ); - - /* We jump back to here if wd_sanity_check() - * recoverably triggers an alert. - */ - retry: - ++tries; - - if (dotfd >= 0) + else if (!no_side_effects (predicates->pred_next)) { - /* Stat the directory we're going to. */ - set_stat_placeholders(statbuf_dest); - if (0 == options.xstat(dest, statbuf_dest)) - { - statflag = true; - -#ifdef S_ISLNK - /* symlink_follow_option might be set to SymlinkFollowOk, which - * would allow us to chdir() into a symbolic link. This is - * only useful for the case where the directory we're - * chdir()ing into is the basename of a command line - * argument, for example where "foo/bar/baz" is specified on - * the command line. When -P is in effect (the default), - * baz will not be followed if it is a symlink, but if bar - * is a symlink, it _should_ be followed. Hence we need the - * ability to override the policy set by following_links(). - */ - if (!following_links() && S_ISLNK(statbuf_dest->st_mode)) - { - /* We're not supposed to be following links, but this is - * a link. Check symlink_follow_option to see if we should - * make a special exception. - */ - if (symlink_follow_option == SymlinkFollowOk) - { - /* We need to re-stat() the file so that the - * sanity check can pass. - */ - if (0 != stat(dest, statbuf_dest)) - { - rv = SafeChdirFailNonexistent; - rv_set = true; - saved_errno = errno; - goto fail; - } - statflag = true; - } - else - { - /* Not following symlinks, so the attempt to - * chdir() into a symlink should be prevented. - */ - rv = SafeChdirFailSymlink; - rv_set = true; - saved_errno = 0; /* silence the error message */ - goto fail; - } - } -#endif -#ifdef S_ISDIR - /* Although the immediately following chdir() would detect - * the fact that this is not a directory for us, this would - * result in an extra system call that fails. Anybody - * examining the system-call trace should ideally not be - * concerned that something is actually failing. - */ - if (!S_ISDIR(statbuf_dest->st_mode)) - { - rv = SafeChdirFailNotDir; - rv_set = true; - saved_errno = 0; /* silence the error message */ - goto fail; - } -#endif - - if (options.debug_options & DebugSearch) - fprintf(stderr, "safely_chdir(): chdir(\"%s\")\n", dest); - - if (0 == chdir(dest)) - { - /* check we ended up where we wanted to go */ - boolean changed = false; - if (!wd_sanity_check(".", program_name, ".", - statbuf_dest->st_dev, - statbuf_dest->st_ino, - &statbuf_arrived, - 0, __LINE__, direction, - isfatal, - &changed)) - { - /* Only allow one failure. */ - if (RETRY_IF_SANITY_CHECK_FAILS == isfatal) - { - if (0 == fchdir(dotfd)) - { - isfatal = FATAL_IF_SANITY_CHECK_FAILS; - goto retry; - } - else - { - /* Failed to return to original directory, - * but we know that the current working - * directory is not the one that we intend - * to be in. Since fchdir() failed, we - * can't recover from this and so this error - * is fatal. - */ - error(1, errno, - "failed to return to parent directory"); - } - } - else - { - /* XXX: not sure what to use as an excuse here. */ - rv = SafeChdirFailNonexistent; - rv_set = true; - saved_errno = 0; - goto fail; - } - } - - close(dotfd); - return SafeChdirOK; - } - else - { - saved_errno = errno; - if (ENOENT == saved_errno) - { - rv = SafeChdirFailNonexistent; - rv_set = true; - if (options.ignore_readdir_race) - errno = 0; /* don't issue err msg */ - } - else if (ENOTDIR == saved_errno) - { - /* This can happen if the we stat a directory, - * and then file system activity changes it into - * a non-directory. - */ - saved_errno = 0; /* don't issue err msg */ - rv = SafeChdirFailNotDir; - rv_set = true; - } - else - { - rv = SafeChdirFailChdirFailed; - rv_set = true; - } - goto fail; - } - } - else - { - saved_errno = errno; - rv = SafeChdirFailStat; - rv_set = true; - - if ( (ENOENT == saved_errno) || (0 == state.curdepth)) - saved_errno = 0; /* don't issue err msg */ - goto fail; - } + /* One or more predicates that produce output were given; + remove the unneeded initial `('. */ + cur_pred = predicates; + predicates = predicates->pred_next; + free ((char *) cur_pred); } else { - /* We do not have read permissions on "." */ - rv = SafeChdirFailWouldBeUnableToReturn; - rv_set = true; - goto fail; + /* `( user-supplied-expression ) -print'. */ + parse_close (argv, &argc); + parse_print (argv, &argc); } - /* This is the success path, so we clear errno. The caller probably - * won't be calling error() anyway. - */ - saved_errno = 0; - - /* We use the same exit path for success or failure. - * which has occurred is recorded in RV. - */ - fail: - /* We do not call error() as this would result in a duplicate error - * message when the caller does the same thing. - */ - if (saved_errno) - errno = saved_errno; - - if (dotfd >= 0) - { - close(dotfd); - dotfd = -1; - } - - *did_stat = statflag; - assert (rv_set); - return rv; -} +#ifdef DEBUG + printf ("Predicate List:\n"); + print_list (predicates); +#endif /* DEBUG */ -#if defined O_NOFOLLOW -/* Safely change working directory to the specified subdirectory. If - * we are not allowed to follow symbolic links, we use open() with - * O_NOFOLLOW, followed by fchdir(). This ensures that we don't - * follow symbolic links (of course, we do follow them if the -L - * option is in effect). - */ -static enum SafeChdirStatus -safely_chdir_nofollow(const char *dest, - enum TraversalDirection direction, - struct stat *statbuf_dest, - enum ChdirSymlinkHandling symlink_follow_option, - boolean *did_stat) -{ - int extraflags, fd; - - (void) direction; - (void) statbuf_dest; - - extraflags = 0; - *did_stat = false; - - switch (symlink_follow_option) - { - case SymlinkFollowOk: - extraflags = 0; - break; - - case SymlinkHandleDefault: - if (following_links()) - extraflags = 0; - else - extraflags = O_NOFOLLOW; - break; - } - - errno = 0; - fd = open(dest, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - |extraflags); - if (fd < 0) - { - switch (errno) - { - case ELOOP: - return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */ - case ENOENT: - return SafeChdirFailNonexistent; - default: - return SafeChdirFailDestUnreadable; - } - } - - errno = 0; - if (0 == fchdir(fd)) - { - close(fd); - return SafeChdirOK; - } - else - { - int saved_errno = errno; - close(fd); - errno = saved_errno; - - switch (errno) - { - case ENOTDIR: - return SafeChdirFailNotDir; - - case EACCES: - case EBADF: /* Shouldn't happen */ - case EINTR: - case EIO: - default: - return SafeChdirFailChdirFailed; - } - } -} -#endif + /* Done parsing the predicates. Build the evaluation tree. */ + cur_pred = predicates; + eval_tree = get_expr (&cur_pred, NO_PREC); +#ifdef DEBUG + printf ("Eval Tree:\n"); + print_tree (eval_tree, 0); +#endif /* DEBUG */ -static enum SafeChdirStatus -safely_chdir(const char *dest, - enum TraversalDirection direction, - struct stat *statbuf_dest, - enum ChdirSymlinkHandling symlink_follow_option, - boolean *did_stat) -{ - enum SafeChdirStatus result; - - /* We're about to leave a directory. If there are any -execdir - * argument lists which have been built but have not yet been - * processed, do them now because they must be done in the same - * directory. - */ - complete_pending_execdirs(get_current_dirfd()); - -#if !defined(O_NOFOLLOW) - options.open_nofollow_available = false; -#endif - if (options.open_nofollow_available) - { - result = safely_chdir_nofollow(dest, direction, statbuf_dest, - symlink_follow_option, did_stat); - if (SafeChdirFailDestUnreadable != result) - { - return result; - } - else - { - /* Savannah bug #15384: fall through to use safely_chdir_lstat - * if the directory is not readable. - */ - /* Do nothing. */ - } - } - /* Even if O_NOFOLLOW is available, we may need to use the alternative - * method, since parent of the start point may be executable but not - * readable. - */ - return safely_chdir_lstat(dest, direction, statbuf_dest, - symlink_follow_option, did_stat); -} + /* Rearrange the eval tree in optimal-predicate order. */ + opt_expr (&eval_tree); + /* Determine the point, if any, at which to stat the file. */ + mark_stat (eval_tree); - -/* Safely go back to the starting directory. */ -static void -chdir_back (void) -{ - struct stat stat_buf; - boolean dummy; - +#ifdef DEBUG + printf ("Optimized Eval Tree:\n"); + print_tree (eval_tree, 0); +#endif /* DEBUG */ + +#ifndef HAVE_FCHDIR + starting_dir = xgetcwd (); + if (starting_dir == NULL) + error (1, errno, "cannot get current directory"); +#else + starting_desc = open (".", O_RDONLY); if (starting_desc < 0) - { - if (options.debug_options & DebugSearch) - fprintf(stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir); - -#ifdef STAT_MOUNTPOINTS - /* We will need the mounted device list. Get it now if we don't - * already have it. - */ - if (NULL == mounted_devices) - init_mounted_dev_list(1); + error (1, errno, "cannot open current directory"); #endif - - if (chdir (starting_dir) != 0) - fatal_file_error(starting_dir); - - wd_sanity_check(starting_dir, - program_name, - starting_dir, - starting_stat_buf.st_dev, - starting_stat_buf.st_ino, - &stat_buf, 0, __LINE__, - TraversingUp, - FATAL_IF_SANITY_CHECK_FAILS, - &dummy); - } - else - { - if (options.debug_options & DebugSearch) - fprintf(stderr, "chdir_back(): chdir(<starting-point>)\n"); - if (fchdir (starting_desc) != 0) - { - fatal_file_error(starting_dir); - } - } + /* If no paths are given, default to ".". */ + for (i = 1; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + process_top_path (argv[i]); + if (i == 1) + process_top_path ("."); + + exit (exit_status); } -/* Move to the parent of a given directory and then call a function, - * restoring the cwd. Don't bother changing directory if the - * specified directory is a child of "." or is the root directory. - */ +/* Descend PATHNAME, which is a command-line argument. */ + static void -at_top (char *pathname, - mode_t mode, - struct stat *pstat, - void (*action)(char *pathname, - char *basename, - int mode, - struct stat *pstat)) +process_top_path (pathname) + char *pathname; { - int dirchange; - char *parent_dir = dir_name (pathname); - char *base = last_component (pathname); + struct stat stat_buf; - state.curdepth = 0; - state.starting_path_length = strlen (pathname); + curdepth = 0; + path_length = strlen (pathname); - if (0 == *base - || 0 == strcmp(parent_dir, ".")) + /* We stat each pathname given on the command-line twice -- + once here and once in process_path. It's not too bad, though, + since the kernel can read the stat information out of its inode + cache the second time. */ + if ((*xstat) (pathname, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode)) { - dirchange = 0; - base = pathname; - } - else - { - enum TraversalDirection direction; - enum SafeChdirStatus chdir_status; - struct stat st; - boolean did_stat = false; - - dirchange = 1; - if (0 == strcmp(base, "..")) - direction = TraversingUp; - else - direction = TraversingDown; - - /* We pass SymlinkFollowOk to safely_chdir(), which allows it to - * chdir() into a symbolic link. This is only useful for the - * case where the directory we're chdir()ing into is the - * basename of a command line argument, for example where - * "foo/bar/baz" is specified on the command line. When -P is - * in effect (the default), baz will not be followed if it is a - * symlink, but if bar is a symlink, it _should_ be followed. - * Hence we need the ability to override the policy set by - * following_links(). - */ - chdir_status = safely_chdir(parent_dir, direction, &st, SymlinkFollowOk, &did_stat); - if (SafeChdirOK != chdir_status) + if (chdir (pathname) < 0) { - const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir; - if (errno) - error (0, errno, "%s", - safely_quote_err_filename(0, what)); - else - error (0, 0, _("Failed to safely change directory into %s"), - safely_quote_err_filename(0, parent_dir)); - - /* We can't process this command-line argument. */ - state.exit_status = 1; + error (0, errno, "%s", pathname); + exit_status = 1; return; } + process_path (pathname, ".", false, "."); +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) < 0) + error (1, errno, "%s", starting_dir); +#else + if (fchdir (starting_desc)) + error (1, errno, "cannot return to starting directory"); +#endif } - - free (parent_dir); - parent_dir = NULL; - - action(pathname, base, mode, pstat); - - if (dirchange) - { - chdir_back(); - } -} - - -static void do_process_top_dir(char *pathname, - char *base, - int mode, - struct stat *pstat) -{ - (void) pstat; - - process_path (pathname, base, false, ".", mode); - complete_pending_execdirs(get_current_dirfd()); -} - -static void do_process_predicate(char *pathname, - char *base, - int mode, - struct stat *pstat) -{ - (void) mode; - - state.rel_pathname = base; /* cwd_dir_fd was already set by safely_chdir */ - apply_predicate (pathname, pstat, get_eval_tree()); -} - - - - -/* Descend PATHNAME, which is a command-line argument. - - Actions like -execdir assume that we are in the - parent directory of the file we're examining, - and on entry to this function our working directory - is whatever it was when find was invoked. Therefore - If PATHNAME is "." we just leave things as they are. - Otherwise, we figure out what the parent directory is, - and move to that. -*/ -static void -process_top_path (char *pathname, mode_t mode) -{ - at_top(pathname, mode, NULL, do_process_top_dir); + else + process_path (pathname, pathname, false, "."); } - /* Info on each directory in the current tree branch, to avoid getting stuck in symbolic link loops. */ +struct dir_id +{ + ino_t ino; + dev_t dev; +}; static struct dir_id *dir_ids = NULL; /* Entries allocated in `dir_ids'. */ static int dir_alloc = 0; @@ -1082,60 +308,6 @@ static int dir_curr = -1; /* (Arbitrary) number of entries to grow `dir_ids' by. */ #define DIR_ALLOC_STEP 32 - - -/* We've detected a file system loop. This is caused by one of - * two things: - * - * 1. Option -L is in effect and we've hit a symbolic link that - * points to an ancestor. This is harmless. We won't traverse the - * symbolic link. - * - * 2. We have hit a real cycle in the directory hierarchy. In this - * case, we issue a diagnostic message (POSIX requires this) and we - * skip that directory entry. - */ -static void -issue_loop_warning(const char *name, const char *pathname, int level) -{ - struct stat stbuf_link; - if (lstat(name, &stbuf_link) != 0) - stbuf_link.st_mode = S_IFREG; - - if (S_ISLNK(stbuf_link.st_mode)) - { - error(0, 0, - _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."), - safely_quote_err_filename(0, pathname)); - /* XXX: POSIX appears to require that the exit status be non-zero if a - * diagnostic is issued. - */ - } - else - { - int distance = 1 + (dir_curr-level); - /* We have found an infinite loop. POSIX requires us to - * issue a diagnostic. Usually we won't get to here - * because when the leaf optimisation is on, it will cause - * the subdirectory to be skipped. If /a/b/c/d is a hard - * link to /a/b, then the link count of /a/b/c is 2, - * because the ".." entry of /b/b/c/d points to /a, not - * to /a/b/c. - */ - error(0, 0, - ngettext( - "Filesystem loop detected; %s has the same device number and inode as " - "a directory which is %d level higher in the file system hierarchy", - "Filesystem loop detected; %s has the same device number and inode as " - "a directory which is %d levels higher in the file system hierarchy", - (long)distance), - safely_quote_err_filename(0, pathname), - distance); - } -} - - - /* Recursively descend path PATHNAME, applying the predicates. LEAF is true if PATHNAME is known to be in a directory that has no more unexamined subdirectories, and therefore it is not a directory. @@ -1151,57 +323,52 @@ issue_loop_warning(const char *name, const char *pathname, int level) Return nonzero iff PATHNAME is a directory. */ static int -process_path (char *pathname, char *name, boolean leaf, char *parent, - mode_t mode) +process_path (pathname, name, leaf, parent) + char *pathname; + char *name; + boolean leaf; + char *parent; { struct stat stat_buf; static dev_t root_dev; /* Device ID of current argument pathname. */ int i; - struct predicate *eval_tree; - eval_tree = get_eval_tree(); /* Assume it is a non-directory initially. */ stat_buf.st_mode = 0; - state.rel_pathname = name; - state.type = 0; - state.have_stat = false; - state.have_type = false; - - if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) - return 0; - - if (!S_ISDIR (state.type)) + + rel_pathname = name; + + if (leaf) + have_stat = false; + else + { + if ((*xstat) (name, &stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return 0; + } + have_stat = true; + } + + if (!S_ISDIR (stat_buf.st_mode)) { - if (state.curdepth >= options.mindepth) + if (curdepth >= mindepth) apply_predicate (pathname, &stat_buf, eval_tree); return 0; } /* From here on, we're working on a directory. */ - - /* Now we really need to stat the directory, even if we know the - * type, because we need information like struct stat.st_rdev. - */ - if (get_statinfo(pathname, name, &stat_buf) != 0) - return 0; - - state.have_stat = true; - mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */ - state.stop_at_current_level = - options.maxdepth >= 0 - && state.curdepth >= options.maxdepth; + stop_at_current_level = maxdepth >= 0 && curdepth >= maxdepth; /* If we've already seen this directory on this branch, don't descend it again. */ for (i = 0; i <= dir_curr; i++) if (stat_buf.st_ino == dir_ids[i].ino && stat_buf.st_dev == dir_ids[i].dev) - { - state.stop_at_current_level = true; - issue_loop_warning(name, pathname, i); - } - + stop_at_current_level = true; + if (dir_alloc <= ++dir_curr) { dir_alloc += DIR_ALLOC_STEP; @@ -1211,49 +378,28 @@ process_path (char *pathname, char *name, boolean leaf, char *parent, dir_ids[dir_curr].ino = stat_buf.st_ino; dir_ids[dir_curr].dev = stat_buf.st_dev; - if (options.stay_on_filesystem) + if (stay_on_filesystem) { - if (state.curdepth == 0) + if (curdepth == 0) root_dev = stat_buf.st_dev; else if (stat_buf.st_dev != root_dev) - state.stop_at_current_level = true; + stop_at_current_level = true; } - if (options.do_dir_first && state.curdepth >= options.mindepth) + if (do_dir_first && curdepth >= mindepth) apply_predicate (pathname, &stat_buf, eval_tree); - if (options.debug_options & DebugSearch) - fprintf(stderr, "pathname = %s, stop_at_current_level = %d\n", - pathname, state.stop_at_current_level); - - if (state.stop_at_current_level == false) - { - /* Scan directory on disk. */ - process_dir (pathname, name, strlen (pathname), &stat_buf, parent); - } + if (stop_at_current_level == false) + /* Scan directory on disk. */ + process_dir (pathname, name, strlen (pathname), &stat_buf, parent); - if (options.do_dir_first == false && state.curdepth >= options.mindepth) - { - /* The fields in 'state' are now out of date. Correct them. - */ - if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) - return 0; - - if (0 == dir_curr) - { - at_top(pathname, mode, &stat_buf, do_process_predicate); - } - else - { - do_process_predicate(pathname, name, mode, &stat_buf); - } - } + if (do_dir_first == false && curdepth >= mindepth) + apply_predicate (pathname, &stat_buf, eval_tree); dir_curr--; return 1; } - /* Scan directory PATHNAME and recurse through process_path for each entry. @@ -1261,47 +407,36 @@ process_path (char *pathname, char *name, boolean leaf, char *parent, NAME is PATHNAME relative to the current directory. - STATP is the results of *options.xstat on it. + STATP is the results of *xstat on it. PARENT is the path of the parent of NAME, relative to find's starting directory. */ static void -process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent) +process_dir (pathname, name, pathlen, statp, parent) + char *pathname; + char *name; + int pathlen; + struct stat *statp; + char *parent; { + char *name_space; /* Names of files in PATHNAME. */ int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */ - boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */ - unsigned int idx; /* Which entry are we on? */ - struct stat stat_buf; - size_t dircount = 0u; - struct savedir_dirinfo *dirinfo; -#if 0 - printf("process_dir: pathname=%s name=%s statp->st_nlink=%d st_ino=%d\n", - pathname, - name, - (int)statp->st_nlink, - (int)statp->st_ino); -#endif - if (statp->st_nlink < 2) - { - subdirs_unreliable = true; - subdirs_left = 0; - } - else - { - subdirs_unreliable = false; /* not necessarily right */ - subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */ - } - - errno = 0; - dirinfo = xsavedir(name, 0); - - if (dirinfo == NULL) + subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */ + + errno = 0; + /* On some systems (VAX 4.3BSD+NFS), NFS mount points have st_size < 0. */ + name_space = savedir (name, statp->st_size > 0 ? statp->st_size : 512); + if (name_space == NULL) { - assert (errno != 0); - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; + if (errno) + { + error (0, errno, "%s", pathname); + exit_status = 1; + } + else + error (1, 0, "virtual memory exhausted"); } else { @@ -1311,8 +446,7 @@ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, unsigned cur_path_size; /* Bytes allocated for `cur_path'. */ register unsigned file_len; /* Length of each path to process. */ register unsigned pathname_len; /* PATHLEN plus trailing '/'. */ - boolean did_stat = false; - + if (pathname[pathlen - 1] == '/') pathname_len = pathlen + 1; /* For '\0'; already have '/'. */ else @@ -1320,77 +454,15 @@ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, cur_path_size = 0; cur_path = NULL; - /* We're about to leave the directory. If there are any - * -execdir argument lists which have been built but have not - * yet been processed, do them now because they must be done in - * the same directory. - */ - complete_pending_execdirs(get_current_dirfd()); - - if (strcmp (name, ".")) + if (strcmp (name, ".") && chdir (name) < 0) { - enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat); - switch (status) - { - case SafeChdirOK: - /* If there had been a change but wd_sanity_check() - * accepted it, we need to accept that on the - * way back up as well, so modify our record - * of what we think we should see later. - * If there was no change, the assignments are a no-op. - * - * However, before performing the assignment, we need to - * check that we have the stat information. If O_NOFOLLOW - * is available, safely_chdir() will not have needed to use - * stat(), and so stat_buf will just contain random data. - */ - if (!did_stat) - { - /* If there is a link we need to follow it. Hence - * the direct call to stat() not through (options.xstat) - */ - set_stat_placeholders(&stat_buf); - if (0 != stat(".", &stat_buf)) - break; /* skip the assignment. */ - } - dir_ids[dir_curr].dev = stat_buf.st_dev; - dir_ids[dir_curr].ino = stat_buf.st_ino; - - break; - - case SafeChdirFailWouldBeUnableToReturn: - error (0, errno, "."); - state.exit_status = 1; - break; - - case SafeChdirFailNonexistent: - case SafeChdirFailDestUnreadable: - case SafeChdirFailStat: - case SafeChdirFailNotDir: - case SafeChdirFailChdirFailed: - error (0, errno, "%s", - safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return; - - case SafeChdirFailSymlink: - error (0, 0, - _("warning: not following the symbolic link %s"), - safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return; - } + error (0, errno, "%s", pathname); + exit_status = 1; + return; } - for (idx=0; idx < dirinfo->size; ++idx) + for (namep = name_space; *namep; namep += file_len - pathname_len + 1) { - /* savedirinfo() may return dirinfo=NULL if extended information - * is not available. - */ - mode_t mode = (dirinfo->entries[idx].flags & SavedirHaveFileType) ? - dirinfo->entries[idx].type_info : 0; - namep = dirinfo->entries[idx].name; - /* Append this directory entry's name to the path being searched. */ file_len = pathname_len + strlen (namep); if (file_len > cur_path_size) @@ -1406,126 +478,64 @@ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, cur_name = cur_path + pathname_len - 1; strcpy (cur_name, namep); - state.curdepth++; - if (!options.no_leaf_check && !subdirs_unreliable) - { - if (mode && S_ISDIR(mode) && (subdirs_left == 0)) - { - /* This is a subdirectory, but the number of directories we - * have found now exceeds the number we would expect given - * the hard link count on the parent. This is likely to be - * a bug in the file system driver (e.g. Linux's - * /proc file system) or may just be a fact that the OS - * doesn't really handle hard links with Unix semantics. - * In the latter case, -noleaf should be used routinely. - */ - error(0, 0, _("WARNING: Hard link count is wrong for %s (saw only st_nlink=%d but we already saw %d subdirectories): this may be a bug in your file system driver. Automatically turning on find's -noleaf option. Earlier results may have failed to include directories that should have been searched."), - safely_quote_err_filename(0, pathname), - statp->st_nlink, - dircount); - state.exit_status = 1; /* We know the result is wrong, now */ - options.no_leaf_check = true; /* Don't make same - mistake again */ - subdirs_unreliable = 1; - subdirs_left = 1; /* band-aid for this iteration. */ - } - - /* Normal case optimization. On normal Unix - file systems, a directory that has no subdirectories - has two links: its name, and ".". Any additional - links are to the ".." entries of its subdirectories. - Once we have processed as many subdirectories as - there are additional links, we know that the rest of - the entries are non-directories -- in other words, - leaf files. */ - { - int count; - count = process_path (cur_path, cur_name, - subdirs_left == 0, pathname, - mode); - subdirs_left -= count; - dircount += count; - } - } + curdepth++; + if (!no_leaf_check) + /* Normal case optimization. + On normal Unix filesystems, a directory that has no + subdirectories has two links: its name, and ".". Any + additional links are to the ".." entries of its + subdirectories. Once we have processed as many + subdirectories as there are additional links, we know + that the rest of the entries are non-directories -- + in other words, leaf files. */ + subdirs_left -= process_path (cur_path, cur_name, + subdirs_left == 0, pathname); else - { - /* There might be weird (e.g., CD-ROM or MS-DOS) file systems - mounted, which don't have Unix-like directory link counts. */ - process_path (cur_path, cur_name, false, pathname, mode); - } - - state.curdepth--; + /* There might be weird (e.g., CD-ROM or MS-DOS) filesystems + mounted, which don't have Unix-like directory link counts. */ + process_path (cur_path, cur_name, false, pathname); + curdepth--; } - - /* We're about to leave the directory. If there are any - * -execdir argument lists which have been built but have not - * yet been processed, do them now because they must be done in - * the same directory. - */ - complete_pending_execdirs(get_current_dirfd()); - if (strcmp (name, ".")) { - enum SafeChdirStatus status; - struct dir_id did; - - /* We could go back and do the next command-line arg - instead, maybe using longjmp. */ - char const *dir; - boolean deref = following_links() ? true : false; - - if ( (state.curdepth>0) && !deref) - dir = ".."; - else - { - chdir_back (); - dir = parent; - } - - did_stat = false; - status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat); - switch (status) + if (!dereference) { - case SafeChdirOK: - break; - - case SafeChdirFailWouldBeUnableToReturn: - error (1, errno, "."); - return; - - case SafeChdirFailNonexistent: - case SafeChdirFailDestUnreadable: - case SafeChdirFailStat: - case SafeChdirFailSymlink: - case SafeChdirFailNotDir: - case SafeChdirFailChdirFailed: - error (1, errno, "%s", safely_quote_err_filename(0, pathname)); - return; - } - - if (dir_curr > 0) - { - did.dev = dir_ids[dir_curr-1].dev; - did.ino = dir_ids[dir_curr-1].ino; + if (chdir ("..") < 0) + /* We could go back and do the next command-line arg instead, + maybe using longjmp. */ + error (1, errno, "%s", parent); } else { - did.dev = starting_stat_buf.st_dev; - did.ino = starting_stat_buf.st_ino; +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) || chdir (parent)) + error (1, errno, "%s", parent); +#else + if (fchdir (starting_desc) || chdir (parent)) + error (1, errno, "%s", parent); +#endif } } if (cur_path) free (cur_path); - free_dirinfo(dirinfo); + free (name_space); } +} + +/* Return true if there are no side effects in any of the predicates in + predicate list PRED, false if there are any. */ - if (subdirs_unreliable) +static boolean +no_side_effects (pred) + struct predicate *pred; +{ + while (pred != NULL) { - /* Make sure we hasn't used the variable subdirs_left if we knew - * we shouldn't do so. - */ - assert (0 == subdirs_left || options.no_leaf_check); + if (pred->side_effects) + return (false); + pred = pred->pred_next; } + return (true); } diff --git a/find/fstype.c b/find/fstype.c index 75b33713..56492a50 100644 --- a/find/fstype.c +++ b/find/fstype.c @@ -1,9 +1,10 @@ -/* fstype.c -- determine type of file systems that files are on - Copyright (C) 1990, 91, 92, 93, 94, 2000, 2004 Free Software Foundation, Inc. - This program is free software: you can redistribute it and/or modify +/* fstype.c -- determine type of filesystems that files are on + Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 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 @@ -11,108 +12,144 @@ 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/>. -*/ - -/* Written by David MacKenzie <djm@gnu.org>. - * - * Converted to use gnulib's read_file_system_list() - * by James Youngman <jay@gnu.org> (which saves a lot - * of manual hacking of configure.in). - */ + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */ #include <config.h> -#include <errno.h> -#include <stdbool.h> - -#ifdef HAVE_SYS_TYPES_H +#include <stdio.h> #include <sys/types.h> -#endif #include <sys/stat.h> - -/* The presence of unistd.h is assumed by gnulib these days, so we - * might as well assume it too. - */ -#include <unistd.h> - -#include <fcntl.h> -#ifdef HAVE_SYS_MNTIO_H -#include <sys/mntio.h> -#endif -#ifdef HAVE_SYS_MKDEV_H -#include <sys/mkdev.h> -#endif - +#include "defs.h" +#include "modetype.h" +#include <errno.h> #ifdef STDC_HEADERS #include <stdlib.h> #else extern int errno; #endif -#include "defs.h" -#include "../gnulib/lib/dirname.h" -#include "xalloc.h" -#include "modetype.h" +char *strdup (); +char *strstr (); + +static char *filesystem_type_uncached P_((char *path, char *relpath, struct stat *statp)); +static int xatoi P_((char *cp)); + +#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */ +#include <mntent.h> +#if !defined(MOUNTED) +# if defined(MNT_MNTTAB) /* HP-UX. */ +# define MOUNTED MNT_MNTTAB +# endif +# if defined(MNTTABNAME) /* Dynix. */ +# define MOUNTED MNTTABNAME +# endif +#endif +#endif -/* Need declaration of function `xstrtoumax' */ -#include "../gnulib/lib/xstrtol.h" +#ifdef FSTYPE_GETMNT /* Ultrix. */ +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/fs_types.h> +#endif -#include "extendbuf.h" -#include "mountlist.h" -#include "error.h" +#ifdef FSTYPE_USG_STATFS /* SVR3. */ +#include <sys/statfs.h> +#include <sys/fstyp.h> +#endif +#ifdef FSTYPE_STATVFS /* SVR4. */ +#include <sys/statvfs.h> +#include <sys/fstyp.h> +#endif +#ifdef FSTYPE_STATFS /* 4.4BSD. */ +#include <sys/param.h> /* NetBSD needs this. */ +#include <sys/mount.h> -#if ENABLE_NLS -# include <libintl.h> -# define _(Text) gettext (Text) -#else -# define _(Text) Text +#ifndef MFSNAMELEN /* NetBSD defines this. */ +static char * +fstype_to_string (t) + short t; +{ +#ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */ + static char *mn[] = INITMOUNTNAMES; + if (t >= 0 && t <= MOUNT_MAXTYPE) + return mn[t]; + else + return "?"; +#else /* !INITMOUNTNAMES */ + switch (t) + { + case MOUNT_UFS: + return "ufs"; + case MOUNT_NFS: + return "nfs"; +#ifdef MOUNT_PC + case MOUNT_PC: + return "pc"; #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 +#ifdef MOUNT_MFS + case MOUNT_MFS: + return "mfs"; #endif - -static char *file_system_type_uncached PARAMS((const struct stat *statp, const char *path)); - - -/* Get MNTTYPE_IGNORE if it is available. */ -#if HAVE_MNTENT_H -# include <mntent.h> +#ifdef MOUNT_LO + case MOUNT_LO: + return "lofs"; #endif -#if HAVE_SYS_MNTTAB_H -# include <stdio.h> -# include <sys/mnttab.h> +#ifdef MOUNT_TFS + case MOUNT_TFS: + return "tfs"; #endif +#ifdef MOUNT_TMP + case MOUNT_TMP: + return "tmp"; +#endif +#ifdef MOUNT_MSDOS + case MOUNT_MSDOS: + return "msdos"; +#endif +#ifdef MOUNT_ISO9660 + case MOUNT_ISO9660: + return "iso9660fs"; +#endif + default: + return "?"; + } +#endif /* !INITMOUNTNAMES */ +} +#endif /* !MFSNAMELEN */ +#endif /* FSTYPE_STATFS */ +#ifdef FSTYPE_AIX_STATFS /* AIX. */ +#include <sys/vmount.h> +#include <sys/statfs.h> +#define FSTYPE_STATFS /* Otherwise like 4.4BSD. */ +#define f_type f_vfstype - - -static void -free_file_system_list(struct mount_entry *p) +static char * +fstype_to_string (t) + short t; { - while (p) + switch (t) { - struct mount_entry *pnext = p->me_next; - - free(p->me_devname); - free(p->me_mountdir); - - if(p->me_type_malloced) - free(p->me_type); - p->me_next = NULL; - free(p); - p = pnext; + case MNT_AIX: +#if 0 /* NFS filesystems are actually MNT_AIX. */ + return "aix"; +#endif + case MNT_NFS: + return "nfs"; + case MNT_JFS: + return "jfs"; + case MNT_CDROM: + return "cdrom"; + default: + return "?"; } } - - - +#endif /* FSTYPE_AIX_STATFS */ #ifdef AFS #include <netinet/in.h> @@ -128,7 +165,8 @@ free_file_system_list(struct mount_entry *p) #endif static int -in_afs (char *path) +in_afs (path) + char *path; { static char space[2048]; struct ViceIoctl vi; @@ -144,16 +182,19 @@ in_afs (char *path) } #endif /* AFS */ -/* Nonzero if the current file system's type is known. */ +/* Nonzero if the current filesystem's type is known. */ static int fstype_known = 0; -/* Return a static string naming the type of file system that the file PATH, +/* Return a static string naming the type of filesystem that the file PATH, described by STATP, is on. RELPATH is the file name relative to the current directory. - Return "unknown" if its file system type is unknown. */ + Return "unknown" if its filesystem type is unknown. */ char * -filesystem_type (const struct stat *statp, const char *path) +filesystem_type (path, relpath, statp) + char *path; + char *relpath; + struct stat *statp; { static char *current_fstype = NULL; static dev_t current_dev; @@ -165,149 +206,181 @@ filesystem_type (const struct stat *statp, const char *path) free (current_fstype); } current_dev = statp->st_dev; - current_fstype = file_system_type_uncached (statp, path); + current_fstype = filesystem_type_uncached (path, relpath, statp); return current_fstype; } -static int -set_fstype_devno(struct mount_entry *p) +/* Return a newly allocated string naming the type of filesystem that the + file PATH, described by STATP, is on. + RELPATH is the file name relative to the current directory. + Return "unknown" if its filesystem type is unknown. */ + +static char * +filesystem_type_uncached (path, relpath, statp) + char *path; + char *relpath; + struct stat *statp; { - struct stat stbuf; - - if (p->me_dev == (dev_t)-1) + char *type = NULL; + +#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */ + char *table = MOUNTED; + FILE *mfp; + struct mntent *mnt; + + mfp = setmntent (table, "r"); + if (mfp == NULL) + error (1, errno, "%s", table); + + /* Find the entry with the same device number as STATP, and return + that entry's fstype. */ + while (type == NULL && (mnt = getmntent (mfp))) { - set_stat_placeholders(&stbuf); - if (0 == (options.xstat)(p->me_mountdir, &stbuf)) + char *devopt; + dev_t dev; + struct stat disk_stats; + +#ifdef MNTTYPE_IGNORE + if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE)) + continue; +#endif + + /* Newer systems like SunOS 4.1 keep the dev number in the mtab, + in the options string. For older systems, we need to stat the + directory that the filesystem is mounted on to get it. + + Unfortunately, the HPUX 9.x mnttab entries created by automountq + contain a dev= option but the option value does not match the + st_dev value of the file (maybe the lower 16 bits match?). */ + +#if !defined(hpux) && !defined(__hpux__) + devopt = strstr (mnt->mnt_opts, "dev="); + if (devopt) { - p->me_dev = stbuf.st_dev; - return 0; + if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X')) + dev = xatoi (devopt + 6); + else + dev = xatoi (devopt + 4); } else +#endif /* not hpux */ { - return -1; + if (stat (mnt->mnt_dir, &disk_stats) == -1) + error (1, errno, "error in %s: %s", table, mnt->mnt_dir); + dev = disk_stats.st_dev; } - } - return 0; /* not needed */ -} -static struct mount_entry * -must_read_fs_list(bool need_fs_type) -{ - struct mount_entry *entries = read_file_system_list(need_fs_type); - if (NULL == entries) - { - /* We cannot determine for sure which file we were trying to - * use because gnulib has extracted all that stuff away. - * Hence we cannot issue a specific error message here. - */ - error(1, 0, "Cannot read mounted file system list"); + if (dev == statp->st_dev) + type = mnt->mnt_type; } - return entries; -} + if (endmntent (mfp) == 0) + error (0, errno, "%s", table); +#endif +#ifdef FSTYPE_GETMNT /* Ultrix. */ + int offset = 0; + struct fs_data fsd; -/* Return a newly allocated string naming the type of file system that the - file PATH, described by STATP, is on. - RELPATH is the file name relative to the current directory. - Return "unknown" if its file system type is unknown. */ + while (type == NULL + && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0) + { + if (fsd.fd_req.dev == statp->st_dev) + type = gt_names[fsd.fd_req.fstype]; + } +#endif -static char * -file_system_type_uncached (const struct stat *statp, const char *path) -{ - struct mount_entry *entries, *entry; - char *type; +#ifdef FSTYPE_USG_STATFS /* SVR3. */ + struct statfs fss; + char typebuf[FSTYPSZ]; - (void) path; - -#ifdef AFS - if (in_afs(path)) + if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1) { - fstype_known = 1; - return xstrdup("afs"); + /* Don't die if a file was just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); } -#endif - - entries = must_read_fs_list(true); - for (type=NULL, entry=entries; entry; entry=entry->me_next) - { -#ifdef MNTTYPE_IGNORE - if (!strcmp (entry->me_type, MNTTYPE_IGNORE)) - continue; + else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf)) + type = typebuf; #endif - set_fstype_devno(entry); - if (entry->me_dev == statp->st_dev) - { - type = xstrdup(entry->me_type); - break; - } + +#ifdef FSTYPE_STATVFS /* SVR4. */ + struct statvfs fss; + + if (statvfs (relpath, &fss) == -1) + { + /* Don't die if a file was just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); } - free_file_system_list(entries); + else + type = fss.f_basetype; +#endif - /* Don't cache unknown values. */ - fstype_known = (type != NULL); - - return type ? type : xstrdup(_("unknown")); -} +#ifdef FSTYPE_STATFS /* 4.4BSD. */ + struct statfs fss; + char *p; + if (S_ISLNK (statp->st_mode)) + p = dirname (relpath); + else + p = relpath; -char * -get_mounted_filesystems (void) -{ - char *result = NULL; - size_t alloc_size = 0u; - size_t used = 0u; - struct mount_entry *entries, *entry; - - entries = must_read_fs_list(false); - for (entry=entries; entry; entry=entry->me_next) + if (statfs (p, &fss) == -1) { - size_t len; - -#ifdef MNTTYPE_IGNORE - if (!strcmp (entry->me_type, MNTTYPE_IGNORE)) - continue; + /* Don't die if symlink to nonexisting file, or a file that was + just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); + } + else + { +#ifdef MFSNAMELEN /* NetBSD. */ + type = xstrdup (fss.f_fstypename); +#else + type = fstype_to_string (fss.f_type); #endif - set_fstype_devno(entry); - - len = strlen(entry->me_mountdir) + 1; - result = extendbuf(result, used+len, &alloc_size); - strcpy(&result[used], entry->me_mountdir); - used += len; /* len already includes one for the \0 */ } + if (p != relpath) + free (p); +#endif + +#ifdef AFS + if ((!type || !strcmp (type, "xx")) && in_afs (relpath)) + type = "afs"; +#endif - free_file_system_list(entries); - return result; + /* An unknown value can be caused by an ENOENT error condition. + Don't cache those values. */ + fstype_known = (type != NULL); + + return xstrdup (type ? type : "unknown"); } +#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */ +/* Return the value of the hexadecimal number represented by CP. + No prefix (like '0x') or suffix (like 'h') is expected to be + part of CP. */ -dev_t * -get_mounted_devices (size_t *n) +static int +xatoi (cp) + char *cp; { - size_t alloc_size = 0u; - size_t used = 0u; - struct mount_entry *entries, *entry; - dev_t *result = NULL; - - /* Use read_file_system_list() rather than must_read_fs_list() - * because on some system this is always called at startup, - * and find should only exit fatally if it needs to use the - * result of this operation. If we can't get the fs list - * but we never need the information, there is no need to fail. - */ - for (entry = entries = read_file_system_list(false); - entry; - entry = entry->me_next) + int val; + + val = 0; + while (*cp) { - result = extendbuf(result, sizeof(dev_t)*(used+1), &alloc_size); - set_fstype_devno(entry); - result[used] = entry->me_dev; - ++used; + if (*cp >= 'a' && *cp <= 'f') + val = val * 16 + *cp - 'a' + 10; + else if (*cp >= 'A' && *cp <= 'F') + val = val * 16 + *cp - 'A' + 10; + else if (*cp >= '0' && *cp <= '9') + val = val * 16 + *cp - '0'; + else + break; + cp++; } - free_file_system_list(entries); - *n = used; - return result; + return val; } - - - +#endif 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); } diff --git a/find/pred.c b/find/pred.c index 75e6774a..9c8c6828 100644 --- a/find/pred.c +++ b/find/pred.c @@ -1,70 +1,40 @@ /* pred.c -- execute the expression tree. - Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003, - 2004, 2005, 2006, 2007, 2008 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, 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 <stdio.h> #include <fnmatch.h> #include <signal.h> -#include <math.h> #include <pwd.h> #include <grp.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <assert.h> -#include <stdarg.h> -#include <fcntl.h> -#include <locale.h> -#include <openat.h> -#include "xalloc.h" -#include "dirname.h" -#include "human.h" +#include "defs.h" #include "modetype.h" -#include "filemode.h" #include "wait.h" -#include "printquoted.h" -#include "buildcmd.h" -#include "yesno.h" -#include "listfile.h" -#include "stat-time.h" -#include "dircallback.h" -#include "error.h" -#include "verify.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(SIGCHLD) && defined(SIGCLD) #define SIGCHLD SIGCLD #endif - +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct group *getgrgid (); +#endif #if HAVE_DIRENT_H # include <dirent.h> @@ -90,83 +60,40 @@ #define CLOSEDIR(d) closedir (d) #endif - - - -/* Get or fake the disk device blocksize. - Usually defined by sys/param.h (if at all). */ -#ifndef DEV_BSIZE -# ifdef BSIZE -# define DEV_BSIZE BSIZE -# else /* !BSIZE */ -# define DEV_BSIZE 4096 -# endif /* !BSIZE */ -#endif /* !DEV_BSIZE */ - -/* Extract or fake data from a `struct stat'. - ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. - ST_NBLOCKS: Number of blocks in the file, including indirect blocks. - ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ -#ifndef HAVE_STRUCT_STAT_ST_BLOCKS -# define ST_BLKSIZE(statbuf) DEV_BSIZE -# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */ -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0) -# else /* !_POSIX_SOURCE && BSIZE */ -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? st_blocks ((statbuf).st_size) : 0) -# endif /* !_POSIX_SOURCE && BSIZE */ -#else /* HAVE_STRUCT_STAT_ST_BLOCKS */ -/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ -# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ - ? (statbuf).st_blksize : DEV_BSIZE) -# if defined hpux || defined __hpux__ || defined __hpux -/* HP-UX counts st_blocks in 1024-byte units. - This loses when mixing HP-UX and BSD file systems with NFS. */ -# define ST_NBLOCKSIZE 1024 -# else /* !hpux */ -# if defined _AIX && defined _I386 -/* AIX PS/2 counts st_blocks in 4K units. */ -# define ST_NBLOCKSIZE (4 * 1024) -# else /* not AIX PS/2 */ -# if defined _CRAY -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) -# endif /* _CRAY */ -# endif /* not AIX PS/2 */ -# endif /* !hpux */ -#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ - -#ifndef ST_NBLOCKS -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_blocks : 0) +#ifndef S_IFLNK +#define lstat stat #endif -#ifndef ST_NBLOCKSIZE -# define ST_NBLOCKSIZE 512 +/* Extract or fake data from a `struct stat'. + ST_NBLOCKS: Number of 512-byte blocks in the file + (including indirect blocks). + HP-UX, perhaps uniquely, counts st_blocks in 1024-byte units. + This workaround loses when mixing HP-UX and 4BSD filesystems, though. */ +#ifdef _POSIX_SOURCE +# define ST_NBLOCKS(statp) (((statp)->st_size + 512 - 1) / 512) +#else +# ifndef HAVE_ST_BLOCKS +# define ST_NBLOCKS(statp) (st_blocks ((statp)->st_size)) +# else +# if defined(hpux) || defined(__hpux__) +# define ST_NBLOCKS(statp) ((statp)->st_blocks * 2) +# else +# define ST_NBLOCKS(statp) ((statp)->st_blocks) +# endif +# endif #endif +int lstat (); +int stat (); -#undef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); - -static char *format_date PARAMS((struct timespec ts, int kind)); -static char *ctime_format PARAMS((struct timespec ts)); +static boolean insert_lname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); +static boolean launch P_((struct predicate *pred_ptr)); +static char *format_date P_((time_t when, int kind)); #ifdef DEBUG struct pred_assoc { - PRED_FUNC pred_func; + PFB pred_func; char *pred_name; }; @@ -176,16 +103,13 @@ struct pred_assoc pred_table[] = {pred_and, "and "}, {pred_anewer, "anewer "}, {pred_atime, "atime "}, - {pred_closeparen, ") "}, - {pred_cmin, "cmin "}, + {pred_close, ") "}, + {pred_amin, "cmin "}, {pred_cnewer, "cnewer "}, {pred_comma, ", "}, {pred_ctime, "ctime "}, - {pred_delete, "delete "}, {pred_empty, "empty "}, {pred_exec, "exec "}, - {pred_execdir, "execdir "}, - {pred_executable, "executable "}, {pred_false, "false "}, {pred_fprint, "fprint "}, {pred_fprint0, "fprint0 "}, @@ -200,64 +124,66 @@ struct pred_assoc pred_table[] = {pred_links, "links "}, {pred_lname, "lname "}, {pred_ls, "ls "}, - {pred_mmin, "mmin "}, + {pred_amin, "mmin "}, {pred_mtime, "mtime "}, {pred_name, "name "}, {pred_negate, "not "}, {pred_newer, "newer "}, - {pred_newerXY, "newerXY "}, {pred_nogroup, "nogroup "}, {pred_nouser, "nouser "}, {pred_ok, "ok "}, - {pred_okdir, "okdir "}, - {pred_openparen, "( "}, + {pred_open, "( "}, {pred_or, "or "}, {pred_path, "path "}, {pred_perm, "perm "}, {pred_print, "print "}, {pred_print0, "print0 "}, {pred_prune, "prune "}, - {pred_quit, "quit "}, - {pred_readable, "readable "}, {pred_regex, "regex "}, - {pred_samefile,"samefile "}, {pred_size, "size "}, {pred_true, "true "}, {pred_type, "type "}, {pred_uid, "uid "}, {pred_used, "used "}, {pred_user, "user "}, - {pred_writable, "writable "}, {pred_xtype, "xtype "}, {0, "none "} }; -#endif - -/* Returns ts1 - ts2 */ -static double ts_difference(struct timespec ts1, - struct timespec ts2) + +struct op_assoc { - double d = difftime(ts1.tv_sec, ts2.tv_sec) - + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec)); - return d; -} + short type; + char *type_name; +}; - -static int -compare_ts(struct timespec ts1, - struct timespec ts2) +struct op_assoc type_table[] = { - if ((ts1.tv_sec == ts2.tv_sec) && - (ts1.tv_nsec == ts2.tv_nsec)) - { - return 0; - } - else - { - double diff = ts_difference(ts1, ts2); - return diff < 0.0 ? -1 : +1; - } -} + {NO_TYPE, "no "}, + {PRIMARY_TYPE, "primary "}, + {UNI_OP, "uni_op "}, + {BI_OP, "bi_op "}, + {OPEN_PAREN, "open_paren "}, + {CLOSE_PAREN, "close_paren "}, + {-1, "unknown "} +}; + +struct prec_assoc +{ + short prec; + char *prec_name; +}; + +struct prec_assoc prec_table[] = +{ + {NO_PREC, "no "}, + {COMMA_PREC, "comma "}, + {OR_PREC, "or "}, + {AND_PREC, "and "}, + {NEGATE_PREC, "negate "}, + {MAX_PREC, "max "}, + {-1, "unknown "} +}; +#endif /* DEBUG */ /* Predicate processing routines. @@ -267,212 +193,210 @@ compare_ts(struct timespec ts1, Return true if the file passes this predicate, false if not. */ - -/* pred_timewindow - * - * Returns true if THE_TIME is - * COMP_GT: after the specified time - * COMP_LT: before the specified time - * COMP_EQ: after the specified time but by not more than WINDOW seconds. - */ -static boolean -pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window) +boolean +pred_amin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - switch (pred_ptr->args.reftime.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; - + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; case COMP_LT: - return compare_ts(ts, pred_ptr->args.reftime.ts) < 0; - + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; case COMP_EQ: - { - /* consider "find . -mtime 0". - * - * Here, the origin is exactly 86400 seconds before the start - * of the program (since -daystart was not specified). This - * function will be called with window=86400 and - * pred_ptr->args.reftime.ts as the origin. Hence a file - * created the instant the program starts will show a time - * difference (value of delta) of 86400. Similarly, a file - * created exactly 24h ago would be the newest file which was - * _not_ created today. So, if delta is 0.0, the file - * was not created today. If the delta is 86400, the file - * was created this instant. - */ - double delta = ts_difference(ts, pred_ptr->args.reftime.ts); - return (delta > 0.0 && delta <= window); - } + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; } - assert (0); - abort (); -} - - -boolean -pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) &pathname; - return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60); + return (false); } boolean -pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_and (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left == NULL - || apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) + || (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) { - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } else - return false; + return (false); } boolean -pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_anewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0; + if (stat_buf->st_atime > pred_ptr->args.time) + return (true); + return (false); } boolean -pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_atime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); } boolean -pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_close (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - (void) &pred_ptr; - - return true; + return (true); } boolean -pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_cmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); } boolean -pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_cnewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0; + if (stat_buf->st_ctime > pred_ptr->args.time) + return (true); + return (false); } boolean -pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_comma (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left != NULL) + (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) { - apply_predicate(pathname, stat_buf,pred_ptr->pred_left); + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; } - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } boolean -pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ctime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS); -} - -static boolean -perform_delete(int flags) -{ - return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags); -} - - -boolean -pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pred_ptr; - (void) stat_buf; - if (strcmp (state.rel_pathname, ".")) + switch (pred_ptr->args.info.kind) { - int flags=0; - if (state.have_stat && S_ISDIR(stat_buf->st_mode)) - flags |= AT_REMOVEDIR; - if (perform_delete(flags)) - { - return true; - } - else - { - if (EISDIR == errno) - { - if ((flags & AT_REMOVEDIR) == 0) - { - /* unlink() operation failed because we should have done rmdir(). */ - flags |= AT_REMOVEDIR; - if (perform_delete(flags)) - return true; - } - } - } - error (0, errno, _("cannot delete %s"), - safely_quote_err_filename(0, pathname)); - /* Previously I had believed that having the -delete action - * return false provided the user with control over whether an - * error message is issued. While this is true, the policy of - * not affecting the exit status is contrary to the POSIX - * requirement that diagnostic messages are accompanied by a - * nonzero exit status. While -delete is not a POSIX option and - * we can therefore opt not to follow POSIX in this case, that - * seems somewhat arbitrary and confusing. So, as of - * findutils-4.3.11, we also set the exit status in this case. - */ - state.exit_status = 1; - return false; - } - else - { - /* nothing to do. */ - return true; + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; } + return (false); } boolean -pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_empty (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - if (S_ISDIR (stat_buf->st_mode)) { - int fd; DIR *d; struct dirent *dp; boolean empty = true; errno = 0; - if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - )) < 0) - { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; - } - d = fdopendir (fd); + d = opendir (rel_pathname); if (d == NULL) { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } for (dp = readdir (d); dp; dp = readdir (d)) { @@ -486,9 +410,9 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } if (CLOSEDIR (d)) { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } return (empty); } @@ -498,288 +422,162 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ return (false); } -static boolean -new_impl_pred_exec (int dirfd, const char *pathname, - struct stat *stat_buf, - struct predicate *pred_ptr, - const char *prefix, size_t pfxlen) +boolean +pred_exec (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct exec_val *execp = &pred_ptr->args.exec_vec; - size_t len = strlen(pathname); - - (void) stat_buf; - execp->dirfd = dirfd; - if (execp->multiple) - { - /* Push the argument onto the current list. - * The command may or may not be run at this point, - * depending on the command line length limits. - */ - bc_push_arg(&execp->ctl, - &execp->state, - pathname, len+1, - prefix, pfxlen, - 0); - - /* remember that there are pending execdirs. */ - state.execdirs_outstanding = true; - - /* POSIX: If the primary expression is punctuated by a plus - * sign, the primary shall always evaluate as true - */ - return true; - } - else - { - int i; + int i; + int path_pos; + struct exec_val *execp; /* Pointer for efficiency. */ - for (i=0; i<execp->num_args; ++i) - { - bc_do_insert(&execp->ctl, - &execp->state, - execp->replace_vec[i], - strlen(execp->replace_vec[i]), - prefix, pfxlen, - pathname, len, - 0); - } + execp = &pred_ptr->args.exec_vec; - /* Actually invoke the command. */ - return execp->ctl.exec_callback(&execp->ctl, - &execp->state); + /* Replace "{}" with the real path in each affected arg. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + { + register char *from, *to; + + i = execp->paths[path_pos].offset; + execp->vec[i] = + xmalloc (strlen (execp->paths[path_pos].origarg) + 1 + + (strlen (pathname) - 2) * execp->paths[path_pos].count); + for (from = execp->paths[path_pos].origarg, to = execp->vec[i]; *from; ) + if (from[0] == '{' && from[1] == '}') + { + to = stpcpy (to, pathname); + from += 2; + } + else + *to++ = *from++; + *to = *from; /* Copy null. */ } -} + i = launch (pred_ptr); -boolean -pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - return new_impl_pred_exec(get_start_dirfd(), - pathname, stat_buf, pred_ptr, NULL, 0); -} + /* Free the temporary args. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + free (execp->vec[execp->paths[path_pos].offset]); -boolean -pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; - (void) &pathname; - return new_impl_pred_exec (get_current_dirfd(), - state.rel_pathname, stat_buf, pred_ptr, - prefix, (prefix ? 2 : 0)); + return (i); } boolean -pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_false (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - (void) &pred_ptr; - - return (false); } boolean -pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - FILE * stream = pred_ptr->args.printf_vec.stream; - list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf, - options.start_time.tv_sec, - options.output_block_size, - pred_ptr->literal_control_chars, stream); - return true; -} - -boolean -pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - - print_quoted(pred_ptr->args.printf_vec.stream, - pred_ptr->args.printf_vec.quote_opts, - pred_ptr->args.printf_vec.dest_is_tty, - "%s\n", - pathname); - return true; + list_file (pathname, rel_pathname, stat_buf, pred_ptr->args.stream); + return (true); } boolean -pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fprint (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - FILE * fp = pred_ptr->args.printf_vec.stream; - - (void) &stat_buf; - - fputs (pathname, fp); - putc (0, fp); - return true; -} - - - -static char* -mode_to_filetype(mode_t m) -{ -#define HANDLE_TYPE(t,letter) if (m==t) { return letter; } -#ifdef S_IFREG - HANDLE_TYPE(S_IFREG, "f"); /* regular file */ -#endif -#ifdef S_IFDIR - HANDLE_TYPE(S_IFDIR, "d"); /* directory */ -#endif -#ifdef S_IFLNK - HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */ -#endif -#ifdef S_IFSOCK - HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */ -#endif -#ifdef S_IFBLK - HANDLE_TYPE(S_IFBLK, "b"); /* block device */ -#endif -#ifdef S_IFCHR - HANDLE_TYPE(S_IFCHR, "c"); /* character device */ -#endif -#ifdef S_IFIFO - HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */ -#endif -#ifdef S_IFDOOR - HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */ -#endif - return "U"; /* Unknown */ -} - -static double -file_sparseness(const struct stat *p) -{ -#if defined HAVE_STRUCT_STAT_ST_BLOCKS - if (0 == p->st_size) - { - if (0 == p->st_blocks) - return 1.0; - else - return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL; - } - else - { - double blklen = file_blocksize(p) * (double)p->st_blocks; - return blklen / p->st_size; - } -#else - return 1.0; -#endif + fputs (pathname, pred_ptr->args.stream); + putc ('\n', pred_ptr->args.stream); + return (true); } - - -static void -checked_fprintf(struct format_val *dest, const char *fmt, ...) -{ - int rv; - va_list ap; - - va_start(ap, fmt); - rv = vfprintf(dest->stream, fmt, ap); - if (rv < 0) - nonfatal_file_error(dest->filename); -} - - -static void -checked_print_quoted (struct format_val *dest, - const char *format, const char *s) +boolean +pred_fprint0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty, - format, s); - if (rv < 0) - nonfatal_file_error(dest->filename); + fputs (pathname, pred_ptr->args.stream); + putc (0, pred_ptr->args.stream); + return (true); } - -static void -checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest) +boolean +pred_fprintf (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - int items_written = fwrite(p, siz, nmemb, dest->stream); - if (items_written < nmemb) - nonfatal_file_error(dest->filename); -} + FILE *fp = pred_ptr->args.printf_vec.stream; + struct segment *segment; + char *cp; -static void -checked_fflush(struct format_val *dest) -{ - if (0 != fflush(dest->stream)) + for (segment = pred_ptr->args.printf_vec.segment; segment; + segment = segment->next) { - nonfatal_file_error(dest->filename); - } -} + if (segment->kind & 0xff00) /* Component of date. */ + { + time_t t; -static void -do_fprintf(struct format_val *dest, - struct segment *segment, - const char *pathname, - const struct stat *stat_buf) -{ - char hbuf[LONGEST_HUMAN_READABLE + 1]; - const char *cp; + switch (segment->kind & 0xff) + { + case 'A': + t = stat_buf->st_atime; + break; + case 'C': + t = stat_buf->st_ctime; + break; + case 'T': + t = stat_buf->st_mtime; + break; + default: + abort (); + } + fprintf (fp, segment->text, + format_date (t, (segment->kind >> 8) & 0xff)); + continue; + } - switch (segment->segkind) - { - case KIND_PLAIN: /* Plain text string (no % conversion). */ - /* trusted */ - checked_fwrite(segment->text, 1, segment->text_len, dest); - break; - - case KIND_STOP: /* Terminate argument and flush output. */ - /* trusted */ - checked_fwrite(segment->text, 1, segment->text_len, dest); - checked_fflush(dest); - break; - - case KIND_FORMAT: - switch (segment->format_char[0]) + switch (segment->kind) { + case KIND_PLAIN: /* Plain text string (no % conversion). */ + fwrite (segment->text, 1, segment->text_len, fp); + break; + case KIND_STOP: /* Terminate argument and flush output. */ + fwrite (segment->text, 1, segment->text_len, fp); + fflush (fp); + return (true); case 'a': /* atime in `ctime' format. */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf))); + cp = ctime (&stat_buf->st_atime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; case 'b': /* size in 512-byte blocks */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), - hbuf, human_ceiling, - ST_NBLOCKSIZE, 512)); + fprintf (fp, segment->text, ST_NBLOCKS (stat_buf)); break; case 'c': /* ctime in `ctime' format */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf))); + cp = ctime (&stat_buf->st_ctime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; case 'd': /* depth in search tree */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, state.curdepth); - break; - case 'D': /* Device on which file exists (stat.st_dev) */ - /* trusted */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_dev, hbuf, - human_ceiling, 1, 1)); + fprintf (fp, segment->text, curdepth); break; - case 'f': /* base name of path */ - /* sanitised */ - { - char *base = base_name (pathname); - checked_print_quoted (dest, segment->text, base); - free (base); - } + case 'f': /* basename of path */ + cp = strrchr (pathname, '/'); + if (cp) + cp++; + else + cp = pathname; + fprintf (fp, segment->text, cp); break; - case 'F': /* file system type */ - /* trusted */ - checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname)); + case 'F': /* filesystem type */ + fprintf (fp, segment->text, + filesystem_type (pathname, rel_pathname, stat_buf)); break; case 'g': /* group name */ - /* trusted */ - /* (well, the actual group is selected by the user but - * its name was selected by the system administrator) - */ { struct group *g; @@ -787,158 +585,77 @@ do_fprintf(struct format_val *dest, if (g) { segment->text[segment->text_len] = 's'; - checked_fprintf (dest, segment->text, g->gr_name); + fprintf (fp, segment->text, g->gr_name); break; } - else - { - /* Do nothing. */ - /*FALLTHROUGH*/ - } + /* else fallthru */ } - /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/ - case 'G': /* GID number */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_gid, hbuf, - human_ceiling, 1, 1)); + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_gid); break; case 'h': /* leading directories part of path */ - /* sanitised */ { + char cc; + cp = strrchr (pathname, '/'); if (cp == NULL) /* No leading directories. */ - { - /* If there is no slash in the pathname, we still - * print the string because it contains characters - * other than just '%s'. The %h expands to ".". - */ - checked_print_quoted (dest, segment->text, "."); - } - else - { - char *s = strdup(pathname); - s[cp - pathname] = 0; - checked_print_quoted (dest, segment->text, s); - free(s); - } + break; + cc = *cp; + *cp = '\0'; + fprintf (fp, segment->text, pathname); + *cp = cc; + break; } - break; - case 'H': /* ARGV element file was found under */ - /* trusted */ { - char *s = xmalloc(state.starting_path_length+1); - memcpy(s, pathname, state.starting_path_length); - s[state.starting_path_length] = 0; - checked_fprintf (dest, segment->text, s); - free(s); - } - break; + char cc = pathname[path_length]; + pathname[path_length] = '\0'; + fprintf (fp, segment->text, pathname); + pathname[path_length] = cc; + break; + } case 'i': /* inode number */ - /* UNTRUSTED, but not exploitable I think */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_ino, hbuf, - human_ceiling, - 1, 1)); + fprintf (fp, segment->text, stat_buf->st_ino); break; case 'k': /* size in 1K blocks */ - /* UNTRUSTED, but not exploitable I think */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), - hbuf, human_ceiling, - ST_NBLOCKSIZE, 1024)); + fprintf (fp, segment->text, (ST_NBLOCKS (stat_buf) + 1) / 2); break; case 'l': /* object of symlink */ - /* sanitised */ #ifdef S_ISLNK { char *linkname = 0; if (S_ISLNK (stat_buf->st_mode)) { - linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); + linkname = get_link_name (pathname, rel_pathname); if (linkname == 0) - state.exit_status = 1; + exit_status = 1; } if (linkname) { - checked_print_quoted (dest, segment->text, linkname); + fprintf (fp, segment->text, linkname); free (linkname); } else - { - /* We still need to honour the field width etc., so this is - * not a no-op. - */ - checked_print_quoted (dest, segment->text, ""); - } + fprintf (fp, segment->text, ""); } #endif /* S_ISLNK */ break; - - case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */ - /* UNTRUSTED, probably unexploitable */ - { - char modestring[16] ; - filemodestring (stat_buf, modestring); - modestring[10] = '\0'; - checked_fprintf (dest, segment->text, modestring); - } - break; - case 'm': /* mode as octal number (perms only) */ - /* UNTRUSTED, probably unexploitable */ - { - /* Output the mode portably using the traditional numbers, - even if the host unwisely uses some other numbering - scheme. But help the compiler in the common case where - the host uses the traditional numbering scheme. */ - mode_t m = stat_buf->st_mode; - boolean traditional_numbering_scheme = - (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000 - && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100 - && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010 - && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001); - checked_fprintf (dest, segment->text, - (traditional_numbering_scheme - ? m & MODE_ALL - : ((m & S_ISUID ? 04000 : 0) - | (m & S_ISGID ? 02000 : 0) - | (m & S_ISVTX ? 01000 : 0) - | (m & S_IRUSR ? 00400 : 0) - | (m & S_IWUSR ? 00200 : 0) - | (m & S_IXUSR ? 00100 : 0) - | (m & S_IRGRP ? 00040 : 0) - | (m & S_IWGRP ? 00020 : 0) - | (m & S_IXGRP ? 00010 : 0) - | (m & S_IROTH ? 00004 : 0) - | (m & S_IWOTH ? 00002 : 0) - | (m & S_IXOTH ? 00001 : 0)))); - } + fprintf (fp, segment->text, stat_buf->st_mode & 07777); break; - case 'n': /* number of links */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_nlink, - hbuf, - human_ceiling, - 1, 1)); + fprintf (fp, segment->text, stat_buf->st_nlink); break; - case 'p': /* pathname */ - /* sanitised */ - checked_print_quoted (dest, segment->text, pathname); + fprintf (fp, segment->text, pathname); break; - case 'P': /* pathname with ARGV element stripped */ - /* sanitised */ - if (state.curdepth > 0) + if (curdepth) { - cp = pathname + state.starting_path_length; + cp = pathname + path_length; if (*cp == '/') /* Move past the slash between the ARGV element and the rest of the pathname. But if the ARGV element @@ -947,36 +664,18 @@ do_fprintf(struct format_val *dest, cp++; } else - { - cp = ""; - } - checked_print_quoted (dest, segment->text, cp); + cp = ""; + fprintf (fp, segment->text, cp); break; - case 's': /* size in bytes */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_size, - hbuf, human_ceiling, 1, 1)); + fprintf (fp, segment->text, stat_buf->st_size); break; - - case 'S': /* sparseness */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, file_sparseness(stat_buf));; - break; - case 't': /* mtime in `ctime' format */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - ctime_format (get_stat_mtime(stat_buf))); + cp = ctime (&stat_buf->st_mtime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; - case 'u': /* user name */ - /* trusted */ - /* (well, the actual user is selected by the user on systems - * where chown is not restricted, but the user name was - * selected by the system administrator) - */ { struct passwd *p; @@ -984,177 +683,50 @@ do_fprintf(struct format_val *dest, if (p) { segment->text[segment->text_len] = 's'; - checked_fprintf (dest, segment->text, p->pw_name); + fprintf (fp, segment->text, p->pw_name); break; } /* else fallthru */ } - /* FALLTHROUGH*/ /* .. to case U */ - case 'U': /* UID number */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_uid, hbuf, - human_ceiling, 1, 1)); - break; - - /* %Y: type of file system entry like `ls -l`: - * (d,-,l,s,p,b,c,n) n=nonexistent(symlink) - */ - case 'Y': /* in case of symlink */ - /* trusted */ - { -#ifdef S_ISLNK - if (S_ISLNK (stat_buf->st_mode)) - { - struct stat sbuf; - /* If we would normally follow links, do not do so. - * If we would normally not follow links, do so. - */ - if ((following_links() ? lstat : stat) - (state.rel_pathname, &sbuf) != 0) - { - if ( errno == ENOENT ) - { - checked_fprintf (dest, segment->text, "N"); - break; - } - else if ( errno == ELOOP ) - { - checked_fprintf (dest, segment->text, "L"); - break; - } - else - { - checked_fprintf (dest, segment->text, "?"); - error (0, errno, "%s", - safely_quote_err_filename(0, pathname)); - /* exit_status = 1; - return ; */ - break; - } - } - checked_fprintf (dest, segment->text, - mode_to_filetype(sbuf.st_mode & S_IFMT)); - } -#endif /* S_ISLNK */ - else - { - checked_fprintf (dest, segment->text, - mode_to_filetype(stat_buf->st_mode & S_IFMT)); - } - } - break; - - case 'y': - /* trusted */ - { - checked_fprintf (dest, segment->text, - mode_to_filetype(stat_buf->st_mode & S_IFMT)); - } + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_uid); break; } - /* end of KIND_FORMAT case */ - break; - } -} - -boolean -pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - struct format_val *dest = &pred_ptr->args.printf_vec; - struct segment *segment; - - for (segment = dest->segment; segment; segment = segment->next) - { - if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */ - { - struct timespec ts; - int valid = 0; - - switch (segment->format_char[0]) - { - case 'A': - ts = get_stat_atime(stat_buf); - valid = 1; - break; - case 'B': - ts = get_stat_birthtime(stat_buf); - if ('@' == segment->format_char[1]) - valid = 1; - else - valid = (ts.tv_nsec >= 0); - break; - case 'C': - ts = get_stat_ctime(stat_buf); - valid = 1; - break; - case 'T': - ts = get_stat_mtime(stat_buf); - valid = 1; - break; - default: - assert (0); - abort (); - } - /* We trust the output of format_date not to contain - * nasty characters, though the value of the date - * is itself untrusted data. - */ - if (valid) - { - /* trusted */ - checked_fprintf (dest, segment->text, - format_date (ts, segment->format_char[1])); - } - else - { - /* The specified timestamp is not available, output - * nothing for the timestamp, but use the rest (so that - * for example find foo -printf '[%Bs] %p\n' can print - * "[] foo"). - */ - /* trusted */ - checked_fprintf (dest, segment->text, ""); - } - } - else - { - /* Print a segment which is not a date. */ - do_fprintf(dest, segment, pathname, stat_buf); - } } - return true; + return (true); } boolean -pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fstype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0) - return true; - else - return false; + if (strcmp (filesystem_type (pathname, rel_pathname, stat_buf), + pred_ptr->args.str) == 0) + return (true); + return (false); } boolean -pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_gid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid == pred_ptr->args.info.l_val) return (true); break; } @@ -1162,10 +734,11 @@ pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_pt } boolean -pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_group (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - if (pred_ptr->args.gid == stat_buf->st_gid) return (true); else @@ -1173,60 +746,46 @@ pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ilname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return match_lname (pathname, stat_buf, pred_ptr, true); -} - -/* Common code between -name, -iname. PATHNAME is being visited, STR - is name to compare basename against, and FLAGS are passed to - fnmatch. Recall that 'find / -name /' is one of the few times where a '/' - in the -name must actually find something. */ -static boolean -pred_name_common (const char *pathname, const char *str, int flags) -{ - boolean b; - /* We used to use last_component() here, but that would not allow us to modify the - * input string, which is const. We could optimise by duplicating the string only - * if we need to modify it, and I'll do that if there is a measurable - * performance difference on a machine built after 1990... - */ - char *base = base_name (pathname); - /* remove trailing slashes, but leave "/" or "//foo" unchanged. */ - strip_trailing_slashes(base); - - /* FNM_PERIOD is not used here because POSIX requires that it not be. - * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html - */ - b = fnmatch (str, base, flags) == 0; - free (base); - return b; + return insert_lname (pathname, stat_buf, pred_ptr, true); } boolean -pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_iname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD); + char *base; + + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD | FNM_CASEFOLD) == 0) + return (true); + return (false); } boolean -pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_inum (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino == pred_ptr->args.info.l_val) return (true); break; } @@ -1234,32 +793,34 @@ pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ipath (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0) return (true); return (false); } boolean -pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_links (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink == pred_ptr->args.info.l_val) return (true); break; } @@ -1267,19 +828,26 @@ pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_lname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return match_lname (pathname, stat_buf, pred_ptr, false); + return insert_lname (pathname, stat_buf, pred_ptr, false); } static boolean -match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case) +insert_lname (pathname, stat_buf, pred_ptr, ignore_case) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; + boolean ignore_case; { boolean ret = false; #ifdef S_ISLNK if (S_ISLNK (stat_buf->st_mode)) { - char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); + char *linkname = get_link_name (pathname, rel_pathname); if (linkname) { if (fnmatch (pred_ptr->args.str, linkname, @@ -1289,103 +857,122 @@ match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred } } #endif /* S_ISLNK */ - return ret; -} - -boolean -pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - return pred_fls(pathname, stat_buf, pred_ptr); + return (ret); } boolean -pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60); + list_file (pathname, rel_pathname, stat_buf, stdout); + return (true); } boolean -pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_mmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); } boolean -pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_mtime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - return pred_name_common (pathname, pred_ptr->args.str, 0); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); } boolean -pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_name (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right); -} + char *base; -boolean -pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0; + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD) == 0) + return (true); + return (false); } boolean -pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_negate (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct timespec ts; - boolean collected = false; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - - switch (pred_ptr->args.reftime.xval) + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) { - case XVAL_TIME: - assert (pred_ptr->args.reftime.xval != XVAL_TIME); - return false; - - case XVAL_ATIME: - ts = get_stat_atime(stat_buf); - collected = true; - break; - - case XVAL_BIRTHTIME: - ts = get_stat_birthtime(stat_buf); - collected = true; - if (ts.tv_nsec < 0); + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) { - /* XXX: Cannot determine birth time. Warn once. */ - error(0, 0, _("Warning: cannot determine birth time of file %s"), - safely_quote_err_filename(0, pathname)); - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } - break; - - case XVAL_CTIME: - ts = get_stat_ctime(stat_buf); - collected = true; - break; - - case XVAL_MTIME: - ts = get_stat_mtime(stat_buf); - collected = true; - break; + have_stat = true; } - - assert (collected); - return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; + return (!(*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } boolean -pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_newer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (stat_buf->st_mtime > pred_ptr->args.time) + return (true); + return (false); +} + +boolean +pred_nogroup (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - #ifdef CACHE_IDS extern char *gid_unused; @@ -1396,249 +983,157 @@ pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pre } boolean -pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_nouser (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { #ifdef CACHE_IDS extern char *uid_unused; -#endif - - (void) pathname; - (void) pred_ptr; - -#ifdef CACHE_IDS + return uid_unused[(unsigned) stat_buf->st_uid]; #else return getpwuid (stat_buf->st_uid) == NULL; #endif } - -static boolean -is_ok(const char *program, const char *arg) +boolean +pred_ok (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { + int i, yes; + fflush (stdout); - /* The draft open standard requires that, in the POSIX locale, - the last non-blank character of this prompt be '?'. - The exact format is not specified. - This standard does not have requirements for locales other than POSIX - */ - /* XXX: printing UNTRUSTED data here. */ - fprintf (stderr, _("< %s ... %s > ? "), program, arg); + fprintf (stderr, "< %s ... %s > ? ", + pred_ptr->args.exec_vec.vec[0], pathname); fflush (stderr); - return yesno(); -} - -boolean -pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_start_dirfd(), - pathname, stat_buf, pred_ptr, NULL, 0); - else - return false; -} - -boolean -pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; - if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_current_dirfd(), - state.rel_pathname, stat_buf, pred_ptr, - prefix, (prefix ? 2 : 0)); - else - return false; + i = getchar (); + yes = (i == 'y' || i == 'Y'); + while (i != EOF && i != '\n') + i = getchar (); + if (!yes) + return (false); + return pred_exec (pathname, stat_buf, pred_ptr); } boolean -pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_open (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - return true; + return (true); } boolean -pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_or (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left == NULL - || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) + || !(*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) { - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } else - return true; + return (true); } boolean -pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_path (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; if (fnmatch (pred_ptr->args.str, pathname, 0) == 0) return (true); return (false); } boolean -pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_perm (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - mode_t mode = stat_buf->st_mode; - mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0]; - (void) pathname; - switch (pred_ptr->args.perm.kind) + if (pred_ptr->args.perm & 010000) { - case PERM_AT_LEAST: - return (mode & perm_val) == perm_val; - break; - - case PERM_ANY: - /* True if any of the bits set in the mask are also set in the file's mode. - * - * - * Otherwise, if onum is prefixed by a hyphen, the primary shall - * evaluate as true if at least all of the bits specified in - * onum that are also set in the octal mask 07777 are set. - * - * Eric Blake's interpretation is that the mode argument is zero, - - */ - if (0 == perm_val) - return true; /* Savannah bug 14748; we used to return false */ - else - return (mode & perm_val) != 0; - break; - - case PERM_EXACT: - return (mode & MODE_ALL) == perm_val; - break; - - default: - abort (); - break; + /* Magic flag set in parse_perm: + true if at least the given bits are set. */ + if ((stat_buf->st_mode & 07777 & pred_ptr->args.perm) + == (pred_ptr->args.perm & 07777)) + return (true); } -} - - -struct access_check_args -{ - const char *filename; - int access_type; - int cb_errno; -}; - - -static int -access_callback(void *context) -{ - int rv; - struct access_check_args *args = context; - if ((rv = access(args->filename, args->access_type)) < 0) - args->cb_errno = errno; - return rv; -} - -static int -can_access(int access_type) -{ - struct access_check_args args; - args.filename = state.rel_pathname; - args.access_type = access_type; - args.cb_errno = 0; - return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args); -} - - -boolean -pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(X_OK); -} - -boolean -pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(R_OK); -} - -boolean -pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(W_OK); -} - -boolean -pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) stat_buf; - (void) pred_ptr; - - print_quoted(pred_ptr->args.printf_vec.stream, - pred_ptr->args.printf_vec.quote_opts, - pred_ptr->args.printf_vec.dest_is_tty, - "%s\n", pathname); - return true; + else if (pred_ptr->args.perm & 020000) + { + /* Magic flag set in parse_perm: + true if any of the given bits are set. */ + if ((stat_buf->st_mode & 07777) & pred_ptr->args.perm) + return (true); + } + else + { + /* True if exactly the given bits are set. */ + if ((stat_buf->st_mode & 07777) == pred_ptr->args.perm) + return (true); + } + return (false); } boolean -pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_print (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return pred_fprint0(pathname, stat_buf, pred_ptr); + puts (pathname); + return (true); } boolean -pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_print0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - - if (options.do_dir_first == true && /* no effect with -depth */ - stat_buf != NULL && - S_ISDIR(stat_buf->st_mode)) - state.stop_at_current_level = true; - - /* findutils used to return options.do_dir_first here, so that -prune - * returns true only if -depth is not in effect. But POSIX requires - * that -prune always evaluate as true. - */ - return true; + fputs (pathname, stdout); + putc (0, stdout); + return (true); } boolean -pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_prune (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - /* Run any cleanups. This includes executing any command lines - * we have partly built but not executed. - */ - cleanup(); - - /* Since -exec and friends don't leave child processes running in the - * background, there is no need to wait for them here. - */ - exit(state.exit_status); /* 0 for success, etc. */ + stop_at_current_level = true; + return (do_dir_first); /* This is what SunOS find seems to do. */ } boolean -pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_regex (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { int len = strlen (pathname); -(void) stat_buf; if (re_match (pred_ptr->args.regex, pathname, len, 0, (struct re_registers *) NULL) == len) return (true); @@ -1646,13 +1141,15 @@ pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_size (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - uintmax_t f_val; + unsigned long f_val; - (void) pathname; - f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize) - + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0)); + f_val = (stat_buf->st_size + pred_ptr->args.size.blocksize - 1) + / pred_ptr->args.size.blocksize; switch (pred_ptr->args.size.kind) { case COMP_GT: @@ -1672,57 +1169,22 @@ pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - /* Potential optimisation: because of the loop protection, we always - * know the device of the current directory, hence the device number - * of the file we're currently considering. If -L is not in effect, - * and the device number of the file we're looking for is not the - * same as the device number of the current directory, this - * predicate cannot return true. Hence there would be no need to - * stat the file we're looking at. - */ - (void) pathname; - - /* We will often still have an fd open on the file under consideration, - * but that's just to ensure inode number stability by maintaining - * a reference to it; we don't need the file for anything else. - */ - return stat_buf->st_ino == pred_ptr->args.samefileid.ino - && stat_buf->st_dev == pred_ptr->args.samefileid.dev; -} - -boolean -pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_true (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - return true; + return (true); } boolean -pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_type (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - mode_t mode; - mode_t type = pred_ptr->args.type; - - assert (state.have_type); - - if (0 == state.type) - { - /* This can sometimes happen with broken NFS servers. - * See Savannah bug #16378. - */ - return false; - } - - (void) pathname; - - if (state.have_stat) - mode = stat_buf->st_mode; - else - mode = state.type; + unsigned long mode = stat_buf->st_mode; + unsigned long type = pred_ptr->args.type; #ifndef S_IFMT /* POSIX system; check `mode' the slow way. */ @@ -1739,9 +1201,6 @@ pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_p #ifdef S_IFSOCK || (S_ISSOCK (mode) && type == S_IFSOCK) #endif -#ifdef S_IFDOOR - || (S_ISDOOR (mode) && type == S_IFDOOR) -#endif ) #else /* S_IFMT */ /* Unix system; check `mode' the fast way. */ @@ -1753,21 +1212,23 @@ pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_uid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid == pred_ptr->args.info.l_val) return (true); break; } @@ -1775,29 +1236,39 @@ pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_pt } boolean -pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_used (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct timespec delta, at, ct; + time_t delta; - (void) pathname; - - /* TODO: this needs to be retested carefully (manually, if necessary) */ - at = get_stat_atime(stat_buf); - ct = get_stat_ctime(stat_buf); - delta.tv_sec = at.tv_sec - ct.tv_sec; - delta.tv_nsec = at.tv_nsec - ct.tv_nsec; - if (delta.tv_nsec < 0) + delta = stat_buf->st_atime - stat_buf->st_ctime; /* Use difftime? */ + switch (pred_ptr->args.info.kind) { - delta.tv_nsec += 1000000000; - delta.tv_sec -= 1; + case COMP_GT: + if (delta > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (delta < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((delta >= (time_t) pred_ptr->args.info.l_val) + && (delta < (time_t) pred_ptr->args.info.l_val + DAYSECS)) + return (true); + break; } - return pred_timewindow(delta, pred_ptr, DAYSECS); + return (false); } boolean -pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_user (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; if (pred_ptr->args.uid == stat_buf->st_uid) return (true); else @@ -1805,40 +1276,24 @@ pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_xtype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */ - int (*ystat) (const char*, struct stat *p); + struct stat sbuf; + int (*ystat) (); - /* If we would normally stat the link itself, stat the target instead. - * If we would normally follow the link, stat the link itself instead. - */ - if (following_links()) - ystat = optionp_stat; - else - ystat = optionl_stat; - - set_stat_placeholders(&sbuf); - if ((*ystat) (state.rel_pathname, &sbuf) != 0) + ystat = xstat == lstat ? stat : lstat; + if ((*ystat) (rel_pathname, &sbuf) != 0) { - if (following_links() && errno == ENOENT) - { - /* If we failed to follow the symlink, - * fall back on looking at the symlink itself. - */ - /* Mimic behavior of ls -lL. */ - return (pred_type (pathname, stat_buf, pred_ptr)); - } - else - { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - } - return false; + if (ystat == stat && errno == ENOENT) + /* Mimic behavior of ls -lL. */ + return (pred_type (pathname, stat_buf, pred_ptr)); + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } - /* Now that we have our stat() information, query it in the same - * way that -type does. - */ return (pred_type (pathname, &sbuf, pred_ptr)); } @@ -1860,84 +1315,21 @@ pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ zero, and the exit arg (status high) is 0. Otherwise return false, possibly printing an error message. */ - static boolean -prep_child_for_exec (boolean close_stdin, int dirfd) -{ - boolean ok = true; - if (close_stdin) - { - const char inputfile[] = "/dev/null"; - - if (close(0) < 0) - { - error(0, errno, _("Cannot close standard input")); - ok = false; - } - else - { - if (open(inputfile, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ) < 0) - { - /* This is not entirely fatal, since - * executing the child with a closed - * stdin is almost as good as executing it - * with its stdin attached to /dev/null. - */ - error (0, errno, "%s", safely_quote_err_filename(0, inputfile)); - /* do not set ok=false, it is OK to continue anyway. */ - } - } - } - - /* Even if DebugSearch is set, don't announce our change of - * directory, since we're not going to emit a subsequent - * announcement of a call to stat() anyway, as we're about to exec - * something. - */ - if (dirfd != AT_FDCWD) - { - assert (dirfd >= 0); - if (0 != fchdir(dirfd)) - { - /* If we cannot execute our command in the correct directory, - * we should not execute it at all. - */ - error(0, errno, _("Failed to change directory")); - ok = false; - } - } - return ok; -} - - - -int -launch (const struct buildcmd_control *ctl, - struct buildcmd_state *buildstate) +launch (pred_ptr) + struct predicate *pred_ptr; { - int wait_status; - pid_t child_pid; + int status; + pid_t wait_ret, child_pid; + struct exec_val *execp; /* Pointer for efficiency. */ static int first_time = 1; - const struct exec_val *execp = buildstate->usercontext; - if (!execp->use_current_dir) - { - assert (starting_desc >= 0); - assert (execp->dirfd == starting_desc); - } - - - /* Null terminate the arg list. */ - bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false); - + execp = &pred_ptr->args.exec_vec; + /* Make sure output of command doesn't get mixed with find output. */ fflush (stdout); fflush (stderr); - + /* Make sure to listen for the kids. */ if (first_time) { @@ -1947,275 +1339,150 @@ launch (const struct buildcmd_control *ctl, child_pid = fork (); if (child_pid == -1) - error (1, errno, _("cannot fork")); + error (1, errno, "cannot fork"); if (child_pid == 0) { - /* We are the child. */ - assert (starting_desc >= 0); - if (!prep_child_for_exec(execp->close_stdin, execp->dirfd)) + /* We be the child. */ +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) < 0) { - _exit(1); + error (0, errno, "%s", starting_dir); + _exit (1); } - - execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); - error (0, errno, "%s", - safely_quote_err_filename(0, buildstate->cmd_argv[0])); +#else + if (fchdir (starting_desc) < 0) + { + error (0, errno, "cannot return to starting directory"); + _exit (1); + } +#endif + execvp (execp->vec[0], execp->vec); + error (0, errno, "%s", execp->vec[0]); _exit (1); } - - /* In parent; set up for next time. */ - bc_clear_args(ctl, buildstate); - - - while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) + wait_ret = wait (&status); + if (wait_ret == -1) { - if (errno != EINTR) - { - error (0, errno, _("error waiting for %s"), - safely_quote_err_filename(0, buildstate->cmd_argv[0])); - state.exit_status = 1; - return 0; /* FAIL */ - } + error (0, errno, "error waiting for %s", execp->vec[0]); + exit_status = 1; + return (false); } - - if (WIFSIGNALED (wait_status)) + if (wait_ret != child_pid) { - error (0, 0, _("%s terminated by signal %d"), - quotearg_n_style(0, options.err_quoting_style, - buildstate->cmd_argv[0]), - WTERMSIG (wait_status)); - - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - - return 1; /* OK */ + error (0, 0, "wait got pid %d, expected pid %d", wait_ret, child_pid); + exit_status = 1; + return (false); } - - if (0 == WEXITSTATUS (wait_status)) + if (WIFSTOPPED (status)) { - return 1; /* OK */ + error (0, 0, "%s stopped by signal %d", + execp->vec[0], WSTOPSIG (status)); + exit_status = 1; + return (false); } - else + if (WIFSIGNALED (status)) { - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - return 0; /* FAIL */ + error (0, 0, "%s terminated by signal %d", + execp->vec[0], WTERMSIG (status)); + exit_status = 1; + return (false); } - + return (!WEXITSTATUS (status)); } - /* Return a static string formatting the time WHEN according to the - * strftime format character KIND. - * - * This function contains a number of assertions. These look like - * runtime checks of the results of computations, which would be a - * problem since external events should not be tested for with - * "assert" (instead you should use "if"). However, they are not - * really runtime checks. The assertions actually exist to verify - * that the various buffers are correctly sized. - */ + strftime format character KIND. */ + static char * -format_date (struct timespec ts, int kind) -{ - /* In theory, we use an extra 10 characters for 9 digits of - * nanoseconds and 1 for the decimal point. However, the real - * world is more complex than that. - * - * For example, some systems return junk in the tv_nsec part of - * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel - * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE - * runtime and examining files on an msdos filesytem. So for that - * reason we set NS_BUF_LEN to 32, which is simply "long enough" as - * opposed to "exactly the right size". Note that the behaviour of - * NetBSD appears to be a result of the use of uninitialised data, - * as it's not 100% reproducible (more like 25%). - */ - enum { - NS_BUF_LEN = 32, - DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/ - }; - static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS, - MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))]; - char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/ - int charsprinted, need_ns_suffix; - struct tm *tm; - char fmt[6]; - - /* human_readable() assumes we pass a buffer which is at least as - * long as LONGEST_HUMAN_READABLE. We use an assertion here to - * ensure that no nasty unsigned overflow happend in our calculation - * of the size of buf. Do the assertion here rather than in the - * code for %@ so that we find the problem quickly if it exists. If - * you want to submit a patch to move this into the if statement, go - * ahead, I'll apply it. But include performance timings - * demonstrating that the performance difference is actually - * measurable. - */ - verify (sizeof(buf) >= LONGEST_HUMAN_READABLE); - - charsprinted = 0; - need_ns_suffix = 0; - - /* Format the main part of the time. */ - if (kind == '+') +format_date (when, kind) + time_t when; + int kind; +{ + static char fmt[3]; + static char buf[64]; /* More than enough space. */ + + if (kind == '@') { - strcpy (fmt, "%F+%T"); - need_ns_suffix = 1; + sprintf (buf, "%ld", when); + return (buf); } else { fmt[0] = '%'; fmt[1] = kind; fmt[2] = '\0'; - - /* %a, %c, and %t are handled in ctime_format() */ - switch (kind) - { - case 'S': - case 'T': - case 'X': - case '@': - need_ns_suffix = 1; - break; - default: - need_ns_suffix = 0; - break; - } + if (strftime (buf, sizeof (buf), fmt, localtime (&when))) + return (buf); } + return ""; +} + +#ifdef DEBUG +/* Return a pointer to the string representation of + the predicate function PRED_FUNC. */ - if (need_ns_suffix) - { - /* Format the nanoseconds part. Leave a trailing zero to - * discourage people from writing scripts which extract the - * fractional part of the timestamp by using column offsets. - * The reason for discouraging this is that in the future, the - * granularity may not be nanoseconds. - */ - ns_buf[0] = 0; - charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec); - assert (charsprinted < NS_BUF_LEN); - } +char * +find_pred_name (pred_func) + PFB pred_func; +{ + int i; - if (kind != '@' - && (tm = localtime (&ts.tv_sec)) - && strftime (buf, sizeof buf, fmt, tm)) - { - /* For %AS, %CS, %TS, add the fractional part of the seconds - * information. - */ - if (need_ns_suffix) - { - assert ((sizeof buf - strlen(buf)) > strlen(ns_buf)); - strcat(buf, ns_buf); - } - return buf; - } - else - { - uintmax_t w = ts.tv_sec; - size_t used, len, remaining; - - /* XXX: note that we are negating an unsigned type which is the - * widest possible unsigned type. - */ - char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1, - human_ceiling, 1, 1); - assert (p > buf); - assert (p < (buf + (sizeof buf))); - if (ts.tv_sec < 0) - *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */ - - /* Add the nanoseconds part. Because we cannot enforce a - * particlar implementation of human_readable, we cannot assume - * any particular value for (p-buf). So we need to be careful - * that there is enough space remaining in the buffer. - */ - if (need_ns_suffix) - { - len = strlen(p); - used = (p-buf) + len; /* Offset into buf of current end */ - assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */ - remaining = sizeof buf - used - 1u; /* allow space for NUL */ - - if (strlen(ns_buf) >= remaining) - { - error(0, 0, - "charsprinted=%ld but remaining=%lu: ns_buf=%s", - (long)charsprinted, (unsigned long)remaining, ns_buf); - } - assert (strlen(ns_buf) < remaining); - strcat(p, ns_buf); - } - return p; - } + for (i = 0; pred_table[i].pred_func != 0; i++) + if (pred_table[i].pred_func == pred_func) + break; + return (pred_table[i].pred_name); } -static const char *weekdays[] = - { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; -static char * months[] = - { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; +static char * +type_name (type) + short type; +{ + int i; + for (i = 0; type_table[i].type != (short) -1; i++) + if (type_table[i].type == type) + break; + return (type_table[i].type_name); +} static char * -ctime_format (struct timespec ts) +prec_name (prec) + short prec; { - const struct tm * ptm; -#define TIME_BUF_LEN 1024u - static char resultbuf[TIME_BUF_LEN]; - int nout; - - ptm = localtime(&ts.tv_sec); - if (ptm) - { - assert (ptm->tm_wday >= 0); - assert (ptm->tm_wday < 7); - assert (ptm->tm_mon >= 0); - assert (ptm->tm_mon < 12); - assert (ptm->tm_hour >= 0); - assert (ptm->tm_hour < 24); - assert (ptm->tm_min < 60); - assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */ - - /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */ - nout = snprintf(resultbuf, TIME_BUF_LEN, - "%3s %3s %2d %02d:%02d:%02d.%010ld %04d", - weekdays[ptm->tm_wday], - months[ptm->tm_mon], - ptm->tm_mday, - ptm->tm_hour, - ptm->tm_min, - ptm->tm_sec, - (long int)ts.tv_nsec, - 1900 + ptm->tm_year); - - assert (nout < TIME_BUF_LEN); - return resultbuf; - } - else - { - /* The time cannot be represented as a struct tm. - Output it as an integer. */ - return format_date (ts, '@'); - } + int i; + + for (i = 0; prec_table[i].prec != (short) -1; i++) + if (prec_table[i].prec == prec) + break; + return (prec_table[i].prec_name); +} + +/* Walk the expression tree NODE to stdout. + INDENT is the number of levels to indent the left margin. */ + +void +print_tree (node, indent) + struct predicate *node; + int indent; +{ + int i; + + if (node == NULL) + return; + for (i = 0; i < indent; i++) + printf (" "); + printf ("pred = %s type = %s prec = %s addr = %x\n", + find_pred_name (node->pred_func), + type_name (node->p_type), prec_name (node->p_prec), node); + for (i = 0; i < indent; i++) + printf (" "); + printf ("left:\n"); + print_tree (node->pred_left, indent + 1); + for (i = 0; i < indent; i++) + printf (" "); + printf ("right:\n"); + print_tree (node->pred_right, indent + 1); } /* Copy STR into BUF and trim blanks from the end of BUF. @@ -2239,8 +1506,10 @@ blank_rtrim (str, buf) } /* Print out the predicate list starting at NODE. */ + void -print_list (FILE *fp, struct predicate *node) +print_list (node) + struct predicate *node; { struct predicate *cur; char name[256]; @@ -2248,161 +1517,9 @@ print_list (FILE *fp, struct predicate *node) cur = node; while (cur != NULL) { - fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name)); + printf ("%s ", blank_rtrim (find_pred_name (cur->pred_func), name)); cur = cur->pred_next; } - fprintf (fp, "\n"); -} - -/* Print out the predicate list starting at NODE. */ -static void -print_parenthesised(FILE *fp, struct predicate *node) -{ - int parens = 0; - - if (node) - { - if ((pred_is(node, pred_or) || pred_is(node, pred_and)) - && node->pred_left == NULL) - { - /* We print "<nothing> or X" as just "X" - * We print "<nothing> and X" as just "X" - */ - print_parenthesised(fp, node->pred_right); - } - else - { - if (node->pred_left || node->pred_right) - parens = 1; - - if (parens) - fprintf(fp, "%s", " ( "); - print_optlist(fp, node); - if (parens) - fprintf(fp, "%s", " ) "); - } - } -} - -void -print_optlist (FILE *fp, const struct predicate *p) -{ - if (p) - { - print_parenthesised(fp, p->pred_left); - fprintf (fp, - "%s%s", - p->need_stat ? "[call stat] " : "", - p->need_type ? "[need type] " : ""); - print_predicate(fp, p); - fprintf(fp, " [%g] ", p->est_success_rate); - if (options.debug_options & DebugSuccessRates) - { - fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits); - if (p->perf.visits) - { - double real_rate = (double)p->perf.successes / (double)p->perf.visits; - fprintf(fp, "=%g] ", real_rate); - } - else - { - fprintf(fp, "=_] "); - } - } - print_parenthesised(fp, p->pred_right); - } -} - -void show_success_rates(const struct predicate *p) -{ - if (options.debug_options & DebugSuccessRates) - { - fprintf(stderr, "Predicate success rates after completion:\n"); - print_optlist(stderr, p); - fprintf(stderr, "\n"); - } -} - - - - -#ifdef _NDEBUG -/* If _NDEBUG is defined, the assertions will do nothing. Hence - * there is no point in having a function body for pred_sanity_check() - * if that preprocessor macro is defined. - */ -void -pred_sanity_check(const struct predicate *predicates) -{ - /* Do nothing, since assert is a no-op with _NDEBUG set */ - return; -} -#else -void -pred_sanity_check(const struct predicate *predicates) -{ - const struct predicate *p; - - for (p=predicates; p != NULL; p=p->pred_next) - { - /* All predicates must do something. */ - assert (p->pred_func != NULL); - - /* All predicates must have a parser table entry. */ - assert (p->parser_entry != NULL); - - /* If the parser table tells us that just one predicate function is - * possible, verify that that is still the one that is in effect. - * If the parser has NULL for the predicate function, that means that - * the parse_xxx function fills it in, so we can't check it. - */ - if (p->parser_entry->pred_func) - { - assert (p->parser_entry->pred_func == p->pred_func); - } - - switch (p->parser_entry->type) - { - /* Options all take effect during parsing, so there should - * be no predicate entries corresponding to them. Hence we - * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION - * items. - * - * This is a silly way of coding this test, but it prevents - * a compiler warning (i.e. otherwise it would think that - * there would be case statements missing). - */ - case ARG_OPTION: - case ARG_POSITIONAL_OPTION: - assert (p->parser_entry->type != ARG_OPTION); - assert (p->parser_entry->type != ARG_POSITIONAL_OPTION); - break; - - case ARG_ACTION: - assert(p->side_effects); /* actions have side effects. */ - if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit)) - { - /* actions other than -prune and -quit should - * inhibit the default -print - */ - assert (p->no_default_print); - } - break; - - /* We happen to know that the only user of ARG_SPECIAL_PARSE - * is a test, so handle it like ARG_TEST. - */ - case ARG_SPECIAL_PARSE: - case ARG_TEST: - case ARG_PUNCTUATION: - case ARG_NOOP: - /* Punctuation and tests should have no side - * effects and not inhibit default print. - */ - assert (!p->no_default_print); - assert (!p->side_effects); - break; - } - } + printf ("\n"); } -#endif +#endif /* DEBUG */ diff --git a/find/tree.c b/find/tree.c index 7420c604..22092427 100644 --- a/find/tree.c +++ b/find/tree.c @@ -1,62 +1,29 @@ /* tree.c -- helper functions to build and evaluate the expression tree. - Copyright (C) 1990, 91, 92, 93, 94, 2000, 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, 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 <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> #include "defs.h" -#include <assert.h> -#include <stdlib.h> - -#include "xalloc.h" -#include "error.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 - - - -/* All predicates for each path to process. */ -static struct predicate *predicates = NULL; - -/* The root of the evaluation tree. */ -static struct predicate *eval_tree = NULL; - -/* The last predicate allocated. */ -static struct predicate *last_pred = NULL; - - -static struct predicate *scan_rest PARAMS((struct predicate **input, - struct predicate *head, - short int prev_prec)); -static void merge_pred PARAMS((struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p)); -static struct predicate *set_new_parent PARAMS((struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp)); -static const char *cost_name PARAMS((enum EvaluationCost cost)); - +static struct predicate *scan_rest P_((struct predicate **input, struct predicate *head, int prev_prec)); +static void merge_pred P_((struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p)); +static struct predicate *set_new_parent P_((struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp)); /* Return a pointer to a tree that represents the expression prior to non-unary operator *INPUT. @@ -78,48 +45,20 @@ static const char *cost_name PARAMS((enum EvaluationCost cost)); our caller, so get_expr recurses. */ struct predicate * -get_expr (struct predicate **input, - short int prev_prec, - const struct predicate* prev_pred) +get_expr (input, prev_prec) + struct predicate **input; + short prev_prec; { - struct predicate *next = NULL; - struct predicate *this_pred = (*input); + struct predicate *next; if (*input == NULL) - error (1, 0, _("invalid expression")); - + error (1, 0, "invalid expression"); switch ((*input)->p_type) { case NO_TYPE: - error (1, 0, _("invalid expression")); - break; - case BI_OP: - /* e.g. "find . -a" */ - error (1, 0, _("invalid expression; you have used a binary operator '%s' with nothing before it."), this_pred->p_name); - break; - case CLOSE_PAREN: - if ((UNI_OP == prev_pred->p_type - || BI_OP == prev_pred->p_type) - && !this_pred->artificial) - { - /* e.g. "find \( -not \)" or "find \( -true -a \" */ - error(1, 0, _("expected an expression between '%s' and ')'"), - prev_pred->p_name); - } - else if ( (*input)->artificial ) - { - /* We have reached the end of the user-supplied predicates - * unexpectedly. - */ - /* e.g. "find . -true -a" */ - error (1, 0, _("expected an expression after '%s'"), prev_pred->p_name); - } - else - { - error (1, 0, _("invalid expression; you have too many ')'")); - } + error (1, 0, "invalid expression"); break; case PRIMARY_TYPE: @@ -130,33 +69,20 @@ get_expr (struct predicate **input, case UNI_OP: next = *input; *input = (*input)->pred_next; - next->pred_right = get_expr (input, NEGATE_PREC, next); + next->pred_right = get_expr (input, NEGATE_PREC); break; case OPEN_PAREN: - if ( (NULL == (*input)->pred_next) || (*input)->pred_next->artificial ) - { - /* user typed something like "find . (", and so the ) we are - * looking at is from the artificial "( ) -print" that we - * add. - */ - error (1, 0, _("invalid expression; expected to find a ')' but didn't see one. Perhaps you need an extra predicate after '%s'"), this_pred->p_name); - } - prev_pred = (*input); *input = (*input)->pred_next; - if ( (*input)->p_type == CLOSE_PAREN ) - { - error (1, 0, _("invalid expression; empty parentheses are not allowed.")); - } - next = get_expr (input, NO_PREC, prev_pred); + next = get_expr (input, NO_PREC); if ((*input == NULL) || ((*input)->p_type != CLOSE_PAREN)) - error (1, 0, _("invalid expression; I was expecting to find a ')' somewhere but did not see one.")); + error (1, 0, "invalid expression"); *input = (*input)->pred_next; /* move over close */ break; - + default: - error (1, 0, _("oops -- invalid expression type!")); + error (1, 0, "oops -- invalid expression type!"); break; } @@ -171,7 +97,7 @@ get_expr (struct predicate **input, { next = scan_rest (input, next, prev_prec); if (next == NULL) - error (1, 0, _("invalid expression")); + error (1, 0, "invalid expression"); } return (next); } @@ -190,9 +116,10 @@ get_expr (struct predicate **input, PREV_PREC is the precedence of the previous predicate element. */ static struct predicate * -scan_rest (struct predicate **input, - struct predicate *head, - short int prev_prec) +scan_rest (input, head, prev_prec) + struct predicate **input; + struct predicate *head; + short prev_prec; { struct predicate *tree; /* The new tree we are building. */ @@ -207,410 +134,26 @@ scan_rest (struct predicate **input, case PRIMARY_TYPE: case UNI_OP: case OPEN_PAREN: - /* I'm not sure how we get here, so it is not obvious what - * sort of mistakes might give rise to this condition. - */ - error (1, 0, _("invalid expression")); + error (1, 0, "invalid expression"); break; case BI_OP: - { - struct predicate *prev = (*input); - (*input)->pred_left = tree; - tree = *input; - *input = (*input)->pred_next; - tree->pred_right = get_expr (input, tree->p_prec, prev); - break; - } + (*input)->pred_left = tree; + tree = *input; + *input = (*input)->pred_next; + tree->pred_right = get_expr (input, tree->p_prec); + break; case CLOSE_PAREN: - return tree; + return (tree); default: - error (1, 0, - _("oops -- invalid expression type (%d)!"), - (int)(*input)->p_type); + error (1, 0, "oops -- invalid expression type!"); break; } } - return tree; -} - -/* Returns true if the specified predicate is reorderable. */ -static boolean -predicate_is_cost_free(const struct predicate *p) -{ - if (pred_is(p, pred_name) || - pred_is(p, pred_path) || - pred_is(p, pred_iname) || - pred_is(p, pred_ipath)) - { - /* Traditionally (at least 4.1.7 through 4.2.x) GNU find always - * optimised these cases. - */ - return true; - } - else if (options.optimisation_level > 0) - { - if (pred_is(p, pred_and) || - pred_is(p, pred_negate) || - pred_is(p, pred_comma) || - pred_is(p, pred_or)) - return false; - else - return NeedsNothing == p->p_cost; - } - else - { - return false; - } -} - -/* Prints a predicate */ -void print_predicate(FILE *fp, const struct predicate *p) -{ - fprintf (fp, "%s%s%s", - p->p_name, - p->arg_text ? " " : "", - p->arg_text ? p->arg_text : ""); -} - - -struct predlist -{ - struct predicate *head; - struct predicate *tail; -}; - -static void -predlist_init(struct predlist *p) -{ - p->head = p->tail = NULL; -} - -static void -predlist_insert(struct predlist *list, - struct predicate *curr, - struct predicate **pprev) -{ - struct predicate **insertpos = &(list->head); - - *pprev = curr->pred_left; - if (options.optimisation_level > 2) - { - /* Insert the new node in the list after any other entries which - * are more selective. - */ - if (0) - while ( (*insertpos) && ((*insertpos)->est_success_rate < curr->est_success_rate) ) - { - insertpos = &((*insertpos)->pred_left); - } - } - curr->pred_left = (*insertpos); - (*insertpos) = curr; - if (NULL == list->tail) - list->tail = list->head; -} - -static int -pred_cost_compare(const struct predicate *p1, const struct predicate *p2, boolean wantfailure) -{ - if (p1->p_cost == p2->p_cost) - { - if (p1->est_success_rate == p2->est_success_rate) - return 0; - else if (wantfailure) - return p1->est_success_rate < p2->est_success_rate ? -1 : 1; - else - return p1->est_success_rate < p2->est_success_rate ? 1 : -1; - } - else - { - return p1->p_cost < p2->p_cost ? -1 : 1; - } -} - - -static void -predlist_merge_sort(struct predlist *list, - struct predicate **last) -{ - struct predlist new_list; - struct predicate *p, *q; - - if (NULL == list->head) - return; /* nothing to do */ - - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "%s:\n", "predlist before merge sort"); - print_tree(stderr, list->head, 2); - } - - calculate_derived_rates(list->head); - predlist_init(&new_list); - while (list->head) - { - /* remove head of source list */ - q = list->head; - list->head = list->head->pred_left; - q->pred_left = NULL; - - /* insert it into the new list */ - for (p=new_list.head; p; p=p->pred_left) - { - /* If these operations are OR operations, we want to get a - * successful test as soon as possible, to take advantage of - * the short-circuit evaluation. If they're AND, we want to - * get an unsuccessful result early for the same reason. - * Therefore we invert the sense of the comparison for the - * OR case. We only want to invert the sense of the success - * rate comparison, not the operation cost comparison. Hence we - * pass a flag into pred_cost_compare(). - */ - boolean wantfailure = (OR_PREC != p->p_prec); - if (pred_cost_compare(p->pred_right, q->pred_right, wantfailure) >= 0) - break; - } - if (p) - { - /* insert into existing list */ - q->pred_left = p->pred_left; - if (NULL == q->pred_left) - new_list.tail = q; - p->pred_left = q; - } - else - { - q->pred_left = new_list.head; /* prepend */ - new_list.head = q; - if (NULL == new_list.tail) - new_list.tail = q; /* first item in new list */ - } - } - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "%s:\n", "predlist after merge sort"); - print_tree(stderr, new_list.head, 2); - } - - calculate_derived_rates(new_list.head); - merge_pred(new_list.head, new_list.tail, last); - predlist_init(list); -} - -static void -merge_lists(struct predlist lists[], int nlists, - struct predlist *name_list, - struct predlist *regex_list, - struct predicate **last) -{ - int i; - static void (*mergefn)(struct predlist *, struct predicate**); - - mergefn = predlist_merge_sort; - - mergefn(name_list, last); - mergefn(regex_list, last); - - for (i=0; i<nlists; i++) - mergefn(&lists[i], last); -} - - - -static boolean -subtree_has_side_effects(const struct predicate *p) -{ - if (p) - { - return p->side_effects - || subtree_has_side_effects(p->pred_left) - || subtree_has_side_effects(p->pred_right); - } - else - { - - return false; - } -} - -static int -worst_cost (const struct predicate *p) -{ - if (p) - { - unsigned int cost_r, cost_l, worst; - cost_l = worst_cost(p->pred_left); - cost_r = worst_cost(p->pred_right); - worst = (cost_l > cost_r) ? cost_l : cost_r; - if (worst < p->p_cost) - worst = p->p_cost; - return worst; - } - else - { - return 0; - } -} - - - -static void -perform_arm_swap(struct predicate *p) -{ - struct predicate *tmp = p->pred_left->pred_right; - p->pred_left->pred_right = p->pred_right; - p->pred_right = tmp; -} - -/* Consider swapping p->pred_left->pred_right with p->pred_right, - * if that yields a faster evaluation. Normally the left predicate is - * evaluated first. - * - * If the operation is an OR, we want the left predicate to be the one that - * succeeds most often. If it is an AND, we want it to be the predicate that - * fails most often. - * - * We don't consider swapping arms of an operator where their cost is - * different or where they have side effects. - * - * A viable test case for this is - * ./find -D opt -O3 . \! -type f -o -type d - * Here, the ! -type f should be evaluated first, - * as we assume that 95% of inodes are vanilla files. - */ -static boolean -consider_arm_swap(struct predicate *p) -{ - int left_cost, right_cost; - const char *reason = NULL; - struct predicate **pl, **pr; - - if (BI_OP != p->p_type) - reason = "Not a binary operation"; - - if (!reason) - { - if (NULL == p->pred_left || NULL == p->pred_right) - reason = "Doesn't have two arms"; - } - - - if (!reason) - { - if (NULL == p->pred_left->pred_right) - reason = "Left arm has no child on RHS"; - } - pr = &p->pred_right; - pl = &p->pred_left->pred_right; - - if (!reason) - { - if (subtree_has_side_effects(*pl)) - reason = "Left subtree has side-effects"; - } - if (!reason) - { - if (subtree_has_side_effects(*pr)) - reason = "Right subtree has side-effects"; - } - - if (!reason) - { - left_cost = worst_cost(*pl); - right_cost = worst_cost(*pr); - - if (left_cost < right_cost) - { - reason = "efficient as-is"; - } - } - if (!reason) - { - boolean want_swap; - - if (left_cost == right_cost) - { - /* it's a candidate */ - float succ_rate_l = (*pl)->est_success_rate; - float succ_rate_r = (*pr)->est_success_rate; - - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "Success rates: l=%f, r=%f\n", succ_rate_l, succ_rate_r); - } - - if (pred_is(p, pred_or)) - { - want_swap = succ_rate_r < succ_rate_l; - if (!want_swap) - reason = "Operation is OR and right success rate >= left"; - } - else if (pred_is(p, pred_and)) - { - want_swap = succ_rate_r > succ_rate_l; - if (!want_swap) - reason = "Operation is AND and right success rate <= left"; - } - else - { - want_swap = false; - reason = "Not AND or OR"; - } - } - else - { - want_swap = true; - } - - if (want_swap) - { - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "Performing arm swap on:\n"); - print_tree (stderr, p, 0); - } - perform_arm_swap(p); - return true; - } - } - - - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "Not an arm swap candidate (%s):\n", reason); - print_tree (stderr, p, 0); - } - return false; -} - -static boolean -do_arm_swaps(struct predicate *p) -{ - if (p) - { - boolean swapped; - do - { - swapped = false; - if (consider_arm_swap(p) - || do_arm_swaps(p->pred_left) - || do_arm_swaps(p->pred_right)) - { - swapped = true; - } - } while (swapped); - return swapped; - } - else - { - return false; - } + return (tree); } - - /* Optimize the ordering of the predicates in the tree. Rearrange them to minimize work. Strategies: @@ -619,21 +162,10 @@ do_arm_swaps(struct predicate *p) predicates (if any) which have "side effects", such as printing. The grouping implements the partial ordering on predicates which those with side effects impose. + * Place -name, -path, and -regex at the front of a group, with + -name and -path ahead of -regex. Predicates that are moved to the + front of a group by definition do not have side effects. - * Place -name, -iname, -path, -ipath, -regex and -iregex at the front - of a group, with -name, -iname, -path and -ipath ahead of - -regex and -iregex. Predicates which are moved to the front - of a group by definition do not have side effects. Both - -regex and -iregex both use pred_regex. - - If higher optimisation levels have been selected, reordering also - occurs according to the p_cost member of each predicate (which - reflects the performance cost of the test). The ordering also - bears in mind whether these operations are more likely to succeed - or fail. When evauating a chain of OR conditions, we prefer - tests likely to succeed at the front of the list. For AND, we - prefer tests likely to fail at the front of the list. - This routine "normalizes" the predicate tree by ensuring that all expression predicates have AND (or OR or COMMA) parent nodes which are linked along the left edge of the expression tree. @@ -643,27 +175,29 @@ do_arm_swaps(struct predicate *p) to be rearranged. opt_expr may return a new root pointer there. Return true if the tree contains side effects, false if not. */ -static boolean -opt_expr (struct predicate **eval_treep) +boolean +opt_expr (eval_treep) + struct predicate **eval_treep; { - struct predlist regex_list={NULL,NULL}, name_list={NULL,NULL}; - struct predlist cbo_list[NumEvaluationCosts]; - int i; + /* List of -name and -path predicates to move. */ + struct predicate *name_list = NULL; + struct predicate *end_name_list = NULL; + /* List of -regex predicates to move. */ + struct predicate *regex_list = NULL; + struct predicate *end_regex_list = NULL; struct predicate *curr; struct predicate **prevp; /* Address of `curr' node. */ struct predicate **last_sidep; /* Last predicate with side effects. */ - PRED_FUNC pred_func; + PFB pred_func; enum predicate_type p_type; boolean has_side_effects = false; /* Return value. */ enum predicate_precedence prev_prec, /* precedence of last BI_OP in branch */ biop_prec; /* topmost BI_OP precedence in branch */ + if (eval_treep == NULL || *eval_treep == NULL) return (false); - for (i=0; i<NumEvaluationCosts; i++) - predlist_init(&cbo_list[i]); - /* Set up to normalize tree as a left-linked list of ANDs or ORs. Set `curr' to the leftmost node, `prevp' to its address, and `pred_func' to the predicate type of its parent. */ @@ -681,16 +215,14 @@ opt_expr (struct predicate **eval_treep) if (curr->p_type != BI_OP) set_new_parent (curr, prev_prec, prevp); - if (options.debug_options & (DebugExpressionTree|DebugTreeOpt)) - { - /* Normalized tree. */ - fprintf (stderr, "Normalized Eval Tree:\n"); - print_tree (stderr, *eval_treep, 0); - } - +#ifdef DEBUG + /* Normalized tree. */ + printf ("Normalized Eval Tree:\n"); + print_tree (*eval_treep, 0); +#endif + /* Rearrange the predicates. */ prevp = eval_treep; - biop_prec = NO_PREC; /* not COMMA_PREC */ if ((*prevp) && (*prevp)->p_type == BI_OP) biop_prec = (*prevp)->p_prec; while ((curr = *prevp) != NULL) @@ -705,68 +237,44 @@ opt_expr (struct predicate **eval_treep) { if (curr->p_prec != biop_prec) curr = set_new_parent(curr, biop_prec, prevp); + else + biop_prec = curr->p_prec; } /* See which predicate type we have. */ p_type = curr->pred_right->p_type; pred_func = curr->pred_right->pred_func; - switch (p_type) { case NO_TYPE: case PRIMARY_TYPE: - /* Don't rearrange the arguments of the comma operator, it is - not commutative. */ - if (biop_prec == COMMA_PREC) - break; + /* If it's one of our special primaries, move it to the + front of the list for that primary. */ + if (pred_func == pred_name || pred_func == pred_path) + { + *prevp = curr->pred_left; + curr->pred_left = name_list; + name_list = curr; - /* If this predicate has no side effects, consider reordering it. */ - if (!curr->pred_right->side_effects) + if (end_name_list == NULL) + end_name_list = curr; + + continue; + } + + if (pred_func == pred_regex) { - boolean reorder; - - /* If it's one of our special primaries, move it to the - front of the list for that primary. */ - if (predicate_is_cost_free(curr->pred_right)) - { - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "-O%d: promoting cheap predicate ", - (int)options.optimisation_level); - print_predicate(stderr, curr->pred_right); - fprintf(stderr, " into name_list\n"); - } - predlist_insert(&name_list, curr, prevp); - continue; - } - - if (pred_func == pred_regex) - { - predlist_insert(®ex_list, curr, prevp); - continue; - } + *prevp = curr->pred_left; + curr->pred_left = regex_list; + regex_list = curr; + + if (end_regex_list == NULL) + end_regex_list = curr; - reorder = ((options.optimisation_level > 1) - && (NeedsType == curr->pred_right->p_cost) - && !curr->pred_right->need_stat) || - (options.optimisation_level > 2); - - if (reorder) - { - if (options.debug_options & DebugTreeOpt) - { - fprintf(stderr, "-O%d: categorising predicate ", - (int)options.optimisation_level); - print_predicate(stderr, curr->pred_right); - fprintf(stderr, " by cost (%s)\n", - cost_name(curr->pred_right->p_cost)); - } - predlist_insert(&cbo_list[curr->pred_right->p_cost], curr, prevp); - continue; - } + continue; } - + break; case UNI_OP: @@ -785,7 +293,7 @@ opt_expr (struct predicate **eval_treep) all of the user's parentheses. */ default: - error (1, 0, _("oops -- invalid expression type!")); + error (1, 0, "oops -- invalid expression type!"); break; } @@ -794,7 +302,18 @@ opt_expr (struct predicate **eval_treep) last_sidep = prevp; /* Incorporate lists and reset list pointers for this group. */ - merge_lists(cbo_list, NumEvaluationCosts, &name_list, ®ex_list, last_sidep); + if (name_list != NULL) + { + merge_pred (name_list, end_name_list, last_sidep); + name_list = end_name_list = NULL; + } + + if (regex_list != NULL) + { + merge_pred (regex_list, end_regex_list, last_sidep); + regex_list = end_regex_list = NULL; + } + has_side_effects = true; } @@ -803,59 +322,46 @@ opt_expr (struct predicate **eval_treep) /* Do final list merges. */ last_sidep = prevp; - merge_lists(cbo_list, NumEvaluationCosts, &name_list, ®ex_list, last_sidep); - return has_side_effects; -} - -static float -constrain_rate(float rate) -{ - if (rate > 1.0f) - return 1.0; - else if (rate < 0.0) - return 0.0; - else - return rate; + if (name_list != NULL) + merge_pred (name_list, end_name_list, last_sidep); + if (regex_list != NULL) + merge_pred (regex_list, end_regex_list, last_sidep); + + return (has_side_effects); } - + /* Link in a new parent BI_OP node for CURR, at *PREVP, with precedence HIGH_PREC. */ static struct predicate * -set_new_parent (struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp) +set_new_parent (curr, high_prec, prevp) + struct predicate *curr; + enum predicate_precedence high_prec; + struct predicate **prevp; { struct predicate *new_parent; - new_parent = xmalloc (sizeof (struct predicate)); + new_parent = (struct predicate *) xmalloc (sizeof (struct predicate)); new_parent->p_type = BI_OP; new_parent->p_prec = high_prec; new_parent->need_stat = false; - new_parent->need_type = false; - new_parent->p_cost = NeedsNothing; - + switch (high_prec) { case COMMA_PREC: new_parent->pred_func = pred_comma; - new_parent->p_name = ","; - new_parent->est_success_rate = 1.0; break; case OR_PREC: new_parent->pred_func = pred_or; - new_parent->p_name = "-o"; - new_parent->est_success_rate = constrain_rate(curr->est_success_rate); break; case AND_PREC: new_parent->pred_func = pred_and; - new_parent->p_name = "-a"; - new_parent->est_success_rate = constrain_rate(curr->est_success_rate); break; default: ; /* empty */ } new_parent->side_effects = false; - new_parent->no_default_print = false; new_parent->args.str = NULL; new_parent->pred_next = NULL; @@ -865,14 +371,19 @@ set_new_parent (struct predicate *curr, enum predicate_precedence high_prec, str new_parent->pred_right = curr; *prevp = new_parent; - return new_parent; +#ifdef DEBUG + new_parent->p_name = (char *) find_pred_name (new_parent->pred_func); +#endif /* DEBUG */ + + return (new_parent); } /* Merge the predicate list that starts at BEG_LIST and ends at END_LIST into the tree at LAST_P. */ static void -merge_pred (struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p) +merge_pred (beg_list, end_list, last_p) + struct predicate *beg_list, *end_list, **last_p; { end_list->pred_left = *last_p; *last_p = beg_list; @@ -888,768 +399,35 @@ merge_pred (struct predicate *beg_list, struct predicate *end_list, struct predi that a stat is made as late as possible. Return true if the top node in TREE requires a stat, false if not. */ - -struct pred_cost_lookup -{ - PRED_FUNC fn; - enum EvaluationCost cost; -}; -static struct pred_cost_lookup costlookup[] = - { - { pred_amin , NeedsStatInfo }, - { pred_and , NeedsNothing, }, - { pred_anewer , NeedsStatInfo, }, - { pred_atime , NeedsStatInfo, }, - { pred_closeparen, NeedsNothing }, - { pred_cmin , NeedsStatInfo, }, - { pred_cnewer , NeedsStatInfo, }, - { pred_comma , NeedsNothing, }, - { pred_ctime , NeedsStatInfo, }, - { pred_delete , NeedsSyncDiskHit }, - { pred_empty , NeedsStatInfo }, - { pred_exec , NeedsEventualExec }, - { pred_execdir , NeedsEventualExec }, - { pred_executable, NeedsAccessInfo }, - { pred_false , NeedsNothing }, - { pred_fprint , NeedsNothing }, - { pred_fprint0 , NeedsNothing }, - { pred_fprintf , NeedsNothing }, - { pred_fstype , NeedsStatInfo }, /* true for amortised cost */ - { pred_gid , NeedsStatInfo }, - { pred_group , NeedsStatInfo }, - { pred_ilname , NeedsLinkName }, - { pred_iname , NeedsNothing }, - { pred_inum , NeedsStatInfo }, - { pred_ipath , NeedsNothing }, - { pred_links , NeedsStatInfo }, - { pred_lname , NeedsLinkName }, - { pred_ls , NeedsStatInfo }, - { pred_fls , NeedsStatInfo }, - { pred_mmin , NeedsStatInfo }, - { pred_mtime , NeedsStatInfo }, - { pred_name , NeedsNothing }, - { pred_negate , NeedsNothing, }, - { pred_newer , NeedsStatInfo, }, - { pred_newerXY , NeedsStatInfo, }, - { pred_nogroup , NeedsStatInfo }, /* true for amortised cost if caching is on */ - { pred_nouser , NeedsStatInfo }, /* true for amortised cost if caching is on */ - { pred_ok , NeedsUserInteraction }, - { pred_okdir , NeedsUserInteraction }, - { pred_openparen , NeedsNothing }, - { pred_or , NeedsNothing, }, - { pred_path , NeedsNothing }, - { pred_perm , NeedsStatInfo }, - { pred_print , NeedsNothing }, - { pred_print0 , NeedsNothing }, - { pred_prune , NeedsNothing }, - { pred_quit , NeedsNothing }, - { pred_readable , NeedsAccessInfo }, - { pred_regex , NeedsNothing }, - { pred_samefile , NeedsStatInfo }, - { pred_size , NeedsStatInfo }, - { pred_true , NeedsNothing }, - { pred_type , NeedsType }, - { pred_uid , NeedsStatInfo }, - { pred_used , NeedsStatInfo }, - { pred_user , NeedsStatInfo }, - { pred_writable , NeedsAccessInfo }, - { pred_xtype , NeedsType } /* roughly correct unless most files are symlinks */ - }; -static int pred_table_sorted = 0; - -static boolean -check_sorted(void *base, size_t members, size_t membersize, - int (*cmpfn)(const void*, const void*)) -{ - const char *p = base; - size_t i; - for (i=1u; i<members; ++i) - { - int result = cmpfn(p+i*membersize, p+(i-1)*membersize); - if (result < 0) - return false; - result = cmpfn(p+(i-1)*membersize, p+i*membersize); - assert (result <= 0); - } - return true; -} - - -static int -cost_table_comparison(const void *p1, const void *p2) -{ - /* We have to compare the function pointers with memcmp(), - * because ISO C does not allow magnitude comparison of - * function pointers (just equality testing). - */ - const struct pred_cost_lookup *pc1 = p1; - const struct pred_cost_lookup *pc2 = p2; - union { - PRED_FUNC pfn; - char mem[sizeof (PRED_FUNC)]; - } u1, u2; - - u1.pfn = pc1->fn; - u2.pfn = pc2->fn; - return memcmp(u1.mem, u2.mem, sizeof(u1.pfn)); -} - -static enum EvaluationCost -get_pred_cost(const struct predicate *p) -{ - enum EvaluationCost data_requirement_cost = NeedsNothing; - enum EvaluationCost inherent_cost = NeedsUnknown; - - if (p->need_stat) - { - data_requirement_cost = NeedsStatInfo; - } - else if (p->need_type) - { - data_requirement_cost = NeedsType; - } - else - { - data_requirement_cost = NeedsNothing; - } - - if (pred_is(p, pred_exec) || pred_is(p, pred_execdir)) - { - if (p->args.exec_vec.multiple) - inherent_cost = NeedsEventualExec; - else - inherent_cost = NeedsImmediateExec; - } - else if (pred_is(p, pred_fprintf)) - { - /* the parser calculated the cost for us. */ - inherent_cost = p->p_cost; - } - else - { - struct pred_cost_lookup key; - void *entry; - - if (!pred_table_sorted) - { - qsort(costlookup, - sizeof(costlookup)/sizeof(costlookup[0]), - sizeof(costlookup[0]), - cost_table_comparison); - - if (!check_sorted(costlookup, - sizeof(costlookup)/sizeof(costlookup[0]), - sizeof(costlookup[0]), - cost_table_comparison)) - { - error(1, 0, "Failed to sort the costlookup array (indirect)."); - } - pred_table_sorted = 1; - } - key.fn = p->pred_func; - entry = bsearch(&key, costlookup, - sizeof(costlookup)/sizeof(costlookup[0]), - sizeof(costlookup[0]), - cost_table_comparison); - if (entry) - { - inherent_cost = ((const struct pred_cost_lookup*)entry)->cost; - } - else - { - error(0, 0, "warning: no cost entry for predicate %s", p->p_name); - inherent_cost = NeedsUnknown; - } - } - - if (inherent_cost > data_requirement_cost) - return inherent_cost; - else - return data_requirement_cost; -} - -static void -estimate_costs (struct predicate *tree) +boolean +mark_stat (tree) + struct predicate *tree; { - if (tree) - { - estimate_costs(tree->pred_right); - estimate_costs(tree->pred_left); - - tree->p_cost = get_pred_cost(tree); - } -} - -struct predicate* -get_eval_tree(void) -{ - return eval_tree; -} - -static float -getrate(const struct predicate *p) -{ - if (p) - return p->est_success_rate; - else - return 1.0f; -} - - -float -calculate_derived_rates(struct predicate *p) -{ - assert (NULL != p); - - if (p->pred_right) - calculate_derived_rates(p->pred_right); - if (p->pred_left) - calculate_derived_rates(p->pred_left); - - assert (p->p_type != CLOSE_PAREN); - assert (p->p_type != OPEN_PAREN); - - switch (p->p_type) + /* The tree is executed in-order, so walk this way (apologies to Aerosmith) + to find the first predicate for which the stat is needed. */ + switch (tree->p_type) { case NO_TYPE: - assert (NULL == p->pred_right); - assert (NULL == p->pred_left); - return p->est_success_rate; - case PRIMARY_TYPE: - assert (NULL == p->pred_right); - assert (NULL == p->pred_left); - return p->est_success_rate; + return tree->need_stat; case UNI_OP: - /* Unary operators must have exactly one operand */ - assert (pred_is(p, pred_negate)); - assert (NULL == p->pred_left); - p->est_success_rate = (1.0 - p->pred_right->est_success_rate); - return p->est_success_rate; + if (mark_stat (tree->pred_right)) + tree->need_stat = true; + return (false); case BI_OP: - { - float rate; - /* Binary operators must have two operands */ - if (pred_is(p, pred_and)) - { - rate = getrate(p->pred_right) * getrate(p->pred_left); - } - else if (pred_is(p, pred_comma)) - { - rate = 1.0f; - } - else if (pred_is(p, pred_or)) - { - rate = getrate(p->pred_right) + getrate(p->pred_left); - } - else - { - /* only and, or and comma are BI_OP. */ - assert (0); - abort (); - } - p->est_success_rate = constrain_rate(rate); - } - return p->est_success_rate; - - case OPEN_PAREN: - case CLOSE_PAREN: - p->est_success_rate = 1.0; - return p->est_success_rate; - } - assert (0); - abort (); -} - -/* opt_expr() rearranges predicates such that each left subtree is - * rooted at a logical predicate (e.g. and or or). check_normalization() - * asserts that this property still holds. - * - */ -static void check_normalization(struct predicate *p, boolean at_root) -{ - if (at_root) - { - assert (BI_OP == p->p_type); - } - - if (p->pred_left) - { - assert (BI_OP == p->pred_left->p_type); - check_normalization(p->pred_left, false); - } - if (p->pred_right) - { - check_normalization(p->pred_right, false); - } -} - -struct predicate* -build_expression_tree(int argc, char *argv[], int end_of_leading_options) -{ - const struct parser_table *parse_entry; /* Pointer to the parsing table entry for this expression. */ - char *predicate_name; /* Name of predicate being parsed. */ - struct predicate *cur_pred; - const struct parser_table *entry_close, *entry_print, *entry_open; - int i, oldi; - - predicates = NULL; - - /* Find where in ARGV the predicates begin by skipping the list of - * start points. - */ - for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++) - { - /* Do nothing. */ ; - } - - /* Enclose the expression in `( ... )' so a default -print will - apply to the whole expression. */ - entry_open = find_parser("("); - entry_close = find_parser(")"); - entry_print = find_parser("print"); - assert (entry_open != NULL); - assert (entry_close != NULL); - assert (entry_print != NULL); - - parse_openparen (entry_open, argv, &argc); - last_pred->p_name = "("; - predicates->artificial = true; - parse_begin_user_args(argv, argc, last_pred, predicates); - pred_sanity_check(last_pred); - - /* Build the input order list. */ - while (i < argc ) - { - if (!looks_like_expression(argv[i], false)) - { - error (0, 0, _("paths must precede expression: %s"), argv[i]); - usage(stderr, 1, NULL); - } - - predicate_name = argv[i]; - parse_entry = find_parser (predicate_name); - if (parse_entry == NULL) - { - /* Command line option not recognized */ - error (1, 0, _("unknown predicate `%s'"), predicate_name); - } - - /* We have recognised a test of the form -foo. Eat that, - * unless it is a predicate like -newerXY. - */ - if (parse_entry->type != ARG_SPECIAL_PARSE) - { - i++; - } - oldi = i; - if (!(*(parse_entry->parser_func)) (parse_entry, argv, &i)) - { - if (argv[i]) - { - if ( (ARG_SPECIAL_PARSE == parse_entry->type) && (i == oldi) ) - { - /* The special parse function spat out the - * predicate. It must be invalid, or not tasty. - */ - error (1, 0, _("invalid predicate `%s'"), - predicate_name); - } - else - { - error (1, 0, _("invalid argument `%s' to `%s'"), - argv[i], predicate_name); - } - } - else - { - /* Command line option requires an argument */ - error (1, 0, _("missing argument to `%s'"), predicate_name); - } - } - else - { - last_pred->p_name = predicate_name; - - /* If the parser consumed an argument, save it. */ - if (i != oldi) - last_pred->arg_text = argv[oldi]; - else - last_pred->arg_text = NULL; - } - pred_sanity_check(last_pred); - pred_sanity_check(predicates); /* XXX: expensive */ - } - parse_end_user_args(argv, argc, last_pred, predicates); - if (predicates->pred_next == NULL) - { - /* No predicates that do something other than set a global variable - were given; remove the unneeded initial `(' and add `-print'. */ - cur_pred = predicates; - predicates = last_pred = predicates->pred_next; - free (cur_pred); - parse_print (entry_print, argv, &argc); - last_pred->p_name = "-print"; - pred_sanity_check(last_pred); - pred_sanity_check(predicates); /* XXX: expensive */ - } - else if (!default_prints (predicates->pred_next)) - { - /* One or more predicates that produce output were given; - remove the unneeded initial `('. */ - cur_pred = predicates; - predicates = predicates->pred_next; - pred_sanity_check(predicates); /* XXX: expensive */ - free (cur_pred); - } - else - { - /* `( user-supplied-expression ) -print'. */ - parse_closeparen (entry_close, argv, &argc); - last_pred->p_name = ")"; - last_pred->artificial = true; - pred_sanity_check(last_pred); - parse_print (entry_print, argv, &argc); - last_pred->p_name = "-print"; - last_pred->artificial = true; - pred_sanity_check(last_pred); - pred_sanity_check(predicates); /* XXX: expensive */ - } - - if (options.debug_options & (DebugExpressionTree|DebugTreeOpt)) - { - fprintf (stderr, "Predicate List:\n"); - print_list (stderr, predicates); - } - - /* do a sanity check */ - check_option_combinations(predicates); - pred_sanity_check(predicates); - - /* Done parsing the predicates. Build the evaluation tree. */ - cur_pred = predicates; - eval_tree = get_expr (&cur_pred, NO_PREC, NULL); - calculate_derived_rates(eval_tree); - - /* Check if we have any left-over predicates (this fixes - * Debian bug #185202). - */ - if (cur_pred != NULL) - { - /* cur_pred->p_name is often NULL here */ - if (pred_is(cur_pred, pred_closeparen)) - { - /* e.g. "find \( -true \) \)" */ - error (1, 0, _("you have too many ')'")); - } - else - { - if (cur_pred->p_name) - error (1, 0, _("unexpected extra predicate '%s'"), cur_pred->p_name); - else - error (1, 0, _("unexpected extra predicate")); - } - } - - if (options.debug_options & (DebugExpressionTree|DebugTreeOpt)) - { - fprintf (stderr, "Eval Tree:\n"); - print_tree (stderr, eval_tree, 0); - } - - estimate_costs(eval_tree); - - /* Rearrange the eval tree in optimal-predicate order. */ - opt_expr (&eval_tree); - - /* Check that the tree is in normalised order (opt_expr does this) */ - check_normalization(eval_tree, true); - - do_arm_swaps(eval_tree); - - /* Check that the tree is still in normalised order */ - check_normalization(eval_tree, true); - - if (options.debug_options & (DebugExpressionTree|DebugTreeOpt)) - { - fprintf (stderr, "Optimized Eval Tree:\n"); - print_tree (stderr, eval_tree, 0); - fprintf (stderr, "Optimized command line:\n"); - print_optlist(stderr, eval_tree); - fprintf(stderr, "\n"); - } - - return eval_tree; -} - -/* Initialise the performance data for a predicate. - */ -static void -init_pred_perf(struct predicate *pred) -{ - struct predicate_performance_info *p = &pred->perf; - p->visits = p->successes = 0; -} - - -/* Return a pointer to a new predicate structure, which has been - linked in as the last one in the predicates list. - - Set `predicates' to point to the start of the predicates list. - Set `last_pred' to point to the new last predicate in the list. - - Set all cells in the new structure to the default values. */ - -struct predicate * -get_new_pred (const struct parser_table *entry) -{ - register struct predicate *new_pred; - (void) entry; - - /* Options should not be turned into predicates. */ - assert (entry->type != ARG_OPTION); - assert (entry->type != ARG_POSITIONAL_OPTION); - - if (predicates == NULL) - { - predicates = (struct predicate *) - xmalloc (sizeof (struct predicate)); - last_pred = predicates; - } - else - { - new_pred = xmalloc (sizeof (struct predicate)); - last_pred->pred_next = new_pred; - last_pred = new_pred; - } - last_pred->parser_entry = entry; - last_pred->pred_func = NULL; - last_pred->p_name = NULL; - last_pred->p_type = NO_TYPE; - last_pred->p_prec = NO_PREC; - last_pred->side_effects = false; - last_pred->no_default_print = false; - last_pred->need_stat = true; - last_pred->need_type = true; - last_pred->args.str = NULL; - last_pred->pred_next = NULL; - last_pred->pred_left = NULL; - last_pred->pred_right = NULL; - last_pred->literal_control_chars = options.literal_control_chars; - last_pred->artificial = false; - last_pred->est_success_rate = 1.0; - init_pred_perf(last_pred); - return last_pred; -} - -/* Return a pointer to a new predicate, with operator check. - Like get_new_pred, but it checks to make sure that the previous - predicate is an operator. If it isn't, the AND operator is inserted. */ + /* ANDs and ORs are linked along ->left ending in NULL. */ + if (tree->pred_left != NULL) + mark_stat (tree->pred_left); -struct predicate * -get_new_pred_chk_op (const struct parser_table *entry) -{ - struct predicate *new_pred; - static const struct parser_table *entry_and = NULL; - - /* Locate the entry in the parser table for the "and" operator */ - if (NULL == entry_and) - entry_and = find_parser("and"); - - /* Check that it's actually there. If not, that is a bug.*/ - assert (entry_and != NULL); + if (mark_stat (tree->pred_right)) + tree->need_stat = true; - if (last_pred) - switch (last_pred->p_type) - { - case NO_TYPE: - error (1, 0, _("oops -- invalid default insertion of and!")); - break; + return (false); - case PRIMARY_TYPE: - case CLOSE_PAREN: - /* We need to interpose the and operator. */ - new_pred = get_new_pred (entry_and); - new_pred->pred_func = pred_and; - new_pred->p_name = "-a"; - new_pred->p_type = BI_OP; - new_pred->p_prec = AND_PREC; - new_pred->need_stat = false; - new_pred->need_type = false; - new_pred->args.str = NULL; - new_pred->side_effects = false; - new_pred->no_default_print = false; - break; - - default: - break; - } - - new_pred = get_new_pred (entry); - new_pred->parser_entry = entry; - return new_pred; -} - -struct cost_assoc -{ - enum EvaluationCost cost; - char *name; -}; -struct cost_assoc cost_table[] = - { - { NeedsNothing, "Nothing" }, - { NeedsType, "Type" }, - { NeedsStatInfo, "StatInfo" }, - { NeedsLinkName, "LinkName" }, - { NeedsAccessInfo, "AccessInfo" }, - { NeedsSyncDiskHit, "SyncDiskHit" }, - { NeedsEventualExec, "EventualExec" }, - { NeedsImmediateExec, "ImmediateExec" }, - { NeedsUserInteraction, "UserInteraction" }, - { NeedsUnknown, "Unknown" } - }; - -struct prec_assoc -{ - short prec; - char *prec_name; -}; - -static struct prec_assoc prec_table[] = -{ - {NO_PREC, "no"}, - {COMMA_PREC, "comma"}, - {OR_PREC, "or"}, - {AND_PREC, "and"}, - {NEGATE_PREC, "negate"}, - {MAX_PREC, "max"}, - {-1, "unknown "} -}; - -struct op_assoc -{ - short type; - char *type_name; -}; - -static struct op_assoc type_table[] = -{ - {NO_TYPE, "no"}, - {PRIMARY_TYPE, "primary"}, - {UNI_OP, "uni_op"}, - {BI_OP, "bi_op"}, - {OPEN_PAREN, "open_paren "}, - {CLOSE_PAREN, "close_paren "}, - {-1, "unknown"} -}; - -static const char * -cost_name (enum EvaluationCost cost) -{ - unsigned int i; - unsigned int n = sizeof(cost_table)/sizeof(cost_table[0]); - - for (i = 0; i<n; ++i) - if (cost_table[i].cost == cost) - return cost_table[i].name; - return "unknown"; -} - - -static char * -type_name (type) - short type; -{ - int i; - - for (i = 0; type_table[i].type != (short) -1; i++) - if (type_table[i].type == type) - break; - return (type_table[i].type_name); -} - -static char * -prec_name (prec) - short prec; -{ - int i; - - for (i = 0; prec_table[i].prec != (short) -1; i++) - if (prec_table[i].prec == prec) - break; - return (prec_table[i].prec_name); -} - - -/* Walk the expression tree NODE to stdout. - INDENT is the number of levels to indent the left margin. */ - -void -print_tree (FILE *fp, struct predicate *node, int indent) -{ - int i; - - if (node == NULL) - return; - for (i = 0; i < indent; i++) - fprintf (fp, " "); - fprintf (fp, "pred=["); - print_predicate(fp, node); - fprintf (fp, "] type=%s prec=%s", - type_name (node->p_type), prec_name (node->p_prec)); - fprintf (fp, " cost=%s rate=%#03.2g %sside effects ", - cost_name(node->p_cost), - node->est_success_rate, - (node->side_effects ? "" : "no ")); - - if (node->need_stat || node->need_type) - { - int comma = 0; - - fprintf (fp, "Needs "); - if (node->need_stat) - { - fprintf (fp, "stat"); - comma = 1; - } - if (node->need_type) - { - fprintf (fp, "%stype", comma ? "," : ""); - } - } - fprintf (fp, "\n"); - - - for (i = 0; i < indent; i++) - fprintf (fp, " "); - if (NULL == node->pred_left && NULL == node->pred_right) - { - fprintf (fp, "no children.\n"); - } - else - { - if (node->pred_left) - { - fprintf (fp, "left:\n"); - print_tree (fp, node->pred_left, indent + 1); - } - else - { - fprintf (fp, "no left.\n"); - } - - for (i = 0; i < indent; i++) - fprintf (fp, " "); - if (node->pred_right) - { - fprintf (fp, "right:\n"); - print_tree (fp, node->pred_right, indent + 1); - } - else - { - fprintf (fp, "no right.\n"); - } + default: + error (1, 0, "oops -- invalid expression type!"); + return (false); } } diff --git a/find/util.c b/find/util.c index 40f36d5a..95ce57e0 100644 --- a/find/util.c +++ b/find/util.c @@ -1,77 +1,119 @@ /* util.c -- functions for initializing new tree elements, and other things. - Copyright (C) 1990, 91, 92, 93, 94, 2000, 2003, 2004, 2005 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, 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 <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> #include "defs.h" + +/* Return the last component of pathname FNAME, with leading slashes + compressed into one slash. */ -#include <fcntl.h> -#ifdef HAVE_SYS_UTSNAME_H -#include <sys/utsname.h> -#endif -#include <sys/time.h> -#include <ctype.h> -#include <string.h> -#include <limits.h> -#include <errno.h> -#include <assert.h> +char * +basename (fname) + char *fname; +{ + char *p; -#include "xalloc.h" -#include "quotearg.h" -#include "timespec.h" -#include "error.h" -#include "verify.h" -#include "openat.h" + /* For "/", "//", etc., return "/". */ + for (p = fname; *p == '/'; ++p) + /* Do nothing. */ ; + if (*p == '\0') + return p - 1; + p = strrchr (fname, '/'); + return (p == NULL ? fname : p + 1); +} + +/* Return a pointer to a new predicate structure, which has been + linked in as the last one in the predicates list. -#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 + Set `predicates' to point to the start of the predicates list. + Set `last_pred' to point to the new last predicate in the list. + + Set all cells in the new structure to the default values. */ - -struct debug_option_assoc +struct predicate * +get_new_pred () { - char *name; - int val; - char *docstring; -}; -static struct debug_option_assoc debugassoc[] = - { - { "help", DebugHelp, "Explain the various -D options" }, - { "tree", DebugExpressionTree, "Display the expression tree" }, - { "search",DebugSearch, "Navigate the directory tree verbosely" }, - { "stat", DebugStat, "Trace calls to stat(2) and lstat(2)" }, - { "rates", DebugSuccessRates, "Indicate how often each predicate succeeded" }, - { "opt", DebugExpressionTree|DebugTreeOpt, "Show diagnostic information relating to optimisation" }, - { "exec", DebugExec, "Show diagnostic information relating to -exec, -execdir, -ok and -okdir" } - }; -#define N_DEBUGASSOC (sizeof(debugassoc)/sizeof(debugassoc[0])) + register struct predicate *new_pred; + if (predicates == NULL) + { + predicates = (struct predicate *) + xmalloc (sizeof (struct predicate)); + last_pred = predicates; + } + else + { + new_pred = (struct predicate *) xmalloc (sizeof (struct predicate)); + last_pred->pred_next = new_pred; + last_pred = new_pred; + } + last_pred->pred_func = NULL; +#ifdef DEBUG + last_pred->p_name = NULL; +#endif /* DEBUG */ + last_pred->p_type = NO_TYPE; + last_pred->p_prec = NO_PREC; + last_pred->side_effects = false; + last_pred->need_stat = true; + last_pred->args.str = NULL; + last_pred->pred_next = NULL; + last_pred->pred_left = NULL; + last_pred->pred_right = NULL; + return (last_pred); +} + +/* Return a pointer to a new predicate, with operator check. + Like get_new_pred, but it checks to make sure that the previous + predicate is an operator. If it isn't, the AND operator is inserted. */ +struct predicate * +get_new_pred_chk_op () +{ + struct predicate *new_pred; - -/* Add a primary of predicate type PRED_FUNC (described by ENTRY) to the predicate input list. + if (last_pred) + switch (last_pred->p_type) + { + case NO_TYPE: + error (1, 0, "oops -- invalid default insertion of and!"); + break; + + case PRIMARY_TYPE: + case CLOSE_PAREN: + new_pred = get_new_pred (); + new_pred->pred_func = pred_and; +#ifdef DEBUG + new_pred->p_name = find_pred_name (pred_and); +#endif /* DEBUG */ + new_pred->p_type = BI_OP; + new_pred->p_prec = AND_PREC; + new_pred->need_stat = false; + new_pred->args.str = NULL; + + default: + break; + } + return (get_new_pred ()); +} + +/* Add a primary of predicate type PRED_FUNC to the predicate input list. Return a pointer to the predicate node just inserted. @@ -88,952 +130,29 @@ static struct debug_option_assoc debugassoc[] = operator. */ struct predicate * -insert_primary_withpred (const struct parser_table *entry, PRED_FUNC pred_func) +insert_primary (pred_func) + boolean (*pred_func) (); { struct predicate *new_pred; - new_pred = get_new_pred_chk_op (entry); + new_pred = get_new_pred_chk_op (); new_pred->pred_func = pred_func; - new_pred->p_name = entry->parser_name; +#ifdef DEBUG + new_pred->p_name = find_pred_name (pred_func); +#endif /* DEBUG */ new_pred->args.str = NULL; new_pred->p_type = PRIMARY_TYPE; new_pred->p_prec = NO_PREC; - return new_pred; -} - -/* Add a primary described by ENTRY to the predicate input list. - - Return a pointer to the predicate node just inserted. - - Fills in the following cells of the new predicate node: - - pred_func PRED_FUNC - args(.str) NULL - p_type PRIMARY_TYPE - p_prec NO_PREC - - Other cells that need to be filled in are defaulted by - get_new_pred_chk_op, which is used to insure that the prior node is - either not there at all (we are the very first node) or is an - operator. */ -struct predicate * -insert_primary (const struct parser_table *entry) -{ - assert (entry->pred_func != NULL); - return insert_primary_withpred(entry, entry->pred_func); -} - - - -static void -show_valid_debug_options(FILE *fp, int full) -{ - int i; - if (full) - { - fprintf(fp, "Valid arguments for -D:\n"); - for (i=0; i<N_DEBUGASSOC; ++i) - { - fprintf(fp, "%-10s %s\n", - debugassoc[i].name, - debugassoc[i].docstring); - } - } - else - { - for (i=0; i<N_DEBUGASSOC; ++i) - { - fprintf(fp, "%s%s", (i>0 ? "|" : ""), debugassoc[i].name); - } - } + return (new_pred); } void -usage (FILE *fp, int status, char *msg) +usage (msg) + char *msg; { if (msg) - fprintf (fp, "%s: %s\n", program_name, msg); - - fprintf (fp, _("Usage: %s [-H] [-L] [-P] [-Olevel] [-D "), program_name); - show_valid_debug_options(fp, 0); - fprintf (fp, _("] [path...] [expression]\n")); - if (0 != status) - exit (status); -} - -void -set_stat_placeholders(struct stat *p) -{ -#if HAVE_STRUCT_STAT_ST_BIRTHTIME - p->st_birthtime = 0; -#endif -#if HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC - p->st_birthtimensec = 0; -#endif -#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC - p->st_birthtimespec.tv_nsec = -1; -#endif -#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_SEC - p->st_birthtimespec.tv_sec = 0; -#endif -} - - -/* Get the stat information for a file, if it is - * not already known. - */ -int -get_statinfo (const char *pathname, const char *name, struct stat *p) -{ - /* Set markers in fields so we have a good idea if the implementation - * didn't bother to set them (e.g., NetBSD st_birthtimespec for MS-DOS - * files) - */ - if (!state.have_stat) - { - set_stat_placeholders(p); - if (0 == (*options.xstat) (name, p)) - { - if (00000 == p->st_mode) - { - /* Savannah bug #16378. */ - error(0, 0, _("Warning: file %s appears to have mode 0000"), - quotearg_n_style(0, options.err_quoting_style, name)); - } - } - else - { - if (!options.ignore_readdir_race || (errno != ENOENT) ) - { - error (0, errno, "%s", - safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - } - return -1; - } - } - state.have_stat = true; - state.have_type = true; - state.type = p->st_mode; - - return 0; -} - - -/* Get the stat/type information for a file, if it is - * not already known. - */ -int -get_info (const char *pathname, - struct stat *p, - struct predicate *pred_ptr) -{ - boolean todo = false; - - /* If we need the full stat info, or we need the type info but don't - * already have it, stat the file now. - */ - if (pred_ptr->need_stat) - todo = true; - else if ((pred_ptr->need_type && (0 == state.have_type))) - todo = true; - - if (todo) - return get_statinfo(pathname, state.rel_pathname, p); - else - return 0; -} - -/* Determine if we can use O_NOFOLLOW. - */ -#if defined O_NOFOLLOW -boolean -check_nofollow(void) -{ - struct utsname uts; - float release; - - if (0 == O_NOFOLLOW) - { - return false; - } - - if (0 == uname(&uts)) - { - /* POSIX requires that atof() ignore "unrecognised suffixes". */ - release = atof(uts.release); - - if (0 == strcmp("Linux", uts.sysname)) - { - /* Linux kernels 2.1.126 and earlier ignore the O_NOFOLLOW flag. */ - return release >= 2.2; /* close enough */ - } - else if (0 == strcmp("FreeBSD", uts.sysname)) - { - /* FreeBSD 3.0-CURRENT and later support it */ - return release >= 3.1; - } - } - - /* Well, O_NOFOLLOW was defined, so we'll try to use it. */ - return true; -} -#endif - - - -/* Examine the predicate list for instances of -execdir or -okdir - * which have been terminated with '+' (build argument list) rather - * than ';' (singles only). If there are any, run them (this will - * have no effect if there are no arguments waiting). - */ -static void -do_complete_pending_execdirs(struct predicate *p, int dirfd) -{ - if (NULL == p) - return; - - assert (state.execdirs_outstanding); - - do_complete_pending_execdirs(p->pred_left, dirfd); - - if (pred_is(p, pred_execdir) || pred_is(p, pred_okdir)) - { - /* It's an exec-family predicate. p->args.exec_val is valid. */ - if (p->args.exec_vec.multiple) - { - struct exec_val *execp = &p->args.exec_vec; - - /* This one was terminated by '+' and so might have some - * left... Run it if necessary. - */ - if (execp->state.todo) - { - /* There are not-yet-executed arguments. */ - launch (&execp->ctl, &execp->state); - } - } - } - - do_complete_pending_execdirs(p->pred_right, dirfd); -} - -void -complete_pending_execdirs(int dirfd) -{ - if (state.execdirs_outstanding) - { - do_complete_pending_execdirs(get_eval_tree(), dirfd); - state.execdirs_outstanding = false; - } -} - - - -/* Examine the predicate list for instances of -exec which have been - * terminated with '+' (build argument list) rather than ';' (singles - * only). If there are any, run them (this will have no effect if - * there are no arguments waiting). - */ -void -complete_pending_execs(struct predicate *p) -{ - if (NULL == p) - return; - - complete_pending_execs(p->pred_left); - - /* It's an exec-family predicate then p->args.exec_val is valid - * and we can check it. - */ - /* XXX: what about pred_ok() ? */ - if (pred_is(p, pred_exec) && p->args.exec_vec.multiple) - { - struct exec_val *execp = &p->args.exec_vec; - - /* This one was terminated by '+' and so might have some - * left... Run it if necessary. Set state.exit_status if - * there are any problems. - */ - if (execp->state.todo) - { - /* There are not-yet-executed arguments. */ - launch (&execp->ctl, &execp->state); - } - } - - complete_pending_execs(p->pred_right); -} - -static void -traverse_tree(struct predicate *tree, - void (*callback)(struct predicate*)) -{ - if (tree->pred_left) - traverse_tree(tree->pred_left, callback); - - callback(tree); - - if (tree->pred_right) - traverse_tree(tree->pred_right, callback); -} - -static void -flush_and_close_output_files(struct predicate *p) -{ - if (pred_is(p, pred_fprint) - || pred_is(p, pred_fprintf) - || pred_is(p, pred_fls) - || pred_is(p, pred_fprint0)) - { - FILE *f = p->args.printf_vec.stream; - bool failed; - - if (f == stdout || f == stderr) - failed = fflush(p->args.printf_vec.stream) == EOF; - else - failed = fclose(p->args.printf_vec.stream) == EOF; - - if (failed) - nonfatal_file_error(p->args.printf_vec.filename); - } - else if (pred_is(p, pred_print)) - { - if (fflush(p->args.printf_vec.stream) == EOF) - { - nonfatal_file_error(p->args.printf_vec.filename); - } - } - else if (pred_is(p, pred_ls) || pred_is(p, pred_print0)) - { - if (fflush(stdout) == EOF) - { - /* XXX: migrate to printf_vec. */ - nonfatal_file_error("standard output"); - } - } + fprintf (stderr, "%s: %s\n", program_name, msg); + fprintf (stderr, "\ +Usage: %s [path...] [expression]\n", program_name); + exit (1); } - -/* Complete any outstanding commands. - */ -void -cleanup(void) -{ - struct predicate *eval_tree = get_eval_tree(); - if (eval_tree) - { - traverse_tree(eval_tree, complete_pending_execs); - complete_pending_execdirs(get_current_dirfd()); - traverse_tree(eval_tree, flush_and_close_output_files); - } -} - -/* Savannah bug #16378 manifests as an assertion failure in pred_type() - * when an NFS server returns st_mode with value 0 (of course the stat(2) - * system call is itself returning 0 in this case). - */ -#undef DEBUG_SV_BUG_16378 -#if defined DEBUG_SV_BUG_16378 -static int hook_fstatat(int fd, const char *name, struct stat *p, int flags) -{ - static int warned = 0; - - if (!warned) - { - /* No use of _() here; no point asking translators to translate a debug msg */ - error(0, 0, - "Warning: some debug code is enabled for Savannah bug #16378; " - "this should not occur in released versions of findutils!"); - warned = 1; - } - - if (0 == strcmp(name, "./mode0file") - || 0 == strcmp(name, "mode0file")) - { - time_t now = time(NULL); - long day = 86400; - - p->st_rdev = 0; - p->st_dev = 0x300; - p->st_ino = 0; - p->st_mode = 0; /* SV bug #16378 */ - p->st_nlink = 1; - p->st_uid = geteuid(); - p->st_gid = 0; - p->st_size = 42; - p->st_blksize = 32768; - p->st_atime = now-1*day; - p->st_mtime = now-2*day; - p->st_ctime = now-3*day; - - return 0; - } - return fstatat(fd, name, p, flags); -} - -# undef fstatat -# define fstatat(fd,name,p,flags) hook_fstatat((fd),(name),(p),(flags)) -#endif - - -static int -fallback_stat(const char *name, struct stat *p, int prev_rv) -{ - /* Our original stat() call failed. Perhaps we can't follow a - * symbolic link. If that might be the problem, lstat() the link. - * Otherwise, admit defeat. - */ - switch (errno) - { - case ENOENT: - case ENOTDIR: - if (options.debug_options & DebugStat) - fprintf(stderr, "fallback_stat(): stat(%s) failed; falling back on lstat()\n", name); - return fstatat(state.cwd_dir_fd, name, p, AT_SYMLINK_NOFOLLOW); - - case EACCES: - case EIO: - case ELOOP: - case ENAMETOOLONG: -#ifdef EOVERFLOW - case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */ -#endif - default: - return prev_rv; - } -} - - -/* optionh_stat() implements the stat operation when the -H option is - * in effect. - * - * If the item to be examined is a command-line argument, we follow - * symbolic links. If the stat() call fails on the command-line item, - * we fall back on the properties of the symbolic link. - * - * If the item to be examined is not a command-line argument, we - * examine the link itself. - */ -int -optionh_stat(const char *name, struct stat *p) -{ - if (AT_FDCWD != state.cwd_dir_fd) - assert (state.cwd_dir_fd >= 0); - set_stat_placeholders(p); - if (0 == state.curdepth) - { - /* This file is from the command line; deference the link (if it - * is a link). - */ - int rv; - rv = fstatat(state.cwd_dir_fd, name, p, 0); - if (0 == rv) - return 0; /* success */ - else - return fallback_stat(name, p, rv); - } - else - { - /* Not a file on the command line; do not dereference the link. - */ - return fstatat(state.cwd_dir_fd, name, p, AT_SYMLINK_NOFOLLOW); - } -} - -/* optionl_stat() implements the stat operation when the -L option is - * in effect. That option makes us examine the thing the symbolic - * link points to, not the symbolic link itself. - */ -int -optionl_stat(const char *name, struct stat *p) -{ - int rv; - if (AT_FDCWD != state.cwd_dir_fd) - assert (state.cwd_dir_fd >= 0); - - set_stat_placeholders(p); - rv = fstatat(state.cwd_dir_fd, name, p, 0); - if (0 == rv) - return 0; /* normal case. */ - else - return fallback_stat(name, p, rv); -} - -/* optionp_stat() implements the stat operation when the -P option is - * in effect (this is also the default). That option makes us examine - * the symbolic link itself, not the thing it points to. - */ -int -optionp_stat(const char *name, struct stat *p) -{ - assert ((state.cwd_dir_fd >= 0) || (state.cwd_dir_fd==AT_FDCWD)); - set_stat_placeholders(p); - return fstatat(state.cwd_dir_fd, name, p, AT_SYMLINK_NOFOLLOW); -} - - -static uintmax_t stat_count = 0u; - -int -debug_stat (const char *file, struct stat *bufp) -{ - ++stat_count; - fprintf (stderr, "debug_stat (%s)\n", file); - - switch (options.symlink_handling) - { - case SYMLINK_ALWAYS_DEREF: - return optionl_stat(file, bufp); - case SYMLINK_DEREF_ARGSONLY: - return optionh_stat(file, bufp); - case SYMLINK_NEVER_DEREF: - return optionp_stat(file, bufp); - } - /*NOTREACHED*/ - assert (0); - return -1; -} - - -int -following_links(void) -{ - switch (options.symlink_handling) - { - case SYMLINK_ALWAYS_DEREF: - return 1; - case SYMLINK_DEREF_ARGSONLY: - return (state.curdepth == 0); - case SYMLINK_NEVER_DEREF: - default: - return 0; - } -} - - -/* Take a "mode" indicator and fill in the files of 'state'. - */ -int -digest_mode(mode_t mode, - const char *pathname, - const char *name, - struct stat *pstat, - boolean leaf) -{ - /* If we know the type of the directory entry, and it is not a - * symbolic link, we may be able to avoid a stat() or lstat() call. - */ - if (mode) - { - if (S_ISLNK(mode) && following_links()) - { - /* mode is wrong because we should have followed the symlink. */ - if (get_statinfo(pathname, name, pstat) != 0) - return 0; - mode = state.type = pstat->st_mode; - state.have_type = true; - } - else - { - state.have_type = true; - pstat->st_mode = state.type = mode; - } - } - else - { - /* Mode is not yet known; may have to stat the file unless we - * can deduce that it is not a directory (which is all we need to - * know at this stage) - */ - if (leaf) - { - state.have_stat = false; - state.have_type = false;; - state.type = 0; - } - else - { - if (get_statinfo(pathname, name, pstat) != 0) - return 0; - - /* If -L is in effect and we are dealing with a symlink, - * st_mode is the mode of the pointed-to file, while mode is - * the mode of the directory entry (S_IFLNK). Hence now - * that we have the stat information, override "mode". - */ - state.type = pstat->st_mode; - state.have_type = true; - } - } - - /* success. */ - return 1; -} - - -/* Return true if there are no predicates with no_default_print in - predicate list PRED, false if there are any. - Returns true if default print should be performed */ - -boolean -default_prints (struct predicate *pred) -{ - while (pred != NULL) - { - if (pred->no_default_print) - return (false); - pred = pred->pred_next; - } - return (true); -} - -boolean -looks_like_expression(const char *arg, boolean leading) -{ - switch (arg[0]) - { - case '-': - if (arg[1]) /* "-foo" is an expression. */ - return true; - else - return false; /* Just "-" is a filename. */ - break; - - case ')': - case ',': - if (arg[1]) - return false; /* )x and ,z are not expressions */ - else - return !leading; /* A leading ) or , is not either */ - - /* ( and ! are part of an expression, but (2 and !foo are - * filenames. - */ - case '!': - case '(': - if (arg[1]) - return false; - else - return true; - - default: - return false; - } -} - -static void -process_debug_options(char *arg) -{ - const char *p; - char *token_context = NULL; - const char delimiters[] = ","; - boolean empty = true; - size_t i; - - p = strtok_r(arg, delimiters, &token_context); - while (p) - { - empty = false; - - for (i=0; i<N_DEBUGASSOC; ++i) - { - if (0 == strcmp(debugassoc[i].name, p)) - { - options.debug_options |= debugassoc[i].val; - break; - } - } - if (i >= N_DEBUGASSOC) - { - error(0, 0, _("Ignoring unrecognised debug flag %s"), - quotearg_n_style(0, options.err_quoting_style, arg)); - } - p = strtok_r(NULL, delimiters, &token_context); - } - if (empty) - { - error(1, 0, _("Empty argument to the -D option.")); - } - else if (options.debug_options & DebugHelp) - { - show_valid_debug_options(stdout, 1); - exit(0); - } -} - -static void -process_optimisation_option(const char *arg) -{ - if (0 == arg[0]) - { - error(1, 0, _("The -O option must be immediately followed by a decimal integer")); - } - else - { - unsigned long opt_level; - char *end; - - if (!isdigit( (unsigned char) arg[0] )) - { - error(1, 0, _("Please specify a decimal number immediately after -O")); - } - else - { - int prev_errno = errno; - errno = 0; - - opt_level = strtoul(arg, &end, 10); - if ( (0==opt_level) && (end==arg) ) - { - error(1, 0, _("Please specify a decimal number immediately after -O")); - } - else if (*end) - { - /* unwanted trailing characters. */ - error(1, 0, _("Invalid optimisation level %s"), arg); - } - else if ( (ULONG_MAX==opt_level) && errno) - { - error(1, errno, _("Invalid optimisation level %s"), arg); - } - else if (opt_level > USHRT_MAX) - { - /* tricky to test, as on some platforms USHORT_MAX and ULONG_MAX - * can have the same value, though this is unusual. - */ - error(1, 0, _("Optimisation level %lu is too high. " - "If you want to find files very quickly, " - "consider using GNU locate."), - opt_level); - } - else - { - options.optimisation_level = opt_level; - errno = prev_errno; - } - } - } -} - -int -process_leading_options(int argc, char *argv[]) -{ - int i, end_of_leading_options; - - for (i=1; (end_of_leading_options = i) < argc; ++i) - { - if (0 == strcmp("-H", argv[i])) - { - /* Meaning: dereference symbolic links on command line, but nowhere else. */ - set_follow_state(SYMLINK_DEREF_ARGSONLY); - } - else if (0 == strcmp("-L", argv[i])) - { - /* Meaning: dereference all symbolic links. */ - set_follow_state(SYMLINK_ALWAYS_DEREF); - } - else if (0 == strcmp("-P", argv[i])) - { - /* Meaning: never dereference symbolic links (default). */ - set_follow_state(SYMLINK_NEVER_DEREF); - } - else if (0 == strcmp("--", argv[i])) - { - /* -- signifies the end of options. */ - end_of_leading_options = i+1; /* Next time start with the next option */ - break; - } - else if (0 == strcmp("-D", argv[i])) - { - process_debug_options(argv[i+1]); - ++i; /* skip the argument too. */ - } - else if (0 == strncmp("-O", argv[i], 2)) - { - process_optimisation_option(argv[i]+2); - } - else - { - /* Hmm, must be one of - * (a) A path name - * (b) A predicate - */ - end_of_leading_options = i; /* Next time start with this option */ - break; - } - } - return end_of_leading_options; -} - -static struct timespec -now(void) -{ - struct timespec retval; - struct timeval tv; - time_t t; - - if (0 == gettimeofday(&tv, NULL)) - { - retval.tv_sec = tv.tv_sec; - retval.tv_nsec = tv.tv_usec * 1000; /* convert unit from microseconds to nanoseconds */ - return retval; - } - t = time(NULL); - assert (t != (time_t)-1); - retval.tv_sec = t; - retval.tv_nsec = 0; - return retval; -} - -void -set_option_defaults(struct options *p) -{ - if (getenv("POSIXLY_CORRECT")) - p->posixly_correct = true; - else - p->posixly_correct = false; - - /* We call check_nofollow() before setlocale() because the numbers - * for which we check (in the results of uname) definitiely have "." - * as the decimal point indicator even under locales for which that - * is not normally true. Hence atof() would do the wrong thing - * if we call it after setlocale(). - */ -#ifdef O_NOFOLLOW - p->open_nofollow_available = check_nofollow(); -#else - p->open_nofollow_available = false; -#endif - - p->regex_options = RE_SYNTAX_EMACS; - - if (isatty(0)) - { - p->warnings = true; - p->literal_control_chars = false; - } - else - { - p->warnings = false; - p->literal_control_chars = false; /* may change */ - } - if (p->posixly_correct) - { - p->warnings = false; - } - - p->do_dir_first = true; - p->explicit_depth = false; - p->maxdepth = p->mindepth = -1; - - p->start_time = now(); - p->cur_day_start.tv_sec = p->start_time.tv_sec - DAYSECS; - p->cur_day_start.tv_nsec = p->start_time.tv_nsec; - - p->full_days = false; - p->stay_on_filesystem = false; - p->ignore_readdir_race = false; - - if (p->posixly_correct) - p->output_block_size = 512; - else - p->output_block_size = 1024; - - p->debug_options = 0uL; - p->optimisation_level = 0; - - if (getenv("FIND_BLOCK_SIZE")) - { - error (1, 0, _("The environment variable FIND_BLOCK_SIZE is not supported, the only thing that affects the block size is the POSIXLY_CORRECT environment variable")); - } - -#if LEAF_OPTIMISATION - /* The leaf optimisation is enabled. */ - p->no_leaf_check = false; -#else - /* The leaf optimisation is disabled. */ - p->no_leaf_check = true; -#endif - - set_follow_state(SYMLINK_NEVER_DEREF); /* The default is equivalent to -P. */ - - p->err_quoting_style = locale_quoting_style; -} - - -/* get_start_dirfd - * - * Returns the fd for the directory we started in. - */ -int get_start_dirfd(void) -{ - return starting_desc; -} - -/* apply_predicate - * - */ -boolean -apply_predicate(const char *pathname, struct stat *stat_buf, struct predicate *p) -{ - ++p->perf.visits; - - if (p->need_stat || p->need_type) - { - /* We may need a stat here. */ - if (get_info(pathname, stat_buf, p) != 0) - return false; - } - if ((p->pred_func)(pathname, stat_buf, p)) - { - ++(p->perf.successes); - return true; - } - else - { - return false; - } -} - - -/* safely_quote_err_filename - * - */ -const char * -safely_quote_err_filename (int n, char const *arg) -{ - return quotearg_n_style (n, options.err_quoting_style, arg); -} - -/* report_file_err - */ -static void -report_file_err(int exitval, int errno_value, const char *name) -{ - /* It is important that the errno value is passed in as a function - * argument before we call safely_quote_err_filename(), because otherwise - * we might find that safely_quote_err_filename() changes errno. - */ - if (state.exit_status < 1) - state.exit_status = 1; - - error (exitval, errno_value, "%s", safely_quote_err_filename(0, name)); -} - -/* fatal_file_error - * - */ -void -fatal_file_error(const char *name) -{ - report_file_err(1, errno, name); - /*NOTREACHED*/ - abort(); -} - -void -nonfatal_file_error(const char *name) -{ - report_file_err(0, errno, name); -} - diff --git a/find/version.c b/find/version.c new file mode 100644 index 00000000..460883ff --- /dev/null +++ b/find/version.c @@ -0,0 +1 @@ +char *version_string = "4.1"; |