diff options
-rw-r--r-- | include/git2/config.h | 7 | ||||
-rw-r--r-- | src/config.c | 27 | ||||
-rw-r--r-- | src/config_file.c | 42 | ||||
-rw-r--r-- | tests-clar/config/multivar.c | 28 |
4 files changed, 104 insertions, 0 deletions
diff --git a/include/git2/config.h b/include/git2/config.h index afa661fc5..1f037c8ee 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 490d8b52d..ccc7362ac 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 e738064f2..3d29b202b 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 dbb7c8af9..48d284da2 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); +} |