]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
Merge pull request #2895 from ethomson/alloc_overflow
[libgit2.git] / src / iterator.c
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 #include "tree.h"
10 #include "index.h"
11 #include "ignore.h"
12 #include "buffer.h"
13 #include "submodule.h"
14 #include <ctype.h>
15
16 #define ITERATOR_SET_CB(P,NAME_LC) do { \
17 (P)->cb.current = NAME_LC ## _iterator__current; \
18 (P)->cb.advance = NAME_LC ## _iterator__advance; \
19 (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
20 (P)->cb.seek = NAME_LC ## _iterator__seek; \
21 (P)->cb.reset = NAME_LC ## _iterator__reset; \
22 (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
23 (P)->cb.free = NAME_LC ## _iterator__free; \
24 } while (0)
25
26 #define ITERATOR_CASE_FLAGS \
27 (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
28
29 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
30 (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
31 (P)->base.cb = &(P)->cb; \
32 ITERATOR_SET_CB(P,NAME_LC); \
33 (P)->base.repo = (REPO); \
34 (P)->base.start = start ? git__strdup(start) : NULL; \
35 (P)->base.end = end ? git__strdup(end) : NULL; \
36 if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
37 git__free(P); return -1; } \
38 (P)->base.prefixcomp = git__prefixcmp; \
39 (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
40 if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
41 (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
42 } while (0)
43
44 #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
45 #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
46 #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
47 #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
48 #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
49
50 #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
51 #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
52
53 #define iterator__end(I) ((git_iterator *)(I))->end
54 #define iterator__past_end(I,PATH) \
55 (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
56
57
58 static int iterator__reset_range(
59 git_iterator *iter, const char *start, const char *end)
60 {
61 if (start) {
62 if (iter->start)
63 git__free(iter->start);
64 iter->start = git__strdup(start);
65 GITERR_CHECK_ALLOC(iter->start);
66 }
67
68 if (end) {
69 if (iter->end)
70 git__free(iter->end);
71 iter->end = git__strdup(end);
72 GITERR_CHECK_ALLOC(iter->end);
73 }
74
75 iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
76
77 return 0;
78 }
79
80 static int iterator__update_ignore_case(
81 git_iterator *iter,
82 git_iterator_flag_t flags)
83 {
84 int error = 0, ignore_case = -1;
85
86 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
87 ignore_case = true;
88 else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
89 ignore_case = false;
90 else {
91 git_index *index;
92
93 if (!(error = git_repository_index__weakptr(&index, iter->repo)))
94 ignore_case = (index->ignore_case != false);
95 }
96
97 if (ignore_case > 0)
98 iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
99 else if (ignore_case == 0)
100 iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
101
102 iter->prefixcomp = iterator__ignore_case(iter) ?
103 git__prefixcmp_icase : git__prefixcmp;
104
105 return error;
106 }
107
108 GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
109 {
110 if (entry) *entry = NULL;
111 }
112
113
114 static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
115 {
116 GIT_UNUSED(i);
117 iterator__clear_entry(e);
118 return GIT_ITEROVER;
119 }
120
121 static int empty_iterator__seek(git_iterator *i, const char *p)
122 {
123 GIT_UNUSED(i); GIT_UNUSED(p);
124 return -1;
125 }
126
127 static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
128 {
129 GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
130 return 0;
131 }
132
133 static int empty_iterator__at_end(git_iterator *i)
134 {
135 GIT_UNUSED(i);
136 return 1;
137 }
138
139 static void empty_iterator__free(git_iterator *i)
140 {
141 GIT_UNUSED(i);
142 }
143
144 typedef struct {
145 git_iterator base;
146 git_iterator_callbacks cb;
147 } empty_iterator;
148
149 int git_iterator_for_nothing(
150 git_iterator **iter,
151 git_iterator_flag_t flags,
152 const char *start,
153 const char *end)
154 {
155 empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
156 GITERR_CHECK_ALLOC(i);
157
158 #define empty_iterator__current empty_iterator__noop
159 #define empty_iterator__advance empty_iterator__noop
160 #define empty_iterator__advance_into empty_iterator__noop
161
162 ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
163
164 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
165 i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
166
167 *iter = (git_iterator *)i;
168 return 0;
169 }
170
171
172 typedef struct tree_iterator_entry tree_iterator_entry;
173 struct tree_iterator_entry {
174 tree_iterator_entry *parent;
175 const git_tree_entry *te;
176 git_tree *tree;
177 };
178
179 typedef struct tree_iterator_frame tree_iterator_frame;
180 struct tree_iterator_frame {
181 tree_iterator_frame *up, *down;
182
183 size_t n_entries; /* items in this frame */
184 size_t current; /* start of currently active range in frame */
185 size_t next; /* start of next range in frame */
186
187 const char *start;
188 size_t startlen;
189
190 tree_iterator_entry *entries[GIT_FLEX_ARRAY];
191 };
192
193 typedef struct {
194 git_iterator base;
195 git_iterator_callbacks cb;
196 tree_iterator_frame *head, *root;
197 git_pool pool;
198 git_index_entry entry;
199 git_buf path;
200 int path_ambiguities;
201 bool path_has_filename;
202 bool entry_is_current;
203 int (*strncomp)(const char *a, const char *b, size_t sz);
204 } tree_iterator;
205
206 static char *tree_iterator__current_filename(
207 tree_iterator *ti, const git_tree_entry *te)
208 {
209 if (!ti->path_has_filename) {
210 if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
211 return NULL;
212
213 if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
214 return NULL;
215
216 ti->path_has_filename = true;
217 }
218
219 return ti->path.ptr;
220 }
221
222 static void tree_iterator__rewrite_filename(tree_iterator *ti)
223 {
224 tree_iterator_entry *scan = ti->head->entries[ti->head->current];
225 ssize_t strpos = ti->path.size;
226 const git_tree_entry *te;
227
228 if (strpos && ti->path.ptr[strpos - 1] == '/')
229 strpos--;
230
231 for (; scan && (te = scan->te); scan = scan->parent) {
232 strpos -= te->filename_len;
233 memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
234 strpos -= 1; /* separator */
235 }
236 }
237
238 static int tree_iterator__te_cmp(
239 const git_tree_entry *a,
240 const git_tree_entry *b,
241 int (*compare)(const char *, const char *, size_t))
242 {
243 return git_path_cmp(
244 a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
245 b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
246 compare);
247 }
248
249 static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
250 {
251 const tree_iterator_entry *ae = a, *be = b;
252 int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
253
254 if (!cmp) {
255 /* stabilize sort order among equivalent names */
256 if (!ae->parent->te || !be->parent->te)
257 cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
258 else
259 cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
260 }
261
262 return cmp;
263 }
264
265 static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
266 {
267 const tree_iterator_frame *tf = key;
268 const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
269
270 return git_path_cmp(
271 tf->start, tf->startlen, false,
272 te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
273 ((tree_iterator *)p)->strncomp);
274 }
275
276 static bool tree_iterator__move_to_next(
277 tree_iterator *ti, tree_iterator_frame *tf)
278 {
279 if (tf->next > tf->current + 1)
280 ti->path_ambiguities--;
281
282 if (!tf->up) { /* at root */
283 tf->current = tf->next;
284 return false;
285 }
286
287 for (; tf->current < tf->next; tf->current++) {
288 git_tree_free(tf->entries[tf->current]->tree);
289 tf->entries[tf->current]->tree = NULL;
290 }
291
292 return (tf->current < tf->n_entries);
293 }
294
295 static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
296 {
297 int error = 0;
298 const git_tree_entry *te, *last = NULL;
299
300 tf->next = tf->current;
301
302 for (; tf->next < tf->n_entries; tf->next++, last = te) {
303 te = tf->entries[tf->next]->te;
304
305 if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
306 break;
307
308 /* try to load trees for items in [current,next) range */
309 if (!error && git_tree_entry__is_tree(te))
310 error = git_tree_lookup(
311 &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
312 }
313
314 if (tf->next > tf->current + 1)
315 ti->path_ambiguities++;
316
317 /* if a tree lookup failed, advance over this span and return failure */
318 if (error < 0) {
319 tree_iterator__move_to_next(ti, tf);
320 return error;
321 }
322
323 if (last && !tree_iterator__current_filename(ti, last))
324 return -1; /* must have been allocation failure */
325
326 return 0;
327 }
328
329 GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
330 {
331 return (ti->head->current < ti->head->n_entries &&
332 ti->head->entries[ti->head->current]->tree != NULL);
333 }
334
335 static int tree_iterator__push_frame(tree_iterator *ti)
336 {
337 int error = 0;
338 tree_iterator_frame *head = ti->head, *tf = NULL;
339 size_t i, n_entries = 0, alloclen;
340
341 if (head->current >= head->n_entries || !head->entries[head->current]->tree)
342 return GIT_ITEROVER;
343
344 for (i = head->current; i < head->next; ++i)
345 n_entries += git_tree_entrycount(head->entries[i]->tree);
346
347 GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, sizeof(tree_iterator_entry *), n_entries);
348 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, sizeof(tree_iterator_frame));
349
350 tf = git__calloc(1, alloclen);
351 GITERR_CHECK_ALLOC(tf);
352
353 tf->n_entries = n_entries;
354
355 tf->up = head;
356 head->down = tf;
357 ti->head = tf;
358
359 for (i = head->current, n_entries = 0; i < head->next; ++i) {
360 git_tree *tree = head->entries[i]->tree;
361 size_t j, max_j = git_tree_entrycount(tree);
362
363 for (j = 0; j < max_j; ++j) {
364 tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
365 GITERR_CHECK_ALLOC(entry);
366
367 entry->parent = head->entries[i];
368 entry->te = git_tree_entry_byindex(tree, j);
369 entry->tree = NULL;
370
371 tf->entries[n_entries++] = entry;
372 }
373 }
374
375 /* if ignore_case, sort entries case insensitively */
376 if (iterator__ignore_case(ti))
377 git__tsort_r(
378 (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
379
380 /* pick tf->current based on "start" (or start at zero) */
381 if (head->startlen > 0) {
382 git__bsearch_r((void **)tf->entries, tf->n_entries, head,
383 tree_iterator__search_cmp, ti, &tf->current);
384
385 while (tf->current &&
386 !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
387 tf->current--;
388
389 if ((tf->start = strchr(head->start, '/')) != NULL) {
390 tf->start++;
391 tf->startlen = strlen(tf->start);
392 }
393 }
394
395 ti->path_has_filename = ti->entry_is_current = false;
396
397 if ((error = tree_iterator__set_next(ti, tf)) < 0)
398 return error;
399
400 /* autoexpand as needed */
401 if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
402 return tree_iterator__push_frame(ti);
403
404 return 0;
405 }
406
407 static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
408 {
409 tree_iterator_frame *tf = ti->head;
410
411 if (!tf->up)
412 return false;
413
414 ti->head = tf->up;
415 ti->head->down = NULL;
416
417 tree_iterator__move_to_next(ti, tf);
418
419 if (!final) { /* if final, don't bother to clean up */
420 git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
421 git_buf_rtruncate_at_char(&ti->path, '/');
422 }
423
424 git__free(tf);
425
426 return true;
427 }
428
429 static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
430 {
431 while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
432
433 if (!final) {
434 ti->head->current = to_end ? ti->head->n_entries : 0;
435 ti->path_ambiguities = 0;
436 git_buf_clear(&ti->path);
437 }
438 }
439
440 static int tree_iterator__update_entry(tree_iterator *ti)
441 {
442 tree_iterator_frame *tf;
443 const git_tree_entry *te;
444
445 if (ti->entry_is_current)
446 return 0;
447
448 tf = ti->head;
449 te = tf->entries[tf->current]->te;
450
451 ti->entry.mode = te->attr;
452 git_oid_cpy(&ti->entry.id, &te->oid);
453
454 ti->entry.path = tree_iterator__current_filename(ti, te);
455 GITERR_CHECK_ALLOC(ti->entry.path);
456
457 if (ti->path_ambiguities > 0)
458 tree_iterator__rewrite_filename(ti);
459
460 if (iterator__past_end(ti, ti->entry.path)) {
461 tree_iterator__pop_all(ti, true, false);
462 return GIT_ITEROVER;
463 }
464
465 ti->entry_is_current = true;
466
467 return 0;
468 }
469
470 static int tree_iterator__current(
471 const git_index_entry **entry, git_iterator *self)
472 {
473 int error;
474 tree_iterator *ti = (tree_iterator *)self;
475 tree_iterator_frame *tf = ti->head;
476
477 iterator__clear_entry(entry);
478
479 if (tf->current >= tf->n_entries)
480 return GIT_ITEROVER;
481
482 if ((error = tree_iterator__update_entry(ti)) < 0)
483 return error;
484
485 if (entry)
486 *entry = &ti->entry;
487
488 ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
489
490 return 0;
491 }
492
493 static int tree_iterator__advance_into(
494 const git_index_entry **entry, git_iterator *self)
495 {
496 int error = 0;
497 tree_iterator *ti = (tree_iterator *)self;
498
499 iterator__clear_entry(entry);
500
501 if (tree_iterator__at_tree(ti))
502 error = tree_iterator__push_frame(ti);
503
504 if (!error && entry)
505 error = tree_iterator__current(entry, self);
506
507 return error;
508 }
509
510 static int tree_iterator__advance(
511 const git_index_entry **entry, git_iterator *self)
512 {
513 int error;
514 tree_iterator *ti = (tree_iterator *)self;
515 tree_iterator_frame *tf = ti->head;
516
517 iterator__clear_entry(entry);
518
519 if (tf->current >= tf->n_entries)
520 return GIT_ITEROVER;
521
522 if (!iterator__has_been_accessed(ti))
523 return tree_iterator__current(entry, self);
524
525 if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
526 tree_iterator__at_tree(ti))
527 return tree_iterator__advance_into(entry, self);
528
529 if (ti->path_has_filename) {
530 git_buf_rtruncate_at_char(&ti->path, '/');
531 ti->path_has_filename = ti->entry_is_current = false;
532 }
533
534 /* scan forward and up, advancing in frame or popping frame when done */
535 while (!tree_iterator__move_to_next(ti, tf) &&
536 tree_iterator__pop_frame(ti, false))
537 tf = ti->head;
538
539 /* find next and load trees */
540 if ((error = tree_iterator__set_next(ti, tf)) < 0)
541 return error;
542
543 /* deal with include_trees / auto_expand as needed */
544 if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
545 return tree_iterator__advance_into(entry, self);
546
547 return tree_iterator__current(entry, self);
548 }
549
550 static int tree_iterator__seek(git_iterator *self, const char *prefix)
551 {
552 GIT_UNUSED(self); GIT_UNUSED(prefix);
553 return -1;
554 }
555
556 static int tree_iterator__reset(
557 git_iterator *self, const char *start, const char *end)
558 {
559 tree_iterator *ti = (tree_iterator *)self;
560
561 tree_iterator__pop_all(ti, false, false);
562
563 if (iterator__reset_range(self, start, end) < 0)
564 return -1;
565
566 return tree_iterator__push_frame(ti); /* re-expand root tree */
567 }
568
569 static int tree_iterator__at_end(git_iterator *self)
570 {
571 tree_iterator *ti = (tree_iterator *)self;
572 return (ti->head->current >= ti->head->n_entries);
573 }
574
575 static void tree_iterator__free(git_iterator *self)
576 {
577 tree_iterator *ti = (tree_iterator *)self;
578
579 tree_iterator__pop_all(ti, true, false);
580
581 git_tree_free(ti->head->entries[0]->tree);
582 git__free(ti->head);
583 git_pool_clear(&ti->pool);
584 git_buf_free(&ti->path);
585 }
586
587 static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
588 {
589 size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
590 tree_iterator_frame *root = git__calloc(sz, sizeof(char));
591 GITERR_CHECK_ALLOC(root);
592
593 root->n_entries = 1;
594 root->next = 1;
595 root->start = ti->base.start;
596 root->startlen = root->start ? strlen(root->start) : 0;
597 root->entries[0] = git_pool_mallocz(&ti->pool, 1);
598 GITERR_CHECK_ALLOC(root->entries[0]);
599 root->entries[0]->tree = tree;
600
601 ti->head = ti->root = root;
602
603 return 0;
604 }
605
606 int git_iterator_for_tree(
607 git_iterator **iter,
608 git_tree *tree,
609 git_iterator_flag_t flags,
610 const char *start,
611 const char *end)
612 {
613 int error;
614 tree_iterator *ti;
615
616 if (tree == NULL)
617 return git_iterator_for_nothing(iter, flags, start, end);
618
619 if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
620 return error;
621
622 ti = git__calloc(1, sizeof(tree_iterator));
623 GITERR_CHECK_ALLOC(ti);
624
625 ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
626
627 if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
628 goto fail;
629 ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
630
631 if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
632 (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
633 (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
634 goto fail;
635
636 *iter = (git_iterator *)ti;
637 return 0;
638
639 fail:
640 git_iterator_free((git_iterator *)ti);
641 return error;
642 }
643
644
645 typedef struct {
646 git_iterator base;
647 git_iterator_callbacks cb;
648 git_index *index;
649 git_vector entries;
650 git_vector_cmp entry_srch;
651 size_t current;
652 /* when not in autoexpand mode, use these to represent "tree" state */
653 git_buf partial;
654 size_t partial_pos;
655 char restore_terminator;
656 git_index_entry tree_entry;
657 } index_iterator;
658
659 static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
660 {
661 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
662
663 if (ie != NULL && iterator__past_end(ii, ie->path)) {
664 ii->current = git_vector_length(&ii->entries);
665 ie = NULL;
666 }
667
668 return ie;
669 }
670
671 static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
672 {
673 const git_index_entry *ie;
674
675 while ((ie = index_iterator__index_entry(ii)) != NULL &&
676 git_index_entry_stage(ie) != 0)
677 ii->current++;
678
679 return ie;
680 }
681
682 static void index_iterator__next_prefix_tree(index_iterator *ii)
683 {
684 const char *slash;
685
686 if (!iterator__include_trees(ii))
687 return;
688
689 slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
690
691 if (slash != NULL) {
692 ii->partial_pos = (slash - ii->partial.ptr) + 1;
693 ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
694 ii->partial.ptr[ii->partial_pos] = '\0';
695 } else {
696 ii->partial_pos = ii->partial.size;
697 }
698
699 if (index_iterator__index_entry(ii) == NULL)
700 ii->partial_pos = ii->partial.size;
701 }
702
703 static int index_iterator__first_prefix_tree(index_iterator *ii)
704 {
705 const git_index_entry *ie = index_iterator__skip_conflicts(ii);
706 const char *scan, *prior, *slash;
707
708 if (!ie || !iterator__include_trees(ii))
709 return 0;
710
711 /* find longest common prefix with prior index entry */
712 for (scan = slash = ie->path, prior = ii->partial.ptr;
713 *scan && *scan == *prior; ++scan, ++prior)
714 if (*scan == '/')
715 slash = scan;
716
717 if (git_buf_sets(&ii->partial, ie->path) < 0)
718 return -1;
719
720 ii->partial_pos = (slash - ie->path) + 1;
721 index_iterator__next_prefix_tree(ii);
722
723 return 0;
724 }
725
726 #define index_iterator__at_tree(I) \
727 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
728
729 static int index_iterator__current(
730 const git_index_entry **entry, git_iterator *self)
731 {
732 index_iterator *ii = (index_iterator *)self;
733 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
734
735 if (ie != NULL && index_iterator__at_tree(ii)) {
736 ii->tree_entry.path = ii->partial.ptr;
737 ie = &ii->tree_entry;
738 }
739
740 if (entry)
741 *entry = ie;
742
743 ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
744
745 return (ie != NULL) ? 0 : GIT_ITEROVER;
746 }
747
748 static int index_iterator__at_end(git_iterator *self)
749 {
750 index_iterator *ii = (index_iterator *)self;
751 return (ii->current >= git_vector_length(&ii->entries));
752 }
753
754 static int index_iterator__advance(
755 const git_index_entry **entry, git_iterator *self)
756 {
757 index_iterator *ii = (index_iterator *)self;
758 size_t entrycount = git_vector_length(&ii->entries);
759 const git_index_entry *ie;
760
761 if (!iterator__has_been_accessed(ii))
762 return index_iterator__current(entry, self);
763
764 if (index_iterator__at_tree(ii)) {
765 if (iterator__do_autoexpand(ii)) {
766 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
767 index_iterator__next_prefix_tree(ii);
768 } else {
769 /* advance to sibling tree (i.e. find entry with new prefix) */
770 while (ii->current < entrycount) {
771 ii->current++;
772
773 if (!(ie = git_vector_get(&ii->entries, ii->current)) ||
774 ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
775 break;
776 }
777
778 if (index_iterator__first_prefix_tree(ii) < 0)
779 return -1;
780 }
781 } else {
782 if (ii->current < entrycount)
783 ii->current++;
784
785 if (index_iterator__first_prefix_tree(ii) < 0)
786 return -1;
787 }
788
789 return index_iterator__current(entry, self);
790 }
791
792 static int index_iterator__advance_into(
793 const git_index_entry **entry, git_iterator *self)
794 {
795 index_iterator *ii = (index_iterator *)self;
796 const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
797
798 if (ie != NULL && index_iterator__at_tree(ii)) {
799 if (ii->restore_terminator)
800 ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
801 index_iterator__next_prefix_tree(ii);
802 }
803
804 return index_iterator__current(entry, self);
805 }
806
807 static int index_iterator__seek(git_iterator *self, const char *prefix)
808 {
809 GIT_UNUSED(self); GIT_UNUSED(prefix);
810 return -1;
811 }
812
813 static int index_iterator__reset(
814 git_iterator *self, const char *start, const char *end)
815 {
816 index_iterator *ii = (index_iterator *)self;
817 const git_index_entry *ie;
818
819 if (iterator__reset_range(self, start, end) < 0)
820 return -1;
821
822 ii->current = 0;
823
824 if (ii->base.start)
825 git_index_snapshot_find(
826 &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
827
828 if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
829 return 0;
830
831 if (git_buf_sets(&ii->partial, ie->path) < 0)
832 return -1;
833
834 ii->partial_pos = 0;
835
836 if (ii->base.start) {
837 size_t startlen = strlen(ii->base.start);
838
839 ii->partial_pos = (startlen > ii->partial.size) ?
840 ii->partial.size : startlen;
841 }
842
843 index_iterator__next_prefix_tree(ii);
844
845 return 0;
846 }
847
848 static void index_iterator__free(git_iterator *self)
849 {
850 index_iterator *ii = (index_iterator *)self;
851 git_index_snapshot_release(&ii->entries, ii->index);
852 ii->index = NULL;
853 git_buf_free(&ii->partial);
854 }
855
856 int git_iterator_for_index(
857 git_iterator **iter,
858 git_index *index,
859 git_iterator_flag_t flags,
860 const char *start,
861 const char *end)
862 {
863 int error = 0;
864 index_iterator *ii = git__calloc(1, sizeof(index_iterator));
865 GITERR_CHECK_ALLOC(ii);
866
867 if ((error = git_index_snapshot_new(&ii->entries, index)) < 0) {
868 git__free(ii);
869 return error;
870 }
871 ii->index = index;
872
873 ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
874
875 if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) {
876 git_iterator_free((git_iterator *)ii);
877 return error;
878 }
879
880 ii->entry_srch = iterator__ignore_case(ii) ?
881 git_index_entry_isrch : git_index_entry_srch;
882
883 git_vector_set_cmp(&ii->entries, iterator__ignore_case(ii) ?
884 git_index_entry_icmp : git_index_entry_cmp);
885 git_vector_sort(&ii->entries);
886
887 git_buf_init(&ii->partial, 0);
888 ii->tree_entry.mode = GIT_FILEMODE_TREE;
889
890 index_iterator__reset((git_iterator *)ii, NULL, NULL);
891
892 *iter = (git_iterator *)ii;
893 return 0;
894 }
895
896
897 typedef struct fs_iterator_frame fs_iterator_frame;
898 struct fs_iterator_frame {
899 fs_iterator_frame *next;
900 git_vector entries;
901 size_t index;
902 int is_ignored;
903 };
904
905 typedef struct fs_iterator fs_iterator;
906 struct fs_iterator {
907 git_iterator base;
908 git_iterator_callbacks cb;
909 fs_iterator_frame *stack;
910 git_index_entry entry;
911 git_buf path;
912 size_t root_len;
913 uint32_t dirload_flags;
914 int depth;
915
916 int (*enter_dir_cb)(fs_iterator *self);
917 int (*leave_dir_cb)(fs_iterator *self);
918 int (*update_entry_cb)(fs_iterator *self);
919 };
920
921 #define FS_MAX_DEPTH 100
922
923 static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
924 {
925 fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
926 git_vector_cmp entry_compare = CASESELECT(
927 iterator__ignore_case(fi),
928 git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
929
930 if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
931 git__free(ff);
932 ff = NULL;
933 }
934
935 return ff;
936 }
937
938 static void fs_iterator__free_frame(fs_iterator_frame *ff)
939 {
940 git_vector_free_deep(&ff->entries);
941 git__free(ff);
942 }
943
944 static void fs_iterator__pop_frame(
945 fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
946 {
947 if (fi && fi->stack == ff) {
948 if (!ff->next && !pop_last) {
949 memset(&fi->entry, 0, sizeof(fi->entry));
950 return;
951 }
952
953 if (fi->leave_dir_cb)
954 (void)fi->leave_dir_cb(fi);
955
956 fi->stack = ff->next;
957 fi->depth--;
958 }
959
960 fs_iterator__free_frame(ff);
961 }
962
963 static int fs_iterator__update_entry(fs_iterator *fi);
964 static int fs_iterator__advance_over(
965 const git_index_entry **entry, git_iterator *self);
966
967 static int fs_iterator__entry_cmp(const void *i, const void *item)
968 {
969 const fs_iterator *fi = (const fs_iterator *)i;
970 const git_path_with_stat *ps = item;
971 return fi->base.prefixcomp(fi->base.start, ps->path);
972 }
973
974 static void fs_iterator__seek_frame_start(
975 fs_iterator *fi, fs_iterator_frame *ff)
976 {
977 if (!ff)
978 return;
979
980 if (fi->base.start)
981 git_vector_bsearch2(
982 &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
983 else
984 ff->index = 0;
985 }
986
987 static int fs_iterator__expand_dir(fs_iterator *fi)
988 {
989 int error;
990 fs_iterator_frame *ff;
991
992 if (fi->depth > FS_MAX_DEPTH) {
993 giterr_set(GITERR_REPOSITORY,
994 "Directory nesting is too deep (%d)", fi->depth);
995 return -1;
996 }
997
998 ff = fs_iterator__alloc_frame(fi);
999 GITERR_CHECK_ALLOC(ff);
1000
1001 error = git_path_dirload_with_stat(
1002 fi->path.ptr, fi->root_len, fi->dirload_flags,
1003 fi->base.start, fi->base.end, &ff->entries);
1004
1005 if (error < 0) {
1006 git_error_state last_error = { 0 };
1007 giterr_capture(&last_error, error);
1008
1009 /* these callbacks may clear the error message */
1010 fs_iterator__free_frame(ff);
1011 fs_iterator__advance_over(NULL, (git_iterator *)fi);
1012 /* next time return value we skipped to */
1013 fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1014
1015 return giterr_restore(&last_error);
1016 }
1017
1018 if (ff->entries.length == 0) {
1019 fs_iterator__free_frame(ff);
1020 return GIT_ENOTFOUND;
1021 }
1022 fi->base.stat_calls += ff->entries.length;
1023
1024 fs_iterator__seek_frame_start(fi, ff);
1025
1026 ff->next = fi->stack;
1027 fi->stack = ff;
1028 fi->depth++;
1029
1030 if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1031 return error;
1032
1033 return fs_iterator__update_entry(fi);
1034 }
1035
1036 static int fs_iterator__current(
1037 const git_index_entry **entry, git_iterator *self)
1038 {
1039 fs_iterator *fi = (fs_iterator *)self;
1040 const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1041
1042 if (entry)
1043 *entry = fe;
1044
1045 fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1046
1047 return (fe != NULL) ? 0 : GIT_ITEROVER;
1048 }
1049
1050 static int fs_iterator__at_end(git_iterator *self)
1051 {
1052 return (((fs_iterator *)self)->entry.path == NULL);
1053 }
1054
1055 static int fs_iterator__advance_into(
1056 const git_index_entry **entry, git_iterator *iter)
1057 {
1058 int error = 0;
1059 fs_iterator *fi = (fs_iterator *)iter;
1060
1061 iterator__clear_entry(entry);
1062
1063 /* Allow you to explicitly advance into a commit/submodule (as well as a
1064 * tree) to avoid cases where an entry is mislabeled as a submodule in
1065 * the working directory. The fs iterator will never have COMMMIT
1066 * entries on it's own, but a wrapper might add them.
1067 */
1068 if (fi->entry.path != NULL &&
1069 (fi->entry.mode == GIT_FILEMODE_TREE ||
1070 fi->entry.mode == GIT_FILEMODE_COMMIT))
1071 /* returns GIT_ENOTFOUND if the directory is empty */
1072 error = fs_iterator__expand_dir(fi);
1073
1074 if (!error && entry)
1075 error = fs_iterator__current(entry, iter);
1076
1077 if (!error && !fi->entry.path)
1078 error = GIT_ITEROVER;
1079
1080 return error;
1081 }
1082
1083 static int fs_iterator__advance_over(
1084 const git_index_entry **entry, git_iterator *self)
1085 {
1086 int error = 0;
1087 fs_iterator *fi = (fs_iterator *)self;
1088 fs_iterator_frame *ff;
1089 git_path_with_stat *next;
1090
1091 if (entry != NULL)
1092 *entry = NULL;
1093
1094 while (fi->entry.path != NULL) {
1095 ff = fi->stack;
1096 next = git_vector_get(&ff->entries, ++ff->index);
1097
1098 if (next != NULL)
1099 break;
1100
1101 fs_iterator__pop_frame(fi, ff, false);
1102 }
1103
1104 error = fs_iterator__update_entry(fi);
1105
1106 if (!error && entry != NULL)
1107 error = fs_iterator__current(entry, self);
1108
1109 return error;
1110 }
1111
1112 static int fs_iterator__advance(
1113 const git_index_entry **entry, git_iterator *self)
1114 {
1115 fs_iterator *fi = (fs_iterator *)self;
1116
1117 if (!iterator__has_been_accessed(fi))
1118 return fs_iterator__current(entry, self);
1119
1120 /* given include_trees & autoexpand, we might have to go into a tree */
1121 if (iterator__do_autoexpand(fi) &&
1122 fi->entry.path != NULL &&
1123 fi->entry.mode == GIT_FILEMODE_TREE)
1124 {
1125 int error = fs_iterator__advance_into(entry, self);
1126 if (error != GIT_ENOTFOUND)
1127 return error;
1128 /* continue silently past empty directories if autoexpanding */
1129 giterr_clear();
1130 }
1131
1132 return fs_iterator__advance_over(entry, self);
1133 }
1134
1135 static int fs_iterator__seek(git_iterator *self, const char *prefix)
1136 {
1137 GIT_UNUSED(self);
1138 GIT_UNUSED(prefix);
1139 /* pop stack until matching prefix */
1140 /* find prefix item in current frame */
1141 /* push subdirectories as deep as possible while matching */
1142 return 0;
1143 }
1144
1145 static int fs_iterator__reset(
1146 git_iterator *self, const char *start, const char *end)
1147 {
1148 int error;
1149 fs_iterator *fi = (fs_iterator *)self;
1150
1151 while (fi->stack != NULL && fi->stack->next != NULL)
1152 fs_iterator__pop_frame(fi, fi->stack, false);
1153 fi->depth = 0;
1154
1155 if ((error = iterator__reset_range(self, start, end)) < 0)
1156 return error;
1157
1158 fs_iterator__seek_frame_start(fi, fi->stack);
1159
1160 error = fs_iterator__update_entry(fi);
1161 if (error == GIT_ITEROVER)
1162 error = 0;
1163
1164 return error;
1165 }
1166
1167 static void fs_iterator__free(git_iterator *self)
1168 {
1169 fs_iterator *fi = (fs_iterator *)self;
1170
1171 while (fi->stack != NULL)
1172 fs_iterator__pop_frame(fi, fi->stack, true);
1173
1174 git_buf_free(&fi->path);
1175 }
1176
1177 static int fs_iterator__update_entry(fs_iterator *fi)
1178 {
1179 git_path_with_stat *ps;
1180
1181 memset(&fi->entry, 0, sizeof(fi->entry));
1182
1183 if (!fi->stack)
1184 return GIT_ITEROVER;
1185
1186 ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1187 if (!ps)
1188 return GIT_ITEROVER;
1189
1190 git_buf_truncate(&fi->path, fi->root_len);
1191 if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1192 return -1;
1193
1194 if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1195 return GIT_ITEROVER;
1196
1197 fi->entry.path = ps->path;
1198 git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
1199
1200 /* need different mode here to keep directories during iteration */
1201 fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1202
1203 /* allow wrapper to check/update the entry (can force skip) */
1204 if (fi->update_entry_cb &&
1205 fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1206 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1207
1208 /* if this is a tree and trees aren't included, then skip */
1209 if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1210 int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1211 if (error != GIT_ENOTFOUND)
1212 return error;
1213 giterr_clear();
1214 return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1215 }
1216
1217 return 0;
1218 }
1219
1220 static int fs_iterator__initialize(
1221 git_iterator **out, fs_iterator *fi, const char *root)
1222 {
1223 int error;
1224
1225 if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1226 git__free(fi);
1227 return -1;
1228 }
1229 fi->root_len = fi->path.size;
1230
1231 fi->dirload_flags =
1232 (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1233 (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
1234 GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1235
1236 if ((error = fs_iterator__expand_dir(fi)) < 0) {
1237 if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1238 giterr_clear();
1239 error = 0;
1240 } else {
1241 git_iterator_free((git_iterator *)fi);
1242 fi = NULL;
1243 }
1244 }
1245
1246 *out = (git_iterator *)fi;
1247 return error;
1248 }
1249
1250 int git_iterator_for_filesystem(
1251 git_iterator **out,
1252 const char *root,
1253 git_iterator_flag_t flags,
1254 const char *start,
1255 const char *end)
1256 {
1257 fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1258 GITERR_CHECK_ALLOC(fi);
1259
1260 ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1261
1262 if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1263 fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1264
1265 return fs_iterator__initialize(out, fi, root);
1266 }
1267
1268
1269 typedef struct {
1270 fs_iterator fi;
1271 git_ignores ignores;
1272 int is_ignored;
1273
1274 /*
1275 * We may have a tree or the index+snapshot to compare against
1276 * when checking for submodules.
1277 */
1278 git_tree *tree;
1279 git_index *index;
1280 git_vector index_snapshot;
1281 git_vector_cmp entry_srch;
1282
1283 } workdir_iterator;
1284
1285 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1286 {
1287 size_t len;
1288
1289 if (!path || (len = path->size) < 4)
1290 return false;
1291
1292 if (path->ptr[len - 1] == '/')
1293 len--;
1294
1295 if (tolower(path->ptr[len - 1]) != 't' ||
1296 tolower(path->ptr[len - 2]) != 'i' ||
1297 tolower(path->ptr[len - 3]) != 'g' ||
1298 tolower(path->ptr[len - 4]) != '.')
1299 return false;
1300
1301 return (len == 4 || path->ptr[len - 5] == '/');
1302 }
1303
1304 /**
1305 * Figure out if an entry is a submodule.
1306 *
1307 * We consider it a submodule if the path is listed as a submodule in
1308 * either the tree or the index.
1309 */
1310 static int is_submodule(workdir_iterator *wi, git_path_with_stat *ie)
1311 {
1312 int error, is_submodule = 0;
1313
1314 if (wi->tree) {
1315 git_tree_entry *e;
1316
1317 /* remove the trailing slash for finding */
1318 ie->path[ie->path_len-1] = '\0';
1319 error = git_tree_entry_bypath(&e, wi->tree, ie->path);
1320 ie->path[ie->path_len-1] = '/';
1321 if (error < 0 && error != GIT_ENOTFOUND)
1322 return 0;
1323 if (!error) {
1324 is_submodule = e->attr == GIT_FILEMODE_COMMIT;
1325 git_tree_entry_free(e);
1326 }
1327 }
1328
1329 if (!is_submodule && wi->index) {
1330 git_index_entry *e;
1331 size_t pos;
1332
1333 error = git_index_snapshot_find(&pos, &wi->index_snapshot, wi->entry_srch, ie->path, ie->path_len-1, 0);
1334 if (error < 0 && error != GIT_ENOTFOUND)
1335 return 0;
1336
1337 if (!error) {
1338 e = git_vector_get(&wi->index_snapshot, pos);
1339
1340 is_submodule = e->mode == GIT_FILEMODE_COMMIT;
1341 }
1342 }
1343
1344 return is_submodule;
1345 }
1346
1347 static int workdir_iterator__enter_dir(fs_iterator *fi)
1348 {
1349 workdir_iterator *wi = (workdir_iterator *)fi;
1350 fs_iterator_frame *ff = fi->stack;
1351 size_t pos;
1352 git_path_with_stat *entry;
1353 bool found_submodules = false;
1354
1355 /* check if this directory is ignored */
1356 if (git_ignore__lookup(
1357 &ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len) < 0) {
1358 giterr_clear();
1359 ff->is_ignored = GIT_IGNORE_NOTFOUND;
1360 }
1361
1362 /* if this is not the top level directory... */
1363 if (ff->next != NULL) {
1364 ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1365
1366 /* inherit ignored from parent if no rule specified */
1367 if (ff->is_ignored <= GIT_IGNORE_NOTFOUND)
1368 ff->is_ignored = ff->next->is_ignored;
1369
1370 /* push new ignores for files in this directory */
1371 (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1372 }
1373
1374 /* convert submodules to GITLINK and remove trailing slashes */
1375 git_vector_foreach(&ff->entries, pos, entry) {
1376 if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
1377 continue;
1378
1379 if (is_submodule(wi, entry)) {
1380 entry->st.st_mode = GIT_FILEMODE_COMMIT;
1381 entry->path_len--;
1382 entry->path[entry->path_len] = '\0';
1383 found_submodules = true;
1384 }
1385 }
1386
1387 /* if we renamed submodules, re-sort and re-seek to start */
1388 if (found_submodules) {
1389 git_vector_set_sorted(&ff->entries, 0);
1390 git_vector_sort(&ff->entries);
1391 fs_iterator__seek_frame_start(fi, ff);
1392 }
1393
1394 return 0;
1395 }
1396
1397 static int workdir_iterator__leave_dir(fs_iterator *fi)
1398 {
1399 workdir_iterator *wi = (workdir_iterator *)fi;
1400 git_ignore__pop_dir(&wi->ignores);
1401 return 0;
1402 }
1403
1404 static int workdir_iterator__update_entry(fs_iterator *fi)
1405 {
1406 workdir_iterator *wi = (workdir_iterator *)fi;
1407
1408 /* skip over .git entries */
1409 if (workdir_path_is_dotgit(&fi->path))
1410 return GIT_ENOTFOUND;
1411
1412 /* reset is_ignored since we haven't checked yet */
1413 wi->is_ignored = GIT_IGNORE_UNCHECKED;
1414
1415 return 0;
1416 }
1417
1418 static void workdir_iterator__free(git_iterator *self)
1419 {
1420 workdir_iterator *wi = (workdir_iterator *)self;
1421 if (wi->index)
1422 git_index_snapshot_release(&wi->index_snapshot, wi->index);
1423 git_tree_free(wi->tree);
1424 fs_iterator__free(self);
1425 git_ignore__free(&wi->ignores);
1426 }
1427
1428 int git_iterator_for_workdir_ext(
1429 git_iterator **out,
1430 git_repository *repo,
1431 const char *repo_workdir,
1432 git_index *index,
1433 git_tree *tree,
1434 git_iterator_flag_t flags,
1435 const char *start,
1436 const char *end)
1437 {
1438 int error, precompose = 0;
1439 workdir_iterator *wi;
1440
1441 if (!repo_workdir) {
1442 if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1443 return GIT_EBAREREPO;
1444 repo_workdir = git_repository_workdir(repo);
1445 }
1446
1447 /* initialize as an fs iterator then do overrides */
1448 wi = git__calloc(1, sizeof(workdir_iterator));
1449 GITERR_CHECK_ALLOC(wi);
1450 ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1451
1452 wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1453 wi->fi.cb.free = workdir_iterator__free;
1454 wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1455 wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1456 wi->fi.update_entry_cb = workdir_iterator__update_entry;
1457
1458 if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1459 (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
1460 {
1461 git_iterator_free((git_iterator *)wi);
1462 return error;
1463 }
1464
1465 if (tree && (error = git_object_dup((git_object **)&wi->tree, (git_object *)tree)) < 0)
1466 return error;
1467
1468 wi->index = index;
1469 if (index && (error = git_index_snapshot_new(&wi->index_snapshot, index)) < 0) {
1470 git_iterator_free((git_iterator *)wi);
1471 return error;
1472 }
1473 wi->entry_srch = iterator__ignore_case(wi) ?
1474 git_index_entry_isrch : git_index_entry_srch;
1475
1476
1477 /* try to look up precompose and set flag if appropriate */
1478 if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
1479 giterr_clear();
1480 else if (precompose)
1481 wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
1482
1483 return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1484 }
1485
1486
1487 void git_iterator_free(git_iterator *iter)
1488 {
1489 if (iter == NULL)
1490 return;
1491
1492 iter->cb->free(iter);
1493
1494 git__free(iter->start);
1495 git__free(iter->end);
1496
1497 memset(iter, 0, sizeof(*iter));
1498
1499 git__free(iter);
1500 }
1501
1502 int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1503 {
1504 bool desire_ignore_case = (ignore_case != 0);
1505
1506 if (iterator__ignore_case(iter) == desire_ignore_case)
1507 return 0;
1508
1509 if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1510 if (desire_ignore_case)
1511 iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1512 else
1513 iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1514 } else {
1515 giterr_set(GITERR_INVALID,
1516 "Cannot currently set ignore case on non-empty iterators");
1517 return -1;
1518 }
1519
1520 return 0;
1521 }
1522
1523 git_index *git_iterator_get_index(git_iterator *iter)
1524 {
1525 if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1526 return ((index_iterator *)iter)->index;
1527 return NULL;
1528 }
1529
1530 int git_iterator_current_tree_entry(
1531 const git_tree_entry **tree_entry, git_iterator *iter)
1532 {
1533 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1534 *tree_entry = NULL;
1535 else {
1536 tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1537 *tree_entry = (tf->current < tf->n_entries) ?
1538 tf->entries[tf->current]->te : NULL;
1539 }
1540
1541 return 0;
1542 }
1543
1544 int git_iterator_current_parent_tree(
1545 const git_tree **tree_ptr,
1546 git_iterator *iter,
1547 const char *parent_path)
1548 {
1549 tree_iterator *ti = (tree_iterator *)iter;
1550 tree_iterator_frame *tf;
1551 const char *scan = parent_path;
1552 const git_tree_entry *te;
1553
1554 *tree_ptr = NULL;
1555
1556 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1557 return 0;
1558
1559 for (tf = ti->root; *scan; ) {
1560 if (!(tf = tf->down) ||
1561 tf->current >= tf->n_entries ||
1562 !(te = tf->entries[tf->current]->te) ||
1563 ti->strncomp(scan, te->filename, te->filename_len) != 0)
1564 return 0;
1565
1566 scan += te->filename_len;
1567 if (*scan == '/')
1568 scan++;
1569 }
1570
1571 *tree_ptr = tf->entries[tf->current]->tree;
1572 return 0;
1573 }
1574
1575 static void workdir_iterator_update_is_ignored(workdir_iterator *wi)
1576 {
1577 if (git_ignore__lookup(
1578 &wi->is_ignored, &wi->ignores, wi->fi.entry.path) < 0) {
1579 giterr_clear();
1580 wi->is_ignored = GIT_IGNORE_NOTFOUND;
1581 }
1582
1583 /* use ignore from containing frame stack */
1584 if (wi->is_ignored <= GIT_IGNORE_NOTFOUND)
1585 wi->is_ignored = wi->fi.stack->is_ignored;
1586 }
1587
1588 bool git_iterator_current_is_ignored(git_iterator *iter)
1589 {
1590 workdir_iterator *wi = (workdir_iterator *)iter;
1591
1592 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1593 return false;
1594
1595 if (wi->is_ignored != GIT_IGNORE_UNCHECKED)
1596 return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
1597
1598 workdir_iterator_update_is_ignored(wi);
1599
1600 return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
1601 }
1602
1603 bool git_iterator_current_tree_is_ignored(git_iterator *iter)
1604 {
1605 workdir_iterator *wi = (workdir_iterator *)iter;
1606
1607 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1608 return false;
1609
1610 return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
1611 }
1612
1613 int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
1614 {
1615 const git_index_entry *entry;
1616
1617 /* a "done" iterator is after every prefix */
1618 if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1619 return 1;
1620
1621 /* a NULL prefix is after any valid iterator */
1622 if (!path_prefix)
1623 return -1;
1624
1625 return iter->prefixcomp(entry->path, path_prefix);
1626 }
1627
1628 int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1629 {
1630 workdir_iterator *wi = (workdir_iterator *)iter;
1631
1632 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1633 *path = NULL;
1634 else
1635 *path = &wi->fi.path;
1636
1637 return 0;
1638 }
1639
1640 int git_iterator_advance_over_with_status(
1641 const git_index_entry **entryptr,
1642 git_iterator_status_t *status,
1643 git_iterator *iter)
1644 {
1645 int error = 0;
1646 workdir_iterator *wi = (workdir_iterator *)iter;
1647 char *base = NULL;
1648 const git_index_entry *entry;
1649
1650 *status = GIT_ITERATOR_STATUS_NORMAL;
1651
1652 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1653 return git_iterator_advance(entryptr, iter);
1654 if ((error = git_iterator_current(&entry, iter)) < 0)
1655 return error;
1656
1657 if (!S_ISDIR(entry->mode)) {
1658 workdir_iterator_update_is_ignored(wi);
1659 if (wi->is_ignored == GIT_IGNORE_TRUE)
1660 *status = GIT_ITERATOR_STATUS_IGNORED;
1661 return git_iterator_advance(entryptr, iter);
1662 }
1663
1664 *status = GIT_ITERATOR_STATUS_EMPTY;
1665
1666 base = git__strdup(entry->path);
1667 GITERR_CHECK_ALLOC(base);
1668
1669 /* scan inside directory looking for a non-ignored item */
1670 while (entry && !iter->prefixcomp(entry->path, base)) {
1671 workdir_iterator_update_is_ignored(wi);
1672
1673 /* if we found an explicitly ignored item, then update from
1674 * EMPTY to IGNORED
1675 */
1676 if (wi->is_ignored == GIT_IGNORE_TRUE)
1677 *status = GIT_ITERATOR_STATUS_IGNORED;
1678 else if (S_ISDIR(entry->mode)) {
1679 error = git_iterator_advance_into(&entry, iter);
1680
1681 if (!error)
1682 continue;
1683 else if (error == GIT_ENOTFOUND) {
1684 error = 0;
1685 wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
1686 } else
1687 break; /* real error, stop here */
1688 } else {
1689 /* we found a non-ignored item, treat parent as untracked */
1690 *status = GIT_ITERATOR_STATUS_NORMAL;
1691 break;
1692 }
1693
1694 if ((error = git_iterator_advance(&entry, iter)) < 0)
1695 break;
1696 }
1697
1698 /* wrap up scan back to base directory */
1699 while (entry && !iter->prefixcomp(entry->path, base))
1700 if ((error = git_iterator_advance(&entry, iter)) < 0)
1701 break;
1702
1703 *entryptr = entry;
1704 git__free(base);
1705
1706 return error;
1707 }
1708