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.
13 #include "git2/submodule.h"
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; \
26 #define ITERATOR_CASE_FLAGS \
27 (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
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; \
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)
50 #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
51 #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
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)
58 static int iterator__reset_range(
59 git_iterator
*iter
, const char *start
, const char *end
)
63 git__free(iter
->start
);
64 iter
->start
= git__strdup(start
);
65 GITERR_CHECK_ALLOC(iter
->start
);
71 iter
->end
= git__strdup(end
);
72 GITERR_CHECK_ALLOC(iter
->end
);
75 iter
->flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
80 static int iterator__update_ignore_case(
82 git_iterator_flag_t flags
)
84 int error
= 0, ignore_case
= -1;
86 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
88 else if ((flags
& GIT_ITERATOR_DONT_IGNORE_CASE
) != 0)
93 if (!(error
= git_repository_index__weakptr(&index
, iter
->repo
)))
94 ignore_case
= (index
->ignore_case
!= false);
98 iter
->flags
= (iter
->flags
| GIT_ITERATOR_IGNORE_CASE
);
99 else if (ignore_case
== 0)
100 iter
->flags
= (iter
->flags
& ~GIT_ITERATOR_IGNORE_CASE
);
102 iter
->prefixcomp
= iterator__ignore_case(iter
) ?
103 git__prefixcmp_icase
: git__prefixcmp
;
108 GIT_INLINE(void) iterator__clear_entry(const git_index_entry
**entry
)
110 if (entry
) *entry
= NULL
;
114 static int empty_iterator__noop(const git_index_entry
**e
, git_iterator
*i
)
117 iterator__clear_entry(e
);
121 static int empty_iterator__seek(git_iterator
*i
, const char *p
)
123 GIT_UNUSED(i
); GIT_UNUSED(p
);
127 static int empty_iterator__reset(git_iterator
*i
, const char *s
, const char *e
)
129 GIT_UNUSED(i
); GIT_UNUSED(s
); GIT_UNUSED(e
);
133 static int empty_iterator__at_end(git_iterator
*i
)
139 static void empty_iterator__free(git_iterator
*i
)
146 git_iterator_callbacks cb
;
149 int git_iterator_for_nothing(
151 git_iterator_flag_t flags
,
155 empty_iterator
*i
= git__calloc(1, sizeof(empty_iterator
));
156 GITERR_CHECK_ALLOC(i
);
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
162 ITERATOR_BASE_INIT(i
, empty
, EMPTY
, NULL
);
164 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
165 i
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
167 *iter
= (git_iterator
*)i
;
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
;
179 typedef struct tree_iterator_frame tree_iterator_frame
;
180 struct tree_iterator_frame
{
181 tree_iterator_frame
*up
, *down
;
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 */
190 tree_iterator_entry
*entries
[GIT_FLEX_ARRAY
];
195 git_iterator_callbacks cb
;
196 tree_iterator_frame
*head
, *root
;
198 git_index_entry entry
;
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
);
206 static char *tree_iterator__current_filename(
207 tree_iterator
*ti
, const git_tree_entry
*te
)
209 if (!ti
->path_has_filename
) {
210 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
213 if (git_tree_entry__is_tree(te
) && git_buf_putc(&ti
->path
, '/') < 0)
216 ti
->path_has_filename
= true;
222 static void tree_iterator__rewrite_filename(tree_iterator
*ti
)
224 tree_iterator_entry
*scan
= ti
->head
->entries
[ti
->head
->current
];
225 ssize_t strpos
= ti
->path
.size
;
226 const git_tree_entry
*te
;
228 if (strpos
&& ti
->path
.ptr
[strpos
- 1] == '/')
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 */
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))
244 a
->filename
, a
->filename_len
, a
->attr
== GIT_FILEMODE_TREE
,
245 b
->filename
, b
->filename_len
, b
->attr
== GIT_FILEMODE_TREE
,
249 static int tree_iterator__ci_cmp(const void *a
, const void *b
, void *p
)
251 const tree_iterator_entry
*ae
= a
, *be
= b
;
252 int cmp
= tree_iterator__te_cmp(ae
->te
, be
->te
, git__strncasecmp
);
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
);
259 cmp
= tree_iterator__ci_cmp(ae
->parent
, be
->parent
, p
);
265 static int tree_iterator__search_cmp(const void *key
, const void *val
, void *p
)
267 const tree_iterator_frame
*tf
= key
;
268 const git_tree_entry
*te
= ((tree_iterator_entry
*)val
)->te
;
271 tf
->start
, tf
->startlen
, false,
272 te
->filename
, te
->filename_len
, te
->attr
== GIT_FILEMODE_TREE
,
273 ((tree_iterator
*)p
)->strncomp
);
276 static bool tree_iterator__move_to_next(
277 tree_iterator
*ti
, tree_iterator_frame
*tf
)
279 if (tf
->next
> tf
->current
+ 1)
280 ti
->path_ambiguities
--;
282 if (!tf
->up
) { /* at root */
283 tf
->current
= tf
->next
;
287 for (; tf
->current
< tf
->next
; tf
->current
++) {
288 git_tree_free(tf
->entries
[tf
->current
]->tree
);
289 tf
->entries
[tf
->current
]->tree
= NULL
;
292 return (tf
->current
< tf
->n_entries
);
295 static int tree_iterator__set_next(tree_iterator
*ti
, tree_iterator_frame
*tf
)
298 const git_tree_entry
*te
, *last
= NULL
;
300 tf
->next
= tf
->current
;
302 for (; tf
->next
< tf
->n_entries
; tf
->next
++, last
= te
) {
303 te
= tf
->entries
[tf
->next
]->te
;
305 if (last
&& tree_iterator__te_cmp(last
, te
, ti
->strncomp
))
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
);
314 if (tf
->next
> tf
->current
+ 1)
315 ti
->path_ambiguities
++;
317 /* if a tree lookup failed, advance over this span and return failure */
319 tree_iterator__move_to_next(ti
, tf
);
323 if (last
&& !tree_iterator__current_filename(ti
, last
))
324 return -1; /* must have been allocation failure */
329 GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator
*ti
)
331 return (ti
->head
->current
< ti
->head
->n_entries
&&
332 ti
->head
->entries
[ti
->head
->current
]->tree
!= NULL
);
335 static int tree_iterator__push_frame(tree_iterator
*ti
)
338 tree_iterator_frame
*head
= ti
->head
, *tf
= NULL
;
339 size_t i
, n_entries
= 0;
341 if (head
->current
>= head
->n_entries
|| !head
->entries
[head
->current
]->tree
)
344 for (i
= head
->current
; i
< head
->next
; ++i
)
345 n_entries
+= git_tree_entrycount(head
->entries
[i
]->tree
);
347 tf
= git__calloc(sizeof(tree_iterator_frame
) +
348 n_entries
* sizeof(tree_iterator_entry
*), 1);
349 GITERR_CHECK_ALLOC(tf
);
351 tf
->n_entries
= n_entries
;
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
);
361 for (j
= 0; j
< max_j
; ++j
) {
362 tree_iterator_entry
*entry
= git_pool_malloc(&ti
->pool
, 1);
363 GITERR_CHECK_ALLOC(entry
);
365 entry
->parent
= head
->entries
[i
];
366 entry
->te
= git_tree_entry_byindex(tree
, j
);
369 tf
->entries
[n_entries
++] = entry
;
373 /* if ignore_case, sort entries case insensitively */
374 if (iterator__ignore_case(ti
))
376 (void **)tf
->entries
, tf
->n_entries
, tree_iterator__ci_cmp
, tf
);
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
);
383 while (tf
->current
&&
384 !tree_iterator__search_cmp(head
, tf
->entries
[tf
->current
-1], ti
))
387 if ((tf
->start
= strchr(head
->start
, '/')) != NULL
) {
389 tf
->startlen
= strlen(tf
->start
);
393 ti
->path_has_filename
= ti
->entry_is_current
= false;
395 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
398 /* autoexpand as needed */
399 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
400 return tree_iterator__push_frame(ti
);
405 static bool tree_iterator__pop_frame(tree_iterator
*ti
, bool final
)
407 tree_iterator_frame
*tf
= ti
->head
;
413 ti
->head
->down
= NULL
;
415 tree_iterator__move_to_next(ti
, tf
);
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
, '/');
427 static void tree_iterator__pop_all(tree_iterator
*ti
, bool to_end
, bool final
)
429 while (tree_iterator__pop_frame(ti
, final
)) /* pop to root */;
432 ti
->head
->current
= to_end
? ti
->head
->n_entries
: 0;
433 ti
->path_ambiguities
= 0;
434 git_buf_clear(&ti
->path
);
438 static int tree_iterator__update_entry(tree_iterator
*ti
)
440 tree_iterator_frame
*tf
;
441 const git_tree_entry
*te
;
443 if (ti
->entry_is_current
)
447 te
= tf
->entries
[tf
->current
]->te
;
449 ti
->entry
.mode
= te
->attr
;
450 git_oid_cpy(&ti
->entry
.oid
, &te
->oid
);
452 ti
->entry
.path
= tree_iterator__current_filename(ti
, te
);
453 GITERR_CHECK_ALLOC(ti
->entry
.path
);
455 if (ti
->path_ambiguities
> 0)
456 tree_iterator__rewrite_filename(ti
);
458 if (iterator__past_end(ti
, ti
->entry
.path
)) {
459 tree_iterator__pop_all(ti
, true, false);
463 ti
->entry_is_current
= true;
468 static int tree_iterator__current(
469 const git_index_entry
**entry
, git_iterator
*self
)
472 tree_iterator
*ti
= (tree_iterator
*)self
;
473 tree_iterator_frame
*tf
= ti
->head
;
475 iterator__clear_entry(entry
);
477 if (tf
->current
>= tf
->n_entries
)
480 if ((error
= tree_iterator__update_entry(ti
)) < 0)
486 ti
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
491 static int tree_iterator__advance_into(
492 const git_index_entry
**entry
, git_iterator
*self
)
495 tree_iterator
*ti
= (tree_iterator
*)self
;
497 iterator__clear_entry(entry
);
499 if (tree_iterator__at_tree(ti
))
500 error
= tree_iterator__push_frame(ti
);
503 error
= tree_iterator__current(entry
, self
);
508 static int tree_iterator__advance(
509 const git_index_entry
**entry
, git_iterator
*self
)
512 tree_iterator
*ti
= (tree_iterator
*)self
;
513 tree_iterator_frame
*tf
= ti
->head
;
515 iterator__clear_entry(entry
);
517 if (tf
->current
>= tf
->n_entries
)
520 if (!iterator__has_been_accessed(ti
))
521 return tree_iterator__current(entry
, self
);
523 if (iterator__do_autoexpand(ti
) && iterator__include_trees(ti
) &&
524 tree_iterator__at_tree(ti
))
525 return tree_iterator__advance_into(entry
, self
);
527 if (ti
->path_has_filename
) {
528 git_buf_rtruncate_at_char(&ti
->path
, '/');
529 ti
->path_has_filename
= ti
->entry_is_current
= false;
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))
537 /* find next and load trees */
538 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
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
);
545 return tree_iterator__current(entry
, self
);
548 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
550 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
554 static int tree_iterator__reset(
555 git_iterator
*self
, const char *start
, const char *end
)
557 tree_iterator
*ti
= (tree_iterator
*)self
;
559 tree_iterator__pop_all(ti
, false, false);
561 if (iterator__reset_range(self
, start
, end
) < 0)
564 return tree_iterator__push_frame(ti
); /* re-expand root tree */
567 static int tree_iterator__at_end(git_iterator
*self
)
569 tree_iterator
*ti
= (tree_iterator
*)self
;
570 return (ti
->head
->current
>= ti
->head
->n_entries
);
573 static void tree_iterator__free(git_iterator
*self
)
575 tree_iterator
*ti
= (tree_iterator
*)self
;
577 tree_iterator__pop_all(ti
, true, false);
579 git_tree_free(ti
->head
->entries
[0]->tree
);
581 git_pool_clear(&ti
->pool
);
582 git_buf_free(&ti
->path
);
585 static int tree_iterator__create_root_frame(tree_iterator
*ti
, git_tree
*tree
)
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
);
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
;
599 ti
->head
= ti
->root
= root
;
604 int git_iterator_for_tree(
607 git_iterator_flag_t flags
,
615 return git_iterator_for_nothing(iter
, flags
, start
, end
);
617 if ((error
= git_object_dup((git_object
**)&tree
, (git_object
*)tree
)) < 0)
620 ti
= git__calloc(1, sizeof(tree_iterator
));
621 GITERR_CHECK_ALLOC(ti
);
623 ITERATOR_BASE_INIT(ti
, tree
, TREE
, git_tree_owner(tree
));
625 if ((error
= iterator__update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
627 ti
->strncomp
= iterator__ignore_case(ti
) ? git__strncasecmp
: git__strncmp
;
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 */
634 *iter
= (git_iterator
*)ti
;
638 git_iterator_free((git_iterator
*)ti
);
645 git_iterator_callbacks cb
;
648 /* when not in autoexpand mode, use these to represent "tree" state */
651 char restore_terminator
;
652 git_index_entry tree_entry
;
655 static const git_index_entry
*index_iterator__index_entry(index_iterator
*ii
)
657 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
659 if (ie
!= NULL
&& iterator__past_end(ii
, ie
->path
)) {
660 ii
->current
= git_index_entrycount(ii
->index
);
667 static const git_index_entry
*index_iterator__skip_conflicts(index_iterator
*ii
)
669 const git_index_entry
*ie
;
671 while ((ie
= index_iterator__index_entry(ii
)) != NULL
&&
672 git_index_entry_stage(ie
) != 0)
678 static void index_iterator__next_prefix_tree(index_iterator
*ii
)
682 if (!iterator__include_trees(ii
))
685 slash
= strchr(&ii
->partial
.ptr
[ii
->partial_pos
], '/');
688 ii
->partial_pos
= (slash
- ii
->partial
.ptr
) + 1;
689 ii
->restore_terminator
= ii
->partial
.ptr
[ii
->partial_pos
];
690 ii
->partial
.ptr
[ii
->partial_pos
] = '\0';
692 ii
->partial_pos
= ii
->partial
.size
;
695 if (index_iterator__index_entry(ii
) == NULL
)
696 ii
->partial_pos
= ii
->partial
.size
;
699 static int index_iterator__first_prefix_tree(index_iterator
*ii
)
701 const git_index_entry
*ie
= index_iterator__skip_conflicts(ii
);
702 const char *scan
, *prior
, *slash
;
704 if (!ie
|| !iterator__include_trees(ii
))
707 /* find longest common prefix with prior index entry */
708 for (scan
= slash
= ie
->path
, prior
= ii
->partial
.ptr
;
709 *scan
&& *scan
== *prior
; ++scan
, ++prior
)
713 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
716 ii
->partial_pos
= (slash
- ie
->path
) + 1;
717 index_iterator__next_prefix_tree(ii
);
722 #define index_iterator__at_tree(I) \
723 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
725 static int index_iterator__current(
726 const git_index_entry
**entry
, git_iterator
*self
)
728 index_iterator
*ii
= (index_iterator
*)self
;
729 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
731 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
732 ii
->tree_entry
.path
= ii
->partial
.ptr
;
733 ie
= &ii
->tree_entry
;
739 ii
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
741 return (ie
!= NULL
) ? 0 : GIT_ITEROVER
;
744 static int index_iterator__at_end(git_iterator
*self
)
746 index_iterator
*ii
= (index_iterator
*)self
;
747 return (ii
->current
>= git_index_entrycount(ii
->index
));
750 static int index_iterator__advance(
751 const git_index_entry
**entry
, git_iterator
*self
)
753 index_iterator
*ii
= (index_iterator
*)self
;
754 size_t entrycount
= git_index_entrycount(ii
->index
);
755 const git_index_entry
*ie
;
757 if (!iterator__has_been_accessed(ii
))
758 return index_iterator__current(entry
, self
);
760 if (index_iterator__at_tree(ii
)) {
761 if (iterator__do_autoexpand(ii
)) {
762 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
763 index_iterator__next_prefix_tree(ii
);
765 /* advance to sibling tree (i.e. find entry with new prefix) */
766 while (ii
->current
< entrycount
) {
769 if (!(ie
= git_index_get_byindex(ii
->index
, ii
->current
)) ||
770 ii
->base
.prefixcomp(ie
->path
, ii
->partial
.ptr
) != 0)
774 if (index_iterator__first_prefix_tree(ii
) < 0)
778 if (ii
->current
< entrycount
)
781 if (index_iterator__first_prefix_tree(ii
) < 0)
785 return index_iterator__current(entry
, self
);
788 static int index_iterator__advance_into(
789 const git_index_entry
**entry
, git_iterator
*self
)
791 index_iterator
*ii
= (index_iterator
*)self
;
792 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
794 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
795 if (ii
->restore_terminator
)
796 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
797 index_iterator__next_prefix_tree(ii
);
800 return index_iterator__current(entry
, self
);
803 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
805 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
809 static int index_iterator__reset(
810 git_iterator
*self
, const char *start
, const char *end
)
812 index_iterator
*ii
= (index_iterator
*)self
;
813 const git_index_entry
*ie
;
815 if (iterator__reset_range(self
, start
, end
) < 0)
818 ii
->current
= ii
->base
.start
?
819 git_index__prefix_position(ii
->index
, ii
->base
.start
) : 0;
821 if ((ie
= index_iterator__skip_conflicts(ii
)) == NULL
)
824 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
829 if (ii
->base
.start
) {
830 size_t startlen
= strlen(ii
->base
.start
);
832 ii
->partial_pos
= (startlen
> ii
->partial
.size
) ?
833 ii
->partial
.size
: startlen
;
836 index_iterator__next_prefix_tree(ii
);
841 static void index_iterator__free(git_iterator
*self
)
843 index_iterator
*ii
= (index_iterator
*)self
;
844 git_index_free(ii
->index
);
847 git_buf_free(&ii
->partial
);
850 int git_iterator_for_index(
853 git_iterator_flag_t flags
,
857 index_iterator
*ii
= git__calloc(1, sizeof(index_iterator
));
858 GITERR_CHECK_ALLOC(ii
);
860 ITERATOR_BASE_INIT(ii
, index
, INDEX
, git_index_owner(index
));
862 if (index
->ignore_case
) {
863 ii
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
864 ii
->base
.prefixcomp
= git__prefixcmp_icase
;
868 GIT_REFCOUNT_INC(index
);
870 git_buf_init(&ii
->partial
, 0);
871 ii
->tree_entry
.mode
= GIT_FILEMODE_TREE
;
873 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
875 *iter
= (git_iterator
*)ii
;
881 typedef struct fs_iterator_frame fs_iterator_frame
;
882 struct fs_iterator_frame
{
883 fs_iterator_frame
*next
;
888 typedef struct fs_iterator fs_iterator
;
891 git_iterator_callbacks cb
;
892 fs_iterator_frame
*stack
;
893 git_index_entry entry
;
898 int (*enter_dir_cb
)(fs_iterator
*self
);
899 int (*leave_dir_cb
)(fs_iterator
*self
);
900 int (*update_entry_cb
)(fs_iterator
*self
);
903 #define FS_MAX_DEPTH 100
905 static fs_iterator_frame
*fs_iterator__alloc_frame(fs_iterator
*fi
)
907 fs_iterator_frame
*ff
= git__calloc(1, sizeof(fs_iterator_frame
));
908 git_vector_cmp entry_compare
= CASESELECT(
909 iterator__ignore_case(fi
),
910 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
912 if (ff
&& git_vector_init(&ff
->entries
, 0, entry_compare
) < 0) {
920 static void fs_iterator__free_frame(fs_iterator_frame
*ff
)
923 git_path_with_stat
*path
;
925 git_vector_foreach(&ff
->entries
, i
, path
)
927 git_vector_free(&ff
->entries
);
931 static void fs_iterator__pop_frame(
932 fs_iterator
*fi
, fs_iterator_frame
*ff
, bool pop_last
)
934 if (fi
&& fi
->stack
== ff
) {
935 if (!ff
->next
&& !pop_last
) {
936 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
940 if (fi
->leave_dir_cb
)
941 (void)fi
->leave_dir_cb(fi
);
943 fi
->stack
= ff
->next
;
947 fs_iterator__free_frame(ff
);
950 static int fs_iterator__update_entry(fs_iterator
*fi
);
951 static int fs_iterator__advance_over(
952 const git_index_entry
**entry
, git_iterator
*self
);
954 static int fs_iterator__entry_cmp(const void *i
, const void *item
)
956 const fs_iterator
*fi
= (const fs_iterator
*)i
;
957 const git_path_with_stat
*ps
= item
;
958 return fi
->base
.prefixcomp(fi
->base
.start
, ps
->path
);
961 static void fs_iterator__seek_frame_start(
962 fs_iterator
*fi
, fs_iterator_frame
*ff
)
969 &ff
->index
, &ff
->entries
, fs_iterator__entry_cmp
, fi
);
974 static int fs_iterator__expand_dir(fs_iterator
*fi
)
977 fs_iterator_frame
*ff
;
979 if (fi
->depth
> FS_MAX_DEPTH
) {
980 giterr_set(GITERR_REPOSITORY
,
981 "Directory nesting is too deep (%d)", fi
->depth
);
985 ff
= fs_iterator__alloc_frame(fi
);
986 GITERR_CHECK_ALLOC(ff
);
988 error
= git_path_dirload_with_stat(
989 fi
->path
.ptr
, fi
->root_len
, iterator__ignore_case(fi
),
990 fi
->base
.start
, fi
->base
.end
, &ff
->entries
);
993 fs_iterator__free_frame(ff
);
994 fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
998 if (ff
->entries
.length
== 0) {
999 fs_iterator__free_frame(ff
);
1000 return GIT_ENOTFOUND
;
1003 fs_iterator__seek_frame_start(fi
, ff
);
1005 ff
->next
= fi
->stack
;
1009 if (fi
->enter_dir_cb
&& (error
= fi
->enter_dir_cb(fi
)) < 0)
1012 return fs_iterator__update_entry(fi
);
1015 static int fs_iterator__current(
1016 const git_index_entry
**entry
, git_iterator
*self
)
1018 fs_iterator
*fi
= (fs_iterator
*)self
;
1019 const git_index_entry
*fe
= (fi
->entry
.path
== NULL
) ? NULL
: &fi
->entry
;
1024 fi
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
1026 return (fe
!= NULL
) ? 0 : GIT_ITEROVER
;
1029 static int fs_iterator__at_end(git_iterator
*self
)
1031 return (((fs_iterator
*)self
)->entry
.path
== NULL
);
1034 static int fs_iterator__advance_into(
1035 const git_index_entry
**entry
, git_iterator
*iter
)
1038 fs_iterator
*fi
= (fs_iterator
*)iter
;
1040 iterator__clear_entry(entry
);
1042 /* Allow you to explicitly advance into a commit/submodule (as well as a
1043 * tree) to avoid cases where an entry is mislabeled as a submodule in
1044 * the working directory. The fs iterator will never have COMMMIT
1045 * entries on it's own, but a wrapper might add them.
1047 if (fi
->entry
.path
!= NULL
&&
1048 (fi
->entry
.mode
== GIT_FILEMODE_TREE
||
1049 fi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
1050 /* returns GIT_ENOTFOUND if the directory is empty */
1051 error
= fs_iterator__expand_dir(fi
);
1053 if (!error
&& entry
)
1054 error
= fs_iterator__current(entry
, iter
);
1056 if (!error
&& !fi
->entry
.path
)
1057 error
= GIT_ITEROVER
;
1062 static int fs_iterator__advance_over(
1063 const git_index_entry
**entry
, git_iterator
*self
)
1066 fs_iterator
*fi
= (fs_iterator
*)self
;
1067 fs_iterator_frame
*ff
;
1068 git_path_with_stat
*next
;
1073 while (fi
->entry
.path
!= NULL
) {
1075 next
= git_vector_get(&ff
->entries
, ++ff
->index
);
1080 fs_iterator__pop_frame(fi
, ff
, false);
1083 error
= fs_iterator__update_entry(fi
);
1085 if (!error
&& entry
!= NULL
)
1086 error
= fs_iterator__current(entry
, self
);
1091 static int fs_iterator__advance(
1092 const git_index_entry
**entry
, git_iterator
*self
)
1094 fs_iterator
*fi
= (fs_iterator
*)self
;
1096 if (!iterator__has_been_accessed(fi
))
1097 return fs_iterator__current(entry
, self
);
1099 /* given include_trees & autoexpand, we might have to go into a tree */
1100 if (iterator__do_autoexpand(fi
) &&
1101 fi
->entry
.path
!= NULL
&&
1102 fi
->entry
.mode
== GIT_FILEMODE_TREE
)
1104 int error
= fs_iterator__advance_into(entry
, self
);
1105 if (error
!= GIT_ENOTFOUND
)
1107 /* continue silently past empty directories if autoexpanding */
1111 return fs_iterator__advance_over(entry
, self
);
1114 static int fs_iterator__seek(git_iterator
*self
, const char *prefix
)
1118 /* pop stack until matching prefix */
1119 /* find prefix item in current frame */
1120 /* push subdirectories as deep as possible while matching */
1124 static int fs_iterator__reset(
1125 git_iterator
*self
, const char *start
, const char *end
)
1128 fs_iterator
*fi
= (fs_iterator
*)self
;
1130 while (fi
->stack
!= NULL
&& fi
->stack
->next
!= NULL
)
1131 fs_iterator__pop_frame(fi
, fi
->stack
, false);
1134 if ((error
= iterator__reset_range(self
, start
, end
)) < 0)
1137 fs_iterator__seek_frame_start(fi
, fi
->stack
);
1139 error
= fs_iterator__update_entry(fi
);
1140 if (error
== GIT_ITEROVER
)
1146 static void fs_iterator__free(git_iterator
*self
)
1148 fs_iterator
*fi
= (fs_iterator
*)self
;
1150 while (fi
->stack
!= NULL
)
1151 fs_iterator__pop_frame(fi
, fi
->stack
, true);
1153 git_buf_free(&fi
->path
);
1156 static int fs_iterator__update_entry(fs_iterator
*fi
)
1158 git_path_with_stat
*ps
;
1160 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
1163 return GIT_ITEROVER
;
1165 ps
= git_vector_get(&fi
->stack
->entries
, fi
->stack
->index
);
1167 return GIT_ITEROVER
;
1169 git_buf_truncate(&fi
->path
, fi
->root_len
);
1170 if (git_buf_put(&fi
->path
, ps
->path
, ps
->path_len
) < 0)
1173 if (iterator__past_end(fi
, fi
->path
.ptr
+ fi
->root_len
))
1174 return GIT_ITEROVER
;
1176 fi
->entry
.path
= ps
->path
;
1177 git_index_entry__init_from_stat(&fi
->entry
, &ps
->st
);
1179 /* need different mode here to keep directories during iteration */
1180 fi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
1182 /* allow wrapper to check/update the entry (can force skip) */
1183 if (fi
->update_entry_cb
&&
1184 fi
->update_entry_cb(fi
) == GIT_ENOTFOUND
)
1185 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1187 /* if this is a tree and trees aren't included, then skip */
1188 if (fi
->entry
.mode
== GIT_FILEMODE_TREE
&& !iterator__include_trees(fi
)) {
1189 int error
= fs_iterator__advance_into(NULL
, (git_iterator
*)fi
);
1190 if (error
!= GIT_ENOTFOUND
)
1193 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1199 static int fs_iterator__initialize(
1200 git_iterator
**out
, fs_iterator
*fi
, const char *root
)
1204 if (git_buf_sets(&fi
->path
, root
) < 0 || git_path_to_dir(&fi
->path
) < 0) {
1208 fi
->root_len
= fi
->path
.size
;
1210 if ((error
= fs_iterator__expand_dir(fi
)) < 0) {
1211 if (error
== GIT_ENOTFOUND
|| error
== GIT_ITEROVER
) {
1215 git_iterator_free((git_iterator
*)fi
);
1220 *out
= (git_iterator
*)fi
;
1224 int git_iterator_for_filesystem(
1227 git_iterator_flag_t flags
,
1231 fs_iterator
*fi
= git__calloc(1, sizeof(fs_iterator
));
1232 GITERR_CHECK_ALLOC(fi
);
1234 ITERATOR_BASE_INIT(fi
, fs
, FS
, NULL
);
1236 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
1237 fi
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
1239 return fs_iterator__initialize(out
, fi
, root
);
1245 git_ignores ignores
;
1249 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf
*path
)
1253 if (!path
|| (len
= path
->size
) < 4)
1256 if (path
->ptr
[len
- 1] == '/')
1259 if (tolower(path
->ptr
[len
- 1]) != 't' ||
1260 tolower(path
->ptr
[len
- 2]) != 'i' ||
1261 tolower(path
->ptr
[len
- 3]) != 'g' ||
1262 tolower(path
->ptr
[len
- 4]) != '.')
1265 return (len
== 4 || path
->ptr
[len
- 5] == '/');
1268 static int workdir_iterator__enter_dir(fs_iterator
*fi
)
1270 /* only push new ignores if this is not top level directory */
1271 if (fi
->stack
->next
!= NULL
) {
1272 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1273 ssize_t slash_pos
= git_buf_rfind_next(&fi
->path
, '/');
1275 (void)git_ignore__push_dir(&wi
->ignores
, &fi
->path
.ptr
[slash_pos
+ 1]);
1281 static int workdir_iterator__leave_dir(fs_iterator
*fi
)
1283 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1284 git_ignore__pop_dir(&wi
->ignores
);
1288 static int workdir_iterator__update_entry(fs_iterator
*fi
)
1291 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1293 /* skip over .git entries */
1294 if (workdir_path_is_dotgit(&fi
->path
))
1295 return GIT_ENOTFOUND
;
1297 /* reset is_ignored since we haven't checked yet */
1298 wi
->is_ignored
= -1;
1300 /* check if apparent tree entries are actually submodules */
1301 if (fi
->entry
.mode
!= GIT_FILEMODE_TREE
)
1304 error
= git_submodule_lookup(NULL
, fi
->base
.repo
, fi
->entry
.path
);
1308 /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1309 if (!error
|| error
== GIT_EEXISTS
) {
1310 fi
->entry
.mode
= S_IFGITLINK
;
1311 fi
->entry
.path
[strlen(fi
->entry
.path
) - 1] = '\0';
1317 static void workdir_iterator__free(git_iterator
*self
)
1319 workdir_iterator
*wi
= (workdir_iterator
*)self
;
1320 fs_iterator__free(self
);
1321 git_ignore__free(&wi
->ignores
);
1324 int git_iterator_for_workdir(
1326 git_repository
*repo
,
1327 git_iterator_flag_t flags
,
1332 workdir_iterator
*wi
;
1334 if (git_repository__ensure_not_bare(repo
, "scan working directory") < 0)
1335 return GIT_EBAREREPO
;
1337 /* initialize as an fs iterator then do overrides */
1338 wi
= git__calloc(1, sizeof(workdir_iterator
));
1339 GITERR_CHECK_ALLOC(wi
);
1340 ITERATOR_BASE_INIT((&wi
->fi
), fs
, FS
, repo
);
1342 wi
->fi
.base
.type
= GIT_ITERATOR_TYPE_WORKDIR
;
1343 wi
->fi
.cb
.free
= workdir_iterator__free
;
1344 wi
->fi
.enter_dir_cb
= workdir_iterator__enter_dir
;
1345 wi
->fi
.leave_dir_cb
= workdir_iterator__leave_dir
;
1346 wi
->fi
.update_entry_cb
= workdir_iterator__update_entry
;
1348 if ((error
= iterator__update_ignore_case((git_iterator
*)wi
, flags
)) < 0 ||
1349 (error
= git_ignore__for_path(repo
, "", &wi
->ignores
)) < 0)
1351 git_iterator_free((git_iterator
*)wi
);
1355 return fs_iterator__initialize(out
, &wi
->fi
, git_repository_workdir(repo
));
1359 void git_iterator_free(git_iterator
*iter
)
1364 iter
->cb
->free(iter
);
1366 git__free(iter
->start
);
1367 git__free(iter
->end
);
1369 memset(iter
, 0, sizeof(*iter
));
1374 int git_iterator_set_ignore_case(git_iterator
*iter
, bool ignore_case
)
1376 bool desire_ignore_case
= (ignore_case
!= 0);
1378 if (iterator__ignore_case(iter
) == desire_ignore_case
)
1381 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1382 if (desire_ignore_case
)
1383 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
1385 iter
->flags
&= ~GIT_ITERATOR_IGNORE_CASE
;
1387 giterr_set(GITERR_INVALID
,
1388 "Cannot currently set ignore case on non-empty iterators");
1395 git_index
*git_iterator_get_index(git_iterator
*iter
)
1397 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1398 return ((index_iterator
*)iter
)->index
;
1402 int git_iterator_current_tree_entry(
1403 const git_tree_entry
**tree_entry
, git_iterator
*iter
)
1405 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1408 tree_iterator_frame
*tf
= ((tree_iterator
*)iter
)->head
;
1409 *tree_entry
= (tf
->current
< tf
->n_entries
) ?
1410 tf
->entries
[tf
->current
]->te
: NULL
;
1416 int git_iterator_current_parent_tree(
1417 const git_tree
**tree_ptr
,
1419 const char *parent_path
)
1421 tree_iterator
*ti
= (tree_iterator
*)iter
;
1422 tree_iterator_frame
*tf
;
1423 const char *scan
= parent_path
;
1424 const git_tree_entry
*te
;
1428 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1431 for (tf
= ti
->root
; *scan
; ) {
1432 if (!(tf
= tf
->down
) ||
1433 tf
->current
>= tf
->n_entries
||
1434 !(te
= tf
->entries
[tf
->current
]->te
) ||
1435 ti
->strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1438 scan
+= te
->filename_len
;
1443 *tree_ptr
= tf
->entries
[tf
->current
]->tree
;
1447 bool git_iterator_current_is_ignored(git_iterator
*iter
)
1449 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1451 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1454 if (wi
->is_ignored
!= -1)
1455 return (bool)(wi
->is_ignored
!= 0);
1457 if (git_ignore__lookup(
1458 &wi
->ignores
, wi
->fi
.entry
.path
, &wi
->is_ignored
) < 0)
1459 wi
->is_ignored
= true;
1461 return (bool)wi
->is_ignored
;
1464 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1466 const git_index_entry
*entry
;
1468 /* a "done" iterator is after every prefix */
1469 if (git_iterator_current(&entry
, iter
) < 0 || entry
== NULL
)
1472 /* a NULL prefix is after any valid iterator */
1476 return iter
->prefixcomp(entry
->path
, path_prefix
);
1479 int git_iterator_current_workdir_path(git_buf
**path
, git_iterator
*iter
)
1481 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1483 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->fi
.entry
.path
)
1486 *path
= &wi
->fi
.path
;