From 6385fc5ff5d669d3ec99d89f19c5860cf53011ba Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Sun, 17 Mar 2013 20:39:01 +0100 Subject: [PATCH 01/19] added new type and several functions to git_strmap This step is needed to easily add iterators to git_config_backend As well use these new git_strmap functions to implement foreach * git_strmap_iter * git_strmap_has_data(...) * git_strmap_begin(...) * git_strmap_end(...) * git_strmap_next(...) --- src/config_file.c | 5 +++-- src/strmap.c | 34 ++++++++++++++++++++++++++++++++++ src/strmap.h | 12 ++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/strmap.c diff --git a/src/config_file.c b/src/config_file.c index 570f286c8..088f6190d 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -270,7 +270,8 @@ static int file_foreach( } } - git_strmap_foreach(b->values, key, var, + 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); @@ -285,7 +286,7 @@ static int file_foreach( goto cleanup; } } - ); + } cleanup: if (regexp != NULL) diff --git a/src/strmap.c b/src/strmap.c new file mode 100644 index 000000000..1b07359d1 --- /dev/null +++ b/src/strmap.c @@ -0,0 +1,34 @@ +/* + * 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( + const char **key, + 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; + } + + *key = git_strmap_key(map, *iter); + *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..cb079b500 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,13 @@ 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( + const char **key, + void **data, + git_strmap_iter* iter, + git_strmap *map); + #endif From a603c191578f7b33720e36e95421fcd58bc7abe4 Mon Sep 17 00:00:00 2001 From: Nico von Geyso Date: Mon, 18 Mar 2013 21:02:36 +0100 Subject: [PATCH 02/19] replaced foreach() with non callback based iterations in git_config_backend new functions in struct git_config_backend: * iterator_new(...) * iterator_free(...) * next(...) The old callback based foreach style can still be used with `git_config_backend_foreach_match` --- include/git2/config.h | 20 +++++++++ include/git2/sys/config.h | 4 +- src/config.c | 46 +++++++++++++++++++- src/config_file.c | 92 +++++++++++++++++++++++---------------- src/config_file.h | 4 +- 5 files changed, 124 insertions(+), 42 deletions(-) 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); From 4d588d9713bb558e45a8bdf6c41d232bb592b814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 11:24:47 +0200 Subject: [PATCH 03/19] Don't typedef a pointer Make the iterator structure opaque and make sure it compiles. --- include/git2/config.h | 2 +- include/git2/sys/config.h | 2 +- src/config.c | 4 ++-- src/config.h | 5 +++++ src/config_file.c | 18 ++++++++++-------- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index f6fc74ee1..43bc19412 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -61,7 +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 struct git_config_backend_iter git_config_backend_iter; typedef enum { GIT_CVAR_FALSE = 0, diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 61dcce544..f617623a9 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -37,7 +37,7 @@ struct git_config_backend { int (*del)(struct git_config_backend *, const char *key); 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 (*next)(git_config_entry *, git_config_backend_iter *); int (*refresh)(struct git_config_backend *); void (*free)(struct git_config_backend *); }; diff --git a/src/config.c b/src/config.c index b421b3be1..2f800a896 100644 --- a/src/config.c +++ b/src/config.c @@ -328,7 +328,7 @@ int git_config_backend_foreach_match( void *data) { git_config_entry entry; - git_config_backend_iter iter; + git_config_backend_iter* iter; regex_t regex; int result = 0; @@ -343,7 +343,7 @@ int git_config_backend_foreach_match( if (backend->iterator_new(&iter, backend) < 0) return 0; - while(!(backend->next(&iter, &entry, backend) < 0)) { + while(!(backend->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && regexec(®ex, entry.name, 0, NULL, 0) != 0) continue; diff --git a/src/config.h b/src/config.h index c5c11ae14..ea150e968 100644 --- a/src/config.h +++ b/src/config.h @@ -24,6 +24,11 @@ struct git_config { git_vector files; }; +typedef struct { + git_config_backend *backend; + unsigned int flags; +} git_config_backend_iter; + extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_xdg_r(git_buf *system_config_path); extern int git_config_find_system_r(git_buf *system_config_path); diff --git a/src/config_file.c b/src/config_file.c index ff8f8fc15..ea571e9f8 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -28,6 +28,8 @@ typedef struct cvar_t { } cvar_t; typedef struct git_config_file_iter { + git_config_backend *backend; + unsigned int flags; git_strmap_iter iter; cvar_t* next; } git_config_file_iter; @@ -254,7 +256,7 @@ static void backend_free(git_config_backend *_backend) } static int config_iterator_new( - git_config_backend_iter *iter, + git_config_backend_iter **iter, struct git_config_backend* backend) { diskfile_backend *b = (diskfile_backend *)backend; @@ -266,6 +268,7 @@ static int config_iterator_new( *it = git__calloc(1, sizeof(git_config_file_iter)); GITERR_CHECK_ALLOC(it); + (*it)->backend = backend; (*it)->iter = git_strmap_begin(b->values); (*it)->next = NULL; @@ -273,18 +276,17 @@ static int config_iterator_new( } static void config_iterator_free( - git_config_backend_iter iter) + 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) +static int config_iterator_next( + git_config_entry *entry, + git_config_backend_iter *iter) { - diskfile_backend *b = (diskfile_backend *)backend; git_config_file_iter *it = *((git_config_file_iter**) iter); + diskfile_backend *b = (diskfile_backend *)it->backend; int err; cvar_t * var; const char* key; @@ -611,7 +613,7 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->parent.del = config_delete; backend->parent.iterator_new = config_iterator_new; backend->parent.iterator_free = config_iterator_free; - backend->parent.next = config_next; + backend->parent.next = config_iterator_next; backend->parent.refresh = config_refresh; backend->parent.free = backend_free; From 82ae6fcdba2aa0fb21f49b127b5f5f7a637ffc76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 11:55:47 +0200 Subject: [PATCH 04/19] config: compilation fixes --- src/config.h | 4 ++-- src/config_file.c | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/config.h b/src/config.h index ea150e968..2f7c96d7f 100644 --- a/src/config.h +++ b/src/config.h @@ -24,10 +24,10 @@ struct git_config { git_vector files; }; -typedef struct { +struct git_config_backend_iter { git_config_backend *backend; unsigned int flags; -} git_config_backend_iter; +}; extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_xdg_r(git_buf *system_config_path); diff --git a/src/config_file.c b/src/config_file.c index ea571e9f8..849ef0f6f 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -28,8 +28,7 @@ typedef struct cvar_t { } cvar_t; typedef struct git_config_file_iter { - git_config_backend *backend; - unsigned int flags; + git_config_backend_iter parent; git_strmap_iter iter; cvar_t* next; } git_config_file_iter; @@ -260,17 +259,17 @@ static int config_iterator_new( struct git_config_backend* backend) { diskfile_backend *b = (diskfile_backend *)backend; - git_config_file_iter **it= ((git_config_file_iter**) iter); + git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter)); if (!b->values || git_strmap_num_entries(b->values) < 1) return -1; - *it = git__calloc(1, sizeof(git_config_file_iter)); GITERR_CHECK_ALLOC(it); - (*it)->backend = backend; - (*it)->iter = git_strmap_begin(b->values); - (*it)->next = NULL; + it->parent.backend = backend; + it->iter = git_strmap_begin(b->values); + it->next = NULL; + *iter = (git_config_backend_iter *) it; return 0; } @@ -285,9 +284,9 @@ static int config_iterator_next( git_config_entry *entry, git_config_backend_iter *iter) { - git_config_file_iter *it = *((git_config_file_iter**) iter); - diskfile_backend *b = (diskfile_backend *)it->backend; - int err; + git_config_file_iter *it = (git_config_file_iter *) iter; + diskfile_backend *b = (diskfile_backend *) it->parent.backend; + int err = 0; cvar_t * var; const char* key; From 84fec6f628b8c82a70c0aff5dc6a47d1cdb6fbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 13:14:35 +0200 Subject: [PATCH 05/19] config: saner iterator errors Really report an error in foreach if we fail to allocate the iterator, and don't fail if the config is emtpy. --- src/config.c | 6 ++++-- src/config_file.c | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/config.c b/src/config.c index 2f800a896..6c055e0e6 100644 --- a/src/config.c +++ b/src/config.c @@ -340,8 +340,10 @@ int git_config_backend_foreach_match( } } - if (backend->iterator_new(&iter, backend) < 0) - return 0; + if ((result = backend->iterator_new(&iter, backend)) < 0) { + iter = NULL; + return -1; + } while(!(backend->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ diff --git a/src/config_file.c b/src/config_file.c index 849ef0f6f..6eb51ac25 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -261,9 +261,6 @@ static int config_iterator_new( diskfile_backend *b = (diskfile_backend *)backend; git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter)); - if (!b->values || git_strmap_num_entries(b->values) < 1) - return -1; - GITERR_CHECK_ALLOC(it); it->parent.backend = backend; From 4efa32903adf131631d283c914e0a5bf29c49e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 13:41:18 +0200 Subject: [PATCH 06/19] config: get_multivar -> get_multivar_foreach The plain function will return an iterator, so move this one out of the way. --- include/git2/config.h | 4 +-- include/git2/sys/config.h | 2 +- src/config.c | 4 +-- src/config_file.c | 4 +-- src/diff_driver.c | 4 +-- src/remote.c | 2 +- tests-clar/config/multivar.c | 44 ++++++++++++++++---------------- tests-clar/config/validkeyname.c | 2 +- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 43bc19412..a0dc11bb1 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -328,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 * @@ -339,7 +339,7 @@ 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); /** * Set the value of an integer config variable in the config file diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index f617623a9..09c79638a 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -31,7 +31,7 @@ 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 (*get_multivar_foreach)(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); diff --git a/src/config.c b/src/config.c index 6c055e0e6..a4627c9ad 100644 --- a/src/config.c +++ b/src/config.c @@ -574,7 +574,7 @@ 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) { @@ -593,7 +593,7 @@ int git_config_get_multivar( continue; file = internal->file; - if (!(err = file->get_multivar(file, name, regexp, cb, payload))) + if (!(err = file->get_multivar_foreach(file, name, regexp, cb, payload))) ret = 0; else if (err != GIT_ENOTFOUND) return err; diff --git a/src/config_file.c b/src/config_file.c index 6eb51ac25..ed79624eb 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -413,7 +413,7 @@ static int config_get(const git_config_backend *cfg, const char *name, const git return 0; } -static int config_get_multivar( +static int config_get_multivar_foreach( git_config_backend *cfg, const char *name, const char *regex_str, @@ -603,7 +603,7 @@ 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.get_multivar_foreach = config_get_multivar_foreach; backend->parent.set = config_set; backend->parent.set_multivar = config_set_multivar; backend->parent.del = config_delete; 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 158f3e938..003fadaa3 100644 --- a/src/remote.c +++ b/src/remote.c @@ -217,7 +217,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/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index efc431502..390b24c6b 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -46,27 +46,27 @@ 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)); } } @@ -75,31 +75,31 @@ 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); git_config_free(cfg); } @@ -113,11 +113,11 @@ 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_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); cl_assert(n == 3); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); cl_assert(n == 1); git_config_free(cfg); @@ -127,11 +127,11 @@ 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_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); cl_assert(n == 3); n = 0; - cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n)); + cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); cl_assert(n == 1); git_config_free(cfg); @@ -147,7 +147,7 @@ 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_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n)); cl_assert(n == 1); git_config_free(cfg); @@ -161,13 +161,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 +175,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,7 +190,7 @@ 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_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); cl_assert(n == 2); git_config_free(cfg); @@ -198,7 +198,7 @@ void test_config_multivar__replace_multiple(void) 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_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); cl_assert(n == 2); git_config_free(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); From eba7399251cfa95d9346b9b41ca78dc5d43a840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 14:39:32 +0200 Subject: [PATCH 07/19] config: move next() and free() into the iterator Like we have in the references iterator, next and free belong in the iterator itself. --- include/git2/config.h | 2 +- include/git2/sys/config.h | 30 ++++++++++++-- src/config.c | 8 ++-- src/config.h | 5 --- src/config_file.c | 87 ++++++++++++++++++++------------------- 5 files changed, 76 insertions(+), 56 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index a0dc11bb1..950312548 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -61,7 +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 struct git_config_iterator git_config_iterator; typedef enum { GIT_CVAR_FALSE = 0, diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 09c79638a..45599dc69 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -20,6 +20,32 @@ */ 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 + */ + 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 @@ -35,9 +61,7 @@ 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 (*iterator_new)(git_config_backend_iter **, struct git_config_backend *); - void (*iterator_free)(git_config_backend_iter *); - int (*next)(git_config_entry *, git_config_backend_iter *); + 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 a4627c9ad..5bec0f040 100644 --- a/src/config.c +++ b/src/config.c @@ -328,7 +328,7 @@ int git_config_backend_foreach_match( void *data) { git_config_entry entry; - git_config_backend_iter* iter; + git_config_iterator* iter; regex_t regex; int result = 0; @@ -340,12 +340,12 @@ int git_config_backend_foreach_match( } } - if ((result = backend->iterator_new(&iter, backend)) < 0) { + if ((result = backend->iterator(&iter, backend)) < 0) { iter = NULL; return -1; } - while(!(backend->next(&entry, iter) < 0)) { + while(!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && regexec(®ex, entry.name, 0, NULL, 0) != 0) continue; @@ -362,7 +362,7 @@ cleanup: if (regexp != NULL) regfree(®ex); - backend->iterator_free(iter); + iter->free(iter); return result; } diff --git a/src/config.h b/src/config.h index 2f7c96d7f..c5c11ae14 100644 --- a/src/config.h +++ b/src/config.h @@ -24,11 +24,6 @@ struct git_config { git_vector files; }; -struct git_config_backend_iter { - git_config_backend *backend; - unsigned int flags; -}; - extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_xdg_r(git_buf *system_config_path); extern int git_config_find_system_r(git_buf *system_config_path); diff --git a/src/config_file.c b/src/config_file.c index ed79624eb..3e0c6cc0b 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -28,9 +28,9 @@ typedef struct cvar_t { } cvar_t; typedef struct git_config_file_iter { - git_config_backend_iter parent; + git_config_iterator parent; git_strmap_iter iter; - cvar_t* next; + cvar_t* next_var; } git_config_file_iter; @@ -254,8 +254,44 @@ static void backend_free(git_config_backend *_backend) git__free(backend); } +static void config_iterator_free( + git_config_iterator* iter) +{ + git__free(iter); +} + +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; + const char* key; + + if (it->next_var == NULL) { + err = git_strmap_next(&key, (void**) &var, &(it->iter), b->values); + } else { + key = it->next_var->entry->name; + var = it->next_var; + } + + if (err < 0) { + it->next_var = NULL; + return -1; + } + + entry->name = key; + entry->value = var->entry->value; + entry->level = var->entry->level; + it->next_var = CVAR_LIST_NEXT(var); + + return 0; +} + static int config_iterator_new( - git_config_backend_iter **iter, + git_config_iterator **iter, struct git_config_backend* backend) { diskfile_backend *b = (diskfile_backend *)backend; @@ -265,44 +301,11 @@ static int config_iterator_new( it->parent.backend = backend; it->iter = git_strmap_begin(b->values); - it->next = NULL; - *iter = (git_config_backend_iter *) it; + it->next_var = NULL; - return 0; -} - -static void config_iterator_free( - git_config_backend_iter* iter) -{ - git__free(iter); -} - -static int config_iterator_next( - git_config_entry *entry, - git_config_backend_iter *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; - 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; - } - - if (err < 0) { - it->next = NULL; - return -1; - } - - entry->name = key; - entry->value = var->entry->value; - entry->level = var->entry->level; - it->next = CVAR_LIST_NEXT(var); + it->parent.next = config_iterator_next; + it->parent.free = config_iterator_free; + *iter = (git_config_iterator *) it; return 0; } @@ -607,9 +610,7 @@ 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.iterator_new = config_iterator_new; - backend->parent.iterator_free = config_iterator_free; - backend->parent.next = config_iterator_next; + backend->parent.iterator = config_iterator_new; backend->parent.refresh = config_refresh; backend->parent.free = backend_free; From 3a7ffc29c9416c5d182835c7f18c04437366f218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 16:18:07 +0200 Subject: [PATCH 08/19] config: initial multivar iterator --- include/git2/sys/config.h | 6 ++- src/config.c | 18 ++++++++ src/config_file.c | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 45599dc69..477d47271 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -36,9 +36,10 @@ struct git_config_iterator { unsigned int flags; /** - * Return the current entry and advance the iterator + * Return the current entry and advance the iterator. The + * memory belongs to the library. */ - int (*next)(git_config_entry *entry, git_config_iterator *iter); + int (*next)(const git_config_entry *entry, git_config_iterator *iter); /** * Free the iterator @@ -58,6 +59,7 @@ struct git_config_backend { 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 5bec0f040..77c558022 100644 --- a/src/config.c +++ b/src/config.c @@ -602,6 +602,24 @@ int git_config_get_multivar_foreach( return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0; } +struct config_multivar_iter { + git_config_iterator parent; +}; + +int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) +{ + struct config_multivar_iter *iter; + + iter = git__calloc(1, sizeof(struct config_multivar_iter)); + GITERR_CHECK_ALLOC(iter); + + /* get multivar from each */ + + *out = (git_config_iterator *) iter; + + return 0; +} + int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) { git_config_backend *file; diff --git a/src/config_file.c b/src/config_file.c index 3e0c6cc0b..5559bd406 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -416,6 +416,95 @@ 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; + return 0; + } + } 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, @@ -607,6 +696,7 @@ 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; From cca5df6376fd41fb4fbbb9f8a9ff87c38079dfd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 16:59:39 +0200 Subject: [PATCH 09/19] config: hopefully get the iterator to work on multivars --- include/git2/sys/config.h | 2 +- src/config.c | 81 ++++++++++++++++++++++++++++++++++++--- src/config_file.c | 9 +++-- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 477d47271..d5b450a6c 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -39,7 +39,7 @@ struct git_config_iterator { * Return the current entry and advance the iterator. The * memory belongs to the library. */ - int (*next)(const git_config_entry *entry, git_config_iterator *iter); + int (*next)(git_config_entry *entry, git_config_iterator *iter); /** * Free the iterator diff --git a/src/config.c b/src/config.c index 77c558022..f34d5dd29 100644 --- a/src/config.c +++ b/src/config.c @@ -602,18 +602,89 @@ int git_config_get_multivar_foreach( return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0; } -struct config_multivar_iter { +typedef struct { git_config_iterator parent; -}; + git_config_iterator *current; + const char *name; + const char *regexp; + const git_config *cfg; + size_t i; +} multivar_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 multivar_iter_next_empty(git_config_entry *entry, git_config_iterator *_iter) +{ + GIT_UNUSED(entry); + GIT_UNUSED(_iter); + + return GIT_ITEROVER; +} + +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; + + if (current != NULL && + (error = current->next(entry, current)) == 0) + return 0; + + if (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; + if ((error = backend->get_multivar(&iter->current, backend, iter->name, iter->regexp)) < 0) + return -1; + + iter->i = i; + return iter->current->next(entry, iter->current); + + } while(1); + + return GIT_ITEROVER; +} int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) { - struct config_multivar_iter *iter; + multivar_iter *iter; + size_t i; - iter = git__calloc(1, sizeof(struct config_multivar_iter)); + iter = git__calloc(1, sizeof(multivar_iter)); GITERR_CHECK_ALLOC(iter); - /* get multivar from each */ + if (find_next_backend(&i, cfg, cfg->files.length) < 0) + iter->parent.next = multivar_iter_next_empty; + else + iter->parent.next = multivar_iter_next; + + iter->i = cfg->files.length; + iter->cfg = cfg; + iter->name = name; + iter->regexp = regexp; *out = (git_config_iterator *) iter; diff --git a/src/config_file.c b/src/config_file.c index 5559bd406..74b200073 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -433,7 +433,7 @@ static void foreach_iter_free(git_config_iterator *_iter) git__free(iter); } -static int foreach_iter_next(git_config_entry **out, git_config_iterator *_iter) +static int foreach_iter_next(git_config_entry *out, git_config_iterator *_iter) { foreach_iter *iter = (foreach_iter *) _iter; @@ -443,7 +443,9 @@ static int foreach_iter_next(git_config_entry **out, git_config_iterator *_iter) return GIT_ITEROVER; if (!iter->have_regex) { - *out = var->entry; + out->name = var->entry->name; + out->value = var->entry->value; + iter->var = var->next; return 0; } @@ -453,7 +455,8 @@ static int foreach_iter_next(git_config_entry **out, git_config_iterator *_iter) git_config_entry *entry = var->entry; regex_t *regex = &iter->regex;; if (regexec(regex, entry->value, 0, NULL, 0) == 0) { - *out = entry; + out->name = entry->name; + out->value = entry->value; return 0; } } while(var != NULL); From 99dfb538addc06c2f40d29371c52dd43f0d6ceb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 17:57:59 +0200 Subject: [PATCH 10/19] config: working multivar iterator Implement the foreach version as a wrapper around the iterator. --- include/git2/config.h | 10 ++++ include/git2/sys/config.h | 2 +- src/config.c | 89 +++++++++++++++++++++++------------- src/config_file.c | 20 ++++---- tests-clar/config/multivar.c | 14 +++--- 5 files changed, 83 insertions(+), 52 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 950312548..e1d34b997 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -341,6 +341,16 @@ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, c */ 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_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); /** * Set the value of an integer config variable in the config file * with the highest level (usually the local one). diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index d5b450a6c..e369fb8ab 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -39,7 +39,7 @@ struct git_config_iterator { * Return the current entry and advance the iterator. The * memory belongs to the library. */ - int (*next)(git_config_entry *entry, git_config_iterator *iter); + int (*next)(git_config_entry **entry, git_config_iterator *iter); /** * Free the iterator diff --git a/src/config.c b/src/config.c index f34d5dd29..63a386de3 100644 --- a/src/config.c +++ b/src/config.c @@ -327,7 +327,7 @@ int git_config_backend_foreach_match( int (*fn)(const git_config_entry *, void *), void *data) { - git_config_entry entry; + git_config_entry *entry; git_config_iterator* iter; regex_t regex; int result = 0; @@ -347,11 +347,11 @@ int git_config_backend_foreach_match( while(!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ - if (regexp && regexec(®ex, entry.name, 0, NULL, 0) != 0) + if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0) continue; /* abort iterator on non-zero return value */ - if (fn(&entry, data)) { + if (fn(entry, data)) { giterr_clear(); result = GIT_EUSER; goto cleanup; @@ -578,35 +578,36 @@ 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; - /* - * 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) - continue; - file = internal->file; + if ((err = git_config_get_multivar(&iter, cfg, name, regexp)) < 0) + return err; - if (!(err = file->get_multivar_foreach(file, name, regexp, cb, payload))) - ret = 0; - else if (err != GIT_ENOTFOUND) - return err; + found = 0; + while ((err = iter->next(&entry, iter)) == 0) { + found = 1; + if(cb(entry, payload)) { + iter->free(iter); + return GIT_EUSER; + } } - return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0; + 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 *current; - const char *name; - const char *regexp; + char *name; + char *regexp; const git_config *cfg; size_t i; } multivar_iter; @@ -627,7 +628,7 @@ static int find_next_backend(size_t *out, const git_config *cfg, size_t i) return -1; } -static int multivar_iter_next_empty(git_config_entry *entry, git_config_iterator *_iter) +static int multivar_iter_next_empty(git_config_entry **entry, git_config_iterator *_iter) { GIT_UNUSED(entry); GIT_UNUSED(_iter); @@ -635,20 +636,21 @@ static int multivar_iter_next_empty(git_config_entry *entry, git_config_iterator return GIT_ITEROVER; } -static int multivar_iter_next(git_config_entry *entry, git_config_iterator *_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; + int error = 0; if (current != NULL && - (error = current->next(entry, current)) == 0) + (error = current->next(entry, current)) == 0) { return 0; + } - if (error != GIT_ITEROVER) + if (error < 0 && error != GIT_ITEROVER) return error; do { @@ -657,10 +659,15 @@ static int multivar_iter_next(git_config_entry *entry, git_config_iterator *_ite internal = git_vector_get(&iter->cfg->files, i - 1); backend = internal->file; - if ((error = backend->get_multivar(&iter->current, backend, iter->name, iter->regexp)) < 0) - return -1; + iter->i = i - 1; + + error = backend->get_multivar(&iter->current, backend, iter->name, iter->regexp); + if (error == GIT_ENOTFOUND) + continue; + + if (error < 0) + return error; - iter->i = i; return iter->current->next(entry, iter->current); } while(1); @@ -668,6 +675,15 @@ static int multivar_iter_next(git_config_entry *entry, git_config_iterator *_ite return GIT_ITEROVER; } +void multivar_iter_free(git_config_iterator *_iter) +{ + multivar_iter *iter = (multivar_iter *) _iter; + + git__free(iter->name); + git__free(iter->regexp); + git__free(iter); +} + int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) { multivar_iter *iter; @@ -676,6 +692,15 @@ int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, co iter = git__calloc(1, sizeof(multivar_iter)); GITERR_CHECK_ALLOC(iter); + iter->name = git__strdup(name); + GITERR_CHECK_ALLOC(iter->name); + + if (regexp != NULL) { + iter->regexp = git__strdup(regexp); + GITERR_CHECK_ALLOC(iter->regexp); + } + + iter->parent.free = multivar_iter_free; if (find_next_backend(&i, cfg, cfg->files.length) < 0) iter->parent.next = multivar_iter_next_empty; else @@ -683,8 +708,6 @@ int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, co iter->i = cfg->files.length; iter->cfg = cfg; - iter->name = name; - iter->regexp = regexp; *out = (git_config_iterator *) iter; diff --git a/src/config_file.c b/src/config_file.c index 74b200073..38cb9f8b8 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -261,7 +261,7 @@ static void config_iterator_free( } static int config_iterator_next( - git_config_entry *entry, + git_config_entry **entry, git_config_iterator *iter) { git_config_file_iter *it = (git_config_file_iter *) iter; @@ -282,9 +282,7 @@ static int config_iterator_next( return -1; } - entry->name = key; - entry->value = var->entry->value; - entry->level = var->entry->level; + *entry = var->entry; it->next_var = CVAR_LIST_NEXT(var); return 0; @@ -433,19 +431,18 @@ static void foreach_iter_free(git_config_iterator *_iter) git__free(iter); } -static int foreach_iter_next(git_config_entry *out, git_config_iterator *_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->name = var->entry->name; - out->value = var->entry->value; - + *out = var->entry; iter->var = var->next; return 0; } @@ -455,10 +452,11 @@ static int foreach_iter_next(git_config_entry *out, git_config_iterator *_iter) git_config_entry *entry = var->entry; regex_t *regex = &iter->regex;; if (regexec(regex, entry->value, 0, NULL, 0) == 0) { - out->name = entry->name; - out->value = entry->value; + *out = entry; + iter->var = var->next; return 0; } + var = var->next; } while(var != NULL); return GIT_ITEROVER; @@ -550,7 +548,7 @@ static int config_get_multivar_foreach( 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) < 0) + if (fn(var->entry, data)) break; } diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index 390b24c6b..b7283b32f 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -114,11 +114,11 @@ void test_config_multivar__add(void) n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); - cl_assert(n == 3); + cl_assert_equal_i(n, 3); n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 1); + cl_assert_equal_i(n, 1); git_config_free(cfg); @@ -128,11 +128,11 @@ void test_config_multivar__add(void) n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); - cl_assert(n == 3); + cl_assert_equal_i(n, 3); n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 1); + cl_assert_equal_i(n, 1); git_config_free(cfg); } @@ -148,7 +148,7 @@ 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_foreach(cfg, var, NULL, cb, &n)); - cl_assert(n == 1); + cl_assert_equal_i(n, 1); git_config_free(cfg); } @@ -191,7 +191,7 @@ void test_config_multivar__replace_multiple(void) n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 2); + cl_assert_equal_i(n, 2); git_config_free(cfg); @@ -199,7 +199,7 @@ void test_config_multivar__replace_multiple(void) n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n)); - cl_assert(n == 2); + cl_assert_equal_i(n, 2); git_config_free(cfg); } From 1e96c9d5341e5f2b0e1af9a1088cc30d3ffb9a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 20:47:06 +0200 Subject: [PATCH 11/19] config: add _next() and _iterator_free() Make it look like the refs iterator API. --- include/git2/config.h | 17 +++++++++++++++++ src/config.c | 10 ++++++++++ tests-clar/config/multivar.c | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/git2/config.h b/include/git2/config.h index e1d34b997..b338e0c81 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -351,6 +351,23 @@ GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const cha * 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); + +/** + * 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 * with the highest level (usually the local one). diff --git a/src/config.c b/src/config.c index 63a386de3..83b101ded 100644 --- a/src/config.c +++ b/src/config.c @@ -727,6 +727,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)) { diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index b7283b32f..afb993c18 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -70,6 +70,22 @@ static void check_get_multivar_foreach( } } +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_get_multivar(&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; @@ -101,6 +117,8 @@ void test_config_multivar__get(void) cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1)); check_get_multivar_foreach(cfg, 2, 1); + check_get_multivar(cfg, 2); + git_config_free(cfg); } From a319ffaead9290bfe35a0f105ff17dacaf7b6e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 8 Aug 2013 21:00:33 +0200 Subject: [PATCH 12/19] config: fix leaks in the iterators --- src/config.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config.c b/src/config.c index 83b101ded..42273cde8 100644 --- a/src/config.c +++ b/src/config.c @@ -594,6 +594,7 @@ int git_config_get_multivar_foreach( } } + iter->free(iter); if (err == GIT_ITEROVER) err = 0; @@ -661,6 +662,10 @@ static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_it 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) continue; @@ -679,6 +684,9 @@ void multivar_iter_free(git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; + if (iter->current) + iter->current->free(iter->current); + git__free(iter->name); git__free(iter->regexp); git__free(iter); From 5880962d90958bc37c08c21d37c9da480ef20e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Aug 2013 09:05:19 +0200 Subject: [PATCH 13/19] config: introduce _iterator_new() As the name suggests, it iterates over all the entries --- include/git2/config.h | 11 ++++ src/config.c | 123 ++++++++++++++++++++++++++++++--------- src/config_file.c | 2 +- tests-clar/config/read.c | 30 ++++++++++ 4 files changed, 136 insertions(+), 30 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index b338e0c81..aed720fc8 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -452,6 +452,17 @@ GIT_EXTERN(int) git_config_foreach( git_config_foreach_cb callback, 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); + /** * Perform an operation on each config variable matching a regular expression. * diff --git a/src/config.c b/src/config.c index 42273cde8..f7ea6f795 100644 --- a/src/config.c +++ b/src/config.c @@ -315,6 +315,99 @@ 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; + 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; + + return iter->current->next(entry, iter->current); + + } while(1); + + return GIT_ITEROVER; +} + +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); +} + +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_foreach( const git_config *cfg, git_config_foreach_cb cb, void *payload) { @@ -613,30 +706,6 @@ typedef struct { size_t i; } multivar_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 multivar_iter_next_empty(git_config_entry **entry, git_config_iterator *_iter) -{ - GIT_UNUSED(entry); - GIT_UNUSED(_iter); - - return GIT_ITEROVER; -} - static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter) { multivar_iter *iter = (multivar_iter *) _iter; @@ -695,7 +764,6 @@ void multivar_iter_free(git_config_iterator *_iter) int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp) { multivar_iter *iter; - size_t i; iter = git__calloc(1, sizeof(multivar_iter)); GITERR_CHECK_ALLOC(iter); @@ -709,10 +777,7 @@ int git_config_get_multivar(git_config_iterator **out, const git_config *cfg, co } iter->parent.free = multivar_iter_free; - if (find_next_backend(&i, cfg, cfg->files.length) < 0) - iter->parent.next = multivar_iter_next_empty; - else - iter->parent.next = multivar_iter_next; + iter->parent.next = multivar_iter_next; iter->i = cfg->files.length; iter->cfg = cfg; diff --git a/src/config_file.c b/src/config_file.c index 38cb9f8b8..4e564a51d 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -279,7 +279,7 @@ static int config_iterator_next( if (err < 0) { it->next_var = NULL; - return -1; + return err; } *entry = var->entry; diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index a18dca89b..2fb511d9d 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -245,6 +245,36 @@ 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++; + } + + 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; From 54f3a572b4fac419008afa83da56c1b0daee1257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Aug 2013 10:29:11 +0200 Subject: [PATCH 14/19] config: introduce a regex-filtering iterator --- include/git2/config.h | 12 ++++++++ src/config.c | 61 ++++++++++++++++++++++++++++++++++++++++ tests-clar/config/read.c | 33 ++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/include/git2/config.h b/include/git2/config.h index aed720fc8..28216467b 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -463,6 +463,18 @@ GIT_EXTERN(int) git_config_foreach( */ 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. * diff --git a/src/config.c b/src/config.c index f7ea6f795..9d809f8f1 100644 --- a/src/config.c +++ b/src/config.c @@ -319,6 +319,8 @@ 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; @@ -380,6 +382,27 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter) 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; @@ -390,6 +413,14 @@ static void all_iter_free(git_config_iterator *_iter) 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; @@ -408,6 +439,36 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg) return 0; } +int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp) +{ + all_iter *iter; + int result; + + iter = git__calloc(1, sizeof(all_iter)); + GITERR_CHECK_ALLOC(iter); + + if (regexp != NULL) { + 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; + } else { + iter->parent.next = all_iter_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) { diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index 2fb511d9d..395f1cfdb 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -265,6 +265,7 @@ void test_config_read__iterator(void) count++; } + git_config_iterator_free(iter); cl_assert_equal_i(GIT_ITEROVER, ret); cl_assert_equal_i(7, count); @@ -318,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; From d8488b981c1410dd2de2f9fe99764bdae33ca607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Aug 2013 10:37:35 +0200 Subject: [PATCH 15/19] config: implement _foreach and _foreach_match on top of the iterator directly Use a glob iterator instead of going through git_config_backend_foreach_match. This function is left as it's exposed in the API. --- src/config.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/config.c b/src/config.c index 9d809f8f1..3881d73dd 100644 --- a/src/config.c +++ b/src/config.c @@ -455,12 +455,12 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf } iter->parent.next = all_iter_glob_next; + iter->parent.free = all_iter_glob_free; } else { iter->parent.next = all_iter_next; + iter->parent.free = all_iter_free; } - iter->parent.free = all_iter_glob_free; - iter->i = cfg->files.length; iter->cfg = cfg; @@ -527,18 +527,27 @@ int git_config_foreach_match( 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 = git_config_backend_foreach_match(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; } /************** From d8289b9fb416cfe4f83eeb38fe324c077bada3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Aug 2013 11:03:13 +0200 Subject: [PATCH 16/19] config: handle empty backends when iterating --- src/config.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 3881d73dd..061765ac0 100644 --- a/src/config.c +++ b/src/config.c @@ -375,7 +375,12 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter) if (error < 0) return error; - return iter->current->next(entry, iter->current); + error = iter->current->next(entry, iter->current); + /* If this backend is empty, then keep going */ + if (error == GIT_ITEROVER) + continue; + + return error; } while(1); From 86c02614608eb7e9e78c09cdec69aeba689edae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 9 Aug 2013 11:05:02 +0200 Subject: [PATCH 17/19] config: deduplicate iterator creation When the glob iterator is passed NULL regexp, call the non-globbing iterator so we don't have to special-case which functions to call. --- src/config.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/config.c b/src/config.c index 061765ac0..ae4e4816a 100644 --- a/src/config.c +++ b/src/config.c @@ -449,23 +449,20 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf 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 (regexp != NULL) { - 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; - } else { - iter->parent.next = all_iter_next; - iter->parent.free = all_iter_free; + 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; From 43e5dda70249ede020a57f3888356a0dcb96da48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 12 Aug 2013 11:21:57 +0200 Subject: [PATCH 18/19] config: get rid of a useless asignment --- src/config_file.c | 4 +--- src/strmap.c | 2 -- src/strmap.h | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 4e564a51d..7c22be424 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -268,12 +268,10 @@ static int config_iterator_next( diskfile_backend *b = (diskfile_backend *) it->parent.backend; int err = 0; cvar_t * var; - const char* key; if (it->next_var == NULL) { - err = git_strmap_next(&key, (void**) &var, &(it->iter), b->values); + err = git_strmap_next((void**) &var, &(it->iter), b->values); } else { - key = it->next_var->entry->name; var = it->next_var; } diff --git a/src/strmap.c b/src/strmap.c index 1b07359d1..b26a13d1f 100644 --- a/src/strmap.c +++ b/src/strmap.c @@ -8,7 +8,6 @@ #include "strmap.h" int git_strmap_next( - const char **key, void **data, git_strmap_iter* iter, git_strmap *map) @@ -22,7 +21,6 @@ int git_strmap_next( continue; } - *key = git_strmap_key(map, *iter); *data = git_strmap_value_at(map, *iter); ++(*iter); diff --git a/src/strmap.h b/src/strmap.h index cb079b500..8276ab468 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -68,7 +68,6 @@ typedef khiter_t git_strmap_iter; #define git_strmap_end kh_end int git_strmap_next( - const char **key, void **data, git_strmap_iter* iter, git_strmap *map); From f4be8209afd3cc996667196a1e437aac21485691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 14 Aug 2013 00:45:05 +0200 Subject: [PATCH 19/19] config: don't special-case the multivar iterator Build it on top of the normal iterator instead, which lets use re-use a lot of code. --- include/git2/config.h | 2 +- include/git2/sys/config.h | 2 - src/config.c | 123 ++++++++++++--------- src/config.h | 3 + src/config_file.c | 201 +---------------------------------- tests-clar/config/multivar.c | 2 +- 6 files changed, 83 insertions(+), 250 deletions(-) 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++;