| /* |
| * Copyright (C) 2009-2012 the libgit2 contributors |
| * |
| * This file is part of libgit2, distributed under the GNU GPL v2 with |
| * a Linking Exception. For full terms see the included COPYING file. |
| */ |
| |
| #include "iterator.h" |
| #include "tree.h" |
| #include "ignore.h" |
| #include "buffer.h" |
| #include "git2/submodule.h" |
| |
| #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \ |
| (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \ |
| GITERR_CHECK_ALLOC(P); \ |
| (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \ |
| (P)->base.start = start ? git__strdup(start) : NULL; \ |
| (P)->base.end = end ? git__strdup(end) : NULL; \ |
| (P)->base.ignore_case = 0; \ |
| (P)->base.current = NAME_LC ## _iterator__current; \ |
| (P)->base.at_end = NAME_LC ## _iterator__at_end; \ |
| (P)->base.advance = NAME_LC ## _iterator__advance; \ |
| (P)->base.seek = NAME_LC ## _iterator__seek; \ |
| (P)->base.reset = NAME_LC ## _iterator__reset; \ |
| (P)->base.free = NAME_LC ## _iterator__free; \ |
| if ((start && !(P)->base.start) || (end && !(P)->base.end)) \ |
| return -1; \ |
| } while (0) |
| |
| |
| static int empty_iterator__no_item( |
| git_iterator *iter, const git_index_entry **entry) |
| { |
| GIT_UNUSED(iter); |
| *entry = NULL; |
| return 0; |
| } |
| |
| static int empty_iterator__at_end(git_iterator *iter) |
| { |
| GIT_UNUSED(iter); |
| return 1; |
| } |
| |
| static int empty_iterator__noop(git_iterator *iter) |
| { |
| GIT_UNUSED(iter); |
| return 0; |
| } |
| |
| static int empty_iterator__seek(git_iterator *iter, const char *prefix) |
| { |
| GIT_UNUSED(iter); |
| GIT_UNUSED(prefix); |
| return -1; |
| } |
| |
| static void empty_iterator__free(git_iterator *iter) |
| { |
| GIT_UNUSED(iter); |
| } |
| |
| int git_iterator_for_nothing(git_iterator **iter) |
| { |
| git_iterator *i = git__calloc(1, sizeof(git_iterator)); |
| GITERR_CHECK_ALLOC(i); |
| |
| i->type = GIT_ITERATOR_EMPTY; |
| i->current = empty_iterator__no_item; |
| i->at_end = empty_iterator__at_end; |
| i->advance = empty_iterator__no_item; |
| i->seek = empty_iterator__seek; |
| i->reset = empty_iterator__noop; |
| i->free = empty_iterator__free; |
| |
| *iter = i; |
| |
| return 0; |
| } |
| |
| |
| typedef struct tree_iterator_frame tree_iterator_frame; |
| struct tree_iterator_frame { |
| tree_iterator_frame *next, *prev; |
| git_tree *tree; |
| char *start; |
| unsigned int index; |
| }; |
| |
| typedef struct { |
| git_iterator base; |
| git_repository *repo; |
| tree_iterator_frame *stack, *tail; |
| git_index_entry entry; |
| git_buf path; |
| bool path_has_filename; |
| } tree_iterator; |
| |
| static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti) |
| { |
| return (ti->stack == NULL) ? NULL : |
| git_tree_entry_byindex(ti->stack->tree, ti->stack->index); |
| } |
| |
| static char *tree_iterator__current_filename( |
| tree_iterator *ti, const git_tree_entry *te) |
| { |
| if (!ti->path_has_filename) { |
| if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) |
| return NULL; |
| ti->path_has_filename = true; |
| } |
| |
| return ti->path.ptr; |
| } |
| |
| static void tree_iterator__pop_frame(tree_iterator *ti) |
| { |
| tree_iterator_frame *tf = ti->stack; |
| ti->stack = tf->next; |
| if (ti->stack != NULL) { |
| git_tree_free(tf->tree); /* don't free the initial tree */ |
| ti->stack->prev = NULL; /* disconnect prev */ |
| } |
| git__free(tf); |
| } |
| |
| static int tree_iterator__to_end(tree_iterator *ti) |
| { |
| while (ti->stack && ti->stack->next) |
| tree_iterator__pop_frame(ti); |
| |
| if (ti->stack) |
| ti->stack->index = git_tree_entrycount(ti->stack->tree); |
| |
| return 0; |
| } |
| |
| static int tree_iterator__current( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| tree_iterator *ti = (tree_iterator *)self; |
| const git_tree_entry *te = tree_iterator__tree_entry(ti); |
| |
| if (entry) |
| *entry = NULL; |
| |
| if (te == NULL) |
| return 0; |
| |
| ti->entry.mode = te->attr; |
| git_oid_cpy(&ti->entry.oid, &te->oid); |
| |
| ti->entry.path = tree_iterator__current_filename(ti, te); |
| if (ti->entry.path == NULL) |
| return -1; |
| |
| if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0) |
| return tree_iterator__to_end(ti); |
| |
| if (entry) |
| *entry = &ti->entry; |
| |
| return 0; |
| } |
| |
| static int tree_iterator__at_end(git_iterator *self) |
| { |
| return (tree_iterator__tree_entry((tree_iterator *)self) == NULL); |
| } |
| |
| static tree_iterator_frame *tree_iterator__alloc_frame( |
| git_tree *tree, char *start) |
| { |
| tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame)); |
| if (!tf) |
| return NULL; |
| |
| tf->tree = tree; |
| |
| if (start && *start) { |
| tf->start = start; |
| tf->index = git_tree__prefix_position(tree, start); |
| } |
| |
| return tf; |
| } |
| |
| static int tree_iterator__expand_tree(tree_iterator *ti) |
| { |
| int error; |
| git_tree *subtree; |
| const git_tree_entry *te = tree_iterator__tree_entry(ti); |
| tree_iterator_frame *tf; |
| char *relpath; |
| |
| while (te != NULL && git_tree_entry__is_tree(te)) { |
| if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) |
| return -1; |
| |
| /* check that we have not passed the range end */ |
| if (ti->base.end != NULL && |
| git__prefixcmp(ti->path.ptr, ti->base.end) > 0) |
| return tree_iterator__to_end(ti); |
| |
| if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0) |
| return error; |
| |
| relpath = NULL; |
| |
| /* apply range start to new frame if relevant */ |
| if (ti->stack->start && |
| git__prefixcmp(ti->stack->start, te->filename) == 0) |
| { |
| size_t namelen = strlen(te->filename); |
| if (ti->stack->start[namelen] == '/') |
| relpath = ti->stack->start + namelen + 1; |
| } |
| |
| if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL) |
| return -1; |
| |
| tf->next = ti->stack; |
| ti->stack = tf; |
| tf->next->prev = tf; |
| |
| te = tree_iterator__tree_entry(ti); |
| } |
| |
| return 0; |
| } |
| |
| static int tree_iterator__advance( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| int error = 0; |
| tree_iterator *ti = (tree_iterator *)self; |
| const git_tree_entry *te = NULL; |
| |
| if (entry != NULL) |
| *entry = NULL; |
| |
| if (ti->path_has_filename) { |
| git_buf_rtruncate_at_char(&ti->path, '/'); |
| ti->path_has_filename = false; |
| } |
| |
| while (ti->stack != NULL) { |
| te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index); |
| if (te != NULL) |
| break; |
| |
| tree_iterator__pop_frame(ti); |
| |
| git_buf_rtruncate_at_char(&ti->path, '/'); |
| } |
| |
| if (te && git_tree_entry__is_tree(te)) |
| error = tree_iterator__expand_tree(ti); |
| |
| if (!error) |
| error = tree_iterator__current(self, entry); |
| |
| return error; |
| } |
| |
| static int tree_iterator__seek(git_iterator *self, const char *prefix) |
| { |
| GIT_UNUSED(self); |
| GIT_UNUSED(prefix); |
| /* pop stack until matches prefix */ |
| /* seek item in current frame matching prefix */ |
| /* push stack which matches prefix */ |
| return -1; |
| } |
| |
| static void tree_iterator__free(git_iterator *self) |
| { |
| tree_iterator *ti = (tree_iterator *)self; |
| while (ti->stack != NULL) |
| tree_iterator__pop_frame(ti); |
| git_buf_free(&ti->path); |
| } |
| |
| static int tree_iterator__reset(git_iterator *self) |
| { |
| tree_iterator *ti = (tree_iterator *)self; |
| |
| while (ti->stack && ti->stack->next) |
| tree_iterator__pop_frame(ti); |
| |
| if (ti->stack) |
| ti->stack->index = |
| git_tree__prefix_position(ti->stack->tree, ti->base.start); |
| |
| git_buf_clear(&ti->path); |
| |
| return tree_iterator__expand_tree(ti); |
| } |
| |
| int git_iterator_for_tree_range( |
| git_iterator **iter, |
| git_repository *repo, |
| git_tree *tree, |
| const char *start, |
| const char *end) |
| { |
| int error; |
| tree_iterator *ti; |
| |
| if (tree == NULL) |
| return git_iterator_for_nothing(iter); |
| |
| ITERATOR_BASE_INIT(ti, tree, TREE); |
| |
| ti->repo = repo; |
| ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start); |
| |
| if ((error = tree_iterator__expand_tree(ti)) < 0) |
| git_iterator_free((git_iterator *)ti); |
| else |
| *iter = (git_iterator *)ti; |
| |
| return error; |
| } |
| |
| |
| typedef struct { |
| git_iterator base; |
| git_index *index; |
| unsigned int current; |
| } index_iterator; |
| |
| static int index_iterator__current( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| index_iterator *ii = (index_iterator *)self; |
| git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); |
| |
| if (ie != NULL && |
| ii->base.end != NULL && |
| ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0) |
| { |
| ii->current = git_index_entrycount(ii->index); |
| ie = NULL; |
| } |
| |
| if (entry) |
| *entry = ie; |
| |
| return 0; |
| } |
| |
| static int index_iterator__at_end(git_iterator *self) |
| { |
| index_iterator *ii = (index_iterator *)self; |
| return (ii->current >= git_index_entrycount(ii->index)); |
| } |
| |
| static int index_iterator__advance( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| index_iterator *ii = (index_iterator *)self; |
| |
| if (ii->current < git_index_entrycount(ii->index)) |
| ii->current++; |
| |
| return index_iterator__current(self, entry); |
| } |
| |
| static int index_iterator__seek(git_iterator *self, const char *prefix) |
| { |
| GIT_UNUSED(self); |
| GIT_UNUSED(prefix); |
| /* find last item before prefix */ |
| return -1; |
| } |
| |
| static int index_iterator__reset(git_iterator *self) |
| { |
| index_iterator *ii = (index_iterator *)self; |
| ii->current = 0; |
| return 0; |
| } |
| |
| static void index_iterator__free(git_iterator *self) |
| { |
| index_iterator *ii = (index_iterator *)self; |
| git_index_free(ii->index); |
| ii->index = NULL; |
| } |
| |
| int git_iterator_for_index_range( |
| git_iterator **iter, |
| git_repository *repo, |
| const char *start, |
| const char *end) |
| { |
| int error; |
| index_iterator *ii; |
| |
| ITERATOR_BASE_INIT(ii, index, INDEX); |
| |
| if ((error = git_repository_index(&ii->index, repo)) < 0) |
| git__free(ii); |
| else { |
| ii->base.ignore_case = ii->index->ignore_case; |
| ii->current = start ? git_index__prefix_position(ii->index, start) : 0; |
| *iter = (git_iterator *)ii; |
| } |
| |
| return error; |
| } |
| |
| |
| typedef struct workdir_iterator_frame workdir_iterator_frame; |
| struct workdir_iterator_frame { |
| workdir_iterator_frame *next; |
| git_vector entries; |
| unsigned int index; |
| char *start; |
| }; |
| |
| typedef struct { |
| git_iterator base; |
| git_repository *repo; |
| size_t root_len; |
| workdir_iterator_frame *stack; |
| git_ignores ignores; |
| git_index_entry entry; |
| git_buf path; |
| int is_ignored; |
| } workdir_iterator; |
| |
| static int git_path_with_stat_cmp_case(const void *a, const void *b) |
| { |
| const git_path_with_stat *path_with_stat_a = a; |
| const git_path_with_stat *path_with_stat_b = b; |
| |
| return strcmp(path_with_stat_a->path, path_with_stat_b->path); |
| } |
| |
| static int git_path_with_stat_cmp_icase(const void *a, const void *b) |
| { |
| const git_path_with_stat *path_with_stat_a = a; |
| const git_path_with_stat *path_with_stat_b = b; |
| |
| return strcasecmp(path_with_stat_a->path, path_with_stat_b->path); |
| } |
| |
| static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi) |
| { |
| workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); |
| git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, git_path_with_stat_cmp_icase, git_path_with_stat_cmp_case); |
| |
| if (wf == NULL) |
| return NULL; |
| if (git_vector_init(&wf->entries, 0, entry_compare) != 0) { |
| git__free(wf); |
| return NULL; |
| } |
| return wf; |
| } |
| |
| static void workdir_iterator__free_frame(workdir_iterator_frame *wf) |
| { |
| unsigned int i; |
| git_path_with_stat *path; |
| |
| git_vector_foreach(&wf->entries, i, path) |
| git__free(path); |
| git_vector_free(&wf->entries); |
| git__free(wf); |
| } |
| |
| static int workdir_iterator__update_entry(workdir_iterator *wi); |
| |
| static int workdir_iterator__entry_cmp_case(const void *prefix, const void *item) |
| { |
| const git_path_with_stat *ps = item; |
| return git__prefixcmp((const char *)prefix, ps->path); |
| } |
| |
| static int workdir_iterator__entry_cmp_icase(const void *prefix, const void *item) |
| { |
| const git_path_with_stat *ps = item; |
| return git__prefixcmp_icase((const char *)prefix, ps->path); |
| } |
| |
| static int workdir_iterator__expand_dir(workdir_iterator *wi) |
| { |
| int error; |
| workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi); |
| GITERR_CHECK_ALLOC(wf); |
| |
| error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries); |
| if (error < 0 || wf->entries.length == 0) { |
| workdir_iterator__free_frame(wf); |
| return GIT_ENOTFOUND; |
| } |
| |
| git_vector_sort(&wf->entries); |
| |
| if (!wi->stack) |
| wf->start = wi->base.start; |
| else if (wi->stack->start && |
| ITERATOR_PREFIXCMP(wi->base, wi->stack->start, wi->path.ptr + wi->root_len) == 0) |
| wf->start = wi->stack->start; |
| |
| if (wf->start) |
| git_vector_bsearch3( |
| &wf->index, |
| &wf->entries, |
| CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case), |
| wf->start); |
| |
| wf->next = wi->stack; |
| wi->stack = wf; |
| |
| /* only push new ignores if this is not top level directory */ |
| if (wi->stack->next != NULL) { |
| ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/'); |
| (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]); |
| } |
| |
| return workdir_iterator__update_entry(wi); |
| } |
| |
| static int workdir_iterator__current( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| workdir_iterator *wi = (workdir_iterator *)self; |
| *entry = (wi->entry.path == NULL) ? NULL : &wi->entry; |
| return 0; |
| } |
| |
| static int workdir_iterator__at_end(git_iterator *self) |
| { |
| return (((workdir_iterator *)self)->entry.path == NULL); |
| } |
| |
| static int workdir_iterator__advance( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| int error; |
| workdir_iterator *wi = (workdir_iterator *)self; |
| workdir_iterator_frame *wf; |
| git_path_with_stat *next; |
| |
| if (entry != NULL) |
| *entry = NULL; |
| |
| if (wi->entry.path == NULL) |
| return 0; |
| |
| while ((wf = wi->stack) != NULL) { |
| next = git_vector_get(&wf->entries, ++wf->index); |
| if (next != NULL) { |
| /* match git's behavior of ignoring anything named ".git" */ |
| if (STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT "/") == 0 || |
| STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT) == 0) |
| continue; |
| /* else found a good entry */ |
| break; |
| } |
| |
| /* pop workdir directory stack */ |
| wi->stack = wf->next; |
| workdir_iterator__free_frame(wf); |
| git_ignore__pop_dir(&wi->ignores); |
| |
| if (wi->stack == NULL) { |
| memset(&wi->entry, 0, sizeof(wi->entry)); |
| return 0; |
| } |
| } |
| |
| error = workdir_iterator__update_entry(wi); |
| |
| if (!error && entry != NULL) |
| error = workdir_iterator__current(self, entry); |
| |
| return error; |
| } |
| |
| static int workdir_iterator__seek(git_iterator *self, const char *prefix) |
| { |
| GIT_UNUSED(self); |
| GIT_UNUSED(prefix); |
| /* pop stack until matching prefix */ |
| /* find prefix item in current frame */ |
| /* push subdirectories as deep as possible while matching */ |
| return 0; |
| } |
| |
| static int workdir_iterator__reset(git_iterator *self) |
| { |
| workdir_iterator *wi = (workdir_iterator *)self; |
| while (wi->stack != NULL && wi->stack->next != NULL) { |
| workdir_iterator_frame *wf = wi->stack; |
| wi->stack = wf->next; |
| workdir_iterator__free_frame(wf); |
| git_ignore__pop_dir(&wi->ignores); |
| } |
| if (wi->stack) |
| wi->stack->index = 0; |
| return 0; |
| } |
| |
| static void workdir_iterator__free(git_iterator *self) |
| { |
| workdir_iterator *wi = (workdir_iterator *)self; |
| |
| while (wi->stack != NULL) { |
| workdir_iterator_frame *wf = wi->stack; |
| wi->stack = wf->next; |
| workdir_iterator__free_frame(wf); |
| } |
| |
| git_ignore__free(&wi->ignores); |
| git_buf_free(&wi->path); |
| } |
| |
| static int workdir_iterator__update_entry(workdir_iterator *wi) |
| { |
| git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index); |
| |
| git_buf_truncate(&wi->path, wi->root_len); |
| memset(&wi->entry, 0, sizeof(wi->entry)); |
| |
| if (!ps) |
| return 0; |
| |
| if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0) |
| return -1; |
| |
| if (wi->base.end && |
| ITERATOR_PREFIXCMP(wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0) |
| return 0; |
| |
| wi->entry.path = ps->path; |
| |
| /* skip over .git entry */ |
| if (STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT "/") == 0 || |
| STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT) == 0) |
| return workdir_iterator__advance((git_iterator *)wi, NULL); |
| |
| /* if there is an error processing the entry, treat as ignored */ |
| wi->is_ignored = 1; |
| |
| git_index__init_entry_from_stat(&ps->st, &wi->entry); |
| |
| /* need different mode here to keep directories during iteration */ |
| wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); |
| |
| /* if this is a file type we don't handle, treat as ignored */ |
| if (wi->entry.mode == 0) |
| return 0; |
| |
| /* okay, we are far enough along to look up real ignore rule */ |
| if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0) |
| return 0; /* if error, ignore it and ignore file */ |
| |
| /* detect submodules */ |
| if (S_ISDIR(wi->entry.mode)) { |
| int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path); |
| bool is_submodule = (res == 0); |
| if (res == GIT_ENOTFOUND) |
| giterr_clear(); |
| |
| /* if submodule, mark as GITLINK and remove trailing slash */ |
| if (is_submodule) { |
| size_t len = strlen(wi->entry.path); |
| assert(wi->entry.path[len - 1] == '/'); |
| wi->entry.path[len - 1] = '\0'; |
| wi->entry.mode = S_IFGITLINK; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int git_iterator_for_workdir_range( |
| git_iterator **iter, |
| git_repository *repo, |
| const char *start, |
| const char *end) |
| { |
| int error; |
| workdir_iterator *wi; |
| git_index *index; |
| |
| assert(iter && repo); |
| |
| if ((error = git_repository__ensure_not_bare(repo, "scan working directory")) < 0) |
| return error; |
| |
| ITERATOR_BASE_INIT(wi, workdir, WORKDIR); |
| |
| wi->repo = repo; |
| |
| if ((error = git_repository_index(&index, repo)) < 0) { |
| git__free(wi); |
| return error; |
| } |
| |
| /* Set the ignore_case flag for the workdir iterator to match |
| * that of the index. */ |
| wi->base.ignore_case = index->ignore_case; |
| |
| git_index_free(index); |
| |
| if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 || |
| git_path_to_dir(&wi->path) < 0 || |
| git_ignore__for_path(repo, "", &wi->ignores) < 0) |
| { |
| git__free(wi); |
| return -1; |
| } |
| |
| wi->root_len = wi->path.size; |
| |
| if ((error = workdir_iterator__expand_dir(wi)) < 0) { |
| if (error == GIT_ENOTFOUND) |
| error = 0; |
| else { |
| git_iterator_free((git_iterator *)wi); |
| wi = NULL; |
| } |
| } |
| |
| *iter = (git_iterator *)wi; |
| |
| return error; |
| } |
| |
| typedef struct { |
| git_iterator base; |
| git_iterator *wrapped; |
| git_vector entries; |
| git_vector_cmp comparer; |
| git_pool entry_pool; |
| git_pool string_pool; |
| unsigned int position; |
| } spoolandsort_iterator; |
| |
| static int spoolandsort_iterator__current( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| spoolandsort_iterator *si = (spoolandsort_iterator *)self; |
| |
| if (si->position < si->entries.length) |
| *entry = (const git_index_entry *)git_vector_get_const(&si->entries, si->position); |
| else |
| *entry = NULL; |
| |
| return 0; |
| } |
| |
| static int spoolandsort_iterator__at_end(git_iterator *self) |
| { |
| spoolandsort_iterator *si = (spoolandsort_iterator *)self; |
| |
| return 0 == si->entries.length || si->entries.length - 1 <= si->position; |
| } |
| |
| static int spoolandsort_iterator__advance( |
| git_iterator *self, const git_index_entry **entry) |
| { |
| spoolandsort_iterator *si = (spoolandsort_iterator *)self; |
| |
| if (si->position < si->entries.length) |
| *entry = (const git_index_entry *)git_vector_get_const(&si->entries, ++si->position); |
| else |
| *entry = NULL; |
| |
| return 0; |
| } |
| |
| static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix) |
| { |
| GIT_UNUSED(self); |
| GIT_UNUSED(prefix); |
| |
| return -1; |
| } |
| |
| static int spoolandsort_iterator__reset(git_iterator *self) |
| { |
| spoolandsort_iterator *si = (spoolandsort_iterator *)self; |
| |
| si->position = 0; |
| |
| return 0; |
| } |
| |
| static void spoolandsort_iterator__free(git_iterator *self) |
| { |
| spoolandsort_iterator *si = (spoolandsort_iterator *)self; |
| |
| git_pool_clear(&si->string_pool); |
| git_pool_clear(&si->entry_pool); |
| git_vector_free(&si->entries); |
| git_iterator_free(si->wrapped); |
| } |
| |
| int git_iterator_spoolandsort_range( |
| git_iterator **iter, |
| git_iterator *towrap, |
| git_vector_cmp comparer, |
| bool ignore_case, |
| const char *start, |
| const char *end) |
| { |
| spoolandsort_iterator *si; |
| const git_index_entry *item; |
| |
| assert(iter && towrap && comparer); |
| |
| ITERATOR_BASE_INIT(si, spoolandsort, SPOOLANDSORT); |
| si->base.ignore_case = ignore_case; |
| si->wrapped = towrap; |
| si->comparer = comparer; |
| si->position = 0; |
| |
| if (git_vector_init(&si->entries, 16, si->comparer) < 0 || |
| git_iterator_current(towrap, &item) < 0 || |
| git_pool_init(&si->entry_pool, sizeof(git_index_entry), 0) || |
| git_pool_init(&si->string_pool, 1, 0)) |
| { |
| git__free(si); |
| return -1; |
| } |
| |
| while (item) |
| { |
| git_index_entry *clone = git_pool_malloc(&si->entry_pool, 1); |
| memcpy(clone, item, sizeof(git_index_entry)); |
| |
| if (item->path) |
| { |
| clone->path = git_pool_strdup(&si->string_pool, item->path); |
| } |
| |
| git_vector_insert(&si->entries, clone); |
| |
| if (git_iterator_advance(towrap, &item) < 0) |
| { |
| git__free(si); |
| return -1; |
| } |
| } |
| |
| git_vector_sort(&si->entries); |
| |
| *iter = (git_iterator *)si; |
| |
| return 0; |
| } |
| |
| int git_iterator_current_tree_entry( |
| git_iterator *iter, const git_tree_entry **tree_entry) |
| { |
| *tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL : |
| tree_iterator__tree_entry((tree_iterator *)iter); |
| return 0; |
| } |
| |
| int git_iterator_current_parent_tree( |
| git_iterator *iter, |
| const char *parent_path, |
| const git_tree **tree_ptr) |
| { |
| tree_iterator *ti = (tree_iterator *)iter; |
| tree_iterator_frame *tf; |
| const char *scan = parent_path; |
| |
| if (iter->type != GIT_ITERATOR_TREE || ti->stack == NULL) |
| goto notfound; |
| |
| for (tf = ti->tail; tf != NULL; tf = tf->prev) { |
| const git_tree_entry *te; |
| |
| if (!*scan) { |
| *tree_ptr = tf->tree; |
| return 0; |
| } |
| |
| te = git_tree_entry_byindex(tf->tree, tf->index); |
| |
| if (strncmp(scan, te->filename, te->filename_len) != 0) |
| goto notfound; |
| |
| scan += te->filename_len; |
| |
| if (*scan) { |
| if (*scan != '/') |
| goto notfound; |
| scan++; |
| } |
| } |
| |
| notfound: |
| *tree_ptr = NULL; |
| return 0; |
| } |
| |
| int git_iterator_current_is_ignored(git_iterator *iter) |
| { |
| return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 : |
| ((workdir_iterator *)iter)->is_ignored; |
| } |
| |
| int git_iterator_advance_into_directory( |
| git_iterator *iter, const git_index_entry **entry) |
| { |
| workdir_iterator *wi = (workdir_iterator *)iter; |
| |
| if (iter->type == GIT_ITERATOR_WORKDIR && |
| wi->entry.path && |
| S_ISDIR(wi->entry.mode) && |
| !S_ISGITLINK(wi->entry.mode)) |
| { |
| if (workdir_iterator__expand_dir(wi) < 0) |
| /* if error loading or if empty, skip the directory. */ |
| return workdir_iterator__advance(iter, entry); |
| } |
| |
| return entry ? git_iterator_current(iter, entry) : 0; |
| } |
| |
| int git_iterator_cmp( |
| git_iterator *iter, const char *path_prefix) |
| { |
| const git_index_entry *entry; |
| |
| /* a "done" iterator is after every prefix */ |
| if (git_iterator_current(iter, &entry) < 0 || |
| entry == NULL) |
| return 1; |
| |
| /* a NULL prefix is after any valid iterator */ |
| if (!path_prefix) |
| return -1; |
| |
| return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix); |
| } |
| |
| int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path) |
| { |
| workdir_iterator *wi = (workdir_iterator *)iter; |
| |
| if (iter->type != GIT_ITERATOR_WORKDIR || !wi->entry.path) |
| *path = NULL; |
| else |
| *path = &wi->path; |
| |
| return 0; |
| } |
| |