22#include "dir.h"
33#include "iterator.h"
44#include "dir-iterator.h"
5+ #include "string-list.h"
56
67struct dir_iterator_level {
78 DIR * dir ;
89
10+ /*
11+ * The directory entries of the current level. This list will only be
12+ * populated when the iterator is ordered. In that case, `dir` will be
13+ * set to `NULL`.
14+ */
15+ struct string_list entries ;
16+ size_t entries_idx ;
17+
918 /*
1019 * The length of the directory part of path at this level
1120 * (including a trailing '/'):
@@ -43,6 +52,31 @@ struct dir_iterator_int {
4352 unsigned int flags ;
4453};
4554
55+ static int next_directory_entry (DIR * dir , const char * path ,
56+ struct dirent * * out )
57+ {
58+ struct dirent * de ;
59+
60+ repeat :
61+ errno = 0 ;
62+ de = readdir (dir );
63+ if (!de ) {
64+ if (errno ) {
65+ warning_errno ("error reading directory '%s'" ,
66+ path );
67+ return -1 ;
68+ }
69+
70+ return 1 ;
71+ }
72+
73+ if (is_dot_or_dotdot (de -> d_name ))
74+ goto repeat ;
75+
76+ * out = de ;
77+ return 0 ;
78+ }
79+
4680/*
4781 * Push a level in the iter stack and initialize it with information from
4882 * the directory pointed by iter->base->path. It is assumed that this
@@ -72,6 +106,35 @@ static int push_level(struct dir_iterator_int *iter)
72106 return -1 ;
73107 }
74108
109+ string_list_init_dup (& level -> entries );
110+ level -> entries_idx = 0 ;
111+
112+ /*
113+ * When the iterator is sorted we read and sort all directory entries
114+ * directly.
115+ */
116+ if (iter -> flags & DIR_ITERATOR_SORTED ) {
117+ struct dirent * de ;
118+
119+ while (1 ) {
120+ int ret = next_directory_entry (level -> dir , iter -> base .path .buf , & de );
121+ if (ret < 0 ) {
122+ if (errno != ENOENT &&
123+ iter -> flags & DIR_ITERATOR_PEDANTIC )
124+ return -1 ;
125+ continue ;
126+ } else if (ret > 0 ) {
127+ break ;
128+ }
129+
130+ string_list_append (& level -> entries , de -> d_name );
131+ }
132+ string_list_sort (& level -> entries );
133+
134+ closedir (level -> dir );
135+ level -> dir = NULL ;
136+ }
137+
75138 return 0 ;
76139}
77140
@@ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter)
88151 warning_errno ("error closing directory '%s'" ,
89152 iter -> base .path .buf );
90153 level -> dir = NULL ;
154+ string_list_clear (& level -> entries , 0 );
91155
92156 return -- iter -> levels_nr ;
93157}
94158
95159/*
96160 * Populate iter->base with the necessary information on the next iteration
97- * entry, represented by the given dirent de . Return 0 on success and -1
161+ * entry, represented by the given name . Return 0 on success and -1
98162 * otherwise, setting errno accordingly.
99163 */
100164static int prepare_next_entry_data (struct dir_iterator_int * iter ,
101- struct dirent * de )
165+ const char * name )
102166{
103167 int err , saved_errno ;
104168
105- strbuf_addstr (& iter -> base .path , de -> d_name );
169+ strbuf_addstr (& iter -> base .path , name );
106170 /*
107171 * We have to reset these because the path strbuf might have
108172 * been realloc()ed at the previous strbuf_addstr().
@@ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
139203 struct dirent * de ;
140204 struct dir_iterator_level * level =
141205 & iter -> levels [iter -> levels_nr - 1 ];
206+ const char * name ;
142207
143208 strbuf_setlen (& iter -> base .path , level -> prefix_len );
144- errno = 0 ;
145- de = readdir (level -> dir );
146209
147- if (!de ) {
148- if (errno ) {
149- warning_errno ("error reading directory '%s'" ,
150- iter -> base .path .buf );
210+ if (level -> dir ) {
211+ int ret = next_directory_entry (level -> dir , iter -> base .path .buf , & de );
212+ if (ret < 0 ) {
151213 if (iter -> flags & DIR_ITERATOR_PEDANTIC )
152214 goto error_out ;
153- } else if (pop_level (iter ) == 0 ) {
154- return dir_iterator_abort (dir_iterator );
215+ continue ;
216+ } else if (ret > 0 ) {
217+ if (pop_level (iter ) == 0 )
218+ return dir_iterator_abort (dir_iterator );
219+ continue ;
155220 }
156- continue ;
157- }
158221
159- if (is_dot_or_dotdot (de -> d_name ))
160- continue ;
222+ name = de -> d_name ;
223+ } else {
224+ if (level -> entries_idx >= level -> entries .nr ) {
225+ if (pop_level (iter ) == 0 )
226+ return dir_iterator_abort (dir_iterator );
227+ continue ;
228+ }
161229
162- if (prepare_next_entry_data (iter , de )) {
230+ name = level -> entries .items [level -> entries_idx ++ ].string ;
231+ }
232+
233+ if (prepare_next_entry_data (iter , name )) {
163234 if (errno != ENOENT && iter -> flags & DIR_ITERATOR_PEDANTIC )
164235 goto error_out ;
165236 continue ;
@@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
188259 warning_errno ("error closing directory '%s'" ,
189260 iter -> base .path .buf );
190261 }
262+
263+ string_list_clear (& level -> entries , 0 );
191264 }
192265
193266 free (iter -> levels );
0 commit comments