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