From 1afaa5688ab26d17bfb4d3af3250fabeb37768d1 Mon Sep 17 00:00:00 2001 From: jay Date: Mon, 21 Nov 2005 05:42:27 +0000 Subject: [PATCH] Findutils 4.3.x defaults to using the the FTS implementation of find. --- NEWS | 9 + configure.in | 8 +- find/Makefile.am | 26 ++- find/defs.h | 41 ++-- find/find.c | 431 +--------------------------------- find/finddata.c | 48 ++++ find/ftsfind.c | 702 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ find/parser.c | 61 ++++- find/pred.c | 25 +- find/util.c | 382 ++++++++++++++++++++++++++++++ import-gnulib.sh | 2 +- m4/Makefile.am | 3 +- m4/withfts.m4 | 13 ++ 13 files changed, 1287 insertions(+), 464 deletions(-) create mode 100644 find/finddata.c create mode 100644 find/ftsfind.c create mode 100644 m4/withfts.m4 diff --git a/NEWS b/NEWS index 26fbec5..fd03d8b 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,13 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) +* Major changes in release 4.3.0 + +** Functional Changes + +By default, find now uses the fts() function to search the file +system, which means that it can search deeper directory hierarchies. +You can go back to the old filesystem search implementation by using +the configure option '--without-fts'. + * Major changes in release 4.2.26 ** Public Service Announcements diff --git a/configure.in b/configure.in index e3aa3f3..d77bbd2 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([GNU findutils], 4.2.26, [bug-findutils@gnu.org]) +AC_INIT([GNU findutils], 4.3.0-CVS, [bug-findutils@gnu.org]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([find/pred.c]) @@ -8,9 +8,13 @@ AC_CANONICAL_HOST AC_CONFIG_MACRO_DIR(gnulib/m4) dnl Set of available languages. -ALL_LINGUAS="be ca da de el eo es et fi fr ga gl hr hu id it ja ko lg ms nl pl pt pt_BR ro ru sk sl sr sv tr vi zh_CN zh_TW rw" +ALL_LINGUAS="be ca da de el eo es et fi fr ga gl hr hu id it ja ko lg ms nl pl pt pt_BR ro ru sk sl sr sv tr vi zh_CN rw" AC_SUBST(INCLUDES)dnl + +dnl check for --with-fts +FIND_WITH_FTS + AC_ARG_ENABLE(id-cache, [ --enable-id-cache cache all UIDs & GIDs; avoid if using NIS or Hesiod], AC_DEFINE(CACHE_IDS, 1, [Define if you want find -nouser and -nogroup to make tables of diff --git a/find/Makefile.am b/find/Makefile.am index 8e71a32..2f30303 100644 --- a/find/Makefile.am +++ b/find/Makefile.am @@ -1,12 +1,32 @@ AUTOMAKE_OPTIONS = std-options localedir = $(datadir)/locale -bin_PROGRAMS = find # noinst_PROGRAMS = regexprops # regexprops_SOURCES = regexprops.c -find_SOURCES = find.c fstype.c parser.c pred.c tree.c util.c version.c + +noinst_LIBRARIES = libfindtools.a +libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c version.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 = ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ +LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ man_MANS = find.1 SUBDIRS = testsuite diff --git a/find/defs.h b/find/defs.h index c5bfaf7..92dfa1a 100644 --- a/find/defs.h +++ b/find/defs.h @@ -83,9 +83,14 @@ int stat PARAMS((const char *__path, struct stat *__statbuf)); 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)); int get_statinfo PARAMS((const char *pathname, const char *name, struct stat *p)); +#if ! defined HAVE_FCHDIR && ! defined fchdir +# define fchdir(fd) (-1) +#endif + #ifndef S_ISUID @@ -337,9 +342,9 @@ struct predicate const struct parser_table* parser_entry; }; -/* find.c. */ -int get_info PARAMS((const char *pathname, const char *name, struct stat *p, struct predicate *pred_ptr)); -int following_links(void); +/* find.c, ftsfind.c */ +boolean is_fts_enabled(); + /* find library function declarations. */ @@ -429,7 +434,7 @@ boolean pred_amin PARAMS((char *pathname, struct stat *stat_buf, struct predicat boolean pred_and PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_anewer PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_atime PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); -boolean pred_closeparen PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_close PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_cmin PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_cnewer PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_comma PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); @@ -462,7 +467,7 @@ boolean pred_nogroup PARAMS((char *pathname, struct stat *stat_buf, struct predi boolean pred_nouser PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_ok PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_okdir PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); -boolean pred_openparen PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_open PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_or PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_path PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_perm PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); @@ -509,10 +514,15 @@ struct predicate *get_new_pred_chk_op PARAMS((const struct parser_table *entry)) 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((char *msg)); +extern boolean check_nofollow(void); +extern void complete_pending_execs(struct predicate *p); +extern void complete_pending_execdirs(struct predicate *p); +/* find.c. */ +int get_info PARAMS((const char *pathname, const char *name, struct stat *p, struct predicate *pred_ptr)); +int following_links(void); +int digest_mode(mode_t mode, const char *pathname, const char *name, struct stat *pstat, boolean leaf); +boolean default_prints (struct predicate *pred); -extern char *program_name; -extern struct predicate *predicates; -extern struct predicate *last_pred; struct options { @@ -588,8 +598,8 @@ struct state Used for stat, readlink, remove, and opendir. */ char *rel_pathname; - /* Length of current path. */ - int path_length; + /* 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. */ @@ -598,12 +608,15 @@ struct state /* Status value to return to system. */ int exit_status; }; -extern struct state state; +/* finddata.c */ +extern struct state state; extern char const *starting_dir; extern int starting_desc; -#if ! defined HAVE_FCHDIR && ! defined fchdir -# define fchdir(fd) (-1) -#endif +extern struct predicate *eval_tree; +extern char *program_name; +extern struct predicate *predicates; +extern struct predicate *last_pred; + #endif diff --git a/find/find.c b/find/find.c index 7838b8f..36b789e 100644 --- a/find/find.c +++ b/find/find.c @@ -43,10 +43,6 @@ #include #endif -#ifdef HAVE_SYS_UTSNAME_H -#include -#endif - #include "../gnulib/lib/xalloc.h" #include "../gnulib/lib/human.h" #include "../gnulib/lib/canonicalize.h" @@ -88,12 +84,7 @@ 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, struct stat *statp, char *parent)); -static void complete_pending_execdirs(struct predicate *p); -static void complete_pending_execs (struct predicate *p); - - -static boolean default_prints PARAMS((struct predicate *pred)); /* Name this program was run with. */ char *program_name; @@ -104,24 +95,13 @@ struct predicate *predicates; /* The last predicate allocated. */ struct predicate *last_pred; -/* The root of the evaluation tree. */ -static struct predicate *eval_tree = NULL; - - -struct options options; -struct state state; - -/* The full path of the initial working directory, or "." if - STARTING_DESC is nonnegative. */ -char const *starting_dir = "."; - /* 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; /* The stat buffer of the initial working directory. */ -struct stat starting_stat_buf; +static struct stat starting_stat_buf; enum ChdirSymlinkHandling { @@ -145,248 +125,6 @@ enum WdSanityCheckFatality 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; - } -} - - -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: -#ifdef DEBUG_STAT - fprintf(stderr, "fallback_stat(): stat(%s) failed; falling back on lstat()\n", name); -#endif - return lstat(name, p); - - 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 (0 == state.curdepth) - { - /* This file is from the command line; deference the link (if it - * is a link). - */ - int rv = stat(name, p); - 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 lstat(name, p); - } -} - -/* 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 = stat(name, p); - 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) -{ - return lstat(name, p); -} - -#ifdef DEBUG_STAT -static uintmax_t stat_count = 0u; - -static 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); - } -} -#endif /* DEBUG_STAT */ - -void -set_follow_state(enum SymlinkOption opt) -{ - switch (opt) - { - case SYMLINK_ALWAYS_DEREF: /* -L */ - options.xstat = optionl_stat; - options.no_leaf_check = true; - break; - - case SYMLINK_NEVER_DEREF: /* -P (default) */ - options.xstat = optionp_stat; - /* Can't turn no_leaf_check off because the user might have specified - * -noleaf anyway - */ - break; - - case SYMLINK_DEREF_ARGSONLY: /* -H */ - options.xstat = optionh_stat; - options.no_leaf_check = true; - } - - options.symlink_handling = opt; - - /* For DEBUG_STAT, the choice is made at runtime within debug_stat() - * by checking the contents of the symlink_handling variable. - */ -#if defined(DEBUG_STAT) - options.xstat = debug_stat; -#endif /* !DEBUG_STAT */ -} - - -/* Complete any outstanding commands. - */ -void -cleanup(void) -{ - if (eval_tree) - { - complete_pending_execs(eval_tree); - complete_pending_execdirs(eval_tree); - } -} - -/* 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) -{ - if (!state.have_stat && (*options.xstat) (name, p) != 0) - { - if (!options.ignore_readdir_race || (errno != ENOENT) ) - { - error (0, errno, "%s", 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, - const char *name, - struct stat *p, - struct predicate *pred_ptr) -{ - /* If we need the full stat info, or we need the type info but don't - * already have it, stat the file now. - */ - (void) name; - if (pred_ptr->need_stat) - { - return get_statinfo(pathname, state.rel_pathname, p); - } - if ((pred_ptr->need_type && (0 == state.have_type))) - { - return get_statinfo(pathname, state.rel_pathname, p); - } - return 0; -} - -/* Determine if we can use O_NOFOLLOW. - */ -#if defined(O_NOFOLLOW) -static boolean -check_nofollow(void) -{ - struct utsname uts; - float release; - - 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 - -int main (int argc, char **argv) { int i; @@ -528,7 +266,7 @@ main (int argc, char **argv) assert(entry_close != NULL); assert(entry_print != NULL); - parse_openparen (entry_open, argv, &argc); + parse_open (entry_open, argv, &argc); parse_begin_user_args(argv, argc, last_pred, predicates); pred_sanity_check(last_pred); @@ -584,7 +322,7 @@ main (int argc, char **argv) else { /* `( user-supplied-expression ) -print'. */ - parse_closeparen (entry_close, argv, &argc); + parse_close (entry_close, argv, &argc); pred_sanity_check(last_pred); parse_print (entry_print, argv, &argc); pred_sanity_check(last_pred); @@ -691,8 +429,14 @@ main (int argc, char **argv) cleanup(); return state.exit_status; } + +boolean is_fts_enabled() +{ + /* this version of find (i.e. this main()) does not use fts. */ + return false; +} - + static char * specific_dirname(const char *dir) { @@ -1383,7 +1127,7 @@ at_top (char *pathname, char *base = base_name(pathname); state.curdepth = 0; - state.path_length = strlen (pathname); + state.starting_path_length = strlen (pathname); if (0 == strcmp(pathname, parent_dir) || 0 == strcmp(parent_dir, ".")) @@ -1538,65 +1282,6 @@ issue_loop_warning(const char *name, const char *pathname, int level) } } -/* Take a "mode" indicator and fill in the files of 'state'. - */ -static 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; -} - /* Recursively descend path PATHNAME, applying the predicates. @@ -1713,84 +1398,6 @@ process_path (char *pathname, char *name, boolean leaf, char *parent, return 1; } - -/* 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 -complete_pending_execdirs(struct predicate *p) -{ -#if defined(NEW_EXEC) - if (NULL == p) - return; - - complete_pending_execdirs(p->pred_left); - - if (p->pred_func == pred_execdir || p->pred_func == 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); - } - } - } - - complete_pending_execdirs(p->pred_right); -#else - /* nothing to do. */ - return; -#endif -} - -/* 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). - */ -static void -complete_pending_execs(struct predicate *p) -{ -#if defined(NEW_EXEC) - 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. - */ - if (p->pred_func == 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); -#else - /* nothing to do. */ - return; -#endif -} /* Scan directory PATHNAME and recurse through process_path for each entry. @@ -2041,19 +1648,3 @@ process_dir (char *pathname, char *name, int pathlen, struct stat *statp, char * free_dirinfo(dirinfo); } } - -/* 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 */ - -static boolean -default_prints (struct predicate *pred) -{ - while (pred != NULL) - { - if (pred->no_default_print) - return (false); - pred = pred->pred_next; - } - return (true); -} diff --git a/find/finddata.c b/find/finddata.c new file mode 100644 index 0000000..2a3a961 --- /dev/null +++ b/find/finddata.c @@ -0,0 +1,48 @@ +/* finddata.c -- global data for "find". + Copyright (C) 1990, 91, 92, 93, 94, 2000, + 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "defs.h" + + +/* Name this program was run with. */ +char *program_name; + +/* All predicates for each path to process. */ +struct predicate *predicates; + +/* The last predicate allocated. */ +struct predicate *last_pred; + +/* The root of the evaluation tree. */ +struct predicate *eval_tree = NULL; + + +struct options options; +struct state state; + +/* The full path of the initial working directory, or "." if + STARTING_DESC is nonnegative. */ +char const *starting_dir = "."; + +/* 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; + diff --git a/find/ftsfind.c b/find/ftsfind.c new file mode 100644 index 0000000..4b597ec --- /dev/null +++ b/find/ftsfind.c @@ -0,0 +1,702 @@ +/* find -- search for files in a directory hierarchy (fts version) + Copyright (C) 1990, 91, 92, 93, 94, 2000, + 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA.*/ + +/* GNU find was written by Eric Decker , + with enhancements by David MacKenzie , + Jay Plett , + and Tim Wood . + The idea for -print0 and xargs -0 came from + Dan Bernstein . + Improvements have been made by James Youngman . +*/ + + +#include "defs.h" + + +#define USE_SAFE_CHDIR 1 +#undef STAT_MOUNTPOINTS + + +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#else +#include +#endif + + +#include "../gnulib/lib/xalloc.h" +#include "closeout.h" +#include +#include "quotearg.h" +#include "quote.h" +#include "fts_.h" + +#ifdef HAVE_LOCALE_H +#include +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#define textdomain(Domain) +#define bindtextdomain(Package, Directory) +#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 + + +#ifdef STAT_MOUNTPOINTS +static void init_mounted_dev_list(void); +#endif + +/* We have encountered an error which shoudl affect the exit status. + * This is normally used to change the exit status from 0 to 1. + * However, if the exit status is already 2 for example, we don't want to + * reduce it to 1. + */ +static void +error_severity(int level) +{ + if (state.exit_status < level) + state.exit_status = level; +} + +#ifdef DEBUG +#define STRINGIFY(X) #X +#define HANDLECASE(N) case N: return #N; + +static char * +get_fts_info_name(int info) +{ + static char buf[10]; + switch (info) + { + HANDLECASE(FTS_D); + HANDLECASE(FTS_DC); + HANDLECASE(FTS_DEFAULT); + HANDLECASE(FTS_DNR); + HANDLECASE(FTS_DOT); + HANDLECASE(FTS_DP); + HANDLECASE(FTS_ERR); + HANDLECASE(FTS_F); + HANDLECASE(FTS_INIT); + HANDLECASE(FTS_NS); + HANDLECASE(FTS_NSOK); + HANDLECASE(FTS_SL); + HANDLECASE(FTS_SLNONE); + HANDLECASE(FTS_W); + default: + sprintf(buf, "[%d]", info); + return buf; + } +} + +#endif + +static void +visit(FTS *p, FTSENT *ent, struct stat *pstat) +{ + state.curdepth = ent->fts_level; + state.have_stat = (ent->fts_info != FTS_NS) && (ent->fts_info != FTS_NSOK); + state.rel_pathname = ent->fts_accpath; + + /* Apply the predicates to this path. */ + (*(eval_tree)->pred_func)(ent->fts_path, pstat, eval_tree); + + /* Deal with any side effects of applying the predicates. */ + if (state.stop_at_current_level) + { + fts_set(p, ent, FTS_SKIP); + } +} + +static const char* +partial_quotearg_n(int n, char *s, size_t len, enum quoting_style style) +{ + if (0 == len) + { + return quotearg_n_style(n, style, ""); + } + else + { + char saved; + const char *result; + + saved = s[len]; + s[len] = 0; + result = quotearg_n_style(n, style, s); + s[len] = saved; + return result; + } +} + + +/* We've detected a filesystem 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(FTSENT * ent) +{ + if (S_ISLNK(ent->fts_statp->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."), + quotearg_n_style(0, locale_quoting_style, ent->fts_path)); + } + else + { + /* 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, + _("Filesystem loop detected; " + "%s is part of the same filesystem loop as %s."), + quotearg_n_style(0, locale_quoting_style, ent->fts_path), + partial_quotearg_n(1, + ent->fts_cycle->fts_path, + ent->fts_cycle->fts_pathlen, + locale_quoting_style)); + } +} + +/* + * Return true if NAME corresponds to a file which forms part of a + * symbolic link loop. The command + * rm -f a b; ln -s a b; ln -s b a + * produces such a loop. + */ +static boolean +symlink_loop(const char *name) +{ + struct stat stbuf; + int rv; + if (following_links()) + rv = stat(name, &stbuf); + else + rv = lstat(name, &stbuf); + return (0 != rv) && (ELOOP == errno); +} + + +static void +consider_visiting(FTS *p, FTSENT *ent) +{ + struct stat statbuf; + mode_t mode; + +#ifdef DEBUG + fprintf(stderr, + "consider_visiting: end->fts_info=%s, ent->fts_path=%s\n", + get_fts_info_name(ent->fts_info), + quotearg_n(0, ent->fts_path, locale_quoting_style)); +#endif + + /* Cope with various error conditions. */ + if (ent->fts_info == FTS_ERR + || ent->fts_info == FTS_NS + || ent->fts_info == FTS_DNR) + { + error(0, ent->fts_errno, ent->fts_path); + error_severity(1); + return; + } + else if (ent->fts_info == FTS_DC) + { + issue_loop_warning(ent); + error_severity(1); + return; + } + else if (ent->fts_info == FTS_SLNONE) + { + /* fts_read() claims that ent->fts_accpath is a broken symbolic + * link. That would be fine, but if this is part of a symbolic + * link loop, we diagnose the problem and also ensure that the + * eventual return value is nonzero. Note that while the path + * we stat is local (fts_accpath), we print the fill path name + * of the file (fts_path) in the error message. + */ + if (symlink_loop(ent->fts_accpath)) + { + error(0, ELOOP, ent->fts_path); + error_severity(1); + return; + } + } + + /* Not an error, cope with the usual cases. */ + if (ent->fts_info == FTS_NSOK) + { + state.have_stat = false; + mode = 0; + } + else + { + state.have_stat = true; + statbuf = *(ent->fts_statp); + mode = statbuf.st_mode; + } + + if (0 == ent->fts_level && (0u == state.starting_path_length)) + state.starting_path_length = ent->fts_pathlen; + + if (0 != digest_mode(mode, ent->fts_path, ent->fts_name, &statbuf, 0)) + { + /* examine this item. */ + int ignore = 0; + + if (S_ISDIR(statbuf.st_mode) && (ent->fts_info == FTS_NSOK)) + { + /* This is a directory, but fts did not stat it, so + * presumably would not be planning to search its + * children. Force a stat of the file so that the + * children can be checked. + */ + fts_set(p, ent, FTS_AGAIN); + return; + } + + if (options.maxdepth >= 0 && (ent->fts_level > options.maxdepth)) + { + ignore = 1; + fts_set(p, ent, FTS_SKIP); + } + else if ( (ent->fts_info == FTS_D) && !options.do_dir_first ) + { + /* this is the preorder visit, but user said -depth */ + ignore = 1; + } + else if ( (ent->fts_info == FTS_DP) && options.do_dir_first ) + { + /* this is the postorder visit, but user didn't say -depth */ + ignore = 1; + } + else if (ent->fts_level < options.mindepth) + { + ignore = 1; + } + + if (!ignore) + { + visit(p, ent, &statbuf); + } + + + if (ent->fts_info == FTS_DP) + { + /* we're leaving a directory. */ + state.stop_at_current_level = false; + complete_pending_execdirs(eval_tree); + } + } +} + + +static void +find(char *arg) +{ + char * arglist[2]; + int ftsoptions; + FTS *p; + FTSENT *ent; + + + arglist[0] = arg; + arglist[1] = NULL; + + ftsoptions = FTS_NOSTAT; + switch (options.symlink_handling) + { + case SYMLINK_ALWAYS_DEREF: + ftsoptions |= FTS_COMFOLLOW|FTS_LOGICAL; + break; + + case SYMLINK_DEREF_ARGSONLY: + ftsoptions |= FTS_COMFOLLOW; + break; + + case SYMLINK_NEVER_DEREF: + ftsoptions |= FTS_PHYSICAL; + break; + } + + if (options.stay_on_filesystem) + ftsoptions |= FTS_XDEV; + + p = fts_open(arglist, ftsoptions, NULL); + if (NULL == p) + { + error (0, errno, + _("cannot search %s"), + quotearg_n_style(0, locale_quoting_style, arg)); + } + else + { + while ( (ent=fts_read(p)) != NULL ) + { + consider_visiting(p, ent); + } + fts_close(p); + p = NULL; + } +} + + +static void +process_all_startpoints(int argc, char *argv[]) +{ + int i; + + /* figure out how many start points there are */ + for (i = 0; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + { + find(argv[i]); + } + + if (i == 0) + { + /* + * 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] = "."; + find(defaultpath); + } +} + + + + +int +main (int argc, char **argv) +{ + int i; + const struct parser_table *parse_entry; /* Pointer to the parsing table entry for this expression. */ + struct predicate *cur_pred; + char *predicate_name; /* Name of predicate being parsed. */ + int end_of_leading_options = 0; /* First arg after any -H/-L etc. */ + program_name = argv[0]; + const struct parser_table *entry_close, *entry_print, *entry_open; + + + /* 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 + options.open_nofollow_available = check_nofollow(); +#else + options.open_nofollow_available = false; +#endif + + options.regex_options = RE_SYNTAX_EMACS; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + atexit (close_stdout); + + + if (isatty(0)) + { + options.warnings = true; + } + else + { + options.warnings = false; + } + + + predicates = NULL; + last_pred = NULL; + options.do_dir_first = true; + options.maxdepth = options.mindepth = -1; + options.start_time = time (NULL); + options.cur_day_start = options.start_time - DAYSECS; + options.full_days = false; + options.stay_on_filesystem = false; + options.ignore_readdir_race = false; + + state.exit_status = 0; + +#if defined(DEBUG_STAT) + options.xstat = debug_stat; +#endif /* !DEBUG_STAT */ + + if (getenv("POSIXLY_CORRECT")) + options.output_block_size = 512; + else + options.output_block_size = 1024; + + 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. */ + options.no_leaf_check = false; +#else + /* The leaf optimisation is disabled. */ + options.no_leaf_check = true; +#endif + + set_follow_state(SYMLINK_NEVER_DEREF); /* The default is equivalent to -P. */ + +#ifdef DEBUG + fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start)); +#endif /* DEBUG */ + + /* Check for -P, -H or -L 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 + { + /* Hmm, must be one of + * (a) A path name + * (b) A predicate + */ + end_of_leading_options = i; /* Next time start with this option */ + break; + } + } + + /* We are now processing the part of the "find" command line + * after the -H/-L options (if any). + */ + + /* fprintf(stderr, "rest: optind=%ld\n", (long)optind); */ + + /* Find where in ARGV the predicates begin. */ + for (i = end_of_leading_options; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + { + /* fprintf(stderr, "Looks like %s is not a predicate\n", argv[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_open (entry_open, argv, &argc); + parse_begin_user_args(argv, argc, last_pred, predicates); + pred_sanity_check(last_pred); + + /* Build the input order list. */ + while (i < argc) + { + if (strchr ("-!(),", argv[i][0]) == NULL) + usage (_("paths must precede expression")); + predicate_name = argv[i]; + parse_entry = find_parser (predicate_name); + if (parse_entry == NULL) + { + /* Command line option not recognized */ + error (1, 0, _("invalid predicate `%s'"), predicate_name); + } + + i++; + if (!(*(parse_entry->parser_func)) (parse_entry, argv, &i)) + { + if (argv[i] == NULL) + /* Command line option requires an argument */ + error (1, 0, _("missing argument to `%s'"), predicate_name); + else + error (1, 0, _("invalid argument `%s' to `%s'"), + argv[i], predicate_name); + } + + 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 ((char *) cur_pred); + parse_print (entry_print, argv, &argc); + 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 ((char *) cur_pred); + } + else + { + /* `( user-supplied-expression ) -print'. */ + parse_close (entry_close, argv, &argc); + pred_sanity_check(last_pred); + parse_print (entry_print, argv, &argc); + pred_sanity_check(last_pred); + pred_sanity_check(predicates); /* XXX: expensive */ + } + +#ifdef DEBUG + fprintf (stderr, "Predicate List:\n"); + print_list (stderr, predicates); +#endif /* DEBUG */ + + /* do a sanity check */ + pred_sanity_check(predicates); + + /* Done parsing the predicates. Build the evaluation tree. */ + cur_pred = predicates; + eval_tree = get_expr (&cur_pred, NO_PREC); + + /* Check if we have any left-over predicates (this fixes + * Debian bug #185202). + */ + if (cur_pred != NULL) + { + error (1, 0, _("unexpected extra predicate")); + } + +#ifdef DEBUG + fprintf (stderr, "Eval Tree:\n"); + print_tree (stderr, eval_tree, 0); +#endif /* DEBUG */ + + /* 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); + /* Determine the point, if any, at which to determine file type. */ + mark_type (eval_tree); + +#ifdef DEBUG + 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"); +#endif /* DEBUG */ + + /* 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 filesystem 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(); +#endif + } + + + starting_desc = open (".", O_RDONLY); + 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")); + } + + + process_all_startpoints(argc-end_of_leading_options, argv+end_of_leading_options); + + /* 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. + */ + cleanup(); + return state.exit_status; +} + +boolean is_fts_enabled() +{ + /* this version of find (i.e. this main()) uses fts. */ + return true; +} diff --git a/find/parser.c b/find/parser.c index a3a3f7a..889d437 100644 --- a/find/parser.c +++ b/find/parser.c @@ -151,8 +151,8 @@ static boolean parse_quit PARAMS((const struct parser_table*, char *arg boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); -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)); +boolean parse_open PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr)); +boolean parse_close PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr)); @@ -206,8 +206,8 @@ static struct parser_table const parse_table[] = { PARSE_PUNCTUATION("!", negate), PARSE_PUNCTUATION("not", negate), /* GNU */ - PARSE_PUNCTUATION("(", openparen), - PARSE_PUNCTUATION(")", closeparen), + PARSE_PUNCTUATION("(", open), + PARSE_PUNCTUATION(")", close), PARSE_PUNCTUATION(",", comma), /* GNU */ PARSE_PUNCTUATION("a", and), PARSE_TEST ("amin", amin), /* GNU */ @@ -306,6 +306,40 @@ static const char *first_nonoption_arg = NULL; + +void +set_follow_state(enum SymlinkOption opt) +{ + switch (opt) + { + case SYMLINK_ALWAYS_DEREF: /* -L */ + options.xstat = optionl_stat; + options.no_leaf_check = true; + break; + + case SYMLINK_NEVER_DEREF: /* -P (default) */ + options.xstat = optionp_stat; + /* Can't turn no_leaf_check off because the user might have specified + * -noleaf anyway + */ + break; + + case SYMLINK_DEREF_ARGSONLY: /* -H */ + options.xstat = optionh_stat; + options.no_leaf_check = true; + } + + options.symlink_handling = opt; + + /* For DEBUG_STAT, the choice is made at runtime within debug_stat() + * by checking the contents of the symlink_handling variable. + */ +#if defined(DEBUG_STAT) + options.xstat = debug_stat; +#endif /* !DEBUG_STAT */ +} + + void parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates) { @@ -474,7 +508,7 @@ parse_atime (const struct parser_table* entry, char **argv, int *arg_ptr) } boolean -parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_close (const struct parser_table* entry, char **argv, int *arg_ptr) { struct predicate *our_pred; @@ -482,9 +516,9 @@ parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr) (void) arg_ptr; our_pred = get_new_pred (entry); - our_pred->pred_func = pred_closeparen; + our_pred->pred_func = pred_close; #ifdef DEBUG - our_pred->p_name = find_pred_name (pred_closeparen); + our_pred->p_name = find_pred_name (pred_close); #endif /* DEBUG */ our_pred->p_type = CLOSE_PAREN; our_pred->p_prec = NO_PREC; @@ -1208,7 +1242,7 @@ parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr) } boolean -parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr) +parse_open (const struct parser_table* entry, char **argv, int *arg_ptr) { struct predicate *our_pred; @@ -1216,9 +1250,9 @@ parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr) (void) arg_ptr; our_pred = get_new_pred_chk_op (entry); - our_pred->pred_func = pred_openparen; + our_pred->pred_func = pred_open; #ifdef DEBUG - our_pred->p_name = find_pred_name (pred_openparen); + our_pred->p_name = find_pred_name (pred_open); #endif /* DEBUG */ our_pred->p_type = OPEN_PAREN; our_pred->p_prec = NO_PREC; @@ -1701,6 +1735,13 @@ parse_version (const struct parser_table* entry, char **argv, int *arg_ptr) printf("LEAF_OPTIMISATION "); ++features; #endif + + if (is_fts_enabled()) + { + printf("FTS "); + ++features; + } + if (0 == features) { /* For the moment, leave this as English in case someone wants diff --git a/find/pred.c b/find/pred.c index 14f9334..ec8e94a 100644 --- a/find/pred.c +++ b/find/pred.c @@ -157,7 +157,7 @@ static char *ctime_format PARAMS((time_t when)); #ifdef DEBUG struct pred_assoc { - PFB pred_func; + PRED_FUNC pred_func; char *pred_name; }; @@ -167,7 +167,7 @@ struct pred_assoc pred_table[] = {pred_and, "and "}, {pred_anewer, "anewer "}, {pred_atime, "atime "}, - {pred_closeparen, ") "}, + {pred_close, ") "}, {pred_amin, "cmin "}, {pred_cnewer, "cnewer "}, {pred_comma, ", "}, @@ -199,7 +199,7 @@ struct pred_assoc pred_table[] = {pred_nouser, "nouser "}, {pred_ok, "ok "}, {pred_okdir, "okdir "}, - {pred_openparen, "( "}, + {pred_open, "( "}, {pred_or, "or "}, {pred_path, "path "}, {pred_perm, "perm "}, @@ -334,7 +334,7 @@ pred_atime (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) } boolean -pred_closeparen (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_close (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { (void) &pathname; (void) &stat_buf; @@ -709,11 +709,11 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) case 'H': /* ARGV element file was found under */ /* trusted */ { - char cc = pathname[state.path_length]; + char cc = pathname[state.starting_path_length]; - pathname[state.path_length] = '\0'; + pathname[state.starting_path_length] = '\0'; fprintf (fp, segment->text, pathname); - pathname[state.path_length] = cc; + pathname[state.starting_path_length] = cc; break; } case 'i': /* inode number */ @@ -810,7 +810,7 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) /* sanitised */ if (state.curdepth > 0) { - cp = pathname + state.path_length; + cp = pathname + state.starting_path_length; if (*cp == '/') /* Move past the slash between the ARGV element and the rest of the pathname. But if the ARGV element @@ -1187,7 +1187,7 @@ pred_okdir (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) } boolean -pred_openparen (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_open (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { (void) pathname; (void) stat_buf; @@ -1686,15 +1686,14 @@ ctime_format (time_t when) the predicate function PRED_FUNC. */ char * -find_pred_name (pred_func) - PFB pred_func; +find_pred_name (PRED_FUNC func) { int i; for (i = 0; pred_table[i].pred_func != 0; i++) - if (pred_table[i].pred_func == pred_func) + if (pred_table[i].pred_func == func) break; - return (pred_table[i].pred_name); + return pred_table[i].pred_name; } static char * diff --git a/find/util.c b/find/util.c index f8b130b..4d81f3e 100644 --- a/find/util.c +++ b/find/util.c @@ -20,6 +20,17 @@ #include "defs.h" #include "xalloc.h" + +#ifdef HAVE_FCNTL_H +#include +#else +#include +#endif +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif + + #if ENABLE_NLS # include # define _(Text) gettext (Text) @@ -199,3 +210,374 @@ usage (char *msg) Usage: %s [-H] [-L] [-P] [path...] [expression]\n"), program_name); exit (1); } + + +/* 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) +{ + if (!state.have_stat && (*options.xstat) (name, p) != 0) + { + if (!options.ignore_readdir_race || (errno != ENOENT) ) + { + error (0, errno, "%s", 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, + const char *name, + struct stat *p, + struct predicate *pred_ptr) +{ + /* If we need the full stat info, or we need the type info but don't + * already have it, stat the file now. + */ + (void) name; + if (pred_ptr->need_stat) + { + return get_statinfo(pathname, state.rel_pathname, p); + } + if ((pred_ptr->need_type && (0 == state.have_type))) + { + return get_statinfo(pathname, state.rel_pathname, p); + } + return 0; +} + +/* Determine if we can use O_NOFOLLOW. + */ +#if defined(O_NOFOLLOW) +boolean +check_nofollow(void) +{ + struct utsname uts; + float release; + + 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). + */ +void +complete_pending_execdirs(struct predicate *p) +{ +#if defined(NEW_EXEC) + if (NULL == p) + return; + + complete_pending_execdirs(p->pred_left); + + if (p->pred_func == pred_execdir || p->pred_func == 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); + } + } + } + + complete_pending_execdirs(p->pred_right); +#else + /* nothing to do. */ + return; +#endif +} + + +/* 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 defined(NEW_EXEC) + 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. + */ + if (p->pred_func == 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); +#else + /* nothing to do. */ + return; +#endif +} + + +/* Complete any outstanding commands. + */ +void +cleanup(void) +{ + if (eval_tree) + { + complete_pending_execs(eval_tree); + complete_pending_execdirs(eval_tree); + } +} + + +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: +#ifdef DEBUG_STAT + fprintf(stderr, "fallback_stat(): stat(%s) failed; falling back on lstat()\n", name); +#endif + return lstat(name, p); + + 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 (0 == state.curdepth) + { + /* This file is from the command line; deference the link (if it + * is a link). + */ + int rv = stat(name, p); + 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 lstat(name, p); + } +} + +/* 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 = stat(name, p); + 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) +{ + return lstat(name, p); +} + +#ifdef DEBUG_STAT +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); + } +} +#endif /* DEBUG_STAT */ + + +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); +} diff --git a/import-gnulib.sh b/import-gnulib.sh index 528c272..a295f58 100644 --- a/import-gnulib.sh +++ b/import-gnulib.sh @@ -40,7 +40,7 @@ destdir="gnulib" # Modules needed for findutils itself. findutils_modules="\ -alloca argmatch dirname error fileblocks fnmatch-gnu \ +alloca argmatch dirname error fileblocks fnmatch-gnu fts \ getline getopt human idcache lstat malloc memcmp memset mktime \ modechange pathmax quotearg realloc regex rpmatch savedir stdio-safer \ stpcpy strdup strftime strstr strtol strtoul strtoull strtoumax \ diff --git a/m4/Makefile.am b/m4/Makefile.am index 12a1f05..1b13304 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -1,2 +1,3 @@ -EXTRA_DIST = findlib.m4 nullsort.m4 order-bad.bin order-good.bin +EXTRA_DIST = findlib.m4 nullsort.m4 order-bad.bin order-good.bin withfts.m4 + diff --git a/m4/withfts.m4 b/m4/withfts.m4 new file mode 100644 index 0000000..3ee16e5 --- /dev/null +++ b/m4/withfts.m4 @@ -0,0 +1,13 @@ +AC_DEFUN([FIND_WITH_FTS], +[AC_ARG_WITH([fts], +[ --without-fts Use an older mechanism for searching the filesystem, instead of using fts()],[with_fts=$withval],[]) + case $with_fts in + yes|no) ;; + '') with_fts=yes ;; + *) AC_MSG_ERROR([Invalid value for --with-fts: $with_fts]) + esac + AM_CONDITIONAL(WITH_FTS, [[test x"${with_fts-no}" != xno]]) + if test x"${with_fts-no}" != xno ; then + AC_DEFINE(WITH_FTS, 1, [Define if you want to use fts() to do the filesystem search.]) + fi +]) -- 2.11.4.GIT