File Coverage

deps/libgit2/src/iterator.c
Criterion Covered Total %
statement 788 1057 74.5
branch 424 718 59.0
condition n/a
subroutine n/a
pod n/a
total 1212 1775 68.2


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "iterator.h"
9              
10             #include "tree.h"
11             #include "index.h"
12              
13             #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
14             #define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
15             #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
16              
17             #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
18             #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
19             #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
20             #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
21             #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
22             #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
23             #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
24             #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
25             #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
26             #define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS)
27              
28              
29 753           static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
30             {
31 753 100         if (ignore_case)
32 3           iter->flags |= GIT_ITERATOR_IGNORE_CASE;
33             else
34 750           iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
35              
36 753 100         iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp;
37 753 100         iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp;
38 753 100         iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
39 753 100         iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
40              
41 753           git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
42 753           }
43              
44 817           static int iterator_range_init(
45             git_iterator *iter, const char *start, const char *end)
46             {
47 817 100         if (start && *start) {
    50          
48 33           iter->start = git__strdup(start);
49 33 50         GIT_ERROR_CHECK_ALLOC(iter->start);
50              
51 33           iter->start_len = strlen(iter->start);
52             }
53              
54 817 100         if (end && *end) {
    50          
55 33           iter->end = git__strdup(end);
56 33 50         GIT_ERROR_CHECK_ALLOC(iter->end);
57              
58 33           iter->end_len = strlen(iter->end);
59             }
60              
61 817           iter->started = (iter->start == NULL);
62 817           iter->ended = false;
63              
64 817           return 0;
65             }
66              
67 64           static void iterator_range_free(git_iterator *iter)
68             {
69 64 50         if (iter->start) {
70 0           git__free(iter->start);
71 0           iter->start = NULL;
72 0           iter->start_len = 0;
73             }
74              
75 64 50         if (iter->end) {
76 0           git__free(iter->end);
77 0           iter->end = NULL;
78 0           iter->end_len = 0;
79             }
80 64           }
81              
82 64           static int iterator_reset_range(
83             git_iterator *iter, const char *start, const char *end)
84             {
85 64           iterator_range_free(iter);
86 64           return iterator_range_init(iter, start, end);
87             }
88              
89 753           static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist)
90             {
91             size_t i;
92              
93 753 50         if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0)
94 0           return -1;
95              
96 851 100         for (i = 0; i < pathlist->count; i++) {
97 98 50         if (!pathlist->strings[i])
98 0           continue;
99              
100 98 50         if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0)
101 0           return -1;
102             }
103              
104 753           return 0;
105             }
106              
107 753           static int iterator_init_common(
108             git_iterator *iter,
109             git_repository *repo,
110             git_index *index,
111             git_iterator_options *given_opts)
112             {
113             static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT;
114 753 100         git_iterator_options *options = given_opts ? given_opts : &default_opts;
115             bool ignore_case;
116             int precompose;
117             int error;
118              
119 753           iter->repo = repo;
120 753           iter->index = index;
121 753           iter->flags = options->flags;
122              
123 753 100         if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
124 3           ignore_case = true;
125 750 100         } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) {
126 421           ignore_case = false;
127 329 100         } else if (repo) {
128             git_index *index;
129              
130 297 50         if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
131 0           return error;
132              
133 297           ignore_case = !!index->ignore_case;
134              
135 297 50         if (ignore_case == 1)
136 0           iter->flags |= GIT_ITERATOR_IGNORE_CASE;
137             else
138 297           iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
139             } else {
140 32           ignore_case = false;
141             }
142              
143             /* try to look up precompose and set flag if appropriate */
144 753 100         if (repo &&
    50          
145 719 50         (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 &&
146 719           (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) {
147              
148 719 50         if (git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) < 0)
149 0           git_error_clear();
150 719 50         else if (precompose)
151 0           iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
152             }
153              
154 753 100         if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND))
155 171           iter->flags |= GIT_ITERATOR_INCLUDE_TREES;
156              
157 753 50         if ((error = iterator_range_init(iter, options->start, options->end)) < 0 ||
    50          
158 753           (error = iterator_pathlist_init(iter, &options->pathlist)) < 0)
159 0           return error;
160              
161 753           iterator_set_ignore_case(iter, ignore_case);
162 753           return 0;
163             }
164              
165 557           static void iterator_clear(git_iterator *iter)
166             {
167 557           iter->started = false;
168 557           iter->ended = false;
169 557           iter->stat_calls = 0;
170 557           iter->pathlist_walk_idx = 0;
171 557           iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
172 557           }
173              
174 1480           GIT_INLINE(bool) iterator_has_started(
175             git_iterator *iter, const char *path, bool is_submodule)
176             {
177             size_t path_len;
178              
179 1480 100         if (iter->start == NULL || iter->started == true)
    100          
180 1468           return true;
181              
182             /* the starting path is generally a prefix - we have started once we
183             * are prefixed by this path
184             */
185 12           iter->started = (iter->prefixcomp(path, iter->start) >= 0);
186              
187 12 50         if (iter->started)
188 12           return true;
189              
190 0           path_len = strlen(path);
191              
192             /* if, however, we are a submodule, then we support `start` being
193             * suffixed with a `/` for crazy legacy reasons. match `submod`
194             * with a start path of `submod/`.
195             */
196 0 0         if (is_submodule && iter->start_len && path_len == iter->start_len - 1 &&
    0          
    0          
    0          
197 0           iter->start[iter->start_len-1] == '/')
198 0           return true;
199              
200             /* if, however, our current path is a directory, and our starting path
201             * is _beneath_ that directory, then recurse into the directory (even
202             * though we have not yet "started")
203             */
204 0 0         if (path_len > 0 && path[path_len-1] == '/' &&
205 0           iter->strncomp(path, iter->start, path_len) == 0)
206 0           return true;
207              
208 0           return false;
209             }
210              
211 1480           GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path)
212             {
213 1480 100         if (iter->end == NULL)
214 1454           return false;
215 26 50         else if (iter->ended)
216 0           return true;
217              
218 26           iter->ended = (iter->prefixcomp(path, iter->end) > 0);
219 26           return iter->ended;
220             }
221              
222             /* walker for the index and tree iterator that allows it to walk the sorted
223             * pathlist entries alongside sorted iterator entries.
224             */
225 1474           static bool iterator_pathlist_next_is(git_iterator *iter, const char *path)
226             {
227             char *p;
228             size_t path_len, p_len, cmp_len, i;
229             int cmp;
230              
231 1474 100         if (iter->pathlist.length == 0)
232 1339           return true;
233              
234 135           git_vector_sort(&iter->pathlist);
235              
236 135           path_len = strlen(path);
237              
238             /* for comparison, drop the trailing slash on the current '/' */
239 135 50         if (path_len && path[path_len-1] == '/')
    100          
240 6           path_len--;
241              
242 196 100         for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
243 115           p = iter->pathlist.contents[i];
244 115           p_len = strlen(p);
245              
246 115 50         if (p_len && p[p_len-1] == '/')
    50          
247 0           p_len--;
248              
249 115           cmp_len = min(path_len, p_len);
250              
251             /* see if the pathlist entry is a prefix of this path */
252 115           cmp = iter->strncomp(p, path, cmp_len);
253              
254             /* prefix match - see if there's an exact match, or if we were
255             * given a path that matches the directory
256             */
257 115 100         if (cmp == 0) {
258             /* if this pathlist entry is not suffixed with a '/' then
259             * it matches a path that is a file or a directory.
260             * (eg, pathlist = "foo" and path is "foo" or "foo/" or
261             * "foo/something")
262             */
263 80 100         if (p[cmp_len] == '\0' &&
    100          
264 24 50         (path[cmp_len] == '\0' || path[cmp_len] == '/'))
265 50           return true;
266              
267             /* if this pathlist entry _is_ suffixed with a '/' then
268             * it matches only paths that are directories.
269             * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
270             */
271 30 50         if (p[cmp_len] == '/' && path[cmp_len] == '/')
    0          
272 0           return true;
273             }
274              
275             /* this pathlist entry sorts before the given path, try the next */
276 35 100         else if (cmp < 0) {
277 31           iter->pathlist_walk_idx++;
278 31           continue;
279             }
280              
281             /* this pathlist sorts after the given path, no match. */
282 4 50         else if (cmp > 0) {
283 4           break;
284             }
285             }
286              
287 85           return false;
288             }
289              
290             typedef enum {
291             ITERATOR_PATHLIST_NONE = 0,
292             ITERATOR_PATHLIST_IS_FILE = 1,
293             ITERATOR_PATHLIST_IS_DIR = 2,
294             ITERATOR_PATHLIST_IS_PARENT = 3,
295             ITERATOR_PATHLIST_FULL = 4,
296             } iterator_pathlist_search_t;
297              
298 145           static iterator_pathlist_search_t iterator_pathlist_search(
299             git_iterator *iter, const char *path, size_t path_len)
300             {
301             const char *p;
302             size_t idx;
303             int error;
304              
305 145 50         if (iter->pathlist.length == 0)
306 0           return ITERATOR_PATHLIST_FULL;
307              
308 145           git_vector_sort(&iter->pathlist);
309              
310 145           error = git_vector_bsearch2(&idx, &iter->pathlist,
311 145           (git_vector_cmp)iter->strcomp, path);
312              
313             /* the given path was found in the pathlist. since the pathlist only
314             * matches directories when they're suffixed with a '/', analyze the
315             * path string to determine whether it's a directory or not.
316             */
317 145 100         if (error == 0) {
318 38 50         if (path_len && path[path_len-1] == '/')
    50          
319 0           return ITERATOR_PATHLIST_IS_DIR;
320              
321 38           return ITERATOR_PATHLIST_IS_FILE;
322             }
323              
324             /* at this point, the path we're examining may be a directory (though we
325             * don't know that yet, since we're avoiding a stat unless it's necessary)
326             * so walk the pathlist looking for the given path with a '/' after it,
327             */
328 107 100         while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
329 64 100         if (iter->prefixcomp(p, path) != 0)
330 47           break;
331              
332             /* an exact match would have been matched by the bsearch above */
333 17 50         assert(p[path_len]);
334              
335             /* is this a literal directory entry (eg `foo/`) or a file beneath */
336 17 100         if (p[path_len] == '/') {
337 8 50         return (p[path_len+1] == '\0') ?
338             ITERATOR_PATHLIST_IS_DIR :
339             ITERATOR_PATHLIST_IS_PARENT;
340             }
341              
342 9 50         if (p[path_len] > '/')
343 9           break;
344              
345 0           idx++;
346             }
347              
348 145           return ITERATOR_PATHLIST_NONE;
349             }
350              
351             /* Empty iterator */
352              
353 17           static int empty_iterator_noop(const git_index_entry **e, git_iterator *i)
354             {
355             GIT_UNUSED(i);
356              
357 17 50         if (e)
358 17           *e = NULL;
359              
360 17           return GIT_ITEROVER;
361             }
362              
363 0           static int empty_iterator_advance_over(
364             const git_index_entry **e,
365             git_iterator_status_t *s,
366             git_iterator *i)
367             {
368 0           *s = GIT_ITERATOR_STATUS_EMPTY;
369 0           return empty_iterator_noop(e, i);
370             }
371              
372 0           static int empty_iterator_reset(git_iterator *i)
373             {
374             GIT_UNUSED(i);
375 0           return 0;
376             }
377              
378 17           static void empty_iterator_free(git_iterator *i)
379             {
380             GIT_UNUSED(i);
381 17           }
382              
383             typedef struct {
384             git_iterator base;
385             git_iterator_callbacks cb;
386             } empty_iterator;
387              
388 17           int git_iterator_for_nothing(
389             git_iterator **out,
390             git_iterator_options *options)
391             {
392             empty_iterator *iter;
393              
394             static git_iterator_callbacks callbacks = {
395             empty_iterator_noop,
396             empty_iterator_noop,
397             empty_iterator_noop,
398             empty_iterator_advance_over,
399             empty_iterator_reset,
400             empty_iterator_free
401             };
402              
403 17           *out = NULL;
404              
405 17           iter = git__calloc(1, sizeof(empty_iterator));
406 17 50         GIT_ERROR_CHECK_ALLOC(iter);
407              
408 17           iter->base.type = GIT_ITERATOR_EMPTY;
409 17           iter->base.cb = &callbacks;
410 17           iter->base.flags = options->flags;
411              
412 17           *out = &iter->base;
413 17           return 0;
414             }
415              
416             /* Tree iterator */
417              
418             typedef struct {
419             git_tree_entry *tree_entry;
420             const char *parent_path;
421             } tree_iterator_entry;
422              
423             typedef struct {
424             git_tree *tree;
425              
426             /* path to this particular frame (folder) */
427             git_buf path;
428              
429             /* a sorted list of the entries for this frame (folder), these are
430             * actually pointers to the iterator's entry pool.
431             */
432             git_vector entries;
433             tree_iterator_entry *current;
434              
435             size_t next_idx;
436              
437             /* on case insensitive iterations, we also have an array of other
438             * paths that were case insensitively equal to this one, and their
439             * tree objects. we have coalesced the tree entries into this frame.
440             * a child `tree_iterator_entry` will contain a pointer to its actual
441             * parent path.
442             */
443             git_vector similar_trees;
444             git_array_t(git_buf) similar_paths;
445             } tree_iterator_frame;
446              
447             typedef struct {
448             git_iterator base;
449             git_tree *root;
450             git_array_t(tree_iterator_frame) frames;
451              
452             git_index_entry entry;
453             git_buf entry_path;
454              
455             /* a pool of entries to reduce the number of allocations */
456             git_pool entry_pool;
457             } tree_iterator;
458              
459 260           GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame(
460             tree_iterator *iter)
461             {
462 260           return iter->frames.size > 1 ?
463 260 50         &iter->frames.ptr[iter->frames.size-2] : NULL;
464             }
465              
466 1969           GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame(
467             tree_iterator *iter)
468             {
469 1969 100         return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
470             }
471              
472 8           GIT_INLINE(int) tree_entry_cmp(
473             const git_tree_entry *a, const git_tree_entry *b, bool icase)
474             {
475 8 50         return git_path_cmp(
476 16           a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
477 16           b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
478             icase ? git__strncasecmp : git__strncmp);
479             }
480              
481 4           GIT_INLINE(int) tree_iterator_entry_cmp_icase(
482             const void *ptr_a, const void *ptr_b)
483             {
484 4           const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
485 4           const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
486              
487 4           return tree_entry_cmp(a->tree_entry, b->tree_entry, true);
488             }
489              
490 4           static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b)
491             {
492 4           const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
493 4           const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
494              
495 4           int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true);
496              
497             /* stabilize the sort order for filenames that are (case insensitively)
498             * the same by examining the parent path (case sensitively) before
499             * falling back to a case sensitive sort of the filename.
500             */
501 4 50         if (!c && a->parent_path != b->parent_path)
    0          
