Commit a603c191578f7b33720e36e95421fcd58bc7abe4

Nico von Geyso 2013-03-18T21:02:36

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`

diff --git a/include/git2/config.h b/include/git2/config.h
index 827d435..f6fc74e 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 11e59cf..61dcce5 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 2a05854..b421b3b 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(&regex, regexp, REG_EXTENDED)) < 0) {
+			giterr_set_regex(&regex, result);
+			regfree(&regex);
+			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(&regex, entry.name, 0, NULL, 0) != 0)
+			continue;
+
+		/* abort iterator on non-zero return value */
+		if (fn(&entry, data)) {
+			giterr_clear();
+			result = GIT_EUSER;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	if (regexp != NULL)
+		regfree(&regex);
+
+	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 088f619..ff8f8fc 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(&regex, regexp, REG_EXTENDED)) < 0) {
-			giterr_set_regex(&regex, result);
-			regfree(&regex);
-			return -1;
-		}
-	}
+	*it = git__calloc(1, sizeof(git_config_file_iter));
+	GITERR_CHECK_ALLOC(it);
 
-	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);
+	(*it)->iter = git_strmap_begin(b->values);
+	(*it)->next = NULL;
 
-			/* skip non-matching keys if regexp was provided */
-			if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
-				continue;
+	return 0;
+}
 
-			/* abort iterator on non-zero return value */
-			if (fn(var->entry, data)) {
-				giterr_clear();
-				result = GIT_EUSER;
-				goto cleanup;
-			}
-		}
+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;
 	}
 
-cleanup:
-	if (regexp != NULL)
-		regfree(&regex);
+	if (err < 0) {
+		it->next = NULL;
+		return -1;
+	}
 
-	return result;
+	entry->name = key;
+	entry->value = var->entry->value;
+	entry->level = var->entry->level;
+	it->next = CVAR_LIST_NEXT(var);
+
+	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 7445859..d4a1a40 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);