]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
error: Simplify giterr_detach
[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 git_error last_error = {0};
995
996 giterr_detach(&last_error);
997
998 /* these callbacks may clear the error message */
999 fs_iterator__free_frame(ff);
1000 fs_iterator__advance_over(NULL, (git_iterator *)fi);
1001 /* next time return value we skipped to */
1002 fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1003
1004 if (last_error.message) {
1005 giterr_set_str(last_error.klass, last_error.message);
1006 free(last_error.message);
1007 }
1008
1009 return error;
1010 }
1011
1012 if (ff->entries.length == 0) {
1013 fs_iterator__free_frame(ff);
1014 return GIT_ENOTFOUND;
1015 }
1016
1017 fs_iterator__seek_frame_start(fi, ff);
1018
1019 ff->next = fi->stack;
1020 fi->stack = ff;
1021 fi->depth++;
1022
1023 if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1024 return error;
1025
1026 return fs_iterator__update_entry(fi);
1027 }
1028
1029 static int fs_iterator__current(
1030 const git_index_entry **entry, git_iterator *self)
1031 {
1032 fs_iterator *fi = (fs_iterator *)self;
1033 const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1034
1035 if (entry)
1036 *entry = fe;
1037
1038 fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1039
1040 return (fe != NULL) ? 0 : GIT_ITEROVER;
1041 }
1042
1043 static int fs_iterator__at_end(git_iterator *self)
1044 {
1045 return (((fs_iterator *)self)->entry.path == NULL);
1046 }
1047
1048 static int fs_iterator__advance_into(
1049 const git_index_entry **entry, git_iterator *iter)
1050 {
1051 int error = 0;
1052 fs_iterator *fi = (fs_iterator *)iter;
1053
1054 iterator__clear_entry(entry);
1055
1056 /* Allow you to explicitly advance into a commit/submodule (as well as a
1057 * tree) to avoid cases where an entry is mislabeled as a submodule in
1058 * the working directory. The fs iterator will never have COMMMIT
1059 * entries on it's own, but a wrapper might add them.
1060 */
1061 if (fi->entry.path != NULL &&
1062 (fi->entry.mode == GIT_FILEMODE_TREE ||
1063 fi->entry.mode == GIT_FILEMODE_COMMIT))
1064 /* returns GIT_ENOTFOUND if the directory is empty */
1065 error = fs_iterator__expand_dir(fi);
1066
1067 if (!error && entry)
1068 error = fs_iterator__current(entry, iter);
1069
1070 if (!error && !fi->entry.path)
1071 error = GIT_ITEROVER;
1072
1073 return error;
1074 }
1075
1076 static int fs_iterator__advance_over(
1077 const git_index_entry **entry, git_iterator *self)
1078 {
1079 int error = 0;
1080 fs_iterator *fi = (fs_iterator *)self;
1081 fs_iterator_frame *ff;
1082 git_path_with_stat *next;
1083
1084 if (entry != NULL)
1085 *entry = NULL;
1086
1087 while (fi->entry.path != NULL) {
1088 ff = fi->stack;
1089 next = git_vector_get(&ff->entries, ++ff->index);
1090
1091 if (next != NULL)
1092 break;
1093
1094 fs_iterator__pop_frame(fi, ff, false);
1095 }
1096
1097 error = fs_iterator__update_entry(fi);
1098
1099 if (!error && entry != NULL)
1100 error = fs_iterator__current(entry, self);
1101
1102 return error;
1103 }
1104
1105 static int fs_iterator__advance(
1106 const git_index_entry **entry, git_iterator *self)
1107 {
1108 fs_iterator *fi = (fs_iterator *)self;
1109
1110 if (!iterator__has_been_accessed(fi))
1111 return fs_iterator__current(entry, self);
1112
1113 /* given include_trees & autoexpand, we might have to go into a tree */
1114 if (iterator__do_autoexpand(fi) &&
1115 fi->entry.path != NULL &&
1116 fi->entry.mode == GIT_FILEMODE_TREE)
1117 {
1118 int error = fs_iterator__advance_into(entry, self);
1119 if (error != GIT_ENOTFOUND)
1120 return error;
1121 /* continue silently past empty directories if autoexpanding */
1122 giterr_clear();
1123 }
1124
1125 return fs_iterator__advance_over(entry, self);
1126 }
1127
1128 static int fs_iterator__seek(git_iterator *self, const char *prefix)
1129 {
1130 GIT_UNUSED(self);
1131 GIT_UNUSED(prefix);
1132 /* pop stack until matching prefix */
1133 /* find prefix item in current frame */
1134 /* push subdirectories as deep as possible while matching */
1135 return 0;
1136 }
1137
1138 static int fs_iterator__reset(
1139 git_iterator *self, const char *start, const char *end)
1140 {
1141 int error;
1142 fs_iterator *fi = (fs_iterator *)self;
1143
1144 while (fi->stack != NULL && fi->stack->next != NULL)
1145 fs_iterator__pop_frame(fi, fi->stack, false);
1146 fi->depth = 0;
1147
1148 if ((error = iterator__reset_range(self, start, end)) < 0)
1149 return error;
1150
1151 fs_iterator__seek_frame_start(fi, fi->stack);
1152
1153 error = fs_iterator__update_entry(fi);
1154 if (error == GIT_ITEROVER)
1155 error = 0;
1156
1157 return error;
1158 }
1159
1160 static void fs_iterator__free(git_iterator *self)
1161 {
1162 fs_iterator *fi = (fs_iterator *)self;
1163
1164 while (fi->stack != NULL)
1165 fs_iterator__pop_frame(fi, fi->stack, true);
1166
1167 git_buf_free(&fi->path);
1168 }
1169
1170 static int fs_iterator__update_entry(fs_iterator *fi)
1171 {
1172 git_path_with_stat *ps;
1173
1174 memset(&fi->entry, 0, sizeof(fi->entry));
1175
1176 if (!fi->stack)
1177 return GIT_ITEROVER;
1178
1179 ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1180 if (!ps)
1181 return GIT_ITEROVER;
1182
1183 git_buf_truncate(&fi->path, fi->root_len);
1184 if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1185 return -1;
1186
1187 if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1188 return GIT_ITEROVER;
1189
1190 fi->entry.path = ps->path;
1191 git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
1192
1193 /* need different mode here to keep directories during iteration */
1194 fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1195
1196 /* allow wrapper to check/update the entry (can force skip) */
1197 if (fi->update_entry_cb &&
1198 fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1199 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1200
1201 /* if this is a tree and trees aren't included, then skip */
1202 if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1203 int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1204 if (error != GIT_ENOTFOUND)
1205 return error;
1206 giterr_clear();
1207 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1208 }
1209
1210 return 0;
1211 }
1212
1213 static int fs_iterator__initialize(
1214 git_iterator **out, fs_iterator *fi, const char *root)
1215 {
1216 int error;
1217
1218 if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1219 git__free(fi);
1220 return -1;
1221 }
1222 fi->root_len = fi->path.size;
1223
1224 fi->dirload_flags =
1225 (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1226 (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
1227 GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1228
1229 if ((error = fs_iterator__expand_dir(fi)) < 0) {
1230 if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1231 giterr_clear();
1232 error = 0;
1233 } else {
1234 git_iterator_free((git_iterator *)fi);
1235 fi = NULL;
1236 }
1237 }
1238
1239 *out = (git_iterator *)fi;
1240 return error;
1241 }
1242
1243 int git_iterator_for_filesystem(
1244 git_iterator **out,
1245 const char *root,
1246 git_iterator_flag_t flags,
1247 const char *start,
1248 const char *end)
1249 {
1250 fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1251 GITERR_CHECK_ALLOC(fi);
1252
1253 ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1254
1255 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1256 fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1257
1258 return fs_iterator__initialize(out, fi, root);
1259 }
1260
1261
1262 typedef struct {
1263 fs_iterator fi;
1264 git_ignores ignores;
1265 int is_ignored;
1266 } workdir_iterator;
1267
1268 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1269 {
1270 size_t len;
1271
1272 if (!path || (len = path->size) < 4)
1273 return false;
1274
1275 if (path->ptr[len - 1] == '/')
1276 len--;
1277
1278 if (tolower(path->ptr[len - 1]) != 't' ||
1279 tolower(path->ptr[len - 2]) != 'i' ||
1280 tolower(path->ptr[len - 3]) != 'g' ||
1281 tolower(path->ptr[len - 4]) != '.')
1282 return false;
1283
1284 return (len == 4 || path->ptr[len - 5] == '/');
1285 }
1286
1287 static int workdir_iterator__enter_dir(fs_iterator *fi)
1288 {
1289 /* only push new ignores if this is not top level directory */
1290 if (fi->stack->next != NULL) {
1291 workdir_iterator *wi = (workdir_iterator *)fi;
1292 ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1293
1294 (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1295 }
1296
1297 return 0;
1298 }
1299
1300 static int workdir_iterator__leave_dir(fs_iterator *fi)
1301 {
1302 workdir_iterator *wi = (workdir_iterator *)fi;
1303 git_ignore__pop_dir(&wi->ignores);
1304 return 0;
1305 }
1306
1307 static int workdir_iterator__update_entry(fs_iterator *fi)
1308 {
1309 int error = 0;
1310 workdir_iterator *wi = (workdir_iterator *)fi;
1311
1312 /* skip over .git entries */
1313 if (workdir_path_is_dotgit(&fi->path))
1314 return GIT_ENOTFOUND;
1315
1316 /* reset is_ignored since we haven't checked yet */
1317 wi->is_ignored = -1;
1318
1319 /* check if apparent tree entries are actually submodules */
1320 if (fi->entry.mode != GIT_FILEMODE_TREE)
1321 return 0;
1322
1323 error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
1324 if (error < 0)
1325 giterr_clear();
1326
1327 /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1328 if (!error || error == GIT_EEXISTS) {
1329 fi->entry.mode = S_IFGITLINK;
1330 fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
1331 }
1332
1333 return 0;
1334 }
1335
1336 static void workdir_iterator__free(git_iterator *self)
1337 {
1338 workdir_iterator *wi = (workdir_iterator *)self;
1339 fs_iterator__free(self);
1340 git_ignore__free(&wi->ignores);
1341 }
1342
1343 int git_iterator_for_workdir_ext(
1344 git_iterator **out,
1345 git_repository *repo,
1346 const char *repo_workdir,
1347 git_iterator_flag_t flags,
1348 const char *start,
1349 const char *end)
1350 {
1351 int error, precompose = 0;
1352 workdir_iterator *wi;
1353
1354 if (!repo_workdir) {
1355 if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1356 return GIT_EBAREREPO;
1357 repo_workdir = git_repository_workdir(repo);
1358 }
1359
1360 /* initialize as an fs iterator then do overrides */
1361 wi = git__calloc(1, sizeof(workdir_iterator));
1362 GITERR_CHECK_ALLOC(wi);
1363 ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1364
1365 wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1366 wi->fi.cb.free = workdir_iterator__free;
1367 wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1368 wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1369 wi->fi.update_entry_cb = workdir_iterator__update_entry;
1370
1371 if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1372 (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
1373 {
1374 git_iterator_free((git_iterator *)wi);
1375 return error;
1376 }
1377
1378 /* try to look up precompose and set flag if appropriate */
1379 if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
1380 giterr_clear();
1381 else if (precompose)
1382 wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
1383
1384 return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1385 }
1386
1387
1388 void git_iterator_free(git_iterator *iter)
1389 {
1390 if (iter == NULL)
1391 return;
1392
1393 iter->cb->free(iter);
1394
1395 git__free(iter->start);
1396 git__free(iter->end);
1397
1398 memset(iter, 0, sizeof(*iter));
1399
1400 git__free(iter);
1401 }
1402
1403 int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1404 {
1405 bool desire_ignore_case = (ignore_case != 0);
1406
1407 if (iterator__ignore_case(iter) == desire_ignore_case)
1408 return 0;
1409
1410 if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1411 if (desire_ignore_case)
1412 iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1413 else
1414 iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1415 } else {
1416 giterr_set(GITERR_INVALID,
1417 "Cannot currently set ignore case on non-empty iterators");
1418 return -1;
1419 }
1420
1421 return 0;
1422 }
1423
1424 git_index *git_iterator_get_index(git_iterator *iter)
1425 {
1426 if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1427 return ((index_iterator *)iter)->index;
1428 return NULL;
1429 }
1430
1431 int git_iterator_current_tree_entry(
1432 const git_tree_entry **tree_entry, git_iterator *iter)
1433 {
1434 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1435 *tree_entry = NULL;
1436 else {
1437 tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1438 *tree_entry = (tf->current < tf->n_entries) ?
1439 tf->entries[tf->current]->te : NULL;
1440 }
1441
1442 return 0;
1443 }
1444
1445 int git_iterator_current_parent_tree(
1446 const git_tree **tree_ptr,
1447 git_iterator *iter,
1448 const char *parent_path)
1449 {
1450 tree_iterator *ti = (tree_iterator *)iter;
1451 tree_iterator_frame *tf;
1452 const char *scan = parent_path;
1453 const git_tree_entry *te;
1454
1455 *tree_ptr = NULL;
1456
1457 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1458 return 0;
1459
1460 for (tf = ti->root; *scan; ) {
1461 if (!(tf = tf->down) ||
1462 tf->current >= tf->n_entries ||
1463 !(te = tf->entries[tf->current]->te) ||
1464 ti->strncomp(scan, te->filename, te->filename_len) != 0)
1465 return 0;
1466
1467 scan += te->filename_len;
1468 if (*scan == '/')
1469 scan++;
1470 }
1471
1472 *tree_ptr = tf->entries[tf->current]->tree;
1473 return 0;
1474 }
1475
1476 bool git_iterator_current_is_ignored(git_iterator *iter)
1477 {
1478 workdir_iterator *wi = (workdir_iterator *)iter;
1479
1480 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1481 return false;
1482
1483 if (wi->is_ignored != -1)
1484 return (bool)(wi->is_ignored != 0);
1485
1486 if (git_ignore__lookup(
1487 &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1488 wi->is_ignored = true;
1489
1490 return (bool)wi->is_ignored;
1491 }
1492
1493 int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
1494 {
1495 const git_index_entry *entry;
1496
1497 /* a "done" iterator is after every prefix */
1498 if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1499 return 1;
1500
1501 /* a NULL prefix is after any valid iterator */
1502 if (!path_prefix)
1503 return -1;
1504
1505 return iter->prefixcomp(entry->path, path_prefix);
1506 }
1507
1508 int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1509 {
1510 workdir_iterator *wi = (workdir_iterator *)iter;
1511
1512 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1513 *path = NULL;
1514 else
1515 *path = &wi->fi.path;
1516
1517 return 0;
1518 }