RFC: remove 'oldfind'?

Bernhard Voelker <[email protected]>
Newsgroups gmane.comp.gnu.findutils.patches,gmane.comp.gnu.findutils.bugs
Message-ID <[email protected]>
The FTS-based find is the default for a long time; oldfind has not been
installed since 4.5.18 (2015), and was only just used in tests.

Any objection to remove 'oldfind'?

Have a nice day,
Berny
0001-oldfind-remove.patch (text/x-patch, 73.5 KB)
From 5768a03ddfb5e18b1682e339d6cdd24ff721c510 Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <[email protected]>
Date: Sun, 22 Aug 2021 18:41:17 +0200
Subject: [PATCH] oldfind: remove

The FTS-based find is the default for a long time; oldfind has not been
installed since 4.5.18 (2015), and was only just used in tests.

* NEWS: Document the change.
* doc/find-maint.texi (Factor Out Repeated Code): Remove mentioning of
oldfind.
* find/.gitignore (/oldfind): Remove entry.
* find/Makefile.am (check_PROGRAMS): Remove.
(oldfind_SOURCES): Remove.
* find/defs.h (struct dir_id): Remove, it was only used in oldfind.c.
(symlink_handling): Likewise.
Adjust comments wrt oldfind otherwise.
* find/oldfind.c: Remove.
* find/testsuite/config/unix.exp: Remove the code to search for and
to run tests with oldfind.
* find/testsuite/find.posix/dotdotfiles.exp: Adjust comment.
* po/POTFILES.in (find/oldfind.c): Remove entry.
* tests/find/debug-missing-arg.sh: Remove run with oldfind.
* tests/find/exec-plus-last-file.sh: Likewise.
* tests/find/execdir-fd-leak.sh: Likewise.
* tests/find/many-dir-entries-vs-OOM.sh: Likewise.
* tests/find/name-lbracket-literal.sh: Likewise.
* tests/find/printf_escape_c.sh: Likewise.
* tests/find/printf_escapechars.sh: Likewise.
* tests/find/printf_inode.sh: Likewise.
* tests/find/refuse-noop.sh: Likewise.
* tests/find/type_list.sh: Likewise.
* tests/local.mk (built_programs): Remove oldfind from list.
---
 NEWS                                      |    4 +
 doc/find-maint.texi                       |    5 +-
 find/.gitignore                           |    1 -
 find/Makefile.am                          |    7 +-
 find/defs.h                               |   14 +-
 find/oldfind.c                            | 1556 ---------------------
 find/testsuite/config/unix.exp            |   29 +-
 find/testsuite/find.posix/dotdotfiles.exp |    2 +-
 po/POTFILES.in                            |    1 -
 tests/find/debug-missing-arg.sh           |   12 +-
 tests/find/exec-plus-last-file.sh         |   10 +-
 tests/find/execdir-fd-leak.sh             |   12 +-
 tests/find/many-dir-entries-vs-OOM.sh     |   22 +-
 tests/find/name-lbracket-literal.sh       |    5 +-
 tests/find/printf_escape_c.sh             |   17 +-
 tests/find/printf_escapechars.sh          |   50 +-
 tests/find/printf_inode.sh                |   12 +-
 tests/find/refuse-noop.sh                 |   18 +-
 tests/find/type_list.sh                   |  290 ++--
 tests/local.mk                            |    2 +-
 20 files changed, 223 insertions(+), 1846 deletions(-)
 delete mode 100644 find/oldfind.c

diff --git a/NEWS b/NEWS
index 17b08d4a..77bf365c 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,10 @@ GNU findutils NEWS - User visible changes.      -*- outline -*- (allout)
 
   The output of 'find --help' now reads better.
 
+** Changes to the build process
+
+  The find version without FTS, oldfind, has been completely removed.  It has
+  not been installed since 4.5.18 (2015), and was only still used in tests.
 
 * Noteworthy changes in release 4.8.0 (2020-01-09) [stable]
 
diff --git a/doc/find-maint.texi b/doc/find-maint.texi
index 647e6f5d..f033dc72 100644
--- a/doc/find-maint.texi
+++ b/doc/find-maint.texi
@@ -363,10 +363,7 @@ the nature of the input arguments which is in fact not true for the
 context of the now duplicated code.
 
 A good example of the use of refactoring in findutils is the
-@code{collect_arg} function in @file{find/parser.c}.  A less clear-cut
-but larger example is the factoring out of code which would otherwise
-have been duplicated between @file{find/oldfind.c} and
-@code{find/ftsfind.c}.
+@code{collect_arg} function in @file{find/parser.c}.
 
 The findutils test suite is comprehensive enough that refactoring code
 should not generally be a daunting prospect from a testing point of
diff --git a/find/.gitignore b/find/.gitignore
index 2ea5572d..d34536ca 100644
--- a/find/.gitignore
+++ b/find/.gitignore
@@ -4,4 +4,3 @@
 /Makefile.in
 /find
 /libfindtools.a
-/oldfind
diff --git a/find/Makefile.am b/find/Makefile.am
index f8d3bd95..c0779a0f 100644
--- a/find/Makefile.am
+++ b/find/Makefile.am
@@ -20,13 +20,10 @@ localedir = $(datadir)/locale
 noinst_LIBRARIES = libfindtools.a
 libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c exec.c tree.c util.c sharefile.c print.c
 
-# We always build two versions of find, one with fts (called "find"),
-# one without (called "oldfind").  The oldfind binary is no longer
-# installed.
+# We only build the version of find with fts (called "find"),
+# i.e., no longer the one called "oldfind".
 bin_PROGRAMS     = find
-check_PROGRAMS   = oldfind
 find_SOURCES     = ftsfind.c
-oldfind_SOURCES  = oldfind.c
 man_MANS         = find.1
 
 EXTRA_DIST = defs.h sharefile.h print.h $(man_MANS)
diff --git a/find/defs.h b/find/defs.h
index 1b02457d..cb519136 100644
--- a/find/defs.h
+++ b/find/defs.h
@@ -138,14 +138,6 @@ struct perm_val
   mode_t val[2];
 };
 
-/* dir_id is used to support loop detection in oldfind.c
- */
-struct dir_id
-{
-  ino_t ino;
-  dev_t dev;
-};
-
 /* samefile_file_id is used to support the -samefile test.
  */
 struct samefile_file_id
@@ -346,14 +338,13 @@ struct predicate
   const struct parser_table* parser_entry;
 };
 
-/* oldfind.c, ftsfind.c */
+/* ftsfind.c */
 bool is_fts_enabled(int *ftsoptions);
 
 /* find library function declarations.  */
 
 /* find global function declarations.  */
 
-/* oldfind.c */
 /* SymlinkOption represents the choice of
  * -P, -L or -P (default) on the command line.
  */
@@ -363,7 +354,6 @@ enum SymlinkOption
     SYMLINK_ALWAYS_DEREF,	/* Option -L */
     SYMLINK_DEREF_ARGSONLY	/* Option -H */
   };
-extern enum SymlinkOption symlink_handling; /* defined in oldfind.c. */
 
 void set_follow_state (enum SymlinkOption opt);
 void cleanup(void);
