]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
Make iterators use GIT_ITEROVER & smart advance
[libgit2.git] / src / iterator.c
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 #include "tree.h"
10 #include "ignore.h"
11 #include "buffer.h"
12 #include "git2/submodule.h"
13 #include <ctype.h>
14
15 #define ITERATOR_SET_CB(P,NAME_LC) do { \
16 (P)->cb.current = NAME_LC ## _iterator__current; \
17 (P)->cb.advance = NAME_LC ## _iterator__advance; \
18 (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
19 (P)->cb.seek = NAME_LC ## _iterator__seek; \
20 (P)->cb.reset = NAME_LC ## _iterator__reset; \
21 (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
22 (P)->cb.free = NAME_LC ## _iterator__free; \
23 } while (0)
24
25 #define ITERATOR_CASE_FLAGS \
26 (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
27
28 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
29 (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
30 (P)->base.cb = &(P)->cb; \
31 ITERATOR_SET_CB(P,NAME_LC); \
32 (P)->base.repo = (REPO); \
33 (P)->base.start = start ? git__strdup(start) : NULL; \
34 (P)->base.end = end ? git__strdup(end) : NULL; \
35 if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
36 git__free(P); return -1; } \
37 (P)->base.prefixcomp = git__prefixcmp; \
38 (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
39 if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
40 (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
41 } while (0)
42
43 #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
44 #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
45 #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
46 #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
47 #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
48
49 #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
50 #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
51
52 #define iterator__end(I) ((git_iterator *)(I))->end
53 #define iterator__past_end(I,PATH) \
54 (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
55
56
57 static int iterator__reset_range(
58 git_iterator *iter, const char *start, const char *end)
59 {
60 if (start) {
61 if (iter->start)
62 git__free(iter->start);
63 iter->start = git__strdup(start);
64 GITERR_CHECK_ALLOC(iter->start);
65 }
66
67 if (end) {
68 if (iter->end)
69 git__free(iter->end);
70 iter->end = git__strdup(end);
71 GITERR_CHECK_ALLOC(iter->end);
72 }
73
74 iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
75
76 return 0;
77 }
78
79 static int iterator__update_ignore_case(
80 git_iterator *iter,
81 git_iterator_flag_t flags)
82 {
83 int error = 0, ignore_case = -1;
84
85 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
86 ignore_case = true;
87 else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
88 ignore_case = false;
89 else {
90 git_index *index;
91
92 if (!(error = git_repository_index__weakptr(&index, iter->repo)))
93 ignore_case = (index->ignore_case != false);
94 }
95
96 if (ignore_case > 0)
97 iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
98 else if (ignore_case == 0)
99 iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
100
101 iter->prefixcomp = iterator__ignore_case(iter) ?
102 git__prefixcmp_icase : git__prefixcmp;
103
104 return error;
105 }
106
107 GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
108 {
109 if (entry) *entry = NULL;
110 }
111
112
113 static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
114 {
115 GIT_UNUSED(i);
116 iterator__clear_entry(e);
117 return GIT_ITEROVER;
118 }
119
120 static int empty_iterator__seek(git_iterator *i, const char *p)
121 {
122 GIT_UNUSED(i); GIT_UNUSED(p);
123 return -1;
124 }
125
126 static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
127 {
128 GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
129 return 0;
130 }
131
132 static int empty_iterator__at_end(git_iterator *i)
133 {
134 GIT_UNUSED(i);
135 return 1;
136 }
137
138 static void empty_iterator__free(git_iterator *i)
139 {
140 GIT_UNUSED(i);
141 }
142
143 typedef struct {
144 git_iterator base;
145 git_iterator_callbacks cb;
146 } empty_iterator;
147
148 int git_iterator_for_nothing(
149 git_iterator **iter,
150 git_iterator_flag_t flags,
151 const char *start,
152 const char *end)
153 {
154 empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
155 GITERR_CHECK_ALLOC(i);
156
157 #define empty_iterator__current empty_iterator__noop
158 #define empty_iterator__advance empty_iterator__noop
159 #define empty_iterator__advance_into empty_iterator__noop
160
161 ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
162
163 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
164 i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
165
166 *iter = (git_iterator *)i;
167 return 0;
168 }
169
170
171 typedef struct tree_iterator_entry tree_iterator_entry;
172 struct tree_iterator_entry {
173 tree_iterator_entry *parent;
174 const git_tree_entry *te;
175 git_tree *tree;
176 };
177
178 typedef struct tree_iterator_frame tree_iterator_frame;
179 struct tree_iterator_frame {
180 tree_iterator_frame *up, *down;
181
182 size_t n_entries; /* items in this frame */
183 size_t current; /* start of currently active range in frame */
184 size_t next; /* start of next range in frame */
185
186 const char *start;
187 size_t startlen;
188
189 tree_iterator_entry *entries[GIT_FLEX_ARRAY];
190 };
191
192 typedef struct {
193 git_iterator base;
194 git_iterator_callbacks cb;
195 tree_iterator_frame *head, *root;
196 git_pool pool;
197 git_index_entry entry;
198 git_buf path;
199 int path_ambiguities;
200 bool path_has_filename;
201 bool entry_is_current;
202 int (*strncomp)(const char *a, const char *b, size_t sz);
203 } tree_iterator;
204
205 static char *tree_iterator__current_filename(
206 tree_iterator *ti, const git_tree_entry *te)
207 {
208 if (!ti->path_has_filename) {
209 if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
210 return NULL;
211
212 if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
213 return NULL;
214
215 ti->path_has_filename = true;
216 }
217
218 return ti->path.ptr;
219 }
220
221 static void tree_iterator__rewrite_filename(tree_iterator *ti)
222 {
223 tree_iterator_entry *scan = ti->head->entries[ti->head->current];
224 ssize_t strpos = ti->path.size;
225 const git_tree_entry *te;
226
227 if (strpos && ti->path.ptr[strpos - 1] == '/')
228 strpos--;
229
230 for (; scan && (te = scan->te); scan = scan->parent) {
231 strpos -= te->filename_len;
232 memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
233 strpos -= 1; /* separator */
234 }
235 }
236
237 static int tree_iterator__te_cmp(
238 const git_tree_entry *a,
239 const git_tree_entry *b,
240 int (*compare)(const char *, const char *, size_t))
241 {
242 return git_path_cmp(
243 a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
244 b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
245 compare);
246 }
247
248 static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
249 {
250 const tree_iterator_entry *ae = a, *be = b;
251 int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
252
253 if (!cmp) {
254 /* stabilize sort order among equivalent names */
255 if (!ae->parent->te || !be->parent->te)
256 cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
257 else
258 cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
259 }
260
261 return cmp;
262 }
263
264 static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
265 {
266 const tree_iterator_frame *tf = key;
267 const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
268
269 return git_path_cmp(
270 tf->start, tf->startlen, false,
271 te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
272 ((tree_iterator *)p)->strncomp);
273 }
274
275 static bool tree_iterator__move_to_next(
276 tree_iterator *ti, tree_iterator_frame *tf)
277 {
278 if (tf->next > tf->current + 1)
279 ti->path_ambiguities--;
280
281 if (!tf->up) { /* at root */
282 tf->current = tf->next;
283 return false;
284 }
285
286 for (; tf->current < tf->next; tf->current++) {
287 git_tree_free(tf->entries[tf->current]->tree);
288 tf->entries[tf->current]->tree = NULL;
289 }
290
291 return (tf->current < tf->n_entries);
292 }
293
294 static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
295 {
296 int error = 0;
297 const git_tree_entry *te, *last = NULL;
298
299 tf->next = tf->current;
300
301 for (; tf->next < tf->n_entries; tf->next++, last = te) {
302 te = tf->entries[tf->next]->te;
303
304 if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
305 break;
306
307 /* try to load trees for items in [current,next) range */
308 if (!error && git_tree_entry__is_tree(te))
309 error = git_tree_lookup(
310 &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
311 }
312
313 if (tf->next > tf->current + 1)
314 ti->path_ambiguities++;
315
316 /* if a tree lookup failed, advance over this span and return failure */
317 if (error < 0) {
318 tree_iterator__move_to_next(ti, tf);
319 return error;
320 }
321
322 if (last && !tree_iterator__current_filename(ti, last))
323 return -1; /* must have been allocation failure */
324
325 return 0;
326 }
327
328 GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
329 {
330 return (ti->head->current < ti->head->n_entries &&
331 ti->head->entries[ti->head->current]->tree != NULL);
332 }
333
334 static int tree_iterator__push_frame(tree_iterator *ti)
335 {
336 int error = 0;
337 tree_iterator_frame *head = ti->head, *tf = NULL;
338 size_t i, n_entries = 0;
339
340 if (head->current >= head->n_entries || !head->entries[head->current]->tree)
341 return GIT_ITEROVER;
342
343 for (i = head->current; i < head->next; ++i)
344 n_entries += git_tree_entrycount(head->entries[i]->tree);
345
346 tf = git__calloc(sizeof(tree_iterator_frame) +
347 n_entries * sizeof(tree_iterator_entry *), 1);
348 GITERR_CHECK_ALLOC(tf);
349
350 tf->n_entries = n_entries;
351
352 tf->up = head;
353 head->down = tf;
354 ti->head = tf;
355
356 for (i = head->current, n_entries = 0; i < head->next; ++i) {
357 git_tree *tree = head->entries[i]->tree;
358 size_t j, max_j = git_tree_entrycount(tree);
359
360 for (j = 0; j < max_j; ++j) {
361 tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
362 GITERR_CHECK_ALLOC(entry);
363
364 entry->parent = head->entries[i];
365 entry->te = git_tree_entry_byindex(tree, j);
366 entry->tree = NULL;
367
368 tf->entries[n_entries++] = entry;
369 }
370 }
371
372 /* if ignore_case, sort entries case insensitively */
373 if (iterator__ignore_case(ti))
374 git__tsort_r(
375 (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
376
377 /* pick tf->current based on "start" (or start at zero) */
378 if (head->startlen > 0) {
379 git__bsearch_r((void **)tf->entries, tf->n_entries, head,
380 tree_iterator__search_cmp, ti, &tf->current);
381
382 while (tf->current &&
383 !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
384 tf->current--;
385
386 if ((tf->start = strchr(head->start, '/')) != NULL) {
387 tf->start++;
388 tf->startlen = strlen(tf->start);
389 }
390 }
391
392 ti->path_has_filename = ti->entry_is_current = false;
393
394 if ((error = tree_iterator__set_next(ti, tf)) < 0)
395 return error;
396
397 /* autoexpand as needed */
398 if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
399 return tree_iterator__push_frame(ti);
400
401 return 0;
402 }
403
404 static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
405 {
406 tree_iterator_frame *tf = ti->head;
407
408 if (!tf->up)
409 return false;
410
411 ti->head = tf->up;
412 ti->head->down = NULL;
413
414 tree_iterator__move_to_next(ti, tf);
415
416 if (!final) { /* if final, don't bother to clean up */
417 git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
418 git_buf_rtruncate_at_char(&ti->path, '/');
419 }
420
421 git__free(tf);
422
423 return true;
424 }
425
426 static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
427 {
428 while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
429
430 if (!final) {
431 ti->head->current = to_end ? ti->head->n_entries : 0;
432 ti->path_ambiguities = 0;
433 git_buf_clear(&ti->path);
434 }
435 }
436
437 static int tree_iterator__update_entry(tree_iterator *ti)
438 {
439 tree_iterator_frame *tf;
440 const git_tree_entry *te;
441
442 if (ti->entry_is_current)
443 return 0;
444
445 tf = ti->head;
446 te = tf->entries[tf->current]->te;
447
448 ti->entry.mode = te->attr;
449 git_oid_cpy(&ti->entry.oid, &te->oid);
450
451 ti->entry.path = tree_iterator__current_filename(ti, te);
452 GITERR_CHECK_ALLOC(ti->entry.path);
453
454 if (ti->path_ambiguities > 0)
455 tree_iterator__rewrite_filename(ti);
456
457 if (iterator__past_end(ti, ti->entry.path)) {
458 tree_iterator__pop_all(ti, true, false);
459 return GIT_ITEROVER;
460 }
461
462 ti->entry_is_current = true;
463
464 return 0;
465 }
466
467 static int tree_iterator__current(
468 const git_index_entry **entry, git_iterator *self)
469 {
470 int error;
471 tree_iterator *ti = (tree_iterator *)self;
472 tree_iterator_frame *tf = ti->head;
473
474 iterator__clear_entry(entry);
475
476 if (tf->current >= tf->n_entries)
477 return GIT_ITEROVER;
478
479 if ((error = tree_iterator__update_entry(ti)) < 0)
480 return error;
481
482 if (entry)
483 *entry = &ti->entry;
484
485 ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
486
487 return 0;
488 }
489
490 static int tree_iterator__advance_into(
491 const git_index_entry **entry, git_iterator *self)
492 {
493 int error = 0;
494 tree_iterator *ti = (tree_iterator *)self;
495
496 iterator__clear_entry(entry);
497
498 if (tree_iterator__at_tree(ti))
499 error = tree_iterator__push_frame(ti);
500
501 if (!error && entry)
502 error = tree_iterator__current(entry, self);
503
504 return error;
505 }
506
507 static int tree_iterator__advance(
508 const git_index_entry **entry, git_iterator *self)
509 {
510 int error;
511 tree_iterator *ti = (tree_iterator *)self;
512 tree_iterator_frame *tf = ti->head;
513
514 iterator__clear_entry(entry);
515
516 if (tf->current >= tf->n_entries)
517 return GIT_ITEROVER;
518
519 if (!iterator__has_been_accessed(ti))
520 return tree_iterator__current(entry, self);
521
522 if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
523 tree_iterator__at_tree(ti))
524 return tree_iterator__advance_into(entry, self);
525
526 if (ti->path_has_filename) {
527 git_buf_rtruncate_at_char(&ti->path, '/');
528 ti->path_has_filename = ti->entry_is_current = false;
529 }
530
531 /* scan forward and up, advancing in frame or popping frame when done */
532 while (!tree_iterator__move_to_next(ti, tf) &&
533 tree_iterator__pop_frame(ti, false))
534 tf = ti->head;
535
536 /* find next and load trees */
537 if ((error = tree_iterator__set_next(ti, tf)) < 0)
538 return error;
539
540 /* deal with include_trees / auto_expand as needed */
541 if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
542 return tree_iterator__advance_into(entry, self);
543
544 return tree_iterator__current(entry, self);
545 }
546
547 static int tree_iterator__seek(git_iterator *self, const char *prefix)
548 {
549 GIT_UNUSED(self); GIT_UNUSED(prefix);
550 return -1;
551 }
552
553 static int tree_iterator__reset(
554 git_iterator *self, const char *start, const char *end)
555 {
556 tree_iterator *ti = (tree_iterator *)self;
557
558 tree_iterator__pop_all(ti, false, false);
559
560 if (iterator__reset_range(self, start, end) < 0)
561 return -1;
562
563 return tree_iterator__push_frame(ti); /* re-expand root tree */
564 }
565
566 static int tree_iterator__at_end(git_iterator *self)
567 {
568 tree_iterator *ti = (tree_iterator *)self;
569 return (ti->head->current >= ti->head->n_entries);
570 }
571
572 static void tree_iterator__free(git_iterator *self)
573 {
574 tree_iterator *ti = (tree_iterator *)self;
575
576 tree_iterator__pop_all(ti, true, false);
577
578 git_tree_free(ti->head->entries[0]->tree);
579 git__free(ti->head);
580 git_pool_clear(&ti->pool);
581 git_buf_free(&ti->path);
582 }
583
584 static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
585 {
586 size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
587 tree_iterator_frame *root = git__calloc(sz, sizeof(char));
588 GITERR_CHECK_ALLOC(root);
589
590 root->n_entries = 1;
591 root->next = 1;
592 root->start = ti->base.start;
593 root->startlen = root->start ? strlen(root->start) : 0;
594 root->entries[0] = git_pool_mallocz(&ti->pool, 1);
595 GITERR_CHECK_ALLOC(root->entries[0]);
596 root->entries[0]->tree = tree;
597
598 ti->head = ti->root = root;
599
600 return 0;
601 }
602
603 int git_iterator_for_tree(
604 git_iterator **iter,
605 git_tree *tree,
606 git_iterator_flag_t flags,
607 const char *start,
608 const char *end)
609 {
610 int error;
611 tree_iterator *ti;
612
613 if (tree == NULL)
614 return git_iterator_for_nothing(iter, flags, start, end);
615
616 if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
617 return error;
618
619 ti = git__calloc(1, sizeof(tree_iterator));
620 GITERR_CHECK_ALLOC(ti);
621
622 ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
623
624 if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
625 goto fail;
626 ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
627
628 if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
629 (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
630 (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
631 goto fail;
632
633 *iter = (git_iterator *)ti;
634 return 0;
635
636 fail:
637 git_iterator_free((git_iterator *)ti);
638 return error;
639 }
640
641
642 typedef struct {
643 git_iterator base;
644 git_iterator_callbacks cb;
645 git_index *index;
646 size_t current;
647 /* when not in autoexpand mode, use these to represent "tree" state */
648 git_buf partial;
649 size_t partial_pos;
650 char restore_terminator;
651 git_index_entry tree_entry;
652 } index_iterator;
653
654 static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
655 {
656 const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
657
658 if (ie != NULL && iterator__past_end(ii, ie->path)) {
659 ii->current = git_index_entrycount(ii->index);
660 ie = NULL;
661 }
662
663 return ie;
664 }
665
666 static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
667 {
668 const git_index_entry *ie;
669
670 while ((ie = index_iterator__index_entry(ii)) != NULL &&
671 git_index_entry_stage(ie) != 0)
672 ii->current++;
673
674 return ie;
675 }
676
677 static void index_iterator__next_prefix_tree(index_iterator *ii)
678 {
679 const char *slash;
680
681 if (!iterator__include_trees(ii))
682 return;
683
684 slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
685
686 if (slash != NULL) {
687 ii->partial_pos = (slash - ii->partial.ptr) + 1;
688 ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
689 ii->partial.ptr[ii->partial_pos] = '\0';
690 } else {
691 ii->partial_pos = ii->partial.size;
692 }
693
694 if (index_iterator__index_entry(ii) == NULL)
695 ii->partial_pos = ii->partial.size;
696 }
697
698 static int index_iterator__first_prefix_tree(index_iterator *ii)
699 {
700 const git_index_entry *ie = index_iterator__skip_conflicts(ii);
701 const char *scan, *prior, *slash;
702
703 if (!ie || !iterator__include_trees(ii))
704 return 0;
705
706 /* find longest common prefix with prior index entry */
707 for (scan = slash = ie->path, prior = ii->partial.ptr;
708 *scan && *scan == *prior; ++scan, ++prior)
709 if (*scan == '/')
710 slash = scan;
711
712 if (git_buf_sets(&ii->partial, ie->path) < 0)
713 return -1;
714
715 ii->partial_pos = (slash - ie->path) + 1;
716 index_iterator__next_prefix_tree(ii);
717
718 return 0;
719 }
720
721 #define index_iterator__at_tree(I) \
722 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
723
724 static int index_iterator__current(
725 const git_index_entry **entry, git_iterator *self)
726 {
727 index_iterator *ii = (index_iterator *)self;
728 const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
729
730 if (ie != NULL && index_iterator__at_tree(ii)) {
731 ii->tree_entry.path = ii->partial.ptr;
732 ie = &ii->tree_entry;
733 }
734
735 if (entry)
736 *entry = ie;
737
738 ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
739
740 return (ie != NULL) ? 0 : GIT_ITEROVER;
741 }
742
743 static int index_iterator__at_end(git_iterator *self)
744 {
745 index_iterator *ii = (index_iterator *)self;
746 return (ii->current >= git_index_entrycount(ii->index));
747 }
748
749 static int index_iterator__advance(
750 const git_index_entry **entry, git_iterator *self)
751 {
752 index_iterator *ii = (index_iterator *)self;
753 size_t entrycount = git_index_entrycount(ii->index);
754 const git_index_entry *ie;
755
756 if (!iterator__has_been_accessed(ii))
757 return index_iterator__current(entry, self);
758
759 if (index_iterator__at_tree(ii)) {
760 if (iterator__do_autoexpand(ii)) {
761 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
762 index_iterator__next_prefix_tree(ii);
763 } else {
764 /* advance to sibling tree (i.e. find entry with new prefix) */
765 while (ii->current < entrycount) {
766 ii->current++;
767
768 if (!(ie = git_index_get_byindex(ii->index, ii->current)) ||
769 ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
770 break;
771 }
772
773 if (index_iterator__first_prefix_tree(ii) < 0)
774 return -1;
775 }
776 } else {
777 if (ii->current < entrycount)
778 ii->current++;
779
780 if (index_iterator__first_prefix_tree(ii) < 0)
781 return -1;
782 }
783
784 return index_iterator__current(entry, self);
785 }
786
787 static int index_iterator__advance_into(
788 const git_index_entry **entry, git_iterator *self)
789 {
790 index_iterator *ii = (index_iterator *)self;
791 const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
792
793 if (ie != NULL && index_iterator__at_tree(ii)) {
794 if (ii->restore_terminator)
795 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
796 index_iterator__next_prefix_tree(ii);
797 }
798
799 return index_iterator__current(entry, self);
800 }
801
802 static int index_iterator__seek(git_iterator *self, const char *prefix)
803 {
804 GIT_UNUSED(self); GIT_UNUSED(prefix);
805 return -1;
806 }
807
808 static int index_iterator__reset(
809 git_iterator *self, const char *start, const char *end)
810 {
811 index_iterator *ii = (index_iterator *)self;
812 const git_index_entry *ie;
813
814 if (iterator__reset_range(self, start, end) < 0)
815 return -1;
816
817 ii->current = ii->base.start ?
818 git_index__prefix_position(ii->index, ii->base.start) : 0;
819
820 if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
821 return 0;
822
823 if (git_buf_sets(&ii->partial, ie->path) < 0)
824 return -1;
825
826 ii->partial_pos = 0;
827
828 if (ii->base.start) {
829 size_t startlen = strlen(ii->base.start);
830
831 ii->partial_pos = (startlen > ii->partial.size) ?
832 ii->partial.size : startlen;
833 }
834
835 index_iterator__next_prefix_tree(ii);
836
837 return 0;
838 }
839
840 static void index_iterator__free(git_iterator *self)
841 {
842 index_iterator *ii = (index_iterator *)self;
843 git_index_free(ii->index);
844 ii->index = NULL;
845
846 git_buf_free(&ii->partial);
847 }
848
849 int git_iterator_for_index(
850 git_iterator **iter,
851 git_index *index,
852 git_iterator_flag_t flags,
853 const char *start,
854 const char *end)
855 {
856 index_iterator *ii = git__calloc(1, sizeof(index_iterator));
857 GITERR_CHECK_ALLOC(ii);
858
859 ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
860
861 if (index->ignore_case) {
862 ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
863 ii->base.prefixcomp = git__prefixcmp_icase;
864 }
865
866 ii->index = index;
867 GIT_REFCOUNT_INC(index);
868
869 git_buf_init(&ii->partial, 0);
870 ii->tree_entry.mode = GIT_FILEMODE_TREE;
871
872 index_iterator__reset((git_iterator *)ii, NULL, NULL);
873
874 *iter = (git_iterator *)ii;
875
876 return 0;
877 }
878
879
880 typedef struct fs_iterator_frame fs_iterator_frame;
881 struct fs_iterator_frame {
882 fs_iterator_frame *next;
883 git_vector entries;
884 size_t index;
885 };
886
887 typedef struct fs_iterator fs_iterator;
888 struct fs_iterator {
889 git_iterator base;
890 git_iterator_callbacks cb;
891 fs_iterator_frame *stack;
892 git_index_entry entry;
893 git_buf path;
894 size_t root_len;
895 int depth;
896
897 int (*enter_dir_cb)(fs_iterator *self);
898 int (*leave_dir_cb)(fs_iterator *self);
899 int (*update_entry_cb)(fs_iterator *self);
900 };
901
902 #define FS_MAX_DEPTH 100
903
904 static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
905 {
906 fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
907 git_vector_cmp entry_compare = CASESELECT(
908 iterator__ignore_case(fi),
909 git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
910
911 if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
912 git__free(ff);
913 ff = NULL;
914 }
915
916 return ff;
917 }
918
919 static void fs_iterator__free_frame(fs_iterator_frame *ff)
920 {
921 size_t i;
922 git_path_with_stat *path;
923
924 git_vector_foreach(&ff->entries, i, path)
925 git__free(path);
926 git_vector_free(&ff->entries);
927 git__free(ff);
928 }
929
930 static void fs_iterator__pop_frame(
931 fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
932 {
933 if (fi && fi->stack == ff) {
934 if (!ff->next && !pop_last) {
935 memset(&fi->entry, 0, sizeof(fi->entry));
936 return;
937 }
938
939 if (fi->leave_dir_cb)
940 (void)fi->leave_dir_cb(fi);
941
942 fi->stack = ff->next;
943 fi->depth--;
944 }
945
946 fs_iterator__free_frame(ff);
947 }
948
949 static int fs_iterator__update_entry(fs_iterator *fi);
950 static int fs_iterator__advance_over(
951 const git_index_entry **entry, git_iterator *self);
952
953 static int fs_iterator__entry_cmp(const void *i, const void *item)
954 {
955 const fs_iterator *fi = (const fs_iterator *)i;
956 const git_path_with_stat *ps = item;
957 return fi->base.prefixcomp(fi->base.start, ps->path);
958 }
959
960 static void fs_iterator__seek_frame_start(
961 fs_iterator *fi, fs_iterator_frame *ff)
962 {
963 if (!ff)
964 return;
965
966 if (fi->base.start)
967 git_vector_bsearch2(
968 &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
969 else
970 ff->index = 0;
971 }
972
973 static int fs_iterator__expand_dir(fs_iterator *fi)
974 {
975 int error;
976 fs_iterator_frame *ff;
977
978 if (fi->depth > FS_MAX_DEPTH) {
979 giterr_set(GITERR_REPOSITORY,
980 "Directory nesting is too deep (%d)", fi->depth);
981 return -1;
982 }
983
984 ff = fs_iterator__alloc_frame(fi);
985 GITERR_CHECK_ALLOC(ff);
986
987 error = git_path_dirload_with_stat(
988 fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
989 fi->base.start, fi->base.end, &ff->entries);
990
991 if (error < 0) {
992 fs_iterator__free_frame(ff);
993 fs_iterator__advance_over(NULL, (git_iterator *)fi);
994 return error;
995 }
996
997 if (ff->entries.length == 0) {
998 fs_iterator__free_frame(ff);
999 return GIT_ENOTFOUND;
1000 }
1001
1002 fs_iterator__seek_frame_start(fi, ff);
1003
1004 ff->next = fi->stack;
1005 fi->stack = ff;
1006 fi->depth++;
1007
1008 if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1009 return error;
1010
1011 return fs_iterator__update_entry(fi);
1012 }
1013
1014 static int fs_iterator__current(
1015 const git_index_entry **entry, git_iterator *self)
1016 {
1017 fs_iterator *fi = (fs_iterator *)self;
1018 const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1019
1020 if (entry)
1021 *entry = fe;
1022
1023 fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1024
1025 return (fe != NULL) ? 0 : GIT_ITEROVER;
1026 }
1027
1028 static int fs_iterator__at_end(git_iterator *self)
1029 {
1030 return (((fs_iterator *)self)->entry.path == NULL);
1031 }
1032
1033 static int fs_iterator__advance_into(
1034 const git_index_entry **entry, git_iterator *iter)
1035 {
1036 int error = 0;
1037 fs_iterator *fi = (fs_iterator *)iter;
1038
1039 iterator__clear_entry(entry);
1040
1041 /* Allow you to explicitly advance into a commit/submodule (as well as a
1042 * tree) to avoid cases where an entry is mislabeled as a submodule in
1043 * the working directory. The fs iterator will never have COMMMIT
1044 * entries on it's own, but a wrapper might add them.
1045 */
1046 if (fi->entry.path != NULL &&
1047 (fi->entry.mode == GIT_FILEMODE_TREE ||
1048 fi->entry.mode == GIT_FILEMODE_COMMIT))
1049 /* returns GIT_ENOTFOUND if the directory is empty */
1050 error = fs_iterator__expand_dir(fi);
1051
1052 if (!error && entry)
1053 error = fs_iterator__current(entry, iter);
1054
1055 if (!error && !fi->entry.path)
1056 error = GIT_ITEROVER;
1057
1058 return error;
1059 }
1060
1061 static int fs_iterator__advance_over(
1062 const git_index_entry **entry, git_iterator *self)
1063 {
1064 int error = 0;
1065 fs_iterator *fi = (fs_iterator *)self;
1066 fs_iterator_frame *ff;
1067 git_path_with_stat *next;
1068
1069 if (entry != NULL)
1070 *entry = NULL;
1071
1072 while (fi->entry.path != NULL) {
1073 ff = fi->stack;
1074 next = git_vector_get(&ff->entries, ++ff->index);
1075
1076 if (next != NULL)
1077 break;
1078
1079 fs_iterator__pop_frame(fi, ff, false);
1080 }
1081
1082 error = fs_iterator__update_entry(fi);
1083
1084 if (!error && entry != NULL)
1085 error = fs_iterator__current(entry, self);
1086
1087 return error;
1088 }
1089
1090 static int fs_iterator__advance(
1091 const git_index_entry **entry, git_iterator *self)
1092 {
1093 fs_iterator *fi = (fs_iterator *)self;
1094
1095 if (!iterator__has_been_accessed(fi))
1096 return fs_iterator__current(entry, self);
1097
1098 /* given include_trees & autoexpand, we might have to go into a tree */
1099 if (iterator__do_autoexpand(fi) &&
1100 fi->entry.path != NULL &&
1101 fi->entry.mode == GIT_FILEMODE_TREE)
1102 {
1103 int error = fs_iterator__advance_into(entry, self);
1104 if (error != GIT_ENOTFOUND)
1105 return error;
1106 /* continue silently past empty directories if autoexpanding */
1107 giterr_clear();
1108 }
1109
1110 return fs_iterator__advance_over(entry, self);
1111 }
1112
1113 static int fs_iterator__seek(git_iterator *self, const char *prefix)
1114 {
1115 GIT_UNUSED(self);
1116 GIT_UNUSED(prefix);
1117 /* pop stack until matching prefix */
1118 /* find prefix item in current frame */
1119 /* push subdirectories as deep as possible while matching */
1120 return 0;
1121 }
1122
1123 static int fs_iterator__reset(
1124 git_iterator *self, const char *start, const char *end)
1125 {
1126 int error;
1127 fs_iterator *fi = (fs_iterator *)self;
1128
1129 while (fi->stack != NULL && fi->stack->next != NULL)
1130 fs_iterator__pop_frame(fi, fi->stack, false);
1131 fi->depth = 0;
1132
1133 if ((error = iterator__reset_range(self, start, end)) < 0)
1134 return error;
1135
1136 fs_iterator__seek_frame_start(fi, fi->stack);
1137
1138 error = fs_iterator__update_entry(fi);
1139 if (error == GIT_ITEROVER)
1140 error = 0;
1141
1142 return error;
1143 }
1144
1145 static void fs_iterator__free(git_iterator *self)
1146 {
1147 fs_iterator *fi = (fs_iterator *)self;
1148
1149 while (fi->stack != NULL)
1150 fs_iterator__pop_frame(fi, fi->stack, true);
1151
1152 git_buf_free(&fi->path);
1153 }
1154
1155 static int fs_iterator__update_entry(fs_iterator *fi)
1156 {
1157 git_path_with_stat *ps;
1158
1159 memset(&fi->entry, 0, sizeof(fi->entry));
1160
1161 if (!fi->stack)
1162 return GIT_ITEROVER;
1163
1164 ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1165 if (!ps)
1166 return GIT_ITEROVER;
1167
1168 git_buf_truncate(&fi->path, fi->root_len);
1169 if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1170 return -1;
1171
1172 if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1173 return GIT_ITEROVER;
1174
1175 fi->entry.path = ps->path;
1176 git_index_entry__init_from_stat(&fi->entry, &ps->st);
1177
1178 /* need different mode here to keep directories during iteration */
1179 fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1180
1181 /* allow wrapper to check/update the entry (can force skip) */
1182 if (fi->update_entry_cb &&
1183 fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1184 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1185
1186 /* if this is a tree and trees aren't included, then skip */
1187 if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1188 int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1189 if (error != GIT_ENOTFOUND)
1190 return error;
1191 giterr_clear();
1192 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1193 }
1194
1195 return 0;
1196 }
1197
1198 static int fs_iterator__initialize(
1199 git_iterator **out, fs_iterator *fi, const char *root)
1200 {
1201 int error;
1202
1203 if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1204 git__free(fi);
1205 return -1;
1206 }
1207 fi->root_len = fi->path.size;
1208
1209 if ((error = fs_iterator__expand_dir(fi)) < 0) {
1210 if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1211 giterr_clear();
1212 error = 0;
1213 } else {
1214 git_iterator_free((git_iterator *)fi);
1215 fi = NULL;
1216 }
1217 }
1218
1219 *out = (git_iterator *)fi;
1220 return error;
1221 }
1222
1223 int git_iterator_for_filesystem(
1224 git_iterator **out,
1225 const char *root,
1226 git_iterator_flag_t flags,
1227 const char *start,
1228 const char *end)
1229 {
1230 fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1231 GITERR_CHECK_ALLOC(fi);
1232
1233 ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1234
1235 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1236 fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1237
1238 return fs_iterator__initialize(out, fi, root);
1239 }
1240
1241
1242 typedef struct {
1243 fs_iterator fi;
1244 git_ignores ignores;
1245 int is_ignored;
1246 } workdir_iterator;
1247
1248 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1249 {
1250 size_t len;
1251
1252 if (!path || (len = path->size) < 4)
1253 return false;
1254
1255 if (path->ptr[len - 1] == '/')
1256 len--;
1257
1258 if (tolower(path->ptr[len - 1]) != 't' ||
1259 tolower(path->ptr[len - 2]) != 'i' ||
1260 tolower(path->ptr[len - 3]) != 'g' ||
1261 tolower(path->ptr[len - 4]) != '.')
1262 return false;
1263
1264 return (len == 4 || path->ptr[len - 5] == '/');
1265 }
1266
1267 static int workdir_iterator__enter_dir(fs_iterator *fi)
1268 {
1269 /* only push new ignores if this is not top level directory */
1270 if (fi->stack->next != NULL) {
1271 workdir_iterator *wi = (workdir_iterator *)fi;
1272 ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1273
1274 (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1275 }
1276
1277 return 0;
1278 }
1279
1280 static int workdir_iterator__leave_dir(fs_iterator *fi)
1281 {
1282 workdir_iterator *wi = (workdir_iterator *)fi;
1283 git_ignore__pop_dir(&wi->ignores);
1284 return 0;
1285 }
1286
1287 static int workdir_iterator__update_entry(fs_iterator *fi)
1288 {
1289 int error = 0;
1290 workdir_iterator *wi = (workdir_iterator *)fi;
1291
1292 /* skip over .git entries */
1293 if (workdir_path_is_dotgit(&fi->path))
1294 return GIT_ENOTFOUND;
1295
1296 /* reset is_ignored since we haven't checked yet */
1297 wi->is_ignored = -1;
1298
1299 /* check if apparent tree entries are actually submodules */
1300 if (fi->entry.mode != GIT_FILEMODE_TREE)
1301 return 0;
1302
1303 error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
1304 if (error < 0)
1305 giterr_clear();
1306
1307 /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1308 if (!error || error == GIT_EEXISTS) {
1309 fi->entry.mode = S_IFGITLINK;
1310 fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
1311 }
1312
1313 return 0;
1314 }
1315
1316 static void workdir_iterator__free(git_iterator *self)
1317 {
1318 workdir_iterator *wi = (workdir_iterator *)self;
1319 fs_iterator__free(self);
1320 git_ignore__free(&wi->ignores);
1321 }
1322
1323 int git_iterator_for_workdir(
1324 git_iterator **out,
1325 git_repository *repo,
1326 git_iterator_flag_t flags,
1327 const char *start,
1328 const char *end)
1329 {
1330 int error;
1331 workdir_iterator *wi;
1332
1333 if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1334 return GIT_EBAREREPO;
1335
1336 /* initialize as an fs iterator then do overrides */
1337 wi = git__calloc(1, sizeof(workdir_iterator));
1338 GITERR_CHECK_ALLOC(wi);
1339 ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1340
1341 wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1342 wi->fi.cb.free = workdir_iterator__free;
1343 wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1344 wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1345 wi->fi.update_entry_cb = workdir_iterator__update_entry;
1346
1347 if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1348 (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
1349 {
1350 git_iterator_free((git_iterator *)wi);
1351 return error;
1352 }
1353
1354 return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo));
1355 }
1356
1357
1358 void git_iterator_free(git_iterator *iter)
1359 {
1360 if (iter == NULL)
1361 return;
1362
1363 iter->cb->free(iter);
1364
1365 git__free(iter->start);
1366 git__free(iter->end);
1367
1368 memset(iter, 0, sizeof(*iter));
1369
1370 git__free(iter);
1371 }
1372
1373 int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1374 {
1375 bool desire_ignore_case = (ignore_case != 0);
1376
1377 if (iterator__ignore_case(iter) == desire_ignore_case)
1378 return 0;
1379
1380 if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1381 if (desire_ignore_case)
1382 iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1383 else
1384 iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1385 } else {
1386 giterr_set(GITERR_INVALID,
1387 "Cannot currently set ignore case on non-empty iterators");
1388 return -1;
1389 }
1390
1391 return 0;
1392 }
1393
1394 git_index *git_iterator_get_index(git_iterator *iter)
1395 {
1396 if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1397 return ((index_iterator *)iter)->index;
1398 return NULL;
1399 }
1400
1401 int git_iterator_current_tree_entry(
1402 const git_tree_entry **tree_entry, git_iterator *iter)
1403 {
1404 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1405 *tree_entry = NULL;
1406 else {
1407 tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1408 *tree_entry = (tf->current < tf->n_entries) ?
1409 tf->entries[tf->current]->te : NULL;
1410 }
1411
1412 return 0;
1413 }
1414
1415 int git_iterator_current_parent_tree(
1416 const git_tree **tree_ptr,
1417 git_iterator *iter,
1418 const char *parent_path)
1419 {
1420 tree_iterator *ti = (tree_iterator *)iter;
1421 tree_iterator_frame *tf;
1422 const char *scan = parent_path;
1423 const git_tree_entry *te;
1424
1425 *tree_ptr = NULL;
1426
1427 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1428 return 0;
1429
1430 for (tf = ti->root; *scan; ) {
1431 if (!(tf = tf->down) ||
1432 tf->current >= tf->n_entries ||
1433 !(te = tf->entries[tf->current]->te) ||
1434 ti->strncomp(scan, te->filename, te->filename_len) != 0)
1435 return 0;
1436
1437 scan += te->filename_len;
1438 if (*scan == '/')
1439 scan++;
1440 }
1441
1442 *tree_ptr = tf->entries[tf->current]->tree;
1443 return 0;
1444 }
1445
1446 bool git_iterator_current_is_ignored(git_iterator *iter)
1447 {
1448 workdir_iterator *wi = (workdir_iterator *)iter;
1449
1450 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1451 return false;
1452
1453 if (wi->is_ignored != -1)
1454 return (bool)(wi->is_ignored != 0);
1455
1456 if (git_ignore__lookup(
1457 &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1458 wi->is_ignored = true;
1459
1460 return (bool)wi->is_ignored;
1461 }
1462
1463 int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
1464 {
1465 const git_index_entry *entry;
1466
1467 /* a "done" iterator is after every prefix */
1468 if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1469 return 1;
1470
1471 /* a NULL prefix is after any valid iterator */
1472 if (!path_prefix)
1473 return -1;
1474
1475 return iter->prefixcomp(entry->path, path_prefix);
1476 }
1477
1478 int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1479 {
1480 workdir_iterator *wi = (workdir_iterator *)iter;
1481
1482 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1483 *path = NULL;
1484 else
1485 *path = &wi->fi.path;
1486
1487 return 0;
1488 }