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