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