]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
status should ignore conflicts entries in the index
[libgit2.git] / src / iterator.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "iterator.h"
9 #include "tree.h"
10 #include "ignore.h"
11 #include "buffer.h"
12 #include "git2/submodule.h"
13 #include <ctype.h>
14
15 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
16 (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
17 GITERR_CHECK_ALLOC(P); \
18 (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \
19 (P)->base.start = start ? git__strdup(start) : NULL; \
20 (P)->base.end = end ? git__strdup(end) : NULL; \
21 (P)->base.ignore_case = 0; \
22 (P)->base.current = NAME_LC ## _iterator__current; \
23 (P)->base.at_end = NAME_LC ## _iterator__at_end; \
24 (P)->base.advance = NAME_LC ## _iterator__advance; \
25 (P)->base.seek = NAME_LC ## _iterator__seek; \
26 (P)->base.reset = NAME_LC ## _iterator__reset; \
27 (P)->base.free = NAME_LC ## _iterator__free; \
28 if ((start && !(P)->base.start) || (end && !(P)->base.end)) \
29 return -1; \
30 } while (0)
31
32
33 static int empty_iterator__no_item(
34 git_iterator *iter, const git_index_entry **entry)
35 {
36 GIT_UNUSED(iter);
37 *entry = NULL;
38 return 0;
39 }
40
41 static int empty_iterator__at_end(git_iterator *iter)
42 {
43 GIT_UNUSED(iter);
44 return 1;
45 }
46
47 static int empty_iterator__noop(git_iterator *iter)
48 {
49 GIT_UNUSED(iter);
50 return 0;
51 }
52
53 static int empty_iterator__seek(git_iterator *iter, const char *prefix)
54 {
55 GIT_UNUSED(iter);
56 GIT_UNUSED(prefix);
57 return -1;
58 }
59
60 static void empty_iterator__free(git_iterator *iter)
61 {
62 GIT_UNUSED(iter);
63 }
64
65 int git_iterator_for_nothing(git_iterator **iter)
66 {
67 git_iterator *i = git__calloc(1, sizeof(git_iterator));
68 GITERR_CHECK_ALLOC(i);
69
70 i->type = GIT_ITERATOR_EMPTY;
71 i->current = empty_iterator__no_item;
72 i->at_end = empty_iterator__at_end;
73 i->advance = empty_iterator__no_item;
74 i->seek = empty_iterator__seek;
75 i->reset = empty_iterator__noop;
76 i->free = empty_iterator__free;
77
78 *iter = i;
79
80 return 0;
81 }
82
83
84 typedef struct tree_iterator_frame tree_iterator_frame;
85 struct tree_iterator_frame {
86 tree_iterator_frame *next, *prev;
87 git_tree *tree;
88 char *start;
89 size_t index;
90 };
91
92 typedef struct {
93 git_iterator base;
94 git_repository *repo;
95 tree_iterator_frame *stack, *tail;
96 git_index_entry entry;
97 git_buf path;
98 bool path_has_filename;
99 } tree_iterator;
100
101 static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
102 {
103 return (ti->stack == NULL) ? NULL :
104 git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
105 }
106
107 static char *tree_iterator__current_filename(
108 tree_iterator *ti, const git_tree_entry *te)
109 {
110 if (!ti->path_has_filename) {
111 if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
112 return NULL;
113 ti->path_has_filename = true;
114 }
115
116 return ti->path.ptr;
117 }
118
119 static void tree_iterator__pop_frame(tree_iterator *ti)
120 {
121 tree_iterator_frame *tf = ti->stack;
122 ti->stack = tf->next;
123 if (ti->stack != NULL) {
124 git_tree_free(tf->tree); /* don't free the initial tree */
125 ti->stack->prev = NULL; /* disconnect prev */
126 }
127 git__free(tf);
128 }
129
130 static int tree_iterator__to_end(tree_iterator *ti)
131 {
132 while (ti->stack && ti->stack->next)
133 tree_iterator__pop_frame(ti);
134
135 if (ti->stack)
136 ti->stack->index = git_tree_entrycount(ti->stack->tree);
137
138 return 0;
139 }
140
141 static int tree_iterator__current(
142 git_iterator *self, const git_index_entry **entry)
143 {
144 tree_iterator *ti = (tree_iterator *)self;
145 const git_tree_entry *te = tree_iterator__tree_entry(ti);
146
147 if (entry)
148 *entry = NULL;
149
150 if (te == NULL)
151 return 0;
152
153 ti->entry.mode = te->attr;
154 git_oid_cpy(&ti->entry.oid, &te->oid);
155
156 ti->entry.path = tree_iterator__current_filename(ti, te);
157 if (ti->entry.path == NULL)
158 return -1;
159
160 if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
161 return tree_iterator__to_end(ti);
162
163 if (entry)
164 *entry = &ti->entry;
165
166 return 0;
167 }
168
169 static int tree_iterator__at_end(git_iterator *self)
170 {
171 return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
172 }
173
174 static tree_iterator_frame *tree_iterator__alloc_frame(
175 git_tree *tree, char *start)
176 {
177 tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
178 if (!tf)
179 return NULL;
180
181 tf->tree = tree;
182
183 if (start && *start) {
184 tf->start = start;
185 tf->index = git_tree__prefix_position(tree, start);
186 }
187
188 return tf;
189 }
190
191 static int tree_iterator__expand_tree(tree_iterator *ti)
192 {
193 int error;
194 git_tree *subtree;
195 const git_tree_entry *te = tree_iterator__tree_entry(ti);
196 tree_iterator_frame *tf;
197 char *relpath;
198
199 while (te != NULL && git_tree_entry__is_tree(te)) {
200 if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
201 return -1;
202
203 /* check that we have not passed the range end */
204 if (ti->base.end != NULL &&
205 git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
206 return tree_iterator__to_end(ti);
207
208 if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
209 return error;
210
211 relpath = NULL;
212
213 /* apply range start to new frame if relevant */
214 if (ti->stack->start &&
215 git__prefixcmp(ti->stack->start, te->filename) == 0)
216 {
217 size_t namelen = strlen(te->filename);
218 if (ti->stack->start[namelen] == '/')
219 relpath = ti->stack->start + namelen + 1;
220 }
221
222 if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
223 return -1;
224
225 tf->next = ti->stack;
226 ti->stack = tf;
227 tf->next->prev = tf;
228
229 te = tree_iterator__tree_entry(ti);
230 }
231
232 return 0;
233 }
234
235 static int tree_iterator__advance(
236 git_iterator *self, const git_index_entry **entry)
237 {
238 int error = 0;
239 tree_iterator *ti = (tree_iterator *)self;
240 const git_tree_entry *te = NULL;
241
242 if (entry != NULL)
243 *entry = NULL;
244
245 if (ti->path_has_filename) {
246 git_buf_rtruncate_at_char(&ti->path, '/');
247 ti->path_has_filename = false;
248 }
249
250 while (ti->stack != NULL) {
251 te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
252 if (te != NULL)
253 break;
254
255 tree_iterator__pop_frame(ti);
256
257 git_buf_rtruncate_at_char(&ti->path, '/');
258 }
259
260 if (te && git_tree_entry__is_tree(te))
261 error = tree_iterator__expand_tree(ti);
262
263 if (!error)
264 error = tree_iterator__current(self, entry);
265
266 return error;
267 }
268
269 static int tree_iterator__seek(git_iterator *self, const char *prefix)
270 {
271 GIT_UNUSED(self);
272 GIT_UNUSED(prefix);
273 /* pop stack until matches prefix */
274 /* seek item in current frame matching prefix */
275 /* push stack which matches prefix */
276 return -1;
277 }
278
279 static void tree_iterator__free(git_iterator *self)
280 {
281 tree_iterator *ti = (tree_iterator *)self;
282 while (ti->stack != NULL)
283 tree_iterator__pop_frame(ti);
284 git_buf_free(&ti->path);
285 }
286
287 static int tree_iterator__reset(git_iterator *self)
288 {
289 tree_iterator *ti = (tree_iterator *)self;
290
291 while (ti->stack && ti->stack->next)
292 tree_iterator__pop_frame(ti);
293
294 if (ti->stack)
295 ti->stack->index =
296 git_tree__prefix_position(ti->stack->tree, ti->base.start);
297
298 git_buf_clear(&ti->path);
299
300 return tree_iterator__expand_tree(ti);
301 }
302
303 int git_iterator_for_tree_range(
304 git_iterator **iter,
305 git_repository *repo,
306 git_tree *tree,
307 const char *start,
308 const char *end)
309 {
310 int error;
311 tree_iterator *ti;
312
313 if (tree == NULL)
314 return git_iterator_for_nothing(iter);
315
316 ITERATOR_BASE_INIT(ti, tree, TREE);
317
318 ti->repo = repo;
319 ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start);
320
321 if ((error = tree_iterator__expand_tree(ti)) < 0)
322 git_iterator_free((git_iterator *)ti);
323 else
324 *iter = (git_iterator *)ti;
325
326 return error;
327 }
328
329
330 typedef struct {
331 git_iterator base;
332 git_index *index;
333 size_t current;
334 bool free_index;
335 } index_iterator;
336
337 static int index_iterator__current(
338 git_iterator *self, const git_index_entry **entry)
339 {
340 index_iterator *ii = (index_iterator *)self;
341 const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
342
343 if (entry)
344 *entry = ie;
345
346 return 0;
347 }
348
349 static int index_iterator__at_end(git_iterator *self)
350 {
351 index_iterator *ii = (index_iterator *)self;
352 return (ii->current >= git_index_entrycount(ii->index));
353 }
354
355 static void index_iterator__skip_conflicts(
356 index_iterator *ii)
357 {
358 size_t entrycount = git_index_entrycount(ii->index);
359 const git_index_entry *ie;
360
361 while (ii->current < entrycount) {
362 ie = git_index_get_byindex(ii->index, ii->current);
363
364 if (ie == NULL ||
365 (ii->base.end != NULL &&
366 ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)) {
367 ii->current = entrycount;
368 break;
369 }
370
371 if (git_index_entry_stage(ie) == 0)
372 break;
373
374 ii->current++;
375 }
376 }
377
378 static int index_iterator__advance(
379 git_iterator *self, const git_index_entry **entry)
380 {
381 index_iterator *ii = (index_iterator *)self;
382
383 if (ii->current < git_index_entrycount(ii->index))
384 ii->current++;
385
386 index_iterator__skip_conflicts(ii);
387
388 return index_iterator__current(self, entry);
389 }
390
391 static int index_iterator__seek(git_iterator *self, const char *prefix)
392 {
393 GIT_UNUSED(self);
394 GIT_UNUSED(prefix);
395 /* find last item before prefix */
396 return -1;
397 }
398
399 static int index_iterator__reset(git_iterator *self)
400 {
401 index_iterator *ii = (index_iterator *)self;
402 ii->current = ii->base.start ?
403 git_index__prefix_position(ii->index, ii->base.start) : 0;
404 index_iterator__skip_conflicts(ii);
405 return 0;
406 }
407
408 static void index_iterator__free(git_iterator *self)
409 {
410 index_iterator *ii = (index_iterator *)self;
411 if (ii->free_index)
412 git_index_free(ii->index);
413 ii->index = NULL;
414 }
415
416 int git_iterator_for_index_range(
417 git_iterator **iter,
418 git_index *index,
419 const char *start,
420 const char *end)
421 {
422 index_iterator *ii;
423
424 ITERATOR_BASE_INIT(ii, index, INDEX);
425
426 ii->index = index;
427 ii->base.ignore_case = ii->index->ignore_case;
428
429 index_iterator__reset((git_iterator *)ii);
430
431 *iter = (git_iterator *)ii;
432
433 return 0;
434 }
435
436 int git_iterator_for_repo_index_range(
437 git_iterator **iter,
438 git_repository *repo,
439 const char *start,
440 const char *end)
441 {
442 int error;
443 git_index *index;
444
445 if ((error = git_repository_index(&index, repo)) < 0)
446 return error;
447
448 if (!(error = git_iterator_for_index_range(iter, index, start, end)))
449 ((index_iterator *)(*iter))->free_index = true;
450
451 return error;
452 }
453
454 typedef struct workdir_iterator_frame workdir_iterator_frame;
455 struct workdir_iterator_frame {
456 workdir_iterator_frame *next;
457 git_vector entries;
458 size_t index;
459 char *start;
460 };
461
462 typedef struct {
463 git_iterator base;
464 git_repository *repo;
465 size_t root_len;
466 workdir_iterator_frame *stack;
467 git_ignores ignores;
468 git_index_entry entry;
469 git_buf path;
470 int is_ignored;
471 } workdir_iterator;
472
473 static int git_path_with_stat_cmp_case(const void *a, const void *b)
474 {
475 const git_path_with_stat *path_with_stat_a = a;
476 const git_path_with_stat *path_with_stat_b = b;
477
478 return strcmp(path_with_stat_a->path, path_with_stat_b->path);
479 }
480
481 static int git_path_with_stat_cmp_icase(const void *a, const void *b)
482 {
483 const git_path_with_stat *path_with_stat_a = a;
484 const git_path_with_stat *path_with_stat_b = b;
485
486 return strcasecmp(path_with_stat_a->path, path_with_stat_b->path);
487 }
488
489 GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
490 {
491 if (!ps)
492 return false;
493 else {
494 const char *path = ps->path;
495 size_t len = ps->path_len;
496
497 return len >= 4 &&
498 tolower(path[len - 1]) == 't' &&
499 tolower(path[len - 2]) == 'i' &&
500 tolower(path[len - 3]) == 'g' &&
501 path[len - 4] == '.' &&
502 (len == 4 || path[len - 5] == '/');
503 }
504 }
505
506 static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi)
507 {
508 workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
509 git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, git_path_with_stat_cmp_icase, git_path_with_stat_cmp_case);
510
511 if (wf == NULL)
512 return NULL;
513 if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
514 git__free(wf);
515 return NULL;
516 }
517 return wf;
518 }
519
520 static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
521 {
522 unsigned int i;
523 git_path_with_stat *path;
524
525 git_vector_foreach(&wf->entries, i, path)
526 git__free(path);
527 git_vector_free(&wf->entries);
528 git__free(wf);
529 }
530
531 static int workdir_iterator__update_entry(workdir_iterator *wi);
532
533 static int workdir_iterator__entry_cmp_case(const void *prefix, const void *item)
534 {
535 const git_path_with_stat *ps = item;
536 return git__prefixcmp((const char *)prefix, ps->path);
537 }
538
539 static int workdir_iterator__entry_cmp_icase(const void *prefix, const void *item)
540 {
541 const git_path_with_stat *ps = item;
542 return git__prefixcmp_icase((const char *)prefix, ps->path);
543 }
544
545 static int workdir_iterator__expand_dir(workdir_iterator *wi)
546 {
547 int error;
548 workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi);
549 GITERR_CHECK_ALLOC(wf);
550
551 error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
552 if (error < 0 || wf->entries.length == 0) {
553 workdir_iterator__free_frame(wf);
554 return GIT_ENOTFOUND;
555 }
556
557 git_vector_sort(&wf->entries);
558
559 if (!wi->stack)
560 wf->start = wi->base.start;
561 else if (wi->stack->start &&
562 ITERATOR_PREFIXCMP(wi->base, wi->stack->start, wi->path.ptr + wi->root_len) == 0)
563 wf->start = wi->stack->start;
564
565 if (wf->start)
566 git_vector_bsearch3(
567 &wf->index,
568 &wf->entries,
569 CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case),
570 wf->start);
571
572 if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
573 wf->index++;
574
575 wf->next = wi->stack;
576 wi->stack = wf;
577
578 /* only push new ignores if this is not top level directory */
579 if (wi->stack->next != NULL) {
580 ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
581 (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
582 }
583
584 return workdir_iterator__update_entry(wi);
585 }
586
587 static int workdir_iterator__current(
588 git_iterator *self, const git_index_entry **entry)
589 {
590 workdir_iterator *wi = (workdir_iterator *)self;
591 *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
592 return 0;
593 }
594
595 static int workdir_iterator__at_end(git_iterator *self)
596 {
597 return (((workdir_iterator *)self)->entry.path == NULL);
598 }
599
600 static int workdir_iterator__advance(
601 git_iterator *self, const git_index_entry **entry)
602 {
603 int error;
604 workdir_iterator *wi = (workdir_iterator *)self;
605 workdir_iterator_frame *wf;
606 git_path_with_stat *next;
607
608 if (entry != NULL)
609 *entry = NULL;
610
611 if (wi->entry.path == NULL)
612 return 0;
613
614 while ((wf = wi->stack) != NULL) {
615 next = git_vector_get(&wf->entries, ++wf->index);
616 if (next != NULL) {
617 /* match git's behavior of ignoring anything named ".git" */
618 if (path_is_dotgit(next))
619 continue;
620 /* else found a good entry */
621 break;
622 }
623
624 /* pop workdir directory stack */
625 wi->stack = wf->next;
626 workdir_iterator__free_frame(wf);
627 git_ignore__pop_dir(&wi->ignores);
628
629 if (wi->stack == NULL) {
630 memset(&wi->entry, 0, sizeof(wi->entry));
631 return 0;
632 }
633 }
634
635 error = workdir_iterator__update_entry(wi);
636
637 if (!error && entry != NULL)
638 error = workdir_iterator__current(self, entry);
639
640 return error;
641 }
642
643 static int workdir_iterator__seek(git_iterator *self, const char *prefix)
644 {
645 GIT_UNUSED(self);
646 GIT_UNUSED(prefix);
647 /* pop stack until matching prefix */
648 /* find prefix item in current frame */
649 /* push subdirectories as deep as possible while matching */
650 return 0;
651 }
652
653 static int workdir_iterator__reset(git_iterator *self)
654 {
655 workdir_iterator *wi = (workdir_iterator *)self;
656 while (wi->stack != NULL && wi->stack->next != NULL) {
657 workdir_iterator_frame *wf = wi->stack;
658 wi->stack = wf->next;
659 workdir_iterator__free_frame(wf);
660 git_ignore__pop_dir(&wi->ignores);
661 }
662 if (wi->stack)
663 wi->stack->index = 0;
664 return 0;
665 }
666
667 static void workdir_iterator__free(git_iterator *self)
668 {
669 workdir_iterator *wi = (workdir_iterator *)self;
670
671 while (wi->stack != NULL) {
672 workdir_iterator_frame *wf = wi->stack;
673 wi->stack = wf->next;
674 workdir_iterator__free_frame(wf);
675 }
676
677 git_ignore__free(&wi->ignores);
678 git_buf_free(&wi->path);
679 }
680
681 static int workdir_iterator__update_entry(workdir_iterator *wi)
682 {
683 git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
684
685 git_buf_truncate(&wi->path, wi->root_len);
686 memset(&wi->entry, 0, sizeof(wi->entry));
687
688 if (!ps)
689 return 0;
690
691 if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
692 return -1;
693
694 if (wi->base.end &&
695 ITERATOR_PREFIXCMP(wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
696 return 0;
697
698 wi->entry.path = ps->path;
699
700 /* skip over .git entries */
701 if (path_is_dotgit(ps))
702 return workdir_iterator__advance((git_iterator *)wi, NULL);
703
704 wi->is_ignored = -1;
705
706 git_index_entry__init_from_stat(&wi->entry, &ps->st);
707
708 /* need different mode here to keep directories during iteration */
709 wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
710
711 /* if this is a file type we don't handle, treat as ignored */
712 if (wi->entry.mode == 0) {
713 wi->is_ignored = 1;
714 return 0;
715 }
716
717 /* detect submodules */
718 if (S_ISDIR(wi->entry.mode)) {
719 int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
720 bool is_submodule = (res == 0);
721 if (res == GIT_ENOTFOUND)
722 giterr_clear();
723
724 /* if submodule, mark as GITLINK and remove trailing slash */
725 if (is_submodule) {
726 size_t len = strlen(wi->entry.path);
727 assert(wi->entry.path[len - 1] == '/');
728 wi->entry.path[len - 1] = '\0';
729 wi->entry.mode = S_IFGITLINK;
730 }
731 }
732
733 return 0;
734 }
735
736 int git_iterator_for_workdir_range(
737 git_iterator **iter,
738 git_repository *repo,
739 const char *start,
740 const char *end)
741 {
742 int error;
743 workdir_iterator *wi;
744 git_index *index;
745
746 assert(iter && repo);
747
748 if ((error = git_repository__ensure_not_bare(
749 repo, "scan working directory")) < 0)
750 return error;
751
752 ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
753 wi->repo = repo;
754
755 if ((error = git_repository_index__weakptr(&index, repo)) < 0) {
756 git__free(wi);
757 return error;
758 }
759
760 /* Match ignore_case flag for iterator to that of the index */
761 wi->base.ignore_case = index->ignore_case;
762
763 if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
764 git_path_to_dir(&wi->path) < 0 ||
765 git_ignore__for_path(repo, "", &wi->ignores) < 0)
766 {
767 git__free(wi);
768 return -1;
769 }
770
771 wi->root_len = wi->path.size;
772
773 if ((error = workdir_iterator__expand_dir(wi)) < 0) {
774 if (error == GIT_ENOTFOUND)
775 error = 0;
776 else {
777 git_iterator_free((git_iterator *)wi);
778 wi = NULL;
779 }
780 }
781
782 *iter = (git_iterator *)wi;
783
784 return error;
785 }
786
787 typedef struct {
788 git_iterator base;
789 git_iterator *wrapped;
790 git_vector entries;
791 git_vector_cmp comparer;
792 git_pool entry_pool;
793 git_pool string_pool;
794 unsigned int position;
795 } spoolandsort_iterator;
796
797 static int spoolandsort_iterator__current(
798 git_iterator *self, const git_index_entry **entry)
799 {
800 spoolandsort_iterator *si = (spoolandsort_iterator *)self;
801
802 if (si->position < si->entries.length)
803 *entry = (const git_index_entry *)git_vector_get(
804 &si->entries, si->position);
805 else
806 *entry = NULL;
807
808 return 0;
809 }
810
811 static int spoolandsort_iterator__at_end(git_iterator *self)
812 {
813 spoolandsort_iterator *si = (spoolandsort_iterator *)self;
814
815 return 0 == si->entries.length || si->entries.length - 1 <= si->position;
816 }
817
818 static int spoolandsort_iterator__advance(
819 git_iterator *self, const git_index_entry **entry)
820 {
821 spoolandsort_iterator *si = (spoolandsort_iterator *)self;
822
823 if (si->position < si->entries.length)
824 *entry = (const git_index_entry *)git_vector_get(
825 &si->entries, ++si->position);
826 else
827 *entry = NULL;
828
829 return 0;
830 }
831
832 static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix)
833 {
834 GIT_UNUSED(self);
835 GIT_UNUSED(prefix);
836
837 return -1;
838 }
839
840 static int spoolandsort_iterator__reset(git_iterator *self)
841 {
842 spoolandsort_iterator *si = (spoolandsort_iterator *)self;
843
844 si->position = 0;
845
846 return 0;
847 }
848
849 static void spoolandsort_iterator__free(git_iterator *self)
850 {
851 spoolandsort_iterator *si = (spoolandsort_iterator *)self;
852
853 git_pool_clear(&si->string_pool);
854 git_pool_clear(&si->entry_pool);
855 git_vector_free(&si->entries);
856 git_iterator_free(si->wrapped);
857 }
858
859 int git_iterator_spoolandsort_range(
860 git_iterator **iter,
861 git_iterator *towrap,
862 git_vector_cmp comparer,
863 bool ignore_case,
864 const char *start,
865 const char *end)
866 {
867 spoolandsort_iterator *si;
868 const git_index_entry *item;
869
870 assert(iter && towrap && comparer);
871
872 ITERATOR_BASE_INIT(si, spoolandsort, SPOOLANDSORT);
873 si->base.ignore_case = ignore_case;
874 si->wrapped = towrap;
875 si->comparer = comparer;
876 si->position = 0;
877
878 if (git_vector_init(&si->entries, 16, si->comparer) < 0 ||
879 git_iterator_current(towrap, &item) < 0 ||
880 git_pool_init(&si->entry_pool, sizeof(git_index_entry), 0) ||
881 git_pool_init(&si->string_pool, 1, 0))
882 {
883 git__free(si);
884 return -1;
885 }
886
887 while (item)
888 {
889 git_index_entry *clone = git_pool_malloc(&si->entry_pool, 1);
890 memcpy(clone, item, sizeof(git_index_entry));
891
892 if (item->path)
893 {
894 clone->path = git_pool_strdup(&si->string_pool, item->path);
895 }
896
897 git_vector_insert(&si->entries, clone);
898
899 if (git_iterator_advance(towrap, &item) < 0)
900 {
901 git__free(si);
902 return -1;
903 }
904 }
905
906 git_vector_sort(&si->entries);
907
908 *iter = (git_iterator *)si;
909
910 return 0;
911 }
912
913 int git_iterator_current_tree_entry(
914 git_iterator *iter, const git_tree_entry **tree_entry)
915 {
916 *tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :
917 tree_iterator__tree_entry((tree_iterator *)iter);
918 return 0;
919 }
920
921 int git_iterator_current_parent_tree(
922 git_iterator *iter,
923 const char *parent_path,
924 const git_tree **tree_ptr)
925 {
926 tree_iterator *ti = (tree_iterator *)iter;
927 tree_iterator_frame *tf;
928 const char *scan = parent_path;
929
930 if (iter->type != GIT_ITERATOR_TREE || ti->stack == NULL)
931 goto notfound;
932
933 for (tf = ti->tail; tf != NULL; tf = tf->prev) {
934 const git_tree_entry *te;
935
936 if (!*scan) {
937 *tree_ptr = tf->tree;
938 return 0;
939 }
940
941 te = git_tree_entry_byindex(tf->tree, tf->index);
942
943 if (strncmp(scan, te->filename, te->filename_len) != 0)
944 goto notfound;
945
946 scan += te->filename_len;
947
948 if (*scan) {
949 if (*scan != '/')
950 goto notfound;
951 scan++;
952 }
953 }
954
955 notfound:
956 *tree_ptr = NULL;
957 return 0;
958 }
959
960 int git_iterator_current_is_ignored(git_iterator *iter)
961 {
962 workdir_iterator *wi = (workdir_iterator *)iter;
963
964 if (iter->type != GIT_ITERATOR_WORKDIR)
965 return 0;
966
967 if (wi->is_ignored != -1)
968 return wi->is_ignored;
969
970 if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
971 wi->is_ignored = 1;
972
973 return wi->is_ignored;
974 }
975
976 int git_iterator_advance_into_directory(
977 git_iterator *iter, const git_index_entry **entry)
978 {
979 workdir_iterator *wi = (workdir_iterator *)iter;
980
981 if (iter->type == GIT_ITERATOR_WORKDIR &&
982 wi->entry.path &&
983 S_ISDIR(wi->entry.mode) &&
984 !S_ISGITLINK(wi->entry.mode))
985 {
986 if (workdir_iterator__expand_dir(wi) < 0)
987 /* if error loading or if empty, skip the directory. */
988 return workdir_iterator__advance(iter, entry);
989 }
990
991 return entry ? git_iterator_current(iter, entry) : 0;
992 }
993
994 int git_iterator_cmp(
995 git_iterator *iter, const char *path_prefix)
996 {
997 const git_index_entry *entry;
998
999 /* a "done" iterator is after every prefix */
1000 if (git_iterator_current(iter, &entry) < 0 ||
1001 entry == NULL)
1002 return 1;
1003
1004 /* a NULL prefix is after any valid iterator */
1005 if (!path_prefix)
1006 return -1;
1007
1008 return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix);
1009 }
1010
1011 int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path)
1012 {
1013 workdir_iterator *wi = (workdir_iterator *)iter;
1014
1015 if (iter->type != GIT_ITERATOR_WORKDIR || !wi->entry.path)
1016 *path = NULL;
1017 else
1018 *path = &wi->path;
1019
1020 return 0;
1021 }
1022