44#include "dir-iterator.h"
55
66struct dir_iterator_level {
7- int initialized ;
8-
97 DIR * dir ;
108
119 /*
1210 * The length of the directory part of path at this level
1311 * (including a trailing '/'):
1412 */
1513 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 ;
2614};
2715
2816/*
@@ -34,9 +22,11 @@ struct dir_iterator_int {
3422 struct dir_iterator base ;
3523
3624 /*
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.
4030 */
4131 size_t levels_nr ;
4232
@@ -50,113 +40,118 @@ struct dir_iterator_int {
5040 struct dir_iterator_level * levels ;
5141};
5242
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+
53117int dir_iterator_advance (struct dir_iterator * dir_iterator )
54118{
55119 struct dir_iterator_int * iter =
56120 (struct dir_iterator_int * )dir_iterator ;
57121
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. */
58130 while (1 ) {
131+ struct dirent * de ;
59132 struct dir_iterator_level * level =
60133 & iter -> levels [iter -> levels_nr - 1 ];
61- struct dirent * de ;
62134
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'" ,
75142 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 ;
100146 }
101147
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 ;
109150
151+ if (prepare_next_entry_data (iter , de ))
110152 continue ;
111- }
112153
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 ;
160155 }
161156}
162157
@@ -187,17 +182,32 @@ struct dir_iterator *dir_iterator_begin(const char *path)
187182{
188183 struct dir_iterator_int * iter = xcalloc (1 , sizeof (* iter ));
189184 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 ;
193186
194187 strbuf_init (& iter -> base .path , PATH_MAX );
195188 strbuf_addstr (& iter -> base .path , path );
196189
197190 ALLOC_GROW (iter -> levels , 10 , iter -> levels_alloc );
191+ iter -> levels_nr = 0 ;
198192
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+ }
201206
202207 return dir_iterator ;
208+
209+ error_out :
210+ dir_iterator_abort (dir_iterator );
211+ errno = saved_errno ;
212+ return NULL ;
203213}
0 commit comments