Commit 54f3a572b4fac419008afa83da56c1b0daee1257

Carlos Martín Nieto 2013-08-09T10:29:11

config: introduce a regex-filtering iterator

diff --git a/include/git2/config.h b/include/git2/config.h
index aed720f..2821646 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -464,6 +464,18 @@ GIT_EXTERN(int) git_config_foreach(
 GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
 
 /**
+ * Iterate over all the config variables whose name matches a pattern
+ *
+ * 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
+ * @param regexp regular expression to match the names
+ */
+GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
+
+/**
  * 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 f7ea6f7..9d809f8 100644
--- a/src/config.c
+++ b/src/config.c
@@ -319,6 +319,8 @@ typedef struct {
 	git_config_iterator parent;
 	git_config_iterator *current;
 	const git_config *cfg;
+	regex_t regex;
+	int has_regex;
 	size_t i;
 } all_iter;
 
@@ -380,6 +382,27 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
 	return GIT_ITEROVER;
 }
 
+static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+	int error;
+	all_iter *iter = (all_iter *) _iter;
+
+	/*
+	 * We use the "normal" function to grab the next one across
+	 * backends and then apply the regex
+	 */
+	while ((error = all_iter_next(entry, _iter)) == 0) {
+		/* skip non-matching keys if regexp was provided */
+		if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
+			continue;
+
+		/* and simply return if we like the entry's name */
+		return 0;
+	}
+
+	return error;
+}
+
 static void all_iter_free(git_config_iterator *_iter)
 {
 	all_iter *iter = (all_iter *) _iter;
@@ -390,6 +413,14 @@ static void all_iter_free(git_config_iterator *_iter)
 	git__free(iter);
 }
 
+static void all_iter_glob_free(git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+
+	regfree(&iter->regex);
+	all_iter_free(_iter);
+}
+
 int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
 {
 	all_iter *iter;
@@ -408,6 +439,36 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
 	return 0;
 }
 
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+{
+	all_iter *iter;
+	int result;
+
+	iter = git__calloc(1, sizeof(all_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	if (regexp != NULL) {
+		if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
+			giterr_set_regex(&iter->regex, result);
+			regfree(&iter->regex);
+			return -1;
+		}
+
+		iter->parent.next = all_iter_glob_next;
+	} else {
+		iter->parent.next = all_iter_next;
+	}
+
+	iter->parent.free = all_iter_glob_free;
+
+	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)
 {
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
index 2fb511d..395f1cf 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -265,6 +265,7 @@ void test_config_read__iterator(void)
 		count++;
 	}
 
+	git_config_iterator_free(iter);
 	cl_assert_equal_i(GIT_ITEROVER, ret);
 	cl_assert_equal_i(7, count);
 
@@ -318,6 +319,38 @@ void test_config_read__foreach_match(void)
 	git_config_free(cfg);
 }
 
+static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
+{
+	git_config_iterator *iter;
+	git_config_entry *entry;
+	int count, error;
+
+	cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
+
+	count = 0;
+	while ((error = git_config_next(&entry, iter)) == 0)
+		count++;
+
+	cl_assert_equal_i(GIT_ITEROVER, error);
+	cl_assert_equal_i(expected, count);
+	git_config_iterator_free(iter);
+}
+
+void test_config_read__iterator_glob(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+	check_glob_iter(cfg, "core.*", 3);
+	check_glob_iter(cfg, "remote\\.ab.*", 2);
+	check_glob_iter(cfg, ".*url$", 2);
+	check_glob_iter(cfg, ".*dummy.*", 2);
+	check_glob_iter(cfg, ".*nomatch.*", 0);
+
+	git_config_free(cfg);
+}
+
 void test_config_read__whitespace_not_required_around_assignment(void)
 {
 	git_config *cfg;