502 0           c = git__strcmp(a->parent_path, b->parent_path);
503              
504 4 50         if (!c)
505 0           c = tree_entry_cmp(a->tree_entry, b->tree_entry, false);
506              
507 4           return c;
508             }
509              
510 1153           static int tree_iterator_compute_path(
511             git_buf *out,
512             tree_iterator_entry *entry)
513             {
514 1153           git_buf_clear(out);
515              
516 1153 100         if (entry->parent_path)
517 455           git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename);
518             else
519 698           git_buf_puts(out, entry->tree_entry->filename);
520              
521 1153 100         if (git_tree_entry__is_tree(entry->tree_entry))
522 526           git_buf_putc(out, '/');
523              
524 1153 50         if (git_buf_oom(out))
525 0           return -1;
526              
527 1153           return 0;
528             }
529              
530 584           static int tree_iterator_frame_init(
531             tree_iterator *iter,
532             git_tree *tree,
533             tree_iterator_entry *frame_entry)
534             {
535 584           tree_iterator_frame *new_frame = NULL;
536             tree_iterator_entry *new_entry;
537 584           git_tree *dup = NULL;
538             git_tree_entry *tree_entry;
539             git_vector_cmp cmp;
540             size_t i;
541 584           int error = 0;
542              
543 584 100         new_frame = git_array_alloc(iter->frames);
    50          
544 584 50         GIT_ERROR_CHECK_ALLOC(new_frame);
545              
546 584 50         if ((error = git_tree_dup(&dup, tree)) < 0)
547 0           goto done;
548              
549 584           memset(new_frame, 0x0, sizeof(tree_iterator_frame));
550 584           new_frame->tree = dup;
551              
552 584 100         if (frame_entry &&
    50          
553 260           (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0)
554 0           goto done;
555              
556 1168           cmp = iterator__ignore_case(&iter->base) ?
557 584 100         tree_iterator_entry_sort_icase : NULL;
558              
559 584 50         if ((error = git_vector_init(&new_frame->entries,
560 584           dup->entries.size, cmp)) < 0)
561 0           goto done;
562              
563 1570 100         git_array_foreach(dup->entries, i, tree_entry) {
    50          
564 986 50         if ((new_entry = git_pool_malloc(&iter->entry_pool, 1)) == NULL) {
565 0           git_error_set_oom();
566 0           error = -1;
567 0           goto done;
568             }
569              
570 986           new_entry->tree_entry = tree_entry;
571 986           new_entry->parent_path = new_frame->path.ptr;
572              
573 986 50         if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0)
574 0           goto done;
575             }
576              
577 584 100         git_vector_set_sorted(&new_frame->entries,
578             !iterator__ignore_case(&iter->base));
579              
580             done:
581 584 50         if (error < 0) {
582 0           git_tree_free(dup);
583 0 0         git_array_pop(iter->frames);
584             }
585              
586 584           return error;
587             }
588              
589 893           GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry(
590             tree_iterator_frame *frame)
591             {
592 893           return frame->current;
593             }
594              
595 4           GIT_INLINE(int) tree_iterator_frame_push_neighbors(
596             tree_iterator *iter,
597             tree_iterator_frame *parent_frame,
598             tree_iterator_frame *frame,
599             const char *filename)
600             {
601             tree_iterator_entry *entry, *new_entry;
602 4           git_tree *tree = NULL;
603             git_tree_entry *tree_entry;
604             git_buf *path;
605             size_t new_size, i;
606 4           int error = 0;
607              
608 4 50         while (parent_frame->next_idx < parent_frame->entries.length) {
609 0           entry = parent_frame->entries.contents[parent_frame->next_idx];
610              
611 0 0         if (strcasecmp(filename, entry->tree_entry->filename) != 0)
612 0           break;
613              
614 0 0         if ((error = git_tree_lookup(&tree,
615 0           iter->base.repo, entry->tree_entry->oid)) < 0)
616 0           break;
617              
618 0 0         if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
619 0           break;
620              
621 0 0         path = git_array_alloc(parent_frame->similar_paths);
    0          
622 0 0         GIT_ERROR_CHECK_ALLOC(path);
623              
624 0           memset(path, 0, sizeof(git_buf));
625              
626 0 0         if ((error = tree_iterator_compute_path(path, entry)) < 0)
627 0           break;
628              
629 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&new_size,
    0          
630             frame->entries.length, tree->entries.size);
631 0           git_vector_size_hint(&frame->entries, new_size);
632              
633 0 0         git_array_foreach(tree->entries, i, tree_entry) {
    0          
634 0           new_entry = git_pool_malloc(&iter->entry_pool, 1);
635 0 0         GIT_ERROR_CHECK_ALLOC(new_entry);
636              
637 0           new_entry->tree_entry = tree_entry;
638 0           new_entry->parent_path = path->ptr;
639              
640 0 0         if ((error = git_vector_insert(&frame->entries, new_entry)) < 0)
641 0           break;
642             }
643              
644 0 0         if (error)
645 0           break;
646              
647 0           parent_frame->next_idx++;
648             }
649              
650 4           return error;
651             }
652              
653 260           GIT_INLINE(int) tree_iterator_frame_push(
654             tree_iterator *iter, tree_iterator_entry *entry)
655             {
656             tree_iterator_frame *parent_frame, *frame;
657 260           git_tree *tree = NULL;
658             int error;
659              
660 260 50         if ((error = git_tree_lookup(&tree,
661 260 50         iter->base.repo, entry->tree_entry->oid)) < 0 ||
662 260           (error = tree_iterator_frame_init(iter, tree, entry)) < 0)
663             goto done;
664              
665 260           parent_frame = tree_iterator_parent_frame(iter);
666 260           frame = tree_iterator_current_frame(iter);
667              
668             /* if we're case insensitive, then we may have another directory that
669             * is (case insensitively) equal to this one. coalesce those children
670             * into this tree.
671             */
672 260 100         if (iterator__ignore_case(&iter->base))
673 4           error = tree_iterator_frame_push_neighbors(iter,
674 4           parent_frame, frame, entry->tree_entry->filename);
675              
676             done:
677 260           git_tree_free(tree);
678 260           return error;
679             }
680              
681 584           static void tree_iterator_frame_pop(tree_iterator *iter)
682             {
683             tree_iterator_frame *frame;
684 584           git_buf *buf = NULL;
685             git_tree *tree;
686             size_t i;
687              
688 584 50         assert(iter->frames.size);
689              
690 584 50         frame = git_array_pop(iter->frames);
691              
692 584           git_vector_free(&frame->entries);
693 584           git_tree_free(frame->tree);
694              
695             do {
696 584 50         buf = git_array_pop(frame->similar_paths);
697 584           git_buf_dispose(buf);
698 584 50         } while (buf != NULL);
699              
700 584           git_array_clear(frame->similar_paths);
701              
702 584 50         git_vector_foreach(&frame->similar_trees, i, tree)
703 0           git_tree_free(tree);
704              
705 584           git_vector_free(&frame->similar_trees);
706              
707 584           git_buf_dispose(&frame->path);
708 584           }
709              
710 280           static int tree_iterator_current(
711             const git_index_entry **out, git_iterator *i)
712             {
713 280           tree_iterator *iter = (tree_iterator *)i;
714              
715 280 50         if (!iterator__has_been_accessed(i))
716 280           return iter->base.cb->advance(out, i);
717              
718 0 0         if (!iter->frames.size) {
719 0           *out = NULL;
720 0           return GIT_ITEROVER;
721             }
722              
723 0           *out = &iter->entry;
724 0           return 0;
725             }
726              
727 607           static void tree_iterator_set_current(
728             tree_iterator *iter,
729             tree_iterator_frame *frame,
730             tree_iterator_entry *entry)
731             {
732 607           git_tree_entry *tree_entry = entry->tree_entry;
733              
734 607           frame->current = entry;
735              
736 607           memset(&iter->entry, 0x0, sizeof(git_index_entry));
737              
738 607           iter->entry.mode = tree_entry->attr;
739 607           iter->entry.path = iter->entry_path.ptr;
740 607           git_oid_cpy(&iter->entry.id, tree_entry->oid);
741 607           }
742              
743 891           static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
744             {
745 891           tree_iterator *iter = (tree_iterator *)i;
746 891           int error = 0;
747              
748 891           iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
749              
750             /* examine tree entries until we find the next one to return */
751             while (true) {
752             tree_iterator_entry *prev_entry, *entry;
753             tree_iterator_frame *frame;
754             bool is_tree;
755              
756 1709 100         if ((frame = tree_iterator_current_frame(iter)) == NULL) {
757 278           error = GIT_ITEROVER;
758 278           break;
759             }
760              
761             /* no more entries in this frame. pop the frame out */
762 1431 100         if (frame->next_idx == frame->entries.length) {
763 538           tree_iterator_frame_pop(iter);
764 538           continue;
765             }
766              
767             /* we may have coalesced the contents of case-insensitively same-named
768             * directories, so do the sort now.
769             */
770 893 100         if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries))
    100          
