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] 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;