diff --git a/include/git2/config.h b/include/git2/config.h index 827d43544..f6fc74ee1 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_backend_iter* git_config_backend_iter; typedef enum { GIT_CVAR_FALSE = 0, @@ -535,6 +536,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..61dcce544 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -35,7 +35,9 @@ struct git_config_backend { 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_new)(git_config_backend_iter **, struct git_config_backend *); + void (*iterator_free)(git_config_backend_iter *); + int (*next)(git_config_backend_iter *, git_config_entry *, 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..b421b3be1 100644 --- a/src/config.c +++ b/src/config.c @@ -321,6 +321,50 @@ int git_config_foreach( 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_backend_iter iter; + regex_t regex; + int result = 0; + + if (regexp != NULL) { + if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, result); + regfree(®ex); + return -1; + } + } + + if (backend->iterator_new(&iter, backend) < 0) + return 0; + + while(!(backend->next(&iter, &entry, backend) < 0)) { + /* skip non-matching keys if regexp was provided */ + if (regexp && regexec(®ex, 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(®ex); + + backend->iterator_free(iter); + + return result; +} + int git_config_foreach_match( const git_config *cfg, const char *regexp, @@ -335,7 +379,7 @@ int git_config_foreach_match( 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); + ret = git_config_backend_foreach_match(file, regexp, cb, payload); } return ret; diff --git a/src/config_file.c b/src/config_file.c index 088f6190d..ff8f8fc15 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -27,6 +27,12 @@ typedef struct cvar_t { git_config_entry *entry; } cvar_t; +typedef struct git_config_file_iter { + git_strmap_iter iter; + cvar_t* next; +} git_config_file_iter; + + #define CVAR_LIST_HEAD(list) ((list)->head) #define CVAR_LIST_TAIL(list) ((list)->tail) @@ -247,52 +253,60 @@ 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 int config_iterator_new( + git_config_backend_iter *iter, + struct git_config_backend* backend) { diskfile_backend *b = (diskfile_backend *)backend; - cvar_t *var, *next_var; - const char *key; - regex_t regex; - int result = 0; + git_config_file_iter **it= ((git_config_file_iter**) iter); - if (!b->values) - return 0; + if (!b->values || git_strmap_num_entries(b->values) < 1) + return -1; - if (regexp != NULL) { - if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { - giterr_set_regex(®ex, result); - regfree(®ex); - return -1; - } + *it = git__calloc(1, sizeof(git_config_file_iter)); + GITERR_CHECK_ALLOC(it); + + (*it)->iter = git_strmap_begin(b->values); + (*it)->next = NULL; + + return 0; +} + +static void config_iterator_free( + git_config_backend_iter iter) +{ + git__free(iter); +} + +static int config_next( + git_config_backend_iter *iter, + git_config_entry* entry, + struct git_config_backend* backend) +{ + diskfile_backend *b = (diskfile_backend *)backend; + git_config_file_iter *it = *((git_config_file_iter**) iter); + int err; + cvar_t * var; + const char* key; + + if (it->next == NULL) { + err = git_strmap_next(&key, (void**) &var, &(it->iter), b->values); + } else { + key = it->next->entry->name; + var = it->next; } - git_strmap_iter iter = git_strmap_begin(b->values); - while (!(git_strmap_next(&key, (void**) &var, &iter, b->values) < 0)) { - for (; var != NULL; var = next_var) { - next_var = CVAR_LIST_NEXT(var); - - /* skip non-matching keys if regexp was provided */ - if (regexp && regexec(®ex, key, 0, NULL, 0) != 0) - continue; - - /* abort iterator on non-zero return value */ - if (fn(var->entry, data)) { - giterr_clear(); - result = GIT_EUSER; - goto cleanup; - } - } + if (err < 0) { + it->next = NULL; + return -1; } -cleanup: - if (regexp != NULL) - regfree(®ex); + entry->name = key; + entry->value = var->entry->value; + entry->level = var->entry->level; + it->next = CVAR_LIST_NEXT(var); - return result; + return 0; } static int config_set(git_config_backend *cfg, const char *name, const char *value) @@ -595,7 +609,9 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) 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_new = config_iterator_new; + backend->parent.iterator_free = config_iterator_free; + backend->parent.next = config_next; 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);