diff --git a/include/git2/config.h b/include/git2/config.h index 28216467b..f14415148 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -350,7 +350,7 @@ GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const cha * @param regexp regular expression to filter which variables we're * interested in. Use NULL to indicate all */ -GIT_EXTERN(int) git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); +GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); /** * Return the current entry and advance the iterator diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index e369fb8ab..7572ace51 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -58,8 +58,6 @@ struct git_config_backend { /* Open means open the file/database and parse if necessary */ int (*open)(struct git_config_backend *, git_config_level_t level); int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry); - int (*get_multivar_foreach)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload); - int (*get_multivar)(git_config_iterator **, struct git_config_backend *, const char *name, const char *regexp); int (*set)(struct git_config_backend *, const char *key, const char *value); int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value); int (*del)(struct git_config_backend *, const char *key); diff --git a/src/config.c b/src/config.c index ae4e4816a..c98d6a52d 100644 --- a/src/config.c +++ b/src/config.c @@ -747,7 +747,7 @@ int git_config_get_multivar_foreach( git_config_iterator *iter; git_config_entry *entry; - if ((err = git_config_get_multivar(&iter, cfg, name, regexp)) < 0) + if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0) return err; found = 0; @@ -771,92 +771,82 @@ int git_config_get_multivar_foreach( typedef struct { git_config_iterator parent; - git_config_iterator *current; + git_config_iterator *iter; char *name; - char *regexp; - const git_config *cfg; - size_t i; + regex_t regex; + int have_regex; } multivar_iter; static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; - git_config_iterator *current = iter->current; - file_internal *internal; - git_config_backend *backend; - size_t i; int error = 0; - if (current != NULL && - (error = current->next(entry, current)) == 0) { - return 0; - } - - if (error < 0 && error != GIT_ITEROVER) - return error; - - do { - if (find_next_backend(&i, iter->cfg, iter->i) < 0) - return GIT_ITEROVER; - - internal = git_vector_get(&iter->cfg->files, i - 1); - backend = internal->file; - iter->i = i - 1; - - if (iter->current) - iter->current->free(current); - - iter->current = NULL; - error = backend->get_multivar(&iter->current, backend, iter->name, iter->regexp); - if (error == GIT_ENOTFOUND) + while ((error = iter->iter->next(entry, iter->iter)) == 0) { + if (git__strcmp(iter->name, (*entry)->name)) continue; - if (error < 0) - return error; + if (!iter->have_regex) + return 0; - return iter->current->next(entry, iter->current); + if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0) + return 0; + } - } while(1); - - return GIT_ITEROVER; + return error; } void multivar_iter_free(git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; - if (iter->current) - iter->current->free(iter->current); + iter->iter->free(iter->iter); git__free(iter->name); - git__free(iter->regexp); + regfree(&iter->regex); git__free(iter); } -int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) +int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) { - multivar_iter *iter; + multivar_iter *iter = NULL; + git_config_iterator *inner = NULL; + int error; + + if ((error = git_config_iterator_new(&inner, cfg)) < 0) + return error; iter = git__calloc(1, sizeof(multivar_iter)); GITERR_CHECK_ALLOC(iter); - iter->name = git__strdup(name); - GITERR_CHECK_ALLOC(iter->name); + if ((error = git_config__normalize_name(name, &iter->name)) < 0) + goto on_error; if (regexp != NULL) { - iter->regexp = git__strdup(regexp); - GITERR_CHECK_ALLOC(iter->regexp); + error = regcomp(&iter->regex, regexp, REG_EXTENDED); + if (error < 0) { + giterr_set_regex(&iter->regex, error); + error = -1; + regfree(&iter->regex); + goto on_error; + } + + iter->have_regex = 1; } + iter->iter = inner; iter->parent.free = multivar_iter_free; iter->parent.next = multivar_iter_next; - iter->i = cfg->files.length; - iter->cfg = cfg; - *out = (git_config_iterator *) iter; return 0; + +on_error: + + inner->free(inner); + git__free(iter); + return error; } int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) @@ -1125,6 +1115,41 @@ fail_parse: return -1; } +/* Take something the user gave us and make it nice for our hash function */ +int git_config__normalize_name(const char *in, char **out) +{ + char *name, *fdot, *ldot; + + assert(in && out); + + name = git__strdup(in); + GITERR_CHECK_ALLOC(name); + + fdot = strchr(name, '.'); + ldot = strrchr(name, '.'); + + if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1]) + goto invalid; + + /* Validate and downcase up to first dot and after last dot */ + if (git_config_file_normalize_section(name, fdot) < 0 || + git_config_file_normalize_section(ldot + 1, NULL) < 0) + goto invalid; + + /* If there is a middle range, make sure it doesn't have newlines */ + while (fdot < ldot) + if (*fdot++ == '\n') + goto invalid; + + *out = name; + return 0; + +invalid: + git__free(name); + giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in); + return GIT_EINVALIDSPEC; +} + struct rename_data { git_config *config; git_buf *name; diff --git a/src/config.h b/src/config.h index c5c11ae14..85db5e3e1 100644 --- a/src/config.h +++ b/src/config.h @@ -49,4 +49,7 @@ extern int git_config_rename_section( */ extern int git_config_file__ondisk(struct git_config_backend **out, const char *path); +extern int git_config__normalize_name(const char *in, char **out); + + #endif diff --git a/src/config_file.c b/src/config_file.c index 7c22be424..21dc22329 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -136,41 +136,6 @@ int git_config_file_normalize_section(char *start, char *end) return 0; } -/* Take something the user gave us and make it nice for our hash function */ -static int normalize_name(const char *in, char **out) -{ - char *name, *fdot, *ldot; - - assert(in && out); - - name = git__strdup(in); - GITERR_CHECK_ALLOC(name); - - fdot = strchr(name, '.'); - ldot = strrchr(name, '.'); - - if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1]) - goto invalid; - - /* Validate and downcase up to first dot and after last dot */ - if (git_config_file_normalize_section(name, fdot) < 0 || - git_config_file_normalize_section(ldot + 1, NULL) < 0) - goto invalid; - - /* If there is a middle range, make sure it doesn't have newlines */ - while (fdot < ldot) - if (*fdot++ == '\n') - goto invalid; - - *out = name; - return 0; - -invalid: - git__free(name); - giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in); - return GIT_EINVALIDSPEC; -} - static void free_vars(git_strmap *values) { cvar_t *var = NULL; @@ -314,7 +279,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val khiter_t pos; int rval, ret; - if ((rval = normalize_name(name, &key)) < 0) + if ((rval = git_config__normalize_name(name, &key)) < 0) return rval; /* @@ -397,7 +362,7 @@ static int config_get(const git_config_backend *cfg, const char *name, const git khiter_t pos; int error; - if ((error = normalize_name(name, &key)) < 0) + if ((error = git_config__normalize_name(name, &key)) < 0) return error; pos = git_strmap_lookup_index(b->values, key); @@ -412,162 +377,6 @@ static int config_get(const git_config_backend *cfg, const char *name, const git return 0; } -typedef struct { - git_config_iterator parent; - cvar_t *var; - regex_t regex; - int have_regex; -} foreach_iter; - -static void foreach_iter_free(git_config_iterator *_iter) -{ - foreach_iter *iter = (foreach_iter *) _iter; - - if (iter->have_regex) - regfree(&iter->regex); - - git__free(iter); -} - -static int foreach_iter_next(git_config_entry **out, git_config_iterator *_iter) -{ - foreach_iter *iter = (foreach_iter *) _iter; - - cvar_t* var = iter->var; - - - if (var == NULL) - return GIT_ITEROVER; - - if (!iter->have_regex) { - *out = var->entry; - iter->var = var->next; - return 0; - } - - /* For the regex case, we must loop until we find something we like */ - do { - git_config_entry *entry = var->entry; - regex_t *regex = &iter->regex;; - if (regexec(regex, entry->value, 0, NULL, 0) == 0) { - *out = entry; - iter->var = var->next; - return 0; - } - var = var->next; - } while(var != NULL); - - return GIT_ITEROVER; -} - -static int config_get_multivar(git_config_iterator **out, git_config_backend *_backend, - const char *name, const char *regexp) -{ - foreach_iter *iter; - diskfile_backend *b = (diskfile_backend *) _backend; - - char *key; - khiter_t pos; - int error = 0; - - if ((error = normalize_name(name, &key)) < 0) - return error; - - pos = git_strmap_lookup_index(b->values, key); - git__free(key); - - if (!git_strmap_valid_index(b->values, pos)) - return GIT_ENOTFOUND; - - iter = git__calloc(1, sizeof(foreach_iter)); - GITERR_CHECK_ALLOC(iter); - - iter->var = git_strmap_value_at(b->values, pos); - - if (regexp != NULL) { - int result; - - result = regcomp(&iter->regex, regexp, REG_EXTENDED); - if (result < 0) { - giterr_set_regex(&iter->regex, result); - regfree(&iter->regex); - return -1; - } - iter->have_regex = 1; - } - - iter->parent.free = foreach_iter_free; - iter->parent.next = foreach_iter_next; - - *out = (git_config_iterator *) iter; - - return 0; - } - -static int config_get_multivar_foreach( - git_config_backend *cfg, - const char *name, - const char *regex_str, - int (*fn)(const git_config_entry *, void *), - void *data) -{ - cvar_t *var; - diskfile_backend *b = (diskfile_backend *)cfg; - char *key; - khiter_t pos; - int error; - - if ((error = normalize_name(name, &key)) < 0) - return error; - - pos = git_strmap_lookup_index(b->values, key); - git__free(key); - - if (!git_strmap_valid_index(b->values, pos)) - return GIT_ENOTFOUND; - - var = git_strmap_value_at(b->values, pos); - - if (regex_str != NULL) { - regex_t regex; - int result; - - /* regex matching; build the regex */ - result = regcomp(®ex, regex_str, REG_EXTENDED); - if (result < 0) { - giterr_set_regex(®ex, result); - regfree(®ex); - return -1; - } - - /* and throw the callback only on the variables that - * match the regex */ - do { - if (regexec(®ex, var->entry->value, 0, NULL, 0) == 0) { - /* early termination by the user is not an error; - * just break and return successfully */ - if (fn(var->entry, data)) - break; - } - - var = var->next; - } while (var != NULL); - regfree(®ex); - } else { - /* no regex; go through all the variables */ - do { - /* early termination by the user is not an error; - * just break and return successfully */ - if (fn(var->entry, data) < 0) - break; - - var = var->next; - } while (var != NULL); - } - - return 0; -} - static int config_set_multivar( git_config_backend *cfg, const char *name, const char *regexp, const char *value) { @@ -581,7 +390,7 @@ static int config_set_multivar( assert(regexp); - if ((result = normalize_name(name, &key)) < 0) + if ((result = git_config__normalize_name(name, &key)) < 0) return result; pos = git_strmap_lookup_index(b->values, key); @@ -654,7 +463,7 @@ static int config_delete(git_config_backend *cfg, const char *name) int result; khiter_t pos; - if ((result = normalize_name(name, &key)) < 0) + if ((result = git_config__normalize_name(name, &key)) < 0) return result; pos = git_strmap_lookup_index(b->values, key); @@ -694,8 +503,6 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->parent.open = config_open; backend->parent.get = config_get; - backend->parent.get_multivar_foreach = config_get_multivar_foreach; - backend->parent.get_multivar = config_get_multivar; backend->parent.set = config_set; backend->parent.set_multivar = config_set_multivar; backend->parent.del = config_delete; diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index afb993c18..0d552d65e 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -76,7 +76,7 @@ static void check_get_multivar(git_config *cfg, int expected) git_config_entry *entry; int n = 0; - cl_git_pass(git_config_get_multivar(&iter, cfg, _name, NULL)); + cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL)); while (git_config_next(&entry, iter) == 0) n++;