File Coverage

deps/libgit2/src/libgit2/iterator.c
Criterion Covered Total %
statement 797 1072 74.3
branch 426 728 58.5
condition n/a
subroutine n/a
pod n/a
total 1223 1800 67.9


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