@@ -528,7 +518,7 @@ bool apply_predicate(const char *pathname, struct stat *stat_buf, struct predica
 # define pred_is(node, fn) ( ((node)->pred_func) == (fn) )
 
 
-/* oldfind.c. */
+/* util.c. */
 int get_info (const char *pathname, struct stat *p, struct predicate *pred_ptr);
 bool following_links (void);
 bool digest_mode (mode_t *mode, const char *pathname, const char *name, struct stat *pstat, bool leaf);
diff --git a/find/oldfind.c b/find/oldfind.c
deleted file mode 100644
index 8456db59..00000000
--- a/find/oldfind.c
+++ /dev/null
@@ -1,1556 +0,0 @@
-/* find -- search for files in a directory hierarchy
-   Copyright (C) 1990-2021 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
-/* GNU find was written by Eric Decker <[email protected]>,
-   with enhancements by David MacKenzie <[email protected]>,
-   Jay Plett <[email protected]>,
-   and Tim Wood <[email protected]>.
-   The idea for -print0 and xargs -0 came from
-   Dan Bernstein <[email protected]>.
-   Improvements have been made by James Youngman <[email protected]>.
-*/
-
-/* config.h must be included first. */
-#include <config.h>
-
-/* system headers. */
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-/* gnulib headers. */
-#include "canonicalize.h"
-#include "closein.h"
-#include "dirent--.h"
-#include "dirname.h"
-#include "error.h"
-#include "fcntl--.h"
-#include "human.h"
-#include "progname.h"
-#include "save-cwd.h"
-#include "xalloc.h"
-#include "xgetcwd.h"
-
-
-/* find headers. */
-#include "buildcmd.h"
-#include "defs.h"
-#include "die.h"
-#include "fdleak.h"
-#include "system.h"
-
-#undef  STAT_MOUNTPOINTS
-
-#ifdef CLOSEDIR_VOID
-/* Fake a return value. */
-# define CLOSEDIR(d) (closedir (d), 0)
-#else
-# define CLOSEDIR(d) closedir (d)
-#endif
-
-enum
-{
-  NOT_AN_INODE_NUMBER = 0
-};
-
-#ifdef D_INO_IN_DIRENT
-# define D_INO(dp) (dp)->d_ino
-#else
-/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
-# define D_INO(dp) NOT_AN_INODE_NUMBER
-#endif
-
-#ifdef STAT_MOUNTPOINTS
-static void init_mounted_dev_list (int mandatory);
-#endif
-
-static void process_top_path (const char *pathname, mode_t mode, ino_t inum);
-static int process_path (const char *pathname, const char *name, bool leaf, const char *parent, mode_t type, ino_t inum);
-static void process_dir (const char *pathname, const char *name, int pathlen, const struct stat *statp, const char *parent);
-
-/* A file descriptor open to the initial working directory.
-   Doing it this way allows us to work when the i.w.d. has
-   unreadable parents.  */
-extern int starting_desc;
-
-/* The stat buffer of the initial working directory. */
-static struct stat starting_stat_buf;
-
-enum ChdirSymlinkHandling
-  {
-    SymlinkHandleDefault,	/* Normally the right choice */
-    SymlinkFollowOk		/* see comment in process_top_path() */
-  };
-
-
-enum TraversalDirection
-  {
-    TraversingUp,
-    TraversingDown
-  };
-
-enum WdSanityCheckFatality
-  {
-    FATAL_IF_SANITY_CHECK_FAILS,
-    RETRY_IF_SANITY_CHECK_FAILS,
-    NON_FATAL_IF_SANITY_CHECK_FAILS
-  };
-
-#if defined HAVE_STRUCT_DIRENT_D_TYPE
-/* Convert the value of struct dirent.d_type into a value for
- * struct stat.st_mode (at least the file type bits), or zero
- * if the type is DT_UNKNOWN or is a value we don't know about.
- */
-static mode_t
-type_to_mode (unsigned type)
-{
-  switch (type)
-    {
-# ifdef DT_FIFO
-    case DT_FIFO: return S_IFIFO;
-# endif
-# ifdef DT_CHR
-    case DT_CHR:  return S_IFCHR;
-# endif
-# ifdef DT_DIR
-    case DT_DIR:  return S_IFDIR;
-# endif
-# ifdef DT_BLK
-    case DT_BLK:  return S_IFBLK;
-# endif
-# ifdef DT_REG
-    case DT_REG:  return S_IFREG;
-# endif
-# ifdef DT_LNK
-    case DT_LNK:  return S_IFLNK;
-# endif
-# ifdef DT_SOCK
-    case DT_SOCK: return S_IFSOCK;
-# endif
-    default:
-      return 0;			/* Unknown. */
-    }
-}
-#endif
-
-
-/* CAUTION: this is the entry point for the oldfind executable, which is not the binary that
- * will actually get installed.   See ftsfind.c. */
-int
-main (int argc, char **argv)
-{
-  int i;
-  int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
-  struct predicate *eval_tree;
-
-  if (argv[0])
-    set_program_name (argv[0]);
-  else
-    set_program_name ("find");
-
-  state.exit_status = 0;
-
-  if (fd_leak_check_is_enabled ())
-    {
-      remember_non_cloexec_fds ();
-    }
-
-  record_initial_cwd ();
-
-  state.already_issued_stat_error_msg = false;
-  state.shared_files = sharefile_init ("w");
-  if (NULL == state.shared_files)
-    {
-      die (EXIT_FAILURE, errno,
-	   _("Failed to initialize shared-file hash table"));
-    }
-
-  /* Set the option defaults before we do the locale
-   * initialisation as check_nofollow () needs to be executed in the
-   * POSIX locale.
-   */
-  set_option_defaults (&options);
-
-#ifdef HAVE_SETLOCALE
-  setlocale (LC_ALL, "");
-#endif
-  bindtextdomain (PACKAGE, LOCALEDIR);
-  textdomain (PACKAGE);
-  if (atexit (close_stdin))
-    {
-      die (EXIT_FAILURE, errno, _("The atexit library function failed"));
-    }
-
-  /* Check for -P, -H or -L options. */
-  end_of_leading_options = process_leading_options (argc, argv);
-
-  if (options.debug_options & DebugStat)
-    options.xstat = debug_stat;
-
-  if (options.debug_options & DebugTime)
-    fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start.tv_sec));
-
-  /* state.cwd_dir_fd has to be initialized before we call build_expression_tree ()
-   * because command-line parsing may lead us to stat some files.
-   */
-  state.cwd_dir_fd = AT_FDCWD;
-
-  /* We are now processing the part of the "find" command line
-   * after the -H/-L options (if any).
-   */
-  eval_tree = build_expression_tree (argc, argv, end_of_leading_options);
-
-
-  /* safely_chdir () needs to check that it has ended up in the right place.
-   * To avoid bailing out when something gets automounted, it checks if
-   * the target directory appears to have had a directory mounted on it as
-   * we chdir ()ed.  The problem with this is that in order to notice that
-   * a file system was mounted, we would need to lstat () all the mount points.
-   * That strategy loses if our machine is a client of a dead NFS server.
-   *
-   * Hence if safely_chdir () and wd_sanity_check () can manage without needing
-   * to know the mounted device list, we do that.
-   */
-  if (!options.open_nofollow_available)
-    {
-#ifdef STAT_MOUNTPOINTS
-      init_mounted_dev_list (0);
-#endif
-    }
-
-
-  set_stat_placeholders (&starting_stat_buf);
-  if ((*options.xstat) (".", &starting_stat_buf) != 0)
-    die (EXIT_FAILURE, errno, _("cannot stat current directory"));
-
-  /* If no paths are given, default to ".".  */
-  for (i = end_of_leading_options; i < argc && !looks_like_expression (argv[i], true); i++)
-    {
-      process_top_path (argv[i], 0, starting_stat_buf.st_ino);
-    }
-
-  /* If there were no path arguments, default to ".". */
-  if (i == end_of_leading_options)
-    {
-      /*
-       * We use a temporary variable here because some actions modify
-       * the path temporarily.  Hence if we use a string constant,
-       * we get a coredump.  The best example of this is if we say
-       * "find -printf %H" (note, not "find . -printf %H").
-       */
-      char defaultpath[2] = ".";
-      process_top_path (defaultpath, 0, starting_stat_buf.st_ino);
-    }
-
-  /* If "-exec ... {} +" has been used, there may be some
-   * partially-full command lines which have been built,
-   * but which are not yet complete.   Execute those now.
-   */
-  show_success_rates (eval_tree);
-  cleanup ();
-  return state.exit_status;
-}
-
-bool is_fts_enabled (int *ftsoptions)
-{
-  /* this version of find (i.e. this main ()) does not use fts. */
-  *ftsoptions = 0;
-  return false;
-}
-
-
-static char *
-specific_dirname (const char *dir) _GL_ATTRIBUTE_MALLOC;
-static char *
-specific_dirname (const char *dir)
-{
-  char dirbuf[1024];
-
-  if (0 == strcmp (".", dir))
-    {
-      /* OK, what's '.'? */
-      if (NULL != getcwd (dirbuf, sizeof (dirbuf)))
-	{
-	  return strdup (dirbuf);
-	}
-      else
-	{
-	  return strdup (dir);
-	}
-    }
-  else
-    {
-      char *result = canonicalize_filename_mode (dir, CAN_EXISTING);
-      if (NULL == result)
-	return strdup (dir);
-      else
-	return result;
-    }
-}
-
-
-
-/* Return non-zero if FS is the name of a file system that is likely to
- * be automounted
- */
-static int
-fs_likely_to_be_automounted (const char *fs)
-{
-  return ( (0==strcmp (fs, "nfs")) || (0==strcmp (fs, "autofs")) || (0==strcmp (fs, "subfs")));
-}
-
-
-
-#ifdef STAT_MOUNTPOINTS
-static dev_t *mounted_devices = NULL;
-static size_t num_mounted_devices = 0u;
-
-
-static void
-init_mounted_dev_list (int mandatory)
-{
-  assert (NULL == mounted_devices);
-  assert (0 == num_mounted_devices);
-  mounted_devices = get_mounted_devices (&num_mounted_devices);
-  if (mandatory && (NULL == mounted_devices))
-    {
-      die (EXIT_FAILURE, 0, _("Cannot read list of mounted devices."));
-    }
-}
-
-static void
-refresh_mounted_dev_list (void)
-{
-  if (mounted_devices)
-    {
-      free (mounted_devices);
-      mounted_devices = 0;
-    }
-  num_mounted_devices = 0u;
-  init_mounted_dev_list (1);
-}
-
-
-/* Search for device DEV in the array LIST, which is of size N. */
-static int
-dev_present (dev_t dev, const dev_t *list, size_t n)
-{
-  if (list)
-    {
-      while (n-- > 0u)
-	{
-	  if ( (*list++) == dev )
-	    return 1;
-	}
-    }
-  return 0;
-}
-
-enum MountPointStateChange
-  {
-    MountPointRecentlyMounted,
-    MountPointRecentlyUnmounted,
-    MountPointStateUnchanged
-  };
-
-
-
-static enum MountPointStateChange
-get_mount_state (dev_t newdev)
-{
-  int new_is_present, new_was_present;
-
-  new_was_present = dev_present (newdev, mounted_devices, num_mounted_devices);
-  refresh_mounted_dev_list ();
-  new_is_present  = dev_present (newdev, mounted_devices, num_mounted_devices);
-
-  if (new_was_present == new_is_present)
-    return MountPointStateUnchanged;
-  else if (new_is_present)
-    return MountPointRecentlyMounted;
-  else
-    return MountPointRecentlyUnmounted;
-}
-
-
-
-/* We stat()ed a directory, chdir()ed into it (we know this
- * since direction is TraversingDown), stat()ed it again,
- * and noticed that the device numbers are different.  Check
- * if the file system was recently mounted.
- *
- * If it was, it looks like chdir()ing into the directory
- * caused a file system to be mounted.  Maybe automount is
- * running.  Anyway, that's probably OK - but it happens
- * only when we are moving downward.
- *
- * We also allow for the possibility that a similar thing
- * has happened with the unmounting of a file system.  This
- * is much rarer, as it relies on an automounter timeout
- * occurring at exactly the wrong moment.
- */
-static enum WdSanityCheckFatality
-dirchange_is_fatal (const char *specific_what,
-		    enum WdSanityCheckFatality isfatal,
-		    int silent,
-		    struct stat *newinfo)
-{
-  enum MountPointStateChange transition = get_mount_state (newinfo->st_dev);
-  switch (transition)
-    {
-    case MountPointRecentlyUnmounted:
-      isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
-      if (!silent)
-	{
-	  error (0, 0,
-		 _("WARNING: file system %s has recently been unmounted."),
-		 safely_quote_err_filename (0, specific_what));
-	}
-      break;
-
-    case MountPointRecentlyMounted:
-      isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
-      if (!silent)
-	{
-	  error (0, 0,
-		 _("WARNING: file system %s has recently been mounted."),
-		 safely_quote_err_filename (0, specific_what));
-	}
-      break;
-
-    case MountPointStateUnchanged:
-      /* leave isfatal as it is */
-      break;
-    }
-
-  return isfatal;
-}
-
-
-#endif
-
-
-
-/* Examine the results of the stat() of a directory from before we
- * entered or left it, with the results of stat()ing it afterward.  If
- * these are different, the file system tree has been modified while we
- * were traversing it.  That might be an attempt to use a race
- * condition to persuade find to do something it didn't intend
- * (e.g. an attempt by an ordinary user to exploit the fact that root
- * sometimes runs find on the whole file system).  However, this can
- * also happen if automount is running (certainly on Solaris).  With
- * automount, moving into a directory can cause a file system to be
- * mounted there.
- *
- * To cope sensibly with this, we will raise an error if we see the
- * device number change unless we are chdir()ing into a subdirectory,
- * and the directory we moved into has been mounted or unmounted "recently".
- * Here "recently" means since we started "find" or we last re-read
- * the /etc/mnttab file.
- *
- * If the device number does not change but the inode does, that is a
- * problem.
- *
- * If the device number and inode are both the same, we are happy.
- *
- * If a file system is (un)mounted as we chdir() into the directory, that
- * may mean that we're now examining a section of the file system that might
- * have been excluded from consideration (via -prune or -quit for example).
- * Hence we print a warning message to indicate that the output of find
- * might be inconsistent due to the change in the file system.
- */
-static bool
-wd_sanity_check (const char *thing_to_stat,
-		const char *progname,
-		const char *what,
-		dev_t old_dev,
-		ino_t old_ino,
-		struct stat *newinfo,
-		int parent,
-		int line_no,
-		enum TraversalDirection direction,
-		enum WdSanityCheckFatality isfatal,
-		bool *changed) /* output parameter */
-{
-  const char *fstype;
-  char *specific_what = NULL;
-  int silent = 0;
-  const char *current_dir = ".";
-
-  *changed = false;
-
-  set_stat_placeholders (newinfo);
-  if ((*options.xstat) (current_dir, newinfo) != 0)
-    fatal_target_file_error (errno, thing_to_stat);
-
-  if (old_dev != newinfo->st_dev)
-    {
-      *changed = true;
-      specific_what = specific_dirname (what);
-      fstype = filesystem_type (newinfo, current_dir);
-      silent = fs_likely_to_be_automounted (fstype);
-
-      /* This condition is rare, so once we are here it is
-       * reasonable to perform an expensive computation to
-       * determine if we should continue or fail.
-       */
-      if (TraversingDown == direction)
-	{
-#ifdef STAT_MOUNTPOINTS
-	  isfatal = dirchange_is_fatal (specific_what,isfatal,silent,newinfo);
-#else
-	  (void) silent;
-	  isfatal = RETRY_IF_SANITY_CHECK_FAILS;
-#endif
-	}
-
-      switch (isfatal)
-	{
-	case FATAL_IF_SANITY_CHECK_FAILS:
-	  {
-	    fstype = filesystem_type (newinfo, current_dir);
-	    die (EXIT_FAILURE, 0,
-		 _("%s%s changed during execution of %s (old device number %ld, new device number %ld, file system type is %s) [ref %ld]"),
-		 safely_quote_err_filename (0, specific_what),
-		 parent ? "/.." : "",
-		 safely_quote_err_filename (1, progname),
-		 (long) old_dev,
-		 (long) newinfo->st_dev,
-		 fstype,
-		 (long)line_no);
-	    /*NOTREACHED*/
-	    return false;
-	  }
-
-	case NON_FATAL_IF_SANITY_CHECK_FAILS:
-	  {
-	    /* Since the device has changed under us, the inode number
-	     * will almost certainly also be different. However, we have
-	     * already decided that this is not a problem.  Hence we return
-	     * without checking the inode number.
-	     */
-	    free (specific_what);
-	    return true;
-	  }
-
-	case RETRY_IF_SANITY_CHECK_FAILS:
-	  return false;
-	}
-    }
-
-  /* Device number was the same, check if the inode has changed. */
-  if (old_ino != newinfo->st_ino)
-    {
-      *changed = true;
-      specific_what = specific_dirname (what);
-      fstype = filesystem_type (newinfo, current_dir);
-
-      error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0,
-	     0,			/* no relevant errno value */
-	     _("%s%s changed during execution of %s "
-	       "(old inode number %" PRIuMAX ", new inode number %" PRIuMAX
-	       ", file system type is %s) [ref %ld]"),
-	     safely_quote_err_filename (0, specific_what),
-	     parent ? "/.." : "",
-	     safely_quote_err_filename (1, progname),
-	     (uintmax_t) old_ino,
-	     (uintmax_t) newinfo->st_ino,
-	     fstype,
-	     (long)line_no);
-      free (specific_what);
-      return false;
-    }
-
-  return true;
-}
-
-enum SafeChdirStatus
-  {
-    SafeChdirOK,
-    SafeChdirFailSymlink,
-    SafeChdirFailNotDir,
-    SafeChdirFailStat,
-    SafeChdirFailWouldBeUnableToReturn,
-    SafeChdirFailChdirFailed,
-    SafeChdirFailNonexistent,
-    SafeChdirFailDestUnreadable
-  };
-
-/* Safely perform a change in directory.  We do this by calling
- * lstat() on the subdirectory, using chdir() to move into it, and
- * then lstat()ing ".".  We compare the results of the two stat calls
- * to see if they are consistent.  If not, we sound the alarm.
- *
- * If following_links() is true, we do follow symbolic links.
- */
-static enum SafeChdirStatus
-safely_chdir_lstat (const char *dest,
-		    enum TraversalDirection direction,
-		    struct stat *statbuf_dest,
-		    enum ChdirSymlinkHandling symlink_follow_option,
-		    bool *did_stat)
-{
-  struct stat statbuf_arrived;
-  int rv, dotfd=-1;
-  int saved_errno;		/* specific_dirname() changes errno. */
-  bool rv_set = false;
-  bool statflag = false;
-  int tries = 0;
-  enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS;
-
-  saved_errno = errno = 0;
-
-  dotfd = open_cloexec (".", O_RDONLY
-#if defined O_LARGEFILE
-			|O_LARGEFILE
-#endif
-			);
-
-  /* We jump back to here if wd_sanity_check()
-   * recoverably triggers an alert.
-   */
- retry:
-  ++tries;
-
-  if (dotfd >= 0)
-    {
-      /* Stat the directory we're going to. */
-      set_stat_placeholders (statbuf_dest);
-      if (0 == options.xstat (dest, statbuf_dest))
-	{
-	  statflag = true;
-
-#ifdef S_ISLNK
-	  /* symlink_follow_option might be set to SymlinkFollowOk, which
-	   * would allow us to chdir() into a symbolic link.  This is
-	   * only useful for the case where the directory we're
-	   * chdir()ing into is the basename of a command line
-	   * argument, for example where "foo/bar/baz" is specified on
-	   * the command line.  When -P is in effect (the default),
-	   * baz will not be followed if it is a symlink, but if bar
-	   * is a symlink, it _should_ be followed.  Hence we need the
-	   * ability to override the policy set by following_links().
-	   */
-	  if (!following_links () && S_ISLNK(statbuf_dest->st_mode))
-	    {
-	      /* We're not supposed to be following links, but this is
-	       * a link.  Check symlink_follow_option to see if we should
-	       * make a special exception.
-	       */
-	      if (symlink_follow_option == SymlinkFollowOk)
-		{
-		  /* We need to re-stat() the file so that the
-		   * sanity check can pass.
-		   */
-		  if (0 != stat (dest, statbuf_dest))
-		    {
-		      rv = SafeChdirFailNonexistent;
-		      rv_set = true;
-		      saved_errno = errno;
-		      goto fail;
-		    }
-		  statflag = true;
-		}
-	      else
-		{
-		  /* Not following symlinks, so the attempt to
-		   * chdir() into a symlink should be prevented.
-		   */
-		  rv = SafeChdirFailSymlink;
-		  rv_set = true;
-		  saved_errno = 0;	/* silence the error message */
-		  goto fail;
-		}
-	    }
-#endif
-#ifdef S_ISDIR
-	  /* Although the immediately following chdir() would detect
-	   * the fact that this is not a directory for us, this would
-	   * result in an extra system call that fails.  Anybody
-	   * examining the system-call trace should ideally not be
-	   * concerned that something is actually failing.
-	   */
-	  if (!S_ISDIR(statbuf_dest->st_mode))
-	    {
-	      rv = SafeChdirFailNotDir;
-	      rv_set = true;
-	      saved_errno = 0;	/* silence the error message */
-	      goto fail;
-	    }
-#endif
-
-	  if (options.debug_options & DebugSearch)
-	    fprintf (stderr, "safely_chdir(): chdir(\"%s\")\n", dest);
-
-	  if (0 == chdir (dest))
-	    {
-	      /* check we ended up where we wanted to go */
-	      bool changed = false;
-	      if (!wd_sanity_check (".", program_name, ".",
-				    statbuf_dest->st_dev,
-				    statbuf_dest->st_ino,
-				    &statbuf_arrived,
-				    0, __LINE__, direction,
-				    isfatal,
-				    &changed))
-		{
-		  /* Only allow one failure. */
-		  if (RETRY_IF_SANITY_CHECK_FAILS == isfatal)
-		    {
-		      if (0 == fchdir (dotfd))
-			{
-			  isfatal = FATAL_IF_SANITY_CHECK_FAILS;
-			  goto retry;
-			}
-		      else
-			{
-			  /* Failed to return to original directory,
-			   * but we know that the current working
-			   * directory is not the one that we intend
-			   * to be in.  Since fchdir() failed, we
-			   * can't recover from this and so this error
-			   * is fatal.
-			   */
-			  die (EXIT_FAILURE, errno,
-			       _("failed to return to parent directory"));
-			}
-		    }
-		  else
-		    {
-		      /* XXX: not sure what to use as an excuse here. */
-		      rv = SafeChdirFailNonexistent;
-		      rv_set = true;
-		      saved_errno = 0;
-		      goto fail;
-		    }
-		}
-
-	      close (dotfd);
-	      return SafeChdirOK;
-	    }
-	  else
-	    {
-	      saved_errno = errno;
-	      if (ENOENT == saved_errno)
-		{
-		  rv = SafeChdirFailNonexistent;
-		  rv_set = true;
-		  if (options.ignore_readdir_race)
-		    errno = 0;	/* don't issue err msg */
-		}
-	      else if (ENOTDIR == saved_errno)
-		{
-		  /* This can happen if the we stat a directory,
-		   * and then file system activity changes it into
-		   * a non-directory.
-		   */
-		  saved_errno = 0;	/* don't issue err msg */
-		  rv = SafeChdirFailNotDir;
-		  rv_set = true;
-		}
-	      else
-		{
-		  rv = SafeChdirFailChdirFailed;
-		  rv_set = true;
-		}
-	      goto fail;
-	    }
-	}
-      else
-	{
-	  saved_errno = errno;
-	  rv = SafeChdirFailStat;
-	  rv_set = true;
-
-	  if ( (ENOENT == saved_errno) || (0 == state.curdepth))
-	    saved_errno = 0;	/* don't issue err msg */
-	  goto fail;
-	}
-    }
-  else
-    {
-      /* We do not have read permissions on "." */
-      rv = SafeChdirFailWouldBeUnableToReturn;
-      rv_set = true;
-      goto fail;
-    }
-
-  /* This is the success path, so we clear errno.  The caller probably
-   * won't be calling error() anyway.
-   */
-  saved_errno = 0;
-
-  /* We use the same exit path for success or failure.
-   * which has occurred is recorded in RV.
-   */
- fail:
-  /* We do not call error() as this would result in a duplicate error
-   * message when the caller does the same thing.
-   */
-  if (saved_errno)
-    errno = saved_errno;
-
-  if (dotfd >= 0)
-    {
-      close (dotfd);
-      dotfd = -1;
-    }
-
-  *did_stat = statflag;
-  assert (rv_set);
-  return rv;
-}
-
-/* Safely change working directory to the specified subdirectory.  If
- * we are not allowed to follow symbolic links, we use open() with
- * O_NOFOLLOW, followed by fchdir().  This ensures that we don't
- * follow symbolic links (of course, we do follow them if the -L
- * option is in effect).
- */
-static enum SafeChdirStatus
-safely_chdir_nofollow (const char *dest,
-		       enum TraversalDirection direction,
-		       struct stat *statbuf_dest,
-		       enum ChdirSymlinkHandling symlink_follow_option,
-		       bool *did_stat)
-{
-  int extraflags, fd;
-
-  (void) direction;
-  (void) statbuf_dest;
-
-  extraflags = 0;
-  *did_stat = false;
-
-  switch (symlink_follow_option)
-    {
-    case SymlinkFollowOk:
-      extraflags = 0;
-      break;
-
-    case SymlinkHandleDefault:
-      if (following_links ())
-	extraflags = 0;
-      else
-	extraflags = O_NOFOLLOW; /* ... which may still be 0. */
-      break;
-    }
-
-  errno = 0;
-  fd = open (dest, O_RDONLY
-#if defined O_LARGEFILE
-	    |O_LARGEFILE
-#endif
-#if defined O_CLOEXEC
-	    |O_CLOEXEC
-#endif
-	    |extraflags);
-  if (fd < 0)
-    {
-      switch (errno)
-	{
-	case ELOOP:
-	  return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */
-	case ENOENT:
-	  return SafeChdirFailNonexistent;
-	default:
-	  return SafeChdirFailDestUnreadable;
-	}
-    }
-
-  errno = 0;
-  if (0 == fchdir (fd))
-    {
-      close (fd);
-      return SafeChdirOK;
-    }
-  else
-    {
-      int saved_errno = errno;
-      close (fd);
-      errno = saved_errno;
-
-      switch (errno)
-	{
-	case ENOTDIR:
-	  return SafeChdirFailNotDir;
-
-	case EACCES:
-	case EBADF:		/* Shouldn't happen */
-	case EINTR:
-	case EIO:
-	default:
-	  return SafeChdirFailChdirFailed;
-	}
-    }
-}
-
-static enum SafeChdirStatus
-safely_chdir (const char *dest,
-	      enum TraversalDirection direction,
-	      struct stat *statbuf_dest,
-	      enum ChdirSymlinkHandling symlink_follow_option,
-	      bool *did_stat)
-{
-  enum SafeChdirStatus result;
-
-  /* We're about to leave a directory.  If there are any -execdir
-   * argument lists which have been built but have not yet been
-   * processed, do them now because they must be done in the same
-   * directory.
-   */
-  complete_pending_execdirs ();
-
-  /* gnulib defines O_NOFOLLOW to 0 if the OS doesn't have it. */
-  options.open_nofollow_available = !!O_NOFOLLOW;
-  if (options.open_nofollow_available)
-    {
-      result = safely_chdir_nofollow (dest, direction, statbuf_dest,
-				     symlink_follow_option, did_stat);
-      if (SafeChdirFailDestUnreadable != result)
-	{
-	  return result;
-	}
-      else
-	{
-	  /* Savannah bug #15384: fall through to use safely_chdir_lstat
-	   * if the directory is not readable.
-	   */
-	  /* Do nothing. */
-	}
-    }
-  /* Even if O_NOFOLLOW is available, we may need to use the alternative
-   * method, since parent of the start point may be executable but not
-   * readable.
-   */
-  return safely_chdir_lstat (dest, direction, statbuf_dest,
-			     symlink_follow_option, did_stat);
-}
-
-
-
-/* Safely go back to the starting directory. */
-static void
-chdir_back (void)
-{
-  if (options.debug_options & DebugSearch)
-    fprintf (stderr, "chdir_back(): chdir to start point\n");
-
-  restore_cwd (initial_wd);
-}
-
-/* Move to the parent of a given directory and then call a function,
- * restoring the cwd.  Don't bother changing directory if the
- * specified directory is a child of "." or is the root directory.
- */
-static void
-at_top (const char *pathname,
-	mode_t mode,
-	ino_t inum,
-	struct stat *pstat,
-	void (*action)(const char *pathname,
-		       const char *basename,
-		       int mode,
-		       ino_t inum,
-		       struct stat *pstat))
-{
-  int dirchange;
-  char *parent_dir = dir_name (pathname);
-  const char *base = last_component (pathname);
-
-  state.curdepth = 0;
-  state.starting_path_length = strlen (pathname);
-
-  if (0 == *base
-      || 0 == strcmp (parent_dir, "."))
-    {
-      dirchange = 0;
-      base = pathname;
-    }
-  else
-    {
-      enum TraversalDirection direction;
-      enum SafeChdirStatus chdir_status;
-      struct stat st;
-      bool did_stat = false;
-
-      dirchange = 1;
-      if (0 == strcmp (base, ".."))
-	direction = TraversingUp;
-      else
-	direction = TraversingDown;
-
-      /* We pass SymlinkFollowOk to safely_chdir(), which allows it to
-       * chdir() into a symbolic link.  This is only useful for the
-       * case where the directory we're chdir()ing into is the
-       * basename of a command line argument, for example where
-       * "foo/bar/baz" is specified on the command line.  When -P is
-       * in effect (the default), baz will not be followed if it is a
-       * symlink, but if bar is a symlink, it _should_ be followed.
-       * Hence we need the ability to override the policy set by
-       * following_links().
-       */
-      chdir_status = safely_chdir (parent_dir, direction, &st, SymlinkFollowOk, &did_stat);
-      if (SafeChdirOK != chdir_status)
-	{
-	  const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir;
-	  if (errno)
-	    error (0, errno, "%s",
-		   safely_quote_err_filename (0, what));
-	  else
-	    error (0, 0, _("Failed to safely change directory into %s"),
-		   safely_quote_err_filename (0, parent_dir));
-
-	  /* We can't process this command-line argument. */
-	  state.exit_status = 1;
-	  return;
-	}
-    }
-
-  free (parent_dir);
-  parent_dir = NULL;
-
-  action (pathname, base, mode, inum, pstat);
-
-  if (dirchange)
-    {
-      chdir_back ();
-    }
-}
-
-
-static void do_process_top_dir (const char *pathname,
-				const char *base,
-				int mode,
-				ino_t inum,
-				struct stat *pstat)
-{
-  (void) pstat;
-
-  process_path (pathname, base, false, ".", mode, inum);
-  complete_pending_execdirs ();
-}
-
-static void
-do_process_predicate (const char *pathname,
-		      const char *base,
-		      int mode,
-		      ino_t inum,
-		      struct stat *pstat)
-{
-  (void) mode;
-  (void) inum;
-  state.rel_pathname = base;	/* cwd_dir_fd was already set by safely_chdir */
-  apply_predicate (pathname, pstat, get_eval_tree ());
-}
-
-
-
-
-/* Descend PATHNAME, which is a command-line argument.
-
-   Actions like -execdir assume that we are in the
-   parent directory of the file we're examining,
-   and on entry to this function our working directory
-   is whatever it was when find was invoked.  Therefore
-   If PATHNAME is "." we just leave things as they are.
-   Otherwise, we figure out what the parent directory is,
-   and move to that.
-*/
-static void
-process_top_path (const char *pathname, mode_t mode, ino_t inum)
-{
-  at_top (pathname, mode, inum, NULL, do_process_top_dir);
-}
-
-
-/* Info on each directory in the current tree branch, to avoid
-   getting stuck in symbolic link loops.  */
-static struct dir_id *dir_ids = NULL;
-/* Entries allocated in `dir_ids'.  */
-static int dir_alloc = 0;
-/* Index in `dir_ids' of directory currently being searched.
-   This is always the last valid entry.  */
-static int dir_curr = -1;
-/* (Arbitrary) number of entries to grow `dir_ids' by.  */
-#define DIR_ALLOC_STEP 32
-
-
-
-/* We've detected a file system loop.   This is caused by one of
- * two things:
- *
- * 1. Option -L is in effect and we've hit a symbolic link that
- *    points to an ancestor.  This is harmless.  We won't traverse the
- *    symbolic link.
- *
- * 2. We have hit a real cycle in the directory hierarchy.  In this
- *    case, we issue a diagnostic message (POSIX requires this) and we
- *    skip that directory entry.
- */
-static void
-issue_loop_warning (const char *name, const char *pathname, int level)
-{
-  struct stat stbuf_link;
-  if (lstat (name, &stbuf_link) != 0)
-    stbuf_link.st_mode = S_IFREG;
-
-  if (S_ISLNK(stbuf_link.st_mode))
-    {
-      error (0, 0,
-	     _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."),
-	     safely_quote_err_filename (0, pathname));
-      /* XXX: POSIX appears to require that the exit status be non-zero if a
-       * diagnostic is issued.
-       */
-    }
-  else
-    {
-      int distance = 1 + (dir_curr-level);
-      /* We have found an infinite loop.  POSIX requires us to
-       * issue a diagnostic.  Usually we won't get to here
-       * because when the leaf optimisation is on, it will cause
-       * the subdirectory to be skipped.  If /a/b/c/d is a hard
-       * link to /a/b, then the link count of /a/b/c is 2,
-       * because the ".." entry of /b/b/c/d points to /a, not
-       * to /a/b/c.
-       */
-      error (0, 0,
-	     ngettext (
-		       "Filesystem loop detected; %s has the same device number and inode as "
-		       "a directory which is %d level higher in the file system hierarchy",
-		       "Filesystem loop detected; %s has the same device number and inode as "
-		       "a directory which is %d levels higher in the file system hierarchy",
-		       (long)distance),
-	     safely_quote_err_filename (0, pathname),
-	     distance);
-    }
-}
-
-
-
-/* Recursively descend path PATHNAME, applying the predicates.
-   LEAF is true if PATHNAME is known to be in a directory that has no
-   more unexamined subdirectories, and therefore it is not a directory.
-   Knowing this allows us to avoid calling stat as long as possible for
-   leaf files.
-
-   NAME is PATHNAME relative to the current directory.  We access NAME
-   but print PATHNAME.
-
-   PARENT is the path of the parent of NAME, relative to find's
-   starting directory.
-
-   Return nonzero iff PATHNAME is a directory. */
-
-static int
-process_path (const char *pathname, const char *name, bool leaf, const char *parent,
-	      mode_t mode, ino_t inum)
-{
-  struct stat stat_buf;
-  static dev_t root_dev;	/* Device ID of current argument pathname. */
-  int i;
-  struct predicate *eval_tree;
-
-  eval_tree = get_eval_tree ();
-  /* Assume it is a non-directory initially. */
-  stat_buf.st_mode = 0;
-
-  /* The caller usually knows the inode number, either from readdir or
-   * a *stat call.  We use that value (the caller passes 0 to indicate
-   * ignorance of the inode number).
-   */
-  stat_buf.st_ino = inum;
-
-  state.rel_pathname = name;
-  state.type = 0;
-  state.have_stat = false;
-  state.have_type = false;
-  state.already_issued_stat_error_msg = false;
-
-  if (!digest_mode (&mode, pathname, name, &stat_buf, leaf))
-    return 0;
-
-  if (!S_ISDIR (state.type))
-    {
-      if (state.curdepth >= options.mindepth)
-	apply_predicate (pathname, &stat_buf, eval_tree);
-      return 0;
-    }
-
-  /* From here on, we're working on a directory.  */
-
-
-  /* Now we really need to stat the directory, even if we know the
-   * type, because we need information like struct stat.st_rdev.
-   */
-  if (get_statinfo (pathname, name, &stat_buf) != 0)
-    return 0;
-
-  state.have_stat = true;
-  mode = state.type = stat_buf.st_mode;	/* use full info now that we have it. */
-  state.stop_at_current_level =
-    options.maxdepth >= 0
-    && state.curdepth >= options.maxdepth;
-
-  /* If we've already seen this directory on this branch,
-     don't descend it again.  */
-  for (i = 0; i <= dir_curr; i++)
-    if (stat_buf.st_ino == dir_ids[i].ino &&
-	stat_buf.st_dev == dir_ids[i].dev)
-      {
-	state.stop_at_current_level = true;
-	issue_loop_warning (name, pathname, i);
-      }
-
-  if (dir_alloc <= ++dir_curr)
-    {
-      dir_alloc += DIR_ALLOC_STEP;
-      dir_ids = (struct dir_id *)
-	xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id));
-    }
-  dir_ids[dir_curr].ino = stat_buf.st_ino;
-  dir_ids[dir_curr].dev = stat_buf.st_dev;
-
-  if (options.stay_on_filesystem)
-    {
-      if (state.curdepth == 0)
-	root_dev = stat_buf.st_dev;
-      else if (stat_buf.st_dev != root_dev)
-	state.stop_at_current_level = true;
-    }
-
-  if (options.do_dir_first && state.curdepth >= options.mindepth)
-    apply_predicate (pathname, &stat_buf, eval_tree);
-
-  if (options.debug_options & DebugSearch)
-    fprintf (stderr, "pathname = %s, stop_at_current_level = %d\n",
-	     pathname, state.stop_at_current_level);
-
-  if (state.stop_at_current_level == false)
-    {
-      /* Scan directory on disk. */
-      process_dir (pathname, name, strlen (pathname), &stat_buf, parent);
-    }
-
-  if (options.do_dir_first == false && state.curdepth >= options.mindepth)
-    {
-      /* The fields in 'state' are now out of date.  Correct them.
-       */
-      if (!digest_mode (&mode, pathname, name, &stat_buf, leaf))
-	return 0;
-
-      if (0 == dir_curr)
-	{
-	  at_top (pathname, mode, stat_buf.st_ino, &stat_buf,
-		  do_process_predicate);
-	}
-      else
-	{
-	  do_process_predicate (pathname, name, mode, stat_buf.st_ino,
-				&stat_buf);
-	}
-    }
-
-  dir_curr--;
-
-  return 1;
-}
-
-
-/* Scan directory PATHNAME and recurse through process_path for each entry.
-
-   PATHLEN is the length of PATHNAME.
-
-   NAME is PATHNAME relative to the current directory.
-
-   STATP is the results of *options.xstat on it.
-
-   PARENT is the path of the parent of NAME, relative to find's
-   starting directory.  */
-
-static void
-process_dir (const char *pathname, const char *name, int pathlen, const struct stat *statp, const char *parent)
-{
-  int subdirs_left;		/* Number of unexamined subdirs in PATHNAME. */
-  bool subdirs_unreliable;	/* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
-  struct stat stat_buf;
-  size_t dircount = 0u;
-  DIR *dirp;
-
-  if (statp->st_nlink < 2)
-    {
-      subdirs_unreliable = true;
-      subdirs_left = 0;
-    }
-  else
-    {
-      subdirs_unreliable = false; /* not necessarily right */
-      subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */
-    }
-
-  errno = 0;
-  dirp = opendir (name);
-
-  if (dirp == NULL)
-    {
-      assert (errno != 0);
-      error (0, errno, "%s", safely_quote_err_filename (0, pathname));
-      state.exit_status = 1;
-    }
-  else
-    {
-      char *cur_path;		/* Full path of each file to process. */
-      char *cur_name;		/* Base name of each file to process. */
-      unsigned cur_path_size;	/* Bytes allocated for `cur_path'. */
-      register unsigned file_len; /* Length of each path to process. */
-      register unsigned pathname_len; /* PATHLEN plus trailing '/'. */
-      bool did_stat = false;
-
-      if (pathname[pathlen - 1] == '/')
-	pathname_len = pathlen + 1; /* For '\0'; already have '/'. */
-      else
-	pathname_len = pathlen + 2; /* For '/' and '\0'. */
-      cur_path_size = 0;
-      cur_path = NULL;
-
-      /* We're about to leave the directory.  If there are any
-       * -execdir argument lists which have been built but have not
-       * yet been processed, do them now because they must be done in
-       * the same directory.
-       */
-      complete_pending_execdirs ();
-
-      if (strcmp (name, "."))
-	{
-	  enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat);
-	  switch (status)
-	    {
-	    case SafeChdirOK:
-	      /* If there had been a change but wd_sanity_check()
-	       * accepted it, we need to accept that on the
-	       * way back up as well, so modify our record
-	       * of what we think we should see later.
-	       * If there was no change, the assignments are a no-op.
-	       *
-	       * However, before performing the assignment, we need to
-	       * check that we have the stat information.   If O_NOFOLLOW
-	       * is available, safely_chdir() will not have needed to use
-	       * stat(), and so stat_buf will just contain random data.
-	       */
-	      if (!did_stat)
-		{
-		  /* If there is a link we need to follow it.  Hence
-		   * the direct call to stat() not through (options.xstat)
-		   */
-		  set_stat_placeholders (&stat_buf);
-		  if (0 != stat (".", &stat_buf))
-		    break;	/* skip the assignment. */
-		}
-	      dir_ids[dir_curr].dev = stat_buf.st_dev;
-	      dir_ids[dir_curr].ino = stat_buf.st_ino;
-
-	      break;
-
-	    case SafeChdirFailWouldBeUnableToReturn:
-	      error (0, errno, ".");
-	      state.exit_status = 1;
-	      break;
-
-	    case SafeChdirFailNonexistent:
-	    case SafeChdirFailDestUnreadable:
-	    case SafeChdirFailStat:
-	    case SafeChdirFailNotDir:
-	    case SafeChdirFailChdirFailed:
-	      error (0, errno, "%s",
-		     safely_quote_err_filename (0, pathname));
-	      state.exit_status = 1;
-	      return;
-
-	    case SafeChdirFailSymlink:
-	      error (0, 0,
-		     _("warning: not following the symbolic link %s"),
-		     safely_quote_err_filename (0, pathname));
-	      state.exit_status = 1;
-	      return;
-	    }
-	}
-
-      while (1)
-	{
-	  const char *namep;
-	  mode_t mode = 0;
-	  const struct dirent *dp;
-
-	  /* We reset errno here to distinguish between end-of-directory and an error */
-	  errno = 0;
-	  dp = readdir (dirp);
-	  if (NULL == dp)
-	    {
-	      if (errno)
-		{
-		  /* an error occurred, but we are not yet at the end
-		     of the directory stream. */
-		  error (0, errno, "%s", safely_quote_err_filename (0, pathname));
-		  continue;
-		}
-	      else
-		{
-		  break;	/* End of the directory stream. */
-		}
-	    }
-	  else
-	    {
-	      namep = dp->d_name;
-	      /* Skip "", ".", and "..".  "" is returned by at least one buggy
-		 implementation: Solaris 2.4 readdir on NFS file systems.  */
-	      if (!namep[0] ||
-                  (namep[0] == '.' && (namep[1] == 0 ||
-                                       (namep[1] == '.' && namep[2] == 0))))
-		continue;
-	    }
-
-#if defined HAVE_STRUCT_DIRENT_D_TYPE
-	  if (dp->d_type != DT_UNKNOWN)
-	    mode = type_to_mode (dp->d_type);
-#endif
-
-	  /* Append this directory entry's name to the path being searched. */
-	  file_len = pathname_len + strlen (namep);
-	  if (file_len > cur_path_size)
-	    {
-	      cur_path_size = (file_len/1024 + 1) * 1024;
-	      free (cur_path);
-	      cur_path = xmalloc (cur_path_size);
-	      strcpy (cur_path, pathname);
-	      cur_path[pathname_len - 2] = '/';
-	    }
-	  cur_name = cur_path + pathname_len - 1;
-	  strcpy (cur_name, namep);
-
-	  state.curdepth++;
-	  if (!options.no_leaf_check && !subdirs_unreliable)
-	    {
-	      if (mode && S_ISDIR(mode) && (subdirs_left == 0))
-		{
-		  /* This is a subdirectory, but the number of directories we
-		   * have found now exceeds the number we would expect given
-		   * the hard link count on the parent.   This is likely to be
-		   * a bug in the file system driver (e.g. Linux's
-		   * /proc file system) or may just be a fact that the OS
-		   * doesn't really handle hard links with Unix semantics.
-		   * In the latter case, -noleaf should be used routinely.
-		   */
-		  error (0, 0, _("WARNING: Hard link count is wrong for %s (saw only st_nlink=%" PRIuMAX  " but we already saw %" PRIuMAX " subdirectories): this may be a bug in your file system driver.  Automatically turning on find's -noleaf option.  Earlier results may have failed to include directories that should have been searched."),
-			 safely_quote_err_filename(0, pathname),
-			 (uintmax_t) statp->st_nlink,
-			 (uintmax_t) dircount);
-		  state.exit_status = 1; /* We know the result is wrong, now */
-		  options.no_leaf_check = true;	/* Don't make same
-						   mistake again */
-		  subdirs_unreliable = 1;
-		  subdirs_left = 1; /* band-aid for this iteration. */
-		}
-
-	      /* Normal case optimization.  On normal Unix
-		 file systems, a directory that has no subdirectories
-		 has two links: its name, and ".".  Any additional
-		 links are to the ".." entries of its subdirectories.
-		 Once we have processed as many subdirectories as
-		 there are additional links, we know that the rest of
-		 the entries are non-directories -- in other words,
-		 leaf files. */
-	      {
-		int count;
-		count = process_path (cur_path, cur_name,
-				      subdirs_left == 0, pathname,
-				      mode, D_INO(dp));
-		subdirs_left -= count;
-		dircount += count;
-	      }
-	    }
-	  else
-	    {
-	      /* There might be weird (e.g., CD-ROM or MS-DOS) file systems
-		 mounted, which don't have Unix-like directory link counts. */
-	      process_path (cur_path, cur_name, false, pathname, mode,
-			    D_INO(dp));
-	    }
-
-	  state.curdepth--;
-	}
-
-
-      /* We're about to leave the directory.  If there are any
-       * -execdir argument lists which have been built but have not
-       * yet been processed, do them now because they must be done in
-       * the same directory.
-       */
-      complete_pending_execdirs ();
-
-      if (strcmp (name, "."))
-	{
-	  enum SafeChdirStatus status;
-
-	  /* We could go back and do the next command-line arg
-	     instead, maybe using longjmp.  */
-	  char const *dir;
-	  bool deref = following_links () ? true : false;
-
-	  if ( (state.curdepth>0) && !deref)
-	    dir = "..";
-	  else
-	    {
-	      chdir_back ();
-	      dir = parent;
-	    }
-
-	  did_stat = false;
-	  status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat);
-	  switch (status)
-	    {
-	    case SafeChdirOK:
-	      break;
-
-	    case SafeChdirFailWouldBeUnableToReturn:
-	      die (EXIT_FAILURE, errno, ".");
-	      return;
-
-	    case SafeChdirFailNonexistent:
-	    case SafeChdirFailDestUnreadable:
-	    case SafeChdirFailStat:
-	    case SafeChdirFailSymlink:
-	    case SafeChdirFailNotDir:
-	    case SafeChdirFailChdirFailed:
-	      die (EXIT_FAILURE, errno,
-		   "%s", safely_quote_err_filename (0, pathname));
-	      return;
-	    }
-	}
-
-      free (cur_path);
-      CLOSEDIR (dirp);
-    }
-
-  if (subdirs_unreliable)
-    {
-      /* Make sure we hasn't used the variable subdirs_left if we knew
-       * we shouldn't do so.
-       */
-      assert (0 == subdirs_left || options.no_leaf_check);
-    }
-}
diff --git a/find/testsuite/config/unix.exp b/find/testsuite/config/unix.exp
index d9211843..a879c896 100644
--- a/find/testsuite/config/unix.exp
+++ b/find/testsuite/config/unix.exp
@@ -20,39 +20,28 @@
 # written by Rob Savoye <[email protected]>.
 
 