771 6           git_vector_sort(&frame->entries);
772              
773             /* we have more entries in the current frame, that's our next entry */
774 893           prev_entry = tree_iterator_current_entry(frame);
775 893           entry = frame->entries.contents[frame->next_idx];
776 893           frame->next_idx++;
777              
778             /* we can have collisions when iterating case insensitively. (eg,
779             * 'A/a' and 'a/A'). squash this one if it's already been seen.
780             */
781 893 100         if (iterator__ignore_case(&iter->base) &&
    100          
782 4 50         prev_entry &&
783 4           tree_iterator_entry_cmp_icase(prev_entry, entry) == 0)
784 0           continue;
785              
786 893 50         if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0)
787 0           break;
788              
789             /* if this path is before our start, advance over this entry */
790 893 50         if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false))
791 0           continue;
792              
793             /* if this path is after our end, stop */
794 893 100         if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) {
795 6           error = GIT_ITEROVER;
796 6           break;
797             }
798              
799             /* if we have a list of paths we're interested in, examine it */
800 887 100         if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr))
801 20           continue;
802              
803 867           is_tree = git_tree_entry__is_tree(entry->tree_entry);
804              
805             /* if we are *not* including trees then advance over this entry */
806 867 100         if (is_tree && !iterator__include_trees(iter)) {
    50          
807              
808             /* if we've found a tree (and are not returning it to the caller)
809             * and we are autoexpanding, then we want to return the first
810             * child. push the new directory and advance.
811             */
812 260 50         if (iterator__do_autoexpand(iter)) {
813 260 50         if ((error = tree_iterator_frame_push(iter, entry)) < 0)
814 0           break;
815             }
816              
817 260           continue;
818             }
819              
820 607           tree_iterator_set_current(iter, frame, entry);
821              
822             /* if we are autoexpanding, then push this as a new frame, so that
823             * the next call to `advance` will dive into this directory.
824             */
825 607 50         if (is_tree && iterator__do_autoexpand(iter))
    0          
