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