1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * Phong Vo <kpv@research.att.com> * 20*4887Schin * * 21*4887Schin ***********************************************************************/ 22*4887Schin #pragma prototyped 23*4887Schin /* 24*4887Schin * Phong Vo 25*4887Schin * Glenn Fowler 26*4887Schin * AT&T Research 27*4887Schin * 28*4887Schin * fts implementation unwound from the kpv ftwalk() of 1988-10-30 29*4887Schin */ 30*4887Schin 31*4887Schin #include <ast.h> 32*4887Schin #include <ast_dir.h> 33*4887Schin #include <error.h> 34*4887Schin #include <fs3d.h> 35*4887Schin 36*4887Schin struct Ftsent; 37*4887Schin 38*4887Schin typedef int (*Compar_f)(struct Ftsent* const*, struct Ftsent* const*); 39*4887Schin typedef int (*Stat_f)(const char*, struct stat*); 40*4887Schin 41*4887Schin #define _FTS_PRIVATE_ \ 42*4887Schin FTSENT* parent; /* top parent */ \ 43*4887Schin FTSENT* todo; /* todo list */ \ 44*4887Schin FTSENT* top; /* top element */ \ 45*4887Schin FTSENT* root; \ 46*4887Schin FTSENT* bot; /* bottom element */ \ 47*4887Schin FTSENT* free; /* free element */ \ 48*4887Schin FTSENT* diroot; \ 49*4887Schin FTSENT* curdir; \ 50*4887Schin FTSENT* current; /* current element */ \ 51*4887Schin FTSENT* previous; /* previous current */ \ 52*4887Schin FTSENT* dotdot; \ 53*4887Schin FTSENT* link; /* real current fts_link*/ \ 54*4887Schin FTSENT* pwd; /* pwd parent */ \ 55*4887Schin DIR* dir; /* current dir stream */ \ 56*4887Schin Compar_f comparf; /* node comparison func */ \ 57*4887Schin int baselen; /* current strlen(base) */ \ 58*4887Schin int cd; /* chdir status */ \ 59*4887Schin int cpname; \ 60*4887Schin int flags; /* fts_open() flags */ \ 61*4887Schin int homesize; /* sizeof(home) */ \ 62*4887Schin int nd; \ 63*4887Schin unsigned char children; \ 64*4887Schin unsigned char fs3d; \ 65*4887Schin unsigned char nostat; \ 66*4887Schin unsigned char state; /* fts_read() state */ \ 67*4887Schin char* base; /* basename in path */ \ 68*4887Schin char* name; \ 69*4887Schin char* path; /* path workspace */ \ 70*4887Schin char* home; /* home/path buffer */ \ 71*4887Schin char* endbase; /* space to build paths */ \ 72*4887Schin char* endbuf; /* space to build paths */ \ 73*4887Schin char* pad; 74*4887Schin 75*4887Schin /* 76*4887Schin * NOTE: <ftwalk.h> relies on status and statb being the first two elements 77*4887Schin */ 78*4887Schin 79*4887Schin #define _FTSENT_PRIVATE_ \ 80*4887Schin short status; /* internal status */ \ 81*4887Schin struct stat statb; /* fts_statp data */ \ 82*4887Schin int nd; /* popdir() count */ \ 83*4887Schin FTSENT* left; /* left child */ \ 84*4887Schin FTSENT* right; /* right child */ \ 85*4887Schin FTSENT* pwd; /* pwd parent */ \ 86*4887Schin FTSENT* stack; /* getlist() stack */ \ 87*4887Schin FTS* fts; /* for fts verification */ \ 88*4887Schin long nlink; /* FTS_D link count */ \ 89*4887Schin unsigned char must; /* must stat */ \ 90*4887Schin unsigned char type; /* DT_* type */ \ 91*4887Schin unsigned char symlink; /* originally a symlink */ \ 92*4887Schin char name[sizeof(int)]; /* fts_name data */ 93*4887Schin 94*4887Schin #include <fts.h> 95*4887Schin 96*4887Schin #ifndef ENOSYS 97*4887Schin #define ENOSYS EINVAL 98*4887Schin #endif 99*4887Schin 100*4887Schin 101*4887Schin #if MAXNAMLEN > 16 102*4887Schin #define MINNAME 32 103*4887Schin #else 104*4887Schin #define MINNAME 16 105*4887Schin #endif 106*4887Schin 107*4887Schin #define drop(p,f) (((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free)) 108*4887Schin 109*4887Schin #define ACCESS(p,f) ((p)->cd==0?(f)->fts_name:(f)->fts_path) 110*4887Schin #define PATH(f,p,l) ((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p)) 111*4887Schin #define SAME(one,two) ((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev) 112*4887Schin #define SKIPLINK(p,f) ((f)->fts_parent->nlink == 0) 113*4887Schin 114*4887Schin #ifdef D_TYPE 115*4887Schin #define ISTYPE(f,t) ((f)->type == (t)) 116*4887Schin #define TYPE(f,t) ((f)->type = (t)) 117*4887Schin #define SKIP(p,f) ((f)->fts_parent->must == 0 && (((f)->type == DT_UNKNOWN) ? SKIPLINK(p,f) : ((f)->type != DT_DIR && ((f)->type != DT_LNK || ((p)->flags & FTS_PHYSICAL))))) 118*4887Schin #else 119*4887Schin #undef DT_UNKNOWN 120*4887Schin #define DT_UNKNOWN 0 121*4887Schin #undef DT_LNK 122*4887Schin #define DT_LNK 1 123*4887Schin #define ISTYPE(f,t) ((t)==DT_UNKNOWN) 124*4887Schin #define TYPE(f,d) 125*4887Schin #define SKIP(p,f) ((f)->fts_parent->must == 0 && SKIPLINK(p,f)) 126*4887Schin #endif 127*4887Schin 128*4887Schin #ifndef D_FILENO 129*4887Schin #define D_FILENO(d) (1) 130*4887Schin #endif 131*4887Schin 132*4887Schin /* 133*4887Schin * NOTE: a malicious dir rename() could change .. underfoot so we 134*4887Schin * must always verify; undef verify to enable the unsafe code 135*4887Schin */ 136*4887Schin 137*4887Schin #define verify 1 138*4887Schin 139*4887Schin /* 140*4887Schin * FTS_NOSTAT requires a dir with 141*4887Schin * D_TYPE(&dirent_t)!=DT_UNKNOWN 142*4887Schin * OR 143*4887Schin * st_nlink>=2 144*4887Schin */ 145*4887Schin 146*4887Schin #define FTS_children_resume 1 147*4887Schin #define FTS_children_return 2 148*4887Schin #define FTS_error 3 149*4887Schin #define FTS_popstack 4 150*4887Schin #define FTS_popstack_resume 5 151*4887Schin #define FTS_popstack_return 6 152*4887Schin #define FTS_preorder 7 153*4887Schin #define FTS_preorder_resume 8 154*4887Schin #define FTS_preorder_return 9 155*4887Schin #define FTS_readdir 10 156*4887Schin #define FTS_terminal 11 157*4887Schin #define FTS_todo 12 158*4887Schin #define FTS_top_return 13 159*4887Schin 160*4887Schin typedef int (*Notify_f)(FTS*, FTSENT*, void*); 161*4887Schin 162*4887Schin typedef struct Notify_s 163*4887Schin { 164*4887Schin struct Notify_s* next; 165*4887Schin Notify_f notifyf; 166*4887Schin void* context; 167*4887Schin } Notify_t; 168*4887Schin 169*4887Schin static Notify_t* notify; 170*4887Schin 171*4887Schin /* 172*4887Schin * allocate an FTSENT node 173*4887Schin */ 174*4887Schin 175*4887Schin static FTSENT* 176*4887Schin node(FTS* fts, FTSENT* parent, register char* name, register int namelen) 177*4887Schin { 178*4887Schin register FTSENT* f; 179*4887Schin register int n; 180*4887Schin 181*4887Schin if (fts->free && namelen < MINNAME) 182*4887Schin { 183*4887Schin f = fts->free; 184*4887Schin fts->free = f->fts_link; 185*4887Schin } 186*4887Schin else 187*4887Schin { 188*4887Schin n = (namelen < MINNAME ? MINNAME : namelen + 1) - sizeof(int); 189*4887Schin if (!(f = newof(0, FTSENT, 1, n))) 190*4887Schin { 191*4887Schin fts->fts_errno = errno; 192*4887Schin fts->state = FTS_error; 193*4887Schin return 0; 194*4887Schin } 195*4887Schin f->fts = fts; 196*4887Schin } 197*4887Schin TYPE(f, DT_UNKNOWN); 198*4887Schin f->status = 0; 199*4887Schin f->symlink = 0; 200*4887Schin f->fts_level = (f->fts_parent = parent)->fts_level + 1; 201*4887Schin f->fts_link = 0; 202*4887Schin f->fts_pointer = 0; 203*4887Schin f->fts_number = 0; 204*4887Schin f->fts_errno = 0; 205*4887Schin f->fts_namelen = namelen; 206*4887Schin f->fts_name = f->name; 207*4887Schin f->fts_statp = &f->statb; 208*4887Schin memcpy(f->fts_name, name, namelen + 1); 209*4887Schin return f; 210*4887Schin } 211*4887Schin 212*4887Schin /* 213*4887Schin * compare directories by device/inode 214*4887Schin */ 215*4887Schin 216*4887Schin static int 217*4887Schin statcmp(FTSENT* const* pf1, FTSENT* const* pf2) 218*4887Schin { 219*4887Schin register const FTSENT* f1 = *pf1; 220*4887Schin register const FTSENT* f2 = *pf2; 221*4887Schin 222*4887Schin if (f1->statb.st_ino < f2->statb.st_ino) 223*4887Schin return -1; 224*4887Schin if (f1->statb.st_ino > f2->statb.st_ino) 225*4887Schin return 1; 226*4887Schin if (f1->statb.st_dev < f2->statb.st_dev) 227*4887Schin return -1; 228*4887Schin if (f1->statb.st_dev > f2->statb.st_dev) 229*4887Schin return 1; 230*4887Schin 231*4887Schin /* 232*4887Schin * hack for NFS where <dev,ino> may not uniquely identify objects 233*4887Schin */ 234*4887Schin 235*4887Schin if (f1->statb.st_mtime < f2->statb.st_mtime) 236*4887Schin return -1; 237*4887Schin if (f1->statb.st_mtime > f2->statb.st_mtime) 238*4887Schin return 1; 239*4887Schin return 0; 240*4887Schin } 241*4887Schin 242*4887Schin /* 243*4887Schin * search trees with top-down splaying (a la Tarjan and Sleator) 244*4887Schin * when used for insertion sort, this implements a stable sort 245*4887Schin */ 246*4887Schin 247*4887Schin #define RROTATE(r) (t = r->left, r->left = t->right, t->right = r, r = t) 248*4887Schin #define LROTATE(r) (t = r->right, r->right = t->left, t->left = r, r = t) 249*4887Schin 250*4887Schin static FTSENT* 251*4887Schin search(FTSENT* e, FTSENT* root, int(*comparf)(FTSENT* const*, FTSENT* const*), int insert) 252*4887Schin { 253*4887Schin register int cmp; 254*4887Schin register FTSENT* t; 255*4887Schin register FTSENT* left; 256*4887Schin register FTSENT* right; 257*4887Schin register FTSENT* lroot; 258*4887Schin register FTSENT* rroot; 259*4887Schin 260*4887Schin left = right = lroot = rroot = 0; 261*4887Schin while (root) 262*4887Schin { 263*4887Schin if (!(cmp = (*comparf)(&e, &root)) && !insert) 264*4887Schin break; 265*4887Schin if (cmp < 0) 266*4887Schin { 267*4887Schin /* 268*4887Schin * this is the left zig-zig case 269*4887Schin */ 270*4887Schin 271*4887Schin if (root->left && (cmp = (*comparf)(&e, &root->left)) <= 0) 272*4887Schin { 273*4887Schin RROTATE(root); 274*4887Schin if (!cmp && !insert) 275*4887Schin break; 276*4887Schin } 277*4887Schin 278*4887Schin /* 279*4887Schin * stick all things > e to the right tree 280*4887Schin */ 281*4887Schin 282*4887Schin if (right) 283*4887Schin right->left = root; 284*4887Schin else 285*4887Schin rroot = root; 286*4887Schin right = root; 287*4887Schin root = root->left; 288*4887Schin right->left = 0; 289*4887Schin } 290*4887Schin else 291*4887Schin { 292*4887Schin /* 293*4887Schin * this is the right zig-zig case 294*4887Schin */ 295*4887Schin 296*4887Schin if (root->right && (cmp = (*comparf)(&e, &root->right)) >= 0) 297*4887Schin { 298*4887Schin LROTATE(root); 299*4887Schin if (!cmp && !insert) 300*4887Schin break; 301*4887Schin } 302*4887Schin 303*4887Schin /* 304*4887Schin * stick all things <= e to the left tree 305*4887Schin */ 306*4887Schin 307*4887Schin if (left) 308*4887Schin left->right = root; 309*4887Schin else 310*4887Schin lroot = root; 311*4887Schin left = root; 312*4887Schin root = root->right; 313*4887Schin left->right = 0; 314*4887Schin } 315*4887Schin } 316*4887Schin if (!root) 317*4887Schin root = e; 318*4887Schin else 319*4887Schin { 320*4887Schin if (right) 321*4887Schin right->left = root->right; 322*4887Schin else 323*4887Schin rroot = root->right; 324*4887Schin if (left) 325*4887Schin left->right = root->left; 326*4887Schin else 327*4887Schin lroot = root->left; 328*4887Schin } 329*4887Schin root->left = lroot; 330*4887Schin root->right = rroot; 331*4887Schin return root; 332*4887Schin } 333*4887Schin 334*4887Schin /* 335*4887Schin * delete the root element from the tree 336*4887Schin */ 337*4887Schin 338*4887Schin static FTSENT* 339*4887Schin deleteroot(register FTSENT* root) 340*4887Schin { 341*4887Schin register FTSENT* t; 342*4887Schin register FTSENT* left; 343*4887Schin register FTSENT* right; 344*4887Schin 345*4887Schin right = root->right; 346*4887Schin if (!(left = root->left)) 347*4887Schin root = right; 348*4887Schin else 349*4887Schin { 350*4887Schin while (left->right) 351*4887Schin LROTATE(left); 352*4887Schin left->right = right; 353*4887Schin root = left; 354*4887Schin } 355*4887Schin return root; 356*4887Schin } 357*4887Schin 358*4887Schin /* 359*4887Schin * generate ordered fts_link list from binary tree at root 360*4887Schin * FTSENT.stack instead of recursion to avoid blowing the real 361*4887Schin * stack on big directories 362*4887Schin */ 363*4887Schin 364*4887Schin static void 365*4887Schin getlist(register FTSENT** top, register FTSENT** bot, register FTSENT* root) 366*4887Schin { 367*4887Schin register FTSENT* stack = 0; 368*4887Schin 369*4887Schin for (;;) 370*4887Schin { 371*4887Schin if (root->left) 372*4887Schin { 373*4887Schin root->stack = stack; 374*4887Schin stack = root; 375*4887Schin root = root->left; 376*4887Schin } 377*4887Schin else 378*4887Schin { 379*4887Schin for (;;) 380*4887Schin { 381*4887Schin if (*top) 382*4887Schin *bot = (*bot)->fts_link = root; 383*4887Schin else 384*4887Schin *bot = *top = root; 385*4887Schin if (root->right) 386*4887Schin { 387*4887Schin root = root->right; 388*4887Schin break; 389*4887Schin } 390*4887Schin if (!(root = stack)) 391*4887Schin return; 392*4887Schin stack = stack->stack; 393*4887Schin } 394*4887Schin } 395*4887Schin } 396*4887Schin } 397*4887Schin 398*4887Schin /* 399*4887Schin * set directory when curdir is lost in space 400*4887Schin */ 401*4887Schin 402*4887Schin static int 403*4887Schin setdir(register char* home, register char* path) 404*4887Schin { 405*4887Schin register int cdrv; 406*4887Schin 407*4887Schin if (path[0] == '/') 408*4887Schin cdrv = pathcd(path, NiL); 409*4887Schin else 410*4887Schin { 411*4887Schin /* 412*4887Schin * note that path and home are in the same buffer 413*4887Schin */ 414*4887Schin 415*4887Schin path[-1] = '/'; 416*4887Schin cdrv = pathcd(home, NiL); 417*4887Schin path[-1] = 0; 418*4887Schin } 419*4887Schin if (cdrv < 0) 420*4887Schin pathcd(home, NiL); 421*4887Schin return cdrv; 422*4887Schin } 423*4887Schin 424*4887Schin /* 425*4887Schin * set to parent dir 426*4887Schin */ 427*4887Schin 428*4887Schin static int 429*4887Schin setpdir(register char* home, register char* path, register char* base) 430*4887Schin { 431*4887Schin register int c; 432*4887Schin register int cdrv; 433*4887Schin 434*4887Schin if (base > path) 435*4887Schin { 436*4887Schin c = base[0]; 437*4887Schin base[0] = 0; 438*4887Schin cdrv = setdir(home, path); 439*4887Schin base[0] = c; 440*4887Schin } 441*4887Schin else 442*4887Schin cdrv = pathcd(home, NiL); 443*4887Schin return cdrv; 444*4887Schin } 445*4887Schin 446*4887Schin /* 447*4887Schin * pop a set of directories 448*4887Schin */ 449*4887Schin static int 450*4887Schin popdirs(FTS* fts) 451*4887Schin { 452*4887Schin register FTSENT*f; 453*4887Schin register char* s; 454*4887Schin register char* e; 455*4887Schin #ifndef verify 456*4887Schin register int verify; 457*4887Schin #endif 458*4887Schin struct stat sb; 459*4887Schin char buf[PATH_MAX]; 460*4887Schin 461*4887Schin if (!(f = fts->curdir) || f->fts_level < 0) 462*4887Schin return -1; 463*4887Schin e = buf + sizeof(buf) - 4; 464*4887Schin #ifndef verify 465*4887Schin verify = 0; 466*4887Schin #endif 467*4887Schin while (fts->nd > 0) 468*4887Schin { 469*4887Schin for (s = buf; s < e && fts->nd > 0; fts->nd--) 470*4887Schin { 471*4887Schin if (fts->pwd) 472*4887Schin { 473*4887Schin #ifndef verify 474*4887Schin verify |= fts->pwd->symlink; 475*4887Schin #endif 476*4887Schin fts->pwd = fts->pwd->pwd; 477*4887Schin } 478*4887Schin *s++ = '.'; 479*4887Schin *s++ = '.'; 480*4887Schin *s++ = '/'; 481*4887Schin } 482*4887Schin *s = 0; 483*4887Schin if (chdir(buf)) 484*4887Schin return -1; 485*4887Schin } 486*4887Schin return (verify && (stat(".", &sb) < 0 || !SAME(&sb, f->fts_statp))) ? -1 : 0; 487*4887Schin } 488*4887Schin 489*4887Schin /* 490*4887Schin * initialize st from path and fts_info from st 491*4887Schin */ 492*4887Schin 493*4887Schin static int 494*4887Schin info(FTS* fts, register FTSENT* f, const char* path, struct stat* sp, int flags) 495*4887Schin { 496*4887Schin if (path) 497*4887Schin { 498*4887Schin #ifdef S_ISLNK 499*4887Schin if (!f->symlink && (ISTYPE(f, DT_UNKNOWN) || ISTYPE(f, DT_LNK))) 500*4887Schin { 501*4887Schin if (lstat(path, sp) < 0) 502*4887Schin goto bad; 503*4887Schin } 504*4887Schin else 505*4887Schin #endif 506*4887Schin if (stat(path, sp) < 0) 507*4887Schin goto bad; 508*4887Schin } 509*4887Schin #ifdef S_ISLNK 510*4887Schin again: 511*4887Schin #endif 512*4887Schin if (S_ISDIR(sp->st_mode)) 513*4887Schin { 514*4887Schin if ((flags & FTS_NOSTAT) && !fts->fs3d) 515*4887Schin { 516*4887Schin f->fts_parent->nlink--; 517*4887Schin #ifdef D_TYPE 518*4887Schin f->must = 0; 519*4887Schin if ((f->nlink = sp->st_nlink) < 2) 520*4887Schin f->nlink = 2; 521*4887Schin #else 522*4887Schin if ((f->nlink = sp->st_nlink) >= 2) 523*4887Schin f->must = 1; 524*4887Schin else 525*4887Schin f->must = 2; 526*4887Schin #endif 527*4887Schin } 528*4887Schin else 529*4887Schin f->must = 2; 530*4887Schin TYPE(f, DT_DIR); 531*4887Schin f->fts_info = FTS_D; 532*4887Schin } 533*4887Schin #ifdef S_ISLNK 534*4887Schin else if (S_ISLNK((sp)->st_mode)) 535*4887Schin { 536*4887Schin struct stat sb; 537*4887Schin 538*4887Schin f->symlink = 1; 539*4887Schin if (!(flags & FTS_PHYSICAL) && stat(path, &sb) >= 0) 540*4887Schin { 541*4887Schin *sp = sb; 542*4887Schin flags = FTS_PHYSICAL; 543*4887Schin goto again; 544*4887Schin } 545*4887Schin TYPE(f, DT_LNK); 546*4887Schin f->fts_info = FTS_SL; 547*4887Schin } 548*4887Schin #endif 549*4887Schin else 550*4887Schin { 551*4887Schin TYPE(f, DT_REG); 552*4887Schin f->fts_info = FTS_F; 553*4887Schin } 554*4887Schin return 0; 555*4887Schin bad: 556*4887Schin TYPE(f, DT_UNKNOWN); 557*4887Schin f->fts_info = FTS_NS; 558*4887Schin return -1; 559*4887Schin } 560*4887Schin 561*4887Schin /* 562*4887Schin * get top list of elements to process 563*4887Schin */ 564*4887Schin 565*4887Schin static FTSENT* 566*4887Schin toplist(FTS* fts, register char* const* pathnames) 567*4887Schin { 568*4887Schin register char* path; 569*4887Schin register struct stat* sb; 570*4887Schin register FTSENT* f; 571*4887Schin register FTSENT* root; 572*4887Schin int physical; 573*4887Schin int metaphysical; 574*4887Schin char* s; 575*4887Schin FTSENT* top; 576*4887Schin FTSENT* bot; 577*4887Schin struct stat st; 578*4887Schin 579*4887Schin if (fts->flags & FTS_NOSEEDOTDIR) 580*4887Schin fts->flags &= ~FTS_SEEDOTDIR; 581*4887Schin physical = (fts->flags & FTS_PHYSICAL); 582*4887Schin metaphysical = (fts->flags & (FTS_META|FTS_PHYSICAL)) == (FTS_META|FTS_PHYSICAL); 583*4887Schin top = bot = root = 0; 584*4887Schin while (path = *pathnames++) 585*4887Schin { 586*4887Schin /* 587*4887Schin * make elements 588*4887Schin */ 589*4887Schin 590*4887Schin if (!(f = node(fts, fts->parent, path, strlen(path)))) 591*4887Schin break; 592*4887Schin path = f->fts_name; 593*4887Schin if (!physical) 594*4887Schin f->fts_namelen = (fts->flags & FTS_SEEDOTDIR) ? strlen(path) : (pathcanon(path, 0) - path); 595*4887Schin else if (*path != '.') 596*4887Schin { 597*4887Schin f->fts_namelen = strlen(path); 598*4887Schin fts->flags |= FTS_SEEDOTDIR; 599*4887Schin } 600*4887Schin else 601*4887Schin { 602*4887Schin if (fts->flags & FTS_NOSEEDOTDIR) 603*4887Schin { 604*4887Schin fts->flags &= ~FTS_SEEDOTDIR; 605*4887Schin s = path; 606*4887Schin while (*s++ == '.' && *s++ == '/') 607*4887Schin { 608*4887Schin while (*s == '/') 609*4887Schin s++; 610*4887Schin if (!*s) 611*4887Schin break; 612*4887Schin path = f->fts_name; 613*4887Schin while (*path++ = *s++); 614*4887Schin path = f->fts_name; 615*4887Schin } 616*4887Schin } 617*4887Schin else 618*4887Schin fts->flags |= FTS_SEEDOTDIR; 619*4887Schin for (s = path + strlen(path); s > path && *(s - 1) == '/'; s--); 620*4887Schin *s = 0; 621*4887Schin f->fts_namelen = s - path; 622*4887Schin } 623*4887Schin sb = f->fts_statp; 624*4887Schin if (!*path) 625*4887Schin { 626*4887Schin errno = ENOENT; 627*4887Schin f->fts_info = FTS_NS; 628*4887Schin } 629*4887Schin else 630*4887Schin info(fts, f, path, sb, fts->flags); 631*4887Schin #ifdef S_ISLNK 632*4887Schin 633*4887Schin /* 634*4887Schin * don't let any standards committee get 635*4887Schin * away with calling your idea a hack 636*4887Schin */ 637*4887Schin 638*4887Schin if (metaphysical && f->fts_info == FTS_SL && stat(path, &st) >= 0) 639*4887Schin { 640*4887Schin *sb = st; 641*4887Schin info(fts, f, NiL, sb, 0); 642*4887Schin } 643*4887Schin #endif 644*4887Schin if (fts->comparf) 645*4887Schin root = search(f, root, fts->comparf, 1); 646*4887Schin else if (bot) 647*4887Schin { 648*4887Schin bot->fts_link = f; 649*4887Schin bot = f; 650*4887Schin } 651*4887Schin else 652*4887Schin top = bot = f; 653*4887Schin } 654*4887Schin if (fts->comparf) 655*4887Schin getlist(&top, &bot, root); 656*4887Schin return top; 657*4887Schin } 658*4887Schin 659*4887Schin /* 660*4887Schin * resize the path buffer 661*4887Schin * note that free() is not used because we may need to chdir(fts->home) 662*4887Schin * if there isn't enough space to continue 663*4887Schin */ 664*4887Schin 665*4887Schin static int 666*4887Schin resize(register FTS* fts, int inc) 667*4887Schin { 668*4887Schin register char* old; 669*4887Schin register char* newp; 670*4887Schin register int n_old; 671*4887Schin 672*4887Schin /* 673*4887Schin * add space for "/." used in testing FTS_DNX 674*4887Schin */ 675*4887Schin 676*4887Schin n_old = fts->homesize; 677*4887Schin fts->homesize = ((fts->homesize + inc + 4) / PATH_MAX + 1) * PATH_MAX; 678*4887Schin if (!(newp = newof(0, char, fts->homesize, 0))) 679*4887Schin { 680*4887Schin fts->fts_errno = errno; 681*4887Schin fts->state = FTS_error; 682*4887Schin return -1; 683*4887Schin } 684*4887Schin old = fts->home; 685*4887Schin fts->home = newp; 686*4887Schin memcpy(newp, old, n_old); 687*4887Schin if (fts->endbuf) 688*4887Schin fts->endbuf = newp + fts->homesize - 4; 689*4887Schin if (fts->path) 690*4887Schin fts->path = newp + (fts->path - old); 691*4887Schin if (fts->base) 692*4887Schin fts->base = newp + (fts->base - old); 693*4887Schin free(old); 694*4887Schin return 0; 695*4887Schin } 696*4887Schin 697*4887Schin /* 698*4887Schin * open a new fts stream on pathnames 699*4887Schin */ 700*4887Schin 701*4887Schin FTS* 702*4887Schin fts_open(char* const* pathnames, int flags, int (*comparf)(FTSENT* const*, FTSENT* const*)) 703*4887Schin { 704*4887Schin register FTS* fts; 705*4887Schin 706*4887Schin if (!(fts = newof(0, FTS, 1, sizeof(FTSENT)))) 707*4887Schin return 0; 708*4887Schin fts->flags = flags; 709*4887Schin fts->cd = (flags & FTS_NOCHDIR) ? 1 : -1; 710*4887Schin fts->comparf = comparf; 711*4887Schin fts->fs3d = fs3d(FS3D_TEST); 712*4887Schin 713*4887Schin /* 714*4887Schin * set up the path work buffer 715*4887Schin */ 716*4887Schin 717*4887Schin fts->homesize = 2 * PATH_MAX; 718*4887Schin for (;;) 719*4887Schin { 720*4887Schin if (!(fts->home = newof(fts->home, char, fts->homesize, 0))) 721*4887Schin { 722*4887Schin free(fts); 723*4887Schin return 0; 724*4887Schin } 725*4887Schin if (fts->cd > 0 || getcwd(fts->home, fts->homesize)) 726*4887Schin break; 727*4887Schin if (errno == ERANGE) 728*4887Schin fts->homesize += PATH_MAX; 729*4887Schin else 730*4887Schin fts->cd = 1; 731*4887Schin } 732*4887Schin fts->endbuf = fts->home + fts->homesize - 4; 733*4887Schin 734*4887Schin /* 735*4887Schin * initialize the tippity-top 736*4887Schin */ 737*4887Schin 738*4887Schin fts->parent = (FTSENT*)(fts + 1); 739*4887Schin fts->parent->fts_info = FTS_D; 740*4887Schin memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2); 741*4887Schin fts->parent->fts_level = -1; 742*4887Schin fts->parent->fts_statp = &fts->parent->statb; 743*4887Schin fts->parent->must = 2; 744*4887Schin fts->parent->type = DT_UNKNOWN; 745*4887Schin fts->path = fts->home + strlen(fts->home) + 1; 746*4887Schin 747*4887Schin /* 748*4887Schin * make the list of top elements 749*4887Schin */ 750*4887Schin 751*4887Schin if (!pathnames || (flags & FTS_ONEPATH) || !*pathnames) 752*4887Schin { 753*4887Schin char* v[2]; 754*4887Schin 755*4887Schin v[0] = pathnames && (flags & FTS_ONEPATH) ? (char*)pathnames : "."; 756*4887Schin v[1] = 0; 757*4887Schin fts->todo = toplist(fts, v); 758*4887Schin } 759*4887Schin else 760*4887Schin fts->todo = toplist(fts, pathnames); 761*4887Schin if (!fts->todo || fts->todo->fts_info == FTS_NS && !fts->todo->fts_link) 762*4887Schin { 763*4887Schin fts_close(fts); 764*4887Schin return 0; 765*4887Schin } 766*4887Schin return fts; 767*4887Schin } 768*4887Schin 769*4887Schin /* 770*4887Schin * return the next FTS entry 771*4887Schin */ 772*4887Schin 773*4887Schin FTSENT* 774*4887Schin fts_read(register FTS* fts) 775*4887Schin { 776*4887Schin register char* s; 777*4887Schin register int n; 778*4887Schin register FTSENT* f; 779*4887Schin struct dirent* d; 780*4887Schin int i; 781*4887Schin FTSENT* t; 782*4887Schin Notify_t* p; 783*4887Schin #ifdef verify 784*4887Schin struct stat sb; 785*4887Schin #endif 786*4887Schin 787*4887Schin for (;;) switch (fts->state) 788*4887Schin { 789*4887Schin 790*4887Schin case FTS_top_return: 791*4887Schin 792*4887Schin f = fts->todo; 793*4887Schin t = 0; 794*4887Schin while (f) 795*4887Schin if (f->status == FTS_SKIP) 796*4887Schin { 797*4887Schin if (t) 798*4887Schin { 799*4887Schin t->fts_link = f->fts_link; 800*4887Schin drop(fts, f); 801*4887Schin f = t->fts_link; 802*4887Schin } 803*4887Schin else 804*4887Schin { 805*4887Schin fts->todo = f->fts_link; 806*4887Schin drop(fts, f); 807*4887Schin f = fts->todo; 808*4887Schin } 809*4887Schin } 810*4887Schin else 811*4887Schin { 812*4887Schin t = f; 813*4887Schin f = f->fts_link; 814*4887Schin } 815*4887Schin /*FALLTHROUGH*/ 816*4887Schin 817*4887Schin case 0: 818*4887Schin 819*4887Schin if (!(f = fts->todo)) 820*4887Schin return 0; 821*4887Schin /*FALLTHROUGH*/ 822*4887Schin 823*4887Schin case FTS_todo: 824*4887Schin 825*4887Schin /* 826*4887Schin * process the top object on the stack 827*4887Schin */ 828*4887Schin 829*4887Schin fts->root = fts->top = fts->bot = 0; 830*4887Schin 831*4887Schin /* 832*4887Schin * initialize the top level 833*4887Schin */ 834*4887Schin 835*4887Schin if (f->fts_level == 0) 836*4887Schin { 837*4887Schin fts->parent->fts_number = f->fts_number; 838*4887Schin fts->parent->fts_pointer = f->fts_pointer; 839*4887Schin fts->parent->fts_statp = f->fts_statp; 840*4887Schin fts->parent->statb = *f->fts_statp; 841*4887Schin f->fts_parent = fts->parent; 842*4887Schin fts->diroot = 0; 843*4887Schin if (fts->cd == 0) 844*4887Schin pathcd(fts->home, NiL); 845*4887Schin else if (fts->cd < 0) 846*4887Schin fts->cd = 0; 847*4887Schin fts->pwd = f->fts_parent; 848*4887Schin fts->curdir = fts->cd ? 0 : f->fts_parent; 849*4887Schin *(fts->base = fts->path) = 0; 850*4887Schin } 851*4887Schin 852*4887Schin /* 853*4887Schin * chdir to parent if asked for 854*4887Schin */ 855*4887Schin 856*4887Schin if (fts->cd < 0) 857*4887Schin { 858*4887Schin fts->cd = setdir(fts->home, fts->path); 859*4887Schin fts->pwd = f->fts_parent; 860*4887Schin fts->curdir = fts->cd ? 0 : f->fts_parent; 861*4887Schin } 862*4887Schin 863*4887Schin /* 864*4887Schin * add object's name to the path 865*4887Schin */ 866*4887Schin 867*4887Schin if ((fts->baselen = f->fts_namelen) >= (fts->endbuf - fts->base) && resize(fts, fts->baselen)) 868*4887Schin return 0; 869*4887Schin memcpy(fts->base, f->name, fts->baselen + 1); 870*4887Schin fts->name = fts->cd ? fts->path : fts->base; 871*4887Schin /*FALLTHROUGH*/ 872*4887Schin 873*4887Schin case FTS_preorder: 874*4887Schin 875*4887Schin /* 876*4887Schin * check for cycle and open dir 877*4887Schin */ 878*4887Schin 879*4887Schin if (f->fts_info == FTS_D) 880*4887Schin { 881*4887Schin if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0) 882*4887Schin { 883*4887Schin f->fts_info = FTS_DC; 884*4887Schin f->fts_cycle = fts->diroot; 885*4887Schin } 886*4887Schin else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev)) 887*4887Schin { 888*4887Schin /* 889*4887Schin * buffer is known to be large enough here! 890*4887Schin */ 891*4887Schin 892*4887Schin if (fts->base[fts->baselen - 1] != '/') 893*4887Schin memcpy(fts->base + fts->baselen, "/.", 3); 894*4887Schin if (!(fts->dir = opendir(fts->name))) 895*4887Schin f->fts_info = FTS_DNX; 896*4887Schin fts->base[fts->baselen] = 0; 897*4887Schin if (!fts->dir && !(fts->dir = opendir(fts->name))) 898*4887Schin f->fts_info = FTS_DNR; 899*4887Schin } 900*4887Schin } 901*4887Schin f->nd = f->fts_info & ~FTS_DNX; 902*4887Schin if (f->nd || !(fts->flags & FTS_NOPREORDER)) 903*4887Schin { 904*4887Schin fts->current = f; 905*4887Schin fts->link = f->fts_link; 906*4887Schin f->fts_link = 0; 907*4887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 908*4887Schin f->fts_pathlen = (fts->base - f->fts_path) + fts->baselen; 909*4887Schin f->fts_accpath = ACCESS(fts, f); 910*4887Schin fts->state = FTS_preorder_return; 911*4887Schin goto note; 912*4887Schin } 913*4887Schin /*FALLTHROUGH*/ 914*4887Schin 915*4887Schin case FTS_preorder_resume: 916*4887Schin 917*4887Schin /* 918*4887Schin * prune 919*4887Schin */ 920*4887Schin 921*4887Schin if (!fts->dir || f->nd || f->status == FTS_SKIP) 922*4887Schin { 923*4887Schin if (fts->dir) 924*4887Schin { 925*4887Schin closedir(fts->dir); 926*4887Schin fts->dir = 0; 927*4887Schin } 928*4887Schin fts->state = FTS_popstack; 929*4887Schin continue; 930*4887Schin } 931*4887Schin 932*4887Schin /* 933*4887Schin * FTS_D or FTS_DNX, about to read children 934*4887Schin */ 935*4887Schin 936*4887Schin if (fts->cd == 0) 937*4887Schin { 938*4887Schin if ((fts->cd = chdir(fts->name)) < 0) 939*4887Schin pathcd(fts->home, NiL); 940*4887Schin else if (fts->pwd != f) 941*4887Schin { 942*4887Schin f->pwd = fts->pwd; 943*4887Schin fts->pwd = f; 944*4887Schin } 945*4887Schin fts->curdir = fts->cd < 0 ? 0 : f; 946*4887Schin } 947*4887Schin fts->nostat = fts->children > 1 || f->fts_info == FTS_DNX; 948*4887Schin fts->cpname = fts->cd && !fts->nostat || !fts->children && !fts->comparf; 949*4887Schin fts->dotdot = 0; 950*4887Schin fts->endbase = fts->base + fts->baselen; 951*4887Schin if (fts->endbase[-1] != '/') 952*4887Schin *fts->endbase++ = '/'; 953*4887Schin fts->current = f; 954*4887Schin /*FALLTHROUGH*/ 955*4887Schin 956*4887Schin case FTS_readdir: 957*4887Schin 958*4887Schin while (d = readdir(fts->dir)) 959*4887Schin { 960*4887Schin s = d->d_name; 961*4887Schin if (s[0] == '.') 962*4887Schin { 963*4887Schin if (s[1] == 0) 964*4887Schin { 965*4887Schin fts->current->nlink--; 966*4887Schin if (!(fts->flags & FTS_SEEDOT)) 967*4887Schin continue; 968*4887Schin n = 1; 969*4887Schin } 970*4887Schin else if (s[1] == '.' && s[2] == 0) 971*4887Schin { 972*4887Schin fts->current->nlink--; 973*4887Schin if (fts->current->must == 1) 974*4887Schin fts->current->must = 0; 975*4887Schin if (!(fts->flags & FTS_SEEDOT)) 976*4887Schin continue; 977*4887Schin n = 2; 978*4887Schin } 979*4887Schin else 980*4887Schin n = 0; 981*4887Schin } 982*4887Schin else 983*4887Schin n = 0; 984*4887Schin 985*4887Schin /* 986*4887Schin * make a new entry 987*4887Schin */ 988*4887Schin 989*4887Schin i = D_NAMLEN(d); 990*4887Schin if (!(f = node(fts, fts->current, s, i))) 991*4887Schin return 0; 992*4887Schin TYPE(f, D_TYPE(d)); 993*4887Schin 994*4887Schin /* 995*4887Schin * check for space 996*4887Schin */ 997*4887Schin 998*4887Schin if (i >= fts->endbuf - fts->endbase) 999*4887Schin { 1000*4887Schin if (resize(fts, i)) 1001*4887Schin return 0; 1002*4887Schin fts->endbase = fts->base + fts->baselen; 1003*4887Schin if (fts->endbase[-1] != '/') 1004*4887Schin fts->endbase++; 1005*4887Schin } 1006*4887Schin if (fts->cpname) 1007*4887Schin { 1008*4887Schin memcpy(fts->endbase, s, i + 1); 1009*4887Schin if (fts->cd) 1010*4887Schin s = fts->path; 1011*4887Schin } 1012*4887Schin if (n) 1013*4887Schin { 1014*4887Schin /* 1015*4887Schin * don't recurse on . and .. 1016*4887Schin */ 1017*4887Schin 1018*4887Schin if (n == 1) 1019*4887Schin f->fts_statp = fts->current->fts_statp; 1020*4887Schin else 1021*4887Schin { 1022*4887Schin if (f->fts_info != FTS_NS) 1023*4887Schin fts->dotdot = f; 1024*4887Schin if (fts->current->fts_parent->fts_level < 0) 1025*4887Schin { 1026*4887Schin f->fts_statp = &fts->current->fts_parent->statb; 1027*4887Schin info(fts, f, s, f->fts_statp, 0); 1028*4887Schin } 1029*4887Schin else 1030*4887Schin f->fts_statp = fts->current->fts_parent->fts_statp; 1031*4887Schin } 1032*4887Schin f->fts_info = FTS_DOT; 1033*4887Schin } 1034*4887Schin else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags)) 1035*4887Schin f->statb.st_ino = D_FILENO(d); 1036*4887Schin if (fts->comparf) 1037*4887Schin fts->root = search(f, fts->root, fts->comparf, 1); 1038*4887Schin else if (fts->children || f->fts_info == FTS_D || f->fts_info == FTS_SL) 1039*4887Schin { 1040*4887Schin if (fts->top) 1041*4887Schin fts->bot = fts->bot->fts_link = f; 1042*4887Schin else 1043*4887Schin fts->top = fts->bot = f; 1044*4887Schin } 1045*4887Schin else 1046*4887Schin { 1047*4887Schin /* 1048*4887Schin * terminal node 1049*4887Schin */ 1050*4887Schin 1051*4887Schin f->fts_path = PATH(fts, fts->path, 1); 1052*4887Schin f->fts_pathlen = fts->endbase - f->fts_path + f->fts_namelen; 1053*4887Schin f->fts_accpath = ACCESS(fts, f); 1054*4887Schin fts->previous = fts->current; 1055*4887Schin fts->current = f; 1056*4887Schin fts->state = FTS_terminal; 1057*4887Schin goto note; 1058*4887Schin } 1059*4887Schin } 1060*4887Schin 1061*4887Schin /* 1062*4887Schin * done with the directory 1063*4887Schin */ 1064*4887Schin 1065*4887Schin closedir(fts->dir); 1066*4887Schin fts->dir = 0; 1067*4887Schin if (fts->root) 1068*4887Schin getlist(&fts->top, &fts->bot, fts->root); 1069*4887Schin if (fts->children) 1070*4887Schin { 1071*4887Schin /* 1072*4887Schin * try moving back to parent dir 1073*4887Schin */ 1074*4887Schin 1075*4887Schin fts->base[fts->baselen] = 0; 1076*4887Schin if (fts->cd <= 0) 1077*4887Schin { 1078*4887Schin f = fts->current->fts_parent; 1079*4887Schin if (fts->cd < 0 1080*4887Schin || f != fts->curdir 1081*4887Schin || !fts->dotdot 1082*4887Schin || !SAME(f->fts_statp, fts->dotdot->fts_statp) 1083*4887Schin || fts->pwd && fts->pwd->symlink 1084*4887Schin || (fts->cd = chdir("..")) < 0 1085*4887Schin #ifdef verify 1086*4887Schin || stat(".", &sb) < 0 1087*4887Schin || !SAME(&sb, fts->dotdot->fts_statp) 1088*4887Schin #endif 1089*4887Schin ) 1090*4887Schin fts->cd = setpdir(fts->home, fts->path, fts->base); 1091*4887Schin if (fts->pwd) 1092*4887Schin fts->pwd = fts->pwd->pwd; 1093*4887Schin fts->curdir = fts->cd ? 0 : f; 1094*4887Schin } 1095*4887Schin f = fts->current; 1096*4887Schin fts->link = f->fts_link; 1097*4887Schin f->fts_link = fts->top; 1098*4887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 1099*4887Schin f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; 1100*4887Schin f->fts_accpath = ACCESS(fts, f); 1101*4887Schin fts->state = FTS_children_return; 1102*4887Schin goto note; 1103*4887Schin } 1104*4887Schin /*FALLTHROUGH*/ 1105*4887Schin 1106*4887Schin case FTS_children_resume: 1107*4887Schin 1108*4887Schin fts->base[fts->baselen] = 0; 1109*4887Schin if (fts->top) 1110*4887Schin { 1111*4887Schin fts->bot->fts_link = fts->todo; 1112*4887Schin fts->todo = fts->top; 1113*4887Schin fts->top = 0; 1114*4887Schin } 1115*4887Schin /*FALLTHROUGH*/ 1116*4887Schin 1117*4887Schin case FTS_popstack: 1118*4887Schin 1119*4887Schin /* 1120*4887Schin * pop objects completely processed 1121*4887Schin */ 1122*4887Schin 1123*4887Schin fts->nd = 0; 1124*4887Schin f = fts->current; 1125*4887Schin /*FALLTHROUGH*/ 1126*4887Schin 1127*4887Schin case FTS_popstack_resume: 1128*4887Schin 1129*4887Schin while (fts->todo && f == fts->todo) 1130*4887Schin { 1131*4887Schin t = f->fts_parent; 1132*4887Schin if ((f->fts_info & FTS_DP) == FTS_D) 1133*4887Schin { 1134*4887Schin /* 1135*4887Schin * delete from <dev,ino> tree 1136*4887Schin */ 1137*4887Schin 1138*4887Schin if (f != fts->diroot) 1139*4887Schin fts->diroot = search(f, fts->diroot, statcmp, 0); 1140*4887Schin fts->diroot = deleteroot(fts->diroot); 1141*4887Schin if (f == fts->curdir) 1142*4887Schin { 1143*4887Schin fts->nd++; 1144*4887Schin fts->curdir = t; 1145*4887Schin } 1146*4887Schin 1147*4887Schin /* 1148*4887Schin * perform post-order processing 1149*4887Schin */ 1150*4887Schin 1151*4887Schin if (!(fts->flags & FTS_NOPOSTORDER) && 1152*4887Schin f->status != FTS_SKIP && 1153*4887Schin f->status != FTS_NOPOSTORDER) 1154*4887Schin { 1155*4887Schin /* 1156*4887Schin * move to parent dir 1157*4887Schin */ 1158*4887Schin 1159*4887Schin if (fts->nd > 0) 1160*4887Schin fts->cd = popdirs(fts); 1161*4887Schin if (fts->cd < 0) 1162*4887Schin fts->cd = setpdir(fts->home, fts->path, fts->base); 1163*4887Schin fts->curdir = fts->cd ? 0 : t; 1164*4887Schin f->fts_info = FTS_DP; 1165*4887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 1166*4887Schin f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; 1167*4887Schin f->fts_accpath = ACCESS(fts, f); 1168*4887Schin 1169*4887Schin /* 1170*4887Schin * re-stat to update nlink/times 1171*4887Schin */ 1172*4887Schin 1173*4887Schin stat(f->fts_accpath, f->fts_statp); 1174*4887Schin fts->link = f->fts_link; 1175*4887Schin f->fts_link = 0; 1176*4887Schin fts->state = FTS_popstack_return; 1177*4887Schin goto note; 1178*4887Schin } 1179*4887Schin } 1180*4887Schin 1181*4887Schin /* 1182*4887Schin * reset base 1183*4887Schin */ 1184*4887Schin 1185*4887Schin if (fts->base > fts->path + t->fts_namelen) 1186*4887Schin fts->base--; 1187*4887Schin *fts->base = 0; 1188*4887Schin fts->base -= t->fts_namelen; 1189*4887Schin 1190*4887Schin /* 1191*4887Schin * try again or delete from top of stack 1192*4887Schin */ 1193*4887Schin 1194*4887Schin if (f->status == FTS_AGAIN) 1195*4887Schin { 1196*4887Schin f->fts_info = FTS_D; 1197*4887Schin f->status = 0; 1198*4887Schin } 1199*4887Schin else 1200*4887Schin { 1201*4887Schin fts->todo = fts->todo->fts_link; 1202*4887Schin drop(fts, f); 1203*4887Schin } 1204*4887Schin f = t; 1205*4887Schin } 1206*4887Schin 1207*4887Schin /* 1208*4887Schin * reset current directory 1209*4887Schin */ 1210*4887Schin 1211*4887Schin if (fts->nd > 0 && popdirs(fts) < 0) 1212*4887Schin { 1213*4887Schin pathcd(fts->home, NiL); 1214*4887Schin fts->curdir = 0; 1215*4887Schin fts->cd = -1; 1216*4887Schin } 1217*4887Schin if (fts->todo) 1218*4887Schin { 1219*4887Schin if (*fts->base) 1220*4887Schin fts->base += f->fts_namelen; 1221*4887Schin if (*(fts->base - 1) != '/') 1222*4887Schin *fts->base++ = '/'; 1223*4887Schin *fts->base = 0; 1224*4887Schin f = fts->todo; 1225*4887Schin fts->state = FTS_todo; 1226*4887Schin continue; 1227*4887Schin } 1228*4887Schin return 0; 1229*4887Schin 1230*4887Schin case FTS_children_return: 1231*4887Schin 1232*4887Schin f = fts->current; 1233*4887Schin f->fts_link = fts->link; 1234*4887Schin 1235*4887Schin /* 1236*4887Schin * chdir down again 1237*4887Schin */ 1238*4887Schin 1239*4887Schin i = f->fts_info != FTS_DNX; 1240*4887Schin n = f->status == FTS_SKIP; 1241*4887Schin if (!n && fts->cd == 0) 1242*4887Schin { 1243*4887Schin if ((fts->cd = chdir(fts->base)) < 0) 1244*4887Schin pathcd(fts->home, NiL); 1245*4887Schin else if (fts->pwd != f) 1246*4887Schin { 1247*4887Schin f->pwd = fts->pwd; 1248*4887Schin fts->pwd = f; 1249*4887Schin } 1250*4887Schin fts->curdir = fts->cd ? 0 : f; 1251*4887Schin } 1252*4887Schin 1253*4887Schin /* 1254*4887Schin * prune 1255*4887Schin */ 1256*4887Schin 1257*4887Schin if (fts->base[fts->baselen - 1] != '/') 1258*4887Schin fts->base[fts->baselen] = '/'; 1259*4887Schin for (fts->bot = 0, f = fts->top; f; ) 1260*4887Schin if (n || f->status == FTS_SKIP) 1261*4887Schin { 1262*4887Schin if (fts->bot) 1263*4887Schin fts->bot->fts_link = f->fts_link; 1264*4887Schin else 1265*4887Schin fts->top = f->fts_link; 1266*4887Schin drop(fts, f); 1267*4887Schin f = fts->bot ? fts->bot->fts_link : fts->top; 1268*4887Schin } 1269*4887Schin else 1270*4887Schin { 1271*4887Schin if (fts->children > 1 && i) 1272*4887Schin { 1273*4887Schin if (f->status == FTS_STAT) 1274*4887Schin info(fts, f, NiL, f->fts_statp, 0); 1275*4887Schin else if (f->fts_info == FTS_NSOK && !SKIP(fts, f)) 1276*4887Schin { 1277*4887Schin s = f->fts_name; 1278*4887Schin if (fts->cd) 1279*4887Schin { 1280*4887Schin memcpy(fts->endbase, s, f->fts_namelen + 1); 1281*4887Schin s = fts->path; 1282*4887Schin } 1283*4887Schin info(fts, f, s, f->fts_statp, fts->flags); 1284*4887Schin } 1285*4887Schin } 1286*4887Schin fts->bot = f; 1287*4887Schin f = f->fts_link; 1288*4887Schin } 1289*4887Schin fts->children = 0; 1290*4887Schin fts->state = FTS_children_resume; 1291*4887Schin continue; 1292*4887Schin 1293*4887Schin case FTS_popstack_return: 1294*4887Schin 1295*4887Schin f = fts->todo; 1296*4887Schin f->fts_link = fts->link; 1297*4887Schin f->fts_info = f->status == FTS_AGAIN ? FTS_DP : 0; 1298*4887Schin fts->state = FTS_popstack_resume; 1299*4887Schin continue; 1300*4887Schin 1301*4887Schin case FTS_preorder_return: 1302*4887Schin 1303*4887Schin f = fts->current; 1304*4887Schin f->fts_link = fts->link; 1305*4887Schin 1306*4887Schin /* 1307*4887Schin * follow symlink if asked to 1308*4887Schin */ 1309*4887Schin 1310*4887Schin if (f->status == FTS_FOLLOW) 1311*4887Schin { 1312*4887Schin f->status = 0; 1313*4887Schin if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) 1314*4887Schin { 1315*4887Schin info(fts, f, f->fts_accpath, f->fts_statp, 0); 1316*4887Schin if (f->fts_info != FTS_SL) 1317*4887Schin { 1318*4887Schin fts->state = FTS_preorder; 1319*4887Schin continue; 1320*4887Schin } 1321*4887Schin } 1322*4887Schin } 1323*4887Schin 1324*4887Schin /* 1325*4887Schin * about to prune this f and already at home 1326*4887Schin */ 1327*4887Schin 1328*4887Schin if (fts->cd == 0 && f->fts_level == 0 && f->nd) 1329*4887Schin fts->cd = -1; 1330*4887Schin fts->state = FTS_preorder_resume; 1331*4887Schin continue; 1332*4887Schin 1333*4887Schin case FTS_terminal: 1334*4887Schin 1335*4887Schin f = fts->current; 1336*4887Schin if (f->status == FTS_FOLLOW) 1337*4887Schin { 1338*4887Schin f->status = 0; 1339*4887Schin if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) 1340*4887Schin { 1341*4887Schin info(fts, f, f->fts_accpath, f->fts_statp, 0); 1342*4887Schin if (f->symlink && f->fts_info != FTS_SL) 1343*4887Schin { 1344*4887Schin if (!(f->fts_link = fts->top)) 1345*4887Schin fts->bot = f; 1346*4887Schin fts->top = f; 1347*4887Schin fts->current = fts->previous; 1348*4887Schin fts->state = FTS_readdir; 1349*4887Schin continue; 1350*4887Schin } 1351*4887Schin } 1352*4887Schin } 1353*4887Schin f = f->fts_parent; 1354*4887Schin drop(fts, fts->current); 1355*4887Schin fts->current = f; 1356*4887Schin fts->state = FTS_readdir; 1357*4887Schin continue; 1358*4887Schin 1359*4887Schin case FTS_error: 1360*4887Schin 1361*4887Schin return 0; 1362*4887Schin 1363*4887Schin default: 1364*4887Schin 1365*4887Schin fts->fts_errno = EINVAL; 1366*4887Schin fts->state = FTS_error; 1367*4887Schin return 0; 1368*4887Schin 1369*4887Schin } 1370*4887Schin note: 1371*4887Schin for (p = notify; p; p = p->next) 1372*4887Schin if ((n = (*p->notifyf)(fts, f, p->context)) > 0) 1373*4887Schin break; 1374*4887Schin else if (n < 0) 1375*4887Schin { 1376*4887Schin fts->fts_errno = EINVAL; 1377*4887Schin fts->state = FTS_error; 1378*4887Schin return 0; 1379*4887Schin } 1380*4887Schin return f; 1381*4887Schin } 1382*4887Schin 1383*4887Schin /* 1384*4887Schin * set stream or entry flags 1385*4887Schin */ 1386*4887Schin 1387*4887Schin int 1388*4887Schin fts_set(register FTS* fts, register FTSENT* f, int status) 1389*4887Schin { 1390*4887Schin if (fts || !f || f->fts->current != f) 1391*4887Schin return -1; 1392*4887Schin switch (status) 1393*4887Schin { 1394*4887Schin case FTS_AGAIN: 1395*4887Schin break; 1396*4887Schin case FTS_FOLLOW: 1397*4887Schin if (!(f->fts_info & FTS_SL)) 1398*4887Schin return -1; 1399*4887Schin break; 1400*4887Schin case FTS_NOPOSTORDER: 1401*4887Schin break; 1402*4887Schin case FTS_SKIP: 1403*4887Schin if ((f->fts_info & (FTS_D|FTS_P)) != FTS_D) 1404*4887Schin return -1; 1405*4887Schin break; 1406*4887Schin default: 1407*4887Schin return -1; 1408*4887Schin } 1409*4887Schin f->status = status; 1410*4887Schin return 0; 1411*4887Schin } 1412*4887Schin 1413*4887Schin /* 1414*4887Schin * return the list of child entries 1415*4887Schin */ 1416*4887Schin 1417*4887Schin FTSENT* 1418*4887Schin fts_children(register FTS* fts, int flags) 1419*4887Schin { 1420*4887Schin register FTSENT* f; 1421*4887Schin 1422*4887Schin switch (fts->state) 1423*4887Schin { 1424*4887Schin 1425*4887Schin case 0: 1426*4887Schin 1427*4887Schin fts->state = FTS_top_return; 1428*4887Schin return fts->todo; 1429*4887Schin 1430*4887Schin case FTS_preorder_return: 1431*4887Schin 1432*4887Schin fts->children = ((flags | fts->flags) & FTS_NOSTAT) ? 2 : 1; 1433*4887Schin if (f = fts_read(fts)) 1434*4887Schin f = f->fts_link; 1435*4887Schin return f; 1436*4887Schin 1437*4887Schin } 1438*4887Schin return 0; 1439*4887Schin } 1440*4887Schin 1441*4887Schin /* 1442*4887Schin * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags 1443*4887Schin * conditioned by astconf() 1444*4887Schin */ 1445*4887Schin 1446*4887Schin int 1447*4887Schin fts_flags(void) 1448*4887Schin { 1449*4887Schin register char* s; 1450*4887Schin 1451*4887Schin s = astconf("PATH_RESOLVE", NiL, NiL); 1452*4887Schin if (streq(s, "logical")) 1453*4887Schin return FTS_LOGICAL; 1454*4887Schin if (streq(s, "physical")) 1455*4887Schin return FTS_PHYSICAL|FTS_SEEDOTDIR; 1456*4887Schin return FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR; 1457*4887Schin } 1458*4887Schin 1459*4887Schin /* 1460*4887Schin * close an open fts stream 1461*4887Schin */ 1462*4887Schin 1463*4887Schin int 1464*4887Schin fts_close(register FTS* fts) 1465*4887Schin { 1466*4887Schin register FTSENT* f; 1467*4887Schin register FTSENT* x; 1468*4887Schin 1469*4887Schin if (fts->dir) 1470*4887Schin closedir(fts->dir); 1471*4887Schin if (fts->cd == 0) 1472*4887Schin pathcd(fts->home, NiL); 1473*4887Schin free(fts->home); 1474*4887Schin if (fts->state == FTS_children_return) 1475*4887Schin fts->current->fts_link = fts->link; 1476*4887Schin if (fts->top) 1477*4887Schin { 1478*4887Schin fts->bot->fts_link = fts->todo; 1479*4887Schin fts->todo = fts->top; 1480*4887Schin } 1481*4887Schin for (f = fts->todo; f; f = x) 1482*4887Schin { 1483*4887Schin x = f->fts_link; 1484*4887Schin free(f); 1485*4887Schin } 1486*4887Schin for (f = fts->free; f; f = x) 1487*4887Schin { 1488*4887Schin x = f->fts_link; 1489*4887Schin free(f); 1490*4887Schin } 1491*4887Schin return 0; 1492*4887Schin } 1493*4887Schin 1494*4887Schin /* 1495*4887Schin * register function to be called for each fts_read() entry 1496*4887Schin */ 1497*4887Schin 1498*4887Schin int 1499*4887Schin fts_notify(Notify_f notifyf, void* context) 1500*4887Schin { 1501*4887Schin register Notify_t* np; 1502*4887Schin 1503*4887Schin if (!(np = newof(0, Notify_t, 1, 0))) 1504*4887Schin return -1; 1505*4887Schin np->notifyf = notifyf; 1506*4887Schin np->context = context; 1507*4887Schin np->next = notify; 1508*4887Schin notify = np; 1509*4887Schin return 0; 1510*4887Schin } 1511