Prex Home / Browse Source - Prex Version: 0.9.0

root/usr/lib/libc/gen/fts.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fts_open
  2. fts_load
  3. fts_close
  4. fts_read
  5. fts_set
  6. fts_children
  7. fts_build
  8. fts_stat
  9. fts_sort
  10. fts_alloc
  11. fts_lfree
  12. fts_palloc
  13. fts_padjust
  14. fts_maxarglen

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

/* [<][>][^][v][top][bottom][index][help] */