826 0           error = tree_iterator_frame_push(iter, entry);
827              
828 607           break;
829 818           }
830              
831 891 50         if (out)
832 891 100         *out = (error == 0) ? &iter->entry : NULL;
833              
834 891           return error;
835             }
836              
837 0           static int tree_iterator_advance_into(
838             const git_index_entry **out, git_iterator *i)
839             {
840 0           tree_iterator *iter = (tree_iterator *)i;
841             tree_iterator_frame *frame;
842             tree_iterator_entry *prev_entry;
843             int error;
844              
845 0 0         if (out)
846 0           *out = NULL;
847              
848 0 0         if ((frame = tree_iterator_current_frame(iter)) == NULL)
849 0           return GIT_ITEROVER;
850              
851             /* get the last seen entry */
852 0           prev_entry = tree_iterator_current_entry(frame);
853              
854             /* it's legal to call advance_into when auto-expand is on. in this case,
855             * we will have pushed a new (empty) frame on to the stack for this
856             * new directory. since it's empty, its current_entry should be null.
857             */
858 0 0         assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
859              
860 0 0         if (prev_entry) {
861 0 0         if (!git_tree_entry__is_tree(prev_entry->tree_entry))
862 0           return 0;
863              
864 0 0         if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0)
865 0           return error;
866             }
867              
868             /* we've advanced into the directory in question, let advance
869             * find the first entry
870             */
871 0           return tree_iterator_advance(out, i);
872             }
873              
874 0           static int tree_iterator_advance_over(
875             const git_index_entry **out,
876             git_iterator_status_t *status,
877             git_iterator *i)
878             {
879 0           *status = GIT_ITERATOR_STATUS_NORMAL;
880 0           return git_iterator_advance(out, i);
881             }
882              
883 324           static void tree_iterator_clear(tree_iterator *iter)
884             {
885 370 100         while (iter->frames.size)
886 46           tree_iterator_frame_pop(iter);
887              
888 324           git_array_clear(iter->frames);
889              
890 324           git_pool_clear(&iter->entry_pool);
891 324           git_buf_clear(&iter->entry_path);
892              
893 324           iterator_clear(&iter->base);
894 324           }
895              
896 324           static int tree_iterator_init(tree_iterator *iter)
897             {
898             int error;
899              
900 324           git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry));
901              
902 324 50         if ((error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0)
903 0           return error;
904              
905 324           iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
906              
907 324           return 0;
908             }
909              
910 40           static int tree_iterator_reset(git_iterator *i)
911             {
912 40           tree_iterator *iter = (tree_iterator *)i;
913              
914 40           tree_iterator_clear(iter);
915 40           return tree_iterator_init(iter);
916             }
917              
918 284           static void tree_iterator_free(git_iterator *i)
919             {
920 284           tree_iterator *iter = (tree_iterator *)i;
921              
922 284           tree_iterator_clear(iter);
923              
924 284           git_tree_free(iter->root);
925 284           git_buf_dispose(&iter->entry_path);
926 284           }
927              
928 301           int git_iterator_for_tree(
929             git_iterator **out,
930             git_tree *tree,
931             git_iterator_options *options)
932             {
933             tree_iterator *iter;
934             int error;
935              
936             static git_iterator_callbacks callbacks = {
937             tree_iterator_current,
938             tree_iterator_advance,
939             tree_iterator_advance_into,
940             tree_iterator_advance_over,
941             tree_iterator_reset,
942             tree_iterator_free
943             };
944              
945 301           *out = NULL;
946              
947 301 100         if (tree == NULL)
948 17           return git_iterator_for_nothing(out, options);
949              
950 284           iter = git__calloc(1, sizeof(tree_iterator));
951 284 50         GIT_ERROR_CHECK_ALLOC(iter);
952              
953 284           iter->base.type = GIT_ITERATOR_TREE;
954 284           iter->base.cb = &callbacks;
955              
956 284 50         if ((error = iterator_init_common(&iter->base,
957 284 50         git_tree_owner(tree), NULL, options)) < 0 ||
958 284 50         (error = git_tree_dup(&iter->root, tree)) < 0 ||
959             (error = tree_iterator_init(iter)) < 0)
960             goto on_error;
961              
962 284           *out = &iter->base;
963 284           return 0;
964              
965             on_error:
966 0           git_iterator_free(&iter->base);
967 0           return error;
968             }
969              
970 0           int git_iterator_current_tree_entry(
971             const git_tree_entry **tree_entry, git_iterator *i)
972             {
973             tree_iterator *iter;
974             tree_iterator_frame *frame;
975             tree_iterator_entry *entry;
976              
977 0 0         assert(i->type == GIT_ITERATOR_TREE);
978              
979 0           iter = (tree_iterator *)i;
980              
981 0           frame = tree_iterator_current_frame(iter);
982 0           entry = tree_iterator_current_entry(frame);
983              
984 0           *tree_entry = entry->tree_entry;
985 0           return 0;
986             }
987              
988 0           int git_iterator_current_parent_tree(
989             const git_tree **parent_tree, git_iterator *i, size_t depth)
990             {
991             tree_iterator *iter;
992             tree_iterator_frame *frame;
993              
994 0 0         assert(i->type == GIT_ITERATOR_TREE);
995              
996 0           iter = (tree_iterator *)i;
997              
998 0 0         assert(depth < iter->frames.size);
999 0           frame = &iter->frames.ptr[iter->frames.size-depth-1];
1000              
1001 0           *parent_tree = frame->tree;
1002 0           return 0;
1003             }
1004              
1005             /* Filesystem iterator */
1006              
1007             typedef struct {
1008             struct stat st;
1009             size_t path_len;
1010             iterator_pathlist_search_t match;
1011             git_oid id;
1012             char path[GIT_FLEX_ARRAY];
1013             } filesystem_iterator_entry;
1014              
1015             typedef struct {
1016             git_vector entries;
1017             git_pool entry_pool;
1018             size_t next_idx;
1019              
1020             size_t path_len;
1021             int is_ignored;
1022             } filesystem_iterator_frame;
1023              
1024             typedef struct {
1025             git_iterator base;
1026             char *root;
1027             size_t root_len;
1028              
1029             unsigned int dirload_flags;
1030              
1031             git_tree *tree;
1032             git_index *index;
1033             git_vector index_snapshot;
1034              
1035             git_array_t(filesystem_iterator_frame) frames;
1036             git_ignores ignores;
1037              
1038             /* info about the current entry */
1039             git_index_entry entry;
1040             git_buf current_path;
1041             int current_is_ignored;
1042              
1043             /* temporary buffer for advance_over */
1044             git_buf tmp_buf;
1045             } filesystem_iterator;
1046              
1047              
1048 176           GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame(
1049             filesystem_iterator *iter)
1050             {
1051 176           return iter->frames.size > 1 ?
1052 176 50         &iter->frames.ptr[iter->frames.size-2] : NULL;
1053             }
1054              
1055 2256           GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame(
1056             filesystem_iterator *iter)
1057             {
1058 2256 100         return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
1059             }
1060              
1061 243           GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry(
1062             filesystem_iterator_frame *frame)
1063             {
1064 243           return frame->next_idx == 0 ?
1065 243 50         NULL : frame->entries.contents[frame->next_idx-1];
1066             }
1067              
1068 1643           static int filesystem_iterator_entry_cmp(const void *_a, const void *_b)
1069             {
1070 1643           const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1071 1643           const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
1072              
1073 1643           return git__strcmp(a->path, b->path);
1074             }
1075              
1076 8           static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b)
1077             {
1078 8           const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1079 8           const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
1080              
1081 8           return git__strcasecmp(a->path, b->path);
1082             }
1083              
1084             #define FILESYSTEM_MAX_DEPTH 100
1085              
1086             /**
1087             * Figure out if an entry is a submodule.
1088             *
1089             * We consider it a submodule if the path is listed as a submodule in
1090             * either the tree or the index.
1091             */
1092 269           static int filesystem_iterator_is_submodule(
1093             bool *out, filesystem_iterator *iter, const char *path, size_t path_len)
1094             {
1095 269           bool is_submodule = false;
1096             int error;
1097              
1098 269           *out = false;
1099              
1100             /* first see if this path is a submodule in HEAD */
1101 269 100         if (iter->tree) {
1102             git_tree_entry *entry;
1103              
1104 9           error = git_tree_entry_bypath(&entry, iter->tree, path);
1105              
1106 9 100         if (error < 0 && error != GIT_ENOTFOUND)
    50          
1107 0           return error;
1108              
1109 9 100         if (!error) {
1110 8           is_submodule = (entry->attr == GIT_FILEMODE_COMMIT);
1111 9           git_tree_entry_free(entry);
1112             }
1113             }
1114              
1115 269 50         if (!is_submodule && iter->base.index) {
    100          
1116             size_t pos;
1117              
1118 206           error = git_index_snapshot_find(&pos,
1119             &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0);
1120              
1121 206 50         if (error < 0 && error != GIT_ENOTFOUND)
    50          
1122 0           return error;
1123              
1124 206 50         if (!error) {
1125 0           git_index_entry *e = git_vector_get(&iter->index_snapshot, pos);
1126 206           is_submodule = (e->mode == GIT_FILEMODE_COMMIT);
1127             }
1128             }
1129              
1130 269           *out = is_submodule;
1131 269           return 0;
1132             }
1133              
1134 452           static void filesystem_iterator_frame_push_ignores(
1135             filesystem_iterator *iter,
1136             filesystem_iterator_entry *frame_entry,
1137             filesystem_iterator_frame *new_frame)
1138             {
1139             filesystem_iterator_frame *previous_frame;
1140 452 100         const char *path = frame_entry ? frame_entry->path : "";
1141              
1142 452 100         if (!iterator__honor_ignores(&iter->base))
1143 93           return;
1144              
1145 359 50         if (git_ignore__lookup(&new_frame->is_ignored,
1146             &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) {
1147 0           git_error_clear();
1148 0           new_frame->is_ignored = GIT_IGNORE_NOTFOUND;
1149             }
1150              
1151             /* if this is not the top level directory... */
1152 359 100         if (frame_entry) {
1153             const char *relative_path;
1154              
1155 176           previous_frame = filesystem_iterator_parent_frame(iter);
1156              
1157             /* push new ignores for files in this directory */
1158 176           relative_path = frame_entry->path + previous_frame->path_len;
1159              
1160             /* inherit ignored from parent if no rule specified */
1161 176 50         if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND)
1162 176           new_frame->is_ignored = previous_frame->is_ignored;
1163              
1164 176           git_ignore__push_dir(&iter->ignores, relative_path);
1165             }
1166             }
1167              
1168 452           static void filesystem_iterator_frame_pop_ignores(
1169             filesystem_iterator *iter)
1170             {
1171 452 100         if (iterator__honor_ignores(&iter->base))
1172 359           git_ignore__pop_dir(&iter->ignores);
1173 452           }
1174              
1175 1246           GIT_INLINE(bool) filesystem_iterator_examine_path(
1176             bool *is_dir_out,
1177             iterator_pathlist_search_t *match_out,
1178             filesystem_iterator *iter,
1179             filesystem_iterator_entry *frame_entry,
1180             const char *path,
1181             size_t path_len)
1182             {
1183 1246           bool is_dir = 0;
1184 1246           iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL;
1185              
1186 1246           *is_dir_out = false;
1187 1246           *match_out = ITERATOR_PATHLIST_NONE;
1188              
1189 1246 100         if (iter->base.start_len) {
1190 46           int cmp = iter->base.strncomp(path, iter->base.start, path_len);
1191              
1192             /* we haven't stat'ed `path` yet, so we don't yet know if it's a
1193             * directory or not. special case if the current path may be a
1194             * directory that matches the start prefix.
1195             */
1196 46 100         if (cmp == 0) {
1197 6 100         if (iter->base.start[path_len] == '/')
1198 1           is_dir = true;
1199              
1200 5 50         else if (iter->base.start[path_len] != '\0')
1201 0           cmp = -1;
1202             }
1203              
1204 46 100         if (cmp < 0)
1205 20           return false;
1206             }
1207              
1208 1226 100         if (iter->base.end_len) {
1209 26           int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len);
1210              
1211 26 100         if (cmp > 0)
1212 14           return false;
1213             }
1214              
1215             /* if we have a pathlist that we're limiting to, examine this path now
1216             * to avoid a `stat` if we're not interested in the path.
1217             */
1218 1212 100         if (iter->base.pathlist.length) {
1219             /* if our parent was explicitly included, so too are we */
1220 145 100         if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT)
    50          
