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.advance = NAME_LC ## _iterator__advance; \
18 (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
19 (P)->cb.seek = NAME_LC ## _iterator__seek; \
20 (P)->cb.reset = NAME_LC ## _iterator__reset; \
21 (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
22 (P)->cb.free = NAME_LC ## _iterator__free; \
25 #define ITERATOR_CASE_FLAGS \
26 (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
28 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
29 (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
30 (P)->base.cb = &(P)->cb; \
31 ITERATOR_SET_CB(P,NAME_LC); \
32 (P)->base.repo = (REPO); \
33 (P)->base.start = start ? git__strdup(start) : NULL; \
34 (P)->base.end = end ? git__strdup(end) : NULL; \
35 if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
36 git__free(P); return -1; } \
37 (P)->base.prefixcomp = git__prefixcmp; \
38 (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
39 if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
40 (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
43 #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
44 #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
45 #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
46 #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
47 #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
49 #define iterator__end(I) ((git_iterator *)(I))->end
50 #define iterator__past_end(I,PATH) \
51 (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
54 static int iterator__reset_range(
55 git_iterator
*iter
, const char *start
, const char *end
)
59 git__free(iter
->start
);
60 iter
->start
= git__strdup(start
);
61 GITERR_CHECK_ALLOC(iter
->start
);
67 iter
->end
= git__strdup(end
);
68 GITERR_CHECK_ALLOC(iter
->end
);
74 static int iterator__update_ignore_case(
76 git_iterator_flag_t flags
)
78 int error
= 0, ignore_case
= -1;
80 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
82 else if ((flags
& GIT_ITERATOR_DONT_IGNORE_CASE
) != 0)
87 if (!(error
= git_repository_index__weakptr(&index
, iter
->repo
)))
88 ignore_case
= (index
->ignore_case
!= false);
92 iter
->flags
= (iter
->flags
| GIT_ITERATOR_IGNORE_CASE
);
93 else if (ignore_case
== 0)
94 iter
->flags
= (iter
->flags
& ~GIT_ITERATOR_IGNORE_CASE
);
96 iter
->prefixcomp
= iterator__ignore_case(iter
) ?
97 git__prefixcmp_icase
: git__prefixcmp
;
102 GIT_INLINE(void) iterator__clear_entry(const git_index_entry
**entry
)
104 if (entry
) *entry
= NULL
;
108 static int empty_iterator__noop(const git_index_entry
**e
, git_iterator
*i
)
111 iterator__clear_entry(e
);
115 static int empty_iterator__seek(git_iterator
*i
, const char *p
)
117 GIT_UNUSED(i
); GIT_UNUSED(p
);
121 static int empty_iterator__reset(git_iterator
*i
, const char *s
, const char *e
)
123 GIT_UNUSED(i
); GIT_UNUSED(s
); GIT_UNUSED(e
);
127 static int empty_iterator__at_end(git_iterator
*i
)
133 static void empty_iterator__free(git_iterator
*i
)
140 git_iterator_callbacks cb
;
143 int git_iterator_for_nothing(
145 git_iterator_flag_t flags
,
149 empty_iterator
*i
= git__calloc(1, sizeof(empty_iterator
));
150 GITERR_CHECK_ALLOC(i
);
152 #define empty_iterator__current empty_iterator__noop
153 #define empty_iterator__advance empty_iterator__noop
154 #define empty_iterator__advance_into empty_iterator__noop
156 ITERATOR_BASE_INIT(i
, empty
, EMPTY
, NULL
);
158 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
159 i
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
161 *iter
= (git_iterator
*)i
;
166 typedef struct tree_iterator_entry tree_iterator_entry
;
167 struct tree_iterator_entry
{
168 tree_iterator_entry
*parent
;
169 const git_tree_entry
*te
;
173 typedef struct tree_iterator_frame tree_iterator_frame
;
174 struct tree_iterator_frame
{
175 tree_iterator_frame
*up
, *down
;
177 size_t n_entries
; /* items in this frame */
178 size_t current
; /* start of currently active range in frame */
179 size_t next
; /* start of next range in frame */
184 tree_iterator_entry
*entries
[GIT_FLEX_ARRAY
];
189 git_iterator_callbacks cb
;
190 tree_iterator_frame
*head
, *root
;
192 git_index_entry entry
;
194 int path_ambiguities
;
195 bool path_has_filename
;
196 int (*strncomp
)(const char *a
, const char *b
, size_t sz
);
199 static char *tree_iterator__current_filename(
200 tree_iterator
*ti
, const git_tree_entry
*te
)
202 if (!ti
->path_has_filename
) {
203 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
206 if (git_tree_entry__is_tree(te
) && git_buf_putc(&ti
->path
, '/') < 0)
209 ti
->path_has_filename
= true;
215 static void tree_iterator__rewrite_filename(tree_iterator
*ti
)
217 tree_iterator_entry
*scan
= ti
->head
->entries
[ti
->head
->current
];
218 ssize_t strpos
= ti
->path
.size
;
219 const git_tree_entry
*te
;
221 if (strpos
&& ti
->path
.ptr
[strpos
- 1] == '/')
224 for (; scan
&& (te
= scan
->te
); scan
= scan
->parent
) {
225 strpos
-= te
->filename_len
;
226 memcpy(&ti
->path
.ptr
[strpos
], te
->filename
, te
->filename_len
);
227 strpos
-= 1; /* separator */
231 static int tree_iterator__te_cmp(
232 const git_tree_entry
*a
,
233 const git_tree_entry
*b
,
234 int (*compare
)(const char *, const char *, size_t))
237 a
->filename
, a
->filename_len
, a
->attr
== GIT_FILEMODE_TREE
,
238 b
->filename
, b
->filename_len
, b
->attr
== GIT_FILEMODE_TREE
,
242 static int tree_iterator__ci_cmp(const void *a
, const void *b
, void *p
)
244 const tree_iterator_entry
*ae
= a
, *be
= b
;
245 int cmp
= tree_iterator__te_cmp(ae
->te
, be
->te
, git__strncasecmp
);
248 /* stabilize sort order among equivalent names */
249 if (!ae
->parent
->te
|| !be
->parent
->te
)
250 cmp
= tree_iterator__te_cmp(ae
->te
, be
->te
, git__strncmp
);
252 cmp
= tree_iterator__ci_cmp(ae
->parent
, be
->parent
, p
);
258 static int tree_iterator__search_cmp(const void *key
, const void *val
, void *p
)
260 const tree_iterator_frame
*tf
= key
;
261 const git_tree_entry
*te
= ((tree_iterator_entry
*)val
)->te
;
264 tf
->start
, tf
->startlen
, false,
265 te
->filename
, te
->filename_len
, te
->attr
== GIT_FILEMODE_TREE
,
266 ((tree_iterator
*)p
)->strncomp
);
269 static int tree_iterator__set_next(tree_iterator
*ti
, tree_iterator_frame
*tf
)
272 const git_tree_entry
*te
, *last
= NULL
;
274 tf
->next
= tf
->current
;
276 for (; tf
->next
< tf
->n_entries
; tf
->next
++, last
= te
) {
277 te
= tf
->entries
[tf
->next
]->te
;
279 if (last
&& tree_iterator__te_cmp(last
, te
, ti
->strncomp
))
282 /* load trees for items in [current,next) range */
283 if (git_tree_entry__is_tree(te
) &&
284 (error
= git_tree_lookup(
285 &tf
->entries
[tf
->next
]->tree
, ti
->base
.repo
, &te
->oid
)) < 0)
289 if (tf
->next
> tf
->current
+ 1)
290 ti
->path_ambiguities
++;
292 if (last
&& !tree_iterator__current_filename(ti
, last
))
298 GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator
*ti
)
300 return (ti
->head
->current
< ti
->head
->n_entries
&&
301 ti
->head
->entries
[ti
->head
->current
]->tree
!= NULL
);
304 static int tree_iterator__push_frame(tree_iterator
*ti
)
307 tree_iterator_frame
*head
= ti
->head
, *tf
= NULL
;
308 size_t i
, n_entries
= 0;
310 if (head
->current
>= head
->n_entries
|| !head
->entries
[head
->current
]->tree
)
313 for (i
= head
->current
; i
< head
->next
; ++i
)
314 n_entries
+= git_tree_entrycount(head
->entries
[i
]->tree
);
316 tf
= git__calloc(sizeof(tree_iterator_frame
) +
317 n_entries
* sizeof(tree_iterator_entry
*), 1);
318 GITERR_CHECK_ALLOC(tf
);
320 tf
->n_entries
= n_entries
;
326 for (i
= head
->current
, n_entries
= 0; i
< head
->next
; ++i
) {
327 git_tree
*tree
= head
->entries
[i
]->tree
;
328 size_t j
, max_j
= git_tree_entrycount(tree
);
330 for (j
= 0; j
< max_j
; ++j
) {
331 tree_iterator_entry
*entry
= git_pool_malloc(&ti
->pool
, 1);
332 GITERR_CHECK_ALLOC(entry
);
334 entry
->parent
= head
->entries
[i
];
335 entry
->te
= git_tree_entry_byindex(tree
, j
);
338 tf
->entries
[n_entries
++] = entry
;
342 /* if ignore_case, sort entries case insensitively */
343 if (iterator__ignore_case(ti
))
345 (void **)tf
->entries
, tf
->n_entries
, tree_iterator__ci_cmp
, tf
);
347 /* pick tf->current based on "start" (or start at zero) */
348 if (head
->startlen
> 0) {
349 git__bsearch_r((void **)tf
->entries
, tf
->n_entries
, head
,
350 tree_iterator__search_cmp
, ti
, &tf
->current
);
352 while (tf
->current
&&
353 !tree_iterator__search_cmp(head
, tf
->entries
[tf
->current
-1], ti
))
356 if ((tf
->start
= strchr(head
->start
, '/')) != NULL
) {
358 tf
->startlen
= strlen(tf
->start
);
362 ti
->path_has_filename
= false;
364 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
367 /* autoexpand as needed */
368 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
369 return tree_iterator__push_frame(ti
);
374 static bool tree_iterator__move_to_next(
375 tree_iterator
*ti
, tree_iterator_frame
*tf
)
377 if (tf
->next
> tf
->current
+ 1)
378 ti
->path_ambiguities
--;
380 if (!tf
->up
) { /* at root */
381 tf
->current
= tf
->next
;
385 for (; tf
->current
< tf
->next
; tf
->current
++) {
386 git_tree_free(tf
->entries
[tf
->current
]->tree
);
387 tf
->entries
[tf
->current
]->tree
= NULL
;
390 return (tf
->current
< tf
->n_entries
);
393 static bool tree_iterator__pop_frame(tree_iterator
*ti
, bool final
)
395 tree_iterator_frame
*tf
= ti
->head
;
401 ti
->head
->down
= NULL
;
403 tree_iterator__move_to_next(ti
, tf
);
405 if (!final
) { /* if final, don't bother to clean up */
406 git_pool_free_array(&ti
->pool
, tf
->n_entries
, (void **)tf
->entries
);
407 git_buf_rtruncate_at_char(&ti
->path
, '/');
415 static int tree_iterator__pop_all(tree_iterator
*ti
, bool to_end
, bool final
)
417 while (tree_iterator__pop_frame(ti
, final
)) /* pop to root */;
420 ti
->head
->current
= to_end
? ti
->head
->n_entries
: 0;
421 ti
->path_ambiguities
= 0;
422 git_buf_clear(&ti
->path
);
428 static int tree_iterator__current(
429 const git_index_entry
**entry
, git_iterator
*self
)
431 tree_iterator
*ti
= (tree_iterator
*)self
;
432 tree_iterator_frame
*tf
= ti
->head
;
433 const git_tree_entry
*te
;
435 iterator__clear_entry(entry
);
437 if (tf
->current
>= tf
->n_entries
)
439 te
= tf
->entries
[tf
->current
]->te
;
441 ti
->entry
.mode
= te
->attr
;
442 git_oid_cpy(&ti
->entry
.oid
, &te
->oid
);
444 ti
->entry
.path
= tree_iterator__current_filename(ti
, te
);
445 GITERR_CHECK_ALLOC(ti
->entry
.path
);
447 if (ti
->path_ambiguities
> 0)
448 tree_iterator__rewrite_filename(ti
);
450 if (iterator__past_end(ti
, ti
->entry
.path
))
451 return tree_iterator__pop_all(ti
, true, false);
459 static int tree_iterator__advance_into(
460 const git_index_entry
**entry
, git_iterator
*self
)
463 tree_iterator
*ti
= (tree_iterator
*)self
;
465 iterator__clear_entry(entry
);
467 if (tree_iterator__at_tree(ti
) &&
468 !(error
= tree_iterator__push_frame(ti
)))
469 error
= tree_iterator__current(entry
, self
);
474 static int tree_iterator__advance(
475 const git_index_entry
**entry
, git_iterator
*self
)
478 tree_iterator
*ti
= (tree_iterator
*)self
;
479 tree_iterator_frame
*tf
= ti
->head
;
481 iterator__clear_entry(entry
);
483 if (tf
->current
> tf
->n_entries
)
486 if (iterator__do_autoexpand(ti
) && iterator__include_trees(ti
) &&
487 tree_iterator__at_tree(ti
))
488 return tree_iterator__advance_into(entry
, self
);
490 if (ti
->path_has_filename
) {
491 git_buf_rtruncate_at_char(&ti
->path
, '/');
492 ti
->path_has_filename
= false;
495 /* scan forward and up, advancing in frame or popping frame when done */
496 while (!tree_iterator__move_to_next(ti
, tf
) &&
497 tree_iterator__pop_frame(ti
, false))
500 /* find next and load trees */
501 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
504 /* deal with include_trees / auto_expand as needed */
505 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
506 return tree_iterator__advance_into(entry
, self
);
508 return tree_iterator__current(entry
, self
);
511 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
513 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
517 static int tree_iterator__reset(
518 git_iterator
*self
, const char *start
, const char *end
)
520 tree_iterator
*ti
= (tree_iterator
*)self
;
522 tree_iterator__pop_all(ti
, false, false);
524 if (iterator__reset_range(self
, start
, end
) < 0)
527 return tree_iterator__push_frame(ti
); /* re-expand root tree */
530 static int tree_iterator__at_end(git_iterator
*self
)
532 tree_iterator
*ti
= (tree_iterator
*)self
;
533 return (ti
->head
->current
>= ti
->head
->n_entries
);
536 static void tree_iterator__free(git_iterator
*self
)
538 tree_iterator
*ti
= (tree_iterator
*)self
;
540 tree_iterator__pop_all(ti
, true, false);
542 git_tree_free(ti
->head
->entries
[0]->tree
);
544 git_pool_clear(&ti
->pool
);
545 git_buf_free(&ti
->path
);
548 static int tree_iterator__create_root_frame(tree_iterator
*ti
, git_tree
*tree
)
550 size_t sz
= sizeof(tree_iterator_frame
) + sizeof(tree_iterator_entry
);
551 tree_iterator_frame
*root
= git__calloc(sz
, sizeof(char));
552 GITERR_CHECK_ALLOC(root
);
556 root
->start
= ti
->base
.start
;
557 root
->startlen
= root
->start
? strlen(root
->start
) : 0;
558 root
->entries
[0] = git_pool_mallocz(&ti
->pool
, 1);
559 GITERR_CHECK_ALLOC(root
->entries
[0]);
560 root
->entries
[0]->tree
= tree
;
562 ti
->head
= ti
->root
= root
;
567 int git_iterator_for_tree(
570 git_iterator_flag_t flags
,
578 return git_iterator_for_nothing(iter
, flags
, start
, end
);
580 if ((error
= git_object_dup((git_object
**)&tree
, (git_object
*)tree
)) < 0)
583 ti
= git__calloc(1, sizeof(tree_iterator
));
584 GITERR_CHECK_ALLOC(ti
);
586 ITERATOR_BASE_INIT(ti
, tree
, TREE
, git_tree_owner(tree
));
588 if ((error
= iterator__update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
590 ti
->strncomp
= iterator__ignore_case(ti
) ? git__strncasecmp
: git__strncmp
;
592 if ((error
= git_pool_init(&ti
->pool
, sizeof(tree_iterator_entry
),0)) < 0 ||
593 (error
= tree_iterator__create_root_frame(ti
, tree
)) < 0 ||
594 (error
= tree_iterator__push_frame(ti
)) < 0) /* expand root now */
597 *iter
= (git_iterator
*)ti
;
601 git_iterator_free((git_iterator
*)ti
);
608 git_iterator_callbacks cb
;
611 /* when not in autoexpand mode, use these to represent "tree" state */
614 char restore_terminator
;
615 git_index_entry tree_entry
;
618 static const git_index_entry
*index_iterator__index_entry(index_iterator
*ii
)
620 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
622 if (ie
!= NULL
&& iterator__past_end(ii
, ie
->path
)) {
623 ii
->current
= git_index_entrycount(ii
->index
);
630 static const git_index_entry
*index_iterator__skip_conflicts(index_iterator
*ii
)
632 const git_index_entry
*ie
;
634 while ((ie
= index_iterator__index_entry(ii
)) != NULL
&&
635 git_index_entry_stage(ie
) != 0)
641 static void index_iterator__next_prefix_tree(index_iterator
*ii
)
645 if (!iterator__include_trees(ii
))
648 slash
= strchr(&ii
->partial
.ptr
[ii
->partial_pos
], '/');
651 ii
->partial_pos
= (slash
- ii
->partial
.ptr
) + 1;
652 ii
->restore_terminator
= ii
->partial
.ptr
[ii
->partial_pos
];
653 ii
->partial
.ptr
[ii
->partial_pos
] = '\0';
655 ii
->partial_pos
= ii
->partial
.size
;
658 if (index_iterator__index_entry(ii
) == NULL
)
659 ii
->partial_pos
= ii
->partial
.size
;
662 static int index_iterator__first_prefix_tree(index_iterator
*ii
)
664 const git_index_entry
*ie
= index_iterator__skip_conflicts(ii
);
665 const char *scan
, *prior
, *slash
;
667 if (!ie
|| !iterator__include_trees(ii
))
670 /* find longest common prefix with prior index entry */
671 for (scan
= slash
= ie
->path
, prior
= ii
->partial
.ptr
;
672 *scan
&& *scan
== *prior
; ++scan
, ++prior
)
676 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
679 ii
->partial_pos
= (slash
- ie
->path
) + 1;
680 index_iterator__next_prefix_tree(ii
);
685 #define index_iterator__at_tree(I) \
686 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
688 static int index_iterator__current(
689 const git_index_entry
**entry
, git_iterator
*self
)
691 index_iterator
*ii
= (index_iterator
*)self
;
692 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
694 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
695 ii
->tree_entry
.path
= ii
->partial
.ptr
;
696 ie
= &ii
->tree_entry
;
705 static int index_iterator__at_end(git_iterator
*self
)
707 index_iterator
*ii
= (index_iterator
*)self
;
708 return (ii
->current
>= git_index_entrycount(ii
->index
));
711 static int index_iterator__advance(
712 const git_index_entry
**entry
, git_iterator
*self
)
714 index_iterator
*ii
= (index_iterator
*)self
;
715 size_t entrycount
= git_index_entrycount(ii
->index
);
716 const git_index_entry
*ie
;
718 if (index_iterator__at_tree(ii
)) {
719 if (iterator__do_autoexpand(ii
)) {
720 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
721 index_iterator__next_prefix_tree(ii
);
723 /* advance to sibling tree (i.e. find entry with new prefix) */
724 while (ii
->current
< entrycount
) {
727 if (!(ie
= git_index_get_byindex(ii
->index
, ii
->current
)) ||
728 ii
->base
.prefixcomp(ie
->path
, ii
->partial
.ptr
) != 0)
732 if (index_iterator__first_prefix_tree(ii
) < 0)
736 if (ii
->current
< entrycount
)
739 if (index_iterator__first_prefix_tree(ii
) < 0)
743 return index_iterator__current(entry
, self
);
746 static int index_iterator__advance_into(
747 const git_index_entry
**entry
, git_iterator
*self
)
749 index_iterator
*ii
= (index_iterator
*)self
;
750 const git_index_entry
*ie
= git_index_get_byindex(ii
->index
, ii
->current
);
752 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
753 if (ii
->restore_terminator
)
754 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
755 index_iterator__next_prefix_tree(ii
);
758 return index_iterator__current(entry
, self
);
761 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
763 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
767 static int index_iterator__reset(
768 git_iterator
*self
, const char *start
, const char *end
)
770 index_iterator
*ii
= (index_iterator
*)self
;
771 const git_index_entry
*ie
;
773 if (iterator__reset_range(self
, start
, end
) < 0)
776 ii
->current
= ii
->base
.start
?
777 git_index__prefix_position(ii
->index
, ii
->base
.start
) : 0;
779 if ((ie
= index_iterator__skip_conflicts(ii
)) == NULL
)
782 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
787 if (ii
->base
.start
) {
788 size_t startlen
= strlen(ii
->base
.start
);
790 ii
->partial_pos
= (startlen
> ii
->partial
.size
) ?
791 ii
->partial
.size
: startlen
;
794 index_iterator__next_prefix_tree(ii
);
799 static void index_iterator__free(git_iterator
*self
)
801 index_iterator
*ii
= (index_iterator
*)self
;
802 git_index_free(ii
->index
);
805 git_buf_free(&ii
->partial
);
808 int git_iterator_for_index(
811 git_iterator_flag_t flags
,
815 index_iterator
*ii
= git__calloc(1, sizeof(index_iterator
));
816 GITERR_CHECK_ALLOC(ii
);
818 ITERATOR_BASE_INIT(ii
, index
, INDEX
, git_index_owner(index
));
820 if (index
->ignore_case
) {
821 ii
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
822 ii
->base
.prefixcomp
= git__prefixcmp_icase
;
826 GIT_REFCOUNT_INC(index
);
828 git_buf_init(&ii
->partial
, 0);
829 ii
->tree_entry
.mode
= GIT_FILEMODE_TREE
;
831 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
833 *iter
= (git_iterator
*)ii
;
839 typedef struct fs_iterator_frame fs_iterator_frame
;
840 struct fs_iterator_frame
{
841 fs_iterator_frame
*next
;
846 typedef struct fs_iterator fs_iterator
;
849 git_iterator_callbacks cb
;
850 fs_iterator_frame
*stack
;
851 git_index_entry entry
;
856 int (*enter_dir_cb
)(fs_iterator
*self
);
857 int (*leave_dir_cb
)(fs_iterator
*self
);
858 int (*update_entry_cb
)(fs_iterator
*self
);
861 #define FS_MAX_DEPTH 100
863 static fs_iterator_frame
*fs_iterator__alloc_frame(fs_iterator
*fi
)
865 fs_iterator_frame
*ff
= git__calloc(1, sizeof(fs_iterator_frame
));
866 git_vector_cmp entry_compare
= CASESELECT(
867 iterator__ignore_case(fi
),
868 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
870 if (ff
&& git_vector_init(&ff
->entries
, 0, entry_compare
) < 0) {
878 static void fs_iterator__free_frame(fs_iterator_frame
*ff
)
881 git_path_with_stat
*path
;
883 git_vector_foreach(&ff
->entries
, i
, path
)
885 git_vector_free(&ff
->entries
);
889 static void fs_iterator__pop_frame(
890 fs_iterator
*fi
, fs_iterator_frame
*ff
, bool pop_last
)
892 if (fi
&& fi
->stack
== ff
) {
893 if (!ff
->next
&& !pop_last
) {
894 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
898 if (fi
->leave_dir_cb
)
899 (void)fi
->leave_dir_cb(fi
);
901 fi
->stack
= ff
->next
;
905 fs_iterator__free_frame(ff
);
908 static int fs_iterator__update_entry(fs_iterator
*fi
);
910 static int fs_iterator__entry_cmp(const void *i
, const void *item
)
912 const fs_iterator
*fi
= (const fs_iterator
*)i
;
913 const git_path_with_stat
*ps
= item
;
914 return fi
->base
.prefixcomp(fi
->base
.start
, ps
->path
);
917 static void fs_iterator__seek_frame_start(
918 fs_iterator
*fi
, fs_iterator_frame
*ff
)
925 &ff
->index
, &ff
->entries
, fs_iterator__entry_cmp
, fi
);
930 static int fs_iterator__expand_dir(fs_iterator
*fi
)
933 fs_iterator_frame
*ff
;
935 if (fi
->depth
> FS_MAX_DEPTH
) {
936 giterr_set(GITERR_REPOSITORY
,
937 "Directory nesting is too deep (%d)", fi
->depth
);
941 ff
= fs_iterator__alloc_frame(fi
);
942 GITERR_CHECK_ALLOC(ff
);
944 error
= git_path_dirload_with_stat(
945 fi
->path
.ptr
, fi
->root_len
, iterator__ignore_case(fi
),
946 fi
->base
.start
, fi
->base
.end
, &ff
->entries
);
948 if (error
< 0 || ff
->entries
.length
== 0) {
949 fs_iterator__free_frame(ff
);
950 return GIT_ENOTFOUND
;
953 fs_iterator__seek_frame_start(fi
, ff
);
955 ff
->next
= fi
->stack
;
959 if (fi
->enter_dir_cb
&& (error
= fi
->enter_dir_cb(fi
)) < 0)
962 return fs_iterator__update_entry(fi
);
965 static int fs_iterator__current(
966 const git_index_entry
**entry
, git_iterator
*self
)
968 fs_iterator
*fi
= (fs_iterator
*)self
;
970 *entry
= (fi
->entry
.path
== NULL
) ? NULL
: &fi
->entry
;
974 static int fs_iterator__at_end(git_iterator
*self
)
976 return (((fs_iterator
*)self
)->entry
.path
== NULL
);
979 static int fs_iterator__advance_into(
980 const git_index_entry
**entry
, git_iterator
*iter
)
983 fs_iterator
*fi
= (fs_iterator
*)iter
;
985 iterator__clear_entry(entry
);
987 /* Allow you to explicitly advance into a commit/submodule (as well as a
988 * tree) to avoid cases where an entry is mislabeled as a submodule in
989 * the working directory. The fs iterator will never have COMMMIT
990 * entries on it's own, but a wrapper might add them.
992 if (fi
->entry
.path
!= NULL
&&
993 (fi
->entry
.mode
== GIT_FILEMODE_TREE
||
994 fi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
995 /* returns GIT_ENOTFOUND if the directory is empty */
996 error
= fs_iterator__expand_dir(fi
);
999 error
= fs_iterator__current(entry
, iter
);
1004 static int fs_iterator__advance_over(
1005 const git_index_entry
**entry
, git_iterator
*self
)
1008 fs_iterator
*fi
= (fs_iterator
*)self
;
1009 fs_iterator_frame
*ff
;
1010 git_path_with_stat
*next
;
1015 while (fi
->entry
.path
!= NULL
) {
1017 next
= git_vector_get(&ff
->entries
, ++ff
->index
);
1022 fs_iterator__pop_frame(fi
, ff
, false);
1025 error
= fs_iterator__update_entry(fi
);
1027 if (!error
&& entry
!= NULL
)
1028 error
= fs_iterator__current(entry
, self
);
1033 static int fs_iterator__advance(
1034 const git_index_entry
**entry
, git_iterator
*self
)
1036 fs_iterator
*fi
= (fs_iterator
*)self
;
1038 /* given include_trees & autoexpand, we might have to go into a tree */
1039 if (iterator__do_autoexpand(fi
) &&
1040 fi
->entry
.path
!= NULL
&&
1041 fi
->entry
.mode
== GIT_FILEMODE_TREE
)
1043 int error
= fs_iterator__advance_into(entry
, self
);
1044 if (error
!= GIT_ENOTFOUND
)
1046 /* continue silently past empty directories if autoexpanding */
1050 return fs_iterator__advance_over(entry
, self
);
1053 static int fs_iterator__seek(git_iterator
*self
, const char *prefix
)
1057 /* pop stack until matching prefix */
1058 /* find prefix item in current frame */
1059 /* push subdirectories as deep as possible while matching */
1063 static int fs_iterator__reset(
1064 git_iterator
*self
, const char *start
, const char *end
)
1066 fs_iterator
*fi
= (fs_iterator
*)self
;
1068 while (fi
->stack
!= NULL
&& fi
->stack
->next
!= NULL
)
1069 fs_iterator__pop_frame(fi
, fi
->stack
, false);
1072 if (iterator__reset_range(self
, start
, end
) < 0)
1075 fs_iterator__seek_frame_start(fi
, fi
->stack
);
1077 return fs_iterator__update_entry(fi
);
1080 static void fs_iterator__free(git_iterator
*self
)
1082 fs_iterator
*fi
= (fs_iterator
*)self
;
1084 while (fi
->stack
!= NULL
)
1085 fs_iterator__pop_frame(fi
, fi
->stack
, true);
1087 git_buf_free(&fi
->path
);
1090 static int fs_iterator__update_entry(fs_iterator
*fi
)
1092 git_path_with_stat
*ps
=
1093 git_vector_get(&fi
->stack
->entries
, fi
->stack
->index
);
1095 git_buf_truncate(&fi
->path
, fi
->root_len
);
1096 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
1100 if (git_buf_put(&fi
->path
, ps
->path
, ps
->path_len
) < 0)
1102 if (iterator__past_end(fi
, fi
->path
.ptr
+ fi
->root_len
))
1105 fi
->entry
.path
= ps
->path
;
1106 git_index_entry__init_from_stat(&fi
->entry
, &ps
->st
);
1108 /* need different mode here to keep directories during iteration */
1109 fi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
1111 /* allow wrapper to check/update the entry (can force skip) */
1112 if (fi
->update_entry_cb
&&
1113 fi
->update_entry_cb(fi
) == GIT_ENOTFOUND
)
1114 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1116 /* if this is a tree and trees aren't included, then skip */
1117 if (fi
->entry
.mode
== GIT_FILEMODE_TREE
&& !iterator__include_trees(fi
))
1118 return git_iterator_advance(NULL
, (git_iterator
*)fi
);
1123 static int fs_iterator__initialize(
1124 git_iterator
**out
, fs_iterator
*fi
, const char *root
)
1128 if (git_buf_sets(&fi
->path
, root
) < 0 || git_path_to_dir(&fi
->path
) < 0) {
1132 fi
->root_len
= fi
->path
.size
;
1134 if ((error
= fs_iterator__expand_dir(fi
)) == GIT_ENOTFOUND
) {
1139 git_iterator_free((git_iterator
*)fi
);
1143 *out
= (git_iterator
*)fi
;
1147 int git_iterator_for_filesystem(
1150 git_iterator_flag_t flags
,
1154 fs_iterator
*fi
= git__calloc(1, sizeof(fs_iterator
));
1155 GITERR_CHECK_ALLOC(fi
);
1157 ITERATOR_BASE_INIT(fi
, fs
, FS
, NULL
);
1159 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
1160 fi
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
1162 return fs_iterator__initialize(out
, fi
, root
);
1168 git_ignores ignores
;
1172 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf
*path
)
1176 if (!path
|| (len
= path
->size
) < 4)
1179 if (path
->ptr
[len
- 1] == '/')
1182 if (tolower(path
->ptr
[len
- 1]) != 't' ||
1183 tolower(path
->ptr
[len
- 2]) != 'i' ||
1184 tolower(path
->ptr
[len
- 3]) != 'g' ||
1185 tolower(path
->ptr
[len
- 4]) != '.')
1188 return (len
== 4 || path
->ptr
[len
- 5] == '/');
1191 static int workdir_iterator__enter_dir(fs_iterator
*fi
)
1193 /* only push new ignores if this is not top level directory */
1194 if (fi
->stack
->next
!= NULL
) {
1195 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1196 ssize_t slash_pos
= git_buf_rfind_next(&fi
->path
, '/');
1198 (void)git_ignore__push_dir(&wi
->ignores
, &fi
->path
.ptr
[slash_pos
+ 1]);
1204 static int workdir_iterator__leave_dir(fs_iterator
*fi
)
1206 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1207 git_ignore__pop_dir(&wi
->ignores
);
1211 static int workdir_iterator__update_entry(fs_iterator
*fi
)
1214 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1216 /* skip over .git entries */
1217 if (workdir_path_is_dotgit(&fi
->path
))
1218 return GIT_ENOTFOUND
;
1220 /* reset is_ignored since we haven't checked yet */
1221 wi
->is_ignored
= -1;
1223 /* check if apparent tree entries are actually submodules */
1224 if (fi
->entry
.mode
!= GIT_FILEMODE_TREE
)
1227 error
= git_submodule_lookup(NULL
, fi
->base
.repo
, fi
->entry
.path
);
1231 /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1232 if (!error
|| error
== GIT_EEXISTS
) {
1233 fi
->entry
.mode
= S_IFGITLINK
;
1234 fi
->entry
.path
[strlen(fi
->entry
.path
) - 1] = '\0';
1240 static void workdir_iterator__free(git_iterator
*self
)
1242 workdir_iterator
*wi
= (workdir_iterator
*)self
;
1243 fs_iterator__free(self
);
1244 git_ignore__free(&wi
->ignores
);
1247 int git_iterator_for_workdir(
1249 git_repository
*repo
,
1250 git_iterator_flag_t flags
,
1255 workdir_iterator
*wi
;
1257 if (git_repository__ensure_not_bare(repo
, "scan working directory") < 0)
1258 return GIT_EBAREREPO
;
1260 /* initialize as an fs iterator then do overrides */
1261 wi
= git__calloc(1, sizeof(workdir_iterator
));
1262 GITERR_CHECK_ALLOC(wi
);
1263 ITERATOR_BASE_INIT((&wi
->fi
), fs
, FS
, repo
);
1265 wi
->fi
.base
.type
= GIT_ITERATOR_TYPE_WORKDIR
;
1266 wi
->fi
.cb
.free
= workdir_iterator__free
;
1267 wi
->fi
.enter_dir_cb
= workdir_iterator__enter_dir
;
1268 wi
->fi
.leave_dir_cb
= workdir_iterator__leave_dir
;
1269 wi
->fi
.update_entry_cb
= workdir_iterator__update_entry
;
1271 if ((error
= iterator__update_ignore_case((git_iterator
*)wi
, flags
)) < 0 ||
1272 (error
= git_ignore__for_path(repo
, "", &wi
->ignores
)) < 0)
1274 git_iterator_free((git_iterator
*)wi
);
1278 return fs_iterator__initialize(out
, &wi
->fi
, git_repository_workdir(repo
));
1282 void git_iterator_free(git_iterator
*iter
)
1287 iter
->cb
->free(iter
);
1289 git__free(iter
->start
);
1290 git__free(iter
->end
);
1292 memset(iter
, 0, sizeof(*iter
));
1297 int git_iterator_set_ignore_case(git_iterator
*iter
, bool ignore_case
)
1299 bool desire_ignore_case
= (ignore_case
!= 0);
1301 if (iterator__ignore_case(iter
) == desire_ignore_case
)
1304 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1305 if (desire_ignore_case
)
1306 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
1308 iter
->flags
&= ~GIT_ITERATOR_IGNORE_CASE
;
1310 giterr_set(GITERR_INVALID
,
1311 "Cannot currently set ignore case on non-empty iterators");
1318 git_index
*git_iterator_get_index(git_iterator
*iter
)
1320 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1321 return ((index_iterator
*)iter
)->index
;
1325 int git_iterator_current_tree_entry(
1326 const git_tree_entry
**tree_entry
, git_iterator
*iter
)
1328 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1331 tree_iterator_frame
*tf
= ((tree_iterator
*)iter
)->head
;
1332 *tree_entry
= (tf
->current
< tf
->n_entries
) ?
1333 tf
->entries
[tf
->current
]->te
: NULL
;
1339 int git_iterator_current_parent_tree(
1340 const git_tree
**tree_ptr
,
1342 const char *parent_path
)
1344 tree_iterator
*ti
= (tree_iterator
*)iter
;
1345 tree_iterator_frame
*tf
;
1346 const char *scan
= parent_path
;
1347 const git_tree_entry
*te
;
1351 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1354 for (tf
= ti
->root
; *scan
; ) {
1355 if (!(tf
= tf
->down
) ||
1356 tf
->current
>= tf
->n_entries
||
1357 !(te
= tf
->entries
[tf
->current
]->te
) ||
1358 ti
->strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1361 scan
+= te
->filename_len
;
1366 *tree_ptr
= tf
->entries
[tf
->current
]->tree
;
1370 bool git_iterator_current_is_ignored(git_iterator
*iter
)
1372 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1374 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1377 if (wi
->is_ignored
!= -1)
1378 return (bool)(wi
->is_ignored
!= 0);
1380 if (git_ignore__lookup(
1381 &wi
->ignores
, wi
->fi
.entry
.path
, &wi
->is_ignored
) < 0)
1382 wi
->is_ignored
= true;
1384 return (bool)wi
->is_ignored
;
1387 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1389 const git_index_entry
*entry
;
1391 /* a "done" iterator is after every prefix */
1392 if (git_iterator_current(&entry
, iter
) < 0 || entry
== NULL
)
1395 /* a NULL prefix is after any valid iterator */
1399 return iter
->prefixcomp(entry
->path
, path_prefix
);
1402 int git_iterator_current_workdir_path(git_buf
**path
, git_iterator
*iter
)
1404 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1406 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->fi
.entry
.path
)
1409 *path
= &wi
->fi
.path
;