Commit 5880962d90958bc37c08c21d37c9da480ef20e7f

Carlos Martín Nieto 2013-08-09T09:05:19

config: introduce _iterator_new() As the name suggests, it iterates over all the entries

diff --git a/include/git2/config.h b/include/git2/config.h
index b338e0c..aed720f 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -453,6 +453,17 @@ GIT_EXTERN(int) git_config_foreach(
 	void *payload);
 
 /**
+ * Iterate over all the config variables
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ */
+GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
+
+/**
  * Perform an operation on each config variable matching a regular expression.
  *
  * This behaviors like `git_config_foreach` with an additional filter of a
diff --git a/src/config.c b/src/config.c
index 42273cd..f7ea6f7 100644
--- a/src/config.c
+++ b/src/config.c
@@ -315,6 +315,99 @@ int git_config_refresh(git_config *cfg)
  * Loop over all the variables
  */
 
+typedef struct {
+	git_config_iterator parent;
+	git_config_iterator *current;
+	const git_config *cfg;
+	size_t i;
+} all_iter;
+
+static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
+{
+	file_internal *internal;
+
+	for (; i > 0; --i) {
+		internal = git_vector_get(&cfg->files, i - 1);
+		if (!internal || !internal->file)
+			continue;
+
+		*out = i;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+	file_internal *internal;
+	git_config_backend *backend;
+	size_t i;
+	int error = 0;
+
+	if (iter->current != NULL &&
+	    (error = iter->current->next(entry, iter->current)) == 0) {
+		return 0;
+	}
+
+	if (error < 0 && error != GIT_ITEROVER)
+		return error;
+
+	do {
+		if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+			return GIT_ITEROVER;
+
+		internal = git_vector_get(&iter->cfg->files, i - 1);
+		backend = internal->file;
+		iter->i = i - 1;
+
+		if (iter->current)
+			iter->current->free(iter->current);
+
+		iter->current = NULL;
+		error = backend->iterator(&iter->current, backend);
+		if (error == GIT_ENOTFOUND)
+			continue;
+
+		if (error < 0)
+			return error;
+
+		return iter->current->next(entry, iter->current);
+
+	} while(1);
+
+	return GIT_ITEROVER;
+}
+
+static void all_iter_free(git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+
+	if (iter->current)
+		iter->current->free(iter->current);
+
+	git__free(iter);
+}
+
+int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+{
+	all_iter *iter;
+
+	iter = git__calloc(1, sizeof(all_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	iter->parent.free = all_iter_free;
+	iter->parent.next = all_iter_next;
+
+	iter->i = cfg->files.length;
+	iter->cfg = cfg;
+
+	*out = (git_config_iterator *) iter;
+
+	return 0;
+}
+
 int git_config_foreach(
 	const git_config *cfg, git_config_foreach_cb cb, void *payload)
 {
@@ -613,30 +706,6 @@ typedef struct {
 	size_t i;
 } multivar_iter;
 
-static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
-{
-	file_internal *internal;
-
-	for (; i > 0; --i) {
-		internal = git_vector_get(&cfg->files, i - 1);
-		if (!internal || !internal->file)
-			continue;
-
-		*out = i;
-		return 0;
-	}
-
-	return -1;
-}
-
-static int multivar_iter_next_empty(git_config_entry **entry, git_config_iterator *_iter)
-{
-	GIT_UNUSED(entry);
-	GIT_UNUSED(_iter);
-
-	return GIT_ITEROVER;
-}
-
 static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
 {
 	multivar_iter *iter = (multivar_iter *) _iter;
@@ -695,7 +764,6 @@ void multivar_iter_free(git_config_iterator *_iter)
 int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
 {
 	multivar_iter *iter;
-	size_t i;
 
 	iter = git__calloc(1, sizeof(multivar_iter));
 	GITERR_CHECK_ALLOC(iter);
@@ -709,10 +777,7 @@ int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, co
 	}
 
 	iter->parent.free = multivar_iter_free;
-	if (find_next_backend(&i, cfg, cfg->files.length) < 0)
-		iter->parent.next = multivar_iter_next_empty;
-	else
-		iter->parent.next = multivar_iter_next;
+	iter->parent.next = multivar_iter_next;
 
 	iter->i = cfg->files.length;
 	iter->cfg = cfg;
diff --git a/src/config_file.c b/src/config_file.c
index 38cb9f8..4e564a5 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -279,7 +279,7 @@ static int config_iterator_next(
 
 	if (err < 0) {
 		it->next_var = NULL;
-		return -1;
+		return err;
 	}
 
 	*entry = var->entry;
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
index a18dca8..2fb511d 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -245,6 +245,36 @@ void test_config_read__foreach(void)
 	git_config_free(cfg);
 }
 
+void test_config_read__iterator(void)
+{
+	git_config *cfg;
+	git_config_iterator *iter;
+	git_config_entry *entry;
+	int count, ret;
+
+	cl_git_pass(git_config_new(&cfg));
+	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+		GIT_CONFIG_LEVEL_SYSTEM, 0));
+	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+		GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+	count = 0;
+	cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+	while ((ret = git_config_next(&entry, iter)) == 0) {
+		count++;
+	}
+
+	cl_assert_equal_i(GIT_ITEROVER, ret);
+	cl_assert_equal_i(7, count);
+
+	count = 3;
+	cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+	git_config_iterator_free(iter);
+	git_config_free(cfg);
+}
+
 static int count_cfg_entries(const git_config_entry *entry, void *payload)
 {
 	int *count = payload;