Commit 40ce52e5 by Edward Thomson

config: provide two memory-backed config backends

Provide two memory-backed configuration backends -- one that takes a
string in config file format `[section] key=value` and one that takes a
list of strings in `section.key=value` format.
parent 248ac08c
......@@ -43,7 +43,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
goto out;
}
if ((err = git_config_backend_from_string(&backend, (const char*)data, size)) != 0) {
if ((err = git_config_backend_from_string(&backend, (const char*)data, size, NULL)) != 0) {
goto out;
}
if ((err = git_config_add_backend(cfg, backend, 0, NULL, 0)) != 0) {
......
......@@ -125,6 +125,57 @@ GIT_EXTERN(int) git_config_add_backend(
const git_repository *repo,
int force);
/** Options for in-memory configuration backends. */
typedef struct {
unsigned int version;
/**
* The type of this backend (eg, "command line"). If this is
* NULL, then this will be "in-memory".
*/
const char *backend_type;
/**
* The path to the origin; if this is NULL then it will be
* left unset in the resulting configuration entries.
*/
const char *origin_path;
} git_config_backend_memory_options;
#define GIT_CONFIG_BACKEND_MEMORY_OPTIONS_VERSION 1
#define GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT { GIT_CONFIG_BACKEND_MEMORY_OPTIONS_VERSION }
/**
* Create an in-memory configuration backend from a string in standard
* git configuration file format.
*
* @param out the new backend
* @param cfg the configuration that is to be parsed
* @param len the length of the string pointed to by `cfg`
* @param opts the options to initialize this backend with, or NULL
*/
extern int git_config_backend_from_string(
git_config_backend **out,
const char *cfg,
size_t len,
git_config_backend_memory_options *opts);
/**
* Create an in-memory configuration backend from a list of name/value
* pairs.
*
* @param out the new backend
* @param values the configuration values to set (in "key=value" format)
* @param len the length of the values array
* @param opts the options to initialize this backend with, or NULL
*/
extern int git_config_backend_from_values(
git_config_backend **out,
const char **values,
size_t len,
git_config_backend_memory_options *opts);
/** @} */
GIT_END_DECL
#endif
......@@ -37,21 +37,6 @@ extern int git_config_backend_from_file(git_config_backend **out, const char *pa
*/
extern int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source);
/**
* Create an in-memory configuration file backend from a string in standard
* git configuration file format.
*
* @param out the new backend
* @param origin the name of the origin to use (or NULL for "memory")
* @param cfg the configuration that is to be parsed
* @param len the length of the string pointed to by `cfg`
*/
extern int git_config_backend_from_string(
git_config_backend **out,
const char *origin,
const char *cfg,
size_t len);
GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
{
return cfg->open(cfg, level, repo);
......
......@@ -10,16 +10,27 @@
#include "config_backend.h"
#include "config_parse.h"
#include "config_list.h"
#include "strlist.h"
typedef struct {
git_config_backend parent;
char *type;
char *backend_type;
char *origin_path;
git_config_list *config_list;
/* Configuration data in the config file format */
git_str cfg;
/* Array of key=value pairs */
char **values;
size_t values_len;
} config_memory_backend;
typedef struct {
const char *backend_type;
const char *origin_path;
git_config_list *config_list;
git_config_level_t level;
} config_memory_parse_data;
......@@ -71,6 +82,7 @@ static int read_variable_cb(
entry->base.level = parse_data->level;
entry->base.include_depth = 0;
entry->base.backend_type = parse_data->backend_type;
entry->base.origin_path = parse_data->origin_path;
entry->base.free = git_config_list_entry_free;
entry->config_list = parse_data->config_list;
......@@ -80,25 +92,29 @@ static int read_variable_cb(
return result;
}
static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
static int parse_config(
config_memory_backend *memory_backend,
git_config_level_t level)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
git_config_parser parser = GIT_PARSE_CTX_INIT;
config_memory_parse_data parse_data;
int error;
GIT_UNUSED(repo);
if ((error = git_config_parser_init(&parser, "in-memory", memory_backend->cfg.ptr,
memory_backend->cfg.size)) < 0)
if ((error = git_config_parser_init(&parser, "in-memory",
memory_backend->cfg.ptr, memory_backend->cfg.size)) < 0)
goto out;
parse_data.backend_type = git_config_list_add_string(
memory_backend->config_list, memory_backend->type);
memory_backend->config_list, memory_backend->backend_type);
parse_data.origin_path = memory_backend->origin_path ?
git_config_list_add_string(memory_backend->config_list,
memory_backend->origin_path) :
NULL;
parse_data.config_list = memory_backend->config_list;
parse_data.level = level;
if ((error = git_config_parse(&parser, NULL, read_variable_cb, NULL, NULL, &parse_data)) < 0)
if ((error = git_config_parse(&parser, NULL, read_variable_cb,
NULL, NULL, &parse_data)) < 0)
goto out;
out:
......@@ -106,6 +122,74 @@ out:
return error;
}
static int parse_values(
config_memory_backend *memory_backend,
git_config_level_t level)
{
git_config_list_entry *entry;
const char *eql, *backend_type, *origin_path;
size_t name_len, i;
backend_type = git_config_list_add_string(
memory_backend->config_list, memory_backend->backend_type);
GIT_ERROR_CHECK_ALLOC(backend_type);
origin_path = memory_backend->origin_path ?
git_config_list_add_string(memory_backend->config_list,
memory_backend->origin_path) :
NULL;
for (i = 0; i < memory_backend->values_len; i++) {
eql = strchr(memory_backend->values[i], '=');
name_len = eql - memory_backend->values[i];
if (name_len == 0) {
git_error_set(GIT_ERROR_CONFIG, "empty config key");
return -1;
}
entry = git__calloc(1, sizeof(git_config_list_entry));
GIT_ERROR_CHECK_ALLOC(entry);
entry->base.name = git__strndup(memory_backend->values[i], name_len);
GIT_ERROR_CHECK_ALLOC(entry->base.name);
if (eql) {
entry->base.value = git__strdup(eql + 1);
GIT_ERROR_CHECK_ALLOC(entry->base.value);
}
entry->base.level = level;
entry->base.include_depth = 0;
entry->base.backend_type = backend_type;
entry->base.origin_path = origin_path;
entry->base.free = git_config_list_entry_free;
entry->config_list = memory_backend->config_list;
if (git_config_list_append(memory_backend->config_list, entry) < 0)
return -1;
}
return 0;
}
static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
GIT_UNUSED(repo);
if (memory_backend->cfg.size > 0 &&
parse_config(memory_backend, level) < 0)
return -1;
if (memory_backend->values_len > 0 &&
parse_values(memory_backend, level) < 0)
return -1;
return 0;
}
static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
......@@ -192,36 +276,24 @@ static void config_memory_free(git_config_backend *_backend)
if (backend == NULL)
return;
git__free(backend->type);
git__free(backend->origin_path);
git__free(backend->backend_type);
git_config_list_free(backend->config_list);
git_strlist_free(backend->values, backend->values_len);
git_str_dispose(&backend->cfg);
git__free(backend);
}
int git_config_backend_from_string(
git_config_backend **out,
const char *backend_type,
const char *cfg,
size_t len)
static config_memory_backend *config_backend_new(
git_config_backend_memory_options *opts)
{
config_memory_backend *backend;
backend = git__calloc(1, sizeof(config_memory_backend));
GIT_ERROR_CHECK_ALLOC(backend);
if (git_config_list_new(&backend->config_list) < 0) {
git__free(backend);
return -1;
}
if (git_str_set(&backend->cfg, cfg, len) < 0) {
git_config_list_free(backend->config_list);
git__free(backend);
return -1;
}
if ((backend = git__calloc(1, sizeof(config_memory_backend))) == NULL)
return NULL;
backend->type = git__strdup(backend_type ? backend_type : "in-memory");
GIT_ERROR_CHECK_ALLOC(backend->type);
if (git_config_list_new(&backend->config_list) < 0)
goto on_error;
backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
backend->parent.readonly = 1;
......@@ -237,7 +309,66 @@ int git_config_backend_from_string(
backend->parent.snapshot = git_config_backend_snapshot;
backend->parent.free = config_memory_free;
backend->backend_type = git__strdup(opts && opts->backend_type ?
opts->backend_type : "in-memory");
if (backend->backend_type == NULL)
goto on_error;
if (opts && opts->origin_path &&
(backend->origin_path = git__strdup(opts->origin_path)) == NULL)
goto on_error;
return backend;
on_error:
git_config_list_free(backend->config_list);
git__free(backend->origin_path);
git__free(backend->backend_type);
git__free(backend);
return NULL;
}
int git_config_backend_from_string(
git_config_backend **out,
const char *cfg,
size_t len,
git_config_backend_memory_options *opts)
{
config_memory_backend *backend;
if ((backend = config_backend_new(opts)) == NULL)
return -1;
if (git_str_set(&backend->cfg, cfg, len) < 0) {
git_config_list_free(backend->config_list);
git__free(backend);
return -1;
}
*out = (git_config_backend *)backend;
return 0;
}
int git_config_backend_from_values(
git_config_backend **out,
const char **values,
size_t len,
git_config_backend_memory_options *opts)
{
config_memory_backend *backend;
if ((backend = config_backend_new(opts)) == NULL)
return -1;
if (git_strlist_copy(&backend->values, values, len) < 0) {
git_config_list_free(backend->config_list);
git__free(backend);
return -1;
}
backend->values_len = len;
*out = (git_config_backend *)backend;
return 0;
}
/*
* 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 <stdio.h>
#include "git2_util.h"
#include "vector.h"
#include "strlist.h"
int git_strlist_copy(char ***out, const char **in, size_t len)
{
char **dup;
size_t i;
dup = git__calloc(len, sizeof(char *));
GIT_ERROR_CHECK_ALLOC(dup);
for (i = 0; i < len; i++) {
dup[i] = git__strdup(in[i]);
GIT_ERROR_CHECK_ALLOC(dup[i]);
}
*out = dup;
return 0;
}
void git_strlist_free(char **strings, size_t len)
{
size_t i;
if (!strings)
return;
for (i = 0; i < len; i++)
git__free(strings[i]);
git__free(strings);
}
/*
* 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.
*/
#ifndef INCLUDE_runtime_h__
#define INCLUDE_runtime_h__
#include "git2_util.h"
extern int git_strlist_copy(char ***out, const char **in, size_t len);
extern void git_strlist_free(char **strings, size_t len);
#endif
......@@ -34,8 +34,13 @@ static int contains_all_cb(const git_config_entry *entry, void *payload)
int i;
for (i = 0; entries[i].name; i++) {
if (strcmp(entries[i].name, entry->name) ||
strcmp(entries[i].value , entry->value))
if (strcmp(entries[i].name, entry->name))
continue;
if ((entries[i].value == NULL) ^ (entry->value == NULL))
continue;
if (entry->value && strcmp(entries[i].value , entry->value))
continue;
if (entries[i].seen)
......@@ -61,7 +66,23 @@ static void assert_config_contains_all(git_config_backend *backend,
static void setup_backend(const char *cfg)
{
cl_git_pass(git_config_backend_from_string(&backend, "test", cfg, strlen(cfg)));
git_config_backend_memory_options opts =
GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
opts.backend_type = "test";
cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg), &opts));
cl_git_pass(git_config_backend_open(backend, 0, NULL));
}
static void setup_values_backend(const char **values, size_t len)
{
git_config_backend_memory_options opts =
GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
opts.backend_type = "test";
cl_git_pass(git_config_backend_from_values(&backend, values, len, &opts));
cl_git_pass(git_config_backend_open(backend, 0, NULL));
}
......@@ -88,7 +109,13 @@ void test_config_memory__malformed_fails_to_open(void)
const char *cfg =
"[general\n"
"foo=bar\n";
cl_git_pass(git_config_backend_from_string(&backend, "test", cfg, strlen(cfg)));
git_config_backend_memory_options opts =
GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
opts.backend_type = "test";
cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg), &opts));
cl_git_fail(git_config_backend_open(backend, 0, NULL));
}
......@@ -137,3 +164,43 @@ void test_config_memory__foreach_sees_multivar(void)
"foo=bar2\n");
assert_config_contains_all(backend, entries);
}
void test_config_memory__values(void)
{
const char *values[] = {
"general.foo=bar1",
"general.foo=bar2",
"other.key=value",
"empty.value=",
"no.value",
};
struct expected_entry entries[] = {
{ "general.foo", "bar1", 0 },
{ "general.foo", "bar2", 0 },
{ "other.key", "value", 0 },
{ "empty.value", "", 0 },
{ "no.value", NULL, 0 },
{ NULL, NULL, 0 }
};
setup_values_backend(values, 5);
assert_config_contains_all(backend, entries);
}
void test_config_memory__valid_values(void)
{
const char *values[] = {
"general.foo=bar1",
"=bar2",
"other.key=value"
};
git_config_backend_memory_options opts =
GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
opts.backend_type = "test";
cl_git_pass(git_config_backend_from_values(&backend, values, 3, &opts));
cl_git_fail(git_config_backend_open(backend, 0, NULL));
}
......@@ -152,8 +152,14 @@ void test_config_snapshot__snapshot_from_in_memory(void)
git_config_entry *entry;
int i;
git_config_backend_memory_options opts =
GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
opts.backend_type = "test";
opts.origin_path = "hello";
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_backend_from_string(&backend, "test", configuration, strlen(configuration)));
cl_git_pass(git_config_backend_from_string(&backend, configuration, strlen(configuration), &opts));
cl_git_pass(git_config_add_backend(cfg, backend, 0, NULL, 0));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
......@@ -166,7 +172,7 @@ void test_config_snapshot__snapshot_from_in_memory(void)
cl_assert_equal_s("section.key", entry->name);
cl_assert_equal_s("1", entry->value);
cl_assert_equal_s("test", entry->backend_type);
cl_assert_equal_p(NULL, entry->origin_path);
cl_assert_equal_s("hello", entry->origin_path);
git_config_entry_free(entry);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment