]> git.proxmox.com Git - libgit2.git/blob - src/iterator.c
Three submodule status bug fixes
[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_tree__dup(&tree, 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 #define WORKDIR_MAX_DEPTH 100
837
838 typedef struct workdir_iterator_frame workdir_iterator_frame;
839 struct workdir_iterator_frame {
840 workdir_iterator_frame *next;
841 git_vector entries;
842 size_t index;
843 };
844
845 typedef struct {
846 git_iterator base;
847 git_iterator_callbacks cb;
848 workdir_iterator_frame *stack;
849 git_ignores ignores;
850 git_index_entry entry;
851 git_buf path;
852 size_t root_len;
853 int is_ignored;
854 int depth;
855 } workdir_iterator;
856
857 GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
858 {
859 if (!ps)
860 return false;
861 else {
862 const char *path = ps->path;
863 size_t len = ps->path_len;
864
865 if (len < 4)
866 return false;
867 if (path[len - 1] == '/')
868 len--;
869 if (tolower(path[len - 1]) != 't' ||
870 tolower(path[len - 2]) != 'i' ||
871 tolower(path[len - 3]) != 'g' ||
872 tolower(path[len - 4]) != '.')
873 return false;
874 return (len == 4 || path[len - 5] == '/');
875 }
876 }
877
878 static workdir_iterator_frame *workdir_iterator__alloc_frame(
879 workdir_iterator *wi)
880 {
881 workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
882 git_vector_cmp entry_compare = CASESELECT(
883 iterator__ignore_case(wi),
884 git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
885
886 if (wf == NULL)
887 return NULL;
888
889 if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
890 git__free(wf);
891 return NULL;
892 }
893
894 return wf;
895 }
896
897 static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
898 {
899 unsigned int i;
900 git_path_with_stat *path;
901
902 git_vector_foreach(&wf->entries, i, path)
903 git__free(path);
904 git_vector_free(&wf->entries);
905 git__free(wf);
906 }
907
908 static int workdir_iterator__update_entry(workdir_iterator *wi);
909
910 static int workdir_iterator__entry_cmp(const void *i, const void *item)
911 {
912 const workdir_iterator *wi = (const workdir_iterator *)i;
913 const git_path_with_stat *ps = item;
914 return wi->base.prefixcomp(wi->base.start, ps->path);
915 }
916
917 static void workdir_iterator__seek_frame_start(
918 workdir_iterator *wi, workdir_iterator_frame *wf)
919 {
920 if (!wf)
921 return;
922
923 if (wi->base.start)
924 git_vector_bsearch2(
925 &wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
926 else
927 wf->index = 0;
928
929 if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
930 wf->index++;
931 }
932
933 static int workdir_iterator__expand_dir(workdir_iterator *wi)
934 {
935 int error;
936 workdir_iterator_frame *wf;
937
938 wf = workdir_iterator__alloc_frame(wi);
939 GITERR_CHECK_ALLOC(wf);
940
941 error = git_path_dirload_with_stat(
942 wi->path.ptr, wi->root_len, iterator__ignore_case(wi),
943 wi->base.start, wi->base.end, &wf->entries);
944
945 if (error < 0 || wf->entries.length == 0) {
946 workdir_iterator__free_frame(wf);
947 return GIT_ENOTFOUND;
948 }
949
950 if (++(wi->depth) > WORKDIR_MAX_DEPTH) {
951 giterr_set(GITERR_REPOSITORY,
952 "Working directory is too deep (%d)", wi->depth);
953 workdir_iterator__free_frame(wf);
954 return -1;
955 }
956
957 workdir_iterator__seek_frame_start(wi, wf);
958
959 /* only push new ignores if this is not top level directory */
960 if (wi->stack != NULL) {
961 ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
962 (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
963 }
964
965 wf->next = wi->stack;
966 wi->stack = wf;
967
968 return workdir_iterator__update_entry(wi);
969 }
970
971 static int workdir_iterator__current(
972 const git_index_entry **entry, git_iterator *self)
973 {
974 workdir_iterator *wi = (workdir_iterator *)self;
975 if (entry)
976 *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
977 return 0;
978 }
979
980 static int workdir_iterator__at_end(git_iterator *self)
981 {
982 return (((workdir_iterator *)self)->entry.path == NULL);
983 }
984
985 static int workdir_iterator__advance_into(
986 const git_index_entry **entry, git_iterator *iter)
987 {
988 int error = 0;
989 workdir_iterator *wi = (workdir_iterator *)iter;
990
991 iterator__clear_entry(entry);
992
993 /* workdir iterator will allow you to explicitly advance into a
994 * commit/submodule (as well as a tree) to avoid some cases where an
995 * entry is mislabeled as a submodule in the working directory
996 */
997 if (wi->entry.path != NULL &&
998 (wi->entry.mode == GIT_FILEMODE_TREE ||
999 wi->entry.mode == GIT_FILEMODE_COMMIT))
1000 /* returns GIT_ENOTFOUND if the directory is empty */
1001 error = workdir_iterator__expand_dir(wi);
1002
1003 if (!error && entry)
1004 error = workdir_iterator__current(entry, iter);
1005
1006 return error;
1007 }
1008
1009 static int workdir_iterator__advance(
1010 const git_index_entry **entry, git_iterator *self)
1011 {
1012 int error = 0;
1013 workdir_iterator *wi = (workdir_iterator *)self;
1014 workdir_iterator_frame *wf;
1015 git_path_with_stat *next;
1016
1017 /* given include_trees & autoexpand, we might have to go into a tree */
1018 if (iterator__do_autoexpand(wi) &&
1019 wi->entry.path != NULL &&
1020 wi->entry.mode == GIT_FILEMODE_TREE)
1021 {
1022 error = workdir_iterator__advance_into(entry, self);
1023
1024 /* continue silently past empty directories if autoexpanding */
1025 if (error != GIT_ENOTFOUND)
1026 return error;
1027 giterr_clear();
1028 error = 0;
1029 }
1030
1031 if (entry != NULL)
1032 *entry = NULL;
1033
1034 while (wi->entry.path != NULL) {
1035 wf = wi->stack;
1036 next = git_vector_get(&wf->entries, ++wf->index);
1037
1038 if (next != NULL) {
1039 /* match git's behavior of ignoring anything named ".git" */
1040 if (path_is_dotgit(next))
1041 continue;
1042 /* else found a good entry */
1043 break;
1044 }
1045
1046 /* pop stack if anything is left to pop */
1047 if (!wf->next) {
1048 memset(&wi->entry, 0, sizeof(wi->entry));
1049 return 0;
1050 }
1051
1052 wi->stack = wf->next;
1053 wi->depth--;
1054 workdir_iterator__free_frame(wf);
1055 git_ignore__pop_dir(&wi->ignores);
1056 }
1057
1058 error = workdir_iterator__update_entry(wi);
1059
1060 if (!error && entry != NULL)
1061 error = workdir_iterator__current(entry, self);
1062
1063 return error;
1064 }
1065
1066 static int workdir_iterator__seek(git_iterator *self, const char *prefix)
1067 {
1068 GIT_UNUSED(self);
1069 GIT_UNUSED(prefix);
1070 /* pop stack until matching prefix */
1071 /* find prefix item in current frame */
1072 /* push subdirectories as deep as possible while matching */
1073 return 0;
1074 }
1075
1076 static int workdir_iterator__reset(
1077 git_iterator *self, const char *start, const char *end)
1078 {
1079 workdir_iterator *wi = (workdir_iterator *)self;
1080
1081 while (wi->stack != NULL && wi->stack->next != NULL) {
1082 workdir_iterator_frame *wf = wi->stack;
1083 wi->stack = wf->next;
1084 workdir_iterator__free_frame(wf);
1085 git_ignore__pop_dir(&wi->ignores);
1086 }
1087 wi->depth = 0;
1088
1089 if (iterator__reset_range(self, start, end) < 0)
1090 return -1;
1091
1092 workdir_iterator__seek_frame_start(wi, wi->stack);
1093
1094 return workdir_iterator__update_entry(wi);
1095 }
1096
1097 static void workdir_iterator__free(git_iterator *self)
1098 {
1099 workdir_iterator *wi = (workdir_iterator *)self;
1100
1101 while (wi->stack != NULL) {
1102 workdir_iterator_frame *wf = wi->stack;
1103 wi->stack = wf->next;
1104 workdir_iterator__free_frame(wf);
1105 }
1106
1107 git_ignore__free(&wi->ignores);
1108 git_buf_free(&wi->path);
1109 }
1110
1111 static int workdir_iterator__update_entry(workdir_iterator *wi)
1112 {
1113 int error = 0;
1114 git_path_with_stat *ps =
1115 git_vector_get(&wi->stack->entries, wi->stack->index);
1116
1117 git_buf_truncate(&wi->path, wi->root_len);
1118 memset(&wi->entry, 0, sizeof(wi->entry));
1119
1120 if (!ps)
1121 return 0;
1122
1123 /* skip over .git entries */
1124 if (path_is_dotgit(ps))
1125 return workdir_iterator__advance(NULL, (git_iterator *)wi);
1126
1127 if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
1128 return -1;
1129
1130 if (iterator__past_end(wi, wi->path.ptr + wi->root_len))
1131 return 0;
1132
1133 wi->entry.path = ps->path;
1134
1135 wi->is_ignored = -1;
1136
1137 git_index_entry__init_from_stat(&wi->entry, &ps->st);
1138
1139 /* need different mode here to keep directories during iteration */
1140 wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1141
1142 /* if this is a file type we don't handle, treat as ignored */
1143 if (wi->entry.mode == 0) {
1144 wi->is_ignored = 1;
1145 return 0;
1146 }
1147
1148 /* if this isn't a tree, then we're done */
1149 if (wi->entry.mode != GIT_FILEMODE_TREE)
1150 return 0;
1151
1152 /* detect submodules */
1153 error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path);
1154 if (error == GIT_ENOTFOUND)
1155 giterr_clear();
1156
1157 if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */
1158 error = 0;
1159
1160 /* if submodule, mark as GITLINK and remove trailing slash */
1161 if (!error) {
1162 size_t len = strlen(wi->entry.path);
1163 assert(wi->entry.path[len - 1] == '/');
1164 wi->entry.path[len - 1] = '\0';
1165 wi->entry.mode = S_IFGITLINK;
1166 return 0;
1167 }
1168
1169 if (iterator__include_trees(wi))
1170 return 0;
1171
1172 return workdir_iterator__advance(NULL, (git_iterator *)wi);
1173 }
1174
1175 int git_iterator_for_workdir(
1176 git_iterator **iter,
1177 git_repository *repo,
1178 git_iterator_flag_t flags,
1179 const char *start,
1180 const char *end)
1181 {
1182 int error;
1183 workdir_iterator *wi;
1184
1185 assert(iter && repo);
1186
1187 if ((error = git_repository__ensure_not_bare(
1188 repo, "scan working directory")) < 0)
1189 return error;
1190
1191 ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
1192
1193 if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
1194 goto fail;
1195
1196 if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
1197 git_path_to_dir(&wi->path) < 0 ||
1198 git_ignore__for_path(repo, "", &wi->ignores) < 0)
1199 {
1200 git__free(wi);
1201 return -1;
1202 }
1203 wi->root_len = wi->path.size;
1204
1205 if ((error = workdir_iterator__expand_dir(wi)) < 0) {
1206 if (error != GIT_ENOTFOUND)
1207 goto fail;
1208 giterr_clear();
1209 }
1210
1211 *iter = (git_iterator *)wi;
1212 return 0;
1213
1214 fail:
1215 git_iterator_free((git_iterator *)wi);
1216 return error;
1217 }
1218
1219
1220 void git_iterator_free(git_iterator *iter)
1221 {
1222 if (iter == NULL)
1223 return;
1224
1225 iter->cb->free(iter);
1226
1227 git__free(iter->start);
1228 git__free(iter->end);
1229
1230 memset(iter, 0, sizeof(*iter));
1231
1232 git__free(iter);
1233 }
1234
1235 int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1236 {
1237 bool desire_ignore_case = (ignore_case != 0);
1238
1239 if (iterator__ignore_case(iter) == desire_ignore_case)
1240 return 0;
1241
1242 if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1243 if (desire_ignore_case)
1244 iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1245 else
1246 iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1247 } else {
1248 giterr_set(GITERR_INVALID,
1249 "Cannot currently set ignore case on non-empty iterators");
1250 return -1;
1251 }
1252
1253 return 0;
1254 }
1255
1256 git_index *git_iterator_get_index(git_iterator *iter)
1257 {
1258 if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1259 return ((index_iterator *)iter)->index;
1260 return NULL;
1261 }
1262
1263 int git_iterator_current_tree_entry(
1264 const git_tree_entry **tree_entry, git_iterator *iter)
1265 {
1266 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1267 *tree_entry = NULL;
1268 else {
1269 tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1270 *tree_entry = (tf->current < tf->n_entries) ?
1271 tf->entries[tf->current]->te : NULL;
1272 }
1273
1274 return 0;
1275 }
1276
1277 int git_iterator_current_parent_tree(
1278 const git_tree **tree_ptr,
1279 git_iterator *iter,
1280 const char *parent_path)
1281 {
1282 tree_iterator *ti = (tree_iterator *)iter;
1283 tree_iterator_frame *tf;
1284 const char *scan = parent_path;
1285 const git_tree_entry *te;
1286
1287 *tree_ptr = NULL;
1288
1289 if (iter->type != GIT_ITERATOR_TYPE_TREE)
1290 return 0;
1291
1292 for (tf = ti->root; *scan; ) {
1293 if (!(tf = tf->down) ||
1294 tf->current >= tf->n_entries ||
1295 !(te = tf->entries[tf->current]->te) ||
1296 ti->strncomp(scan, te->filename, te->filename_len) != 0)
1297 return 0;
1298
1299 scan += te->filename_len;
1300 if (*scan == '/')
1301 scan++;
1302 }
1303
1304 *tree_ptr = tf->entries[tf->current]->tree;
1305 return 0;
1306 }
1307
1308 bool git_iterator_current_is_ignored(git_iterator *iter)
1309 {
1310 workdir_iterator *wi = (workdir_iterator *)iter;
1311
1312 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1313 return false;
1314
1315 if (wi->is_ignored != -1)
1316 return (bool)(wi->is_ignored != 0);
1317
1318 if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
1319 wi->is_ignored = true;
1320
1321 return (bool)wi->is_ignored;
1322 }
1323
1324 int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
1325 {
1326 const git_index_entry *entry;
1327
1328 /* a "done" iterator is after every prefix */
1329 if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1330 return 1;
1331
1332 /* a NULL prefix is after any valid iterator */
1333 if (!path_prefix)
1334 return -1;
1335
1336 return iter->prefixcomp(entry->path, path_prefix);
1337 }
1338
1339 int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1340 {
1341 workdir_iterator *wi = (workdir_iterator *)iter;
1342
1343 if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path)
1344 *path = NULL;
1345 else
1346 *path = &wi->path;
1347
1348 return 0;
1349 }