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