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