fts.cpp
上传用户:surprise9
上传日期:2007-01-04
资源大小:426k
文件大小:27k
源码类别:

Ftp客户端

开发平台:

Visual C++

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