Support getting multivars
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
diff --git a/include/git2/config.h b/include/git2/config.h
index afa661f..1f037c8 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -29,6 +29,7 @@ struct git_config_file {
/* Open means open the file/database and parse if necessary */
int (*open)(struct git_config_file *);
int (*get)(struct git_config_file *, const char *key, const char **value);
+ int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data);
int (*set)(struct git_config_file *, const char *key, 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);
@@ -206,6 +207,12 @@ GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out)
GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out);
/**
+ * Get each value of a multivar. The callback will be called on each
+ * variable found
+ */
+GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data);
+
+/**
* Set the value of an integer config variable.
*
* @param cfg where to look for the variable
diff --git a/src/config.c b/src/config.c
index 490d8b5..ccc7362 100644
--- a/src/config.c
+++ b/src/config.c
@@ -337,6 +337,33 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out)
return git__throw(error, "Config value '%s' not found", name);
}
+int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
+ int (*fn)(const char *value, void *data), void *data)
+{
+ file_internal *internal;
+ git_config_file *file;
+ int error = GIT_ENOTFOUND;
+ unsigned int i;
+
+
+ if (cfg->files.length == 0)
+ return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
+
+ /*
+ * This loop runs the "wrong" way 'round because we need to
+ * look at every value from the most general to most specific
+ */
+ for (i = cfg->files.length; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ file = internal->file;
+ error = file->get_multivar(file, name, regexp, fn, data);
+ if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
+ git__rethrow(error, "Failed to get multivar");
+ }
+
+ return GIT_SUCCESS;
+}
+
int git_config_find_global_r(git_buf *path)
{
return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
diff --git a/src/config_file.c b/src/config_file.c
index e738064..3d29b20 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -15,6 +15,8 @@
#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
typedef struct cvar_t {
struct cvar_t *next;
@@ -294,6 +296,45 @@ static int config_get(git_config_file *cfg, const char *name, const char **out)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
}
+static int config_get_multivar(git_config_file *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data)
+{
+ cvar_t *var;
+ int error = GIT_SUCCESS;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ regex_t preg;
+
+ if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
+ return error;
+
+ var = git_hashtable_lookup(b->values, key);
+ git__free(key);
+
+ if (var == NULL)
+ return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+
+ if (regexp != NULL) {
+ error = regcomp(&preg, regexp, 0);
+ if (error < 0)
+ return git__throw(GIT_EINVALIDARGS, "Failed to compile regex");
+ }
+
+ do {
+ if (regexp == NULL || !regexec(&preg, var->value, 0, NULL, 0)) {
+ error = fn(var->value, data);
+ if (error < GIT_SUCCESS)
+ goto exit;
+ }
+
+ var = var->next;
+ } while (var != NULL);
+
+ exit:
+ if (regexp != NULL)
+ regfree(&preg);
+ return error;
+}
+
static int config_delete(git_config_file *cfg, const char *name)
{
int error;
@@ -342,6 +383,7 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
backend->parent.open = config_open;
backend->parent.get = config_get;
+ backend->parent.get_multivar = config_get_multivar;
backend->parent.set = config_set;
backend->parent.del = config_delete;
backend->parent.foreach = file_foreach;
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
index dbb7c8a..48d284d 100644
--- a/tests-clar/config/multivar.c
+++ b/tests-clar/config/multivar.c
@@ -22,3 +22,31 @@ void test_config_multivar__foreach(void)
git_config_free(cfg);
}
+
+static int cb(const char *GIT_UNUSED(val), void *data)
+{
+ int *n = (int *) data;
+
+ (*n)++;
+
+ return GIT_SUCCESS;
+}
+
+void test_config_multivar__get(void)
+{
+ git_config *cfg;
+ const char *name = "remote.fancy.fetch";
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, name, "example", cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+}