mirror of
https://github.com/libgit2/libgit2.git
synced 2026-01-25 02:56:17 +00:00
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.
This commit is contained in:
@@ -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 ((backend = git__calloc(1, sizeof(config_memory_backend))) == NULL)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
*out = (git_config_backend *)backend;
|
||||
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;
|
||||
}
|
||||
|
||||
42
src/util/strlist.c
Normal file
42
src/util/strlist.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
16
src/util/strlist.h
Normal file
16
src/util/strlist.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user