summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Martí <vicent@github.com>2013-08-28 06:04:51 -0700
committerVicent Martí <vicent@github.com>2013-08-28 06:04:51 -0700
commitb8b22d774eca054fe43005accd6f3ff58fc1fb62 (patch)
tree69d507f28a168f4f7f4cd3172e331403ff14e36e
parent21a3bbe419e8edb8a119f5c2a2de85f462078d3d (diff)
parentf4be8209afd3cc996667196a1e437aac21485691 (diff)
downloadlibgit2-b8b22d774eca054fe43005accd6f3ff58fc1fb62.tar.gz
Merge pull request #1772 from libgit2/config-iter
Configuration iterators redux
-rw-r--r--include/git2/config.h74
-rw-r--r--include/git2/sys/config.h30
-rw-r--r--src/config.c392
-rw-r--r--src/config.h3
-rw-r--r--src/config_file.c194
-rw-r--r--src/config_file.h4
-rw-r--r--src/diff_driver.c4
-rw-r--r--src/remote.c2
-rw-r--r--src/strmap.c32
-rw-r--r--src/strmap.h11
-rw-r--r--tests-clar/config/multivar.c76
-rw-r--r--tests-clar/config/read.c63
-rw-r--r--tests-clar/config/validkeyname.c2
13 files changed, 680 insertions, 207 deletions
diff --git a/include/git2/config.h b/include/git2/config.h
index 827d43544..f14415148 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -61,6 +61,7 @@ typedef struct {
} git_config_entry;
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
+typedef struct git_config_iterator git_config_iterator;
typedef enum {
GIT_CVAR_FALSE = 0,
@@ -327,7 +328,7 @@ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char
GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
/**
- * Get each value of a multivar.
+ * Get each value of a multivar in a foreach callback
*
* The callback will be called on each variable found
*
@@ -338,7 +339,34 @@ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, c
* @param callback the function to be called on each value of the variable
* @param payload opaque pointer to pass to the callback
*/
-GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+
+/**
+ * Get each value of a multivar
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ */
+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
+ *
+ * @param entry pointer to store the entry
+ * @param iter the iterator
+ * @return 0 or an error code. GIT_ITEROVER if the iteration has completed
+ */
+GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
+
+/**
+ * Free a config iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
/**
* Set the value of an integer config variable in the config file
@@ -425,6 +453,29 @@ GIT_EXTERN(int) git_config_foreach(
void *payload);
/**
+ * Iterate over all the config variables
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ */
+GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
+
+/**
+ * Iterate over all the config variables whose name matches a pattern
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ * @param regexp regular expression to match the names
+ */
+GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
+
+/**
* Perform an operation on each config variable matching a regular expression.
*
* This behaviors like `git_config_foreach` with an additional filter of a
@@ -535,6 +586,25 @@ GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
+/**
+ * Perform an operation on each config variable in given config backend
+ * matching a regular expression.
+ *
+ * This behaviors like `git_config_foreach_match` except instead of all config
+ * entries it just enumerates through the given backend entry.
+ *
+ * @param backend where to get the variables from
+ * @param regexp regular expression to match against config names (can be NULL)
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ */
+GIT_EXTERN(int) git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ int (*fn)(const git_config_entry *, void *),
+ void *data);
+
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 11e59cf03..7572ace51 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -21,6 +21,33 @@
GIT_BEGIN_DECL
/**
+ * Every iterator must have this struct as its first element, so the
+ * API can talk to it. You'd define your iterator as
+ *
+ * struct my_iterator {
+ * git_config_iterator parent;
+ * ...
+ * }
+ *
+ * and assign `iter->parent.backend` to your `git_config_backend`.
+ */
+struct git_config_iterator {
+ git_config_backend *backend;
+ unsigned int flags;
+
+ /**
+ * Return the current entry and advance the iterator. The
+ * memory belongs to the library.
+ */
+ int (*next)(git_config_entry **entry, git_config_iterator *iter);
+
+ /**
+ * Free the iterator
+ */
+ void (*free)(git_config_iterator *iter);
+};
+
+/**
* Generic backend that implements the interface to
* access a configuration file
*/
@@ -31,11 +58,10 @@ 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)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
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);
- int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
+ int (*iterator)(git_config_iterator **, struct git_config_backend *);
int (*refresh)(struct git_config_backend *);
void (*free)(struct git_config_backend *);
};
diff --git a/src/config.c b/src/config.c
index 2a058549f..c98d6a52d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -315,30 +315,241 @@ int git_config_refresh(git_config *cfg)
* Loop over all the variables
*/
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *current;
+ const git_config *cfg;
+ regex_t regex;
+ int has_regex;
+ size_t i;
+} all_iter;
+
+static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
+{
+ file_internal *internal;
+
+ for (; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ if (!internal || !internal->file)
+ continue;
+
+ *out = i;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+ file_internal *internal;
+ git_config_backend *backend;
+ size_t i;
+ int error = 0;
+
+ if (iter->current != NULL &&
+ (error = iter->current->next(entry, iter->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(iter->current);
+
+ iter->current = NULL;
+ error = backend->iterator(&iter->current, backend);
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0)
+ return error;
+
+ error = iter->current->next(entry, iter->current);
+ /* If this backend is empty, then keep going */
+ if (error == GIT_ITEROVER)
+ continue;
+
+ return error;
+
+ } while(1);
+
+ return GIT_ITEROVER;
+}
+
+static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ int error;
+ all_iter *iter = (all_iter *) _iter;
+
+ /*
+ * We use the "normal" function to grab the next one across
+ * backends and then apply the regex
+ */
+ while ((error = all_iter_next(entry, _iter)) == 0) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
+ continue;
+
+ /* and simply return if we like the entry's name */
+ return 0;
+ }
+
+ return error;
+}
+
+static void all_iter_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ if (iter->current)
+ iter->current->free(iter->current);
+
+ git__free(iter);
+}
+
+static void all_iter_glob_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ regfree(&iter->regex);
+ all_iter_free(_iter);
+}
+
+int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+{
+ all_iter *iter;
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ iter->parent.free = all_iter_free;
+ iter->parent.next = all_iter_next;
+
+ iter->i = cfg->files.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+{
+ all_iter *iter;
+ int result;
+
+ if (regexp == NULL)
+ return git_config_iterator_new(out, cfg);
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&iter->regex, result);
+ regfree(&iter->regex);
+ return -1;
+ }
+
+ iter->parent.next = all_iter_glob_next;
+ iter->parent.free = all_iter_glob_free;
+ iter->i = cfg->files.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
int git_config_foreach(
const git_config *cfg, git_config_foreach_cb cb, void *payload)
{
return git_config_foreach_match(cfg, NULL, cb, payload);
}
+int git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ int (*fn)(const git_config_entry *, void *),
+ void *data)
+{
+ git_config_entry *entry;
+ git_config_iterator* iter;
+ regex_t regex;
+ int result = 0;
+
+ if (regexp != NULL) {
+ if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&regex, result);
+ regfree(&regex);
+ return -1;
+ }
+ }
+
+ if ((result = backend->iterator(&iter, backend)) < 0) {
+ iter = NULL;
+ return -1;
+ }
+
+ while(!(iter->next(&entry, iter) < 0)) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexp && regexec(&regex, entry->name, 0, NULL, 0) != 0)
+ continue;
+
+ /* abort iterator on non-zero return value */
+ if (fn(entry, data)) {
+ giterr_clear();
+ result = GIT_EUSER;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (regexp != NULL)
+ regfree(&regex);
+
+ iter->free(iter);
+
+ return result;
+}
+
int git_config_foreach_match(
const git_config *cfg,
const char *regexp,
git_config_foreach_cb cb,
void *payload)
{
- int ret = 0;
- size_t i;
- file_internal *internal;
- git_config_backend *file;
+ int error;
+ git_config_iterator *iter;
+ git_config_entry *entry;
- for (i = 0; i < cfg->files.length && ret == 0; ++i) {
- internal = git_vector_get(&cfg->files, i);
- file = internal->file;
- ret = file->foreach(file, regexp, cb, payload);
+ if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+ return error;
+
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ if(cb(entry, payload)) {
+ giterr_clear();
+ error = GIT_EUSER;
+ break;
+ }
}
- return ret;
+ git_config_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
}
/**************
@@ -528,32 +739,114 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co
return config_error_notfound(name);
}
-int git_config_get_multivar(
+int git_config_get_multivar_foreach(
const git_config *cfg, const char *name, const char *regexp,
git_config_foreach_cb cb, void *payload)
{
- file_internal *internal;
- git_config_backend *file;
- int ret = GIT_ENOTFOUND, err;
- size_t i;
+ int err, found;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+
+ if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+ return err;
+
+ found = 0;
+ while ((err = iter->next(&entry, iter)) == 0) {
+ found = 1;
+ if(cb(entry, payload)) {
+ iter->free(iter);
+ return GIT_EUSER;
+ }
+ }
- /*
- * 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);
- if (!internal || !internal->file)
+ iter->free(iter);
+ if (err == GIT_ITEROVER)
+ err = 0;
+
+ if (found == 0 && err == 0)
+ err = config_error_notfound(name);
+
+ return err;
+}
+
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *iter;
+ char *name;
+ 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;
+ int error = 0;
+
+ while ((error = iter->iter->next(entry, iter->iter)) == 0) {
+ if (git__strcmp(iter->name, (*entry)->name))
continue;
- file = internal->file;
- if (!(err = file->get_multivar(file, name, regexp, cb, payload)))
- ret = 0;
- else if (err != GIT_ENOTFOUND)
- return err;
+ if (!iter->have_regex)
+ return 0;
+
+ if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
+ return 0;
+ }
+
+ return error;
+}
+
+void multivar_iter_free(git_config_iterator *_iter)
+{
+ multivar_iter *iter = (multivar_iter *) _iter;
+
+ iter->iter->free(iter->iter);
+
+ git__free(iter->name);
+ regfree(&iter->regex);
+ git__free(iter);
+}
+
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+{
+ 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);
+
+ if ((error = git_config__normalize_name(name, &iter->name)) < 0)
+ goto on_error;
+
+ if (regexp != NULL) {
+ 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;
}
- return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0;
+ iter->iter = inner;
+ iter->parent.free = multivar_iter_free;
+ iter->parent.next = multivar_iter_next;
+
+ *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)
@@ -569,6 +862,16 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
return file->set_multivar(file, name, regexp, value);
}
+int git_config_next(git_config_entry **entry, git_config_iterator *iter)
+{
+ return iter->next(entry, iter);
+}
+
+void git_config_iterator_free(git_config_iterator *iter)
+{
+ iter->free(iter);
+}
+
static int git_config__find_file_to_path(
char *out, size_t outlen, int (*find)(git_buf *buf))
{
@@ -812,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 1d7b4fb38..efc9df965 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -27,6 +27,13 @@ typedef struct cvar_t {
git_config_entry *entry;
} cvar_t;
+typedef struct git_config_file_iter {
+ git_config_iterator parent;
+ git_strmap_iter iter;
+ cvar_t* next_var;
+} git_config_file_iter;
+
+
#define CVAR_LIST_HEAD(list) ((list)->head)
#define CVAR_LIST_TAIL(list) ((list)->tail)
@@ -129,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;
@@ -247,51 +219,56 @@ static void backend_free(git_config_backend *_backend)
git__free(backend);
}
-static int file_foreach(
- git_config_backend *backend,
- const char *regexp,
- int (*fn)(const git_config_entry *, void *),
- void *data)
+static void config_iterator_free(
+ git_config_iterator* iter)
{
- diskfile_backend *b = (diskfile_backend *)backend;
- cvar_t *var, *next_var;
- const char *key;
- regex_t regex;
- int result = 0;
+ git__free(iter);
+}
- if (!b->values)
- return 0;
+static int config_iterator_next(
+ git_config_entry **entry,
+ git_config_iterator *iter)
+{
+ git_config_file_iter *it = (git_config_file_iter *) iter;
+ diskfile_backend *b = (diskfile_backend *) it->parent.backend;
+ int err = 0;
+ cvar_t * var;
- if (regexp != NULL) {
- if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
- giterr_set_regex(&regex, result);
- regfree(&regex);
- return -1;
- }
+ if (it->next_var == NULL) {
+ err = git_strmap_next((void**) &var, &(it->iter), b->values);
+ } else {
+ var = it->next_var;
}
- git_strmap_foreach(b->values, key, var,
- for (; var != NULL; var = next_var) {
- next_var = CVAR_LIST_NEXT(var);
+ if (err < 0) {
+ it->next_var = NULL;
+ return err;
+ }
- /* skip non-matching keys if regexp was provided */
- if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
- continue;
+ *entry = var->entry;
+ it->next_var = CVAR_LIST_NEXT(var);
- /* abort iterator on non-zero return value */
- if (fn(var->entry, data)) {
- giterr_clear();
- result = GIT_EUSER;
- goto cleanup;
- }
- }
- );
+ return 0;
+}
+
+static int config_iterator_new(
+ git_config_iterator **iter,
+ struct git_config_backend* backend)
+{
+ diskfile_backend *b = (diskfile_backend *)backend;
+ git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
-cleanup:
- if (regexp != NULL)
- regfree(&regex);
+ GITERR_CHECK_ALLOC(it);
- return result;
+ it->parent.backend = backend;
+ it->iter = git_strmap_begin(b->values);
+ it->next_var = NULL;
+
+ it->parent.next = config_iterator_next;
+ it->parent.free = config_iterator_free;
+ *iter = (git_config_iterator *) it;
+
+ return 0;
}
static int config_set(git_config_backend *cfg, const char *name, const char *value)
@@ -302,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;
/*
@@ -385,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);
@@ -400,70 +377,6 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
return 0;
}
-static int config_get_multivar(
- 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(&regex, regex_str, REG_EXTENDED);
- if (result < 0) {
- giterr_set_regex(&regex, result);
- regfree(&regex);
- return -1;
- }
-
- /* and throw the callback only on the variables that
- * match the regex */
- do {
- if (regexec(&regex, 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) < 0)
- break;
- }
-
- var = var->next;
- } while (var != NULL);
- regfree(&regex);
- } 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)
{
@@ -477,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);
@@ -550,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);
@@ -590,11 +503,10 @@ 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 = 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.iterator = config_iterator_new;
backend->parent.refresh = config_refresh;
backend->parent.free = backend_free;
diff --git a/src/config_file.h b/src/config_file.h
index 7445859c4..d4a1a4061 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -42,7 +42,7 @@ GIT_INLINE(int) git_config_file_foreach(
int (*fn)(const git_config_entry *entry, void *data),
void *data)
{
- return cfg->foreach(cfg, NULL, fn, data);
+ return git_config_backend_foreach_match(cfg, NULL, fn, data);
}
GIT_INLINE(int) git_config_file_foreach_match(
@@ -51,7 +51,7 @@ GIT_INLINE(int) git_config_file_foreach_match(
int (*fn)(const git_config_entry *entry, void *data),
void *data)
{
- return cfg->foreach(cfg, regexp, fn, data);
+ return git_config_backend_foreach_match(cfg, regexp, fn, data);
}
extern int git_config_file_normalize_section(char *start, char *end);
diff --git a/src/diff_driver.c b/src/diff_driver.c
index e82dfa50d..bd5a8fbd9 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -187,7 +187,7 @@ static int git_diff_driver_load(
git_buf_truncate(&name, namelen + strlen("diff.."));
git_buf_put(&name, "xfuncname", strlen("xfuncname"));
- if ((error = git_config_get_multivar(
+ if ((error = git_config_get_multivar_foreach(
cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
if (error != GIT_ENOTFOUND)
goto done;
@@ -196,7 +196,7 @@ static int git_diff_driver_load(
git_buf_truncate(&name, namelen + strlen("diff.."));
git_buf_put(&name, "funcname", strlen("funcname"));
- if ((error = git_config_get_multivar(
+ if ((error = git_config_get_multivar_foreach(
cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
if (error != GIT_ENOTFOUND)
goto done;
diff --git a/src/remote.c b/src/remote.c
index 7677e56b2..671075bb7 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -242,7 +242,7 @@ static int get_optional_config(
return -1;
if (cb != NULL)
- error = git_config_get_multivar(config, key, NULL, cb, payload);
+ error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
else
error = git_config_get_string(payload, config, key);
diff --git a/src/strmap.c b/src/strmap.c
new file mode 100644
index 000000000..b26a13d1f
--- /dev/null
+++ b/src/strmap.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "strmap.h"
+
+int git_strmap_next(
+ void **data,
+ git_strmap_iter* iter,
+ git_strmap *map)
+{
+ if (!map)
+ return GIT_ERROR;
+
+ while (*iter != git_strmap_end(map)) {
+ if (!(git_strmap_has_data(map, *iter))) {
+ ++(*iter);
+ continue;
+ }
+
+ *data = git_strmap_value_at(map, *iter);
+
+ ++(*iter);
+
+ return GIT_OK;
+ }
+
+ return GIT_ITEROVER;
+}
diff --git a/src/strmap.h b/src/strmap.h
index 44176a0fc..8276ab468 100644
--- a/src/strmap.h
+++ b/src/strmap.h
@@ -17,6 +17,7 @@
__KHASH_TYPE(str, const char *, void *);
typedef khash_t(str) git_strmap;
+typedef khiter_t git_strmap_iter;
#define GIT__USE_STRMAP \
__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
@@ -31,7 +32,9 @@ typedef khash_t(str) git_strmap;
#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+#define git_strmap_has_data(h, idx) kh_exist(h, idx)
+#define git_strmap_key(h, idx) kh_key(h, idx)
#define git_strmap_value_at(h, idx) kh_val(h, idx)
#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
@@ -61,4 +64,12 @@ typedef khash_t(str) git_strmap;
#define git_strmap_foreach kh_foreach
#define git_strmap_foreach_value kh_foreach_value
+#define git_strmap_begin kh_begin
+#define git_strmap_end kh_end
+
+int git_strmap_next(
+ void **data,
+ git_strmap_iter* iter,
+ git_strmap *map);
+
#endif
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
index efc431502..0d552d65e 100644
--- a/tests-clar/config/multivar.c
+++ b/tests-clar/config/multivar.c
@@ -46,60 +46,78 @@ static int cb(const git_config_entry *entry, void *data)
return 0;
}
-static void check_get_multivar(
+static void check_get_multivar_foreach(
git_config *cfg, int expected, int expected_patterned)
{
int n = 0;
if (expected > 0) {
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert_equal_i(expected, n);
} else {
cl_assert_equal_i(GIT_ENOTFOUND,
- git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
}
n = 0;
if (expected_patterned > 0) {
- cl_git_pass(git_config_get_multivar(cfg, _name, "example", cb, &n));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
cl_assert_equal_i(expected_patterned, n);
} else {
cl_assert_equal_i(GIT_ENOTFOUND,
- git_config_get_multivar(cfg, _name, "example", cb, &n));
+ git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
}
}
+static void check_get_multivar(git_config *cfg, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int n = 0;
+
+ cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL));
+
+ while (git_config_next(&entry, iter) == 0)
+ n++;
+
+ cl_assert_equal_i(expected, n);
+ git_config_iterator_free(iter);
+
+}
+
void test_config_multivar__get(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
- check_get_multivar(cfg, 2, 1);
+ check_get_multivar_foreach(cfg, 2, 1);
/* add another that has the _name entry */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 1));
- check_get_multivar(cfg, 3, 2);
+ check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, 1));
- check_get_multivar(cfg, 3, 2);
+ check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry at the end */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, 1));
- check_get_multivar(cfg, 3, 2);
+ check_get_multivar_foreach(cfg, 3, 2);
/* drop original file */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, 1));
- check_get_multivar(cfg, 1, 1);
+ check_get_multivar_foreach(cfg, 1, 1);
/* drop other file with match */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, 1));
- check_get_multivar(cfg, 0, 0);
+ check_get_multivar_foreach(cfg, 0, 0);
/* reload original file (add different place in order) */
cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1));
- check_get_multivar(cfg, 2, 1);
+ check_get_multivar_foreach(cfg, 2, 1);
+
+ check_get_multivar(cfg, 2);
git_config_free(cfg);
}
@@ -113,12 +131,12 @@ void test_config_multivar__add(void)
cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 3);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 1);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
git_config_free(cfg);
@@ -127,12 +145,12 @@ void test_config_multivar__add(void)
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);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 1);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
git_config_free(cfg);
}
@@ -147,8 +165,8 @@ void test_config_multivar__add_new(void)
cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, var, NULL, cb, &n));
- cl_assert(n == 1);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
+ cl_assert_equal_i(n, 1);
git_config_free(cfg);
}
@@ -161,13 +179,13 @@ void test_config_multivar__replace(void)
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_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
@@ -175,7 +193,7 @@ void test_config_multivar__replace(void)
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_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
@@ -190,16 +208,16 @@ void test_config_multivar__replace_multiple(void)
cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 2);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 2);
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
git_config_free(cfg);
}
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
index a18dca89b..395f1cfdb 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -245,6 +245,37 @@ void test_config_read__foreach(void)
git_config_free(cfg);
}
+void test_config_read__iterator(void)
+{
+ git_config *cfg;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, ret;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ while ((ret = git_config_next(&entry, iter)) == 0) {
+ count++;
+ }
+
+ git_config_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, ret);
+ cl_assert_equal_i(7, count);
+
+ count = 3;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ git_config_iterator_free(iter);
+ git_config_free(cfg);
+}
+
static int count_cfg_entries(const git_config_entry *entry, void *payload)
{
int *count = payload;
@@ -288,6 +319,38 @@ void test_config_read__foreach_match(void)
git_config_free(cfg);
}
+static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, error;
+
+ cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
+
+ count = 0;
+ while ((error = git_config_next(&entry, iter)) == 0)
+ count++;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected, count);
+ git_config_iterator_free(iter);
+}
+
+void test_config_read__iterator_glob(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ check_glob_iter(cfg, "core.*", 3);
+ check_glob_iter(cfg, "remote\\.ab.*", 2);
+ check_glob_iter(cfg, ".*url$", 2);
+ check_glob_iter(cfg, ".*dummy.*", 2);
+ check_glob_iter(cfg, ".*nomatch.*", 0);
+
+ git_config_free(cfg);
+}
+
void test_config_read__whitespace_not_required_around_assignment(void)
{
git_config *cfg;
diff --git a/tests-clar/config/validkeyname.c b/tests-clar/config/validkeyname.c
index 03c13d723..33699737b 100644
--- a/tests-clar/config/validkeyname.c
+++ b/tests-clar/config/validkeyname.c
@@ -28,7 +28,7 @@ static void assert_invalid_config_key_name(const char *name)
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_delete_entry(cfg, name),
GIT_EINVALIDSPEC);
- cl_git_fail_with(git_config_get_multivar(cfg, name, "*", NULL, NULL),
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL),
GIT_EINVALIDSPEC);
cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
GIT_EINVALIDSPEC);