(root)/
findutils-4.9.0/
find/
ftsfind.c
       1  /* find -- search for files in a directory hierarchy (fts version)
       2     Copyright (C) 1990-2022 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16  */
      17  
      18  /* This file was written by James Youngman, based on oldfind.c.
      19  
      20     GNU find was written by Eric Decker <cire@soe.ucsc.edu>,
      21     with enhancements by David MacKenzie <djm@gnu.org>,
      22     Jay Plett <jay@silence.princeton.nj.us>,
      23     and Tim Wood <axolotl!tim@toad.com>.
      24     The idea for -print0 and xargs -0 came from
      25     Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
      26  */
      27  
      28  /* config.h must always be included first. */
      29  #include <config.h>
      30  
      31  
      32  /* system headers. */
      33  #include <assert.h>
      34  #include <errno.h>
      35  #include <fcntl.h>
      36  #include <inttypes.h>
      37  #include <sys/stat.h>
      38  #include <unistd.h>
      39  
      40  /* gnulib headers. */
      41  #include "argv-iter.h"
      42  #include "cloexec.h"
      43  #include "closeout.h"
      44  #include "error.h"
      45  #include "fts_.h"
      46  #include "intprops.h"
      47  #include "progname.h"
      48  #include "quotearg.h"
      49  #include "same-inode.h"
      50  #include "save-cwd.h"
      51  #include "xgetcwd.h"
      52  #include "xalloc.h"
      53  
      54  /* find headers. */
      55  #include "defs.h"
      56  #include "die.h"
      57  #include "dircallback.h"
      58  #include "fdleak.h"
      59  #include "unused-result.h"
      60  #include "system.h"
      61  
      62  
      63  #undef  STAT_MOUNTPOINTS
      64  
      65  
      66  /* FTS_TIGHT_CYCLE_CHECK tries to work around Savannah bug #17877
      67   * (but actually using it doesn't fix the bug).
      68   */
      69  static int ftsoptions = FTS_NOSTAT|FTS_TIGHT_CYCLE_CHECK|FTS_CWDFD|FTS_VERBATIM;
      70  
      71  static int prev_depth = INT_MIN; /* fts_level can be < 0 */
      72  static int curr_fd = -1;
      73  
      74  
      75  static bool find (char *arg) __attribute_warn_unused_result__;
      76  static bool process_all_startpoints (int argc, char *argv[]) __attribute_warn_unused_result__;
      77  
      78  
      79  
      80  static void
      81  left_dir (void)
      82  {
      83    if (ftsoptions & FTS_CWDFD)
      84      {
      85        if (curr_fd >= 0)
      86  	{
      87  	  close (curr_fd);
      88  	  curr_fd = -1;
      89  	}
      90      }
      91    else
      92      {
      93        /* do nothing. */
      94      }
      95  }
      96  
      97  /*
      98   * Signal that we are now inside a directory pointed to by dir_fd.
      99   * The caller can't tell if this is the first time this happens, so
     100   * we have to be careful not to call dup() more than once
     101   */
     102  static void
     103  inside_dir (int dir_fd)
     104  {
     105    if (ftsoptions & FTS_CWDFD)
     106      {
     107        assert (dir_fd == AT_FDCWD || dir_fd >= 0);
     108  
     109        state.cwd_dir_fd = dir_fd;
     110        if (curr_fd < 0)
     111  	{
     112  	  if (AT_FDCWD == dir_fd)
     113  	    {
     114  	      curr_fd = AT_FDCWD;
     115  	    }
     116  	  else if (dir_fd >= 0)
     117  	    {
     118  	      curr_fd = dup_cloexec (dir_fd);
     119  	    }
     120  	  else
     121  	    {
     122  	      /* curr_fd is invalid, but dir_fd is also invalid.
     123  	       * This should not have happened.
     124  	       */
     125  	      assert (curr_fd >= 0 || dir_fd >= 0);
     126  	    }
     127  	}
     128      }
     129    else
     130      {
     131        /* FTS_CWDFD is not in use.  We can always assume that
     132         * AT_FDCWD refers to the directory we are currently searching.
     133         *
     134         * Therefore there is nothing to do.
     135         */
     136      }
     137  }
     138  
     139  
     140  
     141  #ifdef STAT_MOUNTPOINTS
     142  static void init_mounted_dev_list (void);
     143  #endif
     144  
     145  #define HANDLECASE(N) case N: return #N;
     146  
     147  static const char *
     148  get_fts_info_name (int info)
     149  {
     150    static char buf[1 + INT_BUFSIZE_BOUND (info) + 1];
     151    switch (info)
     152      {
     153        HANDLECASE(FTS_D);
     154        HANDLECASE(FTS_DC);
     155        HANDLECASE(FTS_DEFAULT);
     156        HANDLECASE(FTS_DNR);
     157        HANDLECASE(FTS_DOT);
     158        HANDLECASE(FTS_DP);
     159        HANDLECASE(FTS_ERR);
     160        HANDLECASE(FTS_F);
     161        HANDLECASE(FTS_INIT);
     162        HANDLECASE(FTS_NS);
     163        HANDLECASE(FTS_NSOK);
     164        HANDLECASE(FTS_SL);
     165        HANDLECASE(FTS_SLNONE);
     166        HANDLECASE(FTS_W);
     167      default:
     168        sprintf (buf, "[%d]", info);
     169        return buf;
     170      }
     171  }
     172  
     173  static void
     174  visit (FTS *p, FTSENT *ent, struct stat *pstat)
     175  {
     176    struct predicate *eval_tree;
     177  
     178    state.have_stat = (ent->fts_info != FTS_NS) && (ent->fts_info != FTS_NSOK);
     179    state.rel_pathname = ent->fts_accpath;
     180    state.cwd_dir_fd   = p->fts_cwd_fd;
     181  
     182    /* Apply the predicates to this path. */
     183    eval_tree = get_eval_tree ();
     184    apply_predicate (ent->fts_path, pstat, eval_tree);
     185  
     186    /* Deal with any side effects of applying the predicates. */
     187    if (state.stop_at_current_level)
     188      {
     189        fts_set (p, ent, FTS_SKIP);
     190      }
     191  }
     192  
     193  static const char*
     194  partial_quotearg_n (int n, char *s, size_t len, enum quoting_style style)
     195  {
     196    if (0 == len)
     197      {
     198        return quotearg_n_style (n, style, "");
     199      }
     200    else
     201      {
     202        char saved;
     203        const char *result;
     204  
     205        saved = s[len];
     206        s[len] = 0;
     207        result = quotearg_n_style (n, style, s);
     208        s[len] = saved;
     209        return result;
     210      }
     211  }
     212  
     213  
     214  /* We've detected a file system loop.   This is caused by one of
     215   * two things:
     216   *
     217   * 1. Option -L is in effect and we've hit a symbolic link that
     218   *    points to an ancestor.  This is harmless.  We won't traverse the
     219   *    symbolic link.
     220   *
     221   * 2. We have hit a real cycle in the directory hierarchy.  In this
     222   *    case, we issue a diagnostic message (POSIX requires this) and we
     223   *    skip that directory entry.
     224   */
     225  static void
     226  issue_loop_warning (FTSENT * ent)
     227  {
     228    if (S_ISLNK(ent->fts_statp->st_mode))
     229      {
     230        error (0, 0,
     231  	     _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."),
     232  	     safely_quote_err_filename (0, ent->fts_path));
     233      }
     234    else
     235      {
     236        /* We have found an infinite loop.  POSIX requires us to
     237         * issue a diagnostic.  Usually we won't get to here
     238         * because when the leaf optimisation is on, it will cause
     239         * the subdirectory to be skipped.  If /a/b/c/d is a hard
     240         * link to /a/b, then the link count of /a/b/c is 2,
     241         * because the ".." entry of /a/b/c/d points to /a, not
     242         * to /a/b/c.
     243         */
     244        error (0, 0,
     245  	     _("File system loop detected; "
     246  	       "%s is part of the same file system loop as %s."),
     247  	     safely_quote_err_filename (0, ent->fts_path),
     248  	     partial_quotearg_n (1,
     249  				 ent->fts_cycle->fts_path,
     250  				 ent->fts_cycle->fts_pathlen,
     251  				 options.err_quoting_style));
     252      }
     253  }
     254  
     255  /*
     256   * Return true if NAME corresponds to a file which forms part of a
     257   * symbolic link loop.  The command
     258   *      rm -f a b; ln -s a b; ln -s b a
     259   * produces such a loop.
     260   */
     261  static bool
     262  symlink_loop (const char *name)
     263  {
     264    struct stat stbuf;
     265    const int rv = options.xstat (name, &stbuf);
     266    return (0 != rv) && (ELOOP == errno);
     267  }
     268  
     269  
     270  static void
     271  consider_visiting (FTS *p, FTSENT *ent)
     272  {
     273    struct stat statbuf;
     274    mode_t mode;
     275    int ignore, isdir;
     276  
     277    if (options.debug_options & DebugSearch)
     278      fprintf (stderr,
     279  	     "consider_visiting (early): %s: "
     280  	     "fts_info=%-6s, fts_level=%2d, prev_depth=%d "
     281  	     "fts_path=%s, fts_accpath=%s\n",
     282  	     quotearg_n_style (0, options.err_quoting_style, ent->fts_path),
     283  	     get_fts_info_name (ent->fts_info),
     284  	     (int)ent->fts_level, prev_depth,
     285  	     quotearg_n_style (1, options.err_quoting_style, ent->fts_path),
     286  	     quotearg_n_style (2, options.err_quoting_style, ent->fts_accpath));
     287  
     288    if (ent->fts_info == FTS_DP)
     289      {
     290        left_dir ();
     291      }
     292    else if (ent->fts_level > prev_depth || ent->fts_level==0)
     293      {
     294        left_dir ();
     295      }
     296    inside_dir (p->fts_cwd_fd);
     297    prev_depth = ent->fts_level;
     298  
     299    statbuf.st_ino = ent->fts_statp->st_ino;
     300  
     301    /* Cope with various error conditions. */
     302    if (ent->fts_info == FTS_ERR)
     303      {
     304        nonfatal_target_file_error (ent->fts_errno, ent->fts_path);
     305        return;
     306      }
     307    if (ent->fts_info == FTS_DNR)
     308      {
     309        nonfatal_target_file_error (ent->fts_errno, ent->fts_path);
     310        if (options.do_dir_first)
     311  	{
     312  	  /* Return for unreadable directories without -depth.
     313  	   * With -depth, the directory itself has to be processed, yet the
     314  	   * error message above has to be output.
     315  	   */
     316  	  return;
     317  	}
     318      }
     319    else if (ent->fts_info == FTS_DC)
     320      {
     321        issue_loop_warning (ent);
     322        state.exit_status = EXIT_FAILURE;
     323        return;
     324      }
     325    else if (ent->fts_info == FTS_SLNONE)
     326      {
     327        /* fts_read() claims that ent->fts_accpath is a broken symbolic
     328         * link.  That would be fine, but if this is part of a symbolic
     329         * link loop, we diagnose the problem and also ensure that the
     330         * eventual return value is nonzero.   Note that while the path
     331         * we stat is local (fts_accpath), we print the full path name
     332         * of the file (fts_path) in the error message.
     333         */
     334        if (symlink_loop (ent->fts_accpath))
     335  	{
     336  	  nonfatal_target_file_error (ELOOP, ent->fts_path);
     337  	  return;
     338  	}
     339      }
     340    else if (ent->fts_info == FTS_NS)
     341      {
     342        if (ent->fts_level == 0)
     343  	{
     344  	  /* e.g., nonexistent starting point */
     345  	  nonfatal_target_file_error (ent->fts_errno, ent->fts_path);
     346  	  return;
     347  	}
     348        else
     349  	{
     350  	  /* The following if statement fixes Savannah bug #19605
     351  	   * (failure to diagnose a symbolic link loop)
     352  	   */
     353  	  if (symlink_loop (ent->fts_accpath))
     354  	    {
     355  	      nonfatal_target_file_error (ELOOP, ent->fts_path);
     356  	      return;
     357  	    }
     358  	  else
     359  	    {
     360  	      nonfatal_target_file_error (ent->fts_errno, ent->fts_path);
     361  	      /* Continue despite the error, as file name without stat info
     362  	       * might be better than not even processing the file name. This
     363  	       * can lead to repeated error messages later on, though, if a
     364  	       * predicate requires stat information.
     365  	       *
     366  	       * Not printing an error message here would be even more wrong,
     367  	       * though, as this could cause the contents of a directory to be
     368  	       * silently ignored, as the directory wouldn't be identified as
     369  	       * such.
     370  	       */
     371  	    }
     372  
     373  	}
     374      }
     375  
     376    /* Cope with the usual cases. */
     377    if (ent->fts_info == FTS_NSOK
     378        || ent->fts_info == FTS_NS /* e.g. symlink loop */)
     379      {
     380        assert (!state.have_stat);
     381        assert (ent->fts_info == FTS_NSOK || state.type == 0);
     382        mode = state.type;
     383      }
     384    else
     385      {
     386        state.have_stat = true;
     387        state.have_type = true;
     388        statbuf = *(ent->fts_statp);
     389        state.type = mode = statbuf.st_mode;
     390  
     391        if (00000 == mode)
     392  	{
     393  	  /* Savannah bug #16378. */
     394  	  error (0, 0, _("WARNING: file %s appears to have mode 0000"),
     395  		 quotearg_n_style (0, options.err_quoting_style, ent->fts_path));
     396  	}
     397      }
     398  
     399    /* update state.curdepth before calling digest_mode(), because digest_mode
     400     * may call following_links().
     401     */
     402    state.curdepth = ent->fts_level;
     403    if (mode)
     404      {
     405        if (!digest_mode (&mode, ent->fts_path, ent->fts_name, &statbuf, 0))
     406  	return;
     407      }
     408  
     409    /* examine this item. */
     410    ignore = 0;
     411    isdir = S_ISDIR(mode)
     412      || (FTS_D  == ent->fts_info)
     413      || (FTS_DP == ent->fts_info)
     414      || (FTS_DC == ent->fts_info);
     415  
     416    if (isdir && (ent->fts_info == FTS_NSOK))
     417      {
     418        /* This is a directory, but fts did not stat it, so
     419         * presumably would not be planning to search its
     420         * children.  Force a stat of the file so that the
     421         * children can be checked.
     422         */
     423        fts_set (p, ent, FTS_AGAIN);
     424        return;
     425      }
     426  
     427    if (options.maxdepth >= 0)
     428      {
     429        if (ent->fts_level >= options.maxdepth)
     430  	{
     431  	  fts_set (p, ent, FTS_SKIP); /* descend no further */
     432  
     433  	  if (ent->fts_level > options.maxdepth)
     434  	    ignore = 1;		/* don't even look at this one */
     435  	}
     436      }
     437  
     438    if ( (ent->fts_info == FTS_D) && !options.do_dir_first )
     439      {
     440        /* this is the preorder visit, but user said -depth */
     441        ignore = 1;
     442      }
     443    else if ( (ent->fts_info == FTS_DP) && options.do_dir_first )
     444      {
     445        /* this is the postorder visit, but user didn't say -depth */
     446        ignore = 1;
     447      }
     448    else if (ent->fts_level < options.mindepth)
     449      {
     450        ignore = 1;
     451      }
     452  
     453    if (options.debug_options & DebugSearch)
     454      fprintf (stderr,
     455  	     "consider_visiting (late): %s: "
     456  	     "fts_info=%-6s, isdir=%d ignore=%d have_stat=%d have_type=%d \n",
     457  	     quotearg_n_style (0, options.err_quoting_style, ent->fts_path),
     458  	     get_fts_info_name (ent->fts_info),
     459  	     isdir, ignore, state.have_stat, state.have_type);
     460  
     461    if (!ignore)
     462      {
     463        visit (p, ent, &statbuf);
     464      }
     465  
     466    if (ent->fts_info == FTS_DP)
     467      {
     468        /* we're leaving a directory. */
     469        state.stop_at_current_level = false;
     470      }
     471  }
     472  
     473  
     474  
     475  static bool
     476  find (char *arg)
     477  {
     478    char * arglist[2];
     479    FTS *p;
     480    FTSENT *ent;
     481  
     482    state.starting_path_length = strlen (arg);
     483    inside_dir (AT_FDCWD);
     484  
     485    arglist[0] = arg;
     486    arglist[1] = NULL;
     487  
     488    switch (options.symlink_handling)
     489      {
     490      case SYMLINK_ALWAYS_DEREF:
     491        ftsoptions |= FTS_COMFOLLOW|FTS_LOGICAL;
     492        break;
     493  
     494      case SYMLINK_DEREF_ARGSONLY:
     495        ftsoptions |= FTS_COMFOLLOW|FTS_PHYSICAL;
     496        break;
     497  
     498      case SYMLINK_NEVER_DEREF:
     499        ftsoptions |= FTS_PHYSICAL;
     500        break;
     501      }
     502  
     503    if (options.stay_on_filesystem)
     504      ftsoptions |= FTS_XDEV;
     505  
     506    p = fts_open (arglist, ftsoptions, NULL);
     507    if (NULL == p)
     508      {
     509        error (0, errno, _("cannot search %s"),
     510  	     safely_quote_err_filename (0, arg));
     511        state.exit_status = EXIT_FAILURE;
     512      }
     513    else
     514      {
     515        int level = INT_MIN;
     516  
     517        while ( (errno=0, ent=fts_read (p)) != NULL )
     518  	{
     519  	  if (state.execdirs_outstanding && ((int)ent->fts_level != level))
     520  	    {
     521  	      /* If we changed level, perform any outstanding
     522  	       * execdirs.  If we see a sequence of directory entries
     523  	       * like this: fffdfffdfff, we could build a command line
     524  	       * of 9 files, but this simple-minded implementation
     525  	       * builds a command line for only 3 files at a time
     526  	       * (since fts descends into the directories).
     527  	       */
     528  	      complete_pending_execdirs ();
     529  	    }
     530  	  level = (int)ent->fts_level;
     531  
     532  	  state.already_issued_stat_error_msg = false;
     533  	  state.have_stat = false;
     534  	  state.have_type = !!ent->fts_statp->st_mode;
     535  	  state.type = state.have_type ? ent->fts_statp->st_mode : 0;
     536  	  consider_visiting (p, ent);
     537  	}
     538        /* fts_read returned NULL; distinguish between "finished" and "error". */
     539        if (errno)
     540  	{
     541  	  error (0, errno,
     542  		 "failed to read file names from file system at or below %s",
     543  		 safely_quote_err_filename (0, arg));
     544  	  state.exit_status = EXIT_FAILURE;
     545  	  return false;
     546  	}
     547  
     548        if (0 != fts_close (p))
     549  	{
     550  	  /* Here we break the abstraction of fts_close a bit, because we
     551  	   * are going to skip the rest of the start points, and return with
     552  	   * nonzero exit status.  Hence we need to issue a diagnostic on
     553  	   * stderr. */
     554  	  error (0, errno,
     555  		 _("failed to restore working directory after searching %s"),
     556  		 arg);
     557  	  state.exit_status = EXIT_FAILURE;
     558  	  return false;
     559  	}
     560        p = NULL;
     561      }
     562    return true;
     563  }
     564  
     565  
     566  static bool
     567  process_all_startpoints (int argc, char *argv[])
     568  {
     569    /* Did the user pass starting points on the command line?  */
     570    bool argv_starting_points = 0 < argc && !looks_like_expression (argv[0], true);
     571  
     572    FILE *stream = NULL;
     573    char const* files0_filename_quoted = NULL;
     574  
     575    struct argv_iterator *ai;
     576    if (options.files0_from)
     577      {
     578        /* Option -files0-from must not be combined with passing starting points
     579         * on the command line.  */
     580        if (argv_starting_points)
     581          {
     582            error (0, 0, _("extra operand %s"), safely_quote_err_filename (0, argv[0]));
     583            die (EXIT_FAILURE, 0, "%s",
     584                     _("file operands cannot be combined with -files0-from"));
     585          }
     586  
     587        if (0 == strcmp (options.files0_from, "-"))
     588          {
     589            /* Option -files0-from with argument "-" (=stdin) must not be combined
     590             * with the -ok, -okdir actions: getting the user confirmation would
     591             * mess with stdin.  */
     592            if (options.ok_prompt_stdin)
     593              {
     594                die (EXIT_FAILURE, 0, "%s\n",
     595                     _("option -files0-from reading from standard input"
     596                     " cannot be combined with -ok, -okdir"));
     597              }
     598            files0_filename_quoted = safely_quote_err_filename (0, _("(standard input)"));
     599            stream = stdin;
     600          }
     601        else
     602          {
     603            files0_filename_quoted = safely_quote_err_filename (0, options.files0_from);
     604            stream = fopen (options.files0_from, "r");
     605            if (stream == NULL)
     606              die (EXIT_FAILURE, errno, _("cannot open %s for reading"),
     607                   files0_filename_quoted);
     608  
     609            const int fd = fileno (stream);
     610            assert (fd >= 0);
     611            if (options.ok_prompt_stdin)
     612              {
     613                /* Check if the given file is associated to the same stream as
     614                 * standard input - which is not allowed with -ok, -okdir.  This
     615                 * is the case with special device names symlinks for stdin like
     616                 *   $ find -files0-from /dev/stdin -ok
     617                 * or when the given FILE is also associated to stdin:
     618                 *   $ find -files0-from FILE -ok < FILE
     619                 */
     620                struct stat sb1, sb2;
     621                if (fstat (fd, &sb1) == 0 && fstat (STDIN_FILENO, &sb2) == 0
     622                      && SAME_INODE (sb1, sb2))
     623                  {
     624                    die (EXIT_FAILURE, 0, "%s: %s\n",
     625                             _("option -files0-from: standard input must not refer"
     626                               " to the same file when combined with -ok, -okdir"),
     627                             files0_filename_quoted);
     628                  }
     629              }
     630            set_cloexec_flag (fd, true);
     631         }
     632        ai = argv_iter_init_stream (stream);
     633      }
     634    else
     635      {
     636        if (!argv_starting_points)
     637          {
     638            /* If no starting points are given on the comman line, then
     639             * fall back to processing the current directory, i.e., ".".
     640             * We use a temporary variable here because some actions modify
     641             * the path temporarily.  Hence if we use a string constant,
     642             * we get a coredump.  The best example of this is if we say
     643             * "find -printf %H" (note, not "find . -printf %H").
     644             */
     645            char defaultpath[2] = ".";
     646            return find (defaultpath);
     647          }
     648  
     649        /* Process the starting point(s) from the command line.  */
     650        ai = argv_iter_init_argv (argv);
     651      }
     652  
     653    if (!ai)
     654      xalloc_die ();
     655  
     656    bool ok = true;
     657    while (true)
     658      {
     659        enum argv_iter_err ai_err;
     660        char *file_name = argv_iter (ai, &ai_err);
     661        if (!file_name)
     662          {
     663            switch (ai_err)
     664              {
     665              case AI_ERR_EOF:
     666                goto argv_iter_done;
     667              case AI_ERR_READ:  /* may only happen with -files0-from  */
     668                error (0, errno, _("%s: read error"), files0_filename_quoted);
     669                state.exit_status = EXIT_FAILURE;
     670                ok = false;
     671                goto argv_iter_done;
     672              case AI_ERR_MEM:
     673                xalloc_die ();
     674              default:
     675                assert (!"unexpected error code from argv_iter");
     676              }
     677          }
     678        /* Report and skip any empty file names before invoking fts.
     679           This works around a glitch in fts, which fails immediately
     680           (without looking at the other file names) when given an empty
     681           file name.  */
     682        if (!file_name[0])
     683          {
     684            /* Diagnose a zero-length file name.  When it's one
     685               among many, knowing the record number may help.  */
     686            if (options.files0_from == NULL)
     687              error (0, ENOENT, "%s", safely_quote_err_filename (0, file_name));
     688            else
     689              {
     690                /* Using the standard 'filename:line-number:' prefix here is
     691                   not totally appropriate, since NUL is the separator, not NL,
     692                   but it might be better than nothing.  */
     693                unsigned long int file_number = argv_iter_n_args (ai);
     694                error (0, 0, "%s:%lu: %s", files0_filename_quoted, file_number,
     695                       _("invalid zero-length file name"));
     696              }
     697            state.exit_status = EXIT_FAILURE;
     698            ok = false;
     699            continue;
     700          }
     701  
     702        /* Terminate loop when processing the start points from command line,
     703           and reaching the first expression.  */
     704        if (!options.files0_from && looks_like_expression (file_name, true))
     705          break;
     706  
     707        state.starting_path_length = strlen (file_name); /* TODO: is this redundant? */
     708        if (!find (file_name))
     709          {
     710            ok = false;
     711            goto argv_iter_done;
     712          }
     713      }
     714   argv_iter_done:
     715  
     716    argv_iter_free (ai);
     717  
     718    if (ok && options.files0_from && (ferror (stream) || fclose (stream) != 0))
     719      die (EXIT_FAILURE, 0, _("error reading %s"), files0_filename_quoted);
     720  
     721    return ok;
     722  }
     723  
     724  
     725  
     726  
     727  int
     728  main (int argc, char **argv)
     729  {
     730    int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
     731    struct predicate *eval_tree;
     732  
     733    if (argv[0])
     734      set_program_name (argv[0]);
     735    else
     736      set_program_name ("find");
     737  
     738    record_initial_cwd ();
     739  
     740    state.already_issued_stat_error_msg = false;
     741    state.exit_status = EXIT_SUCCESS;
     742    state.execdirs_outstanding = false;
     743    state.cwd_dir_fd = AT_FDCWD;
     744  
     745    if (fd_leak_check_is_enabled ())
     746      {
     747        remember_non_cloexec_fds ();
     748      }
     749  
     750    state.shared_files = sharefile_init ("w");
     751    if (NULL == state.shared_files)
     752      {
     753        die (EXIT_FAILURE, errno,
     754  	   _("Failed to initialize shared-file hash table"));
     755      }
     756  
     757    /* Set the option defaults before we do the locale initialisation as
     758     * check_nofollow() needs to be executed in the POSIX locale.
     759     */
     760    set_option_defaults (&options);
     761  
     762  #ifdef HAVE_SETLOCALE
     763    setlocale (LC_ALL, "");
     764  #endif
     765  
     766    bindtextdomain (PACKAGE, LOCALEDIR);
     767    textdomain (PACKAGE);
     768    if (atexit (close_stdout))
     769      {
     770        die (EXIT_FAILURE, errno, _("The atexit library function failed"));
     771      }
     772  
     773    /* Check for -P, -H or -L options.  Also -D and -O, which are
     774     * both GNU extensions.
     775     */
     776    end_of_leading_options = process_leading_options (argc, argv);
     777  
     778    if (options.debug_options & DebugStat)
     779      options.xstat = debug_stat;
     780  
     781  
     782    if (options.debug_options & DebugTime)
     783      fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start.tv_sec));
     784  
     785  
     786    /* We are now processing the part of the "find" command line
     787     * after the -H/-L options (if any).
     788     */
     789    eval_tree = build_expression_tree (argc, argv, end_of_leading_options);
     790  
     791    /* safely_chdir() needs to check that it has ended up in the right place.
     792     * To avoid bailing out when something gets automounted, it checks if
     793     * the target directory appears to have had a directory mounted on it as
     794     * we chdir()ed.  The problem with this is that in order to notice that
     795     * a file system was mounted, we would need to lstat() all the mount points.
     796     * That strategy loses if our machine is a client of a dead NFS server.
     797     *
     798     * Hence if safely_chdir() and wd_sanity_check() can manage without needing
     799     * to know the mounted device list, we do that.
     800     */
     801    if (!options.open_nofollow_available)
     802      {
     803  #ifdef STAT_MOUNTPOINTS
     804        init_mounted_dev_list ();
     805  #endif
     806      }
     807  
     808  
     809    /* process_all_startpoints processes the starting points named on
     810     * the command line.  A false return value from it means that we
     811     * failed to restore the original context.  That means it would not
     812     * be safe to call cleanup() since we might complete an execdir in
     813     * the wrong directory for example.
     814     */
     815    if (process_all_startpoints (argc-end_of_leading_options,
     816  			       argv+end_of_leading_options))
     817      {
     818        /* If "-exec ... {} +" has been used, there may be some
     819         * partially-full command lines which have been built,
     820         * but which are not yet complete.   Execute those now.
     821         */
     822        show_success_rates (eval_tree);
     823        cleanup ();
     824      }
     825    return state.exit_status;
     826  }
     827  
     828  bool
     829  is_fts_enabled (int *fts_options)
     830  {
     831    /* this version of find (i.e. this main()) uses fts. */
     832    *fts_options = ftsoptions;
     833    return true;
     834  }