From 3005855f7e3980185adc63a68c5b8b5f9e3b506f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 5 Feb 2012 00:29:26 +0100 Subject: [PATCH] Implement setting multivars --- include/git2/config.h | 7 ++ src/config.c | 18 ++++++ src/config_file.c | 111 +++++++++++++++++++++++++++++--- tests-clar/config/multivar.c | 40 +++++++++++- tests/resources/config/config11 | Bin 104 -> 100 bytes 5 files changed, 165 insertions(+), 11 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 1f037c8ee..82d9de870 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -31,6 +31,7 @@ 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 (*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); void (*free)(struct git_config_file *); @@ -255,6 +256,12 @@ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value */ GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value); + +/** + * Set a multivar + */ +GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); + /** * Delete a config variable * diff --git a/src/config.c b/src/config.c index ccc7362ac..4ff1b2e72 100644 --- a/src/config.c +++ b/src/config.c @@ -364,6 +364,24 @@ int git_config_get_multivar(git_config *cfg, const char *name, const char *regex return GIT_SUCCESS; } +int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) +{ + file_internal *internal; + git_config_file *file; + int error = GIT_ENOTFOUND; + unsigned int i; + + for (i = cfg->files.length; i > 0; --i) { + internal = git_vector_get(&cfg->files, i - 1); + file = internal->file; + error = file->set_multivar(file, name, regexp, value); + if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) + git__rethrow(error, "Failed to replace 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 3d29b202b..346bb7a6f 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -84,7 +84,7 @@ typedef struct { static int config_parse(diskfile_backend *cfg_file); static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value); -static int config_write(diskfile_backend *cfg, const char *key, const char *value); +static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value); static void cvar_free(cvar_t *var) { @@ -240,7 +240,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) git__free(existing->value); existing->value = tmp; - return config_write(b, existing->key, value); + return config_write(b, existing->key, NULL, value); } var = git__malloc(sizeof(cvar_t)); @@ -263,7 +263,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value) cvar_free(old_value); - error = config_write(b, key, value); + error = config_write(b, key, NULL, value); out: if (error < GIT_SUCCESS) @@ -314,7 +314,7 @@ static int config_get_multivar(git_config_file *cfg, const char *name, const cha return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name); if (regexp != NULL) { - error = regcomp(&preg, regexp, 0); + error = regcomp(&preg, regexp, REG_EXTENDED); if (error < 0) return git__throw(GIT_EINVALIDARGS, "Failed to compile regex"); } @@ -335,6 +335,88 @@ static int config_get_multivar(git_config_file *cfg, const char *name, const cha return error; } +static int config_set_multivar(git_config_file *cfg, const char *name, const char *regexp, const char *value) +{ + int error; + cvar_t *var; + diskfile_backend *b = (diskfile_backend *)cfg; + char *key; + regex_t preg; + + if (regexp == NULL) + return git__throw(GIT_EINVALIDARGS, "No regex supplied"); + + if ((error = normalize_name(name, &key)) < GIT_SUCCESS) + return error; + + var = git_hashtable_lookup(b->values, key); + free(key); + + if (var == NULL) + return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name); + + error = regcomp(&preg, regexp, REG_EXTENDED); + if (error < 0) + return git__throw(GIT_EINVALIDARGS, "Failed to compile regex"); + + + /* "^$" means we need to addd */ + if (!regexec(&preg, "", 0, NULL, 0)) { + cvar_t *newvar = git__malloc(sizeof(cvar_t)); + if (newvar == NULL) { + error = GIT_ENOMEM; + goto exit; + } + + memset(newvar, 0x0, sizeof(cvar_t)); + newvar->key = git__strdup(var->key); + if (newvar->key == NULL) { + error = GIT_ENOMEM; + goto exit; + } + newvar->value = git__strdup(value); + if (newvar->value == NULL) { + error = GIT_ENOMEM; + goto exit; + } + + while (var->next != NULL) { + var = var->next; + } + + var->next = newvar; + error = config_write(b, var->key, &preg, value); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to update value in file"); + goto exit; + } + } + + do { + if (!regexec(&preg, var->value, 0, NULL, 0)) { + char *tmp = git__strdup(value); + if (tmp == NULL) { + error = GIT_ENOMEM; + goto exit; + } + + free(var->value); + var->value = tmp; + error = config_write(b, var->key, &preg, var->value); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to update value in file"); + goto exit; + } + } + + var = var->next; + } while (var != NULL); + + exit: + regfree(&preg); + return error; +} + static int config_delete(git_config_file *cfg, const char *name) { int error; @@ -359,7 +441,7 @@ static int config_delete(git_config_file *cfg, const char *name) if ((error = git_hashtable_remove2(b->values, var->key, (void **)&old_value)) < GIT_SUCCESS) return git__rethrow(error, "Failed to remove %s from hashtable", key); - error = config_write(b, var->key, NULL); + error = config_write(b, var->key, NULL, NULL); cvar_free(old_value); return error; @@ -385,6 +467,7 @@ int git_config_file__ondisk(git_config_file **out, const char *path) backend->parent.get = config_get; backend->parent.get_multivar = config_get_multivar; backend->parent.set = config_set; + backend->parent.set_multivar = config_set_multivar; backend->parent.del = config_delete; backend->parent.foreach = file_foreach; backend->parent.free = backend_free; @@ -874,7 +957,7 @@ static int write_section(git_filebuf *file, const char *key) /* * This is pretty much the parsing, except we write out anything we don't have */ -static int config_write(diskfile_backend *cfg, const char *key, const char* value) +static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value) { int error = GIT_SUCCESS, c; int section_matches = 0, last_section_matched = 0; @@ -960,6 +1043,10 @@ static int config_write(diskfile_backend *cfg, const char *key, const char* valu if (!last_section_matched) { cfg_consume_line(cfg); break; + } else { + /* As a last attempt, if we were given "^$", we should add it */ + if (preg != NULL && regexec(preg, "", 0, NULL, 0)) + break; } } else { int cmp = -1; @@ -968,6 +1055,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const char* valu if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS) cmp = strcasecmp(name, var_name); + if (preg != NULL) + cmp = regexec(preg, var_value, 0, NULL, 0); + git__free(var_name); git__free(var_value); @@ -1034,8 +1124,10 @@ static int config_write(diskfile_backend *cfg, const char *key, const char* valu /* And now if we just need to add a variable */ if (section_matches) { - error = git_filebuf_printf(&file, "\t%s = %s\n", name, value); - goto cleanup; + if (preg == NULL || !regexec(preg, "", 0, NULL, 0)) { + error = git_filebuf_printf(&file, "\t%s = %s\n", name, value); + goto cleanup; + } } /* Or maybe we need to write out a whole section */ @@ -1043,7 +1135,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const char* valu if (error < GIT_SUCCESS) git__rethrow(error, "Failed to write new section"); - error = git_filebuf_printf(&file, "\t%s = %s\n", name, value); + if (preg == NULL || !regexec(preg, "", 0, NULL, 0)) + error = git_filebuf_printf(&file, "\t%s = %s\n", name, value); cleanup: git__free(section); git__free(current_section); diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index 48d284da2..4cf5a37d6 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -4,7 +4,7 @@ static int mv_read_cb(const char *name, const char *GIT_UNUSED(value), void *dat { int *n = (int *) data; - if (!strcmp(name, "remote.fancy.fetch")) + if (!strcmp(name, "remote.fancy.url")) (*n)++; return 0; @@ -35,7 +35,7 @@ static int cb(const char *GIT_UNUSED(val), void *data) void test_config_multivar__get(void) { git_config *cfg; - const char *name = "remote.fancy.fetch"; + const char *name = "remote.fancy.url"; int n; cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11"))); @@ -50,3 +50,39 @@ void test_config_multivar__get(void) git_config_free(cfg); } + +void test_config_multivar__add(void) +{ + git_config *cfg; + const char *name = "remote.fancy.url"; + int n; + + cl_fixture_sandbox("config"); + cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); + cl_git_pass(git_config_set_multivar(cfg, name, "^$", "git://git.otherplace.org/libgit2")); + + n = 0; + cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n)); + cl_assert(n == 3); + + n = 0; + cl_git_pass(git_config_get_multivar(cfg, name, "otherplace", cb, &n)); + cl_assert(n == 1); + + git_config_free(cfg); + + /* We know it works in memory, let's see if the file is written correctly */ + + cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); + + n = 0; + cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n)); + cl_assert(n == 3); + + + n = 0; + cl_git_pass(git_config_get_multivar(cfg, name, "otherplace", cb, &n)); + cl_assert(n == 1); + + git_config_free(cfg); +} diff --git a/tests/resources/config/config11 b/tests/resources/config/config11 index bda653bbe794205e72dc1070879bb40debc65c4a..880c945897b08ef2f3afc4752f9c2e11819b31e8 100644 GIT binary patch delta 18 Wcmc~unIOtsT9h+UN&`%}D*^yHO9kox delta 22 YcmYevm>|lUmRgdWF;PMT%5YW$096