4 #include "dir-iterator.h"
6 struct dir_iterator_level
{
12 * The length of the directory part of path at this level
13 * (including a trailing '/'):
18 * The last action that has been taken with the current entry
19 * (needed for directories, which have to be included in the
20 * iteration and also iterated into):
29 * The full data structure used to manage the internal directory
30 * iteration state. It includes members that are not part of the
33 struct dir_iterator_int
{
34 struct dir_iterator base
;
37 * The number of levels currently on the stack. This is always
38 * at least 1, because when it becomes zero the iteration is
39 * ended and this struct is freed.
43 /* The number of levels that have been allocated on the stack */
47 * A stack of levels. levels[0] is the uppermost directory
48 * that will be included in this iteration.
50 struct dir_iterator_level
*levels
;
53 int dir_iterator_advance(struct dir_iterator
*dir_iterator
)
55 struct dir_iterator_int
*iter
=
56 (struct dir_iterator_int
*)dir_iterator
;
59 struct dir_iterator_level
*level
=
60 &iter
->levels
[iter
->levels_nr
- 1];
63 if (!level
->initialized
) {
65 * Note: dir_iterator_begin() ensures that
66 * path is not the empty string.
68 if (!is_dir_sep(iter
->base
.path
.buf
[iter
->base
.path
.len
- 1]))
69 strbuf_addch(&iter
->base
.path
, '/');
70 level
->prefix_len
= iter
->base
.path
.len
;
72 level
->dir
= opendir(iter
->base
.path
.buf
);
73 if (!level
->dir
&& errno
!= ENOENT
) {
74 warning("error opening directory %s: %s",
75 iter
->base
.path
.buf
, strerror(errno
));
76 /* Popping the level is handled below */
79 level
->initialized
= 1;
80 } else if (S_ISDIR(iter
->base
.st
.st_mode
)) {
81 if (level
->dir_state
== DIR_STATE_ITER
) {
83 * The directory was just iterated
84 * over; now prepare to iterate into
87 level
->dir_state
= DIR_STATE_RECURSE
;
88 ALLOC_GROW(iter
->levels
, iter
->levels_nr
+ 1,
90 level
= &iter
->levels
[iter
->levels_nr
++];
91 level
->initialized
= 0;
95 * The directory has already been
96 * iterated over and iterated into;
104 * This level is exhausted (or wasn't opened
105 * successfully); pop up a level.
107 if (--iter
->levels_nr
== 0)
108 return dir_iterator_abort(dir_iterator
);
114 * Loop until we find an entry that we can give back
118 strbuf_setlen(&iter
->base
.path
, level
->prefix_len
);
120 de
= readdir(level
->dir
);
123 /* This level is exhausted; pop up a level. */
125 warning("error reading directory %s: %s",
126 iter
->base
.path
.buf
, strerror(errno
));
127 } else if (closedir(level
->dir
))
128 warning("error closing directory %s: %s",
129 iter
->base
.path
.buf
, strerror(errno
));
132 if (--iter
->levels_nr
== 0)
133 return dir_iterator_abort(dir_iterator
);
137 if (is_dot_or_dotdot(de
->d_name
))
140 strbuf_addstr(&iter
->base
.path
, de
->d_name
);
141 if (lstat(iter
->base
.path
.buf
, &iter
->base
.st
) < 0) {
143 warning("error reading path '%s': %s",
150 * We have to set these each time because
151 * the path strbuf might have been realloc()ed.
153 iter
->base
.relative_path
=
154 iter
->base
.path
.buf
+ iter
->levels
[0].prefix_len
;
155 iter
->base
.basename
=
156 iter
->base
.path
.buf
+ level
->prefix_len
;
157 level
->dir_state
= DIR_STATE_ITER
;
164 int dir_iterator_abort(struct dir_iterator
*dir_iterator
)
166 struct dir_iterator_int
*iter
= (struct dir_iterator_int
*)dir_iterator
;
168 for (; iter
->levels_nr
; iter
->levels_nr
--) {
169 struct dir_iterator_level
*level
=
170 &iter
->levels
[iter
->levels_nr
- 1];
172 if (level
->dir
&& closedir(level
->dir
)) {
173 strbuf_setlen(&iter
->base
.path
, level
->prefix_len
);
174 warning("error closing directory %s: %s",
175 iter
->base
.path
.buf
, strerror(errno
));
180 strbuf_release(&iter
->base
.path
);
185 struct dir_iterator
*dir_iterator_begin(const char *path
)
187 struct dir_iterator_int
*iter
= xcalloc(1, sizeof(*iter
));
188 struct dir_iterator
*dir_iterator
= &iter
->base
;
191 die("BUG: empty path passed to dir_iterator_begin()");
193 strbuf_init(&iter
->base
.path
, PATH_MAX
);
194 strbuf_addstr(&iter
->base
.path
, path
);
196 ALLOC_GROW(iter
->levels
, 10, iter
->levels_alloc
);
199 iter
->levels
[0].initialized
= 0;