|
|
1.1 ! root 1: /*- ! 2: * Copyright (c) 1990 The Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * Redistribution and use in source and binary forms are permitted provided ! 6: * that: (1) source distributions retain this entire copyright notice and ! 7: * comment, and (2) distributions including binaries display the following ! 8: * acknowledgement: ``This product includes software developed by the ! 9: * University of California, Berkeley and its contributors'' in the ! 10: * documentation or other materials provided with the distribution and in ! 11: * all advertising materials mentioning features or use of this software. ! 12: * Neither the name of the University nor the names of its contributors may ! 13: * be used to endorse or promote products derived from this software without ! 14: * specific prior written permission. ! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 18: */ ! 19: ! 20: #if defined(LIBC_SCCS) && !defined(lint) ! 21: static char sccsid[] = "@(#)fts.c 5.10 (Berkeley) 6/9/90"; ! 22: #endif /* LIBC_SCCS and not lint */ ! 23: ! 24: #include <sys/param.h> ! 25: #include <sys/stat.h> ! 26: #include <fcntl.h> ! 27: #include <dirent.h> ! 28: #include <errno.h> ! 29: #include <fts.h> ! 30: #include <string.h> ! 31: #include <stdlib.h> ! 32: ! 33: FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root(); ! 34: short fts_stat(); ! 35: ! 36: /* ! 37: * Special case a root of "/" so that slashes aren't appended causing ! 38: * paths to be written as "//foo". ! 39: */ ! 40: #define NAPPEND(p) \ ! 41: (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \ ! 42: p->fts_path[0] == '/' ? 0 : p->fts_pathlen) ! 43: ! 44: #define CHDIR(sp, path) (!(sp->fts_options & FTS_NOCHDIR) && chdir(path)) ! 45: #define FCHDIR(sp, fd) (!(sp->fts_options & FTS_NOCHDIR) && fchdir(fd)) ! 46: ! 47: #define ROOTLEVEL 0 ! 48: #define ROOTPARENTLEVEL -1 ! 49: ! 50: /* fts_build flags */ ! 51: #define BCHILD 1 /* from ftschildren */ ! 52: #define BREAD 2 /* from ftsread */ ! 53: ! 54: static FTS *stream; /* current stream pointer */ ! 55: ! 56: FTS * ! 57: ftsopen(argv, options, compar) ! 58: char *argv[]; ! 59: register int options; ! 60: int (*compar)(); ! 61: { ! 62: register FTS *sp; ! 63: register FTSENT *p, *root; ! 64: register int nitems, maxlen; ! 65: FTSENT *parent, *tmp; ! 66: char *fts_path(); ! 67: ! 68: /* allocate/initialize the stream */ ! 69: if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS)))) ! 70: return(NULL); ! 71: bzero(sp, sizeof(FTS)); ! 72: sp->fts_compar = compar; ! 73: ! 74: /* ! 75: * logical walks turn on NOCHDIR; symbolic links are just too ! 76: * hard to deal with. ! 77: */ ! 78: sp->fts_options = options; ! 79: if (options & FTS_LOGICAL) ! 80: sp->fts_options |= FTS_NOCHDIR; ! 81: ! 82: /* allocate/initialize root's parent */ ! 83: if (!(parent = fts_alloc("", 0))) ! 84: goto mem1; ! 85: parent->fts_level = ROOTPARENTLEVEL; ! 86: ! 87: /* allocate/initialize root(s) */ ! 88: maxlen = -1; ! 89: for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { ! 90: if (!(p = fts_root(*argv))) ! 91: goto mem2; ! 92: if (maxlen < p->fts_namelen) ! 93: maxlen = p->fts_namelen; ! 94: /* ! 95: * if comparison routine supplied, traverse in sorted ! 96: * order; otherwise traverse in the order specified. ! 97: */ ! 98: if (compar) { ! 99: p->fts_link = root; ! 100: root = p; ! 101: p->fts_accpath = p->fts_name; ! 102: if (!(options & FTS_NOSTAT)) ! 103: p->fts_info = fts_stat(p, 0); ! 104: } else { ! 105: p->fts_link = NULL; ! 106: if (!root) ! 107: tmp = root = p; ! 108: else { ! 109: tmp->fts_link = p; ! 110: tmp = p; ! 111: } ! 112: } ! 113: p->fts_level = ROOTLEVEL; ! 114: p->fts_parent = parent; ! 115: } ! 116: if (compar && nitems > 1) ! 117: root = fts_sort(root, nitems); ! 118: ! 119: /* ! 120: * allocate a dummy pointer and make ftsread think that we've just ! 121: * finished the node before the root(s); set p->fts_info to FTS_NS ! 122: * so that everything about the "current" node is ignored. ! 123: */ ! 124: if (!(sp->fts_cur = fts_alloc("", 0))) ! 125: goto mem2; ! 126: sp->fts_cur->fts_link = root; ! 127: sp->fts_cur->fts_info = FTS_NS; ! 128: ! 129: /* start out with at least 1K+ of path space */ ! 130: if (!fts_path(MAX(maxlen, MAXPATHLEN))) ! 131: goto mem3; ! 132: ! 133: /* ! 134: * if using chdir(2), grab a file descriptor pointing to dot to insure ! 135: * that we can get back here; this could be avoided for some paths, ! 136: * but almost certainly not worth the effort. Slashes, symbolic links, ! 137: * and ".." are all fairly nasty problems. Note, if we can't get the ! 138: * descriptor we run anyway, just more slowly. ! 139: */ ! 140: if (!(options & FTS_NOCHDIR) && ! 141: (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) ! 142: sp->fts_options |= FTS_NOCHDIR; ! 143: ! 144: return(sp); ! 145: ! 146: mem3: (void)free((void *)sp->fts_cur); ! 147: mem2: fts_lfree(root); ! 148: (void)free((void *)parent); ! 149: mem1: (void)free((void *)sp); ! 150: return(NULL); ! 151: } ! 152: ! 153: static ! 154: fts_load(p) ! 155: register FTSENT *p; ! 156: { ! 157: register int len; ! 158: register char *cp; ! 159: ! 160: /* ! 161: * load the stream structure for the next traversal; set the ! 162: * accpath field specially so the chdir gets done to the right ! 163: * place and the user can access the first node. ! 164: */ ! 165: len = p->fts_pathlen = p->fts_namelen; ! 166: bcopy(p->fts_name, stream->fts_path, len + 1); ! 167: if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { ! 168: len = strlen(++cp); ! 169: bcopy(cp, p->fts_name, len + 1); ! 170: p->fts_namelen = len; ! 171: } ! 172: p->fts_accpath = p->fts_path = stream->fts_path; ! 173: } ! 174: ! 175: ftsclose(sp) ! 176: FTS *sp; ! 177: { ! 178: register FTSENT *freep, *p; ! 179: int saved_errno; ! 180: ! 181: if (sp->fts_cur) { ! 182: /* ! 183: * this still works if we haven't read anything -- the dummy ! 184: * structure points to the root list, so we step through to ! 185: * the end of the root list which has a valid parent pointer. ! 186: */ ! 187: for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { ! 188: freep = p; ! 189: p = p->fts_link ? p->fts_link : p->fts_parent; ! 190: (void)free((void *)freep); ! 191: } ! 192: (void)free((void *)p); ! 193: } ! 194: ! 195: /* free up child linked list, sort array, path buffer */ ! 196: if (sp->fts_child) ! 197: fts_lfree(sp->fts_child); ! 198: if (sp->fts_array) ! 199: (void)free((void *)sp->fts_array); ! 200: (void)free((char *)sp->fts_path); ! 201: ! 202: /* ! 203: * return to original directory, save errno if necessary; ! 204: * free up the directory buffer ! 205: */ ! 206: if (!(sp->fts_options & FTS_NOCHDIR)) { ! 207: saved_errno = fchdir(sp->fts_sd) ? errno : 0; ! 208: (void)close(sp->fts_sd); ! 209: } ! 210: ! 211: /* free up the stream pointer */ ! 212: (void)free((void *)sp); ! 213: ! 214: /* set errno and return */ ! 215: if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) { ! 216: errno = saved_errno; ! 217: return(-1); ! 218: } ! 219: return(0); ! 220: } ! 221: ! 222: FTSENT * ! 223: ftsread(sp) ! 224: register FTS *sp; ! 225: { ! 226: register FTSENT *p, *tmp; ! 227: register int instr; ! 228: register char *cp; ! 229: static int cd; ! 230: ! 231: /* if finished or unrecoverable error, return NULL */ ! 232: if (!sp->fts_cur || sp->fts_options & FTS__STOP) ! 233: return(NULL); ! 234: ! 235: /* set global stream pointer, and current node pointer */ ! 236: stream = sp; ! 237: p = sp->fts_cur; ! 238: ! 239: /* save and zero out user instructions */ ! 240: instr = p->fts_instr; ! 241: p->fts_instr = 0; ! 242: ! 243: /* if used link pointer for cycle detection, restore it */ ! 244: if (sp->fts_savelink) { ! 245: p->fts_link = sp->fts_savelink; ! 246: sp->fts_savelink = NULL; ! 247: } ! 248: ! 249: /* any type of file may be re-visited; re-stat and return */ ! 250: if (instr == FTS_AGAIN) { ! 251: p->fts_info = fts_stat(p, 0); ! 252: return(p); ! 253: } ! 254: ! 255: /* following a symbolic link */ ! 256: if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { ! 257: p->fts_info = fts_stat(p, 1); ! 258: return(p); ! 259: } ! 260: ! 261: /* directory in pre-order */ ! 262: if (p->fts_info == FTS_D) { ! 263: /* if skipped or crossed mount point, do post-order visit */ ! 264: if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV && ! 265: p->fts_statb.st_dev != sp->sdev) { ! 266: if (sp->fts_child) { ! 267: fts_lfree(sp->fts_child); ! 268: sp->fts_child = NULL; ! 269: } ! 270: p->fts_info = FTS_DP; ! 271: return(p); ! 272: } ! 273: ! 274: /* read the directory if necessary, and return first entry */ ! 275: if (sp->fts_child) ! 276: if (CHDIR(sp, p->fts_accpath)) { ! 277: fts_lfree(sp->fts_child); ! 278: p->fts_info = FTS_DNX; ! 279: } else { ! 280: p = sp->fts_child; ! 281: cp = sp->fts_path + NAPPEND(p->fts_parent); ! 282: *cp++ = '/'; ! 283: bcopy(p->fts_name, cp, p->fts_namelen + 1); ! 284: } ! 285: else { ! 286: if (!(sp->fts_child = fts_build(sp, BREAD))) ! 287: return(p); ! 288: p = sp->fts_child; ! 289: } ! 290: sp->fts_child = NULL; ! 291: return(sp->fts_cur = p); ! 292: } ! 293: ! 294: /* move to next node on this level */ ! 295: next: tmp = p; ! 296: if (p = p->fts_link) { ! 297: /* ! 298: * if root level node, set up paths and return. If not the ! 299: * first time, and it's not an absolute pathname, get back ! 300: * to starting directory. ! 301: */ ! 302: if (p->fts_level == ROOTLEVEL) { ! 303: fts_load(p); ! 304: (void)free((void *)tmp); ! 305: if (cd && ! 306: p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { ! 307: /* should never happen... */ ! 308: p->fts_path = "starting directory"; ! 309: p->fts_info = FTS_ERR; ! 310: sp->fts_options |= FTS__STOP; ! 311: return(sp->fts_cur = p); ! 312: } ! 313: cd = 1; ! 314: p->fts_info = fts_stat(p, 0); ! 315: sp->sdev = p->fts_statb.st_dev; ! 316: } else { ! 317: (void)free((void *)tmp); ! 318: ! 319: /* user may have called ftsset on node */ ! 320: if (p->fts_instr == FTS_SKIP) ! 321: goto next; ! 322: if (p->fts_instr == FTS_FOLLOW) { ! 323: p->fts_info = fts_stat(p, 1); ! 324: p->fts_instr = 0; ! 325: } ! 326: ! 327: /* fill in the paths */ ! 328: cp = sp->fts_path + NAPPEND(p->fts_parent); ! 329: *cp++ = '/'; ! 330: bcopy(p->fts_name, cp, p->fts_namelen + 1); ! 331: ! 332: /* check for directory cycles */ ! 333: if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { ! 334: sp->fts_savelink = p->fts_link; ! 335: p->fts_link = tmp; ! 336: p->fts_info = FTS_DC; ! 337: } ! 338: } ! 339: return(sp->fts_cur = p); ! 340: } ! 341: ! 342: /* move to parent */ ! 343: p = tmp->fts_parent; ! 344: (void)free((void *)tmp); ! 345: ! 346: if (p->fts_level == ROOTPARENTLEVEL) { ! 347: /* ! 348: * done; free everything up and set errno to 0 so the user ! 349: * can distinguish between error and EOF. ! 350: */ ! 351: (void)free((void *)p); ! 352: errno = 0; ! 353: return(sp->fts_cur = NULL); ! 354: } ! 355: ! 356: sp->fts_path[p->fts_pathlen] = '\0'; ! 357: if (CHDIR(sp, "..")) { ! 358: sp->fts_options |= FTS__STOP; ! 359: p->fts_info = FTS_ERR; ! 360: } else ! 361: p->fts_info = FTS_DP; ! 362: return(sp->fts_cur = p); ! 363: } ! 364: ! 365: /* ! 366: * ftsset takes the stream as an argument although it's not used in this ! 367: * implementation; it would be necessary if anyone wanted to add global ! 368: * semantics to fts using ftsset. A possible error return is allowed for ! 369: * similar reasons. ! 370: */ ! 371: /* ARGSUSED */ ! 372: ftsset(sp, p, instr) ! 373: FTS *sp; ! 374: FTSENT *p; ! 375: int instr; ! 376: { ! 377: p->fts_instr = instr; ! 378: return(0); ! 379: } ! 380: ! 381: FTSENT * ! 382: ftschildren(sp) ! 383: register FTS *sp; ! 384: { ! 385: register FTSENT *p; ! 386: int fd; ! 387: ! 388: /* ! 389: * set errno to 0 so that user can tell the difference between an ! 390: * error and a directory without entries. ! 391: */ ! 392: errno = 0; ! 393: p = sp->fts_cur; ! 394: if (p->fts_info != FTS_D || sp->fts_options & FTS__STOP) ! 395: return(NULL); ! 396: if (sp->fts_child) ! 397: fts_lfree(sp->fts_child); ! 398: ! 399: /* ! 400: * if using chdir on a relative path and called BEFORE ftsread on the ! 401: * root of a traversal, we can lose because we need to chdir into the ! 402: * subdirectory, and we don't know where the current directory is to ! 403: * get back so that the upcoming chdir by ftsread will work. ! 404: */ ! 405: if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || ! 406: sp->fts_options & FTS_NOCHDIR) ! 407: return(sp->fts_child = fts_build(sp, BCHILD)); ! 408: ! 409: if ((fd = open(".", O_RDONLY, 0)) < 0) ! 410: return(NULL); ! 411: sp->fts_child = fts_build(sp, BCHILD); ! 412: if (fchdir(fd)) ! 413: return(NULL); ! 414: (void)close(fd); ! 415: return(sp->fts_child); ! 416: } ! 417: ! 418: #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) ! 419: ! 420: FTSENT * ! 421: fts_build(sp, type) ! 422: register FTS *sp; ! 423: int type; ! 424: { ! 425: register struct dirent *dp; ! 426: register FTSENT *p, *head; ! 427: register int nitems; ! 428: DIR *dirp; ! 429: int descend, len, level, maxlen, nlinks, saved_errno; ! 430: char *cp; ! 431: ! 432: p = sp->fts_cur; ! 433: if (!(dirp = opendir(p->fts_accpath))) { ! 434: if (type == BREAD) { ! 435: errno = 0; ! 436: p->fts_info = FTS_DNR; ! 437: } ! 438: return(NULL); ! 439: } ! 440: ! 441: /* ! 442: * the real slowdown in walking the tree is the stat calls. If ! 443: * FTS_NOSTAT is set and it's a physical walk (so that symbolic ! 444: * links can't be directories), fts assumes that the number of ! 445: * subdirectories in a node is equal to the number of links to ! 446: * the parent. This allows stat calls to be skipped in any leaf ! 447: * directories and for any nodes after the directories in the ! 448: * parent node have been found. This empirically cuts the stat ! 449: * calls by about 2/3. ! 450: */ ! 451: nlinks = ! 452: sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ? ! 453: p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1; ! 454: ! 455: /* if told to descend or found links and not told not to descend. */ ! 456: if (nlinks || type == BREAD) { ! 457: if (FCHDIR(sp, dirfd(dirp))) { ! 458: if (type == BREAD) { ! 459: errno = 0; ! 460: p->fts_info = FTS_DNX; ! 461: } ! 462: (void)closedir(dirp); ! 463: return(NULL); ! 464: } ! 465: descend = 1; ! 466: } else ! 467: descend = 0; ! 468: ! 469: /* get max file name length that can be stored in current path */ ! 470: maxlen = sp->fts_pathlen - p->fts_pathlen - 1; ! 471: ! 472: cp = sp->fts_path + (len = NAPPEND(p)); ! 473: *cp++ = '/'; ! 474: ! 475: level = p->fts_level + 1; ! 476: ! 477: /* read the directory, attching each new entry to the `link' pointer */ ! 478: for (head = NULL, nitems = 0; dp = readdir(dirp);) { ! 479: if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT)) ! 480: continue; ! 481: ! 482: if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { ! 483: saved_errno = errno; ! 484: goto mem1; ! 485: } ! 486: if (dp->d_namlen > maxlen) { ! 487: if (!fts_path((int)dp->d_namlen)) { ! 488: /* quit: this stream no longer has a path */ ! 489: sp->fts_options |= FTS__STOP; ! 490: saved_errno = errno; ! 491: (void)free((void *)p); ! 492: mem1: fts_lfree(head); ! 493: if (type == BREAD) ! 494: p->fts_info = FTS_ERR; ! 495: if (descend && CHDIR(sp, "..")) { ! 496: saved_errno = errno; ! 497: sp->fts_options |= FTS__STOP; ! 498: } ! 499: errno = saved_errno; ! 500: (void)closedir(dirp); ! 501: return(NULL); ! 502: } ! 503: maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; ! 504: } ! 505: ! 506: p->fts_pathlen = len + dp->d_namlen + 1; ! 507: p->fts_accpath = ! 508: sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name; ! 509: ! 510: p->fts_parent = sp->fts_cur; ! 511: p->fts_level = level; ! 512: ! 513: if (nlinks) { ! 514: /* make sure fts_stat has a filename to stat */ ! 515: if (sp->fts_options & FTS_NOCHDIR) ! 516: bcopy(p->fts_name, cp, p->fts_namelen + 1); ! 517: p->fts_info = fts_stat(p, 0); ! 518: if (nlinks > 0 && (p->fts_info == FTS_D || ! 519: p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) ! 520: --nlinks; ! 521: } else ! 522: p->fts_info = FTS_NS; ! 523: ! 524: p->fts_link = head; ! 525: head = p; ! 526: ++nitems; ! 527: } ! 528: (void)closedir(dirp); ! 529: ! 530: /* reset the path */ ! 531: if (cp - 1 > sp->fts_path) ! 532: --cp; ! 533: *cp = '\0'; ! 534: ! 535: /* ! 536: * if descended: if were called from ftsread and didn't find anything, ! 537: * or were called from ftschildren, get back. ! 538: */ ! 539: if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { ! 540: sp->fts_options |= FTS__STOP; ! 541: p->fts_info = FTS_ERR; ! 542: return(NULL); ! 543: } ! 544: ! 545: if (!nitems) { ! 546: if (type == BREAD) ! 547: p->fts_info = FTS_DP; ! 548: return(NULL); ! 549: } ! 550: ! 551: if (sp->fts_compar && nitems > 1) ! 552: head = fts_sort(head, nitems); ! 553: ! 554: if (type == BREAD) { ! 555: *cp = '/'; ! 556: bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); ! 557: } ! 558: return(head); ! 559: } ! 560: ! 561: static short ! 562: fts_stat(p, symflag) ! 563: register FTSENT *p; ! 564: int symflag; ! 565: { ! 566: /* ! 567: * detection of symbolic links w/o targets. If FTS_FOLLOW is set, ! 568: * the symlink structure is overwritten with the stat structure of ! 569: * the target. ! 570: */ ! 571: if (stream->fts_options & FTS_LOGICAL || symflag) { ! 572: if (stat(p->fts_accpath, &p->fts_statb)) ! 573: return(symflag || !lstat(p->fts_accpath, ! 574: &p->fts_statb) ? FTS_SLNONE : FTS_ERR); ! 575: } else if (lstat(p->fts_accpath, &p->fts_statb)) ! 576: return(FTS_ERR); ! 577: ! 578: switch(p->fts_statb.st_mode&S_IFMT) { ! 579: case S_IFDIR: ! 580: return(FTS_D); ! 581: case S_IFLNK: ! 582: return(FTS_SL); ! 583: case S_IFREG: ! 584: return(FTS_F); ! 585: } ! 586: return(FTS_DEFAULT); ! 587: } ! 588: ! 589: static FTSENT * ! 590: fts_cycle(p) ! 591: register FTSENT *p; ! 592: { ! 593: register dev_t dev; ! 594: register ino_t ino; ! 595: ! 596: /* ! 597: * cycle detection is brute force; if the tree gets deep enough or ! 598: * the number of symbolic links to directories is really high ! 599: * something faster might be worthwhile. ! 600: */ ! 601: dev = p->fts_statb.st_dev; ! 602: ino = p->fts_statb.st_ino; ! 603: for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) ! 604: if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) ! 605: return(p); ! 606: return(NULL); ! 607: } ! 608: ! 609: #define R(type, nelem, ptr) \ ! 610: (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) ! 611: ! 612: static FTSENT * ! 613: fts_sort(head, nitems) ! 614: FTSENT *head; ! 615: register int nitems; ! 616: { ! 617: register FTSENT **ap, *p; ! 618: ! 619: /* ! 620: * construct an array of pointers to the structures and call qsort(3). ! 621: * Reassemble the array in the order returned by qsort. If unable to ! 622: * sort for memory reasons, return the directory entries in their ! 623: * current order. Allocate enough space for the current needs plus ! 624: * 40 so we don't realloc one entry at a time. ! 625: */ ! 626: if (nitems > stream->fts_nitems) { ! 627: stream->fts_nitems = nitems + 40; ! 628: if (!(stream->fts_array = ! 629: R(FTSENT *, stream->fts_nitems, stream->fts_array))) { ! 630: stream->fts_nitems = 0; ! 631: return(head); ! 632: } ! 633: } ! 634: for (ap = stream->fts_array, p = head; p; p = p->fts_link) ! 635: *ap++ = p; ! 636: qsort((void *)stream->fts_array, nitems, sizeof(FTSENT *), ! 637: stream->fts_compar); ! 638: for (head = *(ap = stream->fts_array); --nitems; ++ap) ! 639: ap[0]->fts_link = ap[1]; ! 640: ap[0]->fts_link = NULL; ! 641: return(head); ! 642: } ! 643: ! 644: static FTSENT * ! 645: fts_alloc(name, len) ! 646: char *name; ! 647: register int len; ! 648: { ! 649: register FTSENT *p; ! 650: ! 651: /* ! 652: * variable sized structures; the name is the last element so ! 653: * allocate enough extra space after the structure to hold it. ! 654: */ ! 655: if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) ! 656: return(NULL); ! 657: bcopy(name, p->fts_name, len + 1); ! 658: p->fts_namelen = len; ! 659: p->fts_path = stream->fts_path; ! 660: p->fts_instr = 0; ! 661: p->fts_local.number = 0; ! 662: p->fts_local.pointer = NULL; ! 663: return(p); ! 664: } ! 665: ! 666: static ! 667: fts_lfree(head) ! 668: register FTSENT *head; ! 669: { ! 670: register FTSENT *p; ! 671: ! 672: while (p = head) { ! 673: head = head->fts_link; ! 674: (void)free((void *)p); ! 675: } ! 676: } ! 677: ! 678: /* ! 679: * allow essentially unlimited paths; certain programs (find, remove, ls) ! 680: * need to work on any tree. Most systems will allow creation of paths ! 681: * much longer than MAXPATHLEN, even though the kernel won't resolve them. ! 682: * Add an extra 128 bytes to the requested size so that we don't realloc ! 683: * the path 2 bytes at a time. ! 684: */ ! 685: static char * ! 686: fts_path(size) ! 687: int size; ! 688: { ! 689: stream->fts_pathlen += size + 128; ! 690: return(stream->fts_path = ! 691: R(char, stream->fts_pathlen, stream->fts_path)); ! 692: } ! 693: ! 694: static FTSENT * ! 695: fts_root(name) ! 696: register char *name; ! 697: { ! 698: register char *cp; ! 699: ! 700: /* ! 701: * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what ! 702: * /a/b/ really is, they don't talk about what a null path component ! 703: * resolves to. This hopefully does what the user intended. Don't ! 704: * allow null pathnames. ! 705: */ ! 706: for (cp = name; *cp; ++cp); ! 707: if (cp == name) { ! 708: errno = ENOENT; ! 709: return(NULL); ! 710: } ! 711: while (--cp > name && *cp == '/'); ! 712: *++cp = '\0'; ! 713: return(fts_alloc(name, cp - name)); ! 714: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.