summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/config.h20
-rw-r--r--src/config.c16
-rw-r--r--src/config_file.c37
-rw-r--r--src/config_file.h17
-rw-r--r--tests-clar/config/read.c75
5 files changed, 153 insertions, 12 deletions
diff --git a/include/git2/config.h b/include/git2/config.h
index 36946c4a5..c46e7fc9d 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -33,7 +33,7 @@ struct git_config_file {
int (*set)(struct git_config_file *, const char *key, const char *value);
int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_file *, const char *key);
- int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data);
+ int (*foreach)(struct git_config_file *, const char *, int (*fn)(const char *, const char *, void *), void *data);
void (*free)(struct git_config_file *);
};
@@ -314,6 +314,24 @@ GIT_EXTERN(int) git_config_foreach(
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
+/**
+ * Perform an operation on each config variable matching a regular expression.
+ *
+ * This behaviors like `git_config_foreach` with an additional filter of a
+ * regular expression that filters which config keys are passed to the
+ * callback.
+ *
+ * @param cfg where to get the variables from
+ * @param regexp regular expression to match against config names
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ * @return 0 or the return value of the callback which didn't return 0
+ */
+GIT_EXTERN(int) git_config_foreach_match(
+ git_config *cfg,
+ const char *regexp,
+ int (*callback)(const char *var_name, const char *value, void *payload),
+ void *payload);
/**
* Query the value of a config variable and return it mapped to
diff --git a/src/config.c b/src/config.c
index d18b85c30..98fb3b20d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -136,17 +136,27 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
* Loop over all the variables
*/
-int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
+int git_config_foreach(
+ git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
+{
+ return git_config_foreach_match(cfg, NULL, fn, data);
+}
+
+int git_config_foreach_match(
+ git_config *cfg,
+ const char *regexp,
+ int (*fn)(const char *, const char *, void *),
+ void *data)
{
int ret = 0;
unsigned int i;
file_internal *internal;
git_config_file *file;
- for(i = 0; i < cfg->files.length && ret == 0; ++i) {
+ for (i = 0; i < cfg->files.length && ret == 0; ++i) {
internal = git_vector_get(&cfg->files, i);
file = internal->file;
- ret = file->foreach(file, fn, data);
+ ret = file->foreach(file, regexp, fn, data);
}
return ret;
diff --git a/src/config_file.c b/src/config_file.c
index fd1aa8d08..1f3ebfca9 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -188,25 +188,46 @@ static void backend_free(git_config_file *_backend)
git__free(backend);
}
-static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
+static int file_foreach(
+ git_config_file *backend,
+ const char *regexp,
+ int (*fn)(const char *, const char *, void *),
+ void *data)
{
diskfile_backend *b = (diskfile_backend *)backend;
cvar_t *var;
const char *key;
+ regex_t regex;
+ int result = 0;
if (!b->values)
return 0;
+ if (regexp != NULL) {
+ if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&regex, result);
+ regfree(&regex);
+ return -1;
+ }
+ }
+
git_strmap_foreach(b->values, key, var,
- do {
- if (fn(key, var->value, data) < 0)
- break;
+ for (; var != NULL; var = CVAR_LIST_NEXT(var)) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
+ continue;
- var = CVAR_LIST_NEXT(var);
- } while (var != NULL);
+ /* abort iterator on non-zero return value */
+ if ((result = fn(key, var->value, data)) != 0)
+ goto cleanup;
+ }
);
- return 0;
+cleanup:
+ if (regexp != NULL)
+ regfree(&regex);
+
+ return result;
}
static int config_set(git_config_file *cfg, const char *name, const char *value)
@@ -337,6 +358,7 @@ static int config_get_multivar(
result = regcomp(&regex, regex_str, REG_EXTENDED);
if (result < 0) {
giterr_set_regex(&regex, result);
+ regfree(&regex);
return -1;
}
@@ -396,6 +418,7 @@ static int config_set_multivar(
if (result < 0) {
git__free(key);
giterr_set_regex(&preg, result);
+ regfree(&preg);
return -1;
}
diff --git a/src/config_file.h b/src/config_file.h
index 0080b5713..c31292881 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -19,12 +19,27 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
cfg->free(cfg);
}
+GIT_INLINE(int) git_config_file_set_string(
+ git_config_file *cfg, const char *name, const char *value)
+{
+ return cfg->set(cfg, name, value);
+}
+
GIT_INLINE(int) git_config_file_foreach(
git_config_file *cfg,
int (*fn)(const char *key, const char *value, void *data),
void *data)
{
- return cfg->foreach(cfg, fn, data);
+ return cfg->foreach(cfg, NULL, fn, data);
+}
+
+GIT_INLINE(int) git_config_file_foreach_match(
+ git_config_file *cfg,
+ const char *regexp,
+ int (*fn)(const char *key, const char *value, void *data),
+ void *data)
+{
+ return cfg->foreach(cfg, regexp, fn, data);
}
#endif
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
index f33bdd89e..a8504da02 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -191,6 +191,81 @@ void test_config_read__escaping_quotes(void)
git_config_free(cfg);
}
+static int count_cfg_entries(
+ const char *var_name, const char *value, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(var_name);
+ GIT_UNUSED(value);
+ (*count)++;
+ return 0;
+}
+
+static int cfg_callback_countdown(
+ const char *var_name, const char *value, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(var_name);
+ GIT_UNUSED(value);
+ (*count)--;
+ if (*count == 0)
+ return -100;
+ return 0;
+}
+
+void test_config_read__foreach(void)
+{
+ git_config *cfg;
+ int count, ret;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ count = 0;
+ cl_git_pass(git_config_foreach(cfg, count_cfg_entries, &count));
+ cl_assert_equal_i(5, count);
+
+ count = 3;
+ cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
+ cl_assert_equal_i(-100, ret);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__foreach_match(void)
+{
+ git_config *cfg;
+ int count;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
+ cl_assert_equal_i(3, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
+ cl_assert_equal_i(0, count);
+
+ git_config_free(cfg);
+}
+
#if 0
BEGIN_TEST(config10, "a repo's config overrides the global config")