Commit 5e0dc4af013e23d0cbc737d8ab2756aaf38e1516

Carlos Martín Nieto 2012-02-04T23:18:30

Support getting multivars

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);
+}