mirror of
https://https.git.savannah.gnu.org/git/findutils.git
synced 2026-01-26 15:39:06 +00:00
Findutils 4.3.x defaults to using the the FTS implementation of find.
This commit is contained in:
parent
db906d83c3
commit
f0759ab8db
9
NEWS
9
NEWS
@ -1,4 +1,13 @@
|
||||
GNU findutils NEWS - User visible changes. -*- outline -*- (allout)
|
||||
* Major changes in release 4.3.0
|
||||
|
||||
** Functional Changes
|
||||
|
||||
By default, find now uses the fts() function to search the file
|
||||
system, which means that it can search deeper directory hierarchies.
|
||||
You can go back to the old filesystem search implementation by using
|
||||
the configure option '--without-fts'.
|
||||
|
||||
* Major changes in release 4.2.26
|
||||
|
||||
** Public Service Announcements
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
41
find/defs.h
41
find/defs.h
@ -83,9 +83,14 @@ int stat PARAMS((const char *__path, struct stat *__statbuf));
|
||||
int optionl_stat PARAMS((const char *name, struct stat *p));
|
||||
int optionp_stat PARAMS((const char *name, struct stat *p));
|
||||
int optionh_stat PARAMS((const char *name, struct stat *p));
|
||||
int debug_stat PARAMS((const char *file, struct stat *bufp));
|
||||
|
||||
int get_statinfo PARAMS((const char *pathname, const char *name, struct stat *p));
|
||||
|
||||
#if ! defined HAVE_FCHDIR && ! defined fchdir
|
||||
# define fchdir(fd) (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef S_ISUID
|
||||
@ -337,9 +342,9 @@ struct predicate
|
||||
const struct parser_table* parser_entry;
|
||||
};
|
||||
|
||||
/* find.c. */
|
||||
int get_info PARAMS((const char *pathname, const char *name, struct stat *p, struct predicate *pred_ptr));
|
||||
int following_links(void);
|
||||
/* find.c, ftsfind.c */
|
||||
boolean is_fts_enabled();
|
||||
|
||||
|
||||
|
||||
/* find library function declarations. */
|
||||
@ -429,7 +434,7 @@ boolean pred_amin PARAMS((char *pathname, struct stat *stat_buf, struct predicat
|
||||
boolean pred_and PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_anewer PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_atime PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_closeparen PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_close PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_cmin PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_cnewer PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_comma PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
@ -462,7 +467,7 @@ boolean pred_nogroup PARAMS((char *pathname, struct stat *stat_buf, struct predi
|
||||
boolean pred_nouser PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_ok PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_okdir PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_openparen PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_open PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_or PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_path PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
boolean pred_perm PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr));
|
||||
@ -509,10 +514,15 @@ struct predicate *get_new_pred_chk_op PARAMS((const struct parser_table *entry))
|
||||
struct predicate *insert_primary PARAMS((const struct parser_table *entry));
|
||||
struct predicate *insert_primary_withpred PARAMS((const struct parser_table *entry, PRED_FUNC fptr));
|
||||
void usage PARAMS((char *msg));
|
||||
extern boolean check_nofollow(void);
|
||||
extern void complete_pending_execs(struct predicate *p);
|
||||
extern void complete_pending_execdirs(struct predicate *p);
|
||||
/* find.c. */
|
||||
int get_info PARAMS((const char *pathname, const char *name, struct stat *p, struct predicate *pred_ptr));
|
||||
int following_links(void);
|
||||
int digest_mode(mode_t mode, const char *pathname, const char *name, struct stat *pstat, boolean leaf);
|
||||
boolean default_prints (struct predicate *pred);
|
||||
|
||||
extern char *program_name;
|
||||
extern struct predicate *predicates;
|
||||
extern struct predicate *last_pred;
|
||||
|
||||
struct options
|
||||
{
|
||||
@ -588,8 +598,8 @@ struct state
|
||||
Used for stat, readlink, remove, and opendir. */
|
||||
char *rel_pathname;
|
||||
|
||||
/* Length of current path. */
|
||||
int path_length;
|
||||
/* Length of starting path. */
|
||||
int starting_path_length;
|
||||
|
||||
/* If true, don't descend past current directory.
|
||||
Can be set by -prune, -maxdepth, and -xdev/-mount. */
|
||||
@ -598,12 +608,15 @@ struct state
|
||||
/* Status value to return to system. */
|
||||
int exit_status;
|
||||
};
|
||||
extern struct state state;
|
||||
|
||||
/* finddata.c */
|
||||
extern struct state state;
|
||||
extern char const *starting_dir;
|
||||
extern int starting_desc;
|
||||
#if ! defined HAVE_FCHDIR && ! defined fchdir
|
||||
# define fchdir(fd) (-1)
|
||||
#endif
|
||||
extern struct predicate *eval_tree;
|
||||
extern char *program_name;
|
||||
extern struct predicate *predicates;
|
||||
extern struct predicate *last_pred;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
431
find/find.c
431
find/find.c
@ -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
48
find/finddata.c
Normal 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
702
find/ftsfind.c
Normal 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;
|
||||
}
|
||||
@ -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
|
||||
|
||||
25
find/pred.c
25
find/pred.c
@ -157,7 +157,7 @@ static char *ctime_format PARAMS((time_t when));
|
||||
#ifdef DEBUG
|
||||
struct pred_assoc
|
||||
{
|
||||
PFB pred_func;
|
||||
PRED_FUNC pred_func;
|
||||
char *pred_name;
|
||||
};
|
||||
|
||||
@ -167,7 +167,7 @@ struct pred_assoc pred_table[] =
|
||||
{pred_and, "and "},
|
||||
{pred_anewer, "anewer "},
|
||||
{pred_atime, "atime "},
|
||||
{pred_closeparen, ") "},
|
||||
{pred_close, ") "},
|
||||
{pred_amin, "cmin "},
|
||||
{pred_cnewer, "cnewer "},
|
||||
{pred_comma, ", "},
|
||||
@ -199,7 +199,7 @@ struct pred_assoc pred_table[] =
|
||||
{pred_nouser, "nouser "},
|
||||
{pred_ok, "ok "},
|
||||
{pred_okdir, "okdir "},
|
||||
{pred_openparen, "( "},
|
||||
{pred_open, "( "},
|
||||
{pred_or, "or "},
|
||||
{pred_path, "path "},
|
||||
{pred_perm, "perm "},
|
||||
@ -334,7 +334,7 @@ pred_atime (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
}
|
||||
|
||||
boolean
|
||||
pred_closeparen (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
pred_close (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
{
|
||||
(void) &pathname;
|
||||
(void) &stat_buf;
|
||||
@ -709,11 +709,11 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
case 'H': /* ARGV element file was found under */
|
||||
/* trusted */
|
||||
{
|
||||
char cc = pathname[state.path_length];
|
||||
char cc = pathname[state.starting_path_length];
|
||||
|
||||
pathname[state.path_length] = '\0';
|
||||
pathname[state.starting_path_length] = '\0';
|
||||
fprintf (fp, segment->text, pathname);
|
||||
pathname[state.path_length] = cc;
|
||||
pathname[state.starting_path_length] = cc;
|
||||
break;
|
||||
}
|
||||
case 'i': /* inode number */
|
||||
@ -810,7 +810,7 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
/* sanitised */
|
||||
if (state.curdepth > 0)
|
||||
{
|
||||
cp = pathname + state.path_length;
|
||||
cp = pathname + state.starting_path_length;
|
||||
if (*cp == '/')
|
||||
/* Move past the slash between the ARGV element
|
||||
and the rest of the pathname. But if the ARGV element
|
||||
@ -1187,7 +1187,7 @@ pred_okdir (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
}
|
||||
|
||||
boolean
|
||||
pred_openparen (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
pred_open (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
|
||||
{
|
||||
(void) pathname;
|
||||
(void) stat_buf;
|
||||
@ -1686,15 +1686,14 @@ ctime_format (time_t when)
|
||||
the predicate function PRED_FUNC. */
|
||||
|
||||
char *
|
||||
find_pred_name (pred_func)
|
||||
PFB pred_func;
|
||||
find_pred_name (PRED_FUNC func)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; pred_table[i].pred_func != 0; i++)
|
||||
if (pred_table[i].pred_func == pred_func)
|
||||
if (pred_table[i].pred_func == func)
|
||||
break;
|
||||
return (pred_table[i].pred_name);
|
||||
return pred_table[i].pred_name;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
||||
382
find/util.c
382
find/util.c
@ -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);
|
||||
}
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
13
m4/withfts.m4
Normal 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
|
||||
])
|
||||
Loading…
x
Reference in New Issue
Block a user