]>
Commit | Line | Data |
---|---|---|
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 | #ifndef INCLUDE_iterator_h__ | |
8 | #define INCLUDE_iterator_h__ | |
9 | ||
10 | #include "common.h" | |
11 | #include "git2/index.h" | |
12 | #include "vector.h" | |
13 | #include "buffer.h" | |
14 | #include "ignore.h" | |
15 | ||
16 | typedef struct git_iterator git_iterator; | |
17 | ||
18 | typedef enum { | |
19 | GIT_ITERATOR_TYPE_EMPTY = 0, | |
20 | GIT_ITERATOR_TYPE_TREE = 1, | |
21 | GIT_ITERATOR_TYPE_INDEX = 2, | |
22 | GIT_ITERATOR_TYPE_WORKDIR = 3, | |
23 | GIT_ITERATOR_TYPE_FS = 4, | |
24 | } git_iterator_type_t; | |
25 | ||
26 | typedef enum { | |
27 | /** ignore case for entry sort order */ | |
28 | GIT_ITERATOR_IGNORE_CASE = (1u << 0), | |
29 | /** force case sensitivity for entry sort order */ | |
30 | GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1), | |
31 | /** return tree items in addition to blob items */ | |
32 | GIT_ITERATOR_INCLUDE_TREES = (1u << 2), | |
33 | /** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */ | |
34 | GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3), | |
35 | /** convert precomposed unicode to decomposed unicode */ | |
36 | GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4), | |
37 | /** include conflicts */ | |
38 | GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5), | |
39 | } git_iterator_flag_t; | |
40 | ||
41 | typedef struct { | |
42 | int (*current)(const git_index_entry **, git_iterator *); | |
43 | int (*advance)(const git_index_entry **, git_iterator *); | |
44 | int (*advance_into)(const git_index_entry **, git_iterator *); | |
45 | int (*seek)(git_iterator *, const char *prefix); | |
46 | int (*reset)(git_iterator *, const char *start, const char *end); | |
47 | int (*at_end)(git_iterator *); | |
48 | void (*free)(git_iterator *); | |
49 | } git_iterator_callbacks; | |
50 | ||
51 | struct git_iterator { | |
52 | git_iterator_type_t type; | |
53 | git_iterator_callbacks *cb; | |
54 | git_repository *repo; | |
55 | char *start; | |
56 | char *end; | |
57 | int (*prefixcomp)(const char *str, const char *prefix); | |
58 | size_t stat_calls; | |
59 | unsigned int flags; | |
60 | }; | |
61 | ||
62 | extern int git_iterator_for_nothing( | |
63 | git_iterator **out, | |
64 | git_iterator_flag_t flags, | |
65 | const char *start, | |
66 | const char *end); | |
67 | ||
68 | /* tree iterators will match the ignore_case value from the index of the | |
69 | * repository, unless you override with a non-zero flag value | |
70 | */ | |
71 | extern int git_iterator_for_tree( | |
72 | git_iterator **out, | |
73 | git_tree *tree, | |
74 | git_iterator_flag_t flags, | |
75 | const char *start, | |
76 | const char *end); | |
77 | ||
78 | /* index iterators will take the ignore_case value from the index; the | |
79 | * ignore_case flags are not used | |
80 | */ | |
81 | extern int git_iterator_for_index( | |
82 | git_iterator **out, | |
83 | git_index *index, | |
84 | git_iterator_flag_t flags, | |
85 | const char *start, | |
86 | const char *end); | |
87 | ||
88 | extern int git_iterator_for_workdir_ext( | |
89 | git_iterator **out, | |
90 | git_repository *repo, | |
91 | const char *repo_workdir, | |
92 | git_index *index, | |
93 | git_tree *tree, | |
94 | git_iterator_flag_t flags, | |
95 | const char *start, | |
96 | const char *end); | |
97 | ||
98 | /* workdir iterators will match the ignore_case value from the index of the | |
99 | * repository, unless you override with a non-zero flag value | |
100 | */ | |
101 | GIT_INLINE(int) git_iterator_for_workdir( | |
102 | git_iterator **out, | |
103 | git_repository *repo, | |
104 | git_index *index, | |
105 | git_tree *tree, | |
106 | git_iterator_flag_t flags, | |
107 | const char *start, | |
108 | const char *end) | |
109 | { | |
110 | return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, flags, start, end); | |
111 | } | |
112 | ||
113 | /* for filesystem iterators, you have to explicitly pass in the ignore_case | |
114 | * behavior that you desire | |
115 | */ | |
116 | extern int git_iterator_for_filesystem( | |
117 | git_iterator **out, | |
118 | const char *root, | |
119 | git_iterator_flag_t flags, | |
120 | const char *start, | |
121 | const char *end); | |
122 | ||
123 | extern void git_iterator_free(git_iterator *iter); | |
124 | ||
125 | /* Return a git_index_entry structure for the current value the iterator | |
126 | * is looking at or NULL if the iterator is at the end. | |
127 | * | |
128 | * The entry may noy be fully populated. Tree iterators will only have a | |
129 | * value mode, OID, and path. Workdir iterators will not have an OID (but | |
130 | * you can use `git_iterator_current_oid()` to calculate it on demand). | |
131 | * | |
132 | * You do not need to free the entry. It is still "owned" by the iterator. | |
133 | * Once you call `git_iterator_advance()` then the old entry is no longer | |
134 | * guaranteed to be valid - it may be freed or just overwritten in place. | |
135 | */ | |
136 | GIT_INLINE(int) git_iterator_current( | |
137 | const git_index_entry **entry, git_iterator *iter) | |
138 | { | |
139 | return iter->cb->current(entry, iter); | |
140 | } | |
141 | ||
142 | /** | |
143 | * Advance to the next item for the iterator. | |
144 | * | |
145 | * If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item. If | |
146 | * GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree | |
147 | * item will skip over all the items under that tree. | |
148 | */ | |
149 | GIT_INLINE(int) git_iterator_advance( | |
150 | const git_index_entry **entry, git_iterator *iter) | |
151 | { | |
152 | return iter->cb->advance(entry, iter); | |
153 | } | |
154 | ||
155 | /** | |
156 | * Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set). | |
157 | * | |
158 | * git_iterator_advance() steps through all items being iterated over | |
159 | * (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES), | |
160 | * but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next | |
161 | * sibling of a tree instead of going to the first child of the tree. In | |
162 | * that case, use this function to advance to the first child of the tree. | |
163 | * | |
164 | * If the current item is not a tree, this is a no-op. | |
165 | * | |
166 | * For filesystem and working directory iterators, a tree (i.e. directory) | |
167 | * can be empty. In that case, this function returns GIT_ENOTFOUND and | |
168 | * does not advance. That can't happen for tree and index iterators. | |
169 | */ | |
170 | GIT_INLINE(int) git_iterator_advance_into( | |
171 | const git_index_entry **entry, git_iterator *iter) | |
172 | { | |
173 | return iter->cb->advance_into(entry, iter); | |
174 | } | |
175 | ||
176 | /** | |
177 | * Advance into a tree or skip over it if it is empty. | |
178 | * | |
179 | * Because `git_iterator_advance_into` may return GIT_ENOTFOUND if the | |
180 | * directory is empty (only with filesystem and working directory | |
181 | * iterators) and a common response is to just call `git_iterator_advance` | |
182 | * when that happens, this bundles the two into a single simple call. | |
183 | */ | |
184 | GIT_INLINE(int) git_iterator_advance_into_or_over( | |
185 | const git_index_entry **entry, git_iterator *iter) | |
186 | { | |
187 | int error = iter->cb->advance_into(entry, iter); | |
188 | if (error == GIT_ENOTFOUND) { | |
189 | giterr_clear(); | |
190 | error = iter->cb->advance(entry, iter); | |
191 | } | |
192 | return error; | |
193 | } | |
194 | ||
195 | /* Seek is currently unimplemented */ | |
196 | GIT_INLINE(int) git_iterator_seek( | |
197 | git_iterator *iter, const char *prefix) | |
198 | { | |
199 | return iter->cb->seek(iter, prefix); | |
200 | } | |
201 | ||
202 | /** | |
203 | * Go back to the start of the iteration. | |
204 | * | |
205 | * This resets the iterator to the start of the iteration. It also allows | |
206 | * you to reset the `start` and `end` pathname boundaries of the iteration | |
207 | * when doing so. | |
208 | */ | |
209 | GIT_INLINE(int) git_iterator_reset( | |
210 | git_iterator *iter, const char *start, const char *end) | |
211 | { | |
212 | return iter->cb->reset(iter, start, end); | |
213 | } | |
214 | ||
215 | /** | |
216 | * Check if the iterator is at the end | |
217 | * | |
218 | * @return 0 if not at end, >0 if at end | |
219 | */ | |
220 | GIT_INLINE(int) git_iterator_at_end(git_iterator *iter) | |
221 | { | |
222 | return iter->cb->at_end(iter); | |
223 | } | |
224 | ||
225 | GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter) | |
226 | { | |
227 | return iter->type; | |
228 | } | |
229 | ||
230 | GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter) | |
231 | { | |
232 | return iter->repo; | |
233 | } | |
234 | ||
235 | GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter) | |
236 | { | |
237 | return iter->flags; | |
238 | } | |
239 | ||
240 | GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter) | |
241 | { | |
242 | return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0); | |
243 | } | |
244 | ||
245 | extern int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case); | |
246 | ||
247 | extern int git_iterator_current_tree_entry( | |
248 | const git_tree_entry **entry_out, git_iterator *iter); | |
249 | ||
250 | extern int git_iterator_current_parent_tree( | |
251 | const git_tree **tree_out, git_iterator *iter, const char *parent_path); | |
252 | ||
253 | extern bool git_iterator_current_is_ignored(git_iterator *iter); | |
254 | ||
255 | extern bool git_iterator_current_tree_is_ignored(git_iterator *iter); | |
256 | ||
257 | extern int git_iterator_cmp( | |
258 | git_iterator *iter, const char *path_prefix); | |
259 | ||
260 | /** | |
261 | * Get full path of the current item from a workdir iterator. This will | |
262 | * return NULL for a non-workdir iterator. The git_buf is still owned by | |
263 | * the iterator; this is exposed just for efficiency. | |
264 | */ | |
265 | extern int git_iterator_current_workdir_path( | |
266 | git_buf **path, git_iterator *iter); | |
267 | ||
268 | /* Return index pointer if index iterator, else NULL */ | |
269 | extern git_index *git_iterator_get_index(git_iterator *iter); | |
270 | ||
271 | typedef enum { | |
272 | GIT_ITERATOR_STATUS_NORMAL = 0, | |
273 | GIT_ITERATOR_STATUS_IGNORED = 1, | |
274 | GIT_ITERATOR_STATUS_EMPTY = 2 | |
275 | } git_iterator_status_t; | |
276 | ||
277 | /* Advance over a directory and check if it contains no files or just | |
278 | * ignored files. | |
279 | * | |
280 | * In a tree or the index, all directories will contain files, but in the | |
281 | * working directory it is possible to have an empty directory tree or a | |
282 | * tree that only contains ignored files. Many Git operations treat these | |
283 | * cases specially. This advances over a directory (presumably an | |
284 | * untracked directory) but checks during the scan if there are any files | |
285 | * and any non-ignored files. | |
286 | */ | |
287 | extern int git_iterator_advance_over_with_status( | |
288 | const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iter); | |
289 | ||
290 | /** | |
291 | * Retrieve the index stored in the iterator. | |
292 | * | |
293 | * Only implemented for the workdir iterator | |
294 | */ | |
295 | extern int git_iterator_index(git_index **out, git_iterator *iter); | |
296 | ||
297 | #endif |