1221 0           match = ITERATOR_PATHLIST_FULL;
1222             else
1223 145           match = iterator_pathlist_search(&iter->base, path, path_len);
1224              
1225 145 100         if (match == ITERATOR_PATHLIST_NONE)
1226 99           return false;
1227              
1228             /* Ensure that the pathlist entry lines up with what we expected */
1229 46 50         if (match == ITERATOR_PATHLIST_IS_DIR ||
    100          
1230             match == ITERATOR_PATHLIST_IS_PARENT)
1231 8           is_dir = true;
1232             }
1233              
1234 1113           *is_dir_out = is_dir;
1235 1113           *match_out = match;
1236 1113           return true;
1237             }
1238              
1239 1113           GIT_INLINE(bool) filesystem_iterator_is_dot_git(
1240             filesystem_iterator *iter, const char *path, size_t path_len)
1241             {
1242             size_t len;
1243              
1244 1113 100         if (!iterator__ignore_dot_git(&iter->base))
1245 191           return false;
1246              
1247 922 50         if ((len = path_len) < 4)
1248 0           return false;
1249              
1250 922 50         if (path[len - 1] == '/')
1251 0           len--;
1252              
1253 922 100         if (git__tolower(path[len - 1]) != 't' ||
    100          
1254 137 50         git__tolower(path[len - 2]) != 'i' ||
1255 137 50         git__tolower(path[len - 3]) != 'g' ||
1256 137           git__tolower(path[len - 4]) != '.')
1257 785           return false;
1258              
1259 137 50         return (len == 4 || path[len - 5] == '/');
    0          
1260             }
1261              
1262 0           static int filesystem_iterator_entry_hash(
1263             filesystem_iterator *iter,
1264             filesystem_iterator_entry *entry)
1265             {
1266 0           git_buf fullpath = GIT_BUF_INIT;
1267             int error;
1268              
1269 0 0         if (S_ISDIR(entry->st.st_mode)) {
1270 0           memset(&entry->id, 0, GIT_OID_RAWSZ);
1271 0           return 0;
1272             }
1273              
1274 0 0         if (iter->base.type == GIT_ITERATOR_WORKDIR)
1275 0           return git_repository_hashfile(&entry->id,
1276 0           iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL);
1277              
1278 0 0         if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path)))
1279 0           error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB);
1280              
1281 0           git_buf_dispose(&fullpath);
1282 0           return error;
1283             }
1284              
1285 976           static int filesystem_iterator_entry_init(
1286             filesystem_iterator_entry **out,
1287             filesystem_iterator *iter,
1288             filesystem_iterator_frame *frame,
1289             const char *path,
1290             size_t path_len,
1291             struct stat *statbuf,
1292             iterator_pathlist_search_t pathlist_match)
1293             {
1294             filesystem_iterator_entry *entry;
1295             size_t entry_size;
1296 976           int error = 0;
1297              
1298 976           *out = NULL;
1299              
1300             /* Make sure to append two bytes, one for the path's null
1301             * termination, one for a possible trailing '/' for folders.
1302             */
1303 976 50         GIT_ERROR_CHECK_ALLOC_ADD(&entry_size,
    50          
1304             sizeof(filesystem_iterator_entry), path_len);
1305 976 50         GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, entry_size, 2);
    50          
1306              
1307 976           entry = git_pool_malloc(&frame->entry_pool, entry_size);
1308 976 50         GIT_ERROR_CHECK_ALLOC(entry);
1309              
1310 976           entry->path_len = path_len;
1311 976           entry->match = pathlist_match;
1312 976           memcpy(entry->path, path, path_len);
1313 976           memcpy(&entry->st, statbuf, sizeof(struct stat));
1314              
1315             /* Suffix directory paths with a '/' */
1316 976 100         if (S_ISDIR(entry->st.st_mode))
1317 269           entry->path[entry->path_len++] = '/';
1318              
1319 976           entry->path[entry->path_len] = '\0';
1320              
1321 976 50         if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
1322 0           error = filesystem_iterator_entry_hash(iter, entry);
1323              
1324 976 50         if (!error)
1325 976           *out = entry;
1326              
1327 976           return error;
1328             }
1329              
1330 454           static int filesystem_iterator_frame_push(
1331             filesystem_iterator *iter,
1332             filesystem_iterator_entry *frame_entry)
1333             {
1334 454           filesystem_iterator_frame *new_frame = NULL;
1335 454           git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
1336 454           git_buf root = GIT_BUF_INIT;
1337             const char *path;
1338             filesystem_iterator_entry *entry;
1339             struct stat statbuf;
1340             size_t path_len;
1341             int error;
1342              
1343 454 50         if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
1344 0           git_error_set(GIT_ERROR_REPOSITORY,
1345             "directory nesting too deep (%"PRIuZ")", iter->frames.size);
1346 0           return -1;
1347             }
1348              
1349 454 100         new_frame = git_array_alloc(iter->frames);
    50          
1350 454 50         GIT_ERROR_CHECK_ALLOC(new_frame);
1351              
1352 454           memset(new_frame, 0, sizeof(filesystem_iterator_frame));
1353              
1354 454 100         if (frame_entry)
1355 239           git_buf_joinpath(&root, iter->root, frame_entry->path);
1356             else
1357 215           git_buf_puts(&root, iter->root);
1358              
1359 454 50         if (git_buf_oom(&root)) {
1360 0           error = -1;
1361 0           goto done;
1362             }
1363              
1364 454 100         new_frame->path_len = frame_entry ? frame_entry->path_len : 0;
1365              
1366             /* Any error here is equivalent to the dir not existing, skip over it */
1367 454 100         if ((error = git_path_diriter_init(
1368 454           &diriter, root.ptr, iter->dirload_flags)) < 0) {
1369 2           error = GIT_ENOTFOUND;
1370 2           goto done;
1371             }
1372              
1373 452 100         if ((error = git_vector_init(&new_frame->entries, 64,
    50          
1374 452           iterator__ignore_case(&iter->base) ?
1375             filesystem_iterator_entry_cmp_icase :
1376             filesystem_iterator_entry_cmp)) < 0)
1377 0           goto done;
1378              
1379 452           git_pool_init(&new_frame->entry_pool, 1);
1380              
1381             /* check if this directory is ignored */
1382 452           filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame);
1383              
1384 1698 100         while ((error = git_path_diriter_next(&diriter)) == 0) {
1385 1246           iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
1386 1246           bool dir_expected = false;
1387              
1388 1246 50         if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
1389 0           goto done;
1390              
1391 1246 50         assert(path_len > iter->root_len);
1392              
1393             /* remove the prefix if requested */
1394 1246           path += iter->root_len;
1395 1246           path_len -= iter->root_len;
1396              
1397             /* examine start / end and the pathlist to see if this path is in it.
1398             * note that since we haven't yet stat'ed the path, we cannot know
1399             * whether it's a directory yet or not, so this can give us an
1400             * expected type (S_IFDIR or S_IFREG) that we should examine)
1401             */
1402 1246 100         if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match,
1403             iter, frame_entry, path, path_len))
1404 270           continue;
1405              
1406             /* TODO: don't need to stat if assume unchanged for this path and
1407             * we have an index, we can just copy the data out of it.
1408             */
1409              
1410 1113 50         if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) {
1411             /* file was removed between readdir and lstat */
1412 0 0         if (error == GIT_ENOTFOUND)
1413 0           continue;
1414              
1415             /* treat the file as unreadable */
1416 0           memset(&statbuf, 0, sizeof(statbuf));
1417 0           statbuf.st_mode = GIT_FILEMODE_UNREADABLE;
1418              
1419 0           error = 0;
1420             }
1421              
1422 1113           iter->base.stat_calls++;
1423              
1424             /* Ignore wacky things in the filesystem */
1425 1113 100         if (!S_ISDIR(statbuf.st_mode) &&
    50          
1426 0 0         !S_ISREG(statbuf.st_mode) &&
1427 0 0         !S_ISLNK(statbuf.st_mode) &&
1428 0           statbuf.st_mode != GIT_FILEMODE_UNREADABLE)
1429 0           continue;
1430              
1431 1113 100         if (filesystem_iterator_is_dot_git(iter, path, path_len))
1432 137           continue;
1433              
1434             /* convert submodules to GITLINK and remove trailing slashes */
1435 976 100         if (S_ISDIR(statbuf.st_mode)) {
1436 269           bool submodule = false;
1437              
1438 269 50         if ((error = filesystem_iterator_is_submodule(&submodule,
1439             iter, path, path_len)) < 0)
1440 0           goto done;
1441              
1442 269 50         if (submodule)
1443 269           statbuf.st_mode = GIT_FILEMODE_COMMIT;
1444             }
1445              
1446             /* Ensure that the pathlist entry lines up with what we expected */
1447 707 50         else if (dir_expected)
1448 0           continue;
1449              
1450 976 50         if ((error = filesystem_iterator_entry_init(&entry,
1451             iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0)
1452 0           goto done;
1453              
1454 976           git_vector_insert(&new_frame->entries, entry);
1455             }
1456              
1457 452 50         if (error == GIT_ITEROVER)
1458 452           error = 0;
1459              
1460             /* sort now that directory suffix is added */
1461 452           git_vector_sort(&new_frame->entries);
1462              
1463             done:
1464 454 100         if (error < 0)
1465 2 50         git_array_pop(iter->frames);
1466              
1467 454           git_buf_dispose(&root);
1468 454           git_path_diriter_free(&diriter);
1469 454           return error;
1470             }
1471              
1472 452           GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter)
1473             {
1474             filesystem_iterator_frame *frame;
1475              
1476 452 50         assert(iter->frames.size);
1477              
1478 452 50         frame = git_array_pop(iter->frames);
1479 452           filesystem_iterator_frame_pop_ignores(iter);
1480              
1481 452           git_pool_clear(&frame->entry_pool);
1482 452           git_vector_free(&frame->entries);
1483 452           }
1484              
1485 895           static void filesystem_iterator_set_current(
1486             filesystem_iterator *iter,
1487             filesystem_iterator_entry *entry)
1488             {
1489             /*
1490             * Index entries are limited to 32 bit timestamps. We can safely
1491             * cast this since workdir times are only used in the cache; any
1492             * mismatch will cause a hash recomputation which is unfortunate
1493             * but affects only people who set their filetimes to 2038.
1494             * (Same with the file size.)
1495             */
1496 895           iter->entry.ctime.seconds = (int32_t)entry->st.st_ctime;
1497 895           iter->entry.mtime.seconds = (int32_t)entry->st.st_mtime;
1498              
1499             #if defined(GIT_USE_NSEC)
1500             iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
1501             iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
1502             #else
1503 895           iter->entry.ctime.nanoseconds = 0;
1504 895           iter->entry.mtime.nanoseconds = 0;
1505             #endif
1506              
1507 895           iter->entry.dev = entry->st.st_dev;
1508 895           iter->entry.ino = entry->st.st_ino;
1509 895           iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode);
1510 895           iter->entry.uid = entry->st.st_uid;
1511 895           iter->entry.gid = entry->st.st_gid;
1512 895           iter->entry.file_size = (uint32_t)entry->st.st_size;
1513              
1514 895 50         if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
1515 0           git_oid_cpy(&iter->entry.id, &entry->id);
1516              
1517 895           iter->entry.path = entry->path;
1518              
1519 895           iter->current_is_ignored = GIT_IGNORE_UNCHECKED;
1520 895           }
1521              
1522 244           static int filesystem_iterator_current(
1523             const git_index_entry **out, git_iterator *i)
1524             {
1525 244           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1526              
1527 244 100         if (!iterator__has_been_accessed(i))
1528 171           return iter->base.cb->advance(out, i);
1529              
1530 73 50         if (!iter->frames.size) {
1531 0           *out = NULL;
1532 0           return GIT_ITEROVER;
1533             }
1534              
1535 73           *out = &iter->entry;
1536 73           return 0;
1537             }
1538              
1539 958           static int filesystem_iterator_is_dir(
1540             bool *is_dir,
1541             const filesystem_iterator *iter,
1542             const filesystem_iterator_entry *entry)
1543             {
1544             struct stat st;
1545 958           git_buf fullpath = GIT_BUF_INIT;
1546 958           int error = 0;
1547              
1548 958 100         if (S_ISDIR(entry->st.st_mode)) {
1549 269           *is_dir = 1;
1550 269           goto done;
1551             }
1552              
1553 689 100         if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) {
    50          
1554 689           *is_dir = 0;
1555 689           goto done;
1556             }
1557              
1558 0 0         if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
    0          
