2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: ftwalk.c /main/3 1995/11/01 17:55:51 rswiston $ */
24 /***************************************************************
26 * AT&T - PROPRIETARY *
28 * THIS IS PROPRIETARY SOURCE CODE LICENSED BY *
31 * Copyright (c) 1995 AT&T Corp. *
32 * All Rights Reserved *
34 * This software is licensed by AT&T Corp. *
35 * under the terms and conditions of the license in *
36 * http://www.research.att.com/orgs/ssr/book/reuse *
38 * This software was created by the *
39 * Software Engineering Research Department *
40 * AT&T Bell Laboratories *
42 * For further information contact *
43 * gsf@research.att.com *
45 ***************************************************************/
47 /* : : generated by proto : : */
49 #if !defined(__PROTO__)
50 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
51 #if defined(__cplusplus)
52 #define __MANGLE__ "C"
57 #define __PROTO__(x) x
59 #define __PARAM__(n,o) n
60 #if !defined(__STDC__) && !defined(__cplusplus)
61 #if !defined(c_plusplus)
72 #define __PROTO__(x) ()
73 #define __OTORP__(x) x
74 #define __PARAM__(n,o) o
82 #if defined(__cplusplus) || defined(c_plusplus)
83 #define __VARARG__ ...
87 #if defined(__STDARG__)
88 #define __VA_START__(p,a) va_start(p,a)
90 #define __VA_START__(p,a) va_start(p)
103 /* function to determine type of an object */
105 #define TYPE(m) (S_ISDIR(m) ? FTW_D : S_ISLNK(m) ? FTW_SL : FTW_F)
107 #define TYPE(m) (S_ISDIR(m) ? FTW_D : FTW_F)
110 /* to set state.status and state.base on calls to user function */
111 #define STATUS(cdrv) (cdrv == 0 ? FTW_NAME : FTW_PATH)
113 /* see if same object */
114 #define SAME(one,two) ((one).st_ino == (two).st_ino && (one).st_dev == (two).st_dev)
117 #define PATH(p,l) ((l) > 0 && (p)[0] == '.' && (p)[1] == '/' ? (p)+2 : (p))
120 Make/free an object of type FTW.
123 #define freeFtw(f) ((f)->link = Free, Free = (f))
125 static Ftw_t *newFtw __PARAM__((register char* name, register int namelen), (name, namelen)) __OTORP__(register char* name; register int namelen;){
129 if(Free && namelen < MINNAME)
130 f = Free, Free = f->link;
133 amount = namelen < MINNAME ? MINNAME : namelen+1;
134 if(!(f = newof(0, Ftw_t, 1, amount-sizeof(int))))
139 f->local.pointer = 0;
140 f->status = FTW_NAME;
141 f->namelen = namelen;
142 memcpy(f->name,name,namelen+1);
147 static int freeAll __PARAM__((register Ftw_t* f, register int rv), (f, rv)) __OTORP__(register Ftw_t* f; register int rv;){
148 register Ftw_t *next;
149 register int freeing;
151 for(freeing = 0; freeing < 2; ++freeing)
166 To compare directories by device/inode.
168 static int statcmp __PARAM__((register Ftw_t* f1, register Ftw_t* f2), (f1, f2)) __OTORP__(register Ftw_t* f1; register Ftw_t* f2;){
170 if((d = f1->statb.st_ino - f2->statb.st_ino) != 0)
172 if((d = f1->statb.st_dev - f2->statb.st_dev) != 0)
174 /* hack for NFS system where (dev,ino) do not uniquely identify objects */
175 return (f1->statb.st_mtime - f2->statb.st_mtime);
179 Search trees with top-down splaying (a la Tarjan and Sleator).
180 When used for insertion sort, this implements a stable sort.
182 #define RROTATE(r) (t = r->left, r->left = t->right, t->right = r, r = t)
183 #define LROTATE(r) (t = r->right, r->right = t->left, t->left = r, r = t)
185 static Ftw_t *search __PARAM__((register Ftw_t* e, register Ftw_t* root, int(*comparf)(Ftw_t*, Ftw_t*), int insert), (e, root, comparf, insert)) __OTORP__(register Ftw_t* e; register Ftw_t* root; int(*comparf)(); int insert;){
187 register Ftw_t *t, *left, *right, *lroot, *rroot;
189 left = right = lroot = rroot = 0;
192 if((cmp = (*comparf)(e,root)) == 0 && !insert)
195 { /* this is the left zig-zig case */
196 if(root->left && (cmp = (*comparf)(e,root->left)) <= 0)
199 if(cmp == 0 && !insert)
203 /* stick all things > e to the right tree */
212 { /* this is the right zig-zig case */
213 if(root->right && (cmp = (*comparf)(e,root->right)) >= 0)
216 if(cmp == 0 && !insert)
220 /* stick all things <= e to the left tree */
235 right->left = root->right;
236 else rroot = root->right;
238 left->right = root->left;
239 else lroot = root->left;
248 ** Delete the root element from the tree
250 static Ftw_t *deleteroot __PARAM__((register Ftw_t* root), (root)) __OTORP__(register Ftw_t* root;){
251 register Ftw_t *t, *left, *right;
268 Convert a binary search tree into a sorted todo (link) list
270 static void getlist __PARAM__((register Ftw_t** top, register Ftw_t** bot, register Ftw_t* root), (top, bot, root)) __OTORP__(register Ftw_t** top; register Ftw_t** bot; register Ftw_t* root;){
272 getlist(top,bot,root->left);
273 if (*top) (*bot)->link = root, *bot = root;
274 else *top = *bot = root;
276 getlist(top,bot,root->right);
280 Set directory when curdir is lost in space
282 static int setdir __PARAM__((register char* home, register char* path), (home, path)) __OTORP__(register char* home; register char* path;){
286 cdrv = pathcd(path,NiL);
288 { /* Note that path and home are in the same buffer */
290 cdrv = pathcd(home,NiL);
294 (void) pathcd(home,NiL);
301 static int setpdir __PARAM__((register char* home, register char* path, register char* base), (home, path, base)) __OTORP__(register char* home; register char* path; register char* base;){
302 register int cdrv, c;
308 cdrv = setdir(home,path);
311 else cdrv = pathcd(home,NiL);
316 Pop a set of directories
318 static int popdirs __PARAM__((register int n_dir, register Ftw_t* ftw), (n_dir, ftw)) __OTORP__(register int n_dir; register Ftw_t* ftw;){
320 register char *s, *endbuf;
323 if(!ftw || ftw->level < 0)
326 endbuf = buf + (PATH_MAX-4);
329 for(s = buf; s < endbuf && n_dir > 0; --n_dir)
330 *s++ = '.', *s++ = '.', *s++ = '/';
335 if(stat(".",&sb) != 0 || !SAME(sb,ftw->statb))
341 Get top list of elt to process
343 static Ftw_t *toplist __PARAM__((register char** paths, int(*statf)(const char*, struct stat*),int(*comparf)(Ftw_t*, Ftw_t*), int metaphysical), (paths, statf, comparf, metaphysical)) __OTORP__(register char** paths; int(*statf)();int(*comparf)(); int metaphysical;){
345 register Ftw_t *f, *root;
347 register struct stat *sb;
350 top = bot = root = 0;
351 for(; *paths; ++paths)
358 if(!(f = newFtw(path,strlen(path))))
362 f->info = (*statf)(path,sb) < 0 ? FTW_NS : TYPE(sb->st_mode);
365 * don't let any standards committee member
366 * get away with calling your idea a hack
369 if (metaphysical && f->info == FTW_SL && stat(path,&st) == 0)
372 f->info = TYPE(sb->st_mode);
377 root = search(f,root,comparf,1);
379 bot->link = f, bot = f;
383 getlist(&top,&bot,root);
389 Note that free() is not used because we may need to chdir(home)
390 if there isn't enough space to continue
392 static int resize __PARAM__((register char** home, register char** endbuf, register char** path, register char** base, int n_buf, int incre), (home, endbuf, path, base, n_buf, incre)) __OTORP__(register char** home; register char** endbuf; register char** path; register char** base; int n_buf; int incre;){
393 register char *old, *newp;
396 /* add space for "/." used in testing FTW_DNX */
398 n_buf = ((n_buf+incre+4)/PATH_MAX + 1)*PATH_MAX;
399 if(!(newp = newof(0, char, n_buf, 0)))
404 memcpy(newp,old,n_old);
406 *endbuf = newp + n_buf - 4;
408 *path = newp + (*path - old);
410 *base = newp + (*base - old);
419 ftwalk __PARAM__((const char *cpath, int (*userf)(Ftw_t*), int flags, int (*comparf)(Ftw_t*, Ftw_t*)), (cpath, userf, flags, comparf)) __OTORP__(const char *cpath; int (*userf)(); int flags; int (*comparf)();){
420 char *path = (char*)cpath;
421 register int cdrv; /* chdir value */
422 int fnrv; /* return value from user function */
423 Ftw_t topf, /* the parent of top elt */
426 int (*statf) __PROTO__((const char*, struct stat*));
427 int preorder, children, postorder;
428 char *endbuf; /* space to build paths */
432 /* decode the flags */
433 children = (flags&FTW_CHILDREN) ? 1 : 0;
434 children = (children && (flags&FTW_DELAY)) ? 2 : children;
435 preorder = ((flags&FTW_POST) == 0 && !children);
436 postorder = (flags&(FTW_POST|FTW_TWICE));
437 cdrv = (flags&FTW_DOT) ? 1 : -1;
439 statf = (flags&FTW_PHYSICAL) ? lstat : pathstat;
443 /* space for home directory and paths */
448 if(!Home && !(Home = newof(0, char, N_buf, 0)))
451 if(cdrv > 0 || getcwd(Home,N_buf))
453 else if(errno == ERANGE)
454 { /* need larger buffer */
461 endbuf = Home + N_buf - 4;
465 /* make the list of top elements */
466 todo = top = bot = 0;
467 if((flags&FTW_MULTIPLE) && path)
468 todo = toplist((char**)path,statf,comparf,(flags&(FTW_META|FTW_PHYSICAL))==(FTW_META|FTW_PHYSICAL));
472 p[0] = path ? path : ".";
474 todo = toplist(p,statf,comparf,(flags&(FTW_META|FTW_PHYSICAL))==(FTW_META|FTW_PHYSICAL));
477 path = Home + strlen(Home) + 1;
482 register Ftw_t *ftw, *f;
483 register struct stat *sb;
484 register char *name, *endbase;
485 Ftw_t *link, *root, *curdir, *diroot, *dotdot;
488 register int level, n_base, nostat, cpname;
490 /* process the top object on the stack */
497 top = bot = root = 0;
499 /* initialize for level 0 */
502 /* initialize parent */
503 memcpy(&topf,ftw,sizeof(topf));
507 topf.pathlen = topf.namelen = 0;
513 (void) pathcd(Home,NiL);
516 curdir = cdrv ? 0 : ftw->parent;
521 /* chdir to parent dir if asked for */
524 cdrv = setdir(Home,path);
525 curdir = cdrv ? 0 : ftw->parent;
528 /* add object's name to the path */
529 if((n_base = ftw->namelen) >= endbuf-base &&
530 (N_buf = resize(&Home,&endbuf,&path,&base,N_buf,n_base)) < 0)
532 memcpy(base,name,n_base+1);
533 name = cdrv ? path : base;
535 /* check for cycle and open dir */
536 if(ftw->info == FTW_D)
538 if((diroot = search(ftw,diroot,statcmp,0)) != ftw || level > 0 && statcmp(ftw,ftw->parent) == 0)
544 { /* buffer is known to be large enough here! */
545 if(base[n_base-1] != '/')
546 memcpy(base+n_base,"/.",3);
547 if(!(dirfp = opendir(name)))
550 if(!dirfp && !(dirfp = opendir(name)))
555 /* call user function in preorder */
556 nd = ftw->info & ~FTW_DNX;
559 ftw->status = STATUS(cdrv);
561 ftw->path = PATH(path,level);
562 ftw->pathlen = (base - ftw->path) + n_base;
563 fnrv = (*userf)(ftw);
568 /* follow symlink if asked to */
569 if(ftw->info == FTW_SL && ftw->status == FTW_FOLLOW)
571 ftw->info = stat(name,sb) ? FTW_NS : TYPE(sb->st_mode);
572 if(ftw->info != FTW_SL)
575 /* about to prune this ftw and already at home */
576 if(cdrv == 0 && level == 0 && nd)
580 /* pruning the search tree */
581 if(!dirfp || nd || ftw->status == FTW_SKIP)
584 closedir(dirfp), dirfp = 0;
588 /* FTW_D or FTW_DNX, about to read children */
591 if((cdrv = chdir(name)) < 0)
592 (void) pathcd(Home,NiL);
593 curdir = cdrv < 0 ? 0 : ftw;
595 nostat = (children > 1 || ftw->info == FTW_DNX);
596 cpname = ((cdrv && !nostat) || (!children && !comparf));
598 endbase = base+n_base;
599 if(endbase[-1] != '/')
602 while(dir = readdir(dirfp))
610 else if(name[1] == '.' && name[2] == '\0')
613 if(!children && nd > 0)
616 /* make a new entry */
618 #if _mem_d_namlen_dirent
621 i = strlen(dir->d_name);
623 if(!(f = newFtw(name,i)))
629 /* check for space */
630 if(i >= endbuf-endbase)
632 N_buf = resize(&Home,&endbuf,&path,&base,N_buf,i);
635 endbase = base+n_base;
636 if(endbase[-1] != '/')
641 memcpy(endbase,name,i+1);
649 memcpy(sb,&(ftw->statb),sizeof(struct stat));
651 else if(nostat || (*statf)(name,sb))
654 #if _mem_d_fileno_dirent || _mem_d_ino_dirent
655 #if !_mem_d_fileno_dirent
656 #define d_fileno d_ino
658 sb->st_ino = dir->d_fileno;
663 else f->info = TYPE(sb->st_mode);
666 { /* don't recurse on . and .. */
667 f->status = FTW_SKIP;
668 if(nd == 2 && f->info != FTW_NS)
672 if(comparf) /* object ordering */
673 root = search(f,root,comparf,1);
674 else if(children || f->info == FTW_D || f->info == FTW_SL)
675 top ? (bot->link = f, bot = f) : (top = bot = f);
677 { /* terminal node */
678 f->status = STATUS(cdrv);
679 f->path = PATH(path,1);
680 f->pathlen = endbase - f->path + f->namelen;
688 /* done with directory reading */
689 closedir(dirfp), dirfp = 0;
692 getlist(&top,&bot,root);
694 /* delay preorder with the children list */
696 { /* try moving back to parent dir */
701 if(cdrv < 0 || curdir != ftw || !dotdot ||
702 !SAME(f->statb,dotdot->statb) ||
703 (cdrv = chdir("..")) < 0)
704 cdrv = setpdir(Home,path,base);
705 curdir = cdrv ? 0 : f;
709 ftw->path = PATH(path,level);
710 ftw->pathlen = (base - ftw->path) + ftw->namelen;
711 ftw->status = STATUS(cdrv);
712 fnrv = (*userf)(ftw);
717 /* chdir down again */
718 nd = (ftw->status == FTW_SKIP);
721 if((cdrv = chdir(base)) < 0)
722 (void) pathcd(Home,NiL);
723 curdir = cdrv ? 0 : ftw;
727 if(base[n_base-1] != '/')
729 for(bot = 0, f = top; f; )
731 if(nd || f->status == FTW_SKIP)
737 f = bot ? bot->link : top;
741 if(children > 1 && ftw->info != FTW_DNX)
742 { /* now read stat buffer */
744 if(f->status == FTW_STAT)
745 f->info = TYPE(sb->st_mode);
755 if((*statf)(name,sb) == 0)
756 f->info = TYPE(sb->st_mode);
760 /* normal continue */
761 bot = f, f = f->link;
767 bot->link = todo, todo = top, top = 0;
769 /* pop objects completely processed */
771 nd = 0; /* count number of ".." */
772 while(todo && ftw == todo)
775 if(ftw->info & FTW_DNX)
777 if(curdir == ftw) /* ASSERT(cdrv == 0) */
783 /* perform post-order processing */
785 ftw->status != FTW_SKIP && ftw->status != FTW_NOPOST)
786 { /* move to parent dir */
789 cdrv = popdirs(nd,curdir);
793 cdrv = setpdir(Home,path,base);
794 curdir = cdrv ? 0 : f;
797 ftw->path = PATH(path,ftw->level);
798 ftw->pathlen = (base - ftw->path) + ftw->namelen;
799 ftw->status = STATUS(cdrv);
802 fnrv = (*userf)(ftw);
806 if(ftw->status == FTW_AGAIN)
810 /* delete from dev/ino tree */
812 diroot = search(ftw,diroot,statcmp,0);
813 diroot = deleteroot(diroot);
817 if(base > path+f->namelen)
822 /* delete from top of stack */
823 if(ftw->status != FTW_AGAIN)
831 /* reset current directory */
832 if(nd > 0 && popdirs(nd,curdir) < 0)
834 (void) pathcd(Home,NiL);
842 base += ftw->namelen;
856 (void) pathcd(Home,NiL);
858 bot->link = todo, todo = top;
859 return freeAll(todo,fnrv);