diff --git a/NEWS b/NEWS index bd60a1ce..9545b14f 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) +* Major changes in release 4.3.0-CVSFTS + * Major changes in release 4.2.24 ** Documentation Changes diff --git a/configure.in b/configure.in index a81faed4..7462f367 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.24, [bug-findutils@gnu.org]) +AC_INIT([GNU findutils], 4.3.0-CVSFTS, [bug-findutils@gnu.org]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([find/pred.c]) @@ -11,6 +11,10 @@ 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 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/debian.rules b/debian.rules deleted file mode 100755 index 7ff16c84..00000000 --- a/debian.rules +++ /dev/null @@ -1,142 +0,0 @@ -#! /usr/bin/make -f -# -# Last updated: 1996/05/27 08:10:50 by Kevin Dalley -# -# To make the binary distribution package, the ``Debianized'' source package -# and the context diff to the original package, type `./debian.rules dist'. -# Make sure that `debian.rules' is executable before the final distribution -# is made. -# -# Invoke each target with `./debian.rules '. All targets should be -# invoked with the package root as the current directory. -# -# The `binary' target must be run as root, as it needs to install files with -# specific ownerships. The `diff' target assumes that you have the original -# source package available, unpacked, in ../$(package)-$(version).orig, or that you have -# the previous revision of the ``Debianized'' source package and context diff -# in the parent directory. - -CC = gcc -CFLAGS = -O2 -LDFLAGS = -s - -# The name of the package (for example, `emacs'). -package = findutils -# The version of the package (for example, `19.28'). -version = 4.1 -# The Debian revision of the package (for example, `2'). -debian = 12 - -build: -# Builds the binary package. - ./configure --prefix=/usr - make CC="$(CC)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ - libexecdir=/usr/lib/locate localstatedir=/var/lib/locate - ( cd doc ; makeinfo find.texi ) - touch stamp-build - -clean: -# Undoes the effect of `make -f debian.rules build'. - make distclean - rm -f stamp-build - rm -rf debian-tmp - -binary: -# Makes a binary package. - test -f stamp-build || make -f debian.rules build - install -d -g root -m 755 -o root debian-tmp - chmod g-s debian-tmp - install -d -g root -m 755 -o root debian-tmp/DEBIAN - install -d -g root -m 755 -o root debian-tmp/etc/cron.daily - install -g root -m 755 -o root cron.find \ - debian-tmp/etc/cron.daily/find - install -d -g root -m 755 -o root debian-tmp/usr/bin - install -g root -m 755 -o root find/find \ - debian-tmp/usr/bin/find - install -g root -m 755 -o root locate/locate \ - debian-tmp/usr/bin/locate - install -g root -m 755 -o root locate/updatedb \ - debian-tmp/usr/bin/updatedb - install -g root -m 755 -o root xargs/xargs \ - debian-tmp/usr/bin/xargs - install -d -g root -m 755 -o root debian-tmp/usr/info - install -g root -m 644 -o root doc/find.info* \ - debian-tmp/usr/info - install -d -g root -m 755 -o root debian-tmp/usr/lib/locate - install -g root -m 755 -o root locate/bigram locate/code \ - locate/frcode debian-tmp/usr/lib/locate - install -d -g root -m 755 -o root debian-tmp/usr/man/man1 - install -g root -m 644 -o root find/find.1 \ - debian-tmp/usr/man/man1/find.1 - install -g root -m 644 -o root locate/locate.1 \ - debian-tmp/usr/man/man1/locate.1 - install -g root -m 644 -o root locate/updatedb.1 \ - debian-tmp/usr/man/man1/updatedb.1 - install -g root -m 644 -o root xargs/xargs.1 \ - debian-tmp/usr/man/man1/xargs.1 - install -d -g root -m 755 -o root debian-tmp/usr/man/man5 - install -d -g root -m 755 -o root debian-tmp/usr/lib/locate - install -d -g root -m 755 -o root debian-tmp/var/lib - install -d -g nogroup -m 2755 -o nobody debian-tmp/var/lib/locate - install -g root -m 644 -o root locate/locatedb.5 \ - debian-tmp/usr/man/man5/locatedb.5 - install -d -g root -m 755 -o root debian-tmp/usr/doc/copyright - install -g root -m 644 -o root debian.README \ - debian-tmp/usr/doc/copyright/$(package) - rm -f debian-tmp/usr/info/find.info*.gz - gzip -9f debian-tmp/usr/info/find.info* - sed -e '1s/=/$(package)/; \ - 2s/=/$(version)-$(debian)/; \ - 3s/=/$(shell dpkg --print-architecture)/;' \ - debian.control > debian-tmp/DEBIAN/control - chmod 644 debian-tmp/DEBIAN/control - install -g root -m 644 -o root debian.conffiles \ - debian-tmp/DEBIAN/conffiles - install -g root -m 755 -o root debian.postinst \ - debian-tmp/DEBIAN/postinst - install -g root -m 755 -o root debian.postrm \ - debian-tmp/DEBIAN/postrm - install -g root -m 755 -o root debian.preinst \ - debian-tmp/DEBIAN/preinst - dpkg --build debian-tmp - mv debian-tmp.deb \ - ../$(package)-$(version)-$(debian).$(shell dpkg --print-architecture).deb - -source: clean -# Makes a source package. - ( cd .. && tar cf - $(package)-$(version) | \ - gzip -9f > $(package)-$(version)-$(debian).tar.gz ) - -diff: clean -# Makes a context diff. - -test -d ../$(package)-$(version).orig -o \ - -f ../$(package)-$(version)-`expr $(debian) - 1`.diff.gz \ - || ( echo "Original source package is not available." ; false ) - -test -d ../$(package)-$(version).orig || make -f debian.rules orig - #cp -a ../$(package)-$(version).orig/doc/find.info* doc - cd .. && \ - (diff -ruN $(package)-$(version).orig $(package)-$(version) \ - >$(package)-$(version)-$(debian).diff; [ $$? = 1 ]) && \ - gzip -9vf $(package)-$(version)-$(debian).diff - -test -f stamp-orig \ - && rm -rf ../$(package)-$(version).orig && rm -f stamp-orig - -dist: binary source diff -# Prepares the package for distribution. - -orig: -# Prepares the original package from the previous -# Debian revision source package and context diff. - ( cd .. \ - && mkdir $(package).orig \ - && cd $(package).orig \ - && tar xzf ../$(package)-$(version)-`expr $(debian) - 1`.tar.gz \ - && cd $(package)-$(version) \ - && ( zcat ../../$(package)-$(version)-`expr $(debian) - 1`.diff.gz \ - | patch -sER -p1 ) \ - && find . -name "*.orig" -exec rm -f {} \; \ - && cd .. \ - && mv $(package)-$(version) ../$(package)-$(version).orig \ - && cd .. \ - && rmdir $(package).orig ) - touch stamp-orig diff --git a/find/Makefile.am b/find/Makefile.am index 8e71a323..2f30303e 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 70fed9e7..81db293d 100644 --- a/find/defs.h +++ b/find/defs.h @@ -86,6 +86,10 @@ int optionh_stat PARAMS((const char *name, struct stat *p)); 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 +341,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. */ @@ -509,10 +513,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 +597,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 +607,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 85c4f517..b5d2b3b4 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,9 +84,6 @@ 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)); @@ -104,24 +97,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 { @@ -143,248 +125,6 @@ enum WdSanityCheckFatality NON_FATAL_IF_SANITY_CHECK_FAILS }; - -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) @@ -690,8 +430,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) { @@ -1377,7 +1123,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, ".")) @@ -1532,65 +1278,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. @@ -1707,84 +1394,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. @@ -2035,19 +1644,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 00000000..2a3a9618 --- /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 00000000..5577faea --- /dev/null +++ b/find/ftsfind.c @@ -0,0 +1,534 @@ +/* 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 "quote.h" +#include "quotearg.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 + + +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 void consider_visiting(FTS *p, FTSENT *ent) +{ + struct stat statbuf; + mode_t mode; + + if (ent->fts_info == FTS_NS || 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(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 fb372057..9a5f7bf2 100644 --- a/find/parser.c +++ b/find/parser.c @@ -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) { @@ -1689,6 +1723,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 93808c8e..9e4eb162 100644 --- a/find/pred.c +++ b/find/pred.c @@ -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 diff --git a/find/util.c b/find/util.c index f8b130be..09cd8de4 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; + +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 */ + + +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 31546062..ae70b08a 100755 --- a/import-gnulib.sh +++ b/import-gnulib.sh @@ -40,9 +40,9 @@ 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 stat stdio-safer \ +modechange pathmax quotearg realloc regex rpmatch savedir stdio-safer \ stpcpy strdup strftime strstr strtol strtoul strtoull strtoumax \ xalloc xalloc-die xgetcwd xstrtol xstrtoumax yesno human filemode \ getline stpcpy canonicalize mountlist closeout gettext stat-macros" diff --git a/m4/Makefile.am b/m4/Makefile.am index 12a1f05c..1b133042 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 00000000..5462f4ba --- /dev/null +++ b/m4/withfts.m4 @@ -0,0 +1,6 @@ +AC_DEFUN(FIND_WITH_FTS, +[AC_ARG_WITH(fts, +[ --with-fts Use fts() to search the filesystem],[]) +AC_DEFINE(WITH_FTS, 1, [Define if you want to use fts() to do the filesystem search.]) +AM_CONDITIONAL(WITH_FTS, [[test x"${with_fts-no}" != xno]]) +])