]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
Remove trace / add git_diff_perfdata struct + api
[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 "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.id, &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 git_vector entries;
648 git_vector_cmp entry_srch;
649 size_t current;
650 /* when not in autoexpand mode, use these to represent "tree" state */
651 git_buf partial;
652 size_t partial_pos;
653 char restore_terminator;
654 git_index_entry tree_entry;
655 } index_iterator;
656
657 static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
658 {
659 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
660
661 if (ie != NULL && iterator__past_end(ii, ie->path)) {
662 ii->current = git_vector_length(&ii->entries);
663 ie = NULL;
664 }
665
666 return ie;
667 }
668
669 static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
670 {
671 const git_index_entry *ie;
672
673 while ((ie = index_iterator__index_entry(ii)) != NULL &&
674 git_index_entry_stage(ie) != 0)
675 ii->current++;
676
677 return ie;
678 }
679
680 static void index_iterator__next_prefix_tree(index_iterator *ii)
681 {
682 const char *slash;
683
684 if (!iterator__include_trees(ii))
685 return;
686
687 slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
688
689 if (slash != NULL) {
690 ii->partial_pos = (slash - ii->partial.ptr) + 1;
691 ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
692 ii->partial.ptr[ii->partial_pos] = '\0';
693 } else {
694 ii->partial_pos = ii->partial.size;
695 }
696
697 if (index_iterator__index_entry(ii) == NULL)
698 ii->partial_pos = ii->partial.size;
699 }
700
701 static int index_iterator__first_prefix_tree(index_iterator *ii)
702 {
703 const git_index_entry *ie = index_iterator__skip_conflicts(ii);
704 const char *scan, *prior, *slash;
705
706 if (!ie || !iterator__include_trees(ii))
707 return 0;
708
709 /* find longest common prefix with prior index entry */
710 for (scan = slash = ie->path, prior = ii->partial.ptr;
711 *scan && *scan == *prior; ++scan, ++prior)
712 if (*scan == '/')
713 slash = scan;
714
715 if (git_buf_sets(&ii->partial, ie->path) < 0)
716 return -1;
717
718 ii->partial_pos = (slash - ie->path) + 1;
719 index_iterator__next_prefix_tree(ii);
720
721 return 0;
722 }
723
724 #define index_iterator__at_tree(I) \
725 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
726
727 static int index_iterator__current(
728 const git_index_entry **entry, git_iterator *self)
729 {
730 index_iterator *ii = (index_iterator *)self;
731 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
732
733 if (ie != NULL && index_iterator__at_tree(ii)) {
734 ii->tree_entry.path = ii->partial.ptr;
735 ie = &ii->tree_entry;
736 }
737
738 if (entry)
739 *entry = ie;
740
741 ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
742
743 return (ie != NULL) ? 0 : GIT_ITEROVER;
744 }
745
746 static int index_iterator__at_end(git_iterator *self)
747 {
748 index_iterator *ii = (index_iterator *)self;
749 return (ii->current >= git_vector_length(&ii->entries));
750 }
751
752 static int index_iterator__advance(
753 const git_index_entry **entry, git_iterator *self)
754 {
755 index_iterator *ii = (index_iterator *)self;
756 size_t entrycount = git_vector_length(&ii->entries);
757 const git_index_entry *ie;
758
759 if (!iterator__has_been_accessed(ii))
760 return index_iterator__current(entry, self);
761
762 if (index_iterator__at_tree(ii)) {
763 if (iterator__do_autoexpand(ii)) {
764 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
765 index_iterator__next_prefix_tree(ii);
766 } else {
767 /* advance to sibling tree (i.e. find entry with new prefix) */
768 while (ii->current < entrycount) {
769 ii->current++;
770
771 if (!(ie = git_vector_get(&ii->entries, ii->current)) ||
772 ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
773 break;
774 }
775
776 if (index_iterator__first_prefix_tree(ii) < 0)
777 return -1;
778 }
779 } else {
780 if (ii->current < entrycount)
781 ii->current++;
782
783 if (index_iterator__first_prefix_tree(ii) < 0)
784 return -1;
785 }
786
787 return index_iterator__current(entry, self);
788 }
789
790 static int index_iterator__advance_into(
791 const git_index_entry **entry, git_iterator *self)
792 {
793 index_iterator *ii = (index_iterator *)self;
794 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
795
796 if (ie != NULL && index_iterator__at_tree(ii)) {
797 if (ii->restore_terminator)
798 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
799 index_iterator__next_prefix_tree(ii);
800 }
801
802 return index_iterator__current(entry, self);
803 }
804
805 static int index_iterator__seek(git_iterator *self, const char *prefix)
806 {
807 GIT_UNUSED(self); GIT_UNUSED(prefix);
808 return -1;
809 }
810
811 static int index_iterator__reset(
812 git_iterator *self, const char *start, const char *end)
813 {
814 index_iterator *ii = (index_iterator *)self;
815 const git_index_entry *ie;
816
817 if (iterator__reset_range(self, start, end) < 0)
818 return -1;
819
820 ii->current = 0;
821
822 if (ii->base.start)
823 git_index_snapshot_find(
824 &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
825
826 if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
827 return 0;
828
829 if (git_buf_sets(&ii->partial, ie->path) < 0)
830 return -1;
831
832 ii->partial_pos = 0;
833
834 if (ii->base.start) {
835 size_t startlen = strlen(ii->base.start);
836
837 ii->partial_pos = (startlen > ii->partial.size) ?
838 ii->partial.size : startlen;
839 }
840
841 index_iterator__next_prefix_tree(ii);
842
843 return 0;
844 }
845
846 static void index_iterator__free(git_iterator *self)
847 {
848 index_iterator *ii = (index_iterator *)self;
849 git_index_snapshot_release(&ii->entries, ii->index);
850 ii->index = NULL;
851 git_buf_free(&ii->partial);
852 }
853
854 int git_iterator_for_index(
855 git_iterator **iter,
856 git_index *index,
857 git_iterator_flag_t flags,
858 const char *start,
859 const char *end)
860 {
861 int error = 0;
862 index_iterator *ii = git__calloc(1, sizeof(index_iterator));
863 GITERR_CHECK_ALLOC(ii);
864
865 if ((error = git_index_snapshot_new(&ii->entries, index)) < 0) {
866 git__free(ii);
867 return error;
868 }
869 ii->index = index;
870
871 ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
872
873 if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) {
874 git_iterator_free((git_iterator *)ii);
875 return error;
876 }
877
878 ii->entry_srch = iterator__ignore_case(ii) ?
879 git_index_entry_isrch : git_index_entry_srch;
880
881 git_vector_set_cmp(&ii->entries, iterator__ignore_case(ii) ?
882 git_index_entry_icmp : git_index_entry_cmp);
883 git_vector_sort(&ii->entries);
884
885 git_buf_init(&ii->partial, 0);
886 ii->tree_entry.mode = GIT_FILEMODE_TREE;
887
888 index_iterator__reset((git_iterator *)ii, NULL, NULL);
889
890 *iter = (git_iterator *)ii;
891 return 0;
892 }
893
894
895 typedef struct fs_iterator_frame fs_iterator_frame;
896 struct fs_iterator_frame {
897 fs_iterator_frame *next;
898 git_vector entries;
899 size_t index;
900 };
901
902 typedef struct fs_iterator fs_iterator;
903 struct fs_iterator {
904 git_iterator base;
905 git_iterator_callbacks cb;
906 fs_iterator_frame *stack;
907 git_index_entry entry;
908 git_buf path;
909 size_t root_len;
910 uint32_t dirload_flags;
911 int depth;
912
913 int (*enter_dir_cb)(fs_iterator *self);
914 int (*leave_dir_cb)(fs_iterator *self);
915 int (*update_entry_cb)(fs_iterator *self);
916 };
917
918 #define FS_MAX_DEPTH 100
919
920 static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
921 {
922 fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
923 git_vector_cmp entry_compare = CASESELECT(
924 iterator__ignore_case(fi),
925 git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
926
927 if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
928 git__free(ff);
929 ff = NULL;
930 }
931
932 return ff;
933 }
934
935 static void fs_iterator__free_frame(fs_iterator_frame *ff)
936 {
937 git_vector_free_deep(&ff->entries);
938 git__free(ff);
939 }
940
941 static void fs_iterator__pop_frame(
942 fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
943 {
944 if (fi && fi->stack == ff) {
945 if (!ff->next && !pop_last) {
946 memset(&fi->entry, 0, sizeof(fi->entry));
947 return;
948 }
949
950 if (fi->leave_dir_cb)
951 (void)fi->leave_dir_cb(fi);
952
953 fi->stack = ff->next;
954 fi->depth--;
955 }
956
957 fs_iterator__free_frame(ff);
958 }
959
960 static int fs_iterator__update_entry(fs_iterator *fi);
961 static int fs_iterator__advance_over(
962 const git_index_entry **entry, git_iterator *self);
963
964 static int fs_iterator__entry_cmp(const void *i, const void *item)
965 {
966 const fs_iterator *fi = (const fs_iterator *)i;
967 const git_path_with_stat *ps = item;
968 return fi->base.prefixcomp(fi->base.start, ps->path);
969 }
970
971 static void fs_iterator__seek_frame_start(
972 fs_iterator *fi, fs_iterator_frame *ff)
973 {
974 if (!ff)
975 return;
976
977 if (fi->base.start)
978 git_vector_bsearch2(
979 &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
980 else
981 ff->index = 0;
982 }
983
984 static int fs_iterator__expand_dir(fs_iterator *fi)
985 {
986 int error;
987 fs_iterator_frame *ff;
988
989 if (fi->depth > FS_MAX_DEPTH) {
990 giterr_set(GITERR_REPOSITORY,
991 "Directory nesting is too deep (%d)", fi->depth);
992 return -1;
993 }
994
995 ff = fs_iterator__alloc_frame(fi);
996 GITERR_CHECK_ALLOC(ff);
997
998 error = git_path_dirload_with_stat(
999 fi->path.ptr, fi->root_len, fi->dirload_flags,
1000 fi->base.start, fi->base.end, &ff->entries);
1001
1002 if (error < 0) {
1003 git_error_state last_error = { 0 };
1004 giterr_capture(&last_error, error);
1005
1006 /* these callbacks may clear the error message */
1007 fs_iterator__free_frame(ff);
1008 fs_iterator__advance_over(NULL, (git_iterator *)fi);
1009 /* next time return value we skipped to */
1010 fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1011
1012 return giterr_restore(&last_error);
1013 }
1014
1015 if (ff->entries.length == 0) {
1016 fs_iterator__free_frame(ff);
1017 return GIT_ENOTFOUND;
1018 }
1019 fi->base.stat_calls += ff->entries.length;
1020
1021 fs_iterator__seek_frame_start(fi, ff);
1022
1023 ff->next = fi->stack;
1024 fi->stack = ff;
1025 fi->depth++;
1026
1027 if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1028 return error;
1029
1030 return fs_iterator__update_entry(fi);
1031 }
1032
1033 static int fs_iterator__current(
1034 const git_index_entry **entry, git_iterator *self)
1035 {
1036 fs_iterator *fi = (fs_iterator *)self;
1037 const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1038
1039 if (entry)
1040 *entry = fe;
1041
1042 fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1043
1044 return (fe != NULL) ? 0 : GIT_ITEROVER;
1045 }
1046
1047 static int fs_iterator__at_end(git_iterator *self)
1048 {
1049 return (((fs_iterator *)self)->entry.path == NULL);
1050 }
1051
1052 static int fs_iterator__advance_into(
1053 const git_index_entry **entry, git_iterator *iter)
1054 {
1055 int error = 0;
1056 fs_iterator *fi = (fs_iterator *)iter;
1057
1058 iterator__clear_entry(entry);
1059
1060 /* Allow you to explicitly advance into a commit/submodule (as well as a
1061 * tree) to avoid cases where an entry is mislabeled as a submodule in
1062 * the working directory. The fs iterator will never have COMMMIT
1063 * entries on it's own, but a wrapper might add them.
1064 */
1065 if (fi->entry.path != NULL &&
1066 (fi->entry.mode == GIT_FILEMODE_TREE ||
1067 fi->entry.mode == GIT_FILEMODE_COMMIT))
1068 /* returns GIT_ENOTFOUND if the directory is empty */
1069 error = fs_iterator__expand_dir(fi);
1070
1071 if (!error && entry)
1072 error = fs_iterator__current(entry, iter);
1073
1074 if (!error && !fi->entry.path)
1075 error = GIT_ITEROVER;
1076
1077 return error;
1078 }
1079
1080 static int fs_iterator__advance_over(
1081 const git_index_entry **entry, git_iterator *self)
1082 {
1083 int error = 0;
1084 fs_iterator *fi = (fs_iterator *)self;
1085 fs_iterator_frame *ff;
1086 git_path_with_stat *next;
1087
1088 if (entry != NULL)
1089 *entry = NULL;
1090
1091 while (fi->entry.path != NULL) {
1092 ff = fi->stack;
1093 next = git_vector_get(&ff->entries, ++ff->index);
1094
1095 if (next != NULL)
1096 break;
1097
1098 fs_iterator__pop_frame(fi, ff, false);
1099 }
1100
1101 error = fs_iterator__update_entry(fi);
1102
1103 if (!error && entry != NULL)
1104 error = fs_iterator__current(entry, self);
1105
1106 return error;
1107 }
1108
1109 static int fs_iterator__advance(
1110 const git_index_entry **entry, git_iterator *self)
1111 {
1112 fs_iterator *fi = (fs_iterator *)self;
1113
1114 if (!iterator__has_been_accessed(fi))
1115 return fs_iterator__current(entry, self);
1116
1117 /* given include_trees & autoexpand, we might have to go into a tree */
1118 if (iterator__do_autoexpand(fi) &&
1119 fi->entry.path != NULL &&
1120 fi->entry.mode == GIT_FILEMODE_TREE)
1121 {
1122 int error = fs_iterator__advance_into(entry, self);
1123 if (error != GIT_ENOTFOUND)
1124 return error;
1125 /* continue silently past empty directories if autoexpanding */
1126 giterr_clear();
1127 }
1128
1129 return fs_iterator__advance_over(entry, self);
1130 }
1131
1132 static int fs_iterator__seek(git_iterator *self, const char *prefix)
1133 {
1134 GIT_UNUSED(self);
1135 GIT_UNUSED(prefix);
1136 /* pop stack until matching prefix */
1137 /* find prefix item in current frame */
1138 /* push subdirectories as deep as possible while matching */
1139 return 0;
1140 }
1141
1142 static int fs_iterator__reset(
1143 git_iterator *self, const char *start, const char *end)
1144 {
1145 int error;
1146 fs_iterator *fi = (fs_iterator *)self;
1147
1148 while (fi->stack != NULL && fi->stack->next != NULL)
1149 fs_iterator__pop_frame(fi, fi->stack, false);
1150 fi->depth = 0;
1151
1152 if ((error = iterator__reset_range(self, start, end)) < 0)
1153 return error;
1154
1155 fs_iterator__seek_frame_start(fi, fi->stack);
1156
1157 error = fs_iterator__update_entry(fi);
1158 if (error == GIT_ITEROVER)
1159 error = 0;
1160
1161 return error;
1162 }
1163
1164 static void fs_iterator__free(git_iterator *self)
1165 {
1166 fs_iterator *fi = (fs_iterator *)self;
1167
1168 while (fi->stack != NULL)
1169 fs_iterator__pop_frame(fi, fi->stack, true);
1170
1171 git_buf_free(&fi->path);
1172 }
1173
1174 static int fs_iterator__update_entry(fs_iterator *fi)
1175 {
1176 git_path_with_stat *ps;
1177
1178 memset(&fi->entry, 0, sizeof(fi->entry));
1179
1180 if (!fi->stack)
1181 return GIT_ITEROVER;
1182
1183 ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1184 if (!ps)
1185 return GIT_ITEROVER;
1186
1187 git_buf_truncate(&fi->path, fi->root_len);
1188 if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1189 return -1;
1190
1191 if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1192 return GIT_ITEROVER;
1193
1194 fi->entry.path = ps->path;
1195 git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
1196
1197 /* need different mode here to keep directories during iteration */
1198 fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1199
1200 /* allow wrapper to check/update the entry (can force skip) */
1201 if (fi->update_entry_cb &&
1202 fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1203 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1204
1205 /* if this is a tree and trees aren't included, then skip */
1206 if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1207 int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1208 if (error != GIT_ENOTFOUND)
1209 return error;
1210 giterr_clear();
1211 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1212 }
1213
1214 return 0;
1215 }
1216
1217 static int fs_iterator__initialize(
1218 git_iterator **out, fs_iterator *fi, const char *root)
1219 {
1220 int error;
1221
1222 if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1223 git__free(fi);
1224 return -1;
1225 }
1226 fi->root_len = fi->path.size;
1227
1228 fi->dirload_flags =
1229 (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1230 (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
1231 GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1232
1233 if ((error = fs_iterator__expand_dir(fi)) < 0) {
1234 if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1235 giterr_clear();
1236 error = 0;
1237 } else {
1238 git_iterator_free((git_iterator *)fi);
1239 fi = NULL;
1240 }
1241 }
1242
1243 *out = (git_iterator *)fi;
1244 return error;
1245 }
1246
1247 int git_iterator_for_filesystem(
1248 git_iterator **out,
1249 const char *root,
1250 git_iterator_flag_t flags,
1251 const char *start,
1252 const char *end)
1253 {
1254 fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1255 GITERR_CHECK_ALLOC(fi);
1256
1257 ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1258
1259 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1260 fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1261
1262 return fs_iterator__initialize(out, fi, root);
1263 }
1264
1265
1266 typedef struct {
1267 fs_iterator fi;
1268 git_ignores ignores;
1269 int is_ignored;
1270 } workdir_iterator;
1271
1272 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1273 {
1274 size_t len;
1275
1276 if (!path || (len = path->size) < 4)
1277 return false;
1278
1279 if (path->ptr[len - 1] == '/')
1280 len--;
1281
1282 if (tolower(path->ptr[len - 1]) != 't' ||
1283 tolower(path->ptr[len - 2]) != 'i' ||
1284 tolower(path->ptr[len - 3]) != 'g' ||
1285 tolower(path->ptr[len - 4]) != '.')
1286 return false;
1287
1288 return (len == 4 || path->ptr[len - 5] == '/');
1289 }
1290
1291 static int workdir_iterator__enter_dir(fs_iterator *fi)
1292 {
1293 fs_iterator_frame *ff = fi->stack;
1294 size_t pos;
1295 git_path_with_stat *entry;
1296 bool found_submodules = false;
1297
1298 /* only push new ignores if this is not top level directory */
1299 if (ff->next != NULL) {
1300 workdir_iterator *wi = (workdir_iterator *)fi;
1301 ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1302
1303 (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1304 }
1305
1306 /* convert submodules to GITLINK and remove trailing slashes */
1307 git_vector_foreach(&ff->entries, pos, entry) {
1308 if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
1309 continue;
1310
1311 if (git_submodule__is_submodule(fi->base.repo, entry->path)) {
1312 entry->st.st_mode = GIT_FILEMODE_COMMIT;
1313 entry->path_len--;
1314 entry->path[entry->path_len] = '\0';
1315 found_submodules = true;
1316 }
1317 }
1318
1319 /* if we renamed submodules, re-sort and re-seek to start */
1320 if (found_submodules) {
1321 git_vector_set_sorted(&ff->entries, 0);
1322 git_vector_sort(&ff->entries);
1323 fs_iterator__seek_frame_start(fi, ff);
1324 }
1325
1326 return 0;
1327 }
1328
1329 static int workdir_iterator__leave_dir(fs_iterator *fi)
1330 {
1331 workdir_iterator *wi = (workdir_iterator *)fi;
1332 git_ignore__pop_dir(&wi->ignores);
1333 return 0;
1334 }
1335
1336 static int workdir_iterator__update_entry(fs_iterator *fi)
1337 {
1338 workdir_iterator *wi = (workdir_iterator *)fi;
1339
1340 /* skip over .git entries */
1341 if (workdir_path_is_dotgit(&fi->path))
1342 return GIT_ENOTFOUND;
1343
1344 /* reset is_ignored since we haven't checked yet */
1345 wi->is_ignored = -1;
1346
1347 return 0;
1348 }
1349
1350 static void workdir_iterator__free(git_iterator *self)
1351 {
1352 workdir_iterator *wi = (workdir_iterator *)self;
1353 fs_iterator__free(self);
1354 git_ignore__free(&wi->ignores);
1355 }
1356
1357 int git_iterator_for_workdir_ext(
1358 git_iterator **out,
1359 git_repository *repo,
1360 const char *repo_workdir,
1361 git_iterator_flag_t flags,
1362 const char *start,
1363 const char *end)
1364 {
1365 int error, precompose = 0;
1366 workdir_iterator *wi;
1367
1368 if (!repo_workdir) {
1369 if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1370 return GIT_EBAREREPO;
1371 repo_workdir = git_repository_workdir(repo);
1372 }
1373
1374 /* initialize as an fs iterator then do overrides */
1375 wi = git__calloc(1, sizeof(workdir_iterator));
1376 GITERR_CHECK_ALLOC(wi);
1377 ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1378
1379 wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1380 wi->fi.cb.free = workdir_iterator__free;
1381 wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1382 wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1383 wi->fi.update_entry_cb = workdir_iterator__update_entry;
1384
1385 if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1386 (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
1387 {
1388 git_iterator_free((git_iterator *)wi);
1389 return error;
1390 }
1391
1392 /* try to look up precompose and set flag if appropriate */
1393 if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
1394 giterr_clear();
1395 else if (precompose)
1396 wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
1397
1398 return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1399 }
1400
1401
1402 void git_iterator_free(git_iterator *iter)
1403 {
1404 if (iter == NULL)
1405 return;
1406
1407 iter->cb->free(iter);
1408
1409 git__free(iter->start);
1410 git__free(iter->end);
1411
1412 memset(iter, 0, sizeof(*iter));
1413
1414 git__free(iter);
1415 }
1416
1417 int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1418 {
1419 bool desire_ignore_case = (ignore_case != 0);
1420
1421 if (iterator__ignore_case(iter) == desire_ignore_case)
1422 return 0;
1423
1424 if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1425 if (desire_ignore_case)
1426 iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1427 else
1428 iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1429 } else {
1430 giterr_set(GITERR_INVALID,
1431 "Cannot currently set ignore case on non-empty iterators");
1432 return -1;
1433 }
1434
1435 return 0;
1436 }
1437
1438 git_index *git_iterator_get_index(git_iterator *iter)
1439 {
1440 if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1441 return ((index_iterator *)iter)->index;
1442 return NULL;
1443 }
1444
1445 int git_iterator_current_tree_entry(
1446 const git_tree_entry **tree_entry, git_iterator *iter)
1447 {
1448 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1449 *tree_entry = NULL;
1450 else {
1451 tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1452 *tree_entry = (tf->current < tf->n_entries) ?
1453 tf->entries[tf->current]->te : NULL;
1454 }
1455
1456 return 0;
1457 }
1458
1459 int git_iterator_current_parent_tree(
1460 const git_tree **tree_ptr,
1461 git_iterator *iter,
1462 const char *parent_path)
1463 {
1464 tree_iterator *ti = (tree_iterator *)iter;
1465 tree_iterator_frame *tf;
1466 const char *scan = parent_path;
1467 const git_tree_entry *te;
1468
1469 *tree_ptr = NULL;
1470
1471 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1472 return 0;
1473
1474 for (tf = ti->root; *scan; ) {
1475 if (!(tf = tf->down) ||
1476 tf->current >= tf->n_entries ||
1477 !(te = tf->entries[tf->current]->te) ||
1478 ti->strncomp(scan, te->filename, te->filename_len) != 0)
1479 return 0;
1480
1481 scan += te->filename_len;
1482 if (*scan == '/')
1483 scan++;
1484 }
1485
1486 *tree_ptr = tf->entries[tf->current]->tree;
1487 return 0;
1488 }
1489
1490 bool git_iterator_current_is_ignored(git_iterator *iter)
1491 {
1492 workdir_iterator *wi = (workdir_iterator *)iter;
1493
1494 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1495 return false;
1496
1497 if (wi->is_ignored != -1)
1498 return (bool)(wi->is_ignored != 0);
1499
1500 if (git_ignore__lookup(
1501 &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1502 wi->is_ignored = true;
1503
1504 return (bool)wi->is_ignored;
1505 }
1506
1507 int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
1508 {
1509 const git_index_entry *entry;
1510
1511 /* a "done" iterator is after every prefix */
1512 if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1513 return 1;
1514
1515 /* a NULL prefix is after any valid iterator */
1516 if (!path_prefix)
1517 return -1;
1518
1519 return iter->prefixcomp(entry->path, path_prefix);
1520 }
1521
1522 int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1523 {
1524 workdir_iterator *wi = (workdir_iterator *)iter;
1525
1526 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1527 *path = NULL;
1528 else
1529 *path = &wi->fi.path;
1530
1531 return 0;
1532 }
1533
1534 int git_iterator_advance_over_with_status(
1535 const git_index_entry **entryptr,
1536 git_iterator_status_t *status,
1537 git_iterator *iter)
1538 {
1539 int error = 0;
1540 workdir_iterator *wi = (workdir_iterator *)iter;
1541 char *base = NULL;
1542 const git_index_entry *entry;
1543
1544 *status = GIT_ITERATOR_STATUS_NORMAL;
1545
1546 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1547 return git_iterator_advance(entryptr, iter);
1548 if ((error = git_iterator_current(&entry, iter)) < 0)
1549 return error;
1550
1551 if (!S_ISDIR(entry->mode)) {
1552 if (git_ignore__lookup(
1553 &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1554 wi->is_ignored = true;
1555 if (wi->is_ignored)
1556 *status = GIT_ITERATOR_STATUS_IGNORED;
1557 return git_iterator_advance(entryptr, iter);
1558 }
1559
1560 *status = GIT_ITERATOR_STATUS_EMPTY;
1561
1562 base = git__strdup(entry->path);
1563 GITERR_CHECK_ALLOC(base);
1564
1565 /* scan inside directory looking for a non-ignored item */
1566 while (entry && !iter->prefixcomp(entry->path, base)) {
1567 if (git_ignore__lookup(
1568 &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1569 wi->is_ignored = true;
1570
1571 /* if we found an explicitly ignored item, then update from
1572 * EMPTY to IGNORED
1573 */
1574 if (wi->is_ignored)
1575 *status = GIT_ITERATOR_STATUS_IGNORED;
1576 else if (S_ISDIR(entry->mode)) {
1577 error = git_iterator_advance_into(&entry, iter);
1578
1579 if (!error)
1580 continue;
1581 else if (error == GIT_ENOTFOUND) {
1582 error = 0;
1583 wi->is_ignored = true; /* mark empty directories as ignored */
1584 } else
1585 break; /* real error, stop here */
1586 } else {
1587 /* we found a non-ignored item, treat parent as untracked */
1588 *status = GIT_ITERATOR_STATUS_NORMAL;
1589 break;
1590 }
1591
1592 if ((error = git_iterator_advance(&entry, iter)) < 0)
1593 break;
1594 }
1595
1596 /* wrap up scan back to base directory */
1597 while (entry && !iter->prefixcomp(entry->path, base))
1598 if ((error = git_iterator_advance(&entry, iter)) < 0)
1599 break;
1600
1601 *entryptr = entry;
1602 git__free(base);
1603
1604 return error;
1605 }
1606