Findutils 4.3.x defaults to using the the FTS implementation of find.

This commit is contained in:
James Youngman 2005-11-21 05:42:27 +00:00
parent db906d83c3
commit f0759ab8db
13 changed files with 1287 additions and 464 deletions

9
NEWS
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -43,10 +43,6 @@
#include <sys/file.h>
#endif
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#include "../gnulib/lib/xalloc.h"
#include "../gnulib/lib/human.h"
#include "../gnulib/lib/canonicalize.h"
@ -88,13 +84,8 @@ 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
{
@ -143,248 +123,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)
@ -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);
}

48
find/finddata.c Normal file
View File

@ -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;

702
find/ftsfind.c Normal file
View File

@ -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 <cire@cisco.com>,
with enhancements by David MacKenzie <djm@gnu.org>,
Jay Plett <jay@silence.princeton.nj.us>,
and Tim Wood <axolotl!tim@toad.com>.
The idea for -print0 and xargs -0 came from
Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
Improvements have been made by James Youngman <jay@gnu.org>.
*/
#include "defs.h"
#define USE_SAFE_CHDIR 1
#undef STAT_MOUNTPOINTS
#include <errno.h>
#include <assert.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#include "../gnulib/lib/xalloc.h"
#include "closeout.h"
#include <modetype.h>
#include "quotearg.h"
#include "quote.h"
#include "fts_.h"
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#if ENABLE_NLS
# include <libintl.h>
# define _(Text) gettext (Text)
#else
# define _(Text) Text
#define textdomain(Domain)
#define bindtextdomain(Package, Directory)
#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;
}

View File

@ -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

View File

@ -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 *

View File

@ -20,6 +20,17 @@
#include "defs.h"
#include "xalloc.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#if ENABLE_NLS
# include <libintl.h>
# 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);
}

View File

@ -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 \

View File

@ -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

13
m4/withfts.m4 Normal file
View File

@ -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
])