2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
12 #include "git2/submodule.h"
15 #define ITERATOR_SET_CB(P,NAME_LC) do { \
16 (P)->cb.current = NAME_LC ## _iterator__current; \
17 (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
18 (P)->cb.advance = NAME_LC ## _iterator__advance; \
19 (P)->cb.seek = NAME_LC ## _iterator__seek; \
20 (P)->cb.reset = NAME_LC ## _iterator__reset; \
21 (P)->cb.free = NAME_LC ## _iterator__free; \
24 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
25 (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
26 GITERR_CHECK_ALLOC(P); \
27 (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
28 (P)->base.cb = &(P)->cb; \
29 ITERATOR_SET_CB(P,NAME_LC); \
30 (P)->base.start = start ? git__strdup(start) : NULL; \
31 (P)->base.end = end ? git__strdup(end) : NULL; \
32 if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
33 git__free(P); return -1; } \
34 (P)->base.prefixcomp = git__prefixcmp; \
37 static int iterator__reset_range(
38 git_iterator
*iter
, const char *start
, const char *end
)
42 git__free(iter
->start
);
43 iter
->start
= git__strdup(start
);
44 GITERR_CHECK_ALLOC(iter
->start
);
50 iter
->end
= git__strdup(end
);
51 GITERR_CHECK_ALLOC(iter
->end
);
57 static int iterator_update_ignore_case(
59 git_iterator_flag_t flags
)
61 int error
= 0, ignore_case
= -1;
63 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
65 else if ((flags
& GIT_ITERATOR_DONT_IGNORE_CASE
) != 0)
70 if (!(error
= git_repository_index__weakptr(&index
, iter
->repo
)))
71 ignore_case
= (index
->ignore_case
!= false);
75 iter
->flags
= (iter
->flags
| GIT_ITERATOR_IGNORE_CASE
);
76 else if (ignore_case
== 0)
77 iter
->flags
= (iter
->flags
& ~GIT_ITERATOR_IGNORE_CASE
);
79 iter
->prefixcomp
= ((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) ?
80 git__prefixcmp_icase
: git__prefixcmp
;
85 static int empty_iterator__no_item(
86 git_iterator
*iter
, const git_index_entry
**entry
)
93 static int empty_iterator__at_end(git_iterator
*iter
)
99 static int empty_iterator__reset(
100 git_iterator
*iter
, const char *start
, const char *end
)
102 GIT_UNUSED(iter
); GIT_UNUSED(start
); GIT_UNUSED(end
);
106 static int empty_iterator__seek(git_iterator
*iter
, const char *prefix
)
108 GIT_UNUSED(iter
); GIT_UNUSED(prefix
);
112 static void empty_iterator__free(git_iterator
*iter
)
119 git_iterator_callbacks cb
;
122 int git_iterator_for_nothing(git_iterator
**iter
, git_iterator_flag_t flags
)
124 empty_iterator
*i
= git__calloc(1, sizeof(empty_iterator
));
125 GITERR_CHECK_ALLOC(i
);
127 i
->base
.type
= GIT_ITERATOR_TYPE_EMPTY
;
129 i
->base
.flags
= flags
;
130 i
->cb
.current
= empty_iterator__no_item
;
131 i
->cb
.at_end
= empty_iterator__at_end
;
132 i
->cb
.advance
= empty_iterator__no_item
;
133 i
->cb
.seek
= empty_iterator__seek
;
134 i
->cb
.reset
= empty_iterator__reset
;
135 i
->cb
.free
= empty_iterator__free
;
137 *iter
= (git_iterator
*)i
;
143 typedef struct tree_iterator_frame tree_iterator_frame
;
144 struct tree_iterator_frame
{
145 tree_iterator_frame
*next
, *prev
;
151 void *icase_data
[GIT_FLEX_ARRAY
];
156 git_iterator_callbacks cb
;
157 tree_iterator_frame
*stack
, *tail
;
158 git_index_entry entry
;
160 bool path_has_filename
;
163 GIT_INLINE(const git_tree_entry
*)tree_iterator__tree_entry(tree_iterator
*ti
)
165 tree_iterator_frame
*tf
= ti
->stack
;
167 if (tf
->index
>= git_tree_entrycount(tf
->tree
))
170 return git_tree_entry_byindex(
171 tf
->tree
, tf
->icase_map
? (size_t)tf
->icase_map
[tf
->index
] : tf
->index
);
174 static char *tree_iterator__current_filename(
175 tree_iterator
*ti
, const git_tree_entry
*te
)
177 if (!ti
->path_has_filename
) {
178 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
180 ti
->path_has_filename
= true;
186 static void tree_iterator__free_frame(tree_iterator_frame
*tf
)
191 git_tree_free(tf
->tree
);
197 static bool tree_iterator__pop_frame(tree_iterator
*ti
)
199 tree_iterator_frame
*tf
= ti
->stack
;
201 /* don't free the initial tree/frame */
205 ti
->stack
= tf
->next
;
206 ti
->stack
->prev
= NULL
;
208 tree_iterator__free_frame(tf
);
213 static int tree_iterator__to_end(tree_iterator
*ti
)
215 while (tree_iterator__pop_frame(ti
)) /* pop all */;
216 ti
->stack
->index
= git_tree_entrycount(ti
->stack
->tree
);
220 static int tree_iterator__current(
221 git_iterator
*self
, const git_index_entry
**entry
)
223 tree_iterator
*ti
= (tree_iterator
*)self
;
224 const git_tree_entry
*te
= tree_iterator__tree_entry(ti
);
232 ti
->entry
.mode
= te
->attr
;
233 git_oid_cpy(&ti
->entry
.oid
, &te
->oid
);
235 ti
->entry
.path
= tree_iterator__current_filename(ti
, te
);
236 if (ti
->entry
.path
== NULL
)
239 if (ti
->base
.end
&& ti
->base
.prefixcomp(ti
->entry
.path
, ti
->base
.end
) > 0)
240 return tree_iterator__to_end(ti
);
248 static int tree_iterator__at_end(git_iterator
*self
)
250 return (tree_iterator__tree_entry((tree_iterator
*)self
) == NULL
);
253 static int tree_iterator__icase_map_cmp(const void *a
, const void *b
, void *data
)
255 git_tree
*tree
= data
;
256 const git_tree_entry
*te1
= git_tree_entry_byindex(tree
, (size_t)a
);
257 const git_tree_entry
*te2
= git_tree_entry_byindex(tree
, (size_t)b
);
258 return te1
? (te2
? git_tree_entry_icmp(te1
, te2
) : 1) : -1;
261 static int tree_iterator__frame_start_icmp(const void *key
, const void *element
)
263 const tree_iterator_frame
*tf
= (const tree_iterator_frame
*)key
;
264 const git_tree_entry
*te
= git_tree_entry_byindex(tf
->tree
, (size_t)element
);
266 return memcmp(tf
->start
, te
->filename
, min(tf
->startlen
, te
->filename_len
));
269 static void tree_iterator__frame_seek_start(tree_iterator_frame
*tf
)
273 else if (!tf
->icase_map
)
274 tf
->index
= git_tree__prefix_position(tf
->tree
, tf
->start
);
277 tf
->icase_map
, git_tree_entrycount(tf
->tree
),
278 tf
, tree_iterator__frame_start_icmp
, &tf
->index
))
280 while (tf
->index
> 0) {
281 /* move back while previous entry is still prefixed */
282 if (tree_iterator__frame_start_icmp(
283 tf
, (const void *)(tf
->index
- 1)))
291 static tree_iterator_frame
*tree_iterator__alloc_frame(
292 tree_iterator
*ti
, git_tree
*tree
, char *start
)
294 size_t i
, max_i
= git_tree_entrycount(tree
);
295 tree_iterator_frame
*tf
=
296 git__calloc(1, sizeof(tree_iterator_frame
) + max_i
* sizeof(void *));
302 if (start
&& *start
) {
304 tf
->startlen
= strlen(start
);
310 if ((ti
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) {
311 tf
->icase_map
= tf
->icase_data
;
313 for (i
= 0; i
< max_i
; ++i
)
314 tf
->icase_map
[i
] = (void *)i
;
317 tf
->icase_map
, max_i
, tree_iterator__icase_map_cmp
, tf
->tree
);
320 tree_iterator__frame_seek_start(tf
);
325 static int tree_iterator__expand_tree(tree_iterator
*ti
)
329 const git_tree_entry
*te
= tree_iterator__tree_entry(ti
);
330 tree_iterator_frame
*tf
;
333 while (te
!= NULL
&& git_tree_entry__is_tree(te
)) {
334 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
337 /* check that we have not passed the range end */
338 if (ti
->base
.end
!= NULL
&&
339 ti
->base
.prefixcomp(ti
->path
.ptr
, ti
->base
.end
) > 0)
340 return tree_iterator__to_end(ti
);
342 if ((error
= git_tree_lookup(&subtree
, ti
->base
.repo
, &te
->oid
)) < 0)
347 /* apply range start to new frame if relevant */
348 if (ti
->stack
->start
&&
349 ti
->base
.prefixcomp(ti
->stack
->start
, te
->filename
) == 0)
351 if (ti
->stack
->start
[te
->filename_len
] == '/')
352 relpath
= ti
->stack
->start
+ te
->filename_len
+ 1;
355 if ((tf
= tree_iterator__alloc_frame(ti
, subtree
, relpath
)) == NULL
)
358 tf
->next
= ti
->stack
;
362 te
= tree_iterator__tree_entry(ti
);
368 static int tree_iterator__advance(
369 git_iterator
*self
, const git_index_entry
**entry
)
372 tree_iterator
*ti
= (tree_iterator
*)self
;
373 const git_tree_entry
*te
= NULL
;
378 if (ti
->path_has_filename
) {
379 git_buf_rtruncate_at_char(&ti
->path
, '/');
380 ti
->path_has_filename
= false;
386 if ((te
= tree_iterator__tree_entry(ti
)) != NULL
)
389 if (!tree_iterator__pop_frame(ti
))
390 break; /* no frames left to pop */
392 git_buf_rtruncate_at_char(&ti
->path
, '/');
395 if (te
&& git_tree_entry__is_tree(te
))
396 error
= tree_iterator__expand_tree(ti
);
399 error
= tree_iterator__current(self
, entry
);
404 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
408 /* pop stack until matches prefix */
409 /* seek item in current frame matching prefix */
410 /* push stack which matches prefix */
414 static void tree_iterator__free(git_iterator
*self
)
416 tree_iterator
*ti
= (tree_iterator
*)self
;
418 while (tree_iterator__pop_frame(ti
)) /* pop all */;
420 tree_iterator__free_frame(ti
->stack
);
421 ti
->stack
= ti
->tail
= NULL
;
423 git_buf_free(&ti
->path
);
426 static int tree_iterator__reset(
427 git_iterator
*self
, const char *start
, const char *end
)
429 tree_iterator
*ti
= (tree_iterator
*)self
;
431 while (tree_iterator__pop_frame(ti
)) /* pop all */;
433 if (iterator__reset_range(self
, start
, end
) < 0)
436 /* reset start position */
437 tree_iterator__frame_seek_start(ti
->stack
);
439 git_buf_clear(&ti
->path
);
440 ti
->path_has_filename
= false;
442 return tree_iterator__expand_tree(ti
);
445 int git_iterator_for_tree_range(
448 git_iterator_flag_t flags
,
456 return git_iterator_for_nothing(iter
, flags
);
458 if ((error
= git_tree__dup(&tree
, tree
)) < 0)
461 ITERATOR_BASE_INIT(ti
, tree
, TREE
);
463 ti
->base
.repo
= git_tree_owner(tree
);
465 if ((error
= iterator_update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
468 ti
->stack
= ti
->tail
= tree_iterator__alloc_frame(ti
, tree
, ti
->base
.start
);
470 if ((error
= tree_iterator__expand_tree(ti
)) < 0)
473 *iter
= (git_iterator
*)ti
;
477 git_iterator_free((git_iterator
*)ti
);
484 git_iterator_callbacks cb
;
489 static int index_iterator__current(
490 git_iterator
*self
, const git_index_entry
**entry
)
492 index_iterator
*ii
= (index_iterator
*)self
;
493 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
501 static int index_iterator__at_end(git_iterator
*self
)
503 index_iterator
*ii
= (index_iterator
*)self
;
504 return (ii
->current
>= git_index_entrycount(ii
->index
));
507 static void index_iterator__skip_conflicts(
510 size_t entrycount
= git_index_entrycount(ii
->index
);
511 const git_index_entry
*ie
;
513 while (ii
->current
< entrycount
) {
514 ie
= git_index_get_byindex(ii
->index
, ii
->current
);
517 (ii
->base
.end
!= NULL
&&
518 ii
->base
.prefixcomp(ie
->path
, ii
->base
.end
) > 0)) {
519 ii
->current
= entrycount
;
523 if (git_index_entry_stage(ie
) == 0)
530 static int index_iterator__advance(
531 git_iterator
*self
, const git_index_entry
**entry
)
533 index_iterator
*ii
= (index_iterator
*)self
;
535 if (ii
->current
< git_index_entrycount(ii
->index
))
538 index_iterator__skip_conflicts(ii
);
540 return index_iterator__current(self
, entry
);
543 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
547 /* find last item before prefix */
551 static int index_iterator__reset(
552 git_iterator
*self
, const char *start
, const char *end
)
554 index_iterator
*ii
= (index_iterator
*)self
;
555 if (iterator__reset_range(self
, start
, end
) < 0)
557 ii
->current
= ii
->base
.start
?
558 git_index__prefix_position(ii
->index
, ii
->base
.start
) : 0;
559 index_iterator__skip_conflicts(ii
);
563 static void index_iterator__free(git_iterator
*self
)
565 index_iterator
*ii
= (index_iterator
*)self
;
566 git_index_free(ii
->index
);
570 int git_iterator_for_index_range(
573 git_iterator_flag_t flags
,
581 ITERATOR_BASE_INIT(ii
, index
, INDEX
);
583 ii
->base
.repo
= git_index_owner(index
);
584 if (index
->ignore_case
) {
585 ii
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
586 ii
->base
.prefixcomp
= git__prefixcmp_icase
;
589 GIT_REFCOUNT_INC(index
);
591 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
593 *iter
= (git_iterator
*)ii
;
599 typedef struct workdir_iterator_frame workdir_iterator_frame
;
600 struct workdir_iterator_frame
{
601 workdir_iterator_frame
*next
;
608 git_iterator_callbacks cb
;
609 workdir_iterator_frame
*stack
;
610 int (*entrycmp
)(const void *pfx
, const void *item
);
612 git_index_entry entry
;
618 GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat
*ps
)
623 const char *path
= ps
->path
;
624 size_t len
= ps
->path_len
;
628 if (path
[len
- 1] == '/')
630 if (tolower(path
[len
- 1]) != 't' ||
631 tolower(path
[len
- 2]) != 'i' ||
632 tolower(path
[len
- 3]) != 'g' ||
633 tolower(path
[len
- 4]) != '.')
635 return (len
== 4 || path
[len
- 5] == '/');
639 static workdir_iterator_frame
*workdir_iterator__alloc_frame(
640 workdir_iterator
*wi
)
642 workdir_iterator_frame
*wf
= git__calloc(1, sizeof(workdir_iterator_frame
));
643 git_vector_cmp entry_compare
= CASESELECT(
644 (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0,
645 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
650 if (git_vector_init(&wf
->entries
, 0, entry_compare
) != 0) {
658 static void workdir_iterator__free_frame(workdir_iterator_frame
*wf
)
661 git_path_with_stat
*path
;
663 git_vector_foreach(&wf
->entries
, i
, path
)
665 git_vector_free(&wf
->entries
);
669 static int workdir_iterator__update_entry(workdir_iterator
*wi
);
671 static int workdir_iterator__entry_cmp_case(const void *pfx
, const void *item
)
673 const git_path_with_stat
*ps
= item
;
674 return git__prefixcmp((const char *)pfx
, ps
->path
);
677 static int workdir_iterator__entry_cmp_icase(const void *pfx
, const void *item
)
679 const git_path_with_stat
*ps
= item
;
680 return git__prefixcmp_icase((const char *)pfx
, ps
->path
);
683 static void workdir_iterator__seek_frame_start(
684 workdir_iterator
*wi
, workdir_iterator_frame
*wf
)
691 &wf
->index
, &wf
->entries
, wi
->entrycmp
, wi
->base
.start
);
695 if (path_is_dotgit(git_vector_get(&wf
->entries
, wf
->index
)))
699 static int workdir_iterator__expand_dir(workdir_iterator
*wi
)
702 workdir_iterator_frame
*wf
= workdir_iterator__alloc_frame(wi
);
703 GITERR_CHECK_ALLOC(wf
);
705 error
= git_path_dirload_with_stat(
706 wi
->path
.ptr
, wi
->root_len
,
707 (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0,
708 wi
->base
.start
, wi
->base
.end
, &wf
->entries
);
710 if (error
< 0 || wf
->entries
.length
== 0) {
711 workdir_iterator__free_frame(wf
);
712 return GIT_ENOTFOUND
;
715 workdir_iterator__seek_frame_start(wi
, wf
);
717 /* only push new ignores if this is not top level directory */
718 if (wi
->stack
!= NULL
) {
719 ssize_t slash_pos
= git_buf_rfind_next(&wi
->path
, '/');
720 (void)git_ignore__push_dir(&wi
->ignores
, &wi
->path
.ptr
[slash_pos
+ 1]);
723 wf
->next
= wi
->stack
;
726 return workdir_iterator__update_entry(wi
);
729 static int workdir_iterator__current(
730 git_iterator
*self
, const git_index_entry
**entry
)
732 workdir_iterator
*wi
= (workdir_iterator
*)self
;
733 *entry
= (wi
->entry
.path
== NULL
) ? NULL
: &wi
->entry
;
737 static int workdir_iterator__at_end(git_iterator
*self
)
739 return (((workdir_iterator
*)self
)->entry
.path
== NULL
);
742 static int workdir_iterator__advance(
743 git_iterator
*self
, const git_index_entry
**entry
)
746 workdir_iterator
*wi
= (workdir_iterator
*)self
;
747 workdir_iterator_frame
*wf
;
748 git_path_with_stat
*next
;
753 if (wi
->entry
.path
== NULL
)
758 next
= git_vector_get(&wf
->entries
, ++wf
->index
);
761 /* match git's behavior of ignoring anything named ".git" */
762 if (path_is_dotgit(next
))
764 /* else found a good entry */
768 /* pop stack if anything is left to pop */
770 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
774 wi
->stack
= wf
->next
;
775 workdir_iterator__free_frame(wf
);
776 git_ignore__pop_dir(&wi
->ignores
);
779 error
= workdir_iterator__update_entry(wi
);
781 if (!error
&& entry
!= NULL
)
782 error
= workdir_iterator__current(self
, entry
);
787 static int workdir_iterator__seek(git_iterator
*self
, const char *prefix
)
791 /* pop stack until matching prefix */
792 /* find prefix item in current frame */
793 /* push subdirectories as deep as possible while matching */
797 static int workdir_iterator__reset(
798 git_iterator
*self
, const char *start
, const char *end
)
800 workdir_iterator
*wi
= (workdir_iterator
*)self
;
802 while (wi
->stack
!= NULL
&& wi
->stack
->next
!= NULL
) {
803 workdir_iterator_frame
*wf
= wi
->stack
;
804 wi
->stack
= wf
->next
;
805 workdir_iterator__free_frame(wf
);
806 git_ignore__pop_dir(&wi
->ignores
);
809 if (iterator__reset_range(self
, start
, end
) < 0)
812 workdir_iterator__seek_frame_start(wi
, wi
->stack
);
814 return workdir_iterator__update_entry(wi
);
817 static void workdir_iterator__free(git_iterator
*self
)
819 workdir_iterator
*wi
= (workdir_iterator
*)self
;
821 while (wi
->stack
!= NULL
) {
822 workdir_iterator_frame
*wf
= wi
->stack
;
823 wi
->stack
= wf
->next
;
824 workdir_iterator__free_frame(wf
);
827 git_ignore__free(&wi
->ignores
);
828 git_buf_free(&wi
->path
);
831 static int workdir_iterator__update_entry(workdir_iterator
*wi
)
833 git_path_with_stat
*ps
=
834 git_vector_get(&wi
->stack
->entries
, wi
->stack
->index
);
836 git_buf_truncate(&wi
->path
, wi
->root_len
);
837 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
842 if (git_buf_put(&wi
->path
, ps
->path
, ps
->path_len
) < 0)
846 wi
->base
.prefixcomp(wi
->path
.ptr
+ wi
->root_len
, wi
->base
.end
) > 0)
849 wi
->entry
.path
= ps
->path
;
851 /* skip over .git entries */
852 if (path_is_dotgit(ps
))
853 return workdir_iterator__advance((git_iterator
*)wi
, NULL
);
857 git_index_entry__init_from_stat(&wi
->entry
, &ps
->st
);
859 /* need different mode here to keep directories during iteration */
860 wi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
862 /* if this is a file type we don't handle, treat as ignored */
863 if (wi
->entry
.mode
== 0) {
868 /* detect submodules */
869 if (S_ISDIR(wi
->entry
.mode
)) {
870 int res
= git_submodule_lookup(NULL
, wi
->base
.repo
, wi
->entry
.path
);
871 bool is_submodule
= (res
== 0);
872 if (res
== GIT_ENOTFOUND
)
875 /* if submodule, mark as GITLINK and remove trailing slash */
877 size_t len
= strlen(wi
->entry
.path
);
878 assert(wi
->entry
.path
[len
- 1] == '/');
879 wi
->entry
.path
[len
- 1] = '\0';
880 wi
->entry
.mode
= S_IFGITLINK
;
887 int git_iterator_for_workdir_range(
889 git_repository
*repo
,
890 git_iterator_flag_t flags
,
895 workdir_iterator
*wi
;
897 assert(iter
&& repo
);
899 if ((error
= git_repository__ensure_not_bare(
900 repo
, "scan working directory")) < 0)
903 ITERATOR_BASE_INIT(wi
, workdir
, WORKDIR
);
904 wi
->base
.repo
= repo
;
906 if ((error
= iterator_update_ignore_case((git_iterator
*)wi
, flags
)) < 0)
909 if (git_buf_sets(&wi
->path
, git_repository_workdir(repo
)) < 0 ||
910 git_path_to_dir(&wi
->path
) < 0 ||
911 git_ignore__for_path(repo
, "", &wi
->ignores
) < 0)
917 wi
->root_len
= wi
->path
.size
;
918 wi
->entrycmp
= (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0 ?
919 workdir_iterator__entry_cmp_icase
: workdir_iterator__entry_cmp_case
;
921 if ((error
= workdir_iterator__expand_dir(wi
)) < 0) {
922 if (error
!= GIT_ENOTFOUND
)
927 *iter
= (git_iterator
*)wi
;
931 git_iterator_free((git_iterator
*)wi
);
937 /* replacement callbacks */
938 git_iterator_callbacks cb
;
939 /* original iterator values */
940 git_iterator_callbacks
*orig
;
941 git_iterator_type_t orig_type
;
942 /* spoolandsort data */
945 git_pool string_pool
;
947 } spoolandsort_callbacks
;
949 static int spoolandsort_iterator__current(
950 git_iterator
*self
, const git_index_entry
**entry
)
952 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
954 *entry
= (const git_index_entry
*)
955 git_vector_get(&scb
->entries
, scb
->position
);
960 static int spoolandsort_iterator__at_end(git_iterator
*self
)
962 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
964 return 0 == scb
->entries
.length
|| scb
->entries
.length
- 1 <= scb
->position
;
967 static int spoolandsort_iterator__advance(
968 git_iterator
*self
, const git_index_entry
**entry
)
970 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
972 *entry
= (const git_index_entry
*)
973 git_vector_get(&scb
->entries
, ++scb
->position
);
978 static int spoolandsort_iterator__seek(git_iterator
*self
, const char *prefix
)
986 static int spoolandsort_iterator__reset(
987 git_iterator
*self
, const char *start
, const char *end
)
989 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
991 GIT_UNUSED(start
); GIT_UNUSED(end
);
998 static void spoolandsort_iterator__free_callbacks(spoolandsort_callbacks
*scb
)
1000 git_pool_clear(&scb
->string_pool
);
1001 git_pool_clear(&scb
->entry_pool
);
1002 git_vector_free(&scb
->entries
);
1006 void git_iterator_spoolandsort_pop(git_iterator
*self
)
1008 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
1010 if (self
->type
!= GIT_ITERATOR_TYPE_SPOOLANDSORT
)
1013 self
->cb
= scb
->orig
;
1014 self
->type
= scb
->orig_type
;
1015 self
->flags
^= GIT_ITERATOR_IGNORE_CASE
;
1017 spoolandsort_iterator__free_callbacks(scb
);
1020 static void spoolandsort_iterator__free(git_iterator
*self
)
1022 git_iterator_spoolandsort_pop(self
);
1023 self
->cb
->free(self
);
1026 int git_iterator_spoolandsort_push(git_iterator
*iter
, bool ignore_case
)
1028 const git_index_entry
*item
;
1029 spoolandsort_callbacks
*scb
;
1030 int (*entrycomp
)(const void *a
, const void *b
);
1032 if (((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) == (ignore_case
!= 0))
1035 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1036 iter
->flags
= (iter
->flags
^ GIT_ITERATOR_IGNORE_CASE
);
1040 scb
= git__calloc(1, sizeof(spoolandsort_callbacks
));
1041 GITERR_CHECK_ALLOC(scb
);
1043 ITERATOR_SET_CB(scb
,spoolandsort
);
1045 scb
->orig
= iter
->cb
;
1046 scb
->orig_type
= iter
->type
;
1049 entrycomp
= ignore_case
? git_index_entry__cmp_icase
: git_index_entry__cmp
;
1051 if (git_vector_init(&scb
->entries
, 16, entrycomp
) < 0 ||
1052 git_pool_init(&scb
->entry_pool
, sizeof(git_index_entry
), 0) < 0 ||
1053 git_pool_init(&scb
->string_pool
, 1, 0) < 0 ||
1054 git_iterator_current(iter
, &item
) < 0)
1058 git_index_entry
*clone
= git_pool_malloc(&scb
->entry_pool
, 1);
1062 memcpy(clone
, item
, sizeof(git_index_entry
));
1065 clone
->path
= git_pool_strdup(&scb
->string_pool
, item
->path
);
1070 if (git_vector_insert(&scb
->entries
, clone
) < 0)
1073 if (git_iterator_advance(iter
, &item
) < 0)
1077 git_vector_sort(&scb
->entries
);
1079 iter
->cb
= (git_iterator_callbacks
*)scb
;
1080 iter
->type
= GIT_ITERATOR_TYPE_SPOOLANDSORT
;
1081 iter
->flags
^= GIT_ITERATOR_IGNORE_CASE
;
1086 spoolandsort_iterator__free_callbacks(scb
);
1091 void git_iterator_free(git_iterator
*iter
)
1096 iter
->cb
->free(iter
);
1098 git__free(iter
->start
);
1099 git__free(iter
->end
);
1101 memset(iter
, 0, sizeof(*iter
));
1106 git_index
*git_iterator_index_get_index(git_iterator
*iter
)
1108 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1109 return ((index_iterator
*)iter
)->index
;
1111 if (iter
->type
== GIT_ITERATOR_TYPE_SPOOLANDSORT
&&
1112 ((spoolandsort_callbacks
*)iter
->cb
)->orig_type
== GIT_ITERATOR_TYPE_INDEX
)
1113 return ((index_iterator
*)iter
)->index
;
1118 git_iterator_type_t
git_iterator_inner_type(git_iterator
*iter
)
1120 if (iter
->type
== GIT_ITERATOR_TYPE_SPOOLANDSORT
)
1121 return ((spoolandsort_callbacks
*)iter
->cb
)->orig_type
;
1126 int git_iterator_current_tree_entry(
1127 git_iterator
*iter
, const git_tree_entry
**tree_entry
)
1129 *tree_entry
= (iter
->type
!= GIT_ITERATOR_TYPE_TREE
) ? NULL
:
1130 tree_iterator__tree_entry((tree_iterator
*)iter
);
1134 int git_iterator_current_parent_tree(
1136 const char *parent_path
,
1137 const git_tree
**tree_ptr
)
1139 tree_iterator
*ti
= (tree_iterator
*)iter
;
1140 tree_iterator_frame
*tf
;
1141 const char *scan
= parent_path
;
1142 int (*strncomp
)(const char *a
, const char *b
, size_t sz
);
1144 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
|| ti
->stack
== NULL
)
1147 strncomp
= ((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) ?
1148 git__strncasecmp
: git__strncmp
;
1150 for (tf
= ti
->tail
; tf
!= NULL
; tf
= tf
->prev
) {
1151 const git_tree_entry
*te
;
1154 *tree_ptr
= tf
->tree
;
1158 te
= git_tree_entry_byindex(tf
->tree
,
1159 tf
->icase_map
? (size_t)tf
->icase_map
[tf
->index
] : tf
->index
);
1161 if (strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1164 scan
+= te
->filename_len
;
1178 int git_iterator_current_is_ignored(git_iterator
*iter
)
1180 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1182 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1185 if (wi
->is_ignored
!= -1)
1186 return wi
->is_ignored
;
1188 if (git_ignore__lookup(&wi
->ignores
, wi
->entry
.path
, &wi
->is_ignored
) < 0)
1191 return wi
->is_ignored
;
1194 int git_iterator_advance_into_directory(
1195 git_iterator
*iter
, const git_index_entry
**entry
)
1197 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1199 if (iter
->type
== GIT_ITERATOR_TYPE_WORKDIR
&&
1201 (wi
->entry
.mode
== GIT_FILEMODE_TREE
||
1202 wi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
1204 if (workdir_iterator__expand_dir(wi
) < 0)
1205 /* if error loading or if empty, skip the directory. */
1206 return workdir_iterator__advance(iter
, entry
);
1209 return entry
? git_iterator_current(iter
, entry
) : 0;
1212 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1214 const git_index_entry
*entry
;
1216 /* a "done" iterator is after every prefix */
1217 if (git_iterator_current(iter
, &entry
) < 0 ||
1221 /* a NULL prefix is after any valid iterator */
1225 return iter
->prefixcomp(entry
->path
, path_prefix
);
1228 int git_iterator_current_workdir_path(git_iterator
*iter
, git_buf
**path
)
1230 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1232 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->entry
.path
)