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