config: introduce a regex-filtering iterator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
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;