source src/iterator.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
1 | - | /* | ||
2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
3 | - | * | ||
4 | - | * This file is part of libgit2, distributed under the GNU GPL v2 with | ||
5 | - | * a Linking Exception. For full terms see the included COPYING file. | ||
6 | - | */ | ||
7 | - | |||
8 | - | #include "iterator.h" | ||
9 | - | |||
10 | - | #include "tree.h" | ||
11 | - | #include "index.h" | ||
12 | - | |||
13 | - | #define GIT_ITERATOR_FIRST_ACCESS (1 << 15) | ||
14 | - | #define GIT_ITERATOR_HONOR_IGNORES (1 << 16) | ||
15 | - | #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17) | ||
16 | - | |||
17 | - | #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0) | ||
18 | - | #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE) | ||
19 | - | #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES) | ||
20 | - | #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) | ||
21 | - | #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND) | ||
22 | - | #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS) | ||
23 | - | #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) | ||
24 | - | #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES) | ||
25 | - | #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT) | ||
26 | - | #define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS) | ||
27 | - | |||
28 | - | |||
29 | 18863 | 2 | static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case) | |
30 | - | { | ||
31 | 18863 | 2 | if (ignore_case) | |
32 | 216 | 3 | iter->flags |= GIT_ITERATOR_IGNORE_CASE; | |
33 | - | else | ||
34 | 18647 | 4 | iter->flags &= ~GIT_ITERATOR_IGNORE_CASE; | |
35 | - | |||
36 | 18863 | 5-7 | iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp; | |
37 | 18863 | 8-10 | iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp; | |
38 | 18863 | 11-13 | iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp; | |
39 | 18863 | 14-16 | iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch; | |
40 | - | |||
41 | 18863 | 17 | git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp); | |
42 | 18863 | 18 | } | |
43 | - | |||
44 | 19631 | 2 | static int iterator_range_init( | |
45 | - | git_iterator *iter, const char *start, const char *end) | ||
46 | - | { | ||
47 | 19631 | 2,3 | if (start && *start) { | |
48 | 6259 | 4 | iter->start = git__strdup(start); | |
49 | 6259 | 5,6 | GIT_ERROR_CHECK_ALLOC(iter->start); | |
50 | - | |||
51 | 6259 | 7 | iter->start_len = strlen(iter->start); | |
52 | - | } | ||
53 | - | |||
54 | 19631 | 8,9 | if (end && *end) { | |
55 | 6258 | 10 | iter->end = git__strdup(end); | |
56 | 6258 | 11,12 | GIT_ERROR_CHECK_ALLOC(iter->end); | |
57 | - | |||
58 | 6258 | 13 | iter->end_len = strlen(iter->end); | |
59 | - | } | ||
60 | - | |||
61 | 19631 | 14 | iter->started = (iter->start == NULL); | |
62 | 19631 | 14 | iter->ended = false; | |
63 | - | |||
64 | 19631 | 14 | return 0; | |
65 | - | } | ||
66 | - | |||
67 | 842 | 2 | static void iterator_range_free(git_iterator *iter) | |
68 | - | { | ||
69 | 842 | 2 | if (iter->start) { | |
70 | ##### | 3 | git__free(iter->start); | |
71 | ##### | 4 | iter->start = NULL; | |
72 | ##### | 4 | iter->start_len = 0; | |
73 | - | } | ||
74 | - | |||
75 | 842 | 5 | if (iter->end) { | |
76 | ##### | 6 | git__free(iter->end); | |
77 | ##### | 7 | iter->end = NULL; | |
78 | ##### | 7 | iter->end_len = 0; | |
79 | - | } | ||
80 | 842 | 8 | } | |
81 | - | |||
82 | 842 | 2 | static int iterator_reset_range( | |
83 | - | git_iterator *iter, const char *start, const char *end) | ||
84 | - | { | ||
85 | 842 | 2 | iterator_range_free(iter); | |
86 | 842 | 3 | return iterator_range_init(iter, start, end); | |
87 | - | } | ||
88 | - | |||
89 | 18789 | 2 | static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist) | |
90 | - | { | ||
91 | - | size_t i; | ||
92 | - | |||
93 | 18789 | 2,3 | if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0) | |
94 | ##### | 4 | return -1; | |
95 | - | |||
96 | 22852 | 5,11,12 | for (i = 0; i < pathlist->count; i++) { | |
97 | 4063 | 6 | if (!pathlist->strings[i]) | |
98 | 2 | 7 | continue; | |
99 | - | |||
100 | 4061 | 8,9 | if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0) | |
101 | ##### | 10 | return -1; | |
102 | - | } | ||
103 | - | |||
104 | 18789 | 13 | return 0; | |
105 | - | } | ||
106 | - | |||
107 | 18789 | 2 | static int iterator_init_common( | |
108 | - | git_iterator *iter, | ||
109 | - | git_repository *repo, | ||
110 | - | git_index *index, | ||
111 | - | git_iterator_options *given_opts) | ||
112 | - | { | ||
113 | - | static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT; | ||
114 | 18789 | 2-4 | git_iterator_options *options = given_opts ? given_opts : &default_opts; | |
115 | - | bool ignore_case; | ||
116 | - | int precompose; | ||
117 | - | int error; | ||
118 | - | |||
119 | 18789 | 5 | iter->repo = repo; | |
120 | 18789 | 5 | iter->index = index; | |
121 | 18789 | 5 | iter->flags = options->flags; | |
122 | - | |||
123 | 18789 | 5 | if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) { | |
124 | 58 | 6 | ignore_case = true; | |
125 | 18731 | 7 | } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) { | |
126 | 13099 | 8 | ignore_case = false; | |
127 | 5632 | 9 | } else if (repo) { | |
128 | - | git_index *index; | ||
129 | - | |||
130 | 5141 | 10,11 | if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0) | |
131 | ##### | 12 | return error; | |
132 | - | |||
133 | 5141 | 13 | ignore_case = !!index->ignore_case; | |
134 | - | |||
135 | 5141 | 13 | if (ignore_case == 1) | |
136 | 84 | 14 | iter->flags |= GIT_ITERATOR_IGNORE_CASE; | |
137 | - | else | ||
138 | 5141 | 15,16 | iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE; | |
139 | - | } else { | ||
140 | 491 | 17 | ignore_case = false; | |
141 | - | } | ||
142 | - | |||
143 | - | /* try to look up precompose and set flag if appropriate */ | ||
144 | 18789 | 18,19 | if (repo && | |
145 | 18196 | 19,20 | (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 && | |
146 | 18196 | 20 | (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) { | |
147 | - | |||
148 | 18196 | 21,22 | if (git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) < 0) | |
149 | ##### | 23 | git_error_clear(); | |
150 | 18196 | 24 | else if (precompose) | |
151 | 786 | 25 | iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; | |
152 | - | } | ||
153 | - | |||
154 | 18789 | 26 | if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND)) | |
155 | 2422 | 27 | iter->flags |= GIT_ITERATOR_INCLUDE_TREES; | |
156 | - | |||
157 | 18789 | 28-31 | if ((error = iterator_range_init(iter, options->start, options->end)) < 0 || | |
158 | 18789 | 30 | (error = iterator_pathlist_init(iter, &options->pathlist)) < 0) | |
159 | ##### | 32 | return error; | |
160 | - | |||
161 | 18789 | 33 | iterator_set_ignore_case(iter, ignore_case); | |
162 | 18789 | 34 | return 0; | |
163 | - | } | ||
164 | - | |||
165 | 15730 | 2 | static void iterator_clear(git_iterator *iter) | |
166 | - | { | ||
167 | 15730 | 2 | iter->started = false; | |
168 | 15730 | 2 | iter->ended = false; | |
169 | 15730 | 2 | iter->stat_calls = 0; | |
170 | 15730 | 2 | iter->pathlist_walk_idx = 0; | |
171 | 15730 | 2 | iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS; | |
172 | 15730 | 2 | } | |
173 | - | |||
174 | 740450 | 2 | GIT_INLINE(bool) iterator_has_started( | |
175 | - | git_iterator *iter, const char *path, bool is_submodule) | ||
176 | - | { | ||
177 | - | size_t path_len; | ||
178 | - | |||
179 | 740450 | 2,3 | if (iter->start == NULL || iter->started == true) | |
180 | 652883 | 4 | return true; | |
181 | - | |||
182 | - | /* the starting path is generally a prefix - we have started once we | ||
183 | - | * are prefixed by this path | ||
184 | - | */ | ||
185 | 87567 | 5 | iter->started = (iter->prefixcomp(path, iter->start) >= 0); | |
186 | - | |||
187 | 87567 | 6 | if (iter->started) | |
188 | 6192 | 7 | return true; | |
189 | - | |||
190 | 81375 | 8 | path_len = strlen(path); | |
191 | - | |||
192 | - | /* if, however, we are a submodule, then we support `start` being | ||
193 | - | * suffixed with a `/` for crazy legacy reasons. match `submod` | ||
194 | - | * with a start path of `submod/`. | ||
195 | - | */ | ||
196 | 81375 | 8-11 | if (is_submodule && iter->start_len && path_len == iter->start_len - 1 && | |
197 | 2 | 11 | iter->start[iter->start_len-1] == '/') | |
198 | 2 | 12 | return true; | |
199 | - | |||
200 | - | /* if, however, our current path is a directory, and our starting path | ||
201 | - | * is _beneath_ that directory, then recurse into the directory (even | ||
202 | - | * though we have not yet "started") | ||
203 | - | */ | ||
204 | 81373 | 13,14,16 | if (path_len > 0 && path[path_len-1] == '/' && | |
205 | 18451 | 15 | iter->strncomp(path, iter->start, path_len) == 0) | |
206 | 5621 | 17 | return true; | |
207 | - | |||
208 | 75752 | 18 | return false; | |
209 | - | } | ||
210 | - | |||
211 | 664699 | 2 | GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path) | |
212 | - | { | ||
213 | 664699 | 2 | if (iter->end == NULL) | |
214 | 646180 | 3 | return false; | |
215 | 18519 | 4 | else if (iter->ended) | |
216 | 64 | 5 | return true; | |
217 | - | |||
218 | 18455 | 6 | iter->ended = (iter->prefixcomp(path, iter->end) > 0); | |
219 | 18455 | 7 | return iter->ended; | |
220 | - | } | ||
221 | - | |||
222 | - | /* walker for the index and tree iterator that allows it to walk the sorted | ||
223 | - | * pathlist entries alongside sorted iterator entries. | ||
224 | - | */ | ||
225 | 658630 | 2 | static bool iterator_pathlist_next_is(git_iterator *iter, const char *path) | |
226 | - | { | ||
227 | - | char *p; | ||
228 | - | size_t path_len, p_len, cmp_len, i; | ||
229 | - | int cmp; | ||
230 | - | |||
231 | 658630 | 2 | if (iter->pathlist.length == 0) | |
232 | 645073 | 3 | return true; | |
233 | - | |||
234 | 13557 | 4 | git_vector_sort(&iter->pathlist); | |
235 | - | |||
236 | 13557 | 5 | path_len = strlen(path); | |
237 | - | |||
238 | - | /* for comparison, drop the trailing slash on the current '/' */ | ||
239 | 13557 | 5,6 | if (path_len && path[path_len-1] == '/') | |
240 | 69 | 7 | path_len--; | |
241 | - | |||
242 | 15892 | 8,25,26 | for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) { | |
243 | 9553 | 9 | p = iter->pathlist.contents[i]; | |
244 | 9553 | 9 | p_len = strlen(p); | |
245 | - | |||
246 | 9553 | 9,10 | if (p_len && p[p_len-1] == '/') | |
247 | 904 | 11 | p_len--; | |
248 | - | |||
249 | 9553 | 12 | cmp_len = min(path_len, p_len); | |
250 | - | |||
251 | - | /* see if the pathlist entry is a prefix of this path */ | ||
252 | 9553 | 12 | cmp = iter->strncomp(p, path, cmp_len); | |
253 | - | |||
254 | - | /* prefix match - see if there's an exact match, or if we were | ||
255 | - | * given a path that matches the directory | ||
256 | - | */ | ||
257 | 9553 | 13 | if (cmp == 0) { | |
258 | - | /* if this pathlist entry is not suffixed with a '/' then | ||
259 | - | * it matches a path that is a file or a directory. | ||
260 | - | * (eg, pathlist = "foo" and path is "foo" or "foo/" or | ||
261 | - | * "foo/something") | ||
262 | - | */ | ||
263 | 2217 | 14,15 | if (p[cmp_len] == '\0' && | |
264 | 2004 | 15,16 | (path[cmp_len] == '\0' || path[cmp_len] == '/')) | |
265 | 1983 | 17 | return true; | |
266 | - | |||
267 | - | /* if this pathlist entry _is_ suffixed with a '/' then | ||
268 | - | * it matches only paths that are directories. | ||
269 | - | * (eg, pathlist = "foo/" and path is "foo/" or "foo/something") | ||
270 | - | */ | ||
271 | 234 | 18,19 | if (p[cmp_len] == '/' && path[cmp_len] == '/') | |
272 | 150 | 20 | return true; | |
273 | - | } | ||
274 | - | |||
275 | - | /* this pathlist entry sorts before the given path, try the next */ | ||
276 | 7336 | 21 | else if (cmp < 0) { | |
277 | 2251 | 22 | iter->pathlist_walk_idx++; | |
278 | 2251 | 22 | continue; | |
279 | - | } | ||
280 | - | |||
281 | - | /* this pathlist sorts after the given path, no match. */ | ||
282 | 5085 | 23 | else if (cmp > 0) { | |
283 | 5085 | 24 | break; | |
284 | - | } | ||
285 | - | } | ||
286 | - | |||
287 | 11424 | 27 | return false; | |
288 | - | } | ||
289 | - | |||
290 | - | typedef enum { | ||
291 | - | ITERATOR_PATHLIST_NONE = 0, | ||
292 | - | ITERATOR_PATHLIST_IS_FILE = 1, | ||
293 | - | ITERATOR_PATHLIST_IS_DIR = 2, | ||
294 | - | ITERATOR_PATHLIST_IS_PARENT = 3, | ||
295 | - | ITERATOR_PATHLIST_FULL = 4, | ||
296 | - | } iterator_pathlist_search_t; | ||
297 | - | |||
298 | 5299 | 2 | static iterator_pathlist_search_t iterator_pathlist_search( | |
299 | - | git_iterator *iter, const char *path, size_t path_len) | ||
300 | - | { | ||
301 | - | const char *p; | ||
302 | - | size_t idx; | ||
303 | - | int error; | ||
304 | - | |||
305 | 5299 | 2 | if (iter->pathlist.length == 0) | |
306 | ##### | 3 | return ITERATOR_PATHLIST_FULL; | |
307 | - | |||
308 | 5299 | 4 | git_vector_sort(&iter->pathlist); | |
309 | - | |||
310 | 5299 | 5 | error = git_vector_bsearch2(&idx, &iter->pathlist, | |
311 | 5299 | 5 | (git_vector_cmp)iter->strcomp, path); | |
312 | - | |||
313 | - | /* the given path was found in the pathlist. since the pathlist only | ||
314 | - | * matches directories when they're suffixed with a '/', analyze the | ||
315 | - | * path string to determine whether it's a directory or not. | ||
316 | - | */ | ||
317 | 5299 | 6 | if (error == 0) { | |
318 | 1064 | 7,8 | if (path_len && path[path_len-1] == '/') | |
319 | ##### | 9 | return ITERATOR_PATHLIST_IS_DIR; | |
320 | - | |||
321 | 1064 | 10 | return ITERATOR_PATHLIST_IS_FILE; | |
322 | - | } | ||
323 | - | |||
324 | - | /* at this point, the path we're examining may be a directory (though we | ||
325 | - | * don't know that yet, since we're avoiding a stat unless it's necessary) | ||
326 | - | * so walk the pathlist looking for the given path with a '/' after it, | ||
327 | - | */ | ||
328 | 4267 | 11,25,26 | while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) { | |
329 | 2924 | 12,13 | if (iter->prefixcomp(p, path) != 0) | |
330 | 2769 | 14 | break; | |
331 | - | |||
332 | - | /* an exact match would have been matched by the bsearch above */ | ||
333 | 155 | 15,16 | assert(p[path_len]); | |
334 | - | |||
335 | - | /* is this a literal directory entry (eg `foo/`) or a file beneath */ | ||
336 | 155 | 17 | if (p[path_len] == '/') { | |
337 | 111 | 18-21 | return (p[path_len+1] == '\0') ? | |
338 | - | ITERATOR_PATHLIST_IS_DIR : | ||
339 | - | ITERATOR_PATHLIST_IS_PARENT; | ||
340 | - | } | ||
341 | - | |||
342 | 44 | 22 | if (p[path_len] > '/') | |
343 | 12 | 23 | break; | |
344 | - | |||
345 | 32 | 24 | idx++; | |
346 | - | } | ||
347 | - | |||
348 | 4124 | 27 | return ITERATOR_PATHLIST_NONE; | |
349 | - | } | ||
350 | - | |||
351 | - | /* Empty iterator */ | ||
352 | - | |||
353 | 176 | 2 | static int empty_iterator_noop(const git_index_entry **e, git_iterator *i) | |
354 | - | { | ||
355 | - | GIT_UNUSED(i); | ||
356 | - | |||
357 | 176 | 2 | if (e) | |
358 | 176 | 3 | *e = NULL; | |
359 | - | |||
360 | 176 | 4 | return GIT_ITEROVER; | |
361 | - | } | ||
362 | - | |||
363 | ##### | 2 | static int empty_iterator_advance_over( | |
364 | - | const git_index_entry **e, | ||
365 | - | git_iterator_status_t *s, | ||
366 | - | git_iterator *i) | ||
367 | - | { | ||
368 | ##### | 2 | *s = GIT_ITERATOR_STATUS_EMPTY; | |
369 | ##### | 2 | return empty_iterator_noop(e, i); | |
370 | - | } | ||
371 | - | |||
372 | ##### | 2 | static int empty_iterator_reset(git_iterator *i) | |
373 | - | { | ||
374 | - | GIT_UNUSED(i); | ||
375 | ##### | 2 | return 0; | |
376 | - | } | ||
377 | - | |||
378 | 176 | 2 | static void empty_iterator_free(git_iterator *i) | |
379 | - | { | ||
380 | - | GIT_UNUSED(i); | ||
381 | 176 | 2 | } | |
382 | - | |||
383 | - | typedef struct { | ||
384 | - | git_iterator base; | ||
385 | - | git_iterator_callbacks cb; | ||
386 | - | } empty_iterator; | ||
387 | - | |||
388 | 176 | 2 | int git_iterator_for_nothing( | |
389 | - | git_iterator **out, | ||
390 | - | git_iterator_options *options) | ||
391 | - | { | ||
392 | - | empty_iterator *iter; | ||
393 | - | |||
394 | - | static git_iterator_callbacks callbacks = { | ||
395 | - | empty_iterator_noop, | ||
396 | - | empty_iterator_noop, | ||
397 | - | empty_iterator_noop, | ||
398 | - | empty_iterator_advance_over, | ||
399 | - | empty_iterator_reset, | ||
400 | - | empty_iterator_free | ||
401 | - | }; | ||
402 | - | |||
403 | 176 | 2 | *out = NULL; | |
404 | - | |||
405 | 176 | 2 | iter = git__calloc(1, sizeof(empty_iterator)); | |
406 | 176 | 3,4 | GIT_ERROR_CHECK_ALLOC(iter); | |
407 | - | |||
408 | 176 | 5 | iter->base.type = GIT_ITERATOR_EMPTY; | |
409 | 176 | 5 | iter->base.cb = &callbacks; | |
410 | 176 | 5 | iter->base.flags = options->flags; | |
411 | - | |||
412 | 176 | 5 | *out = &iter->base; | |
413 | 176 | 5 | return 0; | |
414 | - | } | ||
415 | - | |||
416 | - | /* Tree iterator */ | ||
417 | - | |||
418 | - | typedef struct { | ||
419 | - | git_tree_entry *tree_entry; | ||
420 | - | const char *parent_path; | ||
421 | - | } tree_iterator_entry; | ||
422 | - | |||
423 | - | typedef struct { | ||
424 | - | git_tree *tree; | ||
425 | - | |||
426 | - | /* path to this particular frame (folder) */ | ||
427 | - | git_buf path; | ||
428 | - | |||
429 | - | /* a sorted list of the entries for this frame (folder), these are | ||
430 | - | * actually pointers to the iterator's entry pool. | ||
431 | - | */ | ||
432 | - | git_vector entries; | ||
433 | - | tree_iterator_entry *current; | ||
434 | - | |||
435 | - | size_t next_idx; | ||
436 | - | |||
437 | - | /* on case insensitive iterations, we also have an array of other | ||
438 | - | * paths that were case insensitively equal to this one, and their | ||
439 | - | * tree objects. we have coalesced the tree entries into this frame. | ||
440 | - | * a child `tree_iterator_entry` will contain a pointer to its actual | ||
441 | - | * parent path. | ||
442 | - | */ | ||
443 | - | git_vector similar_trees; | ||
444 | - | git_array_t(git_buf) similar_paths; | ||
445 | - | } tree_iterator_frame; | ||
446 | - | |||
447 | - | typedef struct { | ||
448 | - | git_iterator base; | ||
449 | - | git_tree *root; | ||
450 | - | git_array_t(tree_iterator_frame) frames; | ||
451 | - | |||
452 | - | git_index_entry entry; | ||
453 | - | git_buf entry_path; | ||
454 | - | |||
455 | - | /* a pool of entries to reduce the number of allocations */ | ||
456 | - | git_pool entry_pool; | ||
457 | - | } tree_iterator; | ||
458 | - | |||
459 | 180620 | 2 | GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame( | |
460 | - | tree_iterator *iter) | ||
461 | - | { | ||
462 | 180620 | 2 | return iter->frames.size > 1 ? | |
463 | 180620 | 2-4 | &iter->frames.ptr[iter->frames.size-2] : NULL; | |
464 | - | } | ||
465 | - | |||
466 | 1079583 | 2 | GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame( | |
467 | - | tree_iterator *iter) | ||
468 | - | { | ||
469 | 1079583 | 2 | return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL; | |
470 | - | } | ||
471 | - | |||
472 | 1627 | 2 | GIT_INLINE(int) tree_entry_cmp( | |
473 | - | const git_tree_entry *a, const git_tree_entry *b, bool icase) | ||
474 | - | { | ||
475 | 1627 | 2-5,5,5,5 | return git_path_cmp( | |
476 | 1627 | 5,5 | a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE, | |
477 | 1627 | 5,5 | b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE, | |
478 | - | icase ? git__strncasecmp : git__strncmp); | ||
479 | - | } | ||
480 | - | |||
481 | 315 | 2 | GIT_INLINE(int) tree_iterator_entry_cmp_icase( | |
482 | - | const void *ptr_a, const void *ptr_b) | ||
483 | - | { | ||
484 | 315 | 2 | const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a; | |
485 | 315 | 2 | const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b; | |
486 | - | |||
487 | 315 | 2 | return tree_entry_cmp(a->tree_entry, b->tree_entry, true); | |
488 | - | } | ||
489 | - | |||
490 | 1244 | 2 | static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b) | |
491 | - | { | ||
492 | 1244 | 2 | const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a; | |
493 | 1244 | 2 | const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b; | |
494 | - | |||
495 | 1244 | 2 | int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true); | |
496 | - | |||
497 | - | /* stabilize the sort order for filenames that are (case insensitively) | ||
498 | - | * the same by examining the parent path (case sensitively) before | ||
499 | - | * falling back to a case sensitive sort of the filename. | ||
500 | - | */ | ||
501 | 1244 | 3,4 | if (!c && a->parent_path != b->parent_path) | |
502 | 116 | 5 | c = git__strcmp(a->parent_path, b->parent_path); | |
503 | - | |||
504 | 1244 | 6 | if (!c) | |
505 | 68 | 7 | c = tree_entry_cmp(a->tree_entry, b->tree_entry, false); | |
506 | - | |||
507 | 1244 | 8 | return c; | |
508 | - | } | ||
509 | - | |||
510 | 892724 | 2 | static int tree_iterator_compute_path( | |
511 | - | git_buf *out, | ||
512 | - | tree_iterator_entry *entry) | ||
513 | - | { | ||
514 | 892724 | 2 | git_buf_clear(out); | |
515 | - | |||
516 | 892724 | 3 | if (entry->parent_path) | |
517 | 759097 | 4 | git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename); | |
518 | - | else | ||
519 | 133627 | 5 | git_buf_puts(out, entry->tree_entry->filename); | |
520 | - | |||
521 | 892725 | 6,7 | if (git_tree_entry__is_tree(entry->tree_entry)) | |
522 | 379856 | 8 | git_buf_putc(out, '/'); | |
523 | - | |||
524 | 892725 | 9,10 | if (git_buf_oom(out)) | |
525 | ##### | 11 | return -1; | |
526 | - | |||
527 | 892725 | 12 | return 0; | |
528 | - | } | ||
529 | - | |||
530 | 192973 | 2 | static int tree_iterator_frame_init( | |
531 | - | tree_iterator *iter, | ||
532 | - | git_tree *tree, | ||
533 | - | tree_iterator_entry *frame_entry) | ||
534 | - | { | ||
535 | 192973 | 2 | tree_iterator_frame *new_frame = NULL; | |
536 | - | tree_iterator_entry *new_entry; | ||
537 | 192973 | 2 | git_tree *dup = NULL; | |
538 | - | git_tree_entry *tree_entry; | ||
539 | - | git_vector_cmp cmp; | ||
540 | - | size_t i; | ||
541 | 192973 | 2 | int error = 0; | |
542 | - | |||
543 | 192973 | 2-7 | new_frame = git_array_alloc(iter->frames); | |
544 | 192973 | 8,9 | GIT_ERROR_CHECK_ALLOC(new_frame); | |
545 | - | |||
546 | 192973 | 10,11 | if ((error = git_tree_dup(&dup, tree)) < 0) | |
547 | ##### | 12 | goto done; | |
548 | - | |||
549 | 192973 | 13 | memset(new_frame, 0x0, sizeof(tree_iterator_frame)); | |
550 | 192973 | 13 | new_frame->tree = dup; | |
551 | - | |||
552 | 192973 | 13-15 | if (frame_entry && | |
553 | 180620 | 14 | (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0) | |
554 | ##### | 16 | goto done; | |
555 | - | |||
556 | 192973 | 17,20 | cmp = iterator__ignore_case(&iter->base) ? | |
557 | 192973 | 17-19 | tree_iterator_entry_sort_icase : NULL; | |
558 | - | |||
559 | 192973 | 20,21 | if ((error = git_vector_init(&new_frame->entries, | |
560 | 192973 | 20 | dup->entries.size, cmp)) < 0) | |
561 | ##### | 22 | goto done; | |
562 | - | |||
563 | 932104 | 23,31-33 | git_array_foreach(dup->entries, i, tree_entry) { | |
564 | 739131 | 24,25 | if ((new_entry = git_pool_malloc(&iter->entry_pool, 1)) == NULL) { | |
565 | ##### | 26 | git_error_set_oom(); | |
566 | ##### | 27 | error = -1; | |
567 | ##### | 27 | goto done; | |
568 | - | } | ||
569 | - | |||
570 | 739131 | 28 | new_entry->tree_entry = tree_entry; | |
571 | 739131 | 28 | new_entry->parent_path = new_frame->path.ptr; | |
572 | - | |||
573 | 739131 | 28,29 | if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0) | |
574 | ##### | 30 | goto done; | |
575 | - | } | ||
576 | - | |||
577 | 192973 | 34-37 | git_vector_set_sorted(&new_frame->entries, | |
578 | - | !iterator__ignore_case(&iter->base)); | ||
579 | - | |||
580 | - | done: | ||
581 | 192973 | 38 | if (error < 0) { | |
582 | ##### | 39 | git_tree_free(dup); | |
583 | ##### | 40-42 | git_array_pop(iter->frames); | |
584 | - | } | ||
585 | - | |||
586 | 192973 | 43 | return error; | |
587 | - | } | ||
588 | - | |||
589 | 712092 | 2 | GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry( | |
590 | - | tree_iterator_frame *frame) | ||
591 | - | { | ||
592 | 712092 | 2 | return frame->current; | |
593 | - | } | ||
594 | - | |||
595 | 43 | 2 | GIT_INLINE(int) tree_iterator_frame_push_neighbors( | |
596 | - | tree_iterator *iter, | ||
597 | - | tree_iterator_frame *parent_frame, | ||
598 | - | tree_iterator_frame *frame, | ||
599 | - | const char *filename) | ||
600 | - | { | ||
601 | - | tree_iterator_entry *entry, *new_entry; | ||
602 | 43 | 2 | git_tree *tree = NULL; | |
603 | - | git_tree_entry *tree_entry; | ||
604 | - | git_buf *path; | ||
605 | - | size_t new_size, i; | ||
606 | 43 | 2 | int error = 0; | |
607 | - | |||
608 | 155 | 2,43 | while (parent_frame->next_idx < parent_frame->entries.length) { | |
609 | 130 | 3 | entry = parent_frame->entries.contents[parent_frame->next_idx]; | |
610 | - | |||
611 | 130 | 3 | if (strcasecmp(filename, entry->tree_entry->filename) != 0) | |
612 | 18 | 4 | break; | |
613 | - | |||
614 | 112 | 5,6 | if ((error = git_tree_lookup(&tree, | |
615 | 112 | 5 | iter->base.repo, entry->tree_entry->oid)) < 0) | |
616 | ##### | 7 | break; | |
617 | - | |||
618 | 112 | 8,9 | if (git_vector_insert(&parent_frame->similar_trees, tree) < 0) | |
619 | ##### | 10 | break; | |
620 | - | |||
621 | 112 | 11-16 | path = git_array_alloc(parent_frame->similar_paths); | |
622 | 112 | 17,18 | GIT_ERROR_CHECK_ALLOC(path); | |
623 | - | |||
624 | 112 | 19 | memset(path, 0, sizeof(git_buf)); | |
625 | - | |||
626 | 112 | 19,20 | if ((error = tree_iterator_compute_path(path, entry)) < 0) | |
627 | ##### | 21 | break; | |
628 | - | |||
629 | 112 | 22-28 | GIT_ERROR_CHECK_ALLOC_ADD(&new_size, | |
630 | - | frame->entries.length, tree->entries.size); | ||
631 | 112 | 29 | git_vector_size_hint(&frame->entries, new_size); | |
632 | - | |||
633 | 340 | 30,37-39 | git_array_foreach(tree->entries, i, tree_entry) { | |
634 | 228 | 31 | new_entry = git_pool_malloc(&iter->entry_pool, 1); | |
635 | 228 | 32,33 | GIT_ERROR_CHECK_ALLOC(new_entry); | |
636 | - | |||
637 | 228 | 34 | new_entry->tree_entry = tree_entry; | |
638 | 228 | 34 | new_entry->parent_path = path->ptr; | |
639 | - | |||
640 | 228 | 34,35 | if ((error = git_vector_insert(&frame->entries, new_entry)) < 0) | |
641 | ##### | 36 | break; | |
642 | - | } | ||
643 | - | |||
644 | 112 | 40 | if (error) | |
645 | ##### | 41 | break; | |
646 | - | |||
647 | 112 | 42 | parent_frame->next_idx++; | |
648 | - | } | ||
649 | - | |||
650 | 43 | 44 | return error; | |
651 | - | } | ||
652 | - | |||
653 | 180620 | 2 | GIT_INLINE(int) tree_iterator_frame_push( | |
654 | - | tree_iterator *iter, tree_iterator_entry *entry) | ||
655 | - | { | ||
656 | - | tree_iterator_frame *parent_frame, *frame; | ||
657 | 180620 | 2 | git_tree *tree = NULL; | |
658 | - | int error; | ||
659 | - | |||
660 | 180620 | 2,3 | if ((error = git_tree_lookup(&tree, | |
661 | 180620 | 2,4,5 | iter->base.repo, entry->tree_entry->oid)) < 0 || | |
662 | 180620 | 4 | (error = tree_iterator_frame_init(iter, tree, entry)) < 0) | |
663 | - | goto done; | ||
664 | - | |||
665 | 180620 | 6 | parent_frame = tree_iterator_parent_frame(iter); | |
666 | 180620 | 7 | frame = tree_iterator_current_frame(iter); | |
667 | - | |||
668 | - | /* if we're case insensitive, then we may have another directory that | ||
669 | - | * is (case insensitively) equal to this one. coalesce those children | ||
670 | - | * into this tree. | ||
671 | - | */ | ||
672 | 180620 | 8 | if (iterator__ignore_case(&iter->base)) | |
673 | 43 | 9 | error = tree_iterator_frame_push_neighbors(iter, | |
674 | 43 | 9 | parent_frame, frame, entry->tree_entry->filename); | |
675 | - | |||
676 | - | done: | ||
677 | 180620 | 10 | git_tree_free(tree); | |
678 | 180620 | 11 | return error; | |
679 | - | } | ||
680 | - | |||
681 | 192973 | 2 | static void tree_iterator_frame_pop(tree_iterator *iter) | |
682 | - | { | ||
683 | - | tree_iterator_frame *frame; | ||
684 | 192973 | 2 | git_buf *buf = NULL; | |
685 | - | git_tree *tree; | ||
686 | - | size_t i; | ||
687 | - | |||
688 | 192973 | 2,3 | assert(iter->frames.size); | |
689 | - | |||
690 | 192973 | 4-6 | frame = git_array_pop(iter->frames); | |
691 | - | |||
692 | 192973 | 7 | git_vector_free(&frame->entries); | |
693 | 192973 | 8 | git_tree_free(frame->tree); | |
694 | - | |||
695 | - | do { | ||
696 | 193085 | 9-11 | buf = git_array_pop(frame->similar_paths); | |
697 | 193085 | 12 | git_buf_dispose(buf); | |
698 | 193085 | 13 | } while (buf != NULL); | |
699 | - | |||
700 | 192973 | 14 | git_array_clear(frame->similar_paths); | |
701 | - | |||
702 | 193085 | 15,17-19 | git_vector_foreach(&frame->similar_trees, i, tree) | |
703 | 112 | 16 | git_tree_free(tree); | |
704 | - | |||
705 | 192973 | 20 | git_vector_free(&frame->similar_trees); | |
706 | - | |||
707 | 192973 | 21 | git_buf_dispose(&frame->path); | |
708 | 192973 | 22 | } | |
709 | - | |||
710 | 11227 | 2 | static int tree_iterator_current( | |
711 | - | const git_index_entry **out, git_iterator *i) | ||
712 | - | { | ||
713 | 11227 | 2 | tree_iterator *iter = (tree_iterator *)i; | |
714 | - | |||
715 | 11227 | 2 | if (!iterator__has_been_accessed(i)) | |
716 | 11207 | 3 | return iter->base.cb->advance(out, i); | |
717 | - | |||
718 | 20 | 4 | if (!iter->frames.size) { | |
719 | 7 | 5 | *out = NULL; | |
720 | 7 | 5 | return GIT_ITEROVER; | |
721 | - | } | ||
722 | - | |||
723 | 13 | 6 | *out = &iter->entry; | |
724 | 13 | 6 | return 0; | |
725 | - | } | ||
726 | - | |||
727 | 449275 | 2 | static void tree_iterator_set_current( | |
728 | - | tree_iterator *iter, | ||
729 | - | tree_iterator_frame *frame, | ||
730 | - | tree_iterator_entry *entry) | ||
731 | - | { | ||
732 | 449275 | 2 | git_tree_entry *tree_entry = entry->tree_entry; | |
733 | - | |||
734 | 449275 | 2 | frame->current = entry; | |
735 | - | |||
736 | 449275 | 2 | memset(&iter->entry, 0x0, sizeof(git_index_entry)); | |
737 | - | |||
738 | 449275 | 2 | iter->entry.mode = tree_entry->attr; | |
739 | 449275 | 2 | iter->entry.path = iter->entry_path.ptr; | |
740 | 449275 | 2 | git_oid_cpy(&iter->entry.id, tree_entry->oid); | |
741 | 449277 | 3 | } | |
742 | - | |||
743 | 461135 | 2 | static int tree_iterator_advance(const git_index_entry **out, git_iterator *i) | |
744 | - | { | ||
745 | 461135 | 2 | tree_iterator *iter = (tree_iterator *)i; | |
746 | 461135 | 2 | int error = 0; | |
747 | - | |||
748 | 461135 | 2 | iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; | |
749 | - | |||
750 | - | /* examine tree entries until we find the next one to return */ | ||
751 | - | while (true) { | ||
752 | - | tree_iterator_entry *prev_entry, *entry; | ||
753 | - | tree_iterator_frame *frame; | ||
754 | - | bool is_tree; | ||
755 | - | |||
756 | 898935 | 3,4 | if ((frame = tree_iterator_current_frame(iter)) == NULL) { | |
757 | 5966 | 5 | error = GIT_ITEROVER; | |
758 | 5966 | 5 | break; | |
759 | - | } | ||
760 | - | |||
761 | - | /* no more entries in this frame. pop the frame out */ | ||
762 | 892967 | 6 | if (frame->next_idx == frame->entries.length) { | |
763 | 180906 | 7 | tree_iterator_frame_pop(iter); | |
764 | 180906 | 8 | continue; | |
765 | - | } | ||
766 | - | |||
767 | - | /* we may have coalesced the contents of case-insensitively same-named | ||
768 | - | * directories, so do the sort now. | ||
769 | - | */ | ||
770 | 712061 | 9,10 | if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries)) | |
771 | 81 | 11 | git_vector_sort(&frame->entries); | |
772 | - | |||
773 | - | /* we have more entries in the current frame, that's our next entry */ | ||
774 | 712061 | 12 | prev_entry = tree_iterator_current_entry(frame); | |
775 | 712061 | 13 | entry = frame->entries.contents[frame->next_idx]; | |
776 | 712061 | 13 | frame->next_idx++; | |
777 | - | |||
778 | - | /* we can have collisions when iterating case insensitively. (eg, | ||
779 | - | * 'A/a' and 'a/A'). squash this one if it's already been seen. | ||
780 | - | */ | ||
781 | 712061 | 13,14 | if (iterator__ignore_case(&iter->base) && | |
782 | 315 | 16 | prev_entry && | |
783 | 315 | 15 | tree_iterator_entry_cmp_icase(prev_entry, entry) == 0) | |
784 | 68 | 17 | continue; | |
785 | - | |||
786 | 711993 | 18,19 | if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0) | |
787 | ##### | 20 | break; | |
788 | - | |||
789 | - | /* if this path is before our start, advance over this entry */ | ||
790 | 711993 | 21,22 | if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false)) | |
791 | 74932 | 23 | continue; | |
792 | - | |||
793 | - | /* if this path is after our end, stop */ | ||
794 | 637061 | 24,25 | if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) { | |
795 | 5892 | 26 | error = GIT_ITEROVER; | |
796 | 5892 | 26 | break; | |
797 | - | } | ||
798 | - | |||
799 | - | /* if we have a list of paths we're interested in, examine it */ | ||
800 | 631169 | 27,28 | if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr)) | |
801 | 1323 | 29 | continue; | |
802 | - | |||
803 | 629846 | 30 | is_tree = git_tree_entry__is_tree(entry->tree_entry); | |
804 | - | |||
805 | - | /* if we are *not* including trees then advance over this entry */ | ||
806 | 629845 | 31,32 | if (is_tree && !iterator__include_trees(iter)) { | |
807 | - | |||
808 | - | /* if we've found a tree (and are not returning it to the caller) | ||
809 | - | * and we are autoexpanding, then we want to return the first | ||
810 | - | * child. push the new directory and advance. | ||
811 | - | */ | ||
812 | 180571 | 33 | if (iterator__do_autoexpand(iter)) { | |
813 | 180571 | 34,35 | if ((error = tree_iterator_frame_push(iter, entry)) < 0) | |
814 | ##### | 36 | break; | |
815 | - | } | ||
816 | - | |||
817 | 180571 | 37 | continue; | |
818 | - | } | ||
819 | - | |||
820 | 449274 | 38 | tree_iterator_set_current(iter, frame, entry); | |
821 | - | |||
822 | - | /* if we are autoexpanding, then push this as a new frame, so that | ||
823 | - | * the next call to `advance` will dive into this directory. | ||
824 | - | */ | ||
825 | 449276 | 39,40 | if (is_tree && iterator__do_autoexpand(iter)) | |
826 | 42 | 41 | error = tree_iterator_frame_push(iter, entry); | |
827 | - | |||
828 | 449276 | 42 | break; | |
829 | 437800 | 43 | } | |
830 | - | |||
831 | 461134 | 44 | if (out) | |
832 | 461070 | 45-48 | *out = (error == 0) ? &iter->entry : NULL; | |
833 | - | |||
834 | 461134 | 49 | return error; | |
835 | - | } | ||
836 | - | |||
837 | 28 | 2 | static int tree_iterator_advance_into( | |
838 | - | const git_index_entry **out, git_iterator *i) | ||
839 | - | { | ||
840 | 28 | 2 | tree_iterator *iter = (tree_iterator *)i; | |
841 | - | tree_iterator_frame *frame; | ||
842 | - | tree_iterator_entry *prev_entry; | ||
843 | - | int error; | ||
844 | - | |||
845 | 28 | 2 | if (out) | |
846 | 28 | 3 | *out = NULL; | |
847 | - | |||
848 | 28 | 4,5 | if ((frame = tree_iterator_current_frame(iter)) == NULL) | |
849 | ##### | 6 | return GIT_ITEROVER; | |
850 | - | |||
851 | - | /* get the last seen entry */ | ||
852 | 28 | 7 | prev_entry = tree_iterator_current_entry(frame); | |
853 | - | |||
854 | - | /* it's legal to call advance_into when auto-expand is on. in this case, | ||
855 | - | * we will have pushed a new (empty) frame on to the stack for this | ||
856 | - | * new directory. since it's empty, its current_entry should be null. | ||
857 | - | */ | ||
858 | 28 | 8,9 | assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL)); | |
859 | - | |||
860 | 28 | 10 | if (prev_entry) { | |
861 | 7 | 11,12 | if (!git_tree_entry__is_tree(prev_entry->tree_entry)) | |
862 | ##### | 13 | return 0; | |
863 | - | |||
864 | 7 | 14,15 | if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0) | |
865 | ##### | 16 | return error; | |
866 | - | } | ||
867 | - | |||
868 | - | /* we've advanced into the directory in question, let advance | ||
869 | - | * find the first entry | ||
870 | - | */ | ||
871 | 28 | 17 | return tree_iterator_advance(out, i); | |
872 | - | } | ||
873 | - | |||
874 | ##### | 2 | static int tree_iterator_advance_over( | |
875 | - | const git_index_entry **out, | ||
876 | - | git_iterator_status_t *status, | ||
877 | - | git_iterator *i) | ||
878 | - | { | ||
879 | ##### | 2 | *status = GIT_ITERATOR_STATUS_NORMAL; | |
880 | ##### | 2 | return git_iterator_advance(out, i); | |
881 | - | } | ||
882 | - | |||
883 | 12353 | 2 | static void tree_iterator_clear(tree_iterator *iter) | |
884 | - | { | ||
885 | 24420 | 2,4 | while (iter->frames.size) | |
886 | 12067 | 3 | tree_iterator_frame_pop(iter); | |
887 | - | |||
888 | 12353 | 5 | git_array_clear(iter->frames); | |
889 | - | |||
890 | 12353 | 6 | git_pool_clear(&iter->entry_pool); | |
891 | 12353 | 7 | git_buf_clear(&iter->entry_path); | |
892 | - | |||
893 | 12353 | 8 | iterator_clear(&iter->base); | |
894 | 12353 | 9 | } | |
895 | - | |||
896 | 12353 | 2 | static int tree_iterator_init(tree_iterator *iter) | |
897 | - | { | ||
898 | - | int error; | ||
899 | - | |||
900 | 12353 | 2-5 | if ((error = git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry))) < 0 || | |
901 | 12353 | 4 | (error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0) | |
902 | ##### | 6 | return error; | |
903 | - | |||
904 | 12353 | 7 | iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; | |
905 | - | |||
906 | 12353 | 7 | return 0; | |
907 | - | } | ||
908 | - | |||
909 | 621 | 2 | static int tree_iterator_reset(git_iterator *i) | |
910 | - | { | ||
911 | 621 | 2 | tree_iterator *iter = (tree_iterator *)i; | |
912 | - | |||
913 | 621 | 2 | tree_iterator_clear(iter); | |
914 | 621 | 3 | return tree_iterator_init(iter); | |
915 | - | } | ||
916 | - | |||
917 | 11732 | 2 | static void tree_iterator_free(git_iterator *i) | |
918 | - | { | ||
919 | 11732 | 2 | tree_iterator *iter = (tree_iterator *)i; | |
920 | - | |||
921 | 11732 | 2 | tree_iterator_clear(iter); | |
922 | - | |||
923 | 11732 | 3 | git_tree_free(iter->root); | |
924 | 11732 | 4 | git_buf_dispose(&iter->entry_path); | |
925 | 11732 | 5 | } | |
926 | - | |||
927 | 11904 | 2 | int git_iterator_for_tree( | |
928 | - | git_iterator **out, | ||
929 | - | git_tree *tree, | ||
930 | - | git_iterator_options *options) | ||
931 | - | { | ||
932 | - | tree_iterator *iter; | ||
933 | - | int error; | ||
934 | - | |||
935 | - | static git_iterator_callbacks callbacks = { | ||
936 | - | tree_iterator_current, | ||
937 | - | tree_iterator_advance, | ||
938 | - | tree_iterator_advance_into, | ||
939 | - | tree_iterator_advance_over, | ||
940 | - | tree_iterator_reset, | ||
941 | - | tree_iterator_free | ||
942 | - | }; | ||
943 | - | |||
944 | 11904 | 2 | *out = NULL; | |
945 | - | |||
946 | 11904 | 2 | if (tree == NULL) | |
947 | 172 | 3 | return git_iterator_for_nothing(out, options); | |
948 | - | |||
949 | 11732 | 4 | iter = git__calloc(1, sizeof(tree_iterator)); | |
950 | 11732 | 5,6 | GIT_ERROR_CHECK_ALLOC(iter); | |
951 | - | |||
952 | 11732 | 7 | iter->base.type = GIT_ITERATOR_TREE; | |
953 | 11732 | 7 | iter->base.cb = &callbacks; | |
954 | - | |||
955 | 11732 | 7-9 | if ((error = iterator_init_common(&iter->base, | |
956 | 11732 | 10,11 | git_tree_owner(tree), NULL, options)) < 0 || | |
957 | 11732 | 10,12,13 | (error = git_tree_dup(&iter->root, tree)) < 0 || | |
958 | - | (error = tree_iterator_init(iter)) < 0) | ||
959 | - | goto on_error; | ||
960 | - | |||
961 | 11732 | 14 | *out = &iter->base; | |
962 | 11732 | 14 | return 0; | |
963 | - | |||
964 | - | on_error: | ||
965 | ##### | 15 | git_iterator_free(&iter->base); | |
966 | ##### | 16 | return error; | |
967 | - | } | ||
968 | - | |||
969 | 4 | 2 | int git_iterator_current_tree_entry( | |
970 | - | const git_tree_entry **tree_entry, git_iterator *i) | ||
971 | - | { | ||
972 | - | tree_iterator *iter; | ||
973 | - | tree_iterator_frame *frame; | ||
974 | - | tree_iterator_entry *entry; | ||
975 | - | |||
976 | 4 | 2,3 | assert(i->type == GIT_ITERATOR_TREE); | |
977 | - | |||
978 | 4 | 4 | iter = (tree_iterator *)i; | |
979 | - | |||
980 | 4 | 4 | frame = tree_iterator_current_frame(iter); | |
981 | 4 | 5 | entry = tree_iterator_current_entry(frame); | |
982 | - | |||
983 | 4 | 6 | *tree_entry = entry->tree_entry; | |
984 | 4 | 6 | return 0; | |
985 | - | } | ||
986 | - | |||
987 | 9 | 2 | int git_iterator_current_parent_tree( | |
988 | - | const git_tree **parent_tree, git_iterator *i, size_t depth) | ||
989 | - | { | ||
990 | - | tree_iterator *iter; | ||
991 | - | tree_iterator_frame *frame; | ||
992 | - | |||
993 | 9 | 2,3 | assert(i->type == GIT_ITERATOR_TREE); | |
994 | - | |||
995 | 9 | 4 | iter = (tree_iterator *)i; | |
996 | - | |||
997 | 9 | 4,5 | assert(depth < iter->frames.size); | |
998 | 9 | 6 | frame = &iter->frames.ptr[iter->frames.size-depth-1]; | |
999 | - | |||
1000 | 9 | 6 | *parent_tree = frame->tree; | |
1001 | 9 | 6 | return 0; | |
1002 | - | } | ||
1003 | - | |||
1004 | - | /* Filesystem iterator */ | ||
1005 | - | |||
1006 | - | typedef struct { | ||
1007 | - | struct stat st; | ||
1008 | - | size_t path_len; | ||
1009 | - | iterator_pathlist_search_t match; | ||
1010 | - | git_oid id; | ||
1011 | - | char path[GIT_FLEX_ARRAY]; | ||
1012 | - | } filesystem_iterator_entry; | ||
1013 | - | |||
1014 | - | typedef struct { | ||
1015 | - | git_vector entries; | ||
1016 | - | git_pool entry_pool; | ||
1017 | - | size_t next_idx; | ||
1018 | - | |||
1019 | - | size_t path_len; | ||
1020 | - | int is_ignored; | ||
1021 | - | } filesystem_iterator_frame; | ||
1022 | - | |||
1023 | - | typedef struct { | ||
1024 | - | git_iterator base; | ||
1025 | - | char *root; | ||
1026 | - | size_t root_len; | ||
1027 | - | |||
1028 | - | unsigned int dirload_flags; | ||
1029 | - | |||
1030 | - | git_tree *tree; | ||
1031 | - | git_index *index; | ||
1032 | - | git_vector index_snapshot; | ||
1033 | - | |||
1034 | - | git_array_t(filesystem_iterator_frame) frames; | ||
1035 | - | git_ignores ignores; | ||
1036 | - | |||
1037 | - | /* info about the current entry */ | ||
1038 | - | git_index_entry entry; | ||
1039 | - | git_buf current_path; | ||
1040 | - | int current_is_ignored; | ||
1041 | - | |||
1042 | - | /* temporary buffer for advance_over */ | ||
1043 | - | git_buf tmp_buf; | ||
1044 | - | } filesystem_iterator; | ||
1045 | - | |||
1046 | - | |||
1047 | 1570 | 2 | GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame( | |
1048 | - | filesystem_iterator *iter) | ||
1049 | - | { | ||
1050 | 1570 | 2 | return iter->frames.size > 1 ? | |
1051 | 1570 | 2-4 | &iter->frames.ptr[iter->frames.size-2] : NULL; | |
1052 | - | } | ||
1053 | - | |||
1054 | 34029 | 2 | GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame( | |
1055 | - | filesystem_iterator *iter) | ||
1056 | - | { | ||
1057 | 34029 | 2 | return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL; | |
1058 | - | } | ||
1059 | - | |||
1060 | 1277 | 2 | GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry( | |
1061 | - | filesystem_iterator_frame *frame) | ||
1062 | - | { | ||
1063 | 1277 | 2 | return frame->next_idx == 0 ? | |
1064 | 1277 | 2-4 | NULL : frame->entries.contents[frame->next_idx-1]; | |
1065 | - | } | ||
1066 | - | |||
1067 | 51986 | 2 | static int filesystem_iterator_entry_cmp(const void *_a, const void *_b) | |
1068 | - | { | ||
1069 | 51986 | 2 | const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; | |
1070 | 51986 | 2 | const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; | |
1071 | - | |||
1072 | 51986 | 2 | return git__strcmp(a->path, b->path); | |
1073 | - | } | ||
1074 | - | |||
1075 | 810 | 2 | static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b) | |
1076 | - | { | ||
1077 | 810 | 2 | const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; | |
1078 | 810 | 2 | const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; | |
1079 | - | |||
1080 | 810 | 2 | return git__strcasecmp(a->path, b->path); | |
1081 | - | } | ||
1082 | - | |||
1083 | - | #define FILESYSTEM_MAX_DEPTH 100 | ||
1084 | - | |||
1085 | - | /** | ||
1086 | - | * Figure out if an entry is a submodule. | ||
1087 | - | * | ||
1088 | - | * We consider it a submodule if the path is listed as a submodule in | ||
1089 | - | * either the tree or the index. | ||
1090 | - | */ | ||
1091 | 3657 | 2 | static int filesystem_iterator_is_submodule( | |
1092 | - | bool *out, filesystem_iterator *iter, const char *path, size_t path_len) | ||
1093 | - | { | ||
1094 | 3657 | 2 | bool is_submodule = false; | |
1095 | - | int error; | ||
1096 | - | |||
1097 | 3657 | 2 | *out = false; | |
1098 | - | |||
1099 | - | /* first see if this path is a submodule in HEAD */ | ||
1100 | 3657 | 2 | if (iter->tree) { | |
1101 | - | git_tree_entry *entry; | ||
1102 | - | |||
1103 | 37 | 3 | error = git_tree_entry_bypath(&entry, iter->tree, path); | |
1104 | - | |||
1105 | 37 | 4,5 | if (error < 0 && error != GIT_ENOTFOUND) | |
1106 | ##### | 6 | return error; | |
1107 | - | |||
1108 | 37 | 7 | if (!error) { | |
1109 | 21 | 8 | is_submodule = (entry->attr == GIT_FILEMODE_COMMIT); | |
1110 | 37 | 8,9 | git_tree_entry_free(entry); | |
1111 | - | } | ||
1112 | - | } | ||
1113 | - | |||
1114 | 3657 | 10,11 | if (!is_submodule && iter->base.index) { | |
1115 | - | size_t pos; | ||
1116 | - | |||
1117 | 759 | 12 | error = git_index_snapshot_find(&pos, | |
1118 | - | &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0); | ||
1119 | - | |||
1120 | 759 | 13,14 | if (error < 0 && error != GIT_ENOTFOUND) | |
1121 | ##### | 15 | return error; | |
1122 | - | |||
1123 | 759 | 16 | if (!error) { | |
1124 | 206 | 17 | git_index_entry *e = git_vector_get(&iter->index_snapshot, pos); | |
1125 | 759 | 18,19 | is_submodule = (e->mode == GIT_FILEMODE_COMMIT); | |
1126 | - | } | ||
1127 | - | } | ||
1128 | - | |||
1129 | 3657 | 20 | *out = is_submodule; | |
1130 | 3657 | 20 | return 0; | |
1131 | - | } | ||
1132 | - | |||
1133 | 6469 | 2 | static void filesystem_iterator_frame_push_ignores( | |
1134 | - | filesystem_iterator *iter, | ||
1135 | - | filesystem_iterator_entry *frame_entry, | ||
1136 | - | filesystem_iterator_frame *new_frame) | ||
1137 | - | { | ||
1138 | - | filesystem_iterator_frame *previous_frame; | ||
1139 | 6469 | 2-4 | const char *path = frame_entry ? frame_entry->path : ""; | |
1140 | - | |||
1141 | 6469 | 5 | if (!iterator__honor_ignores(&iter->base)) | |
1142 | 6469 | 6,16 | return; | |
1143 | - | |||
1144 | 4146 | 7,8 | if (git_ignore__lookup(&new_frame->is_ignored, | |
1145 | - | &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) { | ||
1146 | ##### | 9 | git_error_clear(); | |
1147 | ##### | 10 | new_frame->is_ignored = GIT_IGNORE_NOTFOUND; | |
1148 | - | } | ||
1149 | - | |||
1150 | - | /* if this is not the top level directory... */ | ||
1151 | 4145 | 11 | if (frame_entry) { | |
1152 | - | const char *relative_path; | ||
1153 | - | |||
1154 | 1570 | 12 | previous_frame = filesystem_iterator_parent_frame(iter); | |
1155 | - | |||
1156 | - | /* push new ignores for files in this directory */ | ||
1157 | 1570 | 13 | relative_path = frame_entry->path + previous_frame->path_len; | |
1158 | - | |||
1159 | - | /* inherit ignored from parent if no rule specified */ | ||
1160 | 1570 | 13 | if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND) | |
1161 | 1540 | 14 | new_frame->is_ignored = previous_frame->is_ignored; | |
1162 | - | |||
1163 | 1570 | 15 | git_ignore__push_dir(&iter->ignores, relative_path); | |
1164 | - | } | ||
1165 | - | } | ||
1166 | - | |||
1167 | 6470 | 2 | static void filesystem_iterator_frame_pop_ignores( | |
1168 | - | filesystem_iterator *iter) | ||
1169 | - | { | ||
1170 | 6470 | 2 | if (iterator__honor_ignores(&iter->base)) | |
1171 | 4146 | 3 | git_ignore__pop_dir(&iter->ignores); | |
1172 | 6470 | 4 | } | |
1173 | - | |||
1174 | 27664 | 2 | GIT_INLINE(bool) filesystem_iterator_examine_path( | |
1175 | - | bool *is_dir_out, | ||
1176 | - | iterator_pathlist_search_t *match_out, | ||
1177 | - | filesystem_iterator *iter, | ||
1178 | - | filesystem_iterator_entry *frame_entry, | ||
1179 | - | const char *path, | ||
1180 | - | size_t path_len) | ||
1181 | - | { | ||
1182 | 27664 | 2 | bool is_dir = 0; | |
1183 | 27664 | 2 | iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL; | |
1184 | - | |||
1185 | 27664 | 2 | *is_dir_out = false; | |
1186 | 27664 | 2 | *match_out = ITERATOR_PATHLIST_NONE; | |
1187 | - | |||
1188 | 27664 | 2 | if (iter->base.start_len) { | |
1189 | 1738 | 3 | int cmp = iter->base.strncomp(path, iter->base.start, path_len); | |
1190 | - | |||
1191 | - | /* we haven't stat'ed `path` yet, so we don't yet know if it's a | ||
1192 | - | * directory or not. special case if the current path may be a | ||
1193 | - | * directory that matches the start prefix. | ||
1194 | - | */ | ||
1195 | 1738 | 4 | if (cmp == 0) { | |
1196 | 115 | 5 | if (iter->base.start[path_len] == '/') | |
1197 | 12 | 6 | is_dir = true; | |
1198 | - | |||
1199 | 103 | 7 | else if (iter->base.start[path_len] != '\0') | |
1200 | 4 | 8 | cmp = -1; | |
1201 | - | } | ||
1202 | - | |||
1203 | 1738 | 9 | if (cmp < 0) | |
1204 | 870 | 10 | return false; | |
1205 | - | } | ||
1206 | - | |||
1207 | 26794 | 11 | if (iter->base.end_len) { | |
1208 | 886 | 12 | int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len); | |
1209 | - | |||
1210 | 886 | 13 | if (cmp > 0) | |
1211 | 364 | 14 | return false; | |
1212 | - | } | ||
1213 | - | |||
1214 | - | /* if we have a pathlist that we're limiting to, examine this path now | ||
1215 | - | * to avoid a `stat` if we're not interested in the path. | ||
1216 | - | */ | ||
1217 | 26430 | 15 | if (iter->base.pathlist.length) { | |
1218 | - | /* if our parent was explicitly included, so too are we */ | ||
1219 | 5484 | 16,17 | if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT) | |
1220 | 185 | 18 | match = ITERATOR_PATHLIST_FULL; | |
1221 | - | else | ||
1222 | 5299 | 19 | match = iterator_pathlist_search(&iter->base, path, path_len); | |
1223 | - | |||
1224 | 5484 | 20 | if (match == ITERATOR_PATHLIST_NONE) | |
1225 | 4124 | 21 | return false; | |
1226 | - | |||
1227 | - | /* Ensure that the pathlist entry lines up with what we expected */ | ||
1228 | 1360 | 22,23 | if (match == ITERATOR_PATHLIST_IS_DIR || | |
1229 | - | match == ITERATOR_PATHLIST_IS_PARENT) | ||
1230 | 111 | 24 | is_dir = true; | |
1231 | - | } | ||
1232 | - | |||
1233 | 22306 | 25 | *is_dir_out = is_dir; | |
1234 | 22306 | 25 | *match_out = match; | |
1235 | 22306 | 25 | return true; | |
1236 | - | } | ||
1237 | - | |||
1238 | 22297 | 2 | GIT_INLINE(bool) filesystem_iterator_is_dot_git( | |
1239 | - | filesystem_iterator *iter, const char *path, size_t path_len) | ||
1240 | - | { | ||
1241 | - | size_t len; | ||
1242 | - | |||
1243 | 22297 | 2 | if (!iterator__ignore_dot_git(&iter->base)) | |
1244 | 6780 | 3 | return false; | |
1245 | - | |||
1246 | 15517 | 4 | if ((len = path_len) < 4) | |
1247 | 1790 | 5 | return false; | |
1248 | - | |||
1249 | 13727 | 6 | if (path[len - 1] == '/') | |
1250 | ##### | 7 | len--; | |
1251 | - | |||
1252 | 13727 | 8,9 | if (git__tolower(path[len - 1]) != 't' || | |
1253 | 6942 | 9,10 | git__tolower(path[len - 2]) != 'i' || | |
1254 | 1785 | 10,11 | git__tolower(path[len - 3]) != 'g' || | |
1255 | 1785 | 11 | git__tolower(path[len - 4]) != '.') | |
1256 | 11942 | 12 | return false; | |
1257 | - | |||
1258 | 1785 | 13 | return (len == 4 || path[len - 5] == '/'); | |
1259 | - | } | ||
1260 | - | |||
1261 | 282 | 2 | static int filesystem_iterator_entry_hash( | |
1262 | - | filesystem_iterator *iter, | ||
1263 | - | filesystem_iterator_entry *entry) | ||
1264 | - | { | ||
1265 | 282 | 2 | git_buf fullpath = GIT_BUF_INIT; | |
1266 | - | int error; | ||
1267 | - | |||
1268 | 282 | 2 | if (S_ISDIR(entry->st.st_mode)) { | |
1269 | ##### | 3 | memset(&entry->id, 0, GIT_OID_RAWSZ); | |
1270 | ##### | 3 | return 0; | |
1271 | - | } | ||
1272 | - | |||
1273 | 282 | 4 | if (iter->base.type == GIT_ITERATOR_WORKDIR) | |
1274 | 282 | 5 | return git_repository_hashfile(&entry->id, | |
1275 | 282 | 5 | iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL); | |
1276 | - | |||
1277 | ##### | 6,7 | if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path))) | |
1278 | ##### | 8 | error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB); | |
1279 | - | |||
1280 | ##### | 9 | git_buf_dispose(&fullpath); | |
1281 | ##### | 10 | return error; | |
1282 | - | } | ||
1283 | - | |||
1284 | 20506 | 2 | static int filesystem_iterator_entry_init( | |
1285 | - | filesystem_iterator_entry **out, | ||
1286 | - | filesystem_iterator *iter, | ||
1287 | - | filesystem_iterator_frame *frame, | ||
1288 | - | const char *path, | ||
1289 | - | size_t path_len, | ||
1290 | - | struct stat *statbuf, | ||
1291 | - | iterator_pathlist_search_t pathlist_match) | ||
1292 | - | { | ||
1293 | - | filesystem_iterator_entry *entry; | ||
1294 | - | size_t entry_size; | ||
1295 | 20506 | 2 | int error = 0; | |
1296 | - | |||
1297 | 20506 | 2 | *out = NULL; | |
1298 | - | |||
1299 | - | /* Make sure to append two bytes, one for the path's null | ||
1300 | - | * termination, one for a possible trailing '/' for folders. | ||
1301 | - | */ | ||
1302 | 20509 | 2-8 | GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, | |
1303 | - | sizeof(filesystem_iterator_entry), path_len); | ||
1304 | 20509 | 9-15 | GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, entry_size, 2); | |
1305 | - | |||
1306 | 20509 | 16 | entry = git_pool_malloc(&frame->entry_pool, entry_size); | |
1307 | 20501 | 17,18 | GIT_ERROR_CHECK_ALLOC(entry); | |
1308 | - | |||
1309 | 20501 | 19 | entry->path_len = path_len; | |
1310 | 20501 | 19 | entry->match = pathlist_match; | |
1311 | 20501 | 19 | memcpy(entry->path, path, path_len); | |
1312 | 20501 | 19 | memcpy(&entry->st, statbuf, sizeof(struct stat)); | |
1313 | - | |||
1314 | - | /* Suffix directory paths with a '/' */ | ||
1315 | 20501 | 19 | if (S_ISDIR(entry->st.st_mode)) | |
1316 | 3463 | 20 | entry->path[entry->path_len++] = '/'; | |
1317 | - | |||
1318 | 20506 | 21 | entry->path[entry->path_len] = '\0'; | |
1319 | - | |||
1320 | 20506 | 21 | if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH) | |
1321 | 282 | 22 | error = filesystem_iterator_entry_hash(iter, entry); | |
1322 | - | |||
1323 | 20506 | 23 | if (!error) | |
1324 | 20502 | 24 | *out = entry; | |
1325 | - | |||
1326 | 20506 | 25 | return error; | |
1327 | - | } | ||
1328 | - | |||
1329 | 6474 | 2 | static int filesystem_iterator_frame_push( | |
1330 | - | filesystem_iterator *iter, | ||
1331 | - | filesystem_iterator_entry *frame_entry) | ||
1332 | - | { | ||
1333 | 6474 | 2 | filesystem_iterator_frame *new_frame = NULL; | |
1334 | 6474 | 2 | git_path_diriter diriter = GIT_PATH_DIRITER_INIT; | |
1335 | 6474 | 2 | git_buf root = GIT_BUF_INIT; | |
1336 | - | const char *path; | ||
1337 | - | filesystem_iterator_entry *entry; | ||
1338 | - | struct stat statbuf; | ||
1339 | - | size_t path_len; | ||
1340 | - | int error; | ||
1341 | - | |||
1342 | 6474 | 2 | if (iter->frames.size == FILESYSTEM_MAX_DEPTH) { | |
1343 | ##### | 3 | git_error_set(GIT_ERROR_REPOSITORY, | |
1344 | - | "directory nesting too deep (%"PRIuZ")", iter->frames.size); | ||
1345 | ##### | 4 | return -1; | |
1346 | - | } | ||
1347 | - | |||
1348 | 6474 | 5-10 | new_frame = git_array_alloc(iter->frames); | |
1349 | 6474 | 11,12 | GIT_ERROR_CHECK_ALLOC(new_frame); | |
1350 | - | |||
1351 | 6474 | 13 | memset(new_frame, 0, sizeof(filesystem_iterator_frame)); | |
1352 | - | |||
1353 | 6474 | 13 | if (frame_entry) | |
1354 | 3396 | 14 | git_buf_joinpath(&root, iter->root, frame_entry->path); | |
1355 | - | else | ||
1356 | 3078 | 15 | git_buf_puts(&root, iter->root); | |
1357 | - | |||
1358 | 6474 | 16,17 | if (git_buf_oom(&root)) { | |
1359 | ##### | 18 | error = -1; | |
1360 | ##### | 18 | goto done; | |
1361 | - | } | ||
1362 | - | |||
1363 | 6474 | 19-21 | new_frame->path_len = frame_entry ? frame_entry->path_len : 0; | |
1364 | - | |||
1365 | - | /* Any error here is equivalent to the dir not existing, skip over it */ | ||
1366 | 6474 | 22,22,23 | if ((error = git_path_diriter_init( | |
1367 | 6474 | 22 | &diriter, root.ptr, iter->dirload_flags)) < 0) { | |
1368 | 4 | 24 | error = GIT_ENOTFOUND; | |
1369 | 4 | 24 | goto done; | |
1370 | - | } | ||
1371 | - | |||
1372 | 6470 | 25-29 | if ((error = git_vector_init(&new_frame->entries, 64, | |
1373 | 6468 | 25 | iterator__ignore_case(&iter->base) ? | |
1374 | - | filesystem_iterator_entry_cmp_icase : | ||
1375 | - | filesystem_iterator_entry_cmp)) < 0) | ||
1376 | ##### | 30 | goto done; | |
1377 | - | |||
1378 | 6470 | 31,32 | if ((error = git_pool_init(&new_frame->entry_pool, 1)) < 0) | |
1379 | ##### | 33 | goto done; | |
1380 | - | |||
1381 | - | /* check if this directory is ignored */ | ||
1382 | 6469 | 34 | filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame); | |
1383 | - | |||
1384 | 34123 | 35,73,74 | while ((error = git_path_diriter_next(&diriter)) == 0) { | |
1385 | 27654 | 36 | iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL; | |
1386 | 27654 | 36 | bool dir_expected = false; | |
1387 | - | |||
1388 | 27656 | 36,37 | if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0) | |
1389 | ##### | 38,71 | goto done; | |
1390 | - | |||
1391 | 27656 | 39,40 | assert(path_len > iter->root_len); | |
1392 | - | |||
1393 | - | /* remove the prefix if requested */ | ||
1394 | 27656 | 41 | path += iter->root_len; | |
1395 | 27656 | 41 | path_len -= iter->root_len; | |
1396 | - | |||
1397 | - | /* examine start / end and the pathlist to see if this path is in it. | ||
1398 | - | * note that since we haven't yet stat'ed the path, we cannot know | ||
1399 | - | * whether it's a directory yet or not, so this can give us an | ||
1400 | - | * expected type (S_IFDIR or S_IFREG) that we should examine) | ||
1401 | - | */ | ||
1402 | 27664 | 41,42 | if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match, | |
1403 | - | iter, frame_entry, path, path_len)) | ||
1404 | 7150 | 43,72 | continue; | |
1405 | - | |||
1406 | - | /* TODO: don't need to stat if assume unchanged for this path and | ||
1407 | - | * we have an index, we can just copy the data out of it. | ||
1408 | - | */ | ||
1409 | - | |||
1410 | 22306 | 44,45 | if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) { | |
1411 | - | /* file was removed between readdir and lstat */ | ||
1412 | 5 | 46 | if (error == GIT_ENOTFOUND) | |
1413 | 2 | 47 | continue; | |
1414 | - | |||
1415 | - | /* treat the file as unreadable */ | ||
1416 | 3 | 48 | memset(&statbuf, 0, sizeof(statbuf)); | |
1417 | 3 | 48 | statbuf.st_mode = GIT_FILEMODE_UNREADABLE; | |
1418 | - | |||
1419 | 3 | 48 | error = 0; | |
1420 | - | } | ||
1421 | - | |||
1422 | 22298 | 49 | iter->base.stat_calls++; | |
1423 | - | |||
1424 | - | /* Ignore wacky things in the filesystem */ | ||
1425 | 22298 | 49,50 | if (!S_ISDIR(statbuf.st_mode) && | |
1426 | 17013 | 50,51 | !S_ISREG(statbuf.st_mode) && | |
1427 | 46 | 51,52 | !S_ISLNK(statbuf.st_mode) && | |
1428 | 4 | 52 | statbuf.st_mode != GIT_FILEMODE_UNREADABLE) | |
1429 | 1 | 53 | continue; | |
1430 | - | |||
1431 | 22299 | 54,55 | if (filesystem_iterator_is_dot_git(iter, path, path_len)) | |
1432 | 1785 | 56 | continue; | |
1433 | - | |||
1434 | - | /* convert submodules to GITLINK and remove trailing slashes */ | ||
1435 | 20514 | 57 | if (S_ISDIR(statbuf.st_mode)) { | |
1436 | 3657 | 58 | bool submodule = false; | |
1437 | - | |||
1438 | 3657 | 58,59 | if ((error = filesystem_iterator_is_submodule(&submodule, | |
1439 | - | iter, path, path_len)) < 0) | ||
1440 | ##### | 60 | goto done; | |
1441 | - | |||
1442 | 3657 | 61 | if (submodule) | |
1443 | 3657 | 62,63 | statbuf.st_mode = GIT_FILEMODE_COMMIT; | |
1444 | - | } | ||
1445 | - | |||
1446 | - | /* Ensure that the pathlist entry lines up with what we expected */ | ||
1447 | 16857 | 64 | else if (dir_expected) | |
1448 | 4 | 65 | continue; | |
1449 | - | |||
1450 | 20510 | 66,67 | if ((error = filesystem_iterator_entry_init(&entry, | |
1451 | - | iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0) | ||
1452 | ##### | 68 | goto done; | |
1453 | - | |||
1454 | 20504 | 69,70 | git_vector_insert(&new_frame->entries, entry); | |
1455 | - | } | ||
1456 | - | |||
1457 | 6469 | 75 | if (error == GIT_ITEROVER) | |
1458 | 6469 | 76 | error = 0; | |
1459 | - | |||
1460 | - | /* sort now that directory suffix is added */ | ||
1461 | 6469 | 77 | git_vector_sort(&new_frame->entries); | |
1462 | - | |||
1463 | - | done: | ||
1464 | 6474 | 78 | if (error < 0) | |
1465 | 4 | 79-81 | git_array_pop(iter->frames); | |
1466 | - | |||
1467 | 6474 | 82 | git_buf_dispose(&root); | |
1468 | 6473 | 83 | git_path_diriter_free(&diriter); | |
1469 | 6474 | 84 | return error; | |
1470 | - | } | ||
1471 | - | |||
1472 | 6470 | 2 | GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter) | |
1473 | - | { | ||
1474 | - | filesystem_iterator_frame *frame; | ||
1475 | - | |||
1476 | 6470 | 2,3 | assert(iter->frames.size); | |
1477 | - | |||
1478 | 6470 | 4-6 | frame = git_array_pop(iter->frames); | |
1479 | 6470 | 7 | filesystem_iterator_frame_pop_ignores(iter); | |
1480 | - | |||
1481 | 6470 | 8 | git_pool_clear(&frame->entry_pool); | |
1482 | 6470 | 9 | git_vector_free(&frame->entries); | |
1483 | 6470 | 10 | } | |
1484 | - | |||
1485 | 18033 | 2 | static void filesystem_iterator_set_current( | |
1486 | - | filesystem_iterator *iter, | ||
1487 | - | filesystem_iterator_entry *entry) | ||
1488 | - | { | ||
1489 | - | /* | ||
1490 | - | * Index entries are limited to 32 bit timestamps. We can safely | ||
1491 | - | * cast this since workdir times are only used in the cache; any | ||
1492 | - | * mismatch will cause a hash recomputation which is unfortunate | ||
1493 | - | * but affects only people who set their filetimes to 2038. | ||
1494 | - | * (Same with the file size.) | ||
1495 | - | */ | ||
1496 | 18033 | 2 | iter->entry.ctime.seconds = (int32_t)entry->st.st_ctime; | |
1497 | 18033 | 2 | iter->entry.mtime.seconds = (int32_t)entry->st.st_mtime; | |
1498 | - | |||
1499 | - | #if defined(GIT_USE_NSEC) | ||
1500 | 18033 | 2 | iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec; | |
1501 | 18033 | 2 | iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec; | |
1502 | - | #else | ||
1503 | - | iter->entry.ctime.nanoseconds = 0; | ||
1504 | - | iter->entry.mtime.nanoseconds = 0; | ||
1505 | - | #endif | ||
1506 | - | |||
1507 | 18033 | 2 | iter->entry.dev = entry->st.st_dev; | |
1508 | 18033 | 2 | iter->entry.ino = entry->st.st_ino; | |
1509 | 18033 | 2 | iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode); | |
1510 | 18036 | 3 | iter->entry.uid = entry->st.st_uid; | |
1511 | 18036 | 3 | iter->entry.gid = entry->st.st_gid; | |
1512 | 18036 | 3 | iter->entry.file_size = (uint32_t)entry->st.st_size; | |
1513 | - | |||
1514 | 18036 | 3 | if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH) | |
1515 | 282 | 4 | git_oid_cpy(&iter->entry.id, &entry->id); | |
1516 | - | |||
1517 | 18036 | 5 | iter->entry.path = entry->path; | |
1518 | - | |||
1519 | 18036 | 5 | iter->current_is_ignored = GIT_IGNORE_UNCHECKED; | |
1520 | 18036 | 5 | } | |
1521 | - | |||
1522 | 3028 | 2 | static int filesystem_iterator_current( | |
1523 | - | const git_index_entry **out, git_iterator *i) | ||
1524 | - | { | ||
1525 | 3028 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1526 | - | |||
1527 | 3028 | 2 | if (!iterator__has_been_accessed(i)) | |
1528 | 2487 | 3 | return iter->base.cb->advance(out, i); | |
1529 | - | |||
1530 | 541 | 4 | if (!iter->frames.size) { | |
1531 | ##### | 5 | *out = NULL; | |
1532 | ##### | 5 | return GIT_ITEROVER; | |
1533 | - | } | ||
1534 | - | |||
1535 | 541 | 6 | *out = &iter->entry; | |
1536 | 541 | 6 | return 0; | |
1537 | - | } | ||
1538 | - | |||
1539 | 20322 | 2 | static int filesystem_iterator_is_dir( | |
1540 | - | bool *is_dir, | ||
1541 | - | const filesystem_iterator *iter, | ||
1542 | - | const filesystem_iterator_entry *entry) | ||
1543 | - | { | ||
1544 | - | struct stat st; | ||
1545 | 20322 | 2 | git_buf fullpath = GIT_BUF_INIT; | |
1546 | 20322 | 2 | int error = 0; | |
1547 | - | |||
1548 | 20322 | 2 | if (S_ISDIR(entry->st.st_mode)) { | |
1549 | 3447 | 3 | *is_dir = 1; | |
1550 | 3447 | 3 | goto done; | |
1551 | - | } | ||
1552 | - | |||
1553 | 16875 | 4,5 | if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) { | |
1554 | 16874 | 6 | *is_dir = 0; | |
1555 | 16874 | 6 | goto done; | |
1556 | - | } | ||
1557 | - | |||
1558 | 1 | 7-10 | if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 || | |
1559 | 1 | 9 | (error = p_stat(fullpath.ptr, &st)) < 0) | |
1560 | - | goto done; | ||
1561 | - | |||
1562 | 1 | 11 | *is_dir = S_ISDIR(st.st_mode); | |
1563 | - | |||
1564 | - | done: | ||
1565 | 20322 | 12 | git_buf_dispose(&fullpath); | |
1566 | 20315 | 13 | return error; | |
1567 | - | } | ||
1568 | - | |||
1569 | 21195 | 2 | static int filesystem_iterator_advance( | |
1570 | - | const git_index_entry **out, git_iterator *i) | ||
1571 | - | { | ||
1572 | 21195 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1573 | - | bool is_dir; | ||
1574 | 21195 | 2 | int error = 0; | |
1575 | - | |||
1576 | 21195 | 2 | iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; | |
1577 | - | |||
1578 | - | /* examine filesystem entries until we find the next one to return */ | ||
1579 | - | while (true) { | ||
1580 | - | filesystem_iterator_frame *frame; | ||
1581 | - | filesystem_iterator_entry *entry; | ||
1582 | - | |||
1583 | 29931 | 3,4 | if ((frame = filesystem_iterator_current_frame(iter)) == NULL) { | |
1584 | 3158 | 5 | error = GIT_ITEROVER; | |
1585 | 3158 | 5 | break; | |
1586 | - | } | ||
1587 | - | |||
1588 | - | /* no more entries in this frame. pop the frame out */ | ||
1589 | 26771 | 6 | if (frame->next_idx == frame->entries.length) { | |
1590 | 6453 | 7 | filesystem_iterator_frame_pop(iter); | |
1591 | 6452 | 8 | continue; | |
1592 | - | } | ||
1593 | - | |||
1594 | - | /* we have more entries in the current frame, that's our next entry */ | ||
1595 | 20318 | 9 | entry = frame->entries.contents[frame->next_idx]; | |
1596 | 20318 | 9 | frame->next_idx++; | |
1597 | - | |||
1598 | 20318 | 9,10 | if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0) | |
1599 | ##### | 11 | break; | |
1600 | - | |||
1601 | 20315 | 12 | if (is_dir) { | |
1602 | 3448 | 13 | if (iterator__do_autoexpand(iter)) { | |
1603 | 2767 | 14 | error = filesystem_iterator_frame_push(iter, entry); | |
1604 | - | |||
1605 | - | /* may get GIT_ENOTFOUND due to races or permission problems | ||
1606 | - | * that we want to quietly swallow | ||
1607 | - | */ | ||
1608 | 2767 | 15 | if (error == GIT_ENOTFOUND) | |
1609 | 1 | 16 | continue; | |
1610 | 2766 | 17 | else if (error < 0) | |
1611 | ##### | 18 | break; | |
1612 | - | } | ||
1613 | - | |||
1614 | 3447 | 19 | if (!iterator__include_trees(iter)) | |
1615 | 2283 | 20 | continue; | |
1616 | - | } | ||
1617 | - | |||
1618 | 18031 | 21 | filesystem_iterator_set_current(iter, entry); | |
1619 | 18036 | 23 | break; | |
1620 | 8736 | 22 | } | |
1621 | - | |||
1622 | 21194 | 24 | if (out) | |
1623 | 21188 | 25-28 | *out = (error == 0) ? &iter->entry : NULL; | |
1624 | - | |||
1625 | 21194 | 29 | return error; | |
1626 | - | } | ||
1627 | - | |||
1628 | 869 | 2 | static int filesystem_iterator_advance_into( | |
1629 | - | const git_index_entry **out, git_iterator *i) | ||
1630 | - | { | ||
1631 | 869 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1632 | - | filesystem_iterator_frame *frame; | ||
1633 | - | filesystem_iterator_entry *prev_entry; | ||
1634 | - | int error; | ||
1635 | - | |||
1636 | 869 | 2 | if (out) | |
1637 | 869 | 3 | *out = NULL; | |
1638 | - | |||
1639 | 869 | 4,5 | if ((frame = filesystem_iterator_current_frame(iter)) == NULL) | |
1640 | ##### | 6 | return GIT_ITEROVER; | |
1641 | - | |||
1642 | - | /* get the last seen entry */ | ||
1643 | 869 | 7 | prev_entry = filesystem_iterator_current_entry(frame); | |
1644 | - | |||
1645 | - | /* it's legal to call advance_into when auto-expand is on. in this case, | ||
1646 | - | * we will have pushed a new (empty) frame on to the stack for this | ||
1647 | - | * new directory. since it's empty, its current_entry should be null. | ||
1648 | - | */ | ||
1649 | 869 | 8,9 | assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL)); | |
1650 | - | |||
1651 | 869 | 10 | if (prev_entry) { | |
1652 | 629 | 11,12 | if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT && | |
1653 | 629 | 12 | !S_ISDIR(prev_entry->st.st_mode)) | |
1654 | ##### | 13 | return 0; | |
1655 | - | |||
1656 | 629 | 14,15 | if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0) | |
1657 | 2 | 16 | return error; | |
1658 | - | } | ||
1659 | - | |||
1660 | - | /* we've advanced into the directory in question, let advance | ||
1661 | - | * find the first entry | ||
1662 | - | */ | ||
1663 | 867 | 17 | return filesystem_iterator_advance(out, i); | |
1664 | - | } | ||
1665 | - | |||
1666 | 92 | 2 | int git_iterator_current_workdir_path(git_buf **out, git_iterator *i) | |
1667 | - | { | ||
1668 | 92 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1669 | - | const git_index_entry *entry; | ||
1670 | - | |||
1671 | 92 | 2,3 | if (i->type != GIT_ITERATOR_FS && | |
1672 | 92 | 3 | i->type != GIT_ITERATOR_WORKDIR) { | |
1673 | ##### | 4 | *out = NULL; | |
1674 | ##### | 4 | return 0; | |
1675 | - | } | ||
1676 | - | |||
1677 | 92 | 5 | git_buf_truncate(&iter->current_path, iter->root_len); | |
1678 | - | |||
1679 | 92 | 6,7,9 | if (git_iterator_current(&entry, i) < 0 || | |
1680 | 92 | 8 | git_buf_puts(&iter->current_path, entry->path) < 0) | |
1681 | ##### | 10 | return -1; | |
1682 | - | |||
1683 | 92 | 11 | *out = &iter->current_path; | |
1684 | 92 | 11 | return 0; | |
1685 | - | } | ||
1686 | - | |||
1687 | 3009 | 2 | GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry) | |
1688 | - | { | ||
1689 | - | #if defined(GIT_WIN32) && !defined(__MINGW32__) | ||
1690 | - | return (entry && entry->mode) ? | ||
1691 | - | (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) : | ||
1692 | - | GIT_DIR_FLAG_UNKNOWN; | ||
1693 | - | #else | ||
1694 | - | GIT_UNUSED(entry); | ||
1695 | 3009 | 2 | return GIT_DIR_FLAG_UNKNOWN; | |
1696 | - | #endif | ||
1697 | - | } | ||
1698 | - | |||
1699 | 3009 | 2 | static void filesystem_iterator_update_ignored(filesystem_iterator *iter) | |
1700 | - | { | ||
1701 | - | filesystem_iterator_frame *frame; | ||
1702 | 3009 | 2 | git_dir_flag dir_flag = entry_dir_flag(&iter->entry); | |
1703 | - | |||
1704 | 3015 | 3,4 | if (git_ignore__lookup(&iter->current_is_ignored, | |
1705 | - | &iter->ignores, iter->entry.path, dir_flag) < 0) { | ||
1706 | ##### | 5 | git_error_clear(); | |
1707 | ##### | 6 | iter->current_is_ignored = GIT_IGNORE_NOTFOUND; | |
1708 | - | } | ||
1709 | - | |||
1710 | - | /* use ignore from containing frame stack */ | ||
1711 | 3015 | 7 | if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) { | |
1712 | 2533 | 8 | frame = filesystem_iterator_current_frame(iter); | |
1713 | 2533 | 9 | iter->current_is_ignored = frame->is_ignored; | |
1714 | - | } | ||
1715 | 3015 | 10 | } | |
1716 | - | |||
1717 | 3063 | 2 | GIT_INLINE(bool) filesystem_iterator_current_is_ignored( | |
1718 | - | filesystem_iterator *iter) | ||
1719 | - | { | ||
1720 | 3063 | 2 | if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED) | |
1721 | 3009 | 3 | filesystem_iterator_update_ignored(iter); | |
1722 | - | |||
1723 | 3069 | 4 | return (iter->current_is_ignored == GIT_IGNORE_TRUE); | |
1724 | - | } | ||
1725 | - | |||
1726 | 7227 | 2 | bool git_iterator_current_is_ignored(git_iterator *i) | |
1727 | - | { | ||
1728 | 7227 | 2 | filesystem_iterator *iter = NULL; | |
1729 | - | |||
1730 | 7227 | 2 | if (i->type != GIT_ITERATOR_WORKDIR) | |
1731 | 4702 | 3 | return false; | |
1732 | - | |||
1733 | 2525 | 4 | iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1734 | - | |||
1735 | 2525 | 4 | return filesystem_iterator_current_is_ignored(iter); | |
1736 | - | } | ||
1737 | - | |||
1738 | 312 | 2 | bool git_iterator_current_tree_is_ignored(git_iterator *i) | |
1739 | - | { | ||
1740 | 312 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1741 | - | filesystem_iterator_frame *frame; | ||
1742 | - | |||
1743 | 312 | 2 | if (i->type != GIT_ITERATOR_WORKDIR) | |
1744 | ##### | 3 | return false; | |
1745 | - | |||
1746 | 312 | 4 | frame = filesystem_iterator_current_frame(iter); | |
1747 | 312 | 5 | return (frame->is_ignored == GIT_IGNORE_TRUE); | |
1748 | - | } | ||
1749 | - | |||
1750 | 408 | 2 | static int filesystem_iterator_advance_over( | |
1751 | - | const git_index_entry **out, | ||
1752 | - | git_iterator_status_t *status, | ||
1753 | - | git_iterator *i) | ||
1754 | - | { | ||
1755 | 408 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1756 | - | filesystem_iterator_frame *current_frame; | ||
1757 | - | filesystem_iterator_entry *current_entry; | ||
1758 | 408 | 2 | const git_index_entry *entry = NULL; | |
1759 | - | const char *base; | ||
1760 | 408 | 2 | int error = 0; | |
1761 | - | |||
1762 | 408 | 2 | *out = NULL; | |
1763 | 408 | 2 | *status = GIT_ITERATOR_STATUS_NORMAL; | |
1764 | - | |||
1765 | 408 | 2,3 | assert(iterator__has_been_accessed(i)); | |
1766 | - | |||
1767 | 408 | 4 | current_frame = filesystem_iterator_current_frame(iter); | |
1768 | 408 | 5,6 | assert(current_frame); | |
1769 | 408 | 7 | current_entry = filesystem_iterator_current_entry(current_frame); | |
1770 | 408 | 8,9 | assert(current_entry); | |
1771 | - | |||
1772 | 408 | 10,11 | if ((error = git_iterator_current(&entry, i)) < 0) | |
1773 | ##### | 12 | return error; | |
1774 | - | |||
1775 | 408 | 13 | if (!S_ISDIR(entry->mode)) { | |
1776 | 311 | 14,15 | if (filesystem_iterator_current_is_ignored(iter)) | |
1777 | 37 | 16 | *status = GIT_ITERATOR_STATUS_IGNORED; | |
1778 | - | |||
1779 | 311 | 17 | return filesystem_iterator_advance(out, i); | |
1780 | - | } | ||
1781 | - | |||
1782 | 97 | 18 | git_buf_clear(&iter->tmp_buf); | |
1783 | 97 | 19,20 | if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0) | |
1784 | ##### | 21 | return error; | |
1785 | - | |||
1786 | 97 | 22 | base = iter->tmp_buf.ptr; | |
1787 | - | |||
1788 | - | /* scan inside the directory looking for files. if we find nothing, | ||
1789 | - | * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to | ||
1790 | - | * IGNORED. if we find a real actual item, upgrade all the way to NORMAL | ||
1791 | - | * and then stop. | ||
1792 | - | * | ||
1793 | - | * however, if we're here looking for a pathlist item (but are not | ||
1794 | - | * actually in the pathlist ourselves) then start at FILTERED instead of | ||
1795 | - | * EMPTY. callers then know that this path was not something they asked | ||
1796 | - | * about. | ||
1797 | - | */ | ||
1798 | 97 | 22-24 | *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ? | |
1799 | - | GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY; | ||
1800 | - | |||
1801 | 254 | 25,40-42 | while (entry && !iter->base.prefixcomp(entry->path, base)) { | |
1802 | 228 | 26,27 | if (filesystem_iterator_current_is_ignored(iter)) { | |
1803 | - | /* if we found an explicitly ignored item, then update from | ||
1804 | - | * EMPTY to IGNORED | ||
1805 | - | */ | ||
1806 | 24 | 28 | *status = GIT_ITERATOR_STATUS_IGNORED; | |
1807 | 204 | 29 | } else if (S_ISDIR(entry->mode)) { | |
1808 | 139 | 30 | error = filesystem_iterator_advance_into(&entry, i); | |
1809 | - | |||
1810 | 139 | 31 | if (!error) | |
1811 | 133 | 32 | continue; | |
1812 | - | |||
1813 | - | /* this directory disappeared, ignore it */ | ||
1814 | 6 | 33 | else if (error == GIT_ENOTFOUND) | |
1815 | 1 | 34 | error = 0; | |
1816 | - | |||
1817 | - | /* a real error occurred */ | ||
1818 | - | else | ||
1819 | 5 | 35 | break; | |
1820 | - | } else { | ||
1821 | - | /* we found a non-ignored item, treat parent as untracked */ | ||
1822 | 65 | 36 | *status = GIT_ITERATOR_STATUS_NORMAL; | |
1823 | 65 | 36 | break; | |
1824 | - | } | ||
1825 | - | |||
1826 | 25 | 37,38 | if ((error = git_iterator_advance(&entry, i)) < 0) | |
1827 | 1 | 39 | break; | |
1828 | - | } | ||
1829 | - | |||
1830 | - | /* wrap up scan back to base directory */ | ||
1831 | 168 | 43,47-49 | while (entry && !iter->base.prefixcomp(entry->path, base)) { | |
1832 | 79 | 44,45 | if ((error = git_iterator_advance(&entry, i)) < 0) | |
1833 | 8 | 46 | break; | |
1834 | - | } | ||
1835 | - | |||
1836 | 97 | 50 | if (!error) | |
1837 | 83 | 51 | *out = entry; | |
1838 | - | |||
1839 | 97 | 52 | return error; | |
1840 | - | } | ||
1841 | - | |||
1842 | 3078 | 2 | static void filesystem_iterator_clear(filesystem_iterator *iter) | |
1843 | - | { | ||
1844 | 3095 | 2,4 | while (iter->frames.size) | |
1845 | 17 | 3 | filesystem_iterator_frame_pop(iter); | |
1846 | - | |||
1847 | 3078 | 5 | git_array_clear(iter->frames); | |
1848 | 3078 | 6 | git_ignore__free(&iter->ignores); | |
1849 | - | |||
1850 | 3078 | 7 | git_buf_dispose(&iter->tmp_buf); | |
1851 | - | |||
1852 | 3078 | 8 | iterator_clear(&iter->base); | |
1853 | 3078 | 9 | } | |
1854 | - | |||
1855 | 3078 | 2 | static int filesystem_iterator_init(filesystem_iterator *iter) | |
1856 | - | { | ||
1857 | - | int error; | ||
1858 | - | |||
1859 | 3078 | 2-4 | if (iterator__honor_ignores(&iter->base) && | |
1860 | 2575 | 3 | (error = git_ignore__for_path(iter->base.repo, | |
1861 | - | ".gitignore", &iter->ignores)) < 0) | ||
1862 | ##### | 5 | return error; | |
1863 | - | |||
1864 | 3078 | 6,7 | if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0) | |
1865 | 1 | 8 | return error; | |
1866 | - | |||
1867 | 3077 | 9 | iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; | |
1868 | - | |||
1869 | 3077 | 9 | return 0; | |
1870 | - | } | ||
1871 | - | |||
1872 | 73 | 2 | static int filesystem_iterator_reset(git_iterator *i) | |
1873 | - | { | ||
1874 | 73 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1875 | - | |||
1876 | 73 | 2 | filesystem_iterator_clear(iter); | |
1877 | 73 | 3 | return filesystem_iterator_init(iter); | |
1878 | - | } | ||
1879 | - | |||
1880 | 3005 | 2 | static void filesystem_iterator_free(git_iterator *i) | |
1881 | - | { | ||
1882 | 3005 | 2 | filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base); | |
1883 | 3005 | 2 | git__free(iter->root); | |
1884 | 3005 | 3 | git_buf_dispose(&iter->current_path); | |
1885 | 3005 | 4 | git_tree_free(iter->tree); | |
1886 | 3005 | 5 | if (iter->index) | |
1887 | 2415 | 6 | git_index_snapshot_release(&iter->index_snapshot, iter->index); | |
1888 | 3005 | 7 | filesystem_iterator_clear(iter); | |
1889 | 3005 | 8 | } | |
1890 | - | |||
1891 | 3005 | 2 | static int iterator_for_filesystem( | |
1892 | - | git_iterator **out, | ||
1893 | - | git_repository *repo, | ||
1894 | - | const char *root, | ||
1895 | - | git_index *index, | ||
1896 | - | git_tree *tree, | ||
1897 | - | git_iterator_t type, | ||
1898 | - | git_iterator_options *options) | ||
1899 | - | { | ||
1900 | - | filesystem_iterator *iter; | ||
1901 | - | size_t root_len; | ||
1902 | - | int error; | ||
1903 | - | |||
1904 | - | static git_iterator_callbacks callbacks = { | ||
1905 | - | filesystem_iterator_current, | ||
1906 | - | filesystem_iterator_advance, | ||
1907 | - | filesystem_iterator_advance_into, | ||
1908 | - | filesystem_iterator_advance_over, | ||
1909 | - | filesystem_iterator_reset, | ||
1910 | - | filesystem_iterator_free | ||
1911 | - | }; | ||
1912 | - | |||
1913 | 3005 | 2 | *out = NULL; | |
1914 | - | |||
1915 | 3005 | 2 | if (root == NULL) | |
1916 | ##### | 3 | return git_iterator_for_nothing(out, options); | |
1917 | - | |||
1918 | 3005 | 4 | iter = git__calloc(1, sizeof(filesystem_iterator)); | |
1919 | 3005 | 5,6 | GIT_ERROR_CHECK_ALLOC(iter); | |
1920 | - | |||
1921 | 3005 | 7 | iter->base.type = type; | |
1922 | 3005 | 7 | iter->base.cb = &callbacks; | |
1923 | - | |||
1924 | 3005 | 7 | root_len = strlen(root); | |
1925 | - | |||
1926 | 3005 | 7 | iter->root = git__malloc(root_len+2); | |
1927 | 3005 | 8,9 | GIT_ERROR_CHECK_ALLOC(iter->root); | |
1928 | - | |||
1929 | 3005 | 10 | memcpy(iter->root, root, root_len); | |
1930 | - | |||
1931 | 3005 | 10,11 | if (root_len == 0 || root[root_len-1] != '/') { | |
1932 | 12 | 12 | iter->root[root_len] = '/'; | |
1933 | 12 | 12 | root_len++; | |
1934 | - | } | ||
1935 | 3005 | 13 | iter->root[root_len] = '\0'; | |
1936 | 3005 | 13 | iter->root_len = root_len; | |
1937 | - | |||
1938 | 3005 | 13,14 | if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0) | |
1939 | ##### | 15 | goto on_error; | |
1940 | - | |||
1941 | 3005 | 16,17 | if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0) | |
1942 | ##### | 18 | goto on_error; | |
1943 | - | |||
1944 | 3005 | 19-21 | if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0) | |
1945 | ##### | 22 | goto on_error; | |
1946 | - | |||
1947 | 3005 | 23-25 | if (index && | |
1948 | 2415 | 24 | (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0) | |
1949 | ##### | 26 | goto on_error; | |
1950 | - | |||
1951 | 3005 | 27 | iter->index = index; | |
1952 | 3005 | 30 | iter->dirload_flags = | |
1953 | 3005 | 27,30 | (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) | | |
1954 | 3005 | 27 | (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ? | |
1955 | 3005 | 27-29 | GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0); | |
1956 | - | |||
1957 | 3005 | 30,31 | if ((error = filesystem_iterator_init(iter)) < 0) | |
1958 | 1 | 32 | goto on_error; | |
1959 | - | |||
1960 | 3004 | 33 | *out = &iter->base; | |
1961 | 3004 | 33 | return 0; | |
1962 | - | |||
1963 | - | on_error: | ||
1964 | 1 | 34 | git_iterator_free(&iter->base); | |
1965 | 1 | 35 | return error; | |
1966 | - | } | ||
1967 | - | |||
1968 | 496 | 2 | int git_iterator_for_filesystem( | |
1969 | - | git_iterator **out, | ||
1970 | - | const char *root, | ||
1971 | - | git_iterator_options *options) | ||
1972 | - | { | ||
1973 | 496 | 2 | return iterator_for_filesystem(out, | |
1974 | - | NULL, root, NULL, NULL, GIT_ITERATOR_FS, options); | ||
1975 | - | } | ||
1976 | - | |||
1977 | 2511 | 2 | int git_iterator_for_workdir_ext( | |
1978 | - | git_iterator **out, | ||
1979 | - | git_repository *repo, | ||
1980 | - | const char *repo_workdir, | ||
1981 | - | git_index *index, | ||
1982 | - | git_tree *tree, | ||
1983 | - | git_iterator_options *given_opts) | ||
1984 | - | { | ||
1985 | 2511 | 2 | git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT; | |
1986 | - | |||
1987 | 2511 | 2 | if (!repo_workdir) { | |
1988 | 1692 | 3,4 | if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) | |
1989 | 2 | 5 | return GIT_EBAREREPO; | |
1990 | - | |||
1991 | 1690 | 6 | repo_workdir = git_repository_workdir(repo); | |
1992 | - | } | ||
1993 | - | |||
1994 | - | /* upgrade to a workdir iterator, adding necessary internal flags */ | ||
1995 | 2509 | 7 | if (given_opts) | |
1996 | 2509 | 8 | memcpy(&options, given_opts, sizeof(git_iterator_options)); | |
1997 | - | |||
1998 | 2509 | 9 | options.flags |= GIT_ITERATOR_HONOR_IGNORES | | |
1999 | - | GIT_ITERATOR_IGNORE_DOT_GIT; | ||
2000 | - | |||
2001 | 2509 | 9 | return iterator_for_filesystem(out, | |
2002 | - | repo, repo_workdir, index, tree, GIT_ITERATOR_WORKDIR, &options); | ||
2003 | - | } | ||
2004 | - | |||
2005 | - | |||
2006 | - | /* Index iterator */ | ||
2007 | - | |||
2008 | - | |||
2009 | - | typedef struct { | ||
2010 | - | git_iterator base; | ||
2011 | - | git_vector entries; | ||
2012 | - | size_t next_idx; | ||
2013 | - | |||
2014 | - | /* the pseudotree entry */ | ||
2015 | - | git_index_entry tree_entry; | ||
2016 | - | git_buf tree_buf; | ||
2017 | - | bool skip_tree; | ||
2018 | - | |||
2019 | - | const git_index_entry *entry; | ||
2020 | - | } index_iterator; | ||
2021 | - | |||
2022 | 3729 | 2 | static int index_iterator_current( | |
2023 | - | const git_index_entry **out, git_iterator *i) | ||
2024 | - | { | ||
2025 | 3729 | 2 | index_iterator *iter = (index_iterator *)i; | |
2026 | - | |||
2027 | 3729 | 2 | if (!iterator__has_been_accessed(i)) | |
2028 | 3544 | 3 | return iter->base.cb->advance(out, i); | |
2029 | - | |||
2030 | 185 | 4 | if (iter->entry == NULL) { | |
2031 | ##### | 5 | *out = NULL; | |
2032 | ##### | 5 | return GIT_ITEROVER; | |
2033 | - | } | ||
2034 | - | |||
2035 | 185 | 6 | *out = iter->entry; | |
2036 | 185 | 6 | return 0; | |
2037 | - | } | ||
2038 | - | |||
2039 | 326 | 2 | static bool index_iterator_create_pseudotree( | |
2040 | - | const git_index_entry **out, | ||
2041 | - | index_iterator *iter, | ||
2042 | - | const char *path) | ||
2043 | - | { | ||
2044 | - | const char *prev_path, *relative_path, *dirsep; | ||
2045 | - | size_t common_len; | ||
2046 | - | |||
2047 | 326 | 2-4 | prev_path = iter->entry ? iter->entry->path : ""; | |
2048 | - | |||
2049 | - | /* determine if the new path is in a different directory from the old */ | ||
2050 | 326 | 5 | common_len = git_path_common_dirlen(prev_path, path); | |
2051 | 326 | 6 | relative_path = path + common_len; | |
2052 | - | |||
2053 | 326 | 6 | if ((dirsep = strchr(relative_path, '/')) == NULL) | |
2054 | 278 | 7 | return false; | |
2055 | - | |||
2056 | 48 | 8 | git_buf_clear(&iter->tree_buf); | |
2057 | 48 | 9 | git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1); | |
2058 | - | |||
2059 | 48 | 10 | iter->tree_entry.mode = GIT_FILEMODE_TREE; | |
2060 | 48 | 10 | iter->tree_entry.path = iter->tree_buf.ptr; | |
2061 | - | |||
2062 | 48 | 10 | *out = &iter->tree_entry; | |
2063 | 48 | 10 | return true; | |
2064 | - | } | ||
2065 | - | |||
2066 | 22 | 2 | static int index_iterator_skip_pseudotree(index_iterator *iter) | |
2067 | - | { | ||
2068 | 22 | 2,3 | assert(iterator__has_been_accessed(&iter->base)); | |
2069 | 22 | 4,5 | assert(S_ISDIR(iter->entry->mode)); | |
2070 | - | |||
2071 | - | while (true) { | ||
2072 | 224 | 6 | const git_index_entry *next_entry = NULL; | |
2073 | - | |||
2074 | 224 | 6 | if (++iter->next_idx >= iter->entries.length) | |
2075 | 4 | 7 | return GIT_ITEROVER; | |
2076 | - | |||
2077 | 220 | 8 | next_entry = iter->entries.contents[iter->next_idx]; | |
2078 | - | |||
2079 | 220 | 8,9 | if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path, | |
2080 | - | iter->tree_buf.size) != 0) | ||
2081 | 18 | 10 | break; | |
2082 | 202 | 11 | } | |
2083 | - | |||
2084 | 18 | 12 | iter->skip_tree = false; | |
2085 | 18 | 12 | return 0; | |
2086 | - | } | ||
2087 | - | |||
2088 | 20548 | 2 | static int index_iterator_advance( | |
2089 | - | const git_index_entry **out, git_iterator *i) | ||
2090 | - | { | ||
2091 | 20548 | 2 | index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); | |
2092 | 20548 | 2 | const git_index_entry *entry = NULL; | |
2093 | - | bool is_submodule; | ||
2094 | 20548 | 2 | int error = 0; | |
2095 | - | |||
2096 | 20548 | 2 | iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; | |
2097 | - | |||
2098 | - | while (true) { | ||
2099 | 32512 | 3 | if (iter->next_idx >= iter->entries.length) { | |
2100 | 4035 | 4 | error = GIT_ITEROVER; | |
2101 | 4035 | 4 | break; | |
2102 | - | } | ||
2103 | - | |||
2104 | - | /* we were not asked to expand this pseudotree. advance over it. */ | ||
2105 | 28477 | 5 | if (iter->skip_tree) { | |
2106 | 6 | 6 | index_iterator_skip_pseudotree(iter); | |
2107 | 6 | 7 | continue; | |
2108 | - | } | ||
2109 | - | |||
2110 | 28471 | 8 | entry = iter->entries.contents[iter->next_idx]; | |
2111 | 28471 | 8 | is_submodule = S_ISGITLINK(entry->mode); | |
2112 | - | |||
2113 | 28471 | 8,9 | if (!iterator_has_started(&iter->base, entry->path, is_submodule)) { | |
2114 | 820 | 10 | iter->next_idx++; | |
2115 | 820 | 10 | continue; | |
2116 | - | } | ||
2117 | - | |||
2118 | 27651 | 11,12 | if (iterator_has_ended(&iter->base, entry->path)) { | |
2119 | 138 | 13 | error = GIT_ITEROVER; | |
2120 | 138 | 13 | break; | |
2121 | - | } | ||
2122 | - | |||
2123 | - | /* if we have a list of paths we're interested in, examine it */ | ||
2124 | 27513 | 14,15 | if (!iterator_pathlist_next_is(&iter->base, entry->path)) { | |
2125 | 10101 | 16 | iter->next_idx++; | |
2126 | 10101 | 16 | continue; | |
2127 | - | } | ||
2128 | - | |||
2129 | - | /* if this is a conflict, skip it unless we're including conflicts */ | ||
2130 | 17413 | 17-19 | if (git_index_entry_is_conflict(entry) && | |
2131 | 1136 | 19 | !iterator__include_conflicts(&iter->base)) { | |
2132 | 1037 | 20 | iter->next_idx++; | |
2133 | 1037 | 20 | continue; | |
2134 | - | } | ||
2135 | - | |||
2136 | - | /* we've found what will be our next _file_ entry. but if we are | ||
2137 | - | * returning trees entries, we may need to return a pseudotree | ||
2138 | - | * entry that will contain this. don't advance over this entry, | ||
2139 | - | * though, we still need to return it on the next `advance`. | ||
2140 | - | */ | ||
2141 | 16376 | 21,23 | if (iterator__include_trees(&iter->base) && | |
2142 | 326 | 22 | index_iterator_create_pseudotree(&entry, iter, entry->path)) { | |
2143 | - | |||
2144 | - | /* Note whether this pseudo tree should be expanded or not */ | ||
2145 | 48 | 24 | iter->skip_tree = iterator__dont_autoexpand(&iter->base); | |
2146 | 48 | 24 | break; | |
2147 | - | } | ||
2148 | - | |||
2149 | 16328 | 25 | iter->next_idx++; | |
2150 | 16328 | 25 | break; | |
2151 | 11964 | 26 | } | |
2152 | - | |||
2153 | 20549 | 27-29 | iter->entry = (error == 0) ? entry : NULL; | |
2154 | - | |||
2155 | 20549 | 30 | if (out) | |
2156 | 20531 | 31 | *out = iter->entry; | |
2157 | - | |||
2158 | 20549 | 32 | return error; | |
2159 | - | } | ||
2160 | - | |||
2161 | 19 | 2 | static int index_iterator_advance_into( | |
2162 | - | const git_index_entry **out, git_iterator *i) | ||
2163 | - | { | ||
2164 | 19 | 2 | index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); | |
2165 | - | |||
2166 | 19 | 2 | if (! S_ISDIR(iter->tree_entry.mode)) { | |
2167 | ##### | 3 | if (out) | |
2168 | ##### | 4 | *out = NULL; | |
2169 | - | |||
2170 | ##### | 5 | return 0; | |
2171 | - | } | ||
2172 | - | |||
2173 | 19 | 6 | iter->skip_tree = false; | |
2174 | 19 | 6 | return index_iterator_advance(out, i); | |
2175 | - | } | ||
2176 | - | |||
2177 | 66 | 2 | static int index_iterator_advance_over( | |
2178 | - | const git_index_entry **out, | ||
2179 | - | git_iterator_status_t *status, | ||
2180 | - | git_iterator *i) | ||
2181 | - | { | ||
2182 | 66 | 2 | index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); | |
2183 | - | const git_index_entry *entry; | ||
2184 | - | int error; | ||
2185 | - | |||
2186 | 66 | 2,3 | if ((error = index_iterator_current(&entry, i)) < 0) | |
2187 | ##### | 4 | return error; | |
2188 | - | |||
2189 | 66 | 5 | if (S_ISDIR(entry->mode)) | |
2190 | 16 | 6 | index_iterator_skip_pseudotree(iter); | |
2191 | - | |||
2192 | 66 | 7 | *status = GIT_ITERATOR_STATUS_NORMAL; | |
2193 | 66 | 7 | return index_iterator_advance(out, i); | |
2194 | - | } | ||
2195 | - | |||
2196 | 299 | 2 | static void index_iterator_clear(index_iterator *iter) | |
2197 | - | { | ||
2198 | 299 | 2 | iterator_clear(&iter->base); | |
2199 | 299 | 3 | } | |
2200 | - | |||
2201 | 4351 | 2 | static int index_iterator_init(index_iterator *iter) | |
2202 | - | { | ||
2203 | 4351 | 2 | iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; | |
2204 | 4351 | 2 | iter->next_idx = 0; | |
2205 | 4351 | 2 | iter->skip_tree = false; | |
2206 | 4351 | 2 | return 0; | |
2207 | - | } | ||
2208 | - | |||
2209 | 299 | 2 | static int index_iterator_reset(git_iterator *i) | |
2210 | - | { | ||
2211 | 299 | 2 | index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); | |
2212 | - | |||
2213 | 299 | 2 | index_iterator_clear(iter); | |
2214 | 299 | 3 | return index_iterator_init(iter); | |
2215 | - | } | ||
2216 | - | |||
2217 | 4052 | 2 | static void index_iterator_free(git_iterator *i) | |
2218 | - | { | ||
2219 | 4052 | 2 | index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base); | |
2220 | - | |||
2221 | 4052 | 2 | git_index_snapshot_release(&iter->entries, iter->base.index); | |
2222 | 4052 | 3 | git_buf_dispose(&iter->tree_buf); | |
2223 | 4052 | 4 | } | |
2224 | - | |||
2225 | 4052 | 2 | int git_iterator_for_index( | |
2226 | - | git_iterator **out, | ||
2227 | - | git_repository *repo, | ||
2228 | - | git_index *index, | ||
2229 | - | git_iterator_options *options) | ||
2230 | - | { | ||
2231 | - | index_iterator *iter; | ||
2232 | - | int error; | ||
2233 | - | |||
2234 | - | static git_iterator_callbacks callbacks = { | ||
2235 | - | index_iterator_current, | ||
2236 | - | index_iterator_advance, | ||
2237 | - | index_iterator_advance_into, | ||
2238 | - | index_iterator_advance_over, | ||
2239 | - | index_iterator_reset, | ||
2240 | - | index_iterator_free | ||
2241 | - | }; | ||
2242 | - | |||
2243 | 4052 | 2 | *out = NULL; | |
2244 | - | |||
2245 | 4052 | 2 | if (index == NULL) | |
2246 | ##### | 3 | return git_iterator_for_nothing(out, options); | |
2247 | - | |||
2248 | 4052 | 4 | iter = git__calloc(1, sizeof(index_iterator)); | |
2249 | 4052 | 5,6 | GIT_ERROR_CHECK_ALLOC(iter); | |
2250 | - | |||
2251 | 4052 | 7 | iter->base.type = GIT_ITERATOR_INDEX; | |
2252 | 4052 | 7 | iter->base.cb = &callbacks; | |
2253 | - | |||
2254 | 4052 | 7-10 | if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 || | |
2255 | 4052 | 9,11,12 | (error = git_index_snapshot_new(&iter->entries, index)) < 0 || | |
2256 | - | (error = index_iterator_init(iter)) < 0) | ||
2257 | - | goto on_error; | ||
2258 | - | |||
2259 | 4052 | 13-16 | git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ? | |
2260 | - | git_index_entry_icmp : git_index_entry_cmp); | ||
2261 | 4052 | 17 | git_vector_sort(&iter->entries); | |
2262 | - | |||
2263 | 4052 | 18 | *out = &iter->base; | |
2264 | 4052 | 18 | return 0; | |
2265 | - | |||
2266 | - | on_error: | ||
2267 | ##### | 19 | git_iterator_free(&iter->base); | |
2268 | ##### | 20 | return error; | |
2269 | - | } | ||
2270 | - | |||
2271 | - | |||
2272 | - | /* Iterator API */ | ||
2273 | - | |||
2274 | 842 | 2 | int git_iterator_reset_range( | |
2275 | - | git_iterator *i, const char *start, const char *end) | ||
2276 | - | { | ||
2277 | 842 | 2,3 | if (iterator_reset_range(i, start, end) < 0) | |
2278 | ##### | 4 | return -1; | |
2279 | - | |||
2280 | 842 | 5 | return i->cb->reset(i); | |
2281 | - | } | ||
2282 | - | |||
2283 | 74 | 2 | void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case) | |
2284 | - | { | ||
2285 | 74 | 2,3 | assert(!iterator__has_been_accessed(i)); | |
2286 | 74 | 4 | iterator_set_ignore_case(i, ignore_case); | |
2287 | 74 | 5 | } | |
2288 | - | |||
2289 | 20330 | 2 | void git_iterator_free(git_iterator *iter) | |
2290 | - | { | ||
2291 | 20330 | 2 | if (iter == NULL) | |
2292 | 20329 | 3,9 | return; | |
2293 | - | |||
2294 | 18965 | 4 | iter->cb->free(iter); | |
2295 | - | |||
2296 | 18964 | 5 | git_vector_free(&iter->pathlist); | |
2297 | 18965 | 6 | git__free(iter->start); | |
2298 | 18964 | 7 | git__free(iter->end); | |
2299 | - | |||
2300 | 18964 | 8 | memset(iter, 0, sizeof(*iter)); | |
2301 | - | |||
2302 | 18964 | 8 | git__free(iter); | |
2303 | - | } | ||
2304 | - | |||
2305 | 65 | 2 | int git_iterator_foreach( | |
2306 | - | git_iterator *iterator, | ||
2307 | - | git_iterator_foreach_cb cb, | ||
2308 | - | void *data) | ||
2309 | - | { | ||
2310 | - | const git_index_entry *iterator_item; | ||
2311 | 65 | 2 | int error = 0; | |
2312 | - | |||
2313 | 65 | 2,3 | if ((error = git_iterator_current(&iterator_item, iterator)) < 0) | |
2314 | ##### | 4 | goto done; | |
2315 | - | |||
2316 | 65 | 5,6 | if ((error = cb(iterator_item, data)) != 0) | |
2317 | ##### | 7 | goto done; | |
2318 | - | |||
2319 | - | while (true) { | ||
2320 | 386 | 8,9 | if ((error = git_iterator_advance(&iterator_item, iterator)) < 0) | |
2321 | 65 | 10 | goto done; | |
2322 | - | |||
2323 | 321 | 11,12 | if ((error = cb(iterator_item, data)) != 0) | |
2324 | ##### | 13 | goto done; | |
2325 | 321 | 14 | } | |
2326 | - | |||
2327 | - | done: | ||
2328 | 65 | 15 | if (error == GIT_ITEROVER) | |
2329 | 65 | 16 | error = 0; | |
2330 | - | |||
2331 | 65 | 17 | return error; | |
2332 | - | } | ||
2333 | - | |||
2334 | 377 | 2 | int git_iterator_walk( | |
2335 | - | git_iterator **iterators, | ||
2336 | - | size_t cnt, | ||
2337 | - | git_iterator_walk_cb cb, | ||
2338 | - | void *data) | ||
2339 | - | { | ||
2340 | - | const git_index_entry **iterator_item; /* next in each iterator */ | ||
2341 | - | const git_index_entry **cur_items; /* current path in each iter */ | ||
2342 | - | const git_index_entry *first_match; | ||
2343 | - | size_t i, j; | ||
2344 | 377 | 2 | int error = 0; | |
2345 | - | |||
2346 | 377 | 2 | iterator_item = git__calloc(cnt, sizeof(git_index_entry *)); | |
2347 | 377 | 3 | cur_items = git__calloc(cnt, sizeof(git_index_entry *)); | |
2348 | - | |||
2349 | 377 | 4,5 | GIT_ERROR_CHECK_ALLOC(iterator_item); | |
2350 | 377 | 6,7 | GIT_ERROR_CHECK_ALLOC(cur_items); | |
2351 | - | |||
2352 | - | /* Set up the iterators */ | ||
2353 | 1468 | 8,13,14 | for (i = 0; i < cnt; i++) { | |
2354 | 1091 | 9 | error = git_iterator_current(&iterator_item[i], iterators[i]); | |
2355 | - | |||
2356 | 1091 | 10,11 | if (error < 0 && error != GIT_ITEROVER) | |
2357 | ##### | 12 | goto done; | |
2358 | - | } | ||
2359 | - | |||
2360 | - | while (true) { | ||
2361 | 19471 | 15-17 | for (i = 0; i < cnt; i++) | |
2362 | 14537 | 16 | cur_items[i] = NULL; | |
2363 | - | |||
2364 | 4934 | 18 | first_match = NULL; | |
2365 | - | |||
2366 | - | /* Find the next path(s) to consume from each iterator */ | ||
2367 | 19471 | 18,31,32 | for (i = 0; i < cnt; i++) { | |
2368 | 14537 | 19 | if (iterator_item[i] == NULL) | |
2369 | 3452 | 20 | continue; | |
2370 | - | |||
2371 | 11085 | 21 | if (first_match == NULL) { | |
2372 | 4557 | 22 | first_match = iterator_item[i]; | |
2373 | 4557 | 22 | cur_items[i] = iterator_item[i]; | |
2374 | - | } else { | ||
2375 | 6528 | 23 | int path_diff = git_index_entry_cmp(iterator_item[i], first_match); | |
2376 | - | |||
2377 | 6528 | 24 | if (path_diff < 0) { | |
2378 | - | /* Found an index entry that sorts before the one we're | ||
2379 | - | * looking at. Forget that we've seen the other and | ||
2380 | - | * look at the other iterators for this path. | ||
2381 | - | */ | ||
2382 | 809 | 25-27 | for (j = 0; j < i; j++) | |
2383 | 466 | 26 | cur_items[j] = NULL; | |
2384 | - | |||
2385 | 343 | 28 | first_match = iterator_item[i]; | |
2386 | 343 | 28 | cur_items[i] = iterator_item[i]; | |
2387 | 6185 | 29 | } else if (path_diff == 0) { | |
2388 | 4540 | 30 | cur_items[i] = iterator_item[i]; | |
2389 | - | } | ||
2390 | - | } | ||
2391 | - | } | ||
2392 | - | |||
2393 | 4934 | 33 | if (first_match == NULL) | |
2394 | 377 | 34 | break; | |
2395 | - | |||
2396 | 4557 | 35,36 | if ((error = cb(cur_items, data)) != 0) | |
2397 | ##### | 37 | goto done; | |
2398 | - | |||
2399 | - | /* Advance each iterator that participated */ | ||
2400 | 18003 | 38,45,46 | for (i = 0; i < cnt; i++) { | |
2401 | 13446 | 39 | if (cur_items[i] == NULL) | |
2402 | 4425 | 40 | continue; | |
2403 | - | |||
2404 | 9021 | 41 | error = git_iterator_advance(&iterator_item[i], iterators[i]); | |
2405 | - | |||
2406 | 9021 | 42,43 | if (error < 0 && error != GIT_ITEROVER) | |
2407 | ##### | 44 | goto done; | |
2408 | - | } | ||
2409 | 4557 | 47 | } | |
2410 | - | |||
2411 | - | done: | ||
2412 | 377 | 48 | git__free((git_index_entry **)iterator_item); | |
2413 | 377 | 49 | git__free((git_index_entry **)cur_items); | |
2414 | - | |||
2415 | 377 | 50 | if (error == GIT_ITEROVER) | |
2416 | 377 | 51 | error = 0; | |
2417 | - | |||
2418 | 377 | 52 | return error; | |
2419 | - | } |