-global OLDFIND
 global FTSFIND
 
 verbose "base_dir is $base_dir" 2
 global env;
 set env(GNU_FINDUTILS_FD_LEAK_CHECK) "1"
 
-# look for OLDFIND and FTSFIND
-if { ![info exists OLDFIND] || ![info exists FTSFIND] } {
-    verbose "Searching for oldfind"
+# look for FTSFIND
+if { ![info exists FTSFIND] } {
+    verbose "Searching for find"
     set dir "$base_dir/.."
 
     set objfile "ftsfind.o"
     if ![file exists "$dir/$objfile"] then {
 	error "dir is $dir, but I cannot see $objfile in that directory"
     }
-    set OLDFIND [findfile $dir/oldfind $dir/oldfind [transform oldfind]]
     set FTSFIND [findfile $dir/find    $dir/find    [transform find   ]]
 }
 
 verbose "ftsfind is at $FTSFIND" 2
-verbose "oldfind is at $OLDFIND" 2
-
-if { [ string equal $FTSFIND $OLDFIND ] } {
-    error "OLDFIND and FTSFIND are set to $FTSFIND, which can't be right"
-}
 
 if [file exists $FTSFIND] then {
-    if [file exists $OLDFIND] then {
-	verbose "FTSFIND=$FTSFIND and OLDFIND=$OLDFIND both exist." 2
-    } else {
-	error "OLDFIND=$OLDFIND, but that program does not exist"
-    }
+    verbose "FTSFIND=$FTSFIND exists." 2
 } else {
     error "FTSFIND=$FTSFIND, but that program does not exist (base_dir is $base_dir)"
 }
@@ -198,10 +187,8 @@ proc optimisation_levels_to_test {} {
 }
 
 proc find_start { passfail options {infile ""} {output ""} {setup ""}} {
-    global OLDFIND
     global FTSFIND
     global FINDFLAGS
-    global SKIP_OLD
     global SKIP_NEW
 
     if {$infile != ""} then {
@@ -214,18 +201,10 @@ proc find_start { passfail options {infile ""} {output ""} {setup ""}} {
 	error "$FTSFIND, program does not exist"
 	exit 1
     }
-    if {[which $OLDFIND] == 0} then {
-	error "$OLDFIND, program does not exist"
-	exit 1
-    }
 
     # Now run the test with each binary, once with each optimisation level.
     foreach optlevel [optimisation_levels_to_test] {
 	set flags "$FINDFLAGS -O$optlevel"
-	if { ![info exists SKIP_OLD] || ! $SKIP_OLD } {
-	    eval $setup
-	    do_find_start old-O$optlevel  $OLDFIND $flags $passfail $options $infile $output
-	}
 	if { ![info exists SKIP_NEW] || !$SKIP_NEW } {
 	    eval $setup
 	    do_find_start new-O$optlevel  $FTSFIND $flags $passfail $options $infile $output
diff --git a/find/testsuite/find.posix/dotdotfiles.exp b/find/testsuite/find.posix/dotdotfiles.exp
index 80df7c6c..9a45ea24 100644
--- a/find/testsuite/find.posix/dotdotfiles.exp
+++ b/find/testsuite/find.posix/dotdotfiles.exp
@@ -1,6 +1,6 @@
 # Test entries starting with "..", e.g. "..tmp".
 # Commit v4.5.10-95-ga29e61b introduced a regression
-# which made oldfind(1) skip such entries.
+# which made (no longer built) oldfind(1) skip such entries.
 # This is Savannah bug #45090.
 exec rm -rf tmp
 exec mkdir tmp tmp/..tmp
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 813b708c..ea323a92 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -9,7 +9,6 @@
 find/exec.c
 find/fstype.c
 find/ftsfind.c
-find/oldfind.c
 find/parser.c
 find/pred.c
 find/print.c
diff --git a/tests/find/debug-missing-arg.sh b/tests/find/debug-missing-arg.sh
index ce8dffaf..776f295d 100755
--- a/tests/find/debug-missing-arg.sh
+++ b/tests/find/debug-missing-arg.sh
@@ -19,14 +19,10 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
-# Exercise both find executables.
-for exe in find oldfind; do
-  rm -f out err || framework_failure_
-  returns_ 1 "$exe" -D >/dev/null 2> err || fail=1
-  grep -F "find: Missing argument after the -D option." err \
-    || { cat err; fail=1; }
-done
+returns_ 1 find -D >/dev/null 2> err || fail=1
+grep -F "find: Missing argument after the -D option." err \
+  || { cat err; fail=1; }
 
 Exit $fail
diff --git a/tests/find/exec-plus-last-file.sh b/tests/find/exec-plus-last-file.sh
index 596f544b..e9001b64 100755
--- a/tests/find/exec-plus-last-file.sh
+++ b/tests/find/exec-plus-last-file.sh
@@ -21,7 +21,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # Require seq(1) for this test - which may not be available
 # on some systems, e.g on some *BSDs.
@@ -57,10 +57,8 @@ mkdir "$DIR" \
   || framework_failure_
 
 
-for exe in find oldfind; do
-  "$exe" "$DIR" -type f -exec "$CMD" '{}' + > out || fail=1
-  LC_ALL=C sort out > out2 || fail=1
-  compare exp out2 || fail=1
-done
+find "$DIR" -type f -exec "$CMD" '{}' + > out || fail=1
+LC_ALL=C sort out > out2 || fail=1
+compare exp out2 || fail=1
 
 Exit $fail
diff --git a/tests/find/execdir-fd-leak.sh b/tests/find/execdir-fd-leak.sh
index 4fbf0a5c..2b680f5e 100755
--- a/tests/find/execdir-fd-leak.sh
+++ b/tests/find/execdir-fd-leak.sh
@@ -19,7 +19,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # seq is not required by POSIX, so we have manual lists of number here instead.
 three_to_thirty_five="3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35"
@@ -55,12 +55,10 @@ make_test_data() {
 make_test_data \
   || framework_failure_ "failed to set up the test"
 
-for exe in find oldfind; do
-  ( ulimit -n 30 && \
-    ${exe} . -type f -execdir cat '{}' \; >/dev/null; \
-  ) \
-  || { echo "Option -execdir of ${exe} leaks file descriptors" >&2 ; \
+( ulimit -n 30 && \
+  find . -type f -execdir cat '{}' \; >/dev/null; \
+) \
+  || { echo "Option -execdir leaks file descriptors" >&2 ; \
        fail=1 ; }
-done
 
 Exit $fail
diff --git a/tests/find/many-dir-entries-vs-OOM.sh b/tests/find/many-dir-entries-vs-OOM.sh
index 8ab343f7..d2edb253 100755
--- a/tests/find/many-dir-entries-vs-OOM.sh
+++ b/tests/find/many-dir-entries-vs-OOM.sh
@@ -19,7 +19,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # Mark as expensive.
 expensive_
@@ -77,18 +77,12 @@ cd ..
 # Create a small directory as reference to determine lower ulimit.
 mkdir dir2 && touch dir2/a dir2/b dir2/c || framework_failure_
 
-# We don't check oldfind, as it uses savedir, meaning that
-# it stores all the directory entries.  Hence the excessive
-# memory consumption bug applies to oldfind even though it is
-# not using fts.
-for exe in find oldfind; do
-  # Determine memory consumption for the trivial case.
-  vm="$(get_min_ulimit_v_ ${exe} dir2 -fprint dummy)" \
-    || skip_ "this shell lacks ulimit support"
-
-  # Allow 35MiB more memory than above.
-  ( ulimit -v $(($vm + 35000)) && ${exe} dir >/dev/null ) \
-    || { echo "${exe}: memory consumption is too high" >&2; fail=1; }
-done
+# Determine memory consumption for the trivial case.
+vm="$(get_min_ulimit_v_ find dir2 -fprint dummy)" \
+  || skip_ "this shell lacks ulimit support"
+
+# Allow 35MiB more memory than above.
+( ulimit -v $(($vm + 35000)) && find dir >/dev/null ) \
+  || { echo "memory consumption is too high" >&2; fail=1; }
 
 Exit $fail
diff --git a/tests/find/name-lbracket-literal.sh b/tests/find/name-lbracket-literal.sh
index 9f5583d2..b3a6be58 100755
--- a/tests/find/name-lbracket-literal.sh
+++ b/tests/find/name-lbracket-literal.sh
@@ -18,7 +18,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # Prepare a file named '['.
 touch '[' || framework_failure_
@@ -27,7 +27,4 @@ echo './[' > exp || framework_failure_
 find -name '[' -print > out || fail=1
 compare exp out || fail=1
 
-oldfind -name '[' -print > out2 || fail=1
-compare exp out2 || fail=1
-
 Exit $fail
diff --git a/tests/find/printf_escape_c.sh b/tests/find/printf_escape_c.sh
index 6a938a2d..530ac7b8 100755
--- a/tests/find/printf_escape_c.sh
+++ b/tests/find/printf_escape_c.sh
@@ -17,19 +17,16 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 echo 'hello^.^world' > exp || framework_failure_
 
-for executable in oldfind find; do
-  rm -f out || framework_failure_
-  $executable . -maxdepth 0 \
-    -printf 'hello^\cthere' \
-    -exec printf %s {} \; \
-    -printf '^world\n' \
-    > out || fail=1
+find . -maxdepth 0 \
+  -printf 'hello^\cthere' \
+  -exec printf %s {} \; \
+  -printf '^world\n' \
+  > out || fail=1
 
-  compare exp out || fail=1
-done
+compare exp out || fail=1
 
 Exit $fail
diff --git a/tests/find/printf_escapechars.sh b/tests/find/printf_escapechars.sh
index 8d6dcebe..55900e59 100755
--- a/tests/find/printf_escapechars.sh
+++ b/tests/find/printf_escapechars.sh
@@ -17,7 +17,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # Check for working od(1).
 echo test | od -c >/dev/null \
@@ -54,33 +54,29 @@ _EOD_
 # Prepare expected stderr.
 echo "warning: unrecognized escape" > experr || framework_failure_
 
-for executable in oldfind find; do
-  rm -f out* err* || framework_failure_
+find . -maxdepth 0 \
+  -printf 'OCTAL1: \1\n' \
+  -printf 'OCTAL2: \02\n' \
+  -printf 'OCTAL3: \003\n' \
+  -printf 'OCTAL4: \0044\n' \
+  -printf 'OCTAL8: \0028\n' \
+  -printf 'BEL: \a\n' \
+  -printf 'CR: \r\n' \
+  -printf 'FF: \f\n' \
+  -printf 'TAB: \t\n' \
+  -printf 'VTAB: \v\n' \
+  -printf 'BS: \b\n' \
+  -printf 'BACKSLASH: \\\n' \
+  -printf 'UNKNOWN: \z\n' \
+  > out 2> err || fail=1
 
-  $executable . -maxdepth 0 \
-    -printf 'OCTAL1: \1\n' \
-    -printf 'OCTAL2: \02\n' \
-    -printf 'OCTAL3: \003\n' \
-    -printf 'OCTAL4: \0044\n' \
-    -printf 'OCTAL8: \0028\n' \
-    -printf 'BEL: \a\n' \
-    -printf 'CR: \r\n' \
-    -printf 'FF: \f\n' \
-    -printf 'TAB: \t\n' \
-    -printf 'VTAB: \v\n' \
-    -printf 'BS: \b\n' \
-    -printf 'BACKSLASH: \\\n' \
-    -printf 'UNKNOWN: \z\n' \
-    > out 2> err || fail=1
+# Some 'od' implementations (e.g. on the *BSDs) produce different indentation
+# and trailing spaces, therefore squeeze the former and remove the latter.
+od -t o1 < out | sed 's/  */ /g; s/ *$//;' > out2 || framework_failure_
+compare expout out2 || fail=1
 
-  # Some 'od' implementations (e.g. on the *BSDs) produce different indentation
-  # and trailing spaces, therefore squeeze the former and remove the latter.
-  od -t o1 < out | sed 's/  */ /g; s/ *$//;' > out2 || framework_failure_
-  compare expout out2 || fail=1
-
-  sed 's/^.*\(warning: unrecognized escape\) .*$/\1/' err > err2 \
-    || framework_failure_
-  compare experr err2 || fail=1
-done
+sed 's/^.*\(warning: unrecognized escape\) .*$/\1/' err > err2 \
+  || framework_failure_
+compare experr err2 || fail=1
 
 Exit $fail
diff --git a/tests/find/printf_inode.sh b/tests/find/printf_inode.sh
index 53925d2b..40e918ff 100755
--- a/tests/find/printf_inode.sh
+++ b/tests/find/printf_inode.sh
@@ -17,7 +17,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 make_canonical() {
   sed -e '
@@ -33,11 +33,9 @@ make_canonical() {
 # Let ls(1) create the expected output.
 ls -i file | make_canonical > exp || framework_failure_
 
-for executable in oldfind find; do
-  rm -f out out2
-  $executable file -printf '%i_%p\n' > out || fail=1
-  make_canonical < out > out2 || framework_failure_
-  compare exp out2 || fail=1
-done
+rm -f out out2
+find file -printf '%i_%p\n' > out || fail=1
+make_canonical < out > out2 || framework_failure_
+compare exp out2 || fail=1
 
 Exit $fail
diff --git a/tests/find/refuse-noop.sh b/tests/find/refuse-noop.sh
index d896ee26..a2a1a53f 100755
--- a/tests/find/refuse-noop.sh
+++ b/tests/find/refuse-noop.sh
@@ -19,18 +19,16 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # Exercise both the previous name of the pseudo-option '-noop',
-# and the now renamed '---noop' option for both find executables.
-for exe in find oldfind; do
-  for opt in 'noop' '--noop'; do
-    rm -f out err || framework_failure_
-    returns_ 1 "$exe" "-${opt}" > out 2> err || fail=1
-    compare /dev/null out || fail=1
-    grep "find: unknown predicate .-${opt}." err \
-      || { cat err; fail=1; }
-  done
+# and the now renamed '---noop' option.
+for opt in 'noop' '--noop'; do
+  rm -f out err || framework_failure_
+  returns_ 1 find "-${opt}" > out 2> err || fail=1
+  compare /dev/null out || fail=1
+  grep "find: unknown predicate .-${opt}." err \
+    || { cat err; fail=1; }
 done
 
 Exit $fail
diff --git a/tests/find/type_list.sh b/tests/find/type_list.sh
index 06c5c8b4..0165806d 100755
--- a/tests/find/type_list.sh
+++ b/tests/find/type_list.sh
@@ -18,7 +18,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; fu_path_prepend_
-print_ver_ find oldfind
+print_ver_ find
 
 # This test is in 'all_root_tests' to get better coverage for file types a
 # regular user cannot create.  Still, it is run during 'make check' as well.
@@ -116,175 +116,171 @@ make_test_data dir \
 find dir -mindepth 1 -ls
 
 fail=0
-for exe in find oldfind; do
-
-  # Negative tests first.  Expect the output to be empty.
-  > exp
-
-  # Ensure empty type arguments are rejected.
-  returns_ 1 "${exe}" dir -mindepth 1 -type '' > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Arguments to -type should contain at least one letter' err \
-    || { cat err; fail=1; }
-
-  returns_ 1 "${exe}" dir -mindepth 1 -xtype '' > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Arguments to -xtype should contain at least one letter' err \
-    || { cat err; fail=1; }
-
-  # Ensure non-separated type arguments are rejected.
-  returns_ 1 "${exe}" dir -mindepth 1 -type fd > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Must separate multiple arguments to -type' err \
-    || { cat err; fail=1; }
-
-  returns_ 1 "${exe}" dir -mindepth 1 -xtype fd > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Must separate multiple arguments to -xtype' err \
-    || { cat err; fail=1; }
-
-  # Ensure unterminated type list arguments are rejected.
-  returns_ 1 "${exe}" dir -mindepth 1 -type f, > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Last file type in list argument to -type is missing' err \
-    ||  { cat err; fail=1; }
-
-  returns_ 1 "${exe}" dir -mindepth 1 -xtype f, > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Last file type in list argument to -xtype is missing' err \
-    ||  { cat err; fail=1; }
-
-  # Ensure duplicate entries in the type list arguments are rejected.
-  returns_ 1 "${exe}" dir -mindepth 1 -type f,f > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Duplicate file type .* in the argument list to -type' err \
-    ||  { cat err; fail=1; }
-
-  returns_ 1 "${exe}" dir -mindepth 1 -xtype f,f > out 2> err || fail=1
-  compare exp out || fail=1
-  grep 'Duplicate file type .* in the argument list to -xtype' err \
-    ||  { cat err; fail=1; }
-
-  # Continue with positive tests.
-  # Files only
-  grep -e '/reg$' all > exp
-  "${exe}" dir -type f > out || fail=1
+
+# Negative tests first.  Expect the output to be empty.
+> exp
+
+# Ensure empty type arguments are rejected.
+returns_ 1 find dir -mindepth 1 -type '' > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Arguments to -type should contain at least one letter' err \
+  || { cat err; fail=1; }
+
+returns_ 1 find dir -mindepth 1 -xtype '' > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Arguments to -xtype should contain at least one letter' err \
+  || { cat err; fail=1; }
+
+# Ensure non-separated type arguments are rejected.
+returns_ 1 find dir -mindepth 1 -type fd > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Must separate multiple arguments to -type' err \
+  || { cat err; fail=1; }
+
+returns_ 1 find dir -mindepth 1 -xtype fd > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Must separate multiple arguments to -xtype' err \
+  || { cat err; fail=1; }
+
+# Ensure unterminated type list arguments are rejected.
+returns_ 1 find dir -mindepth 1 -type f, > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Last file type in list argument to -type is missing' err \
+  ||  { cat err; fail=1; }
+
+returns_ 1 find dir -mindepth 1 -xtype f, > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Last file type in list argument to -xtype is missing' err \
+  ||  { cat err; fail=1; }
+
+# Ensure duplicate entries in the type list arguments are rejected.
+returns_ 1 find dir -mindepth 1 -type f,f > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Duplicate file type .* in the argument list to -type' err \
+  ||  { cat err; fail=1; }
+
+returns_ 1 find dir -mindepth 1 -xtype f,f > out 2> err || fail=1
+compare exp out || fail=1
+grep 'Duplicate file type .* in the argument list to -xtype' err \
+  ||  { cat err; fail=1; }
+
+# Continue with positive tests.
+# Files only
+grep -e '/reg$' all > exp
+find dir -type f > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+# Symbolic links only.
+if [ $HAVE_LINK = 1 ]; then
+  grep -e 'link$' all > exp
+  find dir -type l > out || fail=1
   sort -o out out
   compare exp out || fail=1;
 
-  # Symbolic links only.
-  if [ $HAVE_LINK = 1 ]; then
-
-    grep -e 'link$' all > exp
-    "${exe}" dir -type l > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-
-    grep -e 'dangling-link$' all > exp
-    "${exe}" dir -xtype l > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-  fi
-
-  # Files and directories.
-  grep -e '/reg$' -e '/dir$' all > exp
-  "${exe}" dir -mindepth 1 -type f,d > out || fail=1
+  grep -e 'dangling-link$' all > exp
+  find dir -xtype l > out || fail=1
   sort -o out out
   compare exp out || fail=1;
-
-  grep -e '/reg' -e '/dir' all > exp
-  "${exe}" dir -mindepth 1 -xtype f,d > out || fail=1
+fi
+
+# Files and directories.
+grep -e '/reg$' -e '/dir$' all > exp
+find dir -mindepth 1 -type f,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+grep -e '/reg' -e '/dir' all > exp
+find dir -mindepth 1 -xtype f,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+# Block devices.
+grep -e '/reg$' -e '/dir$' -e '/blk$' all > exp
+find dir -mindepth 1 -type b,f,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+grep -e '/reg' -e '/dir' -e '/blk' all > exp
+find dir -mindepth 1 -xtype b,f,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+# Character devices.
+grep -e '/reg$' -e '/dir$' -e '/chr$' all > exp
+find dir -mindepth 1 -type f,c,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+grep -e '/reg' -e '/dir' -e '/chr' all > exp
+find dir -mindepth 1 -xtype f,c,d > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+# FIFOs.
+if [ $HAVE_FIFO = 1 ]; then
+  grep -e '/reg$' -e '/dir$' -e '/fifo$' all > exp
+  find dir -mindepth 1 -type f,d,p > out || fail=1
   sort -o out out
   compare exp out || fail=1;
 
-  # Block devices.
-  grep -e '/reg$' -e '/dir$' -e '/blk$' all > exp
-  "${exe}" dir -mindepth 1 -type b,f,d > out || fail=1
+  grep -e '/reg' -e '/dir' -e '/fifo' all > exp
+  find dir -mindepth 1 -xtype f,d,p > out || fail=1
   sort -o out out
   compare exp out || fail=1;
+fi
 
-  grep -e '/reg' -e '/dir' -e '/blk' all > exp
-  "${exe}" dir -mindepth 1 -xtype b,f,d > out || fail=1
+# Sockets.
+if [ $HAVE_SOCK = 1 ]; then
+  grep -e '/reg$' -e '/dir$' -e '/sock$' all > exp
+  find dir -mindepth 1 -type f,d,s > out || fail=1
   sort -o out out
   compare exp out || fail=1;
 
-  # Character devices.
-  grep -e '/reg$' -e '/dir$' -e '/chr$' all > exp
-  "${exe}" dir -mindepth 1 -type f,c,d > out || fail=1
+  grep -e '/reg' -e '/dir' -e '/sock' all > exp
+  find dir -mindepth 1 -xtype f,d,s > out || fail=1
   sort -o out out
   compare exp out || fail=1;
+fi
 
-  grep -e '/reg' -e '/dir' -e '/chr' all > exp
-  "${exe}" dir -mindepth 1 -xtype f,c,d > out || fail=1
+# Symbolic links.
+if [ $HAVE_LINK = 1 ]; then
+  grep -e '/reg$' -e 'link$' all > exp
+  find dir -mindepth 1 -type f,l > out || fail=1
   sort -o out out
   compare exp out || fail=1;
 
-  # FIFOs.
-  if [ $HAVE_FIFO = 1 ]; then
-    grep -e '/reg$' -e '/dir$' -e '/fifo$' all > exp
-    "${exe}" dir -mindepth 1 -type f,d,p > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-
-    grep -e '/reg' -e '/dir' -e '/fifo' all > exp
-    "${exe}" dir -mindepth 1 -xtype f,d,p > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-  fi
-
-  # Sockets.
-  if [ $HAVE_SOCK = 1 ]; then
-    grep -e '/reg$' -e '/dir$' -e '/sock$' all > exp
-    "${exe}" dir -mindepth 1 -type f,d,s > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-
-    grep -e '/reg' -e '/dir' -e '/sock' all > exp
-    "${exe}" dir -mindepth 1 -xtype f,d,s > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-  fi
-
-  # Symbolic links.
-  if [ $HAVE_LINK = 1 ]; then
-
-    grep -e '/reg$' -e 'link$' all > exp
-    "${exe}" dir -mindepth 1 -type f,l > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-
-    grep -e '/reg' -e 'dangling-link$' all > exp
-    "${exe}" dir -mindepth 1 -xtype f,l > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-  fi
-
-  # -xtype: all but the dangling symlink.
-  t='f,d,b,c'
-  [ $HAVE_FIFO = 1 ] && t="$t,p"
-  [ $HAVE_SOCK = 1 ] && t="$t,s"
-  [ $HAVE_DOOR = 1 ] && t="$t,D"
-  grep -v 'dangling-link$' all > exp
-  "${exe}" dir -mindepth 1 -xtype "$t" > out || fail=1
+  grep -e '/reg' -e 'dangling-link$' all > exp
+  find dir -mindepth 1 -xtype f,l > out || fail=1
   sort -o out out
   compare exp out || fail=1;
-
-  # negation
-  if [ $HAVE_LINK = 1 ]; then
-    "${exe}" dir -mindepth 1 -not -xtype l > out || fail=1
-    sort -o out out
-    compare exp out || fail=1;
-  fi
-
-  # Finally: full list
-  [ $HAVE_LINK = 1 ] && t="$t,l"
-  "${exe}" dir -mindepth 1 -type "$t" > out || fail=1
+fi
+
+# -xtype: all but the dangling symlink.
+t='f,d,b,c'
+[ $HAVE_FIFO = 1 ] && t="$t,p"
+[ $HAVE_SOCK = 1 ] && t="$t,s"
+[ $HAVE_DOOR = 1 ] && t="$t,D"
+grep -v 'dangling-link$' all > exp
+find dir -mindepth 1 -xtype "$t" > out || fail=1
+sort -o out out
+compare exp out || fail=1;
+
+# negation
+if [ $HAVE_LINK = 1 ]; then
+  find dir -mindepth 1 -not -xtype l > out || fail=1
   sort -o out out
-  compare all out || fail=1;
+  compare exp out || fail=1;
+fi
 
-  "${exe}" dir -mindepth 1 -xtype "$t" > out || fail=1
-  sort -o out out
-  compare all out || fail=1;
-done
+# Finally: full list
+[ $HAVE_LINK = 1 ] && t="$t,l"
+find dir -mindepth 1 -type "$t" > out || fail=1
+sort -o out out
+compare all out || fail=1;
+
+find dir -mindepth 1 -xtype "$t" > out || fail=1
+sort -o out out
+compare all out || fail=1;
 
 Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index 5b5a0e64..17efc591 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -15,7 +15,7 @@
 ## You should have received a copy of the GNU General Public License
 ## along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-built_programs = find oldfind xargs frcode locate updatedb
+built_programs = find xargs frcode locate updatedb
 
 # Indirections required so that we'll still be able to know the
 # complete list of our tests even if the user overrides TESTS
-- 
2.33.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.