1559 0           (error = p_stat(fullpath.ptr, &st)) < 0)
1560             goto done;
1561              
1562 0           *is_dir = S_ISDIR(st.st_mode);
1563              
1564             done:
1565 958           git_buf_dispose(&fullpath);
1566 958           return error;
1567             }
1568              
1569 1102           static int filesystem_iterator_advance(
1570             const git_index_entry **out, git_iterator *i)
1571             {
1572 1102           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1573             bool is_dir;
1574 1102           int error = 0;
1575              
1576 1102           iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1577              
1578             /* examine filesystem entries until we find the next one to return */
1579             while (true) {
1580             filesystem_iterator_frame *frame;
1581             filesystem_iterator_entry *entry;
1582              
1583 1611 100         if ((frame = filesystem_iterator_current_frame(iter)) == NULL) {
1584 207           error = GIT_ITEROVER;
1585 207           break;
1586             }
1587              
1588             /* no more entries in this frame. pop the frame out */
1589 1404 100         if (frame->next_idx == frame->entries.length) {
1590 446           filesystem_iterator_frame_pop(iter);
1591 446           continue;
1592             }
1593              
1594             /* we have more entries in the current frame, that's our next entry */
1595 958           entry = frame->entries.contents[frame->next_idx];
1596 958           frame->next_idx++;
1597              
1598 958 50         if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0)
1599 0           break;
1600              
1601 958 100         if (is_dir) {
1602 269 100         if (iterator__do_autoexpand(iter)) {
1603 63           error = filesystem_iterator_frame_push(iter, entry);
1604              
1605             /* may get GIT_ENOTFOUND due to races or permission problems
1606             * that we want to quietly swallow
1607             */
1608 63 50         if (error == GIT_ENOTFOUND)
1609 0           continue;
1610 63 50         else if (error < 0)
1611 0           break;
1612             }
1613              
1614 269 100         if (!iterator__include_trees(iter))
1615 63           continue;
1616             }
1617              
1618 895           filesystem_iterator_set_current(iter, entry);
1619 895           break;
1620 509           }
1621              
1622 1102 50         if (out)
1623 1102 100         *out = (error == 0) ? &iter->entry : NULL;
1624              
1625 1102           return error;
1626             }
1627              
1628 176           static int filesystem_iterator_advance_into(
1629             const git_index_entry **out, git_iterator *i)
1630             {
1631 176           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1632             filesystem_iterator_frame *frame;
1633             filesystem_iterator_entry *prev_entry;
1634             int error;
1635              
1636 176 50         if (out)
1637 176           *out = NULL;
1638              
1639 176 50         if ((frame = filesystem_iterator_current_frame(iter)) == NULL)
1640 0           return GIT_ITEROVER;
1641              
1642             /* get the last seen entry */
1643 176           prev_entry = filesystem_iterator_current_entry(frame);
1644              
1645             /* it's legal to call advance_into when auto-expand is on. in this case,
1646             * we will have pushed a new (empty) frame on to the stack for this
1647             * new directory. since it's empty, its current_entry should be null.
1648             */
1649 176 50         assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
1650              
1651 176 50         if (prev_entry) {
1652 176 50         if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
    50          
1653 176           !S_ISDIR(prev_entry->st.st_mode))
1654 0           return 0;
1655              
1656 176 50         if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0)
1657 0           return error;
1658             }
1659              
1660             /* we've advanced into the directory in question, let advance
1661             * find the first entry
1662             */
1663 176           return filesystem_iterator_advance(out, i);
1664             }
1665              
1666 6           int git_iterator_current_workdir_path(git_buf **out, git_iterator *i)
1667             {
1668 6           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1669             const git_index_entry *entry;
1670              
1671 6 50         if (i->type != GIT_ITERATOR_FS &&
    50          
1672 6           i->type != GIT_ITERATOR_WORKDIR) {
1673 0           *out = NULL;
1674 0           return 0;
1675             }
1676              
1677 6           git_buf_truncate(&iter->current_path, iter->root_len);
1678              
1679 12           if (git_iterator_current(&entry, i) < 0 ||
1680 6           git_buf_puts(&iter->current_path, entry->path) < 0)
1681 0           return -1;
1682              
1683 6           *out = &iter->current_path;
1684 6           return 0;
1685             }
1686              
1687 405           GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry)
1688             {
1689             #if defined(GIT_WIN32) && !defined(__MINGW32__)
1690             return (entry && entry->mode) ?
1691             (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) :
1692             GIT_DIR_FLAG_UNKNOWN;
1693             #else
1694             GIT_UNUSED(entry);
1695 405           return GIT_DIR_FLAG_UNKNOWN;
1696             #endif
1697             }
1698              
1699 405           static void filesystem_iterator_update_ignored(filesystem_iterator *iter)
1700             {
1701             filesystem_iterator_frame *frame;
1702 405           git_dir_flag dir_flag = entry_dir_flag(&iter->entry);
1703              
1704 405 50         if (git_ignore__lookup(&iter->current_is_ignored,
1705             &iter->ignores, iter->entry.path, dir_flag) < 0) {
1706 0           git_error_clear();
1707 0           iter->current_is_ignored = GIT_IGNORE_NOTFOUND;
1708             }
1709              
1710             /* use ignore from containing frame stack */
1711 405 100         if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) {
1712 395           frame = filesystem_iterator_current_frame(iter);
1713 395           iter->current_is_ignored = frame->is_ignored;
1714             }
1715 405           }
1716              
1717 416           GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
1718             filesystem_iterator *iter)
1719             {
1720 416 100         if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED)
1721 405           filesystem_iterator_update_ignored(iter);
1722              
1723 416           return (iter->current_is_ignored == GIT_IGNORE_TRUE);
1724             }
1725              
1726 381           bool git_iterator_current_is_ignored(git_iterator *i)
1727             {
1728 381           filesystem_iterator *iter = NULL;
1729              
1730 381 100         if (i->type != GIT_ITERATOR_WORKDIR)
1731 49           return false;
1732              
1733 332           iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1734              
1735 332           return filesystem_iterator_current_is_ignored(iter);
1736             }
1737              
1738 7           bool git_iterator_current_tree_is_ignored(git_iterator *i)
1739             {
1740 7           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1741             filesystem_iterator_frame *frame;
1742              
1743 7 50         if (i->type != GIT_ITERATOR_WORKDIR)
1744 0           return false;
1745              
1746 7           frame = filesystem_iterator_current_frame(iter);
1747 7           return (frame->is_ignored == GIT_IGNORE_TRUE);
1748             }
1749              
1750 67           static int filesystem_iterator_advance_over(
1751             const git_index_entry **out,
1752             git_iterator_status_t *status,
1753             git_iterator *i)
1754             {
1755 67           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1756             filesystem_iterator_frame *current_frame;
1757             filesystem_iterator_entry *current_entry;
1758 67           const git_index_entry *entry = NULL;
1759             const char *base;
1760 67           int error = 0;
1761              
1762 67           *out = NULL;
1763 67           *status = GIT_ITERATOR_STATUS_NORMAL;
1764              
1765 67 50         assert(iterator__has_been_accessed(i));
1766              
1767 67           current_frame = filesystem_iterator_current_frame(iter);
1768 67 50         assert(current_frame);
1769 67           current_entry = filesystem_iterator_current_entry(current_frame);
1770 67 50         assert(current_entry);
1771              
1772 67 50         if ((error = git_iterator_current(&entry, i)) < 0)
1773 0           return error;
1774              
1775 67 100         if (!S_ISDIR(entry->mode)) {
1776 49 100         if (filesystem_iterator_current_is_ignored(iter))
1777 1           *status = GIT_ITERATOR_STATUS_IGNORED;
1778              
1779 49           return filesystem_iterator_advance(out, i);
1780             }
1781              
1782 18           git_buf_clear(&iter->tmp_buf);
1783 18 50         if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0)
1784 0           return error;
1785              
1786 18           base = iter->tmp_buf.ptr;
1787              
1788             /* scan inside the directory looking for files. if we find nothing,
1789             * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
1790             * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
1791             * and then stop.
1792             *
1793             * however, if we're here looking for a pathlist item (but are not
1794             * actually in the pathlist ourselves) then start at FILTERED instead of
1795             * EMPTY. callers then know that this path was not something they asked
1796             * about.
1797             */
1798 18 50         *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ?
1799             GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY;
1800              
1801 36 50         while (entry && !iter->base.prefixcomp(entry->path, base)) {
    100          
1802 35 50         if (filesystem_iterator_current_is_ignored(iter)) {
1803             /* if we found an explicitly ignored item, then update from
1804             * EMPTY to IGNORED
1805             */
1806 0           *status = GIT_ITERATOR_STATUS_IGNORED;
1807 35 100         } else if (S_ISDIR(entry->mode)) {
1808 18           error = filesystem_iterator_advance_into(&entry, i);
1809              
1810 18 50         if (!error)
1811 18           continue;
1812              
1813             /* this directory disappeared, ignore it */
1814 0 0         else if (error == GIT_ENOTFOUND)
1815 0           error = 0;
1816              
1817             /* a real error occurred */
1818             else
1819 0           break;
1820             } else {
1821             /* we found a non-ignored item, treat parent as untracked */
1822 17           *status = GIT_ITERATOR_STATUS_NORMAL;
1823 17           break;
1824             }
1825              
1826 0 0         if ((error = git_iterator_advance(&entry, i)) < 0)
1827 0           break;
1828             }
1829              
1830             /* wrap up scan back to base directory */
1831 34 50         while (entry && !iter->base.prefixcomp(entry->path, base)) {
    100          
1832 17 100         if ((error = git_iterator_advance(&entry, i)) < 0)
1833 1           break;
1834             }
1835              
1836 18 100         if (!error)
1837 17           *out = entry;
1838              
1839 67           return error;
1840             }
1841              
1842 215           static void filesystem_iterator_clear(filesystem_iterator *iter)
1843             {
1844 221 100         while (iter->frames.size)
1845 6           filesystem_iterator_frame_pop(iter);
1846              
1847 215           git_array_clear(iter->frames);
1848 215           git_ignore__free(&iter->ignores);
1849              
1850 215           git_buf_dispose(&iter->tmp_buf);
1851              
1852 215           iterator_clear(&iter->base);
1853 215           }
1854              
1855 215           static int filesystem_iterator_init(filesystem_iterator *iter)
1856             {
1857             int error;
1858              
1859 215 100         if (iterator__honor_ignores(&iter->base) &&
    50          
1860 183           (error = git_ignore__for_path(iter->base.repo,
1861             ".gitignore", &iter->ignores)) < 0)
1862 0           return error;
1863              
1864 215 100         if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0)
1865 2           return error;
1866              
1867 213           iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1868              
1869 213           return 0;
1870             }
1871              
1872 6           static int filesystem_iterator_reset(git_iterator *i)
1873             {
1874 6           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1875              
1876 6           filesystem_iterator_clear(iter);
1877 6           return filesystem_iterator_init(iter);
1878             }
1879              
1880 209           static void filesystem_iterator_free(git_iterator *i)
1881             {
1882 209           filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
1883 209           git__free(iter->root);
1884 209           git_buf_dispose(&iter->current_path);
1885 209           git_tree_free(iter->tree);
1886 209 100         if (iter->index)
1887 171           git_index_snapshot_release(&iter->index_snapshot, iter->index);
1888 209           filesystem_iterator_clear(iter);
1889 209           }
1890              
1891 209           static int iterator_for_filesystem(
1892             git_iterator **out,
1893             git_repository *repo,
1894             const char *root,
1895             git_index *index,
1896             git_tree *tree,
1897             git_iterator_t type,
1898             git_iterator_options *options)
1899             {
1900             filesystem_iterator *iter;
1901             size_t root_len;
1902             int error;
1903              
1904             static git_iterator_callbacks callbacks = {
1905             filesystem_iterator_current,
1906             filesystem_iterator_advance,
1907             filesystem_iterator_advance_into,
1908             filesystem_iterator_advance_over,
1909             filesystem_iterator_reset,
1910             filesystem_iterator_free
1911             };
1912              
1913 209           *out = NULL;
1914              
1915 209 50         if (root == NULL)
1916 0           return git_iterator_for_nothing(out, options);
1917              
1918 209           iter = git__calloc(1, sizeof(filesystem_iterator));
1919 209 50         GIT_ERROR_CHECK_ALLOC(iter);
1920              
1921 209           iter->base.type = type;
1922 209           iter->base.cb = &callbacks;
1923              
1924 209           root_len = strlen(root);
1925              
1926 209           iter->root = git__malloc(root_len+2);
1927 209 50         GIT_ERROR_CHECK_ALLOC(iter->root);
1928              
1929 209           memcpy(iter->root, root, root_len);
1930              
1931 209 50         if (root_len == 0 || root[root_len-1] != '/') {
    50          
1932 0           iter->root[root_len] = '/';
1933 0           root_len++;
1934             }
1935 209           iter->root[root_len] = '\0';
1936 209           iter->root_len = root_len;
1937              
1938 209 50         if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0)
1939 0           goto on_error;
1940              
1941 209 50         if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0)
1942 0           goto on_error;
1943              
1944 209 100         if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0)
    50          
1945 0           goto on_error;
1946              
1947 209 100         if (index &&
    50          
1948 171           (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0)
1949 0           goto on_error;
1950              
1951 209           iter->index = index;
1952 209           iter->dirload_flags =
1953 418           (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1954 209           (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
1955 209 50         GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1956              
1957 209 100         if ((error = filesystem_iterator_init(iter)) < 0)
1958 2           goto on_error;
1959              
1960 207           *out = &iter->base;
1961 207           return 0;
1962              
1963             on_error:
1964 2           git_iterator_free(&iter->base);
1965 2           return error;
1966             }
1967              
1968 32           int git_iterator_for_filesystem(
1969             git_iterator **out,
1970             const char *root,
1971             git_iterator_options *options)
1972             {
1973 32           return iterator_for_filesystem(out,
1974             NULL, root, NULL, NULL, GIT_ITERATOR_FS, options);
1975             }
1976              
1977 177           int git_iterator_for_workdir_ext(
1978             git_iterator **out,
1979             git_repository *repo,
1980             const char *repo_workdir,
1981             git_index *index,
1982             git_tree *tree,
1983             git_iterator_options *given_opts)
1984             {
1985 177           git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
1986              
1987 177 100         if (!repo_workdir) {
1988 124 50         if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1989 0           return GIT_EBAREREPO;
1990              
1991 124           repo_workdir = git_repository_workdir(repo);
1992             }
1993              
1994             /* upgrade to a workdir iterator, adding necessary internal flags */
1995 177 50         if (given_opts)
1996 177           memcpy(&options, given_opts, sizeof(git_iterator_options));
1997              
1998 177           options.flags |= GIT_ITERATOR_HONOR_IGNORES |
1999             GIT_ITERATOR_IGNORE_DOT_GIT;
2000              
2001 177           return iterator_for_filesystem(out,
2002             repo, repo_workdir, index, tree, GIT_ITERATOR_WORKDIR, &options);
2003             }
2004              
2005              
2006             /* Index iterator */
2007              
2008              
2009             typedef struct {
2010             git_iterator base;
2011             git_vector entries;
2012             size_t next_idx;
2013              
2014             /* the pseudotree entry */
2015             git_index_entry tree_entry;
2016             git_buf tree_buf;
2017             bool skip_tree;
2018              
2019             const git_index_entry *entry;
2020             } index_iterator;
2021              
2022 259           static int index_iterator_current(
2023             const git_index_entry **out, git_iterator *i)
2024             {
2025 259           index_iterator *iter = (index_iterator *)i;
2026              
2027 259 50         if (!iterator__has_been_accessed(i))
2028 259           return iter->base.cb->advance(out, i);
2029              
2030 0 0         if (iter->entry == NULL) {
2031 0           *out = NULL;
2032 0           return GIT_ITEROVER;
2033             }
2034              
2035 0           *out = iter->entry;
2036 0           return 0;
2037             }
2038              
2039 0           static bool index_iterator_create_pseudotree(
2040             const git_index_entry **out,
2041             index_iterator *iter,
2042             const char *path)
2043             {
2044             const char *prev_path, *relative_path, *dirsep;
2045             size_t common_len;
2046              
2047 0 0         prev_path = iter->entry ? iter->entry->path : "";
2048              
2049             /* determine if the new path is in a different directory from the old */
2050 0           common_len = git_path_common_dirlen(prev_path, path);
2051 0           relative_path = path + common_len;
2052              
2053 0 0         if ((dirsep = strchr(relative_path, '/')) == NULL)
2054 0           return false;
2055              
2056 0           git_buf_clear(&iter->tree_buf);
2057 0           git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1);
2058              
2059 0           iter->tree_entry.mode = GIT_FILEMODE_TREE;
2060 0           iter->tree_entry.path = iter->tree_buf.ptr;
2061              
2062 0           *out = &iter->tree_entry;
2063 0           return true;
2064             }
2065              
2066 0           static int index_iterator_skip_pseudotree(index_iterator *iter)
2067             {
2068 0 0         assert(iterator__has_been_accessed(&iter->base));
2069 0 0         assert(S_ISDIR(iter->entry->mode));
2070              
2071             while (true) {
2072 0           const git_index_entry *next_entry = NULL;
2073              
2074 0 0         if (++iter->next_idx >= iter->entries.length)
2075 0           return GIT_ITEROVER;
2076              
2077 0           next_entry = iter->entries.contents[iter->next_idx];
2078              
2079 0 0         if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path,
2080             iter->tree_buf.size) != 0)
2081 0           break;
2082 0           }
2083              
2084 0           iter->skip_tree = false;
2085 0           return 0;
2086             }
2087              
2088 760           static int index_iterator_advance(
2089             const git_index_entry **out, git_iterator *i)
2090             {
2091 760           index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
2092 760           const git_index_entry *entry = NULL;
2093             bool is_submodule;
2094 760           int error = 0;
2095              
2096 760           iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
2097              
2098             while (true) {
2099 847 100         if (iter->next_idx >= iter->entries.length) {
2100 260           error = GIT_ITEROVER;
2101 260           break;
2102             }
2103              
2104             /* we were not asked to expand this pseudotree. advance over it. */
2105 587 50         if (iter->skip_tree) {
2106 0           index_iterator_skip_pseudotree(iter);
2107 0           continue;
2108             }
2109              
2110 587           entry = iter->entries.contents[iter->next_idx];
2111 587           is_submodule = S_ISGITLINK(entry->mode);
2112              
2113 587 50         if (!iterator_has_started(&iter->base, entry->path, is_submodule)) {
2114 0           iter->next_idx++;
2115 0           continue;
2116             }
2117              
2118 587 50         if (iterator_has_ended(&iter->base, entry->path)) {
2119 0           error = GIT_ITEROVER;
2120 0           break;
2121             }
2122              
2123             /* if we have a list of paths we're interested in, examine it */
2124 587 100         if (!iterator_pathlist_next_is(&iter->base, entry->path)) {
2125 65           iter->next_idx++;
2126 65           continue;
2127             }
2128              
2129             /* if this is a conflict, skip it unless we're including conflicts */
2130 522 100         if (git_index_entry_is_conflict(entry) &&
    50          
2131 22           !iterator__include_conflicts(&iter->base)) {
2132 22           iter->next_idx++;
2133 22           continue;
2134             }
2135              
2136             /* we've found what will be our next _file_ entry. but if we are
2137             * returning trees entries, we may need to return a pseudotree
2138             * entry that will contain this. don't advance over this entry,
2139             * though, we still need to return it on the next `advance`.
2140             */
2141 500           if (iterator__include_trees(&iter->base) &&
2142 0           index_iterator_create_pseudotree(&entry, iter, entry->path)) {
2143              
2144             /* Note whether this pseudo tree should be expanded or not */
2145 0           iter->skip_tree = iterator__dont_autoexpand(&iter->base);
2146 0           break;
2147             }
2148              
2149 500           iter->next_idx++;
2150 500           break;
2151 87           }
2152              
2153 760 100         iter->entry = (error == 0) ? entry : NULL;
2154              
2155 760 50         if (out)
2156 760           *out = iter->entry;
2157              
2158 760           return error;
2159             }
2160              
2161 0           static int index_iterator_advance_into(
2162             const git_index_entry **out, git_iterator *i)
2163             {
2164 0           index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
2165              
2166 0 0         if (! S_ISDIR(iter->tree_entry.mode)) {
2167 0 0         if (out)
2168 0           *out = NULL;
2169              
2170 0           return 0;
2171             }
2172              
2173 0           iter->skip_tree = false;
2174 0           return index_iterator_advance(out, i);
2175             }
2176              
2177 0           static int index_iterator_advance_over(
2178             const git_index_entry **out,
2179             git_iterator_status_t *status,
2180             git_iterator *i)
2181             {
2182 0           index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
2183             const git_index_entry *entry;
2184             int error;
2185              
2186 0 0         if ((error = index_iterator_current(&entry, i)) < 0)
2187 0           return error;
2188              
2189 0 0         if (S_ISDIR(entry->mode))
2190 0           index_iterator_skip_pseudotree(iter);
2191              
2192 0           *status = GIT_ITERATOR_STATUS_NORMAL;
2193 0           return index_iterator_advance(out, i);
2194             }
2195              
2196 18           static void index_iterator_clear(index_iterator *iter)
2197             {
2198 18           iterator_clear(&iter->base);
2199 18           }
2200              
2201 278           static int index_iterator_init(index_iterator *iter)
2202             {
2203 278           iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
2204 278           iter->next_idx = 0;
2205 278           iter->skip_tree = false;
2206 278           return 0;
2207             }
2208              
2209 18           static int index_iterator_reset(git_iterator *i)
2210             {
2211 18           index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
2212              
2213 18           index_iterator_clear(iter);
2214 18           return index_iterator_init(iter);
2215             }
2216              
2217 260           static void index_iterator_free(git_iterator *i)
2218             {
2219 260           index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
2220              
2221 260           git_index_snapshot_release(&iter->entries, iter->base.index);
2222 260           git_buf_dispose(&iter->tree_buf);
2223 260           }
2224              
2225 260           int git_iterator_for_index(
2226             git_iterator **out,
2227             git_repository *repo,
2228             git_index *index,
2229             git_iterator_options *options)
2230             {
2231             index_iterator *iter;
2232             int error;
2233              
2234             static git_iterator_callbacks callbacks = {
2235             index_iterator_current,
2236             index_iterator_advance,
2237             index_iterator_advance_into,
2238             index_iterator_advance_over,
2239             index_iterator_reset,
2240             index_iterator_free
2241             };
2242              
2243 260           *out = NULL;
2244              
2245 260 50         if (index == NULL)
2246 0           return git_iterator_for_nothing(out, options);
2247              
2248 260           iter = git__calloc(1, sizeof(index_iterator));
2249 260 50         GIT_ERROR_CHECK_ALLOC(iter);
2250              
2251 260           iter->base.type = GIT_ITERATOR_INDEX;
2252 260           iter->base.cb = &callbacks;
2253              
2254 260 50         if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 ||
    50          
2255 260 50         (error = git_index_snapshot_new(&iter->entries, index)) < 0 ||
2256             (error = index_iterator_init(iter)) < 0)
2257             goto on_error;
2258              
2259 260 50         git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ?
2260             git_index_entry_icmp : git_index_entry_cmp);
2261 260           git_vector_sort(&iter->entries);
2262              
2263 260           *out = &iter->base;
2264 260           return 0;
2265              
2266             on_error:
2267 0           git_iterator_free(&iter->base);
2268 0           return error;
2269             }
2270              
2271              
2272             /* Iterator API */
2273              
2274 64           int git_iterator_reset_range(
2275             git_iterator *i, const char *start, const char *end)
2276             {
2277 64 50         if (iterator_reset_range(i, start, end) < 0)
2278 0           return -1;
2279              
2280 64           return i->cb->reset(i);
2281             }
2282              
2283 0           void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
2284             {
2285 0 0         assert(!iterator__has_been_accessed(i));
2286 0           iterator_set_ignore_case(i, ignore_case);
2287 0           }
2288              
2289 864           void git_iterator_free(git_iterator *iter)
2290             {
2291 864 100         if (iter == NULL)
2292 94           return;
2293              
2294 770           iter->cb->free(iter);
2295              
2296 770           git_vector_free(&iter->pathlist);
2297 770           git__free(iter->start);
2298 770           git__free(iter->end);
2299              
2300 770           memset(iter, 0, sizeof(*iter));
2301              
2302 770           git__free(iter);
2303             }
2304              
2305 0           int git_iterator_foreach(
2306             git_iterator *iterator,
2307             git_iterator_foreach_cb cb,
2308             void *data)
2309             {
2310             const git_index_entry *iterator_item;
2311 0           int error = 0;
2312              
2313 0 0         if ((error = git_iterator_current(&iterator_item, iterator)) < 0)
2314 0           goto done;
2315              
2316 0 0         if ((error = cb(iterator_item, data)) != 0)
2317 0           goto done;
2318              
2319             while (true) {
2320 0 0         if ((error = git_iterator_advance(&iterator_item, iterator)) < 0)
2321 0           goto done;
2322              
2323 0 0         if ((error = cb(iterator_item, data)) != 0)
2324 0           goto done;
2325 0           }
2326              
2327             done:
2328 0 0         if (error == GIT_ITEROVER)
2329 0           error = 0;
2330              
2331 0           return error;
2332             }
2333              
2334 22           int git_iterator_walk(
2335             git_iterator **iterators,
2336             size_t cnt,
2337             git_iterator_walk_cb cb,
2338             void *data)
2339             {
2340             const git_index_entry **iterator_item; /* next in each iterator */
2341             const git_index_entry **cur_items; /* current path in each iter */
2342             const git_index_entry *first_match;
2343             size_t i, j;
2344 22           int error = 0;
2345              
2346 22           iterator_item = git__calloc(cnt, sizeof(git_index_entry *));
2347 22           cur_items = git__calloc(cnt, sizeof(git_index_entry *));
2348              
2349 22 50         GIT_ERROR_CHECK_ALLOC(iterator_item);
2350 22 50         GIT_ERROR_CHECK_ALLOC(cur_items);
2351              
2352             /* Set up the iterators */
2353 88 100         for (i = 0; i < cnt; i++) {
2354 66           error = git_iterator_current(&iterator_item[i], iterators[i]);
2355              
2356 66 50         if (error < 0 && error != GIT_ITEROVER)
    0          
2357 0           goto done;
2358             }
2359              
2360             while (true) {
2361 280 100         for (i = 0; i < cnt; i++)
2362 210           cur_items[i] = NULL;
2363              
2364 70           first_match = NULL;
2365              
2366             /* Find the next path(s) to consume from each iterator */
2367 280 100         for (i = 0; i < cnt; i++) {
2368 210 100         if (iterator_item[i] == NULL)
2369 70           continue;
2370              
2371 140 100         if (first_match == NULL) {
2372 48           first_match = iterator_item[i];
2373 48           cur_items[i] = iterator_item[i];
2374             } else {
2375 92           int path_diff = git_index_entry_cmp(iterator_item[i], first_match);
2376              
2377 92 100         if (path_diff < 0) {
2378             /* Found an index entry that sorts before the one we're
2379             * looking at. Forget that we've seen the other and
2380             * look at the other iterators for this path.
2381             */
2382 3 100         for (j = 0; j < i; j++)
2383 2           cur_items[j] = NULL;
2384              
2385 1           first_match = iterator_item[i];
2386 1           cur_items[i] = iterator_item[i];
2387 91 100         } else if (path_diff == 0) {
2388 90           cur_items[i] = iterator_item[i];
2389             }
2390             }
2391             }
2392              
2393 70 100         if (first_match == NULL)
2394 22           break;
2395              
2396 48 50         if ((error = cb(cur_items, data)) != 0)
2397 0           goto done;
2398              
2399             /* Advance each iterator that participated */
2400 192 100         for (i = 0; i < cnt; i++) {
2401 144 100         if (cur_items[i] == NULL)
2402 7           continue;
2403              
2404 137           error = git_iterator_advance(&iterator_item[i], iterators[i]);
2405              
2406 137 100         if (error < 0 && error != GIT_ITEROVER)
    50          
2407 0           goto done;
2408             }
2409 48           }
2410              
2411             done:
2412 22           git__free((git_index_entry **)iterator_item);
2413 22           git__free((git_index_entry **)cur_items);
2414              
2415 22 50         if (error == GIT_ITEROVER)
2416 22           error = 0;
2417              
2418 22           return error;
2419             }