(root)/
gawk-5.2.2/
extension/
gawkfts.c
       1  /*	$NetBSD: fts.c,v 1.44 2012/03/14 00:25:19 christos Exp $	*/
       2  
       3  /*-
       4   * Copyright (c) 1990, 1993, 1994
       5   *	The Regents of the University of California.  All rights reserved.
       6   *
       7   * Redistribution and use in source and binary forms, with or without
       8   * modification, are permitted provided that the following conditions
       9   * are met:
      10   * 1. Redistributions of source code must retain the above copyright
      11   *    notice, this list of conditions and the following disclaimer.
      12   * 2. Redistributions in binary form must reproduce the above copyright
      13   *    notice, this list of conditions and the following disclaimer in the
      14   *    documentation and/or other materials provided with the distribution.
      15   * 3. Neither the name of the University nor the names of its contributors
      16   *    may be used to endorse or promote products derived from this software
      17   *    without specific prior written permission.
      18   *
      19   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      22   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      23   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      24   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      25   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      27   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      28   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      29   * SUCH DAMAGE.
      30   */
      31  
      32  #if 0
      33  static char sccsid[] = "@(#)fts.c	8.6 (Berkeley) 8/14/94";
      34  #else
      35  /* __RCSID("$NetBSD: fts.c,v 1.44 2012/03/14 00:25:19 christos Exp $"); */
      36  #endif
      37  
      38  #ifdef HAVE_CONFIG_H
      39  #include <config.h>
      40  #endif
      41  
      42  #define _BSD_SOURCE
      43  
      44  #include <limits.h>
      45  /* #include "namespace.h" */
      46  #ifdef HAVE_SYS_PARAM_H
      47  #include <sys/param.h>
      48  #else
      49  #include <stdio.h>
      50  #endif
      51  #include <sys/stat.h>
      52  
      53  #include <assert.h>
      54  #include <dirent.h>
      55  #include <errno.h>
      56  #include <fcntl.h>
      57  #include "gawkfts.h"
      58  #include <stdlib.h>
      59  #include <string.h>
      60  #include <unistd.h>
      61  
      62  #include "gawkdirfd.h"
      63  
      64  #if ! defined(S_ISREG) && defined(S_IFREG)
      65  #define	S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
      66  #endif
      67  
      68  /*
      69  #if ! HAVE_NBTOOL_CONFIG_H
      70  #define	HAVE_STRUCT_DIRENT_D_NAMLEN
      71  #endif
      72  */
      73  
      74  #ifndef MAX
      75  static int MAX(int x, int y)
      76  {
      77  	if (x > y)
      78  		return x;
      79  
      80  	return y;
      81  }
      82  #endif
      83  
      84  
      85  static FTSENT	*fts_alloc(FTS *, const char *, size_t);
      86  static FTSENT	*fts_build(FTS *, int);
      87  static void	 fts_free(FTSENT *);
      88  static void	 fts_lfree(FTSENT *);
      89  static void	 fts_load(FTS *, FTSENT *);
      90  static size_t	 fts_maxarglen(char * const *);
      91  static size_t	 fts_pow2(size_t);
      92  static int	 fts_palloc(FTS *, size_t);
      93  static void	 fts_padjust(FTS *, FTSENT *);
      94  static FTSENT	*fts_sort(FTS *, FTSENT *, size_t);
      95  static unsigned short fts_stat(FTS *, FTSENT *, int);
      96  static int	 fts_safe_changedir(const FTS *, const FTSENT *, int,
      97      const char *);
      98  
      99  #if defined(ALIGNBYTES) && defined(ALIGN)
     100  #define	FTS_ALLOC_ALIGNED	1
     101  #else
     102  #undef	FTS_ALLOC_ALIGNED
     103  #endif
     104  
     105  #ifndef ftsent_namelen_truncate
     106  #define ftsent_namelen_truncate(a)	\
     107      ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
     108  #endif
     109  #ifndef ftsent_pathlen_truncate
     110  #define ftsent_pathlen_truncate(a) \
     111      ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
     112  #endif
     113  #ifndef fts_pathlen_truncate
     114  #define fts_pathlen_truncate(a)	\
     115      ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
     116  #endif
     117  #ifndef fts_nitems_truncate
     118  #define fts_nitems_truncate(a) \
     119      ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
     120  #endif
     121  
     122  #define	ISDOT(a)	(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
     123  
     124  #define	CLR(opt)	(sp->fts_options &= ~(opt))
     125  #define	ISSET(opt)	(sp->fts_options & (opt))
     126  #define	SET(opt)	(sp->fts_options |= (opt))
     127  
     128  #define	CHDIR(sp, path)	(!ISSET(FTS_NOCHDIR) && chdir(path))
     129  #define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
     130  
     131  /* fts_build flags */
     132  #define	BCHILD		1		/* fts_children */
     133  #define	BNAMES		2		/* fts_children, names only */
     134  #define	BREAD		3		/* fts_read */
     135  
     136  #ifndef DTF_HIDEW
     137  #undef FTS_WHITEOUT
     138  #endif
     139  
     140  #define _DIAGASSERT(expression)
     141  
     142  #ifndef MAXPATHLEN
     143  #define MAXPATHLEN	1024	/* a guess */
     144  #endif
     145  
     146  FTS *
     147  fts_open(char * const *argv, int options,
     148      int (*compar)(const FTSENT **, const FTSENT **))
     149  {
     150  	FTS *sp;
     151  	FTSENT *p, *root;
     152  	size_t nitems;
     153  	FTSENT *parent, *tmp = NULL;	/* pacify gcc */
     154  	size_t len;
     155  
     156  	_DIAGASSERT(argv != NULL);
     157  
     158  	/* Options check. */
     159  	if (options & ~FTS_OPTIONMASK) {
     160  		errno = EINVAL;
     161  		return (NULL);
     162  	}
     163  
     164  	/* Allocate/initialize the stream */
     165  	if ((sp = calloc(1, (unsigned int)sizeof(FTS))) == NULL)
     166  		return (NULL);
     167  	sp->fts_compar = compar;
     168  	sp->fts_options = options;
     169  
     170  	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
     171  	if (ISSET(FTS_LOGICAL))
     172  		SET(FTS_NOCHDIR);
     173  
     174  	/*
     175  	 * Start out with 1K of path space, and enough, in any case,
     176  	 * to hold the user's paths.
     177  	 */
     178  	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
     179  		goto mem1;
     180  
     181  	/* Allocate/initialize root's parent. */
     182  	if ((parent = fts_alloc(sp, "", 0)) == NULL)
     183  		goto mem2;
     184  	parent->fts_level = FTS_ROOTPARENTLEVEL;
     185  
     186  	/* Allocate/initialize root(s). */
     187  	for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
     188  		/* Don't allow zero-length paths. */
     189  		if ((len = strlen(*argv)) == 0) {
     190  			errno = ENOENT;
     191  			goto mem3;
     192  		}
     193  
     194  		if ((p = fts_alloc(sp, *argv, len)) == NULL)
     195  			goto mem3;
     196  		p->fts_level = FTS_ROOTLEVEL;
     197  		p->fts_parent = parent;
     198  		p->fts_accpath = p->fts_name;
     199  		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
     200  
     201  		/* Command-line "." and ".." are real directories. */
     202  		if (p->fts_info == FTS_DOT)
     203  			p->fts_info = FTS_D;
     204  
     205  		/*
     206  		 * If comparison routine supplied, traverse in sorted
     207  		 * order; otherwise traverse in the order specified.
     208  		 */
     209  		if (compar) {
     210  			p->fts_link = root;
     211  			root = p;
     212  		} else {
     213  			p->fts_link = NULL;
     214  			if (root == NULL)
     215  				tmp = root = p;
     216  			else {
     217  				tmp->fts_link = p;
     218  				tmp = p;
     219  			}
     220  		}
     221  	}
     222  	if (compar && nitems > 1)
     223  		root = fts_sort(sp, root, nitems);
     224  
     225  	/*
     226  	 * Allocate a dummy pointer and make fts_read think that we've just
     227  	 * finished the node before the root(s); set p->fts_info to FTS_INIT
     228  	 * so that everything about the "current" node is ignored.
     229  	 */
     230  	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
     231  		goto mem4;
     232  	sp->fts_cur->fts_link = root;
     233  	sp->fts_cur->fts_info = FTS_INIT;
     234  
     235  	/*
     236  	 * If using chdir(2), grab a file descriptor pointing to dot to insure
     237  	 * that we can get back here; this could be avoided for some paths,
     238  	 * but almost certainly not worth the effort.  Slashes, symbolic links,
     239  	 * and ".." are all fairly nasty problems.  Note, if we can't get the
     240  	 * descriptor we run anyway, just more slowly.
     241  	 */
     242  #ifndef O_CLOEXEC
     243  #define O_CLOEXEC 0
     244  #endif
     245  	if (!ISSET(FTS_NOCHDIR)) {
     246  		if ((sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1)
     247  			SET(FTS_NOCHDIR);
     248  	}
     249  
     250  	if (nitems == 0)
     251  		fts_free(parent);
     252  
     253  	return (sp);
     254  
     255  mem4:   if (sp->fts_array != NULL)
     256  		free(sp->fts_array);
     257  mem3:	fts_lfree(root);
     258  	fts_free(parent);
     259  mem2:	free(sp->fts_path);
     260  mem1:	free(sp);
     261  	return (NULL);
     262  }
     263  
     264  static void
     265  fts_load(FTS *sp, FTSENT *p)
     266  {
     267  	size_t len;
     268  	char *cp;
     269  
     270  	_DIAGASSERT(sp != NULL);
     271  	_DIAGASSERT(p != NULL);
     272  
     273  	/*
     274  	 * Load the stream structure for the next traversal.  Since we don't
     275  	 * actually enter the directory until after the preorder visit, set
     276  	 * the fts_accpath field specially so the chdir gets done to the right
     277  	 * place and the user can access the first node.  From fts_open it's
     278  	 * known that the path will fit.
     279  	 */
     280  	len = p->fts_pathlen = p->fts_namelen;
     281  	memmove(sp->fts_path, p->fts_name, len + 1);
     282  	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
     283  		len = strlen(++cp);
     284  		memmove(p->fts_name, cp, len + 1);
     285  		p->fts_namelen = ftsent_namelen_truncate(len);
     286  	}
     287  	p->fts_accpath = p->fts_path = sp->fts_path;
     288  	sp->fts_dev = p->fts_dev;
     289  }
     290  
     291  int
     292  fts_close(FTS *sp)
     293  {
     294  	FTSENT *freep, *p;
     295  	int saved_errno = 0;
     296  
     297  	_DIAGASSERT(sp != NULL);
     298  
     299  	/*
     300  	 * This still works if we haven't read anything -- the dummy structure
     301  	 * points to the root list, so we step through to the end of the root
     302  	 * list which has a valid parent pointer.
     303  	 */
     304  	if (sp->fts_cur) {
     305  		if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW)
     306  			(void)close(sp->fts_cur->fts_symfd);
     307  		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
     308  			freep = p;
     309  			p = p->fts_link ? p->fts_link : p->fts_parent;
     310  			fts_free(freep);
     311  		}
     312  		fts_free(p);
     313  	}
     314  
     315  	/* Free up child linked list, sort array, path buffer. */
     316  	if (sp->fts_child)
     317  		fts_lfree(sp->fts_child);
     318  	if (sp->fts_array)
     319  		free(sp->fts_array);
     320  	free(sp->fts_path);
     321  
     322  	/* Return to original directory, save errno if necessary. */
     323  	if (!ISSET(FTS_NOCHDIR)) {
     324  		if (fchdir(sp->fts_rfd) == -1)
     325  			saved_errno = errno;
     326  		(void)close(sp->fts_rfd);
     327  	}
     328  
     329  	/* Free up the stream pointer. */
     330  	free(sp);
     331  	if (saved_errno) {
     332  		errno = saved_errno;
     333  		return -1;
     334  	}
     335  
     336  	return 0;
     337  }
     338  
     339  #if !defined(__FTS_COMPAT_TAILINGSLASH)
     340  
     341  /*
     342   * Special case of "/" at the end of the path so that slashes aren't
     343   * appended which would cause paths to be written as "....//foo".
     344   */
     345  #define	NAPPEND(p)							\
     346  	(p->fts_path[p->fts_pathlen - 1] == '/'				\
     347  	    ? p->fts_pathlen - 1 : p->fts_pathlen)
     348  
     349  #else /* !defined(__FTS_COMPAT_TAILINGSLASH) */
     350  
     351  /*
     352   * compatibility with the old behaviour.
     353   *
     354   * Special case a root of "/" so that slashes aren't appended which would
     355   * cause paths to be written as "//foo".
     356   */
     357  
     358  #define	NAPPEND(p)							\
     359  	(p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 &&	\
     360  	    p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
     361  
     362  #endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */
     363  
     364  FTSENT *
     365  fts_read(FTS *sp)
     366  {
     367  	FTSENT *p, *tmp;
     368  	int instr;
     369  	char *t;
     370  	int saved_errno;
     371  
     372  	_DIAGASSERT(sp != NULL);
     373  
     374  	/* If finished or unrecoverable error, return NULL. */
     375  	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
     376  		return (NULL);
     377  
     378  	/* Set current node pointer. */
     379  	p = sp->fts_cur;
     380  
     381  	/* Save and zero out user instructions. */
     382  	instr = p->fts_instr;
     383  	p->fts_instr = FTS_NOINSTR;
     384  
     385  	/* Any type of file may be re-visited; re-stat and re-turn. */
     386  	if (instr == FTS_AGAIN) {
     387  		p->fts_info = fts_stat(sp, p, 0);
     388  		return (p);
     389  	}
     390  
     391  	/*
     392  	 * Following a symlink -- SLNONE test allows application to see
     393  	 * SLNONE and recover.  If indirecting through a symlink, have
     394  	 * keep a pointer to current location.  If unable to get that
     395  	 * pointer, follow fails.
     396  	 */
     397  	if (instr == FTS_FOLLOW &&
     398  	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
     399  		p->fts_info = fts_stat(sp, p, 1);
     400  		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
     401  			if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, 0))
     402  			    == -1) {
     403  				p->fts_errno = errno;
     404  				p->fts_info = FTS_ERR;
     405  			} else
     406  				p->fts_flags |= FTS_SYMFOLLOW;
     407  		}
     408  		return (p);
     409  	}
     410  
     411  	/* Directory in pre-order. */
     412  	if (p->fts_info == FTS_D) {
     413  		/* If skipped or crossed mount point, do post-order visit. */
     414  		if (instr == FTS_SKIP ||
     415  		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
     416  			if (p->fts_flags & FTS_SYMFOLLOW)
     417  				(void)close(p->fts_symfd);
     418  			if (sp->fts_child) {
     419  				fts_lfree(sp->fts_child);
     420  				sp->fts_child = NULL;
     421  			}
     422  			p->fts_info = FTS_DP;
     423  			return (p);
     424  		}
     425  
     426  		/* Rebuild if only read the names and now traversing. */
     427  		if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
     428  			CLR(FTS_NAMEONLY);
     429  			fts_lfree(sp->fts_child);
     430  			sp->fts_child = NULL;
     431  		}
     432  
     433  		/*
     434  		 * Cd to the subdirectory.
     435  		 *
     436  		 * If have already read and now fail to chdir, whack the list
     437  		 * to make the names come out right, and set the parent errno
     438  		 * so the application will eventually get an error condition.
     439  		 * Set the FTS_DONTCHDIR flag so that when we logically change
     440  		 * directories back to the parent we don't do a chdir.
     441  		 *
     442  		 * If haven't read do so.  If the read fails, fts_build sets
     443  		 * FTS_STOP or the fts_info field of the node.
     444  		 */
     445  		if (sp->fts_child) {
     446  			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
     447  				p->fts_errno = errno;
     448  				p->fts_flags |= FTS_DONTCHDIR;
     449  				for (p = sp->fts_child; p; p = p->fts_link)
     450  					p->fts_accpath =
     451  					    p->fts_parent->fts_accpath;
     452  			}
     453  		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
     454  			if (ISSET(FTS_STOP))
     455  				return (NULL);
     456  			return (p);
     457  		}
     458  		p = sp->fts_child;
     459  		sp->fts_child = NULL;
     460  		goto name;
     461  	}
     462  
     463  	/* Move to the next node on this level. */
     464  next:	tmp = p;
     465  	if ((p = p->fts_link) != NULL) {
     466  		fts_free(tmp);
     467  
     468  		/*
     469  		 * If reached the top, return to the original directory, and
     470  		 * load the paths for the next root.
     471  		 */
     472  		if (p->fts_level == FTS_ROOTLEVEL) {
     473  			if (FCHDIR(sp, sp->fts_rfd)) {
     474  				SET(FTS_STOP);
     475  				return (NULL);
     476  			}
     477  			fts_load(sp, p);
     478  			return (sp->fts_cur = p);
     479  		}
     480  
     481  		/*
     482  		 * User may have called fts_set on the node.  If skipped,
     483  		 * ignore.  If followed, get a file descriptor so we can
     484  		 * get back if necessary.
     485  		 */
     486  		if (p->fts_instr == FTS_SKIP)
     487  			goto next;
     488  		if (p->fts_instr == FTS_FOLLOW) {
     489  			p->fts_info = fts_stat(sp, p, 1);
     490  			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
     491  				if ((p->fts_symfd =
     492  				    open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) {
     493  					p->fts_errno = errno;
     494  					p->fts_info = FTS_ERR;
     495  				} else
     496  					p->fts_flags |= FTS_SYMFOLLOW;
     497  			}
     498  			p->fts_instr = FTS_NOINSTR;
     499  		}
     500  
     501  name:		t = sp->fts_path + NAPPEND(p->fts_parent);
     502  		*t++ = '/';
     503  		memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
     504  		return (sp->fts_cur = p);
     505  	}
     506  
     507  	/* Move up to the parent node. */
     508  	p = tmp->fts_parent;
     509  	fts_free(tmp);
     510  
     511  	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
     512  		/*
     513  		 * Done; free everything up and set errno to 0 so the user
     514  		 * can distinguish between error and EOF.
     515  		 */
     516  		fts_free(p);
     517  		errno = 0;
     518  		return (sp->fts_cur = NULL);
     519  	}
     520  
     521  	/* Nul terminate the pathname. */
     522  	sp->fts_path[p->fts_pathlen] = '\0';
     523  
     524  	/*
     525  	 * Return to the parent directory.  If at a root node or came through
     526  	 * a symlink, go back through the file descriptor.  Otherwise, cd up
     527  	 * one directory.
     528  	 */
     529  	if (p->fts_level == FTS_ROOTLEVEL) {
     530  		if (FCHDIR(sp, sp->fts_rfd)) {
     531  			SET(FTS_STOP);
     532  			return (NULL);
     533  		}
     534  	} else if (p->fts_flags & FTS_SYMFOLLOW) {
     535  		if (FCHDIR(sp, p->fts_symfd)) {
     536  			saved_errno = errno;
     537  			(void)close(p->fts_symfd);
     538  			errno = saved_errno;
     539  			SET(FTS_STOP);
     540  			return (NULL);
     541  		}
     542  		(void)close(p->fts_symfd);
     543  	} else if (!(p->fts_flags & FTS_DONTCHDIR) &&
     544  	    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
     545  		SET(FTS_STOP);
     546  		return (NULL);
     547  	}
     548  	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
     549  	return (sp->fts_cur = p);
     550  }
     551  
     552  /*
     553   * Fts_set takes the stream as an argument although it's not used in this
     554   * implementation; it would be necessary if anyone wanted to add global
     555   * semantics to fts using fts_set.  An error return is allowed for similar
     556   * reasons.
     557   */
     558  /* ARGSUSED */
     559  int
     560  fts_set(FTS *sp, FTSENT *p, int instr)
     561  {
     562  	(void) sp;	/* silence warnings */
     563  	_DIAGASSERT(sp != NULL);
     564  	_DIAGASSERT(p != NULL);
     565  
     566  	if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
     567  	    instr != FTS_NOINSTR && instr != FTS_SKIP) {
     568  		errno = EINVAL;
     569  		return (1);
     570  	}
     571  	p->fts_instr = instr;
     572  	return (0);
     573  }
     574  
     575  FTSENT *
     576  fts_children(FTS *sp, int instr)
     577  {
     578  	FTSENT *p;
     579  	int fd;
     580  
     581  	_DIAGASSERT(sp != NULL);
     582  
     583  	if (instr && instr != FTS_NAMEONLY) {
     584  		errno = EINVAL;
     585  		return (NULL);
     586  	}
     587  
     588  	/* Set current node pointer. */
     589  	p = sp->fts_cur;
     590  
     591  	/*
     592  	 * Errno set to 0 so user can distinguish empty directory from
     593  	 * an error.
     594  	 */
     595  	errno = 0;
     596  
     597  	/* Fatal errors stop here. */
     598  	if (ISSET(FTS_STOP))
     599  		return (NULL);
     600  
     601  	/* Return logical hierarchy of user's arguments. */
     602  	if (p->fts_info == FTS_INIT)
     603  		return (p->fts_link);
     604  
     605  	/*
     606  	 * If not a directory being visited in pre-order, stop here.  Could
     607  	 * allow FTS_DNR, assuming the user has fixed the problem, but the
     608  	 * same effect is available with FTS_AGAIN.
     609  	 */
     610  	if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
     611  		return (NULL);
     612  
     613  	/* Free up any previous child list. */
     614  	if (sp->fts_child)
     615  		fts_lfree(sp->fts_child);
     616  
     617  	if (instr == FTS_NAMEONLY) {
     618  		SET(FTS_NAMEONLY);
     619  		instr = BNAMES;
     620  	} else
     621  		instr = BCHILD;
     622  
     623  	/*
     624  	 * If using chdir on a relative path and called BEFORE fts_read does
     625  	 * its chdir to the root of a traversal, we can lose -- we need to
     626  	 * chdir into the subdirectory, and we don't know where the current
     627  	 * directory is, so we can't get back so that the upcoming chdir by
     628  	 * fts_read will work.
     629  	 */
     630  	if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
     631  	    ISSET(FTS_NOCHDIR))
     632  		return (sp->fts_child = fts_build(sp, instr));
     633  
     634  	if ((fd = open(".", O_RDONLY, 0)) == -1)
     635  		return (sp->fts_child = NULL);
     636  	sp->fts_child = fts_build(sp, instr);
     637  	if (fchdir(fd)) {
     638  		(void)close(fd);
     639  		return (NULL);
     640  	}
     641  	(void)close(fd);
     642  	return (sp->fts_child);
     643  }
     644  
     645  /*
     646   * This is the tricky part -- do not casually change *anything* in here.  The
     647   * idea is to build the linked list of entries that are used by fts_children
     648   * and fts_read.  There are lots of special cases.
     649   *
     650   * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
     651   * set and it's a physical walk (so that symbolic links can't be directories),
     652   * we can do things quickly.  First, if it's a 4.4BSD file system, the type
     653   * of the file is in the directory entry.  Otherwise, we assume that the number
     654   * of subdirectories in a node is equal to the number of links to the parent.
     655   * The former skips all stat calls.  The latter skips stat calls in any leaf
     656   * directories and for any files after the subdirectories in the directory have
     657   * been found, cutting the stat calls by about 2/3.
     658   */
     659  static FTSENT *
     660  fts_build(FTS *sp, int type)
     661  {
     662  	struct dirent *dp;
     663  	FTSENT *p, *head;
     664  	size_t nitems;
     665  	FTSENT *cur, *tail;
     666  	DIR *dirp;
     667  	void *oldaddr;
     668  	size_t dnamlen;
     669  	int cderrno, descend, level, nlinks, saved_errno, nostat, doadjust;
     670  	size_t len, maxlen;
     671  #ifdef FTS_WHITEOUT
     672  	int oflag;
     673  #endif
     674  	char *cp = NULL;	/* pacify gcc */
     675  
     676  	_DIAGASSERT(sp != NULL);
     677  
     678  	/* Set current node pointer. */
     679  	cur = sp->fts_cur;
     680  
     681  	/*
     682  	 * Open the directory for reading.  If this fails, we're done.
     683  	 * If being called from fts_read, set the fts_info field.
     684  	 */
     685  #if 0 /* def FTS_WHITEOUT */
     686  	if (ISSET(FTS_WHITEOUT))
     687  		oflag = DTF_NODUP|DTF_REWIND;
     688  	else
     689  		oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
     690  #else
     691  #define	__opendir2(path, flag) opendir(path)
     692  #endif
     693  	if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
     694  		if (type == BREAD) {
     695  			cur->fts_info = FTS_DNR;
     696  			cur->fts_errno = errno;
     697  		}
     698  		return (NULL);
     699  	}
     700  
     701  	/*
     702  	 * Nlinks is the number of possible entries of type directory in the
     703  	 * directory if we're cheating on stat calls, 0 if we're not doing
     704  	 * any stat calls at all, -1 if we're doing stats on everything.
     705  	 */
     706  	if (type == BNAMES) {
     707  		nlinks = 0;
     708  		nostat = 1;
     709  	} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
     710  		nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
     711  		nostat = 1;
     712  	} else {
     713  		nlinks = -1;
     714  		nostat = 0;
     715  	}
     716  
     717  #ifdef notdef
     718  	(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
     719  	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
     720  	    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
     721  #endif
     722  	/*
     723  	 * If we're going to need to stat anything or we want to descend
     724  	 * and stay in the directory, chdir.  If this fails we keep going,
     725  	 * but set a flag so we don't chdir after the post-order visit.
     726  	 * We won't be able to stat anything, but we can still return the
     727  	 * names themselves.  Note, that since fts_read won't be able to
     728  	 * chdir into the directory, it will have to return different path
     729  	 * names than before, i.e. "a/b" instead of "b".  Since the node
     730  	 * has already been visited in pre-order, have to wait until the
     731  	 * post-order visit to return the error.  There is a special case
     732  	 * here, if there was nothing to stat then it's not an error to
     733  	 * not be able to stat.  This is all fairly nasty.  If a program
     734  	 * needed sorted entries or stat information, they had better be
     735  	 * checking FTS_NS on the returned nodes.
     736  	 */
     737  	cderrno = 0;
     738  	if (nlinks || type == BREAD) {
     739  		if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
     740  			if (nlinks && type == BREAD)
     741  				cur->fts_errno = errno;
     742  			cur->fts_flags |= FTS_DONTCHDIR;
     743  			descend = 0;
     744  			cderrno = errno;
     745  		} else
     746  			descend = 1;
     747  	} else
     748  		descend = 0;
     749  
     750  	/*
     751  	 * Figure out the max file name length that can be stored in the
     752  	 * current path -- the inner loop allocates more path as necessary.
     753  	 * We really wouldn't have to do the maxlen calculations here, we
     754  	 * could do them in fts_read before returning the path, but it's a
     755  	 * lot easier here since the length is part of the dirent structure.
     756  	 *
     757  	 * If not changing directories set a pointer so that can just append
     758  	 * each new name into the path.
     759  	 */
     760  	len = NAPPEND(cur);
     761  	if (ISSET(FTS_NOCHDIR)) {
     762  		cp = sp->fts_path + len;
     763  		*cp++ = '/';
     764  	}
     765  	len++;
     766  	maxlen = sp->fts_pathlen - len;
     767  
     768  #if defined(__FTS_COMPAT_LEVEL)
     769  	if (cur->fts_level == SHRT_MAX) {
     770  		(void)closedir(dirp);
     771  		cur->fts_info = FTS_ERR;
     772  		SET(FTS_STOP);
     773  		errno = ENAMETOOLONG;
     774  		return (NULL);
     775  	}
     776  #endif
     777  
     778  	level = cur->fts_level + 1;
     779  
     780  	/* Read the directory, attaching each entry to the `link' pointer. */
     781  	doadjust = 0;
     782  	for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {
     783  
     784  		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
     785  			continue;
     786  
     787  #if defined(HAVE_STRUCT_DIRENT_D_NAMLEN)
     788  		dnamlen = dp->d_namlen;
     789  #else
     790  		dnamlen = strlen(dp->d_name);
     791  #endif
     792  		if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
     793  			goto mem1;
     794  		if (dnamlen >= maxlen) {	/* include space for NUL */
     795  			oldaddr = sp->fts_path;
     796  			if (fts_palloc(sp, dnamlen + len + 1)) {
     797  				/*
     798  				 * No more memory for path or structures.  Save
     799  				 * errno, free up the current structure and the
     800  				 * structures already allocated.
     801  				 */
     802  mem1:				saved_errno = errno;
     803  				if (p)
     804  					fts_free(p);
     805  				fts_lfree(head);
     806  				(void)closedir(dirp);
     807  				errno = saved_errno;
     808  				cur->fts_info = FTS_ERR;
     809  				SET(FTS_STOP);
     810  				return (NULL);
     811  			}
     812  			/* Did realloc() change the pointer? */
     813  			if (oldaddr != sp->fts_path) {
     814  				doadjust = 1;
     815  				if (ISSET(FTS_NOCHDIR))
     816  					cp = sp->fts_path + len;
     817  			}
     818  			maxlen = sp->fts_pathlen - len;
     819  		}
     820  
     821  #if defined(__FTS_COMPAT_LENGTH)
     822  		if (len + dnamlen >= USHRT_MAX) {
     823  			/*
     824  			 * In an FTSENT, fts_pathlen is an unsigned short
     825  			 * so it is possible to wraparound here.
     826  			 * If we do, free up the current structure and the
     827  			 * structures already allocated, then error out
     828  			 * with ENAMETOOLONG.
     829  			 */
     830  			fts_free(p);
     831  			fts_lfree(head);
     832  			(void)closedir(dirp);
     833  			cur->fts_info = FTS_ERR;
     834  			SET(FTS_STOP);
     835  			errno = ENAMETOOLONG;
     836  			return (NULL);
     837  		}
     838  #endif
     839  		p->fts_level = level;
     840  		p->fts_pathlen = ftsent_pathlen_truncate(len + dnamlen);
     841  		p->fts_parent = sp->fts_cur;
     842  
     843  #ifdef FTS_WHITEOUT
     844  		if (dp->d_type == DT_WHT)
     845  			p->fts_flags |= FTS_ISW;
     846  #endif
     847  
     848  		if (cderrno) {
     849  			if (nlinks) {
     850  				p->fts_info = FTS_NS;
     851  				p->fts_errno = cderrno;
     852  			} else
     853  				p->fts_info = FTS_NSOK;
     854  			p->fts_accpath = cur->fts_accpath;
     855  		} else if (nlinks == 0
     856  #ifdef DT_DIR
     857  		    || (nostat &&
     858  		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
     859  #endif
     860  		    ) {
     861  			p->fts_accpath =
     862  			    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
     863  			p->fts_info = FTS_NSOK;
     864  		} else {
     865  			/* Build a file name for fts_stat to stat. */
     866  			if (ISSET(FTS_NOCHDIR)) {
     867  				p->fts_accpath = p->fts_path;
     868  				memmove(cp, p->fts_name,
     869  				        (size_t)(p->fts_namelen + 1));
     870  			} else
     871  				p->fts_accpath = p->fts_name;
     872  			/* Stat it. */
     873  			p->fts_info = fts_stat(sp, p, 0);
     874  
     875  			/* Decrement link count if applicable. */
     876  			if (nlinks > 0 && (p->fts_info == FTS_D ||
     877  			    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
     878  				--nlinks;
     879  		}
     880  
     881  		/* We walk in directory order so "ls -f" doesn't get upset. */
     882  		p->fts_link = NULL;
     883  		if (head == NULL)
     884  			head = tail = p;
     885  		else {
     886  			tail->fts_link = p;
     887  			tail = p;
     888  		}
     889  		++nitems;
     890  	}
     891  	(void)closedir(dirp);
     892  
     893  	/*
     894  	 * If had to realloc the path, adjust the addresses for the rest
     895  	 * of the tree.
     896  	 */
     897  	if (doadjust)
     898  		fts_padjust(sp, head);
     899  
     900  	/*
     901  	 * If not changing directories, reset the path back to original
     902  	 * state.
     903  	 */
     904  	if (ISSET(FTS_NOCHDIR)) {
     905  		if (len == sp->fts_pathlen || nitems == 0)
     906  			--cp;
     907  		*cp = '\0';
     908  	}
     909  
     910  	/*
     911  	 * If descended after called from fts_children or after called from
     912  	 * fts_read and nothing found, get back.  At the root level we use
     913  	 * the saved fd; if one of fts_open()'s arguments is a relative path
     914  	 * to an empty directory, we wind up here with no other way back.  If
     915  	 * can't get back, we're done.
     916  	 */
     917  	if (descend && (type == BCHILD || !nitems) &&
     918  	    (cur->fts_level == FTS_ROOTLEVEL ?
     919  	    FCHDIR(sp, sp->fts_rfd) :
     920  	    fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
     921  		fts_lfree(head);
     922  		cur->fts_info = FTS_ERR;
     923  		SET(FTS_STOP);
     924  		return (NULL);
     925  	}
     926  
     927  	/* If didn't find anything, return NULL. */
     928  	if (!nitems) {
     929  		if (type == BREAD)
     930  			cur->fts_info = FTS_DP;
     931  		return (NULL);
     932  	}
     933  
     934  	/* Sort the entries. */
     935  	if (sp->fts_compar && nitems > 1)
     936  		head = fts_sort(sp, head, nitems);
     937  	return (head);
     938  }
     939  
     940  static unsigned short
     941  fts_stat(FTS *sp, FTSENT *p, int follow)
     942  {
     943  	FTSENT *t;
     944  	dev_t dev;
     945  	ino_t ino;
     946  	struct stat *sbp, sb;
     947  	int saved_errno;
     948  
     949  	_DIAGASSERT(sp != NULL);
     950  	_DIAGASSERT(p != NULL);
     951  
     952  	/* If user needs stat info, stat buffer already allocated. */
     953  	sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
     954  
     955  #ifdef FTS_WHITEOUT
     956  	/* check for whiteout */
     957  	if (p->fts_flags & FTS_ISW) {
     958  		if (sbp != &sb) {
     959  			memset(sbp, '\0', sizeof (*sbp));
     960  			sbp->st_mode = S_IFWHT;
     961  		}
     962  		return (FTS_W);
     963  	}
     964  #endif
     965  
     966  	/*
     967  	 * If doing a logical walk, or application requested FTS_FOLLOW, do
     968  	 * a stat(2).  If that fails, check for a non-existent symlink.  If
     969  	 * fail, set the errno from the stat call.
     970  	 */
     971  	if (ISSET(FTS_LOGICAL) || follow) {
     972  		if (stat(p->fts_accpath, sbp)) {
     973  			saved_errno = errno;
     974  			if (!lstat(p->fts_accpath, sbp)) {
     975  				errno = 0;
     976  				return (FTS_SLNONE);
     977  			}
     978  			p->fts_errno = saved_errno;
     979  			goto err;
     980  		}
     981  	} else if (lstat(p->fts_accpath, sbp)) {
     982  		p->fts_errno = errno;
     983  err:		memset(sbp, 0, sizeof(*sbp));
     984  		return (FTS_NS);
     985  	}
     986  
     987  	if (S_ISDIR(sbp->st_mode)) {
     988  		/*
     989  		 * Set the device/inode.  Used to find cycles and check for
     990  		 * crossing mount points.  Also remember the link count, used
     991  		 * in fts_build to limit the number of stat calls.  It is
     992  		 * understood that these fields are only referenced if fts_info
     993  		 * is set to FTS_D.
     994  		 */
     995  		dev = p->fts_dev = sbp->st_dev;
     996  		ino = p->fts_ino = sbp->st_ino;
     997  		p->fts_nlink = sbp->st_nlink;
     998  
     999  		if (ISDOT(p->fts_name))
    1000  			return (FTS_DOT);
    1001  
    1002  		/*
    1003  		 * Cycle detection is done by brute force when the directory
    1004  		 * is first encountered.  If the tree gets deep enough or the
    1005  		 * number of symbolic links to directories is high enough,
    1006  		 * something faster might be worthwhile.
    1007  		 */
    1008  		for (t = p->fts_parent;
    1009  		    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
    1010  			if (ino == t->fts_ino && dev == t->fts_dev) {
    1011  				p->fts_cycle = t;
    1012  				return (FTS_DC);
    1013  			}
    1014  		return (FTS_D);
    1015  	}
    1016  	if (S_ISLNK(sbp->st_mode))
    1017  		return (FTS_SL);
    1018  	if (S_ISREG(sbp->st_mode))
    1019  		return (FTS_F);
    1020  	return (FTS_DEFAULT);
    1021  }
    1022  
    1023  static FTSENT *
    1024  fts_sort(FTS *sp, FTSENT *head, size_t nitems)
    1025  {
    1026  	FTSENT **ap, *p;
    1027  
    1028  	_DIAGASSERT(sp != NULL);
    1029  	_DIAGASSERT(head != NULL);
    1030  
    1031  	/*
    1032  	 * Construct an array of pointers to the structures and call qsort(3).
    1033  	 * Reassemble the array in the order returned by qsort.  If unable to
    1034  	 * sort for memory reasons, return the directory entries in their
    1035  	 * current order.  Allocate enough space for the current needs plus
    1036  	 * 40 so don't realloc one entry at a time.
    1037  	 */
    1038  	if (nitems > sp->fts_nitems) {
    1039  		FTSENT **new;
    1040  
    1041  		new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40));
    1042  		if (new == 0)
    1043  			return (head);
    1044  		sp->fts_array = new;
    1045  		sp->fts_nitems = fts_nitems_truncate(nitems + 40);
    1046  	}
    1047  	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
    1048  		*ap++ = p;
    1049  	qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),
    1050  		(int (*)(const void *, const void *))sp->fts_compar);
    1051  	for (head = *(ap = sp->fts_array); --nitems; ++ap)
    1052  		ap[0]->fts_link = ap[1];
    1053  	ap[0]->fts_link = NULL;
    1054  	return (head);
    1055  }
    1056  
    1057  static FTSENT *
    1058  fts_alloc(FTS *sp, const char *name, size_t namelen)
    1059  {
    1060  	FTSENT *p;
    1061  #if defined(FTS_ALLOC_ALIGNED)
    1062  	size_t len;
    1063  #endif
    1064  
    1065  	_DIAGASSERT(sp != NULL);
    1066  	_DIAGASSERT(name != NULL);
    1067  
    1068  #if defined(FTS_ALLOC_ALIGNED)
    1069  	/*
    1070  	 * The file name is a variable length array and no stat structure is
    1071  	 * necessary if the user has set the nostat bit.  Allocate the FTSENT
    1072  	 * structure, the file name and the stat structure in one chunk, but
    1073  	 * be careful that the stat structure is reasonably aligned.  Since the
    1074  	 * fts_name field is declared to be of size 1, the fts_name pointer is
    1075  	 * namelen + 2 before the first possible address of the stat structure.
    1076  	 */
    1077  	len = sizeof(FTSENT) + namelen;
    1078  	if (!ISSET(FTS_NOSTAT))
    1079  		len += sizeof(*(p->fts_statp)) + ALIGNBYTES;
    1080  	if ((p = malloc(len)) == NULL)
    1081  		return (NULL);
    1082  
    1083  	if (!ISSET(FTS_NOSTAT))
    1084  		p->fts_statp = (struct stat *)ALIGN(
    1085  		    (unsigned long)(p->fts_name + namelen + 2));
    1086  #else
    1087  	if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL)
    1088  		return (NULL);
    1089  
    1090  	if (!ISSET(FTS_NOSTAT))
    1091  		if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) {
    1092  			free(p);
    1093  			return (NULL);
    1094  		}
    1095  #endif
    1096  
    1097          if (ISSET(FTS_NOSTAT))
    1098                  p->fts_statp = NULL;
    1099  
    1100  	/* Copy the name plus the trailing NULL. */
    1101  	memmove(p->fts_name, name, namelen + 1);
    1102  
    1103  	p->fts_namelen = ftsent_namelen_truncate(namelen);
    1104  	p->fts_path = sp->fts_path;
    1105  	p->fts_errno = 0;
    1106  	p->fts_flags = 0;
    1107  	p->fts_instr = FTS_NOINSTR;
    1108  	p->fts_number = 0;
    1109  	p->fts_pointer = NULL;
    1110  	return (p);
    1111  }
    1112  
    1113  static void
    1114  fts_free(FTSENT *p)
    1115  {
    1116  #if !defined(FTS_ALLOC_ALIGNED)
    1117  	if (p->fts_statp)
    1118  		free(p->fts_statp);
    1119  #endif
    1120  	free(p);
    1121  }
    1122  
    1123  static void
    1124  fts_lfree(FTSENT *head)
    1125  {
    1126  	FTSENT *p;
    1127  
    1128  	/* XXX: head may be NULL ? */
    1129  
    1130  	/* Free a linked list of structures. */
    1131  	while ((p = head) != NULL) {
    1132  		head = head->fts_link;
    1133  		fts_free(p);
    1134  	}
    1135  }
    1136  
    1137  static size_t
    1138  fts_pow2(size_t x)
    1139  {
    1140  
    1141  	x--;
    1142  	x |= x>>1;
    1143  	x |= x>>2;
    1144  	x |= x>>4;
    1145  	x |= x>>8;
    1146  	x |= x>>16;
    1147  #if LONG_BIT > 32
    1148  	x |= x>>32;
    1149  #endif
    1150  #if LONG_BIT > 64
    1151  	x |= x>>64;
    1152  #endif
    1153  	x++;
    1154  	return (x);
    1155  }
    1156  
    1157  /*
    1158   * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
    1159   * Most systems will allow creation of paths much longer than MAXPATHLEN, even
    1160   * though the kernel won't resolve them.  Round up the new size to a power of 2,
    1161   * so we don't realloc the path 2 bytes at a time.
    1162   */
    1163  static int
    1164  fts_palloc(FTS *sp, size_t size)
    1165  {
    1166  	char *new;
    1167  
    1168  	_DIAGASSERT(sp != NULL);
    1169  
    1170  #ifdef __FTS_COMPAT_LENGTH
    1171  	/* Protect against fts_pathlen overflow. */
    1172  	if (size > USHRT_MAX + 1) {
    1173  		errno = ENAMETOOLONG;
    1174  		return (1);
    1175  	}
    1176  #endif
    1177  	size = fts_pow2(size);
    1178  	new = realloc(sp->fts_path, size);
    1179  	if (new == 0)
    1180  		return (1);
    1181  	sp->fts_path = new;
    1182  	sp->fts_pathlen = fts_pathlen_truncate(size);
    1183  	return (0);
    1184  }
    1185  
    1186  /*
    1187   * When the path is realloc'd, have to fix all of the pointers in structures
    1188   * already returned.
    1189   */
    1190  static void
    1191  fts_padjust(FTS *sp, FTSENT *head)
    1192  {
    1193  	FTSENT *p;
    1194  	char *addr;
    1195  
    1196  	_DIAGASSERT(sp != NULL);
    1197  
    1198  #define	ADJUST(p) do {							\
    1199  	if ((p)->fts_accpath != (p)->fts_name)				\
    1200  		(p)->fts_accpath =					\
    1201  		    addr + ((p)->fts_accpath - (p)->fts_path);		\
    1202  	(p)->fts_path = addr;						\
    1203  } while (/*CONSTCOND*/0)
    1204  
    1205  	addr = sp->fts_path;
    1206  
    1207  	/* Adjust the current set of children. */
    1208  	for (p = sp->fts_child; p; p = p->fts_link)
    1209  		ADJUST(p);
    1210  
    1211  	/* Adjust the rest of the tree, including the current level. */
    1212  	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
    1213  		ADJUST(p);
    1214  		p = p->fts_link ? p->fts_link : p->fts_parent;
    1215  	}
    1216  }
    1217  
    1218  static size_t
    1219  fts_maxarglen(char * const *argv)
    1220  {
    1221  	size_t len, max;
    1222  
    1223  	_DIAGASSERT(argv != NULL);
    1224  
    1225  	for (max = 0; *argv; ++argv)
    1226  		if ((len = strlen(*argv)) > max)
    1227  			max = len;
    1228  	return (max + 1);
    1229  }
    1230  
    1231  /*
    1232   * Change to dir specified by fd or p->fts_accpath without getting
    1233   * tricked by someone changing the world out from underneath us.
    1234   * Assumes p->fts_dev and p->fts_ino are filled in.
    1235   */
    1236  static int
    1237  fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path)
    1238  {
    1239  	int oldfd = fd, ret = -1;
    1240  	struct stat sb;
    1241  
    1242  	if (ISSET(FTS_NOCHDIR))
    1243  		return 0;
    1244  
    1245  	if (oldfd < 0 && (path == NULL || (fd = open(path, O_RDONLY)) == -1))
    1246  		return -1;
    1247  
    1248  	if (fstat(fd, &sb) == -1)
    1249  		goto bail;
    1250  
    1251  	if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {
    1252  		errno = ENOENT;
    1253  		goto bail;
    1254  	}
    1255  
    1256  	ret = fchdir(fd);
    1257  
    1258  bail:
    1259  	if (oldfd < 0) {
    1260  		int save_errno = errno;
    1261  		(void)close(fd);
    1262  		errno = save_errno;
    1263  	}
    1264  	return ret;
    1265  }