4
#include "dir-iterator.h"
5
6
struct dir_iterator_level {
7
- int initialized;
8
-
7
DIR *dir;
8
9
/*
11
* (including a trailing '/'):
12
*/
13
size_t prefix_len;
16
-
17
- /*
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):
21
- */
22
- enum {
23
- DIR_STATE_ITER,
24
- DIR_STATE_RECURSE
25
- } dir_state;
14
};
15
16
/*
22
struct dir_iterator base;
23
24
/*
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.
25
+ * The number of levels currently on the stack. After the first
26
+ * call to dir_iterator_begin(), if it succeeds to open the
27
+ * first level's dir, this will always be at least 1. Then,
28
+ * when it comes to zero the iteration is ended and this
29
+ * struct is freed.
30
*/
31
size_t levels_nr;
32
40
struct dir_iterator_level *levels;
41
};
42
43
+/*
44
+ * Push a level in the iter stack and initialize it with information from
45
+ * the directory pointed by iter->base->path. It is assumed that this
46
+ * strbuf points to a valid directory path. Return 0 on success and -1
47
+ * otherwise, leaving the stack unchanged.
48
+ */
49
+static int push_level(struct dir_iterator_int *iter)
50
+{
51
+ struct dir_iterator_level *level;
52
+
53
+ ALLOC_GROW(iter->levels, iter->levels_nr + 1, iter->levels_alloc);
54
+ level = &iter->levels[iter->levels_nr++];
55
+
56
+ if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1]))
57
+ strbuf_addch(&iter->base.path, '/');
58
+ level->prefix_len = iter->base.path.len;
59
+
60
+ level->dir = opendir(iter->base.path.buf);
61
+ if (!level->dir) {
62
+ if (errno != ENOENT) {
63
+ warning_errno("error opening directory '%s'",
64
+ iter->base.path.buf);
65
+ }
66
+ iter->levels_nr--;
67
+ return -1;
68
+ }
69
+
70
+ return 0;
71
+}
72
+
73
+/*
74
+ * Pop the top level on the iter stack, releasing any resources associated
75
+ * with it. Return the new value of iter->levels_nr.
76
+ */
77
+static int pop_level(struct dir_iterator_int *iter)
78
+{
79
+ struct dir_iterator_level *level =
80
+ &iter->levels[iter->levels_nr - 1];
81
+
82
+ if (level->dir && closedir(level->dir))
83
+ warning_errno("error closing directory '%s'",
84
+ iter->base.path.buf);
85
+ level->dir = NULL;
86
+
87
+ return --iter->levels_nr;
88
+}
89
+
90
+/*
91
+ * Populate iter->base with the necessary information on the next iteration
92
+ * entry, represented by the given dirent de. Return 0 on success and -1
93
+ * otherwise.
94
+ */
95
+static int prepare_next_entry_data(struct dir_iterator_int *iter,
96
+ struct dirent *de)
97
+{
98
+ strbuf_addstr(&iter->base.path, de->d_name);
99
+ /*
100
+ * We have to reset these because the path strbuf might have
101
+ * been realloc()ed at the previous strbuf_addstr().
102
+ */
103
+ iter->base.relative_path = iter->base.path.buf +
104
+ iter->levels[0].prefix_len;
105
+ iter->base.basename = iter->base.path.buf +
106
+ iter->levels[iter->levels_nr - 1].prefix_len;
107
+
108
+ if (lstat(iter->base.path.buf, &iter->base.st)) {
109
+ if (errno != ENOENT)
110
+ warning_errno("failed to stat '%s'", iter->base.path.buf);
111
+ return -1;
112
+ }
113
+
114
+ return 0;
115
+}
116
+
117
int dir_iterator_advance(struct dir_iterator *dir_iterator)
118
{
119
struct dir_iterator_int *iter =
120
(struct dir_iterator_int *)dir_iterator;
121
122
+ if (S_ISDIR(iter->base.st.st_mode)) {
123
+ if (push_level(iter) && iter->levels_nr == 0) {
124
+ /* Pushing the first level failed */
125
+ return dir_iterator_abort(dir_iterator);
126
+ }
127
+ }
128
+
129
+ /* Loop until we find an entry that we can give back to the caller. */
130
while (1) {
131
+ struct dirent *de;
132
struct dir_iterator_level *level =
133
&iter->levels[iter->levels_nr - 1];
61
- struct dirent *de;
134
63
- if (!level->initialized) {
64
- /*
65
- * Note: dir_iterator_begin() ensures that
66
- * path is not the empty string.
67
- */
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;
71
-
72
- level->dir = opendir(iter->base.path.buf);
73
- if (!level->dir && errno != ENOENT) {
74
- warning_errno("error opening directory '%s'",
135
+ strbuf_setlen(&iter->base.path, level->prefix_len);
136
+ errno = 0;
137
+ de = readdir(level->dir);
138
+
139
+ if (!de) {
140
+ if (errno)
141
+ warning_errno("error reading directory '%s'",
142
iter->base.path.buf);
76
- /* Popping the level is handled below */
77
- }
78
-
79
- level->initialized = 1;
80
- } else if (S_ISDIR(iter->base.st.st_mode)) {
81
- if (level->dir_state == DIR_STATE_ITER) {
82
- /*
83
- * The directory was just iterated
84
- * over; now prepare to iterate into
85
- * it.
86
- */
87
- level->dir_state = DIR_STATE_RECURSE;
88
- ALLOC_GROW(iter->levels, iter->levels_nr + 1,
89
- iter->levels_alloc);
90
- level = &iter->levels[iter->levels_nr++];
91
- level->initialized = 0;
92
- continue;
93
- } else {
94
- /*
95
- * The directory has already been
96
- * iterated over and iterated into;
97
- * we're done with it.
98
- */
99
- }
143
+ else if (pop_level(iter) == 0)
144
+ return dir_iterator_abort(dir_iterator);
145
+ continue;
146
}
147
102
- if (!level->dir) {
103
- /*
104
- * This level is exhausted (or wasn't opened
105
- * successfully); pop up a level.
106
- */
107
- if (--iter->levels_nr == 0)
108
- return dir_iterator_abort(dir_iterator);
148
+ if (is_dot_or_dotdot(de->d_name))
149
+ continue;
150
151
+ if (prepare_next_entry_data(iter, de))
152
continue;
111
- }
153
113
- /*
114
- * Loop until we find an entry that we can give back
115
- * to the caller:
116
- */
117
- while (1) {
118
- strbuf_setlen(&iter->base.path, level->prefix_len);
119
- errno = 0;
120
- de = readdir(level->dir);
121
-
122
- if (!de) {
123
- /* This level is exhausted; pop up a level. */
124
- if (errno) {
125
- warning_errno("error reading directory '%s'",
126
- iter->base.path.buf);
127
- } else if (closedir(level->dir))
128
- warning_errno("error closing directory '%s'",
129
- iter->base.path.buf);
130
-
131
- level->dir = NULL;
132
- if (--iter->levels_nr == 0)
133
- return dir_iterator_abort(dir_iterator);
134
- break;
135
- }
136
-
137
- if (is_dot_or_dotdot(de->d_name))
138
- continue;
139
-
140
- strbuf_addstr(&iter->base.path, de->d_name);
141
- if (lstat(iter->base.path.buf, &iter->base.st) < 0) {
142
- if (errno != ENOENT)
143
- warning_errno("failed to stat '%s'",
144
- iter->base.path.buf);
145
- continue;
146
- }
147
-
148
- /*
149
- * We have to set these each time because
150
- * the path strbuf might have been realloc()ed.
151
- */
152
- iter->base.relative_path =
153
- iter->base.path.buf + iter->levels[0].prefix_len;
154
- iter->base.basename =
155
- iter->base.path.buf + level->prefix_len;
156
- level->dir_state = DIR_STATE_ITER;
157
-
158
- return ITER_OK;
159
- }
154
+ return ITER_OK;
155
}
156
}
157
182
{
183
struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
184
struct dir_iterator *dir_iterator = &iter->base;
190
-
191
- if (!path || !*path)
192
- BUG("empty path passed to dir_iterator_begin()");
185
+ int saved_errno;
186
187
strbuf_init(&iter->base.path, PATH_MAX);
188
strbuf_addstr(&iter->base.path, path);
189
190
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
191
+ iter->levels_nr = 0;
192
199
- iter->levels_nr = 1;
200
- iter->levels[0].initialized = 0;
193
+ /*
194
+ * Note: stat already checks for NULL or empty strings and
195
+ * inexistent paths.
196
+ */
197
+ if (stat(iter->base.path.buf, &iter->base.st) < 0) {
198
+ saved_errno = errno;
199
+ goto error_out;
200
+ }
201
+
202
+ if (!S_ISDIR(iter->base.st.st_mode)) {
203
+ saved_errno = ENOTDIR;
204
+ goto error_out;
205
+ }
206
207
return dir_iterator;
208
+
209
+error_out:
210
+ dir_iterator_abort(dir_iterator);
211
+ errno = saved_errno;
212
+ return NULL;
213
}