1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
28 * fts implementation unwound from the kpv ftwalk() of 1988-10-30
39 typedef int (*Compar_f
)(struct Ftsent
* const*, struct Ftsent
* const*);
40 typedef int (*Stat_f
)(const char*, struct stat
*);
42 #define _fts_status status
43 #define _fts_statb statb
45 #define _FTS_PRIVATE_ \
46 FTSENT* parent; /* top parent */ \
47 FTSENT* todo; /* todo list */ \
48 FTSENT* top; /* top element */ \
50 FTSENT* bot; /* bottom element */ \
51 FTSENT* free; /* free element */ \
54 FTSENT* current; /* current element */ \
55 FTSENT* previous; /* previous current */ \
57 FTSENT* link; /* real current fts_link*/ \
58 FTSENT* pwd; /* pwd parent */ \
59 DIR* dir; /* current dir stream */ \
60 Compar_f comparf; /* node comparison func */ \
61 size_t baselen; /* current strlen(base) */ \
62 size_t homesize; /* sizeof(home) */ \
63 int cd; /* chdir status */ \
65 int flags; /* fts_open() flags */ \
67 unsigned char children; \
69 unsigned char nostat; \
70 unsigned char state; /* fts_read() state */ \
71 char* base; /* basename in path */ \
73 char* path; /* path workspace */ \
74 char* home; /* home/path buffer */ \
75 char* endbase; /* space to build paths */ \
76 char* endbuf; /* space to build paths */ \
77 char* pad[2]; /* $0.02 to splain this */
80 * NOTE: <ftwalk.h> relies on status and statb being the first two elements
83 #define _FTSENT_PRIVATE_ \
84 int nd; /* popdir() count */ \
85 FTSENT* left; /* left child */ \
86 FTSENT* right; /* right child */ \
87 FTSENT* pwd; /* pwd parent */ \
88 FTSENT* stack; /* getlist() stack */ \
89 long nlink; /* FTS_D link count */ \
90 unsigned char must; /* must stat */ \
91 unsigned char type; /* DT_* type */ \
92 unsigned char symlink; /* originally a symlink */ \
93 char name[sizeof(int)]; /* fts_name data */
108 #define drop(p,f) (((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free))
110 #define ACCESS(p,f) ((p)->cd==0?(f)->fts_name:(f)->fts_path)
111 #define PATH(f,p,l) ((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p))
112 #define SAME(one,two) ((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev)
113 #define SKIPLINK(p,f) ((f)->fts_parent->nlink == 0)
116 #define ISTYPE(f,t) ((f)->type == (t))
117 #define TYPE(f,t) ((f)->type = (t))
118 #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)))))
124 #define ISTYPE(f,t) ((t)==DT_UNKNOWN)
126 #define SKIP(p,f) ((f)->fts_parent->must == 0 && SKIPLINK(p,f))
130 #define D_FILENO(d) (1)
134 * NOTE: a malicious dir rename() could change .. underfoot so we
135 * must always verify; undef verify to enable the unsafe code
141 * FTS_NOSTAT requires a dir with
142 * D_TYPE(&dirent_t)!=DT_UNKNOWN
147 #define FTS_children_resume 1
148 #define FTS_children_return 2
150 #define FTS_popstack 4
151 #define FTS_popstack_resume 5
152 #define FTS_popstack_return 6
153 #define FTS_preorder 7
154 #define FTS_preorder_resume 8
155 #define FTS_preorder_return 9
156 #define FTS_readdir 10
157 #define FTS_terminal 11
159 #define FTS_top_return 13
161 typedef int (*Notify_f
)(FTS
*, FTSENT
*, void*);
163 typedef struct Notify_s
165 struct Notify_s
* next
;
170 static Notify_t
* notify
;
173 * allocate an FTSENT node
177 node(FTS
* fts
, FTSENT
* parent
, register char* name
, register size_t namelen
)
182 if (fts
->free
&& namelen
< MINNAME
)
185 fts
->free
= f
->fts_link
;
189 n
= (namelen
< MINNAME
? MINNAME
: namelen
+ 1) - sizeof(int);
190 if (!(f
= newof(0, FTSENT
, 1, n
)))
192 fts
->fts_errno
= errno
;
193 fts
->state
= FTS_error
;
201 f
->fts_level
= (f
->fts_parent
= parent
)->fts_level
+ 1;
202 #if __OBSOLETE__ < 20140101
203 f
->_fts_level
= (short)f
->fts_level
;
209 f
->fts_namelen
= namelen
;
210 #if __OBSOLETE__ < 20140101
211 f
->_fts_namelen
= (unsigned short)f
->fts_namelen
;
213 f
->fts_name
= f
->name
;
214 f
->fts_statp
= &f
->statb
;
215 memcpy(f
->fts_name
, name
, namelen
+ 1);
220 * compare directories by device/inode
224 statcmp(FTSENT
* const* pf1
, FTSENT
* const* pf2
)
226 register const FTSENT
* f1
= *pf1
;
227 register const FTSENT
* f2
= *pf2
;
229 if (f1
->statb
.st_ino
< f2
->statb
.st_ino
)
231 if (f1
->statb
.st_ino
> f2
->statb
.st_ino
)
233 if (f1
->statb
.st_dev
< f2
->statb
.st_dev
)
235 if (f1
->statb
.st_dev
> f2
->statb
.st_dev
)
239 * hack for NFS where <dev,ino> may not uniquely identify objects
242 if (f1
->statb
.st_mtime
< f2
->statb
.st_mtime
)
244 if (f1
->statb
.st_mtime
> f2
->statb
.st_mtime
)
250 * search trees with top-down splaying (a la Tarjan and Sleator)
251 * when used for insertion sort, this implements a stable sort
254 #define RROTATE(r) (t = r->left, r->left = t->right, t->right = r, r = t)
255 #define LROTATE(r) (t = r->right, r->right = t->left, t->left = r, r = t)
258 search(FTSENT
* e
, FTSENT
* root
, int(*comparf
)(FTSENT
* const*, FTSENT
* const*), int insert
)
262 register FTSENT
* left
;
263 register FTSENT
* right
;
264 register FTSENT
* lroot
;
265 register FTSENT
* rroot
;
267 left
= right
= lroot
= rroot
= 0;
270 if (!(cmp
= (*comparf
)(&e
, &root
)) && !insert
)
275 * this is the left zig-zig case
278 if (root
->left
&& (cmp
= (*comparf
)(&e
, &root
->left
)) <= 0)
286 * stick all things > e to the right tree
300 * this is the right zig-zig case
303 if (root
->right
&& (cmp
= (*comparf
)(&e
, &root
->right
)) >= 0)
311 * stick all things <= e to the left tree
328 right
->left
= root
->right
;
332 left
->right
= root
->left
;
342 * delete the root element from the tree
346 deleteroot(register FTSENT
* root
)
349 register FTSENT
* left
;
350 register FTSENT
* right
;
353 if (!(left
= root
->left
))
366 * generate ordered fts_link list from binary tree at root
367 * FTSENT.stack instead of recursion to avoid blowing the real
368 * stack on big directories
372 getlist(register FTSENT
** top
, register FTSENT
** bot
, register FTSENT
* root
)
374 register FTSENT
* stack
= 0;
389 *bot
= (*bot
)->fts_link
= root
;
399 (*bot
)->fts_link
= 0;
402 stack
= stack
->stack
;
409 * set directory when curdir is lost in space
413 setdir(register char* home
, register char* path
)
418 cdrv
= pathcd(path
, NiL
);
422 * note that path and home are in the same buffer
426 cdrv
= pathcd(home
, NiL
);
439 setpdir(register char* home
, register char* path
, register char* base
)
448 cdrv
= setdir(home
, path
);
452 cdrv
= pathcd(home
, NiL
);
457 * pop a set of directories
471 if (!(f
= fts
->curdir
) || f
->fts_level
< 0)
473 e
= buf
+ sizeof(buf
) - 4;
479 for (s
= buf
; s
< e
&& fts
->nd
> 0; fts
->nd
--)
484 verify
|= fts
->pwd
->symlink
;
486 fts
->pwd
= fts
->pwd
->pwd
;
496 return (verify
&& (stat(".", &sb
) < 0 || !SAME(&sb
, f
->fts_statp
))) ? -1 : 0;
500 * initialize st from path and fts_info from st
504 info(FTS
* fts
, register FTSENT
* f
, const char* path
, struct stat
* sp
, int flags
)
509 if (!f
->symlink
&& (ISTYPE(f
, DT_UNKNOWN
) || ISTYPE(f
, DT_LNK
)))
511 if (lstat(path
, sp
) < 0)
516 if (stat(path
, sp
) < 0)
522 if (S_ISDIR(sp
->st_mode
))
524 if ((flags
& FTS_NOSTAT
) && !fts
->fs3d
)
526 f
->fts_parent
->nlink
--;
528 if ((f
->nlink
= sp
->st_nlink
) < 2)
536 if ((f
->nlink
= sp
->st_nlink
) >= 2)
548 else if (S_ISLNK((sp
)->st_mode
))
553 if (!(flags
& FTS_PHYSICAL
) && stat(path
, &sb
) >= 0)
556 flags
= FTS_PHYSICAL
;
560 f
->fts_info
= FTS_SL
;
571 f
->fts_info
= FTS_NS
;
576 * get top list of elements to process
577 * ordering delayed until first fts_read()
578 * to give caller a chance to set fts->handle
582 toplist(FTS
* fts
, register char* const* pathnames
)
586 register FTSENT
* top
;
587 register FTSENT
* bot
;
593 if (fts
->flags
& FTS_NOSEEDOTDIR
)
594 fts
->flags
&= ~FTS_SEEDOTDIR
;
595 physical
= (fts
->flags
& FTS_PHYSICAL
);
596 metaphysical
= (fts
->flags
& (FTS_META
|FTS_PHYSICAL
)) == (FTS_META
|FTS_PHYSICAL
);
598 while (path
= *pathnames
++)
604 if (!(f
= node(fts
, fts
->parent
, path
, strlen(path
))))
608 f
->fts_namelen
= (fts
->flags
& FTS_SEEDOTDIR
) ? strlen(path
) : (pathcanon(path
, 0) - path
);
609 else if (*path
!= '.')
611 f
->fts_namelen
= strlen(path
);
612 fts
->flags
|= FTS_SEEDOTDIR
;
616 if (fts
->flags
& FTS_NOSEEDOTDIR
)
618 fts
->flags
&= ~FTS_SEEDOTDIR
;
620 while (*s
++ == '.' && *s
++ == '/')
627 while (*path
++ = *s
++);
632 fts
->flags
|= FTS_SEEDOTDIR
;
633 for (s
= path
+ strlen(path
); s
> path
&& *(s
- 1) == '/'; s
--);
635 f
->fts_namelen
= s
- path
;
637 #if __OBSOLETE__ < 20140101
638 f
->_fts_namelen
= (unsigned short)f
->fts_namelen
;
643 f
->fts_info
= FTS_NS
;
646 info(fts
, f
, path
, f
->fts_statp
, fts
->flags
);
650 * don't let any standards committee get
651 * away with calling your idea a hack
654 if (metaphysical
&& f
->fts_info
== FTS_SL
&& stat(path
, &st
) >= 0)
657 info(fts
, f
, NiL
, f
->fts_statp
, 0);
672 * order fts->todo if fts->comparf != 0
679 register FTSENT
* root
;
683 top
= bot
= root
= 0;
684 for (f
= fts
->todo
; f
; f
= f
->fts_link
)
685 root
= search(f
, root
, fts
->comparf
, 1);
686 getlist(&top
, &bot
, root
);
691 * resize the path buffer
692 * note that free() is not used because we may need to chdir(fts->home)
693 * if there isn't enough space to continue
697 resize(register FTS
* fts
, size_t inc
)
701 register size_t n_old
;
704 * add space for "/." used in testing FTS_DNX
707 n_old
= fts
->homesize
;
708 fts
->homesize
= ((fts
->homesize
+ inc
+ 4) / PATH_MAX
+ 1) * PATH_MAX
;
709 if (!(newp
= newof(0, char, fts
->homesize
, 0)))
711 fts
->fts_errno
= errno
;
712 fts
->state
= FTS_error
;
717 memcpy(newp
, old
, n_old
);
719 fts
->endbuf
= newp
+ fts
->homesize
- 4;
721 fts
->path
= newp
+ (fts
->path
- old
);
723 fts
->base
= newp
+ (fts
->base
- old
);
729 * open a new fts stream on pathnames
733 fts_open(char* const* pathnames
, int flags
, int (*comparf
)(FTSENT
* const*, FTSENT
* const*))
737 if (!(fts
= newof(0, FTS
, 1, sizeof(FTSENT
))))
740 fts
->cd
= (flags
& FTS_NOCHDIR
) ? 1 : -1;
741 fts
->comparf
= comparf
;
742 fts
->fs3d
= fs3d(FS3D_TEST
);
745 * set up the path work buffer
748 fts
->homesize
= 2 * PATH_MAX
;
751 if (!(fts
->home
= newof(fts
->home
, char, fts
->homesize
, 0)))
756 if (fts
->cd
> 0 || getcwd(fts
->home
, fts
->homesize
))
759 fts
->homesize
+= PATH_MAX
;
763 fts
->endbuf
= fts
->home
+ fts
->homesize
- 4;
766 * initialize the tippity-top
769 fts
->parent
= (FTSENT
*)(fts
+ 1);
770 fts
->parent
->fts_info
= FTS_D
;
771 memcpy(fts
->parent
->fts_accpath
= fts
->parent
->fts_path
= fts
->parent
->fts_name
= fts
->parent
->name
, ".", 2);
772 fts
->parent
->fts_level
= -1;
773 #if __OBSOLETE__ < 20140101
774 fts
->parent
->_fts_level
= (short)fts
->parent
->fts_level
;
776 fts
->parent
->fts_statp
= &fts
->parent
->statb
;
777 fts
->parent
->must
= 2;
778 fts
->parent
->type
= DT_UNKNOWN
;
779 fts
->path
= fts
->home
+ strlen(fts
->home
) + 1;
782 * make the list of top elements
785 if (!pathnames
|| (flags
& FTS_ONEPATH
) || !*pathnames
)
789 v
[0] = pathnames
&& (flags
& FTS_ONEPATH
) ? (char*)pathnames
: ".";
791 fts
->todo
= toplist(fts
, v
);
794 fts
->todo
= toplist(fts
, pathnames
);
795 if (!fts
->todo
|| fts
->todo
->fts_info
== FTS_NS
&& !fts
->todo
->fts_link
)
804 * return the next FTS entry
808 fts_read(register FTS
* fts
)
830 if (f
->status
== FTS_SKIP
)
834 t
->fts_link
= f
->fts_link
;
840 fts
->todo
= f
->fts_link
;
854 if (!fts
->state
&& fts
->comparf
)
856 if (!(f
= fts
->todo
))
863 * process the top object on the stack
866 fts
->root
= fts
->top
= fts
->bot
= 0;
869 * initialize the top level
872 if (f
->fts_level
== 0)
874 fts
->parent
->fts_number
= f
->fts_number
;
875 fts
->parent
->fts_pointer
= f
->fts_pointer
;
876 fts
->parent
->fts_statp
= f
->fts_statp
;
877 fts
->parent
->statb
= *f
->fts_statp
;
878 f
->fts_parent
= fts
->parent
;
881 pathcd(fts
->home
, NiL
);
882 else if (fts
->cd
< 0)
884 fts
->pwd
= f
->fts_parent
;
885 fts
->curdir
= fts
->cd
? 0 : f
->fts_parent
;
886 *(fts
->base
= fts
->path
) = 0;
890 * chdir to parent if asked for
895 fts
->cd
= setdir(fts
->home
, fts
->path
);
896 fts
->pwd
= f
->fts_parent
;
897 fts
->curdir
= fts
->cd
? 0 : f
->fts_parent
;
901 * add object's name to the path
904 if ((fts
->baselen
= f
->fts_namelen
) >= (fts
->endbuf
- fts
->base
) && resize(fts
, fts
->baselen
))
906 memcpy(fts
->base
, f
->name
, fts
->baselen
+ 1);
907 fts
->name
= fts
->cd
? fts
->path
: fts
->base
;
913 * check for cycle and open dir
916 if (f
->fts_info
== FTS_D
)
918 if ((fts
->diroot
= search(f
, fts
->diroot
, statcmp
, 0)) != f
|| f
->fts_level
> 0 && (t
= f
) && statcmp(&t
, &f
->fts_parent
) == 0)
920 f
->fts_info
= FTS_DC
;
921 f
->fts_cycle
= fts
->diroot
;
923 else if (!(fts
->flags
& FTS_TOP
) && (!(fts
->flags
& FTS_XDEV
) || f
->statb
.st_dev
== f
->fts_parent
->statb
.st_dev
))
926 * buffer is known to be large enough here!
929 if (fts
->base
[fts
->baselen
- 1] != '/')
930 memcpy(fts
->base
+ fts
->baselen
, "/.", 3);
931 if (!(fts
->dir
= opendir(fts
->name
)))
932 f
->fts_info
= FTS_DNX
;
933 fts
->base
[fts
->baselen
] = 0;
934 if (!fts
->dir
&& !(fts
->dir
= opendir(fts
->name
)))
935 f
->fts_info
= FTS_DNR
;
938 f
->nd
= f
->fts_info
& ~FTS_DNX
;
939 if (f
->nd
|| !(fts
->flags
& FTS_NOPREORDER
))
942 fts
->link
= f
->fts_link
;
944 f
->fts_path
= PATH(fts
, fts
->path
, f
->fts_level
);
945 f
->fts_pathlen
= (fts
->base
- f
->fts_path
) + fts
->baselen
;
946 f
->fts_accpath
= ACCESS(fts
, f
);
947 fts
->state
= FTS_preorder_return
;
952 case FTS_preorder_resume
:
958 if (!fts
->dir
|| f
->nd
|| f
->status
== FTS_SKIP
)
965 fts
->state
= FTS_popstack
;
970 * FTS_D or FTS_DNX, about to read children
975 if ((fts
->cd
= chdir(fts
->name
)) < 0)
976 pathcd(fts
->home
, NiL
);
977 else if (fts
->pwd
!= f
)
982 fts
->curdir
= fts
->cd
< 0 ? 0 : f
;
984 fts
->nostat
= fts
->children
> 1 || f
->fts_info
== FTS_DNX
;
985 fts
->cpname
= fts
->cd
&& !fts
->nostat
|| !fts
->children
&& !fts
->comparf
;
987 fts
->endbase
= fts
->base
+ fts
->baselen
;
988 if (fts
->endbase
[-1] != '/')
989 *fts
->endbase
++ = '/';
995 while (d
= readdir(fts
->dir
))
1002 fts
->current
->nlink
--;
1003 if (!(fts
->flags
& FTS_SEEDOT
))
1007 else if (s
[1] == '.' && s
[2] == 0)
1009 fts
->current
->nlink
--;
1010 if (fts
->current
->must
== 1)
1011 fts
->current
->must
= 0;
1012 if (!(fts
->flags
& FTS_SEEDOT
))
1027 if (!(f
= node(fts
, fts
->current
, s
, i
)))
1035 if (i
>= fts
->endbuf
- fts
->endbase
)
1039 fts
->endbase
= fts
->base
+ fts
->baselen
;
1040 if (fts
->endbase
[-1] != '/')
1045 memcpy(fts
->endbase
, s
, i
+ 1);
1052 * don't recurse on . and ..
1056 f
->fts_statp
= fts
->current
->fts_statp
;
1059 if (f
->fts_info
!= FTS_NS
)
1061 if (fts
->current
->fts_parent
->fts_level
< 0)
1063 f
->fts_statp
= &fts
->current
->fts_parent
->statb
;
1064 info(fts
, f
, s
, f
->fts_statp
, 0);
1067 f
->fts_statp
= fts
->current
->fts_parent
->fts_statp
;
1069 f
->fts_info
= FTS_DOT
;
1071 else if ((fts
->nostat
|| SKIP(fts
, f
)) && (f
->fts_info
= FTS_NSOK
) || info(fts
, f
, s
, &f
->statb
, fts
->flags
))
1072 f
->statb
.st_ino
= D_FILENO(d
);
1074 fts
->root
= search(f
, fts
->root
, fts
->comparf
, 1);
1075 else if (fts
->children
|| f
->fts_info
== FTS_D
|| f
->fts_info
== FTS_SL
)
1078 fts
->bot
= fts
->bot
->fts_link
= f
;
1080 fts
->top
= fts
->bot
= f
;
1088 f
->fts_path
= PATH(fts
, fts
->path
, 1);
1089 f
->fts_pathlen
= fts
->endbase
- f
->fts_path
+ f
->fts_namelen
;
1090 f
->fts_accpath
= ACCESS(fts
, f
);
1091 fts
->previous
= fts
->current
;
1093 fts
->state
= FTS_terminal
;
1099 * done with the directory
1105 getlist(&fts
->top
, &fts
->bot
, fts
->root
);
1109 * try moving back to parent dir
1112 fts
->base
[fts
->baselen
] = 0;
1115 f
= fts
->current
->fts_parent
;
1119 || !SAME(f
->fts_statp
, fts
->dotdot
->fts_statp
)
1120 || fts
->pwd
&& fts
->pwd
->symlink
1121 || (fts
->cd
= chdir("..")) < 0
1123 || stat(".", &sb
) < 0
1124 || !SAME(&sb
, fts
->dotdot
->fts_statp
)
1127 fts
->cd
= setpdir(fts
->home
, fts
->path
, fts
->base
);
1129 fts
->pwd
= fts
->pwd
->pwd
;
1130 fts
->curdir
= fts
->cd
? 0 : f
;
1133 fts
->link
= f
->fts_link
;
1134 f
->fts_link
= fts
->top
;
1135 f
->fts_path
= PATH(fts
, fts
->path
, f
->fts_level
);
1136 f
->fts_pathlen
= (fts
->base
- f
->fts_path
) + f
->fts_namelen
;
1137 f
->fts_accpath
= ACCESS(fts
, f
);
1138 fts
->state
= FTS_children_return
;
1143 case FTS_children_resume
:
1145 fts
->base
[fts
->baselen
] = 0;
1148 fts
->bot
->fts_link
= fts
->todo
;
1149 fts
->todo
= fts
->top
;
1157 * pop objects completely processed
1164 case FTS_popstack_resume
:
1166 while (fts
->todo
&& f
== fts
->todo
)
1169 if ((f
->fts_info
& FTS_DP
) == FTS_D
)
1172 * delete from <dev,ino> tree
1175 if (f
!= fts
->diroot
)
1176 fts
->diroot
= search(f
, fts
->diroot
, statcmp
, 0);
1177 fts
->diroot
= deleteroot(fts
->diroot
);
1178 if (f
== fts
->curdir
)
1185 * perform post-order processing
1188 if (!(fts
->flags
& FTS_NOPOSTORDER
) &&
1189 f
->status
!= FTS_SKIP
&&
1190 f
->status
!= FTS_NOPOSTORDER
)
1193 * move to parent dir
1197 fts
->cd
= popdirs(fts
);
1199 fts
->cd
= setpdir(fts
->home
, fts
->path
, fts
->base
);
1200 fts
->curdir
= fts
->cd
? 0 : t
;
1201 f
->fts_info
= FTS_DP
;
1202 f
->fts_path
= PATH(fts
, fts
->path
, f
->fts_level
);
1203 f
->fts_pathlen
= (fts
->base
- f
->fts_path
) + f
->fts_namelen
;
1204 f
->fts_accpath
= ACCESS(fts
, f
);
1207 * re-stat to update nlink/times
1210 stat(f
->fts_accpath
, f
->fts_statp
);
1211 fts
->link
= f
->fts_link
;
1213 fts
->state
= FTS_popstack_return
;
1222 if (fts
->base
> fts
->path
+ t
->fts_namelen
)
1225 fts
->base
-= t
->fts_namelen
;
1228 * try again or delete from top of stack
1231 if (f
->status
== FTS_AGAIN
)
1233 f
->fts_info
= FTS_D
;
1238 fts
->todo
= fts
->todo
->fts_link
;
1245 * reset current directory
1248 if (fts
->nd
> 0 && popdirs(fts
) < 0)
1250 pathcd(fts
->home
, NiL
);
1257 fts
->base
+= f
->fts_namelen
;
1258 if (*(fts
->base
- 1) != '/')
1262 fts
->state
= FTS_todo
;
1267 case FTS_children_return
:
1270 f
->fts_link
= fts
->link
;
1276 i
= f
->fts_info
!= FTS_DNX
;
1277 n
= f
->status
== FTS_SKIP
;
1278 if (!n
&& fts
->cd
== 0)
1280 if ((fts
->cd
= chdir(fts
->base
)) < 0)
1281 pathcd(fts
->home
, NiL
);
1282 else if (fts
->pwd
!= f
)
1287 fts
->curdir
= fts
->cd
? 0 : f
;
1294 if (fts
->base
[fts
->baselen
- 1] != '/')
1295 fts
->base
[fts
->baselen
] = '/';
1296 for (fts
->bot
= 0, f
= fts
->top
; f
; )
1297 if (n
|| f
->status
== FTS_SKIP
)
1300 fts
->bot
->fts_link
= f
->fts_link
;
1302 fts
->top
= f
->fts_link
;
1304 f
= fts
->bot
? fts
->bot
->fts_link
: fts
->top
;
1308 if (fts
->children
> 1 && i
)
1310 if (f
->status
== FTS_STAT
)
1311 info(fts
, f
, NiL
, f
->fts_statp
, 0);
1312 else if (f
->fts_info
== FTS_NSOK
&& !SKIP(fts
, f
))
1317 memcpy(fts
->endbase
, s
, f
->fts_namelen
+ 1);
1320 info(fts
, f
, s
, f
->fts_statp
, fts
->flags
);
1327 fts
->state
= FTS_children_resume
;
1330 case FTS_popstack_return
:
1333 f
->fts_link
= fts
->link
;
1334 f
->fts_info
= f
->status
== FTS_AGAIN
? FTS_DP
: 0;
1335 fts
->state
= FTS_popstack_resume
;
1338 case FTS_preorder_return
:
1341 f
->fts_link
= fts
->link
;
1344 * follow symlink if asked to
1347 if (f
->status
== FTS_FOLLOW
)
1350 if (f
->fts_info
== FTS_SL
|| ISTYPE(f
, DT_LNK
) || f
->fts_info
== FTS_NSOK
)
1352 info(fts
, f
, f
->fts_accpath
, f
->fts_statp
, 0);
1353 if (f
->fts_info
!= FTS_SL
)
1355 fts
->state
= FTS_preorder
;
1362 * about to prune this f and already at home
1365 if (fts
->cd
== 0 && f
->fts_level
== 0 && f
->nd
)
1367 fts
->state
= FTS_preorder_resume
;
1373 if (f
->status
== FTS_FOLLOW
)
1376 if (f
->fts_info
== FTS_SL
|| ISTYPE(f
, DT_LNK
) || f
->fts_info
== FTS_NSOK
)
1378 info(fts
, f
, f
->fts_accpath
, f
->fts_statp
, 0);
1379 if (f
->symlink
&& f
->fts_info
!= FTS_SL
)
1381 if (!(f
->fts_link
= fts
->top
))
1384 fts
->current
= fts
->previous
;
1385 fts
->state
= FTS_readdir
;
1391 drop(fts
, fts
->current
);
1393 fts
->state
= FTS_readdir
;
1402 fts
->fts_errno
= EINVAL
;
1403 fts
->state
= FTS_error
;
1408 #if __OBSOLETE__ < 20140101
1409 f
->_fts_pathlen
= (unsigned short)f
->fts_pathlen
;
1411 for (p
= notify
; p
; p
= p
->next
)
1412 if ((n
= (*p
->notifyf
)(fts
, f
, p
->context
)) > 0)
1416 fts
->fts_errno
= EINVAL
;
1417 fts
->state
= FTS_error
;
1424 * set stream or entry flags
1428 fts_set(register FTS
* fts
, register FTSENT
* f
, int status
)
1430 if (fts
|| !f
|| f
->fts
->current
!= f
)
1437 if (!(f
->fts_info
& FTS_SL
))
1440 case FTS_NOPOSTORDER
:
1443 if ((f
->fts_info
& (FTS_D
|FTS_P
)) != FTS_D
)
1454 * return the list of child entries
1458 fts_children(register FTS
* fts
, int flags
)
1469 fts
->state
= FTS_top_return
;
1472 case FTS_preorder_return
:
1474 fts
->children
= ((flags
| fts
->flags
) & FTS_NOSTAT
) ? 2 : 1;
1475 if (f
= fts_read(fts
))
1484 * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags
1485 * conditioned by astconf()
1493 s
= astconf("PATH_RESOLVE", NiL
, NiL
);
1494 if (streq(s
, "logical"))
1496 if (streq(s
, "physical"))
1497 return FTS_PHYSICAL
|FTS_SEEDOTDIR
;
1498 return FTS_META
|FTS_PHYSICAL
|FTS_SEEDOTDIR
;
1502 * return 1 if ent is mounted on a local filesystem
1506 fts_local(FTSENT
* ent
)
1511 return statvfs(ent
->fts_path
, &fs
) || (fs
.f_flag
& ST_LOCAL
);
1513 return !strgrpmatch(fmtfs(ent
->fts_statp
), "([an]fs|samb)", NiL
, 0, STR_LEFT
|STR_ICASE
);
1518 * close an open fts stream
1522 fts_close(register FTS
* fts
)
1530 pathcd(fts
->home
, NiL
);
1532 if (fts
->state
== FTS_children_return
)
1533 fts
->current
->fts_link
= fts
->link
;
1536 fts
->bot
->fts_link
= fts
->todo
;
1537 fts
->todo
= fts
->top
;
1539 for (f
= fts
->todo
; f
; f
= x
)
1544 for (f
= fts
->free
; f
; f
= x
)
1554 * register function to be called for each fts_read() entry
1555 * context==0 => unregister notifyf
1559 fts_notify(Notify_f notifyf
, void* context
)
1561 register Notify_t
* np
;
1562 register Notify_t
* pp
;
1566 if (!(np
= newof(0, Notify_t
, 1, 0)))
1568 np
->notifyf
= notifyf
;
1569 np
->context
= context
;
1575 for (np
= notify
, pp
= 0; np
; pp
= np
, np
= np
->next
)
1576 if (np
->notifyf
== notifyf
)
1579 pp
->next
= np
->next
;