14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1985-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * Phong Vo <kpv@research.att.com> * 204887Schin * * 214887Schin ***********************************************************************/ 224887Schin #pragma prototyped 234887Schin /* 244887Schin * Phong Vo 254887Schin * Glenn Fowler 264887Schin * AT&T Research 274887Schin * 284887Schin * fts implementation unwound from the kpv ftwalk() of 1988-10-30 294887Schin */ 304887Schin 314887Schin #include <ast.h> 324887Schin #include <ast_dir.h> 334887Schin #include <error.h> 344887Schin #include <fs3d.h> 35*8462SApril.Chin@Sun.COM #include <ls.h> 364887Schin 374887Schin struct Ftsent; 384887Schin 394887Schin typedef int (*Compar_f)(struct Ftsent* const*, struct Ftsent* const*); 404887Schin typedef int (*Stat_f)(const char*, struct stat*); 414887Schin 424887Schin #define _FTS_PRIVATE_ \ 434887Schin FTSENT* parent; /* top parent */ \ 444887Schin FTSENT* todo; /* todo list */ \ 454887Schin FTSENT* top; /* top element */ \ 464887Schin FTSENT* root; \ 474887Schin FTSENT* bot; /* bottom element */ \ 484887Schin FTSENT* free; /* free element */ \ 494887Schin FTSENT* diroot; \ 504887Schin FTSENT* curdir; \ 514887Schin FTSENT* current; /* current element */ \ 524887Schin FTSENT* previous; /* previous current */ \ 534887Schin FTSENT* dotdot; \ 544887Schin FTSENT* link; /* real current fts_link*/ \ 554887Schin FTSENT* pwd; /* pwd parent */ \ 564887Schin DIR* dir; /* current dir stream */ \ 574887Schin Compar_f comparf; /* node comparison func */ \ 584887Schin int baselen; /* current strlen(base) */ \ 594887Schin int cd; /* chdir status */ \ 604887Schin int cpname; \ 614887Schin int flags; /* fts_open() flags */ \ 624887Schin int homesize; /* sizeof(home) */ \ 634887Schin int nd; \ 644887Schin unsigned char children; \ 654887Schin unsigned char fs3d; \ 664887Schin unsigned char nostat; \ 674887Schin unsigned char state; /* fts_read() state */ \ 684887Schin char* base; /* basename in path */ \ 694887Schin char* name; \ 704887Schin char* path; /* path workspace */ \ 714887Schin char* home; /* home/path buffer */ \ 724887Schin char* endbase; /* space to build paths */ \ 734887Schin char* endbuf; /* space to build paths */ \ 74*8462SApril.Chin@Sun.COM char* pad[2]; /* $0.02 to splain this */ 754887Schin 764887Schin /* 774887Schin * NOTE: <ftwalk.h> relies on status and statb being the first two elements 784887Schin */ 794887Schin 804887Schin #define _FTSENT_PRIVATE_ \ 814887Schin short status; /* internal status */ \ 824887Schin struct stat statb; /* fts_statp data */ \ 83*8462SApril.Chin@Sun.COM FTS* fts; /* fts_open() handle */ \ 844887Schin int nd; /* popdir() count */ \ 854887Schin FTSENT* left; /* left child */ \ 864887Schin FTSENT* right; /* right child */ \ 874887Schin FTSENT* pwd; /* pwd parent */ \ 884887Schin FTSENT* stack; /* getlist() stack */ \ 894887Schin long nlink; /* FTS_D link count */ \ 904887Schin unsigned char must; /* must stat */ \ 914887Schin unsigned char type; /* DT_* type */ \ 924887Schin unsigned char symlink; /* originally a symlink */ \ 934887Schin char name[sizeof(int)]; /* fts_name data */ 944887Schin 954887Schin #include <fts.h> 964887Schin 974887Schin #ifndef ENOSYS 984887Schin #define ENOSYS EINVAL 994887Schin #endif 1004887Schin 1014887Schin 1024887Schin #if MAXNAMLEN > 16 1034887Schin #define MINNAME 32 1044887Schin #else 1054887Schin #define MINNAME 16 1064887Schin #endif 1074887Schin 1084887Schin #define drop(p,f) (((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free)) 1094887Schin 1104887Schin #define ACCESS(p,f) ((p)->cd==0?(f)->fts_name:(f)->fts_path) 1114887Schin #define PATH(f,p,l) ((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p)) 1124887Schin #define SAME(one,two) ((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev) 1134887Schin #define SKIPLINK(p,f) ((f)->fts_parent->nlink == 0) 1144887Schin 1154887Schin #ifdef D_TYPE 1164887Schin #define ISTYPE(f,t) ((f)->type == (t)) 1174887Schin #define TYPE(f,t) ((f)->type = (t)) 1184887Schin #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))))) 1194887Schin #else 1204887Schin #undef DT_UNKNOWN 1214887Schin #define DT_UNKNOWN 0 1224887Schin #undef DT_LNK 1234887Schin #define DT_LNK 1 1244887Schin #define ISTYPE(f,t) ((t)==DT_UNKNOWN) 1254887Schin #define TYPE(f,d) 1264887Schin #define SKIP(p,f) ((f)->fts_parent->must == 0 && SKIPLINK(p,f)) 1274887Schin #endif 1284887Schin 1294887Schin #ifndef D_FILENO 1304887Schin #define D_FILENO(d) (1) 1314887Schin #endif 1324887Schin 1334887Schin /* 1344887Schin * NOTE: a malicious dir rename() could change .. underfoot so we 1354887Schin * must always verify; undef verify to enable the unsafe code 1364887Schin */ 1374887Schin 1384887Schin #define verify 1 1394887Schin 1404887Schin /* 1414887Schin * FTS_NOSTAT requires a dir with 1424887Schin * D_TYPE(&dirent_t)!=DT_UNKNOWN 1434887Schin * OR 1444887Schin * st_nlink>=2 1454887Schin */ 1464887Schin 1474887Schin #define FTS_children_resume 1 1484887Schin #define FTS_children_return 2 1494887Schin #define FTS_error 3 1504887Schin #define FTS_popstack 4 1514887Schin #define FTS_popstack_resume 5 1524887Schin #define FTS_popstack_return 6 1534887Schin #define FTS_preorder 7 1544887Schin #define FTS_preorder_resume 8 1554887Schin #define FTS_preorder_return 9 1564887Schin #define FTS_readdir 10 1574887Schin #define FTS_terminal 11 1584887Schin #define FTS_todo 12 1594887Schin #define FTS_top_return 13 1604887Schin 1614887Schin typedef int (*Notify_f)(FTS*, FTSENT*, void*); 1624887Schin 1634887Schin typedef struct Notify_s 1644887Schin { 1654887Schin struct Notify_s* next; 1664887Schin Notify_f notifyf; 1674887Schin void* context; 1684887Schin } Notify_t; 1694887Schin 1704887Schin static Notify_t* notify; 1714887Schin 1724887Schin /* 1734887Schin * allocate an FTSENT node 1744887Schin */ 1754887Schin 1764887Schin static FTSENT* 1774887Schin node(FTS* fts, FTSENT* parent, register char* name, register int namelen) 1784887Schin { 1794887Schin register FTSENT* f; 1804887Schin register int n; 1814887Schin 1824887Schin if (fts->free && namelen < MINNAME) 1834887Schin { 1844887Schin f = fts->free; 1854887Schin fts->free = f->fts_link; 1864887Schin } 1874887Schin else 1884887Schin { 1894887Schin n = (namelen < MINNAME ? MINNAME : namelen + 1) - sizeof(int); 1904887Schin if (!(f = newof(0, FTSENT, 1, n))) 1914887Schin { 1924887Schin fts->fts_errno = errno; 1934887Schin fts->state = FTS_error; 1944887Schin return 0; 1954887Schin } 1964887Schin f->fts = fts; 1974887Schin } 1984887Schin TYPE(f, DT_UNKNOWN); 1994887Schin f->status = 0; 2004887Schin f->symlink = 0; 2014887Schin f->fts_level = (f->fts_parent = parent)->fts_level + 1; 2024887Schin f->fts_link = 0; 2034887Schin f->fts_pointer = 0; 2044887Schin f->fts_number = 0; 2054887Schin f->fts_errno = 0; 2064887Schin f->fts_namelen = namelen; 2074887Schin f->fts_name = f->name; 2084887Schin f->fts_statp = &f->statb; 2094887Schin memcpy(f->fts_name, name, namelen + 1); 2104887Schin return f; 2114887Schin } 2124887Schin 2134887Schin /* 2144887Schin * compare directories by device/inode 2154887Schin */ 2164887Schin 2174887Schin static int 2184887Schin statcmp(FTSENT* const* pf1, FTSENT* const* pf2) 2194887Schin { 2204887Schin register const FTSENT* f1 = *pf1; 2214887Schin register const FTSENT* f2 = *pf2; 2224887Schin 2234887Schin if (f1->statb.st_ino < f2->statb.st_ino) 2244887Schin return -1; 2254887Schin if (f1->statb.st_ino > f2->statb.st_ino) 2264887Schin return 1; 2274887Schin if (f1->statb.st_dev < f2->statb.st_dev) 2284887Schin return -1; 2294887Schin if (f1->statb.st_dev > f2->statb.st_dev) 2304887Schin return 1; 2314887Schin 2324887Schin /* 2334887Schin * hack for NFS where <dev,ino> may not uniquely identify objects 2344887Schin */ 2354887Schin 2364887Schin if (f1->statb.st_mtime < f2->statb.st_mtime) 2374887Schin return -1; 2384887Schin if (f1->statb.st_mtime > f2->statb.st_mtime) 2394887Schin return 1; 2404887Schin return 0; 2414887Schin } 2424887Schin 2434887Schin /* 2444887Schin * search trees with top-down splaying (a la Tarjan and Sleator) 2454887Schin * when used for insertion sort, this implements a stable sort 2464887Schin */ 2474887Schin 2484887Schin #define RROTATE(r) (t = r->left, r->left = t->right, t->right = r, r = t) 2494887Schin #define LROTATE(r) (t = r->right, r->right = t->left, t->left = r, r = t) 2504887Schin 2514887Schin static FTSENT* 2524887Schin search(FTSENT* e, FTSENT* root, int(*comparf)(FTSENT* const*, FTSENT* const*), int insert) 2534887Schin { 2544887Schin register int cmp; 2554887Schin register FTSENT* t; 2564887Schin register FTSENT* left; 2574887Schin register FTSENT* right; 2584887Schin register FTSENT* lroot; 2594887Schin register FTSENT* rroot; 2604887Schin 2614887Schin left = right = lroot = rroot = 0; 2624887Schin while (root) 2634887Schin { 2644887Schin if (!(cmp = (*comparf)(&e, &root)) && !insert) 2654887Schin break; 2664887Schin if (cmp < 0) 2674887Schin { 2684887Schin /* 2694887Schin * this is the left zig-zig case 2704887Schin */ 2714887Schin 2724887Schin if (root->left && (cmp = (*comparf)(&e, &root->left)) <= 0) 2734887Schin { 2744887Schin RROTATE(root); 2754887Schin if (!cmp && !insert) 2764887Schin break; 2774887Schin } 2784887Schin 2794887Schin /* 2804887Schin * stick all things > e to the right tree 2814887Schin */ 2824887Schin 2834887Schin if (right) 2844887Schin right->left = root; 2854887Schin else 2864887Schin rroot = root; 2874887Schin right = root; 2884887Schin root = root->left; 2894887Schin right->left = 0; 2904887Schin } 2914887Schin else 2924887Schin { 2934887Schin /* 2944887Schin * this is the right zig-zig case 2954887Schin */ 2964887Schin 2974887Schin if (root->right && (cmp = (*comparf)(&e, &root->right)) >= 0) 2984887Schin { 2994887Schin LROTATE(root); 3004887Schin if (!cmp && !insert) 3014887Schin break; 3024887Schin } 3034887Schin 3044887Schin /* 3054887Schin * stick all things <= e to the left tree 3064887Schin */ 3074887Schin 3084887Schin if (left) 3094887Schin left->right = root; 3104887Schin else 3114887Schin lroot = root; 3124887Schin left = root; 3134887Schin root = root->right; 3144887Schin left->right = 0; 3154887Schin } 3164887Schin } 3174887Schin if (!root) 3184887Schin root = e; 3194887Schin else 3204887Schin { 3214887Schin if (right) 3224887Schin right->left = root->right; 3234887Schin else 3244887Schin rroot = root->right; 3254887Schin if (left) 3264887Schin left->right = root->left; 3274887Schin else 3284887Schin lroot = root->left; 3294887Schin } 3304887Schin root->left = lroot; 3314887Schin root->right = rroot; 3324887Schin return root; 3334887Schin } 3344887Schin 3354887Schin /* 3364887Schin * delete the root element from the tree 3374887Schin */ 3384887Schin 3394887Schin static FTSENT* 3404887Schin deleteroot(register FTSENT* root) 3414887Schin { 3424887Schin register FTSENT* t; 3434887Schin register FTSENT* left; 3444887Schin register FTSENT* right; 3454887Schin 3464887Schin right = root->right; 3474887Schin if (!(left = root->left)) 3484887Schin root = right; 3494887Schin else 3504887Schin { 3514887Schin while (left->right) 3524887Schin LROTATE(left); 3534887Schin left->right = right; 3544887Schin root = left; 3554887Schin } 3564887Schin return root; 3574887Schin } 3584887Schin 3594887Schin /* 3604887Schin * generate ordered fts_link list from binary tree at root 3614887Schin * FTSENT.stack instead of recursion to avoid blowing the real 3624887Schin * stack on big directories 3634887Schin */ 3644887Schin 3654887Schin static void 3664887Schin getlist(register FTSENT** top, register FTSENT** bot, register FTSENT* root) 3674887Schin { 3684887Schin register FTSENT* stack = 0; 3694887Schin 3704887Schin for (;;) 3714887Schin { 3724887Schin if (root->left) 3734887Schin { 3744887Schin root->stack = stack; 3754887Schin stack = root; 3764887Schin root = root->left; 3774887Schin } 3784887Schin else 3794887Schin { 3804887Schin for (;;) 3814887Schin { 3824887Schin if (*top) 3834887Schin *bot = (*bot)->fts_link = root; 3844887Schin else 3854887Schin *bot = *top = root; 3864887Schin if (root->right) 3874887Schin { 3884887Schin root = root->right; 3894887Schin break; 3904887Schin } 3914887Schin if (!(root = stack)) 3924887Schin return; 3934887Schin stack = stack->stack; 3944887Schin } 3954887Schin } 3964887Schin } 3974887Schin } 3984887Schin 3994887Schin /* 4004887Schin * set directory when curdir is lost in space 4014887Schin */ 4024887Schin 4034887Schin static int 4044887Schin setdir(register char* home, register char* path) 4054887Schin { 4064887Schin register int cdrv; 4074887Schin 4084887Schin if (path[0] == '/') 4094887Schin cdrv = pathcd(path, NiL); 4104887Schin else 4114887Schin { 4124887Schin /* 4134887Schin * note that path and home are in the same buffer 4144887Schin */ 4154887Schin 4164887Schin path[-1] = '/'; 4174887Schin cdrv = pathcd(home, NiL); 4184887Schin path[-1] = 0; 4194887Schin } 4204887Schin if (cdrv < 0) 4214887Schin pathcd(home, NiL); 4224887Schin return cdrv; 4234887Schin } 4244887Schin 4254887Schin /* 4264887Schin * set to parent dir 4274887Schin */ 4284887Schin 4294887Schin static int 4304887Schin setpdir(register char* home, register char* path, register char* base) 4314887Schin { 4324887Schin register int c; 4334887Schin register int cdrv; 4344887Schin 4354887Schin if (base > path) 4364887Schin { 4374887Schin c = base[0]; 4384887Schin base[0] = 0; 4394887Schin cdrv = setdir(home, path); 4404887Schin base[0] = c; 4414887Schin } 4424887Schin else 4434887Schin cdrv = pathcd(home, NiL); 4444887Schin return cdrv; 4454887Schin } 4464887Schin 4474887Schin /* 4484887Schin * pop a set of directories 4494887Schin */ 4504887Schin static int 4514887Schin popdirs(FTS* fts) 4524887Schin { 4534887Schin register FTSENT*f; 4544887Schin register char* s; 4554887Schin register char* e; 4564887Schin #ifndef verify 4574887Schin register int verify; 4584887Schin #endif 4594887Schin struct stat sb; 4604887Schin char buf[PATH_MAX]; 4614887Schin 4624887Schin if (!(f = fts->curdir) || f->fts_level < 0) 4634887Schin return -1; 4644887Schin e = buf + sizeof(buf) - 4; 4654887Schin #ifndef verify 4664887Schin verify = 0; 4674887Schin #endif 4684887Schin while (fts->nd > 0) 4694887Schin { 4704887Schin for (s = buf; s < e && fts->nd > 0; fts->nd--) 4714887Schin { 4724887Schin if (fts->pwd) 4734887Schin { 4744887Schin #ifndef verify 4754887Schin verify |= fts->pwd->symlink; 4764887Schin #endif 4774887Schin fts->pwd = fts->pwd->pwd; 4784887Schin } 4794887Schin *s++ = '.'; 4804887Schin *s++ = '.'; 4814887Schin *s++ = '/'; 4824887Schin } 4834887Schin *s = 0; 4844887Schin if (chdir(buf)) 4854887Schin return -1; 4864887Schin } 4874887Schin return (verify && (stat(".", &sb) < 0 || !SAME(&sb, f->fts_statp))) ? -1 : 0; 4884887Schin } 4894887Schin 4904887Schin /* 4914887Schin * initialize st from path and fts_info from st 4924887Schin */ 4934887Schin 4944887Schin static int 4954887Schin info(FTS* fts, register FTSENT* f, const char* path, struct stat* sp, int flags) 4964887Schin { 4974887Schin if (path) 4984887Schin { 4994887Schin #ifdef S_ISLNK 5004887Schin if (!f->symlink && (ISTYPE(f, DT_UNKNOWN) || ISTYPE(f, DT_LNK))) 5014887Schin { 5024887Schin if (lstat(path, sp) < 0) 5034887Schin goto bad; 5044887Schin } 5054887Schin else 5064887Schin #endif 5074887Schin if (stat(path, sp) < 0) 5084887Schin goto bad; 5094887Schin } 5104887Schin #ifdef S_ISLNK 5114887Schin again: 5124887Schin #endif 5134887Schin if (S_ISDIR(sp->st_mode)) 5144887Schin { 5154887Schin if ((flags & FTS_NOSTAT) && !fts->fs3d) 5164887Schin { 5174887Schin f->fts_parent->nlink--; 5184887Schin #ifdef D_TYPE 5194887Schin if ((f->nlink = sp->st_nlink) < 2) 520*8462SApril.Chin@Sun.COM { 521*8462SApril.Chin@Sun.COM f->must = 2; 5224887Schin f->nlink = 2; 523*8462SApril.Chin@Sun.COM } 524*8462SApril.Chin@Sun.COM else 525*8462SApril.Chin@Sun.COM f->must = 0; 5264887Schin #else 5274887Schin if ((f->nlink = sp->st_nlink) >= 2) 5284887Schin f->must = 1; 5294887Schin else 5304887Schin f->must = 2; 5314887Schin #endif 5324887Schin } 5334887Schin else 5344887Schin f->must = 2; 5354887Schin TYPE(f, DT_DIR); 5364887Schin f->fts_info = FTS_D; 5374887Schin } 5384887Schin #ifdef S_ISLNK 5394887Schin else if (S_ISLNK((sp)->st_mode)) 5404887Schin { 5414887Schin struct stat sb; 5424887Schin 5434887Schin f->symlink = 1; 5444887Schin if (!(flags & FTS_PHYSICAL) && stat(path, &sb) >= 0) 5454887Schin { 5464887Schin *sp = sb; 5474887Schin flags = FTS_PHYSICAL; 5484887Schin goto again; 5494887Schin } 5504887Schin TYPE(f, DT_LNK); 5514887Schin f->fts_info = FTS_SL; 5524887Schin } 5534887Schin #endif 5544887Schin else 5554887Schin { 5564887Schin TYPE(f, DT_REG); 5574887Schin f->fts_info = FTS_F; 5584887Schin } 5594887Schin return 0; 5604887Schin bad: 5614887Schin TYPE(f, DT_UNKNOWN); 5624887Schin f->fts_info = FTS_NS; 5634887Schin return -1; 5644887Schin } 5654887Schin 5664887Schin /* 5674887Schin * get top list of elements to process 5684887Schin */ 5694887Schin 5704887Schin static FTSENT* 5714887Schin toplist(FTS* fts, register char* const* pathnames) 5724887Schin { 5734887Schin register char* path; 5744887Schin register struct stat* sb; 5754887Schin register FTSENT* f; 5764887Schin register FTSENT* root; 5774887Schin int physical; 5784887Schin int metaphysical; 5794887Schin char* s; 5804887Schin FTSENT* top; 5814887Schin FTSENT* bot; 5824887Schin struct stat st; 5834887Schin 5844887Schin if (fts->flags & FTS_NOSEEDOTDIR) 5854887Schin fts->flags &= ~FTS_SEEDOTDIR; 5864887Schin physical = (fts->flags & FTS_PHYSICAL); 5874887Schin metaphysical = (fts->flags & (FTS_META|FTS_PHYSICAL)) == (FTS_META|FTS_PHYSICAL); 5884887Schin top = bot = root = 0; 5894887Schin while (path = *pathnames++) 5904887Schin { 5914887Schin /* 5924887Schin * make elements 5934887Schin */ 5944887Schin 5954887Schin if (!(f = node(fts, fts->parent, path, strlen(path)))) 5964887Schin break; 5974887Schin path = f->fts_name; 5984887Schin if (!physical) 5994887Schin f->fts_namelen = (fts->flags & FTS_SEEDOTDIR) ? strlen(path) : (pathcanon(path, 0) - path); 6004887Schin else if (*path != '.') 6014887Schin { 6024887Schin f->fts_namelen = strlen(path); 6034887Schin fts->flags |= FTS_SEEDOTDIR; 6044887Schin } 6054887Schin else 6064887Schin { 6074887Schin if (fts->flags & FTS_NOSEEDOTDIR) 6084887Schin { 6094887Schin fts->flags &= ~FTS_SEEDOTDIR; 6104887Schin s = path; 6114887Schin while (*s++ == '.' && *s++ == '/') 6124887Schin { 6134887Schin while (*s == '/') 6144887Schin s++; 6154887Schin if (!*s) 6164887Schin break; 6174887Schin path = f->fts_name; 6184887Schin while (*path++ = *s++); 6194887Schin path = f->fts_name; 6204887Schin } 6214887Schin } 6224887Schin else 6234887Schin fts->flags |= FTS_SEEDOTDIR; 6244887Schin for (s = path + strlen(path); s > path && *(s - 1) == '/'; s--); 6254887Schin *s = 0; 6264887Schin f->fts_namelen = s - path; 6274887Schin } 6284887Schin sb = f->fts_statp; 6294887Schin if (!*path) 6304887Schin { 6314887Schin errno = ENOENT; 6324887Schin f->fts_info = FTS_NS; 6334887Schin } 6344887Schin else 6354887Schin info(fts, f, path, sb, fts->flags); 6364887Schin #ifdef S_ISLNK 6374887Schin 6384887Schin /* 6394887Schin * don't let any standards committee get 6404887Schin * away with calling your idea a hack 6414887Schin */ 6424887Schin 6434887Schin if (metaphysical && f->fts_info == FTS_SL && stat(path, &st) >= 0) 6444887Schin { 6454887Schin *sb = st; 6464887Schin info(fts, f, NiL, sb, 0); 6474887Schin } 6484887Schin #endif 6494887Schin if (fts->comparf) 6504887Schin root = search(f, root, fts->comparf, 1); 6514887Schin else if (bot) 6524887Schin { 6534887Schin bot->fts_link = f; 6544887Schin bot = f; 6554887Schin } 6564887Schin else 6574887Schin top = bot = f; 6584887Schin } 6594887Schin if (fts->comparf) 6604887Schin getlist(&top, &bot, root); 6614887Schin return top; 6624887Schin } 6634887Schin 6644887Schin /* 6654887Schin * resize the path buffer 6664887Schin * note that free() is not used because we may need to chdir(fts->home) 6674887Schin * if there isn't enough space to continue 6684887Schin */ 6694887Schin 6704887Schin static int 6714887Schin resize(register FTS* fts, int inc) 6724887Schin { 6734887Schin register char* old; 6744887Schin register char* newp; 6754887Schin register int n_old; 6764887Schin 6774887Schin /* 6784887Schin * add space for "/." used in testing FTS_DNX 6794887Schin */ 6804887Schin 6814887Schin n_old = fts->homesize; 6824887Schin fts->homesize = ((fts->homesize + inc + 4) / PATH_MAX + 1) * PATH_MAX; 6834887Schin if (!(newp = newof(0, char, fts->homesize, 0))) 6844887Schin { 6854887Schin fts->fts_errno = errno; 6864887Schin fts->state = FTS_error; 6874887Schin return -1; 6884887Schin } 6894887Schin old = fts->home; 6904887Schin fts->home = newp; 6914887Schin memcpy(newp, old, n_old); 6924887Schin if (fts->endbuf) 6934887Schin fts->endbuf = newp + fts->homesize - 4; 6944887Schin if (fts->path) 6954887Schin fts->path = newp + (fts->path - old); 6964887Schin if (fts->base) 6974887Schin fts->base = newp + (fts->base - old); 6984887Schin free(old); 6994887Schin return 0; 7004887Schin } 7014887Schin 7024887Schin /* 7034887Schin * open a new fts stream on pathnames 7044887Schin */ 7054887Schin 7064887Schin FTS* 7074887Schin fts_open(char* const* pathnames, int flags, int (*comparf)(FTSENT* const*, FTSENT* const*)) 7084887Schin { 7094887Schin register FTS* fts; 7104887Schin 7114887Schin if (!(fts = newof(0, FTS, 1, sizeof(FTSENT)))) 7124887Schin return 0; 7134887Schin fts->flags = flags; 7144887Schin fts->cd = (flags & FTS_NOCHDIR) ? 1 : -1; 7154887Schin fts->comparf = comparf; 7164887Schin fts->fs3d = fs3d(FS3D_TEST); 7174887Schin 7184887Schin /* 7194887Schin * set up the path work buffer 7204887Schin */ 7214887Schin 7224887Schin fts->homesize = 2 * PATH_MAX; 7234887Schin for (;;) 7244887Schin { 7254887Schin if (!(fts->home = newof(fts->home, char, fts->homesize, 0))) 7264887Schin { 7274887Schin free(fts); 7284887Schin return 0; 7294887Schin } 7304887Schin if (fts->cd > 0 || getcwd(fts->home, fts->homesize)) 7314887Schin break; 7324887Schin if (errno == ERANGE) 7334887Schin fts->homesize += PATH_MAX; 7344887Schin else 7354887Schin fts->cd = 1; 7364887Schin } 7374887Schin fts->endbuf = fts->home + fts->homesize - 4; 7384887Schin 7394887Schin /* 7404887Schin * initialize the tippity-top 7414887Schin */ 7424887Schin 7434887Schin fts->parent = (FTSENT*)(fts + 1); 7444887Schin fts->parent->fts_info = FTS_D; 7454887Schin memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2); 7464887Schin fts->parent->fts_level = -1; 7474887Schin fts->parent->fts_statp = &fts->parent->statb; 7484887Schin fts->parent->must = 2; 7494887Schin fts->parent->type = DT_UNKNOWN; 7504887Schin fts->path = fts->home + strlen(fts->home) + 1; 7514887Schin 7524887Schin /* 7534887Schin * make the list of top elements 7544887Schin */ 7554887Schin 7564887Schin if (!pathnames || (flags & FTS_ONEPATH) || !*pathnames) 7574887Schin { 7584887Schin char* v[2]; 7594887Schin 7604887Schin v[0] = pathnames && (flags & FTS_ONEPATH) ? (char*)pathnames : "."; 7614887Schin v[1] = 0; 7624887Schin fts->todo = toplist(fts, v); 7634887Schin } 7644887Schin else 7654887Schin fts->todo = toplist(fts, pathnames); 7664887Schin if (!fts->todo || fts->todo->fts_info == FTS_NS && !fts->todo->fts_link) 7674887Schin { 7684887Schin fts_close(fts); 7694887Schin return 0; 7704887Schin } 7714887Schin return fts; 7724887Schin } 7734887Schin 7744887Schin /* 7754887Schin * return the next FTS entry 7764887Schin */ 7774887Schin 7784887Schin FTSENT* 7794887Schin fts_read(register FTS* fts) 7804887Schin { 7814887Schin register char* s; 7824887Schin register int n; 7834887Schin register FTSENT* f; 7844887Schin struct dirent* d; 7854887Schin int i; 7864887Schin FTSENT* t; 7874887Schin Notify_t* p; 7884887Schin #ifdef verify 7894887Schin struct stat sb; 7904887Schin #endif 7914887Schin 7924887Schin for (;;) switch (fts->state) 7934887Schin { 7944887Schin 7954887Schin case FTS_top_return: 7964887Schin 7974887Schin f = fts->todo; 7984887Schin t = 0; 7994887Schin while (f) 8004887Schin if (f->status == FTS_SKIP) 8014887Schin { 8024887Schin if (t) 8034887Schin { 8044887Schin t->fts_link = f->fts_link; 8054887Schin drop(fts, f); 8064887Schin f = t->fts_link; 8074887Schin } 8084887Schin else 8094887Schin { 8104887Schin fts->todo = f->fts_link; 8114887Schin drop(fts, f); 8124887Schin f = fts->todo; 8134887Schin } 8144887Schin } 8154887Schin else 8164887Schin { 8174887Schin t = f; 8184887Schin f = f->fts_link; 8194887Schin } 8204887Schin /*FALLTHROUGH*/ 8214887Schin 8224887Schin case 0: 8234887Schin 8244887Schin if (!(f = fts->todo)) 8254887Schin return 0; 8264887Schin /*FALLTHROUGH*/ 8274887Schin 8284887Schin case FTS_todo: 8294887Schin 8304887Schin /* 8314887Schin * process the top object on the stack 8324887Schin */ 8334887Schin 8344887Schin fts->root = fts->top = fts->bot = 0; 8354887Schin 8364887Schin /* 8374887Schin * initialize the top level 8384887Schin */ 8394887Schin 8404887Schin if (f->fts_level == 0) 8414887Schin { 8424887Schin fts->parent->fts_number = f->fts_number; 8434887Schin fts->parent->fts_pointer = f->fts_pointer; 8444887Schin fts->parent->fts_statp = f->fts_statp; 8454887Schin fts->parent->statb = *f->fts_statp; 8464887Schin f->fts_parent = fts->parent; 8474887Schin fts->diroot = 0; 8484887Schin if (fts->cd == 0) 8494887Schin pathcd(fts->home, NiL); 8504887Schin else if (fts->cd < 0) 8514887Schin fts->cd = 0; 8524887Schin fts->pwd = f->fts_parent; 8534887Schin fts->curdir = fts->cd ? 0 : f->fts_parent; 8544887Schin *(fts->base = fts->path) = 0; 8554887Schin } 8564887Schin 8574887Schin /* 8584887Schin * chdir to parent if asked for 8594887Schin */ 8604887Schin 8614887Schin if (fts->cd < 0) 8624887Schin { 8634887Schin fts->cd = setdir(fts->home, fts->path); 8644887Schin fts->pwd = f->fts_parent; 8654887Schin fts->curdir = fts->cd ? 0 : f->fts_parent; 8664887Schin } 8674887Schin 8684887Schin /* 8694887Schin * add object's name to the path 8704887Schin */ 8714887Schin 8724887Schin if ((fts->baselen = f->fts_namelen) >= (fts->endbuf - fts->base) && resize(fts, fts->baselen)) 8734887Schin return 0; 8744887Schin memcpy(fts->base, f->name, fts->baselen + 1); 8754887Schin fts->name = fts->cd ? fts->path : fts->base; 8764887Schin /*FALLTHROUGH*/ 8774887Schin 8784887Schin case FTS_preorder: 8794887Schin 8804887Schin /* 8814887Schin * check for cycle and open dir 8824887Schin */ 8834887Schin 8844887Schin if (f->fts_info == FTS_D) 8854887Schin { 8864887Schin if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0) 8874887Schin { 8884887Schin f->fts_info = FTS_DC; 8894887Schin f->fts_cycle = fts->diroot; 8904887Schin } 8914887Schin else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev)) 8924887Schin { 8934887Schin /* 8944887Schin * buffer is known to be large enough here! 8954887Schin */ 8964887Schin 8974887Schin if (fts->base[fts->baselen - 1] != '/') 8984887Schin memcpy(fts->base + fts->baselen, "/.", 3); 8994887Schin if (!(fts->dir = opendir(fts->name))) 9004887Schin f->fts_info = FTS_DNX; 9014887Schin fts->base[fts->baselen] = 0; 9024887Schin if (!fts->dir && !(fts->dir = opendir(fts->name))) 9034887Schin f->fts_info = FTS_DNR; 9044887Schin } 9054887Schin } 9064887Schin f->nd = f->fts_info & ~FTS_DNX; 9074887Schin if (f->nd || !(fts->flags & FTS_NOPREORDER)) 9084887Schin { 9094887Schin fts->current = f; 9104887Schin fts->link = f->fts_link; 9114887Schin f->fts_link = 0; 9124887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 9134887Schin f->fts_pathlen = (fts->base - f->fts_path) + fts->baselen; 9144887Schin f->fts_accpath = ACCESS(fts, f); 9154887Schin fts->state = FTS_preorder_return; 9164887Schin goto note; 9174887Schin } 9184887Schin /*FALLTHROUGH*/ 9194887Schin 9204887Schin case FTS_preorder_resume: 9214887Schin 9224887Schin /* 9234887Schin * prune 9244887Schin */ 9254887Schin 9264887Schin if (!fts->dir || f->nd || f->status == FTS_SKIP) 9274887Schin { 9284887Schin if (fts->dir) 9294887Schin { 9304887Schin closedir(fts->dir); 9314887Schin fts->dir = 0; 9324887Schin } 9334887Schin fts->state = FTS_popstack; 9344887Schin continue; 9354887Schin } 9364887Schin 9374887Schin /* 9384887Schin * FTS_D or FTS_DNX, about to read children 9394887Schin */ 9404887Schin 9414887Schin if (fts->cd == 0) 9424887Schin { 9434887Schin if ((fts->cd = chdir(fts->name)) < 0) 9444887Schin pathcd(fts->home, NiL); 9454887Schin else if (fts->pwd != f) 9464887Schin { 9474887Schin f->pwd = fts->pwd; 9484887Schin fts->pwd = f; 9494887Schin } 9504887Schin fts->curdir = fts->cd < 0 ? 0 : f; 9514887Schin } 9524887Schin fts->nostat = fts->children > 1 || f->fts_info == FTS_DNX; 9534887Schin fts->cpname = fts->cd && !fts->nostat || !fts->children && !fts->comparf; 9544887Schin fts->dotdot = 0; 9554887Schin fts->endbase = fts->base + fts->baselen; 9564887Schin if (fts->endbase[-1] != '/') 9574887Schin *fts->endbase++ = '/'; 9584887Schin fts->current = f; 9594887Schin /*FALLTHROUGH*/ 9604887Schin 9614887Schin case FTS_readdir: 9624887Schin 9634887Schin while (d = readdir(fts->dir)) 9644887Schin { 9654887Schin s = d->d_name; 9664887Schin if (s[0] == '.') 9674887Schin { 9684887Schin if (s[1] == 0) 9694887Schin { 9704887Schin fts->current->nlink--; 9714887Schin if (!(fts->flags & FTS_SEEDOT)) 9724887Schin continue; 9734887Schin n = 1; 9744887Schin } 9754887Schin else if (s[1] == '.' && s[2] == 0) 9764887Schin { 9774887Schin fts->current->nlink--; 9784887Schin if (fts->current->must == 1) 9794887Schin fts->current->must = 0; 9804887Schin if (!(fts->flags & FTS_SEEDOT)) 9814887Schin continue; 9824887Schin n = 2; 9834887Schin } 9844887Schin else 9854887Schin n = 0; 9864887Schin } 9874887Schin else 9884887Schin n = 0; 9894887Schin 9904887Schin /* 9914887Schin * make a new entry 9924887Schin */ 9934887Schin 9944887Schin i = D_NAMLEN(d); 9954887Schin if (!(f = node(fts, fts->current, s, i))) 9964887Schin return 0; 9974887Schin TYPE(f, D_TYPE(d)); 9984887Schin 9994887Schin /* 10004887Schin * check for space 10014887Schin */ 10024887Schin 10034887Schin if (i >= fts->endbuf - fts->endbase) 10044887Schin { 10054887Schin if (resize(fts, i)) 10064887Schin return 0; 10074887Schin fts->endbase = fts->base + fts->baselen; 10084887Schin if (fts->endbase[-1] != '/') 10094887Schin fts->endbase++; 10104887Schin } 10114887Schin if (fts->cpname) 10124887Schin { 10134887Schin memcpy(fts->endbase, s, i + 1); 10144887Schin if (fts->cd) 10154887Schin s = fts->path; 10164887Schin } 10174887Schin if (n) 10184887Schin { 10194887Schin /* 10204887Schin * don't recurse on . and .. 10214887Schin */ 10224887Schin 10234887Schin if (n == 1) 10244887Schin f->fts_statp = fts->current->fts_statp; 10254887Schin else 10264887Schin { 10274887Schin if (f->fts_info != FTS_NS) 10284887Schin fts->dotdot = f; 10294887Schin if (fts->current->fts_parent->fts_level < 0) 10304887Schin { 10314887Schin f->fts_statp = &fts->current->fts_parent->statb; 10324887Schin info(fts, f, s, f->fts_statp, 0); 10334887Schin } 10344887Schin else 10354887Schin f->fts_statp = fts->current->fts_parent->fts_statp; 10364887Schin } 10374887Schin f->fts_info = FTS_DOT; 10384887Schin } 10394887Schin else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags)) 10404887Schin f->statb.st_ino = D_FILENO(d); 10414887Schin if (fts->comparf) 10424887Schin fts->root = search(f, fts->root, fts->comparf, 1); 10434887Schin else if (fts->children || f->fts_info == FTS_D || f->fts_info == FTS_SL) 10444887Schin { 10454887Schin if (fts->top) 10464887Schin fts->bot = fts->bot->fts_link = f; 10474887Schin else 10484887Schin fts->top = fts->bot = f; 10494887Schin } 10504887Schin else 10514887Schin { 10524887Schin /* 10534887Schin * terminal node 10544887Schin */ 10554887Schin 10564887Schin f->fts_path = PATH(fts, fts->path, 1); 10574887Schin f->fts_pathlen = fts->endbase - f->fts_path + f->fts_namelen; 10584887Schin f->fts_accpath = ACCESS(fts, f); 10594887Schin fts->previous = fts->current; 10604887Schin fts->current = f; 10614887Schin fts->state = FTS_terminal; 10624887Schin goto note; 10634887Schin } 10644887Schin } 10654887Schin 10664887Schin /* 10674887Schin * done with the directory 10684887Schin */ 10694887Schin 10704887Schin closedir(fts->dir); 10714887Schin fts->dir = 0; 10724887Schin if (fts->root) 10734887Schin getlist(&fts->top, &fts->bot, fts->root); 10744887Schin if (fts->children) 10754887Schin { 10764887Schin /* 10774887Schin * try moving back to parent dir 10784887Schin */ 10794887Schin 10804887Schin fts->base[fts->baselen] = 0; 10814887Schin if (fts->cd <= 0) 10824887Schin { 10834887Schin f = fts->current->fts_parent; 10844887Schin if (fts->cd < 0 10854887Schin || f != fts->curdir 10864887Schin || !fts->dotdot 10874887Schin || !SAME(f->fts_statp, fts->dotdot->fts_statp) 10884887Schin || fts->pwd && fts->pwd->symlink 10894887Schin || (fts->cd = chdir("..")) < 0 10904887Schin #ifdef verify 10914887Schin || stat(".", &sb) < 0 10924887Schin || !SAME(&sb, fts->dotdot->fts_statp) 10934887Schin #endif 10944887Schin ) 10954887Schin fts->cd = setpdir(fts->home, fts->path, fts->base); 10964887Schin if (fts->pwd) 10974887Schin fts->pwd = fts->pwd->pwd; 10984887Schin fts->curdir = fts->cd ? 0 : f; 10994887Schin } 11004887Schin f = fts->current; 11014887Schin fts->link = f->fts_link; 11024887Schin f->fts_link = fts->top; 11034887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 11044887Schin f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; 11054887Schin f->fts_accpath = ACCESS(fts, f); 11064887Schin fts->state = FTS_children_return; 11074887Schin goto note; 11084887Schin } 11094887Schin /*FALLTHROUGH*/ 11104887Schin 11114887Schin case FTS_children_resume: 11124887Schin 11134887Schin fts->base[fts->baselen] = 0; 11144887Schin if (fts->top) 11154887Schin { 11164887Schin fts->bot->fts_link = fts->todo; 11174887Schin fts->todo = fts->top; 11184887Schin fts->top = 0; 11194887Schin } 11204887Schin /*FALLTHROUGH*/ 11214887Schin 11224887Schin case FTS_popstack: 11234887Schin 11244887Schin /* 11254887Schin * pop objects completely processed 11264887Schin */ 11274887Schin 11284887Schin fts->nd = 0; 11294887Schin f = fts->current; 11304887Schin /*FALLTHROUGH*/ 11314887Schin 11324887Schin case FTS_popstack_resume: 11334887Schin 11344887Schin while (fts->todo && f == fts->todo) 11354887Schin { 11364887Schin t = f->fts_parent; 11374887Schin if ((f->fts_info & FTS_DP) == FTS_D) 11384887Schin { 11394887Schin /* 11404887Schin * delete from <dev,ino> tree 11414887Schin */ 11424887Schin 11434887Schin if (f != fts->diroot) 11444887Schin fts->diroot = search(f, fts->diroot, statcmp, 0); 11454887Schin fts->diroot = deleteroot(fts->diroot); 11464887Schin if (f == fts->curdir) 11474887Schin { 11484887Schin fts->nd++; 11494887Schin fts->curdir = t; 11504887Schin } 11514887Schin 11524887Schin /* 11534887Schin * perform post-order processing 11544887Schin */ 11554887Schin 11564887Schin if (!(fts->flags & FTS_NOPOSTORDER) && 11574887Schin f->status != FTS_SKIP && 11584887Schin f->status != FTS_NOPOSTORDER) 11594887Schin { 11604887Schin /* 11614887Schin * move to parent dir 11624887Schin */ 11634887Schin 11644887Schin if (fts->nd > 0) 11654887Schin fts->cd = popdirs(fts); 11664887Schin if (fts->cd < 0) 11674887Schin fts->cd = setpdir(fts->home, fts->path, fts->base); 11684887Schin fts->curdir = fts->cd ? 0 : t; 11694887Schin f->fts_info = FTS_DP; 11704887Schin f->fts_path = PATH(fts, fts->path, f->fts_level); 11714887Schin f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; 11724887Schin f->fts_accpath = ACCESS(fts, f); 11734887Schin 11744887Schin /* 11754887Schin * re-stat to update nlink/times 11764887Schin */ 11774887Schin 11784887Schin stat(f->fts_accpath, f->fts_statp); 11794887Schin fts->link = f->fts_link; 11804887Schin f->fts_link = 0; 11814887Schin fts->state = FTS_popstack_return; 11824887Schin goto note; 11834887Schin } 11844887Schin } 11854887Schin 11864887Schin /* 11874887Schin * reset base 11884887Schin */ 11894887Schin 11904887Schin if (fts->base > fts->path + t->fts_namelen) 11914887Schin fts->base--; 11924887Schin *fts->base = 0; 11934887Schin fts->base -= t->fts_namelen; 11944887Schin 11954887Schin /* 11964887Schin * try again or delete from top of stack 11974887Schin */ 11984887Schin 11994887Schin if (f->status == FTS_AGAIN) 12004887Schin { 12014887Schin f->fts_info = FTS_D; 12024887Schin f->status = 0; 12034887Schin } 12044887Schin else 12054887Schin { 12064887Schin fts->todo = fts->todo->fts_link; 12074887Schin drop(fts, f); 12084887Schin } 12094887Schin f = t; 12104887Schin } 12114887Schin 12124887Schin /* 12134887Schin * reset current directory 12144887Schin */ 12154887Schin 12164887Schin if (fts->nd > 0 && popdirs(fts) < 0) 12174887Schin { 12184887Schin pathcd(fts->home, NiL); 12194887Schin fts->curdir = 0; 12204887Schin fts->cd = -1; 12214887Schin } 12224887Schin if (fts->todo) 12234887Schin { 12244887Schin if (*fts->base) 12254887Schin fts->base += f->fts_namelen; 12264887Schin if (*(fts->base - 1) != '/') 12274887Schin *fts->base++ = '/'; 12284887Schin *fts->base = 0; 12294887Schin f = fts->todo; 12304887Schin fts->state = FTS_todo; 12314887Schin continue; 12324887Schin } 12334887Schin return 0; 12344887Schin 12354887Schin case FTS_children_return: 12364887Schin 12374887Schin f = fts->current; 12384887Schin f->fts_link = fts->link; 12394887Schin 12404887Schin /* 12414887Schin * chdir down again 12424887Schin */ 12434887Schin 12444887Schin i = f->fts_info != FTS_DNX; 12454887Schin n = f->status == FTS_SKIP; 12464887Schin if (!n && fts->cd == 0) 12474887Schin { 12484887Schin if ((fts->cd = chdir(fts->base)) < 0) 12494887Schin pathcd(fts->home, NiL); 12504887Schin else if (fts->pwd != f) 12514887Schin { 12524887Schin f->pwd = fts->pwd; 12534887Schin fts->pwd = f; 12544887Schin } 12554887Schin fts->curdir = fts->cd ? 0 : f; 12564887Schin } 12574887Schin 12584887Schin /* 12594887Schin * prune 12604887Schin */ 12614887Schin 12624887Schin if (fts->base[fts->baselen - 1] != '/') 12634887Schin fts->base[fts->baselen] = '/'; 12644887Schin for (fts->bot = 0, f = fts->top; f; ) 12654887Schin if (n || f->status == FTS_SKIP) 12664887Schin { 12674887Schin if (fts->bot) 12684887Schin fts->bot->fts_link = f->fts_link; 12694887Schin else 12704887Schin fts->top = f->fts_link; 12714887Schin drop(fts, f); 12724887Schin f = fts->bot ? fts->bot->fts_link : fts->top; 12734887Schin } 12744887Schin else 12754887Schin { 12764887Schin if (fts->children > 1 && i) 12774887Schin { 12784887Schin if (f->status == FTS_STAT) 12794887Schin info(fts, f, NiL, f->fts_statp, 0); 12804887Schin else if (f->fts_info == FTS_NSOK && !SKIP(fts, f)) 12814887Schin { 12824887Schin s = f->fts_name; 12834887Schin if (fts->cd) 12844887Schin { 12854887Schin memcpy(fts->endbase, s, f->fts_namelen + 1); 12864887Schin s = fts->path; 12874887Schin } 12884887Schin info(fts, f, s, f->fts_statp, fts->flags); 12894887Schin } 12904887Schin } 12914887Schin fts->bot = f; 12924887Schin f = f->fts_link; 12934887Schin } 12944887Schin fts->children = 0; 12954887Schin fts->state = FTS_children_resume; 12964887Schin continue; 12974887Schin 12984887Schin case FTS_popstack_return: 12994887Schin 13004887Schin f = fts->todo; 13014887Schin f->fts_link = fts->link; 13024887Schin f->fts_info = f->status == FTS_AGAIN ? FTS_DP : 0; 13034887Schin fts->state = FTS_popstack_resume; 13044887Schin continue; 13054887Schin 13064887Schin case FTS_preorder_return: 13074887Schin 13084887Schin f = fts->current; 13094887Schin f->fts_link = fts->link; 13104887Schin 13114887Schin /* 13124887Schin * follow symlink if asked to 13134887Schin */ 13144887Schin 13154887Schin if (f->status == FTS_FOLLOW) 13164887Schin { 13174887Schin f->status = 0; 13184887Schin if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) 13194887Schin { 13204887Schin info(fts, f, f->fts_accpath, f->fts_statp, 0); 13214887Schin if (f->fts_info != FTS_SL) 13224887Schin { 13234887Schin fts->state = FTS_preorder; 13244887Schin continue; 13254887Schin } 13264887Schin } 13274887Schin } 13284887Schin 13294887Schin /* 13304887Schin * about to prune this f and already at home 13314887Schin */ 13324887Schin 13334887Schin if (fts->cd == 0 && f->fts_level == 0 && f->nd) 13344887Schin fts->cd = -1; 13354887Schin fts->state = FTS_preorder_resume; 13364887Schin continue; 13374887Schin 13384887Schin case FTS_terminal: 13394887Schin 13404887Schin f = fts->current; 13414887Schin if (f->status == FTS_FOLLOW) 13424887Schin { 13434887Schin f->status = 0; 13444887Schin if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) 13454887Schin { 13464887Schin info(fts, f, f->fts_accpath, f->fts_statp, 0); 13474887Schin if (f->symlink && f->fts_info != FTS_SL) 13484887Schin { 13494887Schin if (!(f->fts_link = fts->top)) 13504887Schin fts->bot = f; 13514887Schin fts->top = f; 13524887Schin fts->current = fts->previous; 13534887Schin fts->state = FTS_readdir; 13544887Schin continue; 13554887Schin } 13564887Schin } 13574887Schin } 13584887Schin f = f->fts_parent; 13594887Schin drop(fts, fts->current); 13604887Schin fts->current = f; 13614887Schin fts->state = FTS_readdir; 13624887Schin continue; 13634887Schin 13644887Schin case FTS_error: 13654887Schin 13664887Schin return 0; 13674887Schin 13684887Schin default: 13694887Schin 13704887Schin fts->fts_errno = EINVAL; 13714887Schin fts->state = FTS_error; 13724887Schin return 0; 13734887Schin 13744887Schin } 13754887Schin note: 13764887Schin for (p = notify; p; p = p->next) 13774887Schin if ((n = (*p->notifyf)(fts, f, p->context)) > 0) 13784887Schin break; 13794887Schin else if (n < 0) 13804887Schin { 13814887Schin fts->fts_errno = EINVAL; 13824887Schin fts->state = FTS_error; 13834887Schin return 0; 13844887Schin } 13854887Schin return f; 13864887Schin } 13874887Schin 13884887Schin /* 13894887Schin * set stream or entry flags 13904887Schin */ 13914887Schin 13924887Schin int 13934887Schin fts_set(register FTS* fts, register FTSENT* f, int status) 13944887Schin { 13954887Schin if (fts || !f || f->fts->current != f) 13964887Schin return -1; 13974887Schin switch (status) 13984887Schin { 13994887Schin case FTS_AGAIN: 14004887Schin break; 14014887Schin case FTS_FOLLOW: 14024887Schin if (!(f->fts_info & FTS_SL)) 14034887Schin return -1; 14044887Schin break; 14054887Schin case FTS_NOPOSTORDER: 14064887Schin break; 14074887Schin case FTS_SKIP: 14084887Schin if ((f->fts_info & (FTS_D|FTS_P)) != FTS_D) 14094887Schin return -1; 14104887Schin break; 14114887Schin default: 14124887Schin return -1; 14134887Schin } 14144887Schin f->status = status; 14154887Schin return 0; 14164887Schin } 14174887Schin 14184887Schin /* 14194887Schin * return the list of child entries 14204887Schin */ 14214887Schin 14224887Schin FTSENT* 14234887Schin fts_children(register FTS* fts, int flags) 14244887Schin { 14254887Schin register FTSENT* f; 14264887Schin 14274887Schin switch (fts->state) 14284887Schin { 14294887Schin 14304887Schin case 0: 14314887Schin 14324887Schin fts->state = FTS_top_return; 14334887Schin return fts->todo; 14344887Schin 14354887Schin case FTS_preorder_return: 14364887Schin 14374887Schin fts->children = ((flags | fts->flags) & FTS_NOSTAT) ? 2 : 1; 14384887Schin if (f = fts_read(fts)) 14394887Schin f = f->fts_link; 14404887Schin return f; 14414887Schin 14424887Schin } 14434887Schin return 0; 14444887Schin } 14454887Schin 14464887Schin /* 14474887Schin * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags 14484887Schin * conditioned by astconf() 14494887Schin */ 14504887Schin 14514887Schin int 14524887Schin fts_flags(void) 14534887Schin { 14544887Schin register char* s; 14554887Schin 14564887Schin s = astconf("PATH_RESOLVE", NiL, NiL); 14574887Schin if (streq(s, "logical")) 14584887Schin return FTS_LOGICAL; 14594887Schin if (streq(s, "physical")) 14604887Schin return FTS_PHYSICAL|FTS_SEEDOTDIR; 14614887Schin return FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR; 14624887Schin } 14634887Schin 14644887Schin /* 1465*8462SApril.Chin@Sun.COM * return 1 if ent is mounted on a local filesystem 1466*8462SApril.Chin@Sun.COM */ 1467*8462SApril.Chin@Sun.COM 1468*8462SApril.Chin@Sun.COM int 1469*8462SApril.Chin@Sun.COM fts_local(FTSENT* ent) 1470*8462SApril.Chin@Sun.COM { 1471*8462SApril.Chin@Sun.COM #ifdef ST_LOCAL 1472*8462SApril.Chin@Sun.COM struct statvfs fs; 1473*8462SApril.Chin@Sun.COM 1474*8462SApril.Chin@Sun.COM return statvfs(ent->fts_path, &fs) || (fs.f_flag & ST_LOCAL); 1475*8462SApril.Chin@Sun.COM #else 1476*8462SApril.Chin@Sun.COM return !strgrpmatch(fmtfs(ent->fts_statp), "([an]fs|samb)", NiL, 0, STR_LEFT|STR_ICASE); 1477*8462SApril.Chin@Sun.COM #endif 1478*8462SApril.Chin@Sun.COM } 1479*8462SApril.Chin@Sun.COM 1480*8462SApril.Chin@Sun.COM /* 14814887Schin * close an open fts stream 14824887Schin */ 14834887Schin 14844887Schin int 14854887Schin fts_close(register FTS* fts) 14864887Schin { 14874887Schin register FTSENT* f; 14884887Schin register FTSENT* x; 14894887Schin 14904887Schin if (fts->dir) 14914887Schin closedir(fts->dir); 14924887Schin if (fts->cd == 0) 14934887Schin pathcd(fts->home, NiL); 14944887Schin free(fts->home); 14954887Schin if (fts->state == FTS_children_return) 14964887Schin fts->current->fts_link = fts->link; 14974887Schin if (fts->top) 14984887Schin { 14994887Schin fts->bot->fts_link = fts->todo; 15004887Schin fts->todo = fts->top; 15014887Schin } 15024887Schin for (f = fts->todo; f; f = x) 15034887Schin { 15044887Schin x = f->fts_link; 15054887Schin free(f); 15064887Schin } 15074887Schin for (f = fts->free; f; f = x) 15084887Schin { 15094887Schin x = f->fts_link; 15104887Schin free(f); 15114887Schin } 1512*8462SApril.Chin@Sun.COM free(fts); 15134887Schin return 0; 15144887Schin } 15154887Schin 15164887Schin /* 15174887Schin * register function to be called for each fts_read() entry 1518*8462SApril.Chin@Sun.COM * context==0 => unregister notifyf 15194887Schin */ 15204887Schin 15214887Schin int 15224887Schin fts_notify(Notify_f notifyf, void* context) 15234887Schin { 15244887Schin register Notify_t* np; 1525*8462SApril.Chin@Sun.COM register Notify_t* pp; 15264887Schin 1527*8462SApril.Chin@Sun.COM if (context) 1528*8462SApril.Chin@Sun.COM { 1529*8462SApril.Chin@Sun.COM if (!(np = newof(0, Notify_t, 1, 0))) 1530*8462SApril.Chin@Sun.COM return -1; 1531*8462SApril.Chin@Sun.COM np->notifyf = notifyf; 1532*8462SApril.Chin@Sun.COM np->context = context; 1533*8462SApril.Chin@Sun.COM np->next = notify; 1534*8462SApril.Chin@Sun.COM notify = np; 1535*8462SApril.Chin@Sun.COM } 1536*8462SApril.Chin@Sun.COM else 1537*8462SApril.Chin@Sun.COM { 1538*8462SApril.Chin@Sun.COM for (np = notify, pp = 0; np; pp = np, np = np->next) 1539*8462SApril.Chin@Sun.COM if (np->notifyf == notifyf) 1540*8462SApril.Chin@Sun.COM { 1541*8462SApril.Chin@Sun.COM if (pp) 1542*8462SApril.Chin@Sun.COM pp->next = np->next; 1543*8462SApril.Chin@Sun.COM else 1544*8462SApril.Chin@Sun.COM notify = np->next; 1545*8462SApril.Chin@Sun.COM free(np); 1546*8462SApril.Chin@Sun.COM return 0; 1547*8462SApril.Chin@Sun.COM } 15484887Schin return -1; 1549*8462SApril.Chin@Sun.COM } 15504887Schin return 0; 15514887Schin } 1552