4
4
#include "dir-iterator.h"
5
5
6
6
struct dir_iterator_level {
7
- int initialized ;
8
-
9
7
DIR * dir ;
10
8
11
9
/*
12
10
* The length of the directory part of path at this level
13
11
* (including a trailing '/'):
14
12
*/
15
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 ;
26
14
};
27
15
28
16
/*
@@ -34,9 +22,11 @@ struct dir_iterator_int {
34
22
struct dir_iterator base ;
35
23
36
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.
40
30
*/
41
31
size_t levels_nr ;
42
32
@@ -50,113 +40,118 @@ struct dir_iterator_int {
50
40
struct dir_iterator_level * levels ;
51
41
};
52
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
+
53
117
int dir_iterator_advance (struct dir_iterator * dir_iterator )
54
118
{
55
119
struct dir_iterator_int * iter =
56
120
(struct dir_iterator_int * )dir_iterator ;
57
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. */
58
130
while (1 ) {
131
+ struct dirent * de ;
59
132
struct dir_iterator_level * level =
60
133
& iter -> levels [iter -> levels_nr - 1 ];
61
- struct dirent * de ;
62
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'" ,
75
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 ;
100
146
}
101
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 ;
109
150
151
+ if (prepare_next_entry_data (iter , de ))
110
152
continue ;
111
- }
112
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 ;
160
155
}
161
156
}
162
157
@@ -187,17 +182,32 @@ struct dir_iterator *dir_iterator_begin(const char *path)
187
182
{
188
183
struct dir_iterator_int * iter = xcalloc (1 , sizeof (* iter ));
189
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 ;
193
186
194
187
strbuf_init (& iter -> base .path , PATH_MAX );
195
188
strbuf_addstr (& iter -> base .path , path );
196
189
197
190
ALLOC_GROW (iter -> levels , 10 , iter -> levels_alloc );
191
+ iter -> levels_nr = 0 ;
198
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
+ }
201
206
202
207
return dir_iterator ;
208
+
209
+ error_out :
210
+ dir_iterator_abort (dir_iterator );
211
+ errno = saved_errno ;
212
+ return NULL ;
203
213
}
0 commit comments