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
);
259 return te1
? (te2
? git_tree_entry_icmp(te1
, te2
) : 1) : -1;
262 static int tree_iterator__frame_start_icmp(const void *key
, const void *el
)
264 const tree_iterator_frame
*tf
= (const tree_iterator_frame
*)key
;
265 const git_tree_entry
*te
= git_tree_entry_byindex(tf
->tree
, (size_t)el
);
266 size_t minlen
= min(tf
->startlen
, te
->filename_len
);
268 return git__strncasecmp(tf
->start
, te
->filename
, minlen
);
271 static void tree_iterator__frame_seek_start(tree_iterator_frame
*tf
)
275 else if (!tf
->icase_map
)
276 tf
->index
= git_tree__prefix_position(tf
->tree
, tf
->start
);
279 tf
->icase_map
, git_tree_entrycount(tf
->tree
),
280 tf
, tree_iterator__frame_start_icmp
, &tf
->index
))
282 while (tf
->index
> 0) {
283 /* move back while previous entry is still prefixed */
284 if (tree_iterator__frame_start_icmp(
285 tf
, (const void *)(tf
->index
- 1)))
293 static tree_iterator_frame
*tree_iterator__alloc_frame(
294 tree_iterator
*ti
, git_tree
*tree
, char *start
)
296 size_t i
, max_i
= git_tree_entrycount(tree
);
297 tree_iterator_frame
*tf
=
298 git__calloc(1, sizeof(tree_iterator_frame
) + max_i
* sizeof(void *));
304 if (start
&& *start
) {
306 tf
->startlen
= strlen(start
);
312 if ((ti
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) {
313 tf
->icase_map
= tf
->icase_data
;
315 for (i
= 0; i
< max_i
; ++i
)
316 tf
->icase_map
[i
] = (void *)i
;
319 tf
->icase_map
, max_i
, tree_iterator__icase_map_cmp
, tf
->tree
);
322 tree_iterator__frame_seek_start(tf
);
327 static int tree_iterator__expand_tree(tree_iterator
*ti
)
331 const git_tree_entry
*te
= tree_iterator__tree_entry(ti
);
332 tree_iterator_frame
*tf
;
335 while (te
!= NULL
&& git_tree_entry__is_tree(te
)) {
336 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
339 /* check that we have not passed the range end */
340 if (ti
->base
.end
!= NULL
&&
341 ti
->base
.prefixcomp(ti
->path
.ptr
, ti
->base
.end
) > 0)
342 return tree_iterator__to_end(ti
);
344 if ((error
= git_tree_lookup(&subtree
, ti
->base
.repo
, &te
->oid
)) < 0)
349 /* apply range start to new frame if relevant */
350 if (ti
->stack
->start
&&
351 ti
->base
.prefixcomp(ti
->stack
->start
, te
->filename
) == 0)
353 if (ti
->stack
->start
[te
->filename_len
] == '/')
354 relpath
= ti
->stack
->start
+ te
->filename_len
+ 1;
357 if ((tf
= tree_iterator__alloc_frame(ti
, subtree
, relpath
)) == NULL
)
360 tf
->next
= ti
->stack
;
364 te
= tree_iterator__tree_entry(ti
);
370 static int tree_iterator__advance(
371 git_iterator
*self
, const git_index_entry
**entry
)
374 tree_iterator
*ti
= (tree_iterator
*)self
;
375 const git_tree_entry
*te
= NULL
;
380 if (ti
->path_has_filename
) {
381 git_buf_rtruncate_at_char(&ti
->path
, '/');
382 ti
->path_has_filename
= false;
388 if ((te
= tree_iterator__tree_entry(ti
)) != NULL
)
391 if (!tree_iterator__pop_frame(ti
))
392 break; /* no frames left to pop */
394 git_buf_rtruncate_at_char(&ti
->path
, '/');
397 if (te
&& git_tree_entry__is_tree(te
))
398 error
= tree_iterator__expand_tree(ti
);
401 error
= tree_iterator__current(self
, entry
);
406 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
410 /* pop stack until matches prefix */
411 /* seek item in current frame matching prefix */
412 /* push stack which matches prefix */
416 static void tree_iterator__free(git_iterator
*self
)
418 tree_iterator
*ti
= (tree_iterator
*)self
;
420 while (tree_iterator__pop_frame(ti
)) /* pop all */;
422 tree_iterator__free_frame(ti
->stack
);
423 ti
->stack
= ti
->tail
= NULL
;
425 git_buf_free(&ti
->path
);
428 static int tree_iterator__reset(
429 git_iterator
*self
, const char *start
, const char *end
)
431 tree_iterator
*ti
= (tree_iterator
*)self
;
433 while (tree_iterator__pop_frame(ti
)) /* pop all */;
435 if (iterator__reset_range(self
, start
, end
) < 0)
438 /* reset start position */
439 tree_iterator__frame_seek_start(ti
->stack
);
441 git_buf_clear(&ti
->path
);
442 ti
->path_has_filename
= false;
444 return tree_iterator__expand_tree(ti
);
447 int git_iterator_for_tree_range(
450 git_iterator_flag_t flags
,
458 return git_iterator_for_nothing(iter
, flags
);
460 if ((error
= git_tree__dup(&tree
, tree
)) < 0)
463 ITERATOR_BASE_INIT(ti
, tree
, TREE
);
465 ti
->base
.repo
= git_tree_owner(tree
);
467 if ((error
= iterator_update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
470 ti
->stack
= ti
->tail
= tree_iterator__alloc_frame(ti
, tree
, ti
->base
.start
);
472 if ((error
= tree_iterator__expand_tree(ti
)) < 0)
475 *iter
= (git_iterator
*)ti
;
479 git_iterator_free((git_iterator
*)ti
);
486 git_iterator_callbacks cb
;
491 static int index_iterator__current(
492 git_iterator
*self
, const git_index_entry
**entry
)
494 index_iterator
*ii
= (index_iterator
*)self
;
495 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
503 static int index_iterator__at_end(git_iterator
*self
)
505 index_iterator
*ii
= (index_iterator
*)self
;
506 return (ii
->current
>= git_index_entrycount(ii
->index
));
509 static void index_iterator__skip_conflicts(
512 size_t entrycount
= git_index_entrycount(ii
->index
);
513 const git_index_entry
*ie
;
515 while (ii
->current
< entrycount
) {
516 ie
= git_index_get_byindex(ii
->index
, ii
->current
);
519 (ii
->base
.end
!= NULL
&&
520 ii
->base
.prefixcomp(ie
->path
, ii
->base
.end
) > 0)) {
521 ii
->current
= entrycount
;
525 if (git_index_entry_stage(ie
) == 0)
532 static int index_iterator__advance(
533 git_iterator
*self
, const git_index_entry
**entry
)
535 index_iterator
*ii
= (index_iterator
*)self
;
537 if (ii
->current
< git_index_entrycount(ii
->index
))
540 index_iterator__skip_conflicts(ii
);
542 return index_iterator__current(self
, entry
);
545 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
549 /* find last item before prefix */
553 static int index_iterator__reset(
554 git_iterator
*self
, const char *start
, const char *end
)
556 index_iterator
*ii
= (index_iterator
*)self
;
557 if (iterator__reset_range(self
, start
, end
) < 0)
559 ii
->current
= ii
->base
.start
?
560 git_index__prefix_position(ii
->index
, ii
->base
.start
) : 0;
561 index_iterator__skip_conflicts(ii
);
565 static void index_iterator__free(git_iterator
*self
)
567 index_iterator
*ii
= (index_iterator
*)self
;
568 git_index_free(ii
->index
);
572 int git_iterator_for_index_range(
575 git_iterator_flag_t flags
,
583 ITERATOR_BASE_INIT(ii
, index
, INDEX
);
585 ii
->base
.repo
= git_index_owner(index
);
586 if (index
->ignore_case
) {
587 ii
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
588 ii
->base
.prefixcomp
= git__prefixcmp_icase
;
591 GIT_REFCOUNT_INC(index
);
593 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
595 *iter
= (git_iterator
*)ii
;
601 typedef struct workdir_iterator_frame workdir_iterator_frame
;
602 struct workdir_iterator_frame
{
603 workdir_iterator_frame
*next
;
610 git_iterator_callbacks cb
;
611 workdir_iterator_frame
*stack
;
612 int (*entrycmp
)(const void *pfx
, const void *item
);
614 git_index_entry entry
;
620 GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat
*ps
)
625 const char *path
= ps
->path
;
626 size_t len
= ps
->path_len
;
630 if (path
[len
- 1] == '/')
632 if (tolower(path
[len
- 1]) != 't' ||
633 tolower(path
[len
- 2]) != 'i' ||
634 tolower(path
[len
- 3]) != 'g' ||
635 tolower(path
[len
- 4]) != '.')
637 return (len
== 4 || path
[len
- 5] == '/');
641 static workdir_iterator_frame
*workdir_iterator__alloc_frame(
642 workdir_iterator
*wi
)
644 workdir_iterator_frame
*wf
= git__calloc(1, sizeof(workdir_iterator_frame
));
645 git_vector_cmp entry_compare
= CASESELECT(
646 (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0,
647 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
652 if (git_vector_init(&wf
->entries
, 0, entry_compare
) != 0) {
660 static void workdir_iterator__free_frame(workdir_iterator_frame
*wf
)
663 git_path_with_stat
*path
;
665 git_vector_foreach(&wf
->entries
, i
, path
)
667 git_vector_free(&wf
->entries
);
671 static int workdir_iterator__update_entry(workdir_iterator
*wi
);
673 static int workdir_iterator__entry_cmp_case(const void *pfx
, const void *item
)
675 const git_path_with_stat
*ps
= item
;
676 return git__prefixcmp((const char *)pfx
, ps
->path
);
679 static int workdir_iterator__entry_cmp_icase(const void *pfx
, const void *item
)
681 const git_path_with_stat
*ps
= item
;
682 return git__prefixcmp_icase((const char *)pfx
, ps
->path
);
685 static void workdir_iterator__seek_frame_start(
686 workdir_iterator
*wi
, workdir_iterator_frame
*wf
)
693 &wf
->index
, &wf
->entries
, wi
->entrycmp
, wi
->base
.start
);
697 if (path_is_dotgit(git_vector_get(&wf
->entries
, wf
->index
)))
701 static int workdir_iterator__expand_dir(workdir_iterator
*wi
)
704 workdir_iterator_frame
*wf
= workdir_iterator__alloc_frame(wi
);
705 GITERR_CHECK_ALLOC(wf
);
707 error
= git_path_dirload_with_stat(
708 wi
->path
.ptr
, wi
->root_len
,
709 (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0,
710 wi
->base
.start
, wi
->base
.end
, &wf
->entries
);
712 if (error
< 0 || wf
->entries
.length
== 0) {
713 workdir_iterator__free_frame(wf
);
714 return GIT_ENOTFOUND
;
717 workdir_iterator__seek_frame_start(wi
, wf
);
719 /* only push new ignores if this is not top level directory */
720 if (wi
->stack
!= NULL
) {
721 ssize_t slash_pos
= git_buf_rfind_next(&wi
->path
, '/');
722 (void)git_ignore__push_dir(&wi
->ignores
, &wi
->path
.ptr
[slash_pos
+ 1]);
725 wf
->next
= wi
->stack
;
728 return workdir_iterator__update_entry(wi
);
731 static int workdir_iterator__current(
732 git_iterator
*self
, const git_index_entry
**entry
)
734 workdir_iterator
*wi
= (workdir_iterator
*)self
;
735 *entry
= (wi
->entry
.path
== NULL
) ? NULL
: &wi
->entry
;
739 static int workdir_iterator__at_end(git_iterator
*self
)
741 return (((workdir_iterator
*)self
)->entry
.path
== NULL
);
744 static int workdir_iterator__advance(
745 git_iterator
*self
, const git_index_entry
**entry
)
748 workdir_iterator
*wi
= (workdir_iterator
*)self
;
749 workdir_iterator_frame
*wf
;
750 git_path_with_stat
*next
;
755 if (wi
->entry
.path
== NULL
)
760 next
= git_vector_get(&wf
->entries
, ++wf
->index
);
763 /* match git's behavior of ignoring anything named ".git" */
764 if (path_is_dotgit(next
))
766 /* else found a good entry */
770 /* pop stack if anything is left to pop */
772 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
776 wi
->stack
= wf
->next
;
777 workdir_iterator__free_frame(wf
);
778 git_ignore__pop_dir(&wi
->ignores
);
781 error
= workdir_iterator__update_entry(wi
);
783 if (!error
&& entry
!= NULL
)
784 error
= workdir_iterator__current(self
, entry
);
789 static int workdir_iterator__seek(git_iterator
*self
, const char *prefix
)
793 /* pop stack until matching prefix */
794 /* find prefix item in current frame */
795 /* push subdirectories as deep as possible while matching */
799 static int workdir_iterator__reset(
800 git_iterator
*self
, const char *start
, const char *end
)
802 workdir_iterator
*wi
= (workdir_iterator
*)self
;
804 while (wi
->stack
!= NULL
&& wi
->stack
->next
!= NULL
) {
805 workdir_iterator_frame
*wf
= wi
->stack
;
806 wi
->stack
= wf
->next
;
807 workdir_iterator__free_frame(wf
);
808 git_ignore__pop_dir(&wi
->ignores
);
811 if (iterator__reset_range(self
, start
, end
) < 0)
814 workdir_iterator__seek_frame_start(wi
, wi
->stack
);
816 return workdir_iterator__update_entry(wi
);
819 static void workdir_iterator__free(git_iterator
*self
)
821 workdir_iterator
*wi
= (workdir_iterator
*)self
;
823 while (wi
->stack
!= NULL
) {
824 workdir_iterator_frame
*wf
= wi
->stack
;
825 wi
->stack
= wf
->next
;
826 workdir_iterator__free_frame(wf
);
829 git_ignore__free(&wi
->ignores
);
830 git_buf_free(&wi
->path
);
833 static int workdir_iterator__update_entry(workdir_iterator
*wi
)
835 git_path_with_stat
*ps
=
836 git_vector_get(&wi
->stack
->entries
, wi
->stack
->index
);
838 git_buf_truncate(&wi
->path
, wi
->root_len
);
839 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
844 if (git_buf_put(&wi
->path
, ps
->path
, ps
->path_len
) < 0)
848 wi
->base
.prefixcomp(wi
->path
.ptr
+ wi
->root_len
, wi
->base
.end
) > 0)
851 wi
->entry
.path
= ps
->path
;
853 /* skip over .git entries */
854 if (path_is_dotgit(ps
))
855 return workdir_iterator__advance((git_iterator
*)wi
, NULL
);
859 git_index_entry__init_from_stat(&wi
->entry
, &ps
->st
);
861 /* need different mode here to keep directories during iteration */
862 wi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
864 /* if this is a file type we don't handle, treat as ignored */
865 if (wi
->entry
.mode
== 0) {
870 /* detect submodules */
871 if (S_ISDIR(wi
->entry
.mode
)) {
872 int res
= git_submodule_lookup(NULL
, wi
->base
.repo
, wi
->entry
.path
);
873 bool is_submodule
= (res
== 0);
874 if (res
== GIT_ENOTFOUND
)
877 /* if submodule, mark as GITLINK and remove trailing slash */
879 size_t len
= strlen(wi
->entry
.path
);
880 assert(wi
->entry
.path
[len
- 1] == '/');
881 wi
->entry
.path
[len
- 1] = '\0';
882 wi
->entry
.mode
= S_IFGITLINK
;
889 int git_iterator_for_workdir_range(
891 git_repository
*repo
,
892 git_iterator_flag_t flags
,
897 workdir_iterator
*wi
;
899 assert(iter
&& repo
);
901 if ((error
= git_repository__ensure_not_bare(
902 repo
, "scan working directory")) < 0)
905 ITERATOR_BASE_INIT(wi
, workdir
, WORKDIR
);
906 wi
->base
.repo
= repo
;
908 if ((error
= iterator_update_ignore_case((git_iterator
*)wi
, flags
)) < 0)
911 if (git_buf_sets(&wi
->path
, git_repository_workdir(repo
)) < 0 ||
912 git_path_to_dir(&wi
->path
) < 0 ||
913 git_ignore__for_path(repo
, "", &wi
->ignores
) < 0)
919 wi
->root_len
= wi
->path
.size
;
920 wi
->entrycmp
= (wi
->base
.flags
& GIT_ITERATOR_IGNORE_CASE
) != 0 ?
921 workdir_iterator__entry_cmp_icase
: workdir_iterator__entry_cmp_case
;
923 if ((error
= workdir_iterator__expand_dir(wi
)) < 0) {
924 if (error
!= GIT_ENOTFOUND
)
929 *iter
= (git_iterator
*)wi
;
933 git_iterator_free((git_iterator
*)wi
);
939 /* replacement callbacks */
940 git_iterator_callbacks cb
;
941 /* original iterator values */
942 git_iterator_callbacks
*orig
;
943 git_iterator_type_t orig_type
;
944 /* spoolandsort data */
947 git_pool string_pool
;
949 } spoolandsort_callbacks
;
951 static int spoolandsort_iterator__current(
952 git_iterator
*self
, const git_index_entry
**entry
)
954 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
956 *entry
= (const git_index_entry
*)
957 git_vector_get(&scb
->entries
, scb
->position
);
962 static int spoolandsort_iterator__at_end(git_iterator
*self
)
964 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
966 return 0 == scb
->entries
.length
|| scb
->entries
.length
- 1 <= scb
->position
;
969 static int spoolandsort_iterator__advance(
970 git_iterator
*self
, const git_index_entry
**entry
)
972 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
974 *entry
= (const git_index_entry
*)
975 git_vector_get(&scb
->entries
, ++scb
->position
);
980 static int spoolandsort_iterator__seek(git_iterator
*self
, const char *prefix
)
988 static int spoolandsort_iterator__reset(
989 git_iterator
*self
, const char *start
, const char *end
)
991 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
993 GIT_UNUSED(start
); GIT_UNUSED(end
);
1000 static void spoolandsort_iterator__free_callbacks(spoolandsort_callbacks
*scb
)
1002 git_pool_clear(&scb
->string_pool
);
1003 git_pool_clear(&scb
->entry_pool
);
1004 git_vector_free(&scb
->entries
);
1008 void git_iterator_spoolandsort_pop(git_iterator
*self
)
1010 spoolandsort_callbacks
*scb
= (spoolandsort_callbacks
*)self
->cb
;
1012 if (self
->type
!= GIT_ITERATOR_TYPE_SPOOLANDSORT
)
1015 self
->cb
= scb
->orig
;
1016 self
->type
= scb
->orig_type
;
1017 self
->flags
^= GIT_ITERATOR_IGNORE_CASE
;
1019 spoolandsort_iterator__free_callbacks(scb
);
1022 static void spoolandsort_iterator__free(git_iterator
*self
)
1024 git_iterator_spoolandsort_pop(self
);
1025 self
->cb
->free(self
);
1028 int git_iterator_spoolandsort_push(git_iterator
*iter
, bool ignore_case
)
1030 const git_index_entry
*item
;
1031 spoolandsort_callbacks
*scb
;
1032 int (*entrycomp
)(const void *a
, const void *b
);
1034 if (((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) == (ignore_case
!= 0))
1037 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1038 iter
->flags
= (iter
->flags
^ GIT_ITERATOR_IGNORE_CASE
);
1042 scb
= git__calloc(1, sizeof(spoolandsort_callbacks
));
1043 GITERR_CHECK_ALLOC(scb
);
1045 ITERATOR_SET_CB(scb
,spoolandsort
);
1047 scb
->orig
= iter
->cb
;
1048 scb
->orig_type
= iter
->type
;
1051 entrycomp
= ignore_case
? git_index_entry__cmp_icase
: git_index_entry__cmp
;
1053 if (git_vector_init(&scb
->entries
, 16, entrycomp
) < 0 ||
1054 git_pool_init(&scb
->entry_pool
, sizeof(git_index_entry
), 0) < 0 ||
1055 git_pool_init(&scb
->string_pool
, 1, 0) < 0 ||
1056 git_iterator_current(iter
, &item
) < 0)
1060 git_index_entry
*clone
= git_pool_malloc(&scb
->entry_pool
, 1);
1064 memcpy(clone
, item
, sizeof(git_index_entry
));
1067 clone
->path
= git_pool_strdup(&scb
->string_pool
, item
->path
);
1072 if (git_vector_insert(&scb
->entries
, clone
) < 0)
1075 if (git_iterator_advance(iter
, &item
) < 0)
1079 git_vector_sort(&scb
->entries
);
1081 iter
->cb
= (git_iterator_callbacks
*)scb
;
1082 iter
->type
= GIT_ITERATOR_TYPE_SPOOLANDSORT
;
1083 iter
->flags
^= GIT_ITERATOR_IGNORE_CASE
;
1088 spoolandsort_iterator__free_callbacks(scb
);
1093 void git_iterator_free(git_iterator
*iter
)
1098 iter
->cb
->free(iter
);
1100 git__free(iter
->start
);
1101 git__free(iter
->end
);
1103 memset(iter
, 0, sizeof(*iter
));
1108 git_index
*git_iterator_index_get_index(git_iterator
*iter
)
1110 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1111 return ((index_iterator
*)iter
)->index
;
1113 if (iter
->type
== GIT_ITERATOR_TYPE_SPOOLANDSORT
&&
1114 ((spoolandsort_callbacks
*)iter
->cb
)->orig_type
== GIT_ITERATOR_TYPE_INDEX
)
1115 return ((index_iterator
*)iter
)->index
;
1120 git_iterator_type_t
git_iterator_inner_type(git_iterator
*iter
)
1122 if (iter
->type
== GIT_ITERATOR_TYPE_SPOOLANDSORT
)
1123 return ((spoolandsort_callbacks
*)iter
->cb
)->orig_type
;
1128 int git_iterator_current_tree_entry(
1129 git_iterator
*iter
, const git_tree_entry
**tree_entry
)
1131 *tree_entry
= (iter
->type
!= GIT_ITERATOR_TYPE_TREE
) ? NULL
:
1132 tree_iterator__tree_entry((tree_iterator
*)iter
);
1136 int git_iterator_current_parent_tree(
1138 const char *parent_path
,
1139 const git_tree
**tree_ptr
)
1141 tree_iterator
*ti
= (tree_iterator
*)iter
;
1142 tree_iterator_frame
*tf
;
1143 const char *scan
= parent_path
;
1144 int (*strncomp
)(const char *a
, const char *b
, size_t sz
);
1146 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
|| ti
->stack
== NULL
)
1149 strncomp
= ((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) ?
1150 git__strncasecmp
: git__strncmp
;
1152 for (tf
= ti
->tail
; tf
!= NULL
; tf
= tf
->prev
) {
1153 const git_tree_entry
*te
;
1156 *tree_ptr
= tf
->tree
;
1160 te
= git_tree_entry_byindex(tf
->tree
,
1161 tf
->icase_map
? (size_t)tf
->icase_map
[tf
->index
] : tf
->index
);
1163 if (strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1166 scan
+= te
->filename_len
;
1180 int git_iterator_current_is_ignored(git_iterator
*iter
)
1182 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1184 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1187 if (wi
->is_ignored
!= -1)
1188 return wi
->is_ignored
;
1190 if (git_ignore__lookup(&wi
->ignores
, wi
->entry
.path
, &wi
->is_ignored
) < 0)
1193 return wi
->is_ignored
;
1196 int git_iterator_advance_into_directory(
1197 git_iterator
*iter
, const git_index_entry
**entry
)
1199 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1201 if (iter
->type
== GIT_ITERATOR_TYPE_WORKDIR
&&
1203 (wi
->entry
.mode
== GIT_FILEMODE_TREE
||
1204 wi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
1206 if (workdir_iterator__expand_dir(wi
) < 0)
1207 /* if error loading or if empty, skip the directory. */
1208 return workdir_iterator__advance(iter
, entry
);
1211 return entry
? git_iterator_current(iter
, entry
) : 0;
1214 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1216 const git_index_entry
*entry
;
1218 /* a "done" iterator is after every prefix */
1219 if (git_iterator_current(iter
, &entry
) < 0 ||
1223 /* a NULL prefix is after any valid iterator */
1227 return iter
->prefixcomp(entry
->path
, path_prefix
);
1230 int git_iterator_current_workdir_path(git_iterator
*iter
, git_buf
**path
)
1232 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1234 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->entry
.path
)