Unverified Commit 45647b72 by Edward Thomson Committed by GitHub

Merge pull request #6615 from libgit2/ethomson/config_origin

config: provide an "origin" for config entries
parents af157381 40ce52e5
......@@ -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) {
......
......@@ -62,12 +62,32 @@ typedef enum {
* An entry in a configuration file
*/
typedef struct git_config_entry {
const char *name; /**< Name of the entry (normalised) */
const char *value; /**< String value of the entry */
unsigned int include_depth; /**< Depth of includes where this variable was found */
git_config_level_t level; /**< Which config file this was found in */
void GIT_CALLBACK(free)(struct git_config_entry *entry); /**< Free function for this entry */
void *payload; /**< Opaque value for the free function. Do not read or write */
/** Name of the configuration entry (normalized) */
const char *name;
/** Literal (string) value of the entry */
const char *value;
/** The type of backend that this entry exists in (eg, "file") */
const char *backend_type;
/**
* The path to the origin of this entry. For config files, this is
* the path to the file.
*/
const char *origin_path;
/** Depth of includes where this variable was found */
unsigned int include_depth;
/** Configuration level for the file this was found in */
git_config_level_t level;
/**
* Free function for this entry; for internal purposes. Callers
* should call `git_config_entry_free` to free data.
*/
void GIT_CALLBACK(free)(struct git_config_entry *entry);
} git_config_entry;
/**
......
......@@ -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,15 +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
*
* @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`
*/
extern int git_config_backend_from_string(git_config_backend **out, 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);
......
/*
* 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 "config_entries.h"
typedef struct config_entry_list {
struct config_entry_list *next;
struct config_entry_list *last;
git_config_entry *entry;
} config_entry_list;
typedef struct {
git_config_entry *entry;
bool multivar;
} config_entry_map_head;
typedef struct config_entries_iterator {
git_config_iterator parent;
git_config_entries *entries;
config_entry_list *head;
} config_entries_iterator;
struct git_config_entries {
git_refcount rc;
git_strmap *map;
config_entry_list *list;
};
int git_config_entries_new(git_config_entries **out)
{
git_config_entries *entries;
int error;
entries = git__calloc(1, sizeof(git_config_entries));
GIT_ERROR_CHECK_ALLOC(entries);
GIT_REFCOUNT_INC(entries);
if ((error = git_strmap_new(&entries->map)) < 0)
git__free(entries);
else
*out = entries;
return error;
}
int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry)
{
git_config_entry *duplicated;
int error;
duplicated = git__calloc(1, sizeof(git_config_entry));
GIT_ERROR_CHECK_ALLOC(duplicated);
duplicated->name = git__strdup(entry->name);
GIT_ERROR_CHECK_ALLOC(duplicated->name);
if (entry->value) {
duplicated->value = git__strdup(entry->value);
GIT_ERROR_CHECK_ALLOC(duplicated->value);
}
duplicated->level = entry->level;
duplicated->include_depth = entry->include_depth;
if ((error = git_config_entries_append(entries, duplicated)) < 0)
goto out;
out:
if (error && duplicated) {
git__free((char *) duplicated->name);
git__free((char *) duplicated->value);
git__free(duplicated);
}
return error;
}
int git_config_entries_dup(git_config_entries **out, git_config_entries *entries)
{
git_config_entries *result = NULL;
config_entry_list *head;
int error;
if ((error = git_config_entries_new(&result)) < 0)
goto out;
for (head = entries->list; head; head = head->next)
if ((git_config_entries_dup_entry(result, head->entry)) < 0)
goto out;
*out = result;
result = NULL;
out:
git_config_entries_free(result);
return error;
}
void git_config_entries_incref(git_config_entries *entries)
{
GIT_REFCOUNT_INC(entries);
}
static void config_entries_free(git_config_entries *entries)
{
config_entry_list *list = NULL, *next;
config_entry_map_head *head;
git_strmap_foreach_value(entries->map, head,
git__free((char *) head->entry->name); git__free(head)
);
git_strmap_free(entries->map);
list = entries->list;
while (list != NULL) {
next = list->next;
git__free((char *) list->entry->value);
git__free(list->entry);
git__free(list);
list = next;
}
git__free(entries);
}
void git_config_entries_free(git_config_entries *entries)
{
if (entries)
GIT_REFCOUNT_DEC(entries, config_entries_free);
}
int git_config_entries_append(git_config_entries *entries, git_config_entry *entry)
{
config_entry_list *list_head;
config_entry_map_head *map_head;
if ((map_head = git_strmap_get(entries->map, entry->name)) != NULL) {
map_head->multivar = true;
/*
* This is a micro-optimization for configuration files
* with a lot of same keys. As for multivars the entry's
* key will be the same for all entries, we can just free
* all except the first entry's name and just re-use it.
*/
git__free((char *) entry->name);
entry->name = map_head->entry->name;
} else {
map_head = git__calloc(1, sizeof(*map_head));
if ((git_strmap_set(entries->map, entry->name, map_head)) < 0)
return -1;
}
map_head->entry = entry;
list_head = git__calloc(1, sizeof(config_entry_list));
GIT_ERROR_CHECK_ALLOC(list_head);
list_head->entry = entry;
if (entries->list)
entries->list->last->next = list_head;
else
entries->list = list_head;
entries->list->last = list_head;
return 0;
}
int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key)
{
config_entry_map_head *entry;
if ((entry = git_strmap_get(entries->map, key)) == NULL)
return GIT_ENOTFOUND;
*out = entry->entry;
return 0;
}
int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key)
{
config_entry_map_head *entry;
if ((entry = git_strmap_get(entries->map, key)) == NULL)
return GIT_ENOTFOUND;
if (entry->multivar) {
git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
return -1;
}
if (entry->entry->include_depth) {
git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
return -1;
}
*out = entry->entry;
return 0;
}
static void config_iterator_free(git_config_iterator *iter)
{
config_entries_iterator *it = (config_entries_iterator *) iter;
git_config_entries_free(it->entries);
git__free(it);
}
static int config_iterator_next(
git_config_entry **entry,
git_config_iterator *iter)
{
config_entries_iterator *it = (config_entries_iterator *) iter;
if (!it->head)
return GIT_ITEROVER;
*entry = it->head->entry;
it->head = it->head->next;
return 0;
}
int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries)
{
config_entries_iterator *it;
it = git__calloc(1, sizeof(config_entries_iterator));
GIT_ERROR_CHECK_ALLOC(it);
it->parent.next = config_iterator_next;
it->parent.free = config_iterator_free;
it->head = entries->list;
it->entries = entries;
git_config_entries_incref(entries);
*out = &it->parent;
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 "common.h"
#include "git2/sys/config.h"
#include "config.h"
typedef struct git_config_entries git_config_entries;
int git_config_entries_new(git_config_entries **out);
int git_config_entries_dup(git_config_entries **out, git_config_entries *entries);
int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry);
void git_config_entries_incref(git_config_entries *entries);
void git_config_entries_free(git_config_entries *entries);
/* Add or append the new config option */
int git_config_entries_append(git_config_entries *entries, git_config_entry *entry);
int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key);
int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key);
int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries);
/*
* 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 "config_list.h"
typedef struct config_entry_list {
struct config_entry_list *next;
struct config_entry_list *last;
git_config_list_entry *entry;
} config_entry_list;
typedef struct {
git_config_list_entry *entry;
bool multivar;
} config_entry_map_head;
typedef struct config_list_iterator {
git_config_iterator parent;
git_config_list *list;
config_entry_list *head;
} config_list_iterator;
struct git_config_list {
git_refcount rc;
/* Interned strings - paths to config files or backend types */
git_strmap *strings;
/* Config entries */
git_strmap *map;
config_entry_list *entries;
};
int git_config_list_new(git_config_list **out)
{
git_config_list *config_list;
config_list = git__calloc(1, sizeof(git_config_list));
GIT_ERROR_CHECK_ALLOC(config_list);
GIT_REFCOUNT_INC(config_list);
if (git_strmap_new(&config_list->strings) < 0 ||
git_strmap_new(&config_list->map) < 0) {
git_strmap_free(config_list->strings);
git_strmap_free(config_list->map);
git__free(config_list);
return -1;
}
*out = config_list;
return 0;
}
int git_config_list_dup_entry(git_config_list *config_list, const git_config_entry *entry)
{
git_config_list_entry *duplicated;
int error;
duplicated = git__calloc(1, sizeof(git_config_list_entry));
GIT_ERROR_CHECK_ALLOC(duplicated);
duplicated->base.name = git__strdup(entry->name);
GIT_ERROR_CHECK_ALLOC(duplicated->base.name);
if (entry->value) {
duplicated->base.value = git__strdup(entry->value);
GIT_ERROR_CHECK_ALLOC(duplicated->base.value);
}
duplicated->base.backend_type = git_config_list_add_string(config_list, entry->backend_type);
GIT_ERROR_CHECK_ALLOC(duplicated->base.backend_type);
if (entry->origin_path) {
duplicated->base.origin_path = git_config_list_add_string(config_list, entry->origin_path);
GIT_ERROR_CHECK_ALLOC(duplicated->base.origin_path);
}
duplicated->base.level = entry->level;
duplicated->base.include_depth = entry->include_depth;
duplicated->base.free = git_config_list_entry_free;
duplicated->config_list = config_list;
if ((error = git_config_list_append(config_list, duplicated)) < 0)
goto out;
out:
if (error && duplicated) {
git__free((char *) duplicated->base.name);
git__free((char *) duplicated->base.value);
git__free(duplicated);
}
return error;
}
int git_config_list_dup(git_config_list **out, git_config_list *config_list)
{
git_config_list *result = NULL;
config_entry_list *head;
int error;
if ((error = git_config_list_new(&result)) < 0)
goto out;
for (head = config_list->entries; head; head = head->next)
if ((git_config_list_dup_entry(result, &head->entry->base)) < 0)
goto out;
*out = result;
result = NULL;
out:
git_config_list_free(result);
return error;
}
void git_config_list_incref(git_config_list *config_list)
{
GIT_REFCOUNT_INC(config_list);
}
static void config_list_free(git_config_list *config_list)
{
config_entry_list *entry_list = NULL, *next;
config_entry_map_head *head;
char *str;
git_strmap_foreach_value(config_list->strings, str, {
git__free(str);
});
git_strmap_free(config_list->strings);
git_strmap_foreach_value(config_list->map, head, {
git__free((char *) head->entry->base.name);
git__free(head);
});
git_strmap_free(config_list->map);
entry_list = config_list->entries;
while (entry_list != NULL) {
next = entry_list->next;
git__free((char *) entry_list->entry->base.value);
git__free(entry_list->entry);
git__free(entry_list);
entry_list = next;
}
git__free(config_list);
}
void git_config_list_free(git_config_list *config_list)
{
if (config_list)
GIT_REFCOUNT_DEC(config_list, config_list_free);
}
int git_config_list_append(git_config_list *config_list, git_config_list_entry *entry)
{
config_entry_list *list_head;
config_entry_map_head *map_head;
if ((map_head = git_strmap_get(config_list->map, entry->base.name)) != NULL) {
map_head->multivar = true;
/*
* This is a micro-optimization for configuration files
* with a lot of same keys. As for multivars the entry's
* key will be the same for all list, we can just free
* all except the first entry's name and just re-use it.
*/
git__free((char *) entry->base.name);
entry->base.name = map_head->entry->base.name;
} else {
map_head = git__calloc(1, sizeof(*map_head));
if ((git_strmap_set(config_list->map, entry->base.name, map_head)) < 0)
return -1;
}
map_head->entry = entry;
list_head = git__calloc(1, sizeof(config_entry_list));
GIT_ERROR_CHECK_ALLOC(list_head);
list_head->entry = entry;
if (config_list->entries)
config_list->entries->last->next = list_head;
else
config_list->entries = list_head;
config_list->entries->last = list_head;
return 0;
}
int git_config_list_get(git_config_list_entry **out, git_config_list *config_list, const char *key)
{
config_entry_map_head *entry;
if ((entry = git_strmap_get(config_list->map, key)) == NULL)
return GIT_ENOTFOUND;
*out = entry->entry;
return 0;
}
int git_config_list_get_unique(git_config_list_entry **out, git_config_list *config_list, const char *key)
{
config_entry_map_head *entry;
if ((entry = git_strmap_get(config_list->map, key)) == NULL)
return GIT_ENOTFOUND;
if (entry->multivar) {
git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
return -1;
}
if (entry->entry->base.include_depth) {
git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
return -1;
}
*out = entry->entry;
return 0;
}
static void config_iterator_free(git_config_iterator *iter)
{
config_list_iterator *it = (config_list_iterator *) iter;
git_config_list_free(it->list);
git__free(it);
}
static int config_iterator_next(
git_config_entry **entry,
git_config_iterator *iter)
{
config_list_iterator *it = (config_list_iterator *) iter;
if (!it->head)
return GIT_ITEROVER;
*entry = &it->head->entry->base;
it->head = it->head->next;
return 0;
}
int git_config_list_iterator_new(git_config_iterator **out, git_config_list *config_list)
{
config_list_iterator *it;
it = git__calloc(1, sizeof(config_list_iterator));
GIT_ERROR_CHECK_ALLOC(it);
it->parent.next = config_iterator_next;
it->parent.free = config_iterator_free;
it->head = config_list->entries;
it->list = config_list;
git_config_list_incref(config_list);
*out = &it->parent;
return 0;
}
/* release the map containing the entry as an equivalent to freeing it */
void git_config_list_entry_free(git_config_entry *e)
{
git_config_list_entry *entry = (git_config_list_entry *)e;
git_config_list_free(entry->config_list);
}
const char *git_config_list_add_string(
git_config_list *config_list,
const char *str)
{
const char *s;
if ((s = git_strmap_get(config_list->strings, str)) != NULL)
return s;
if ((s = git__strdup(str)) == NULL ||
git_strmap_set(config_list->strings, s, (void *)s) < 0)
return NULL;
return s;
}
/*
* 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 "common.h"
#include "git2/sys/config.h"
#include "config.h"
typedef struct git_config_list git_config_list;
typedef struct {
git_config_entry base;
git_config_list *config_list;
} git_config_list_entry;
int git_config_list_new(git_config_list **out);
int git_config_list_dup(git_config_list **out, git_config_list *list);
int git_config_list_dup_entry(git_config_list *list, const git_config_entry *entry);
void git_config_list_incref(git_config_list *list);
void git_config_list_free(git_config_list *list);
/* Add or append the new config option */
int git_config_list_append(git_config_list *list, git_config_list_entry *entry);
int git_config_list_get(git_config_list_entry **out, git_config_list *list, const char *key);
int git_config_list_get_unique(git_config_list_entry **out, git_config_list *list, const char *key);
int git_config_list_iterator_new(git_config_iterator **out, git_config_list *list);
const char *git_config_list_add_string(git_config_list *list, const char *str);
void git_config_list_entry_free(git_config_entry *entry);
......@@ -9,16 +9,29 @@
#include "config_backend.h"
#include "config_parse.h"
#include "config_entries.h"
#include "config_list.h"
#include "strlist.h"
typedef struct {
git_config_backend parent;
git_config_entries *entries;
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 {
git_config_entries *entries;
const char *backend_type;
const char *origin_path;
git_config_list *config_list;
git_config_level_t level;
} config_memory_parse_data;
......@@ -39,7 +52,7 @@ static int read_variable_cb(
{
config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
git_str buf = GIT_STR_INIT;
git_config_entry *entry;
git_config_list_entry *entry;
const char *c;
int result;
......@@ -62,35 +75,46 @@ static int read_variable_cb(
if (git_str_oom(&buf))
return -1;
entry = git__calloc(1, sizeof(git_config_entry));
entry = git__calloc(1, sizeof(git_config_list_entry));
GIT_ERROR_CHECK_ALLOC(entry);
entry->name = git_str_detach(&buf);
entry->value = var_value ? git__strdup(var_value) : NULL;
entry->level = parse_data->level;
entry->include_depth = 0;
if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
entry->base.name = git_str_detach(&buf);
entry->base.value = var_value ? git__strdup(var_value) : NULL;
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;
if ((result = git_config_list_append(parse_data->config_list, entry)) < 0)
return result;
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.entries = memory_backend->entries;
parse_data.backend_type = git_config_list_add_string(
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:
......@@ -98,10 +122,85 @@ 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;
return git_config_entries_get(out, memory_backend->entries, key);
git_config_list_entry *entry;
int error;
if ((error = git_config_list_get(&entry, memory_backend->config_list, key)) != 0)
return error;
*out = &entry->base;
return 0;
}
static int config_memory_iterator(
......@@ -109,18 +208,18 @@ static int config_memory_iterator(
git_config_backend *backend)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
git_config_entries *entries;
git_config_list *config_list;
int error;
if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0)
if ((error = git_config_list_dup(&config_list, memory_backend->config_list)) < 0)
goto out;
if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
if ((error = git_config_list_iterator_new(iter, config_list)) < 0)
goto out;
out:
/* Let iterator delete duplicated entries when it's done */
git_config_entries_free(entries);
/* Let iterator delete duplicated config_list when it's done */
git_config_list_free(config_list);
return error;
}
......@@ -177,28 +276,24 @@ static void config_memory_free(git_config_backend *_backend)
if (backend == NULL)
return;
git_config_entries_free(backend->entries);
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 *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_entries_new(&backend->entries) < 0) {
git__free(backend);
return -1;
}
if (git_str_set(&backend->cfg, cfg, len) < 0) {
git_config_entries_free(backend->entries);
git__free(backend);
return -1;
}
if (git_config_list_new(&backend->config_list) < 0)
goto on_error;
backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
backend->parent.readonly = 1;
......@@ -214,7 +309,66 @@ int git_config_backend_from_string(git_config_backend **out, const char *cfg, si
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;
}
......@@ -8,12 +8,12 @@
#include "config_backend.h"
#include "config.h"
#include "config_entries.h"
#include "config_list.h"
typedef struct {
git_config_backend parent;
git_mutex values_mutex;
git_config_entries *entries;
git_config_list *config_list;
git_config_backend *source;
} config_snapshot_backend;
......@@ -28,31 +28,24 @@ static int config_snapshot_iterator(
struct git_config_backend *backend)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent);
git_config_entries *entries = NULL;
git_config_list *config_list = NULL;
int error;
if ((error = git_config_entries_dup(&entries, b->entries)) < 0 ||
(error = git_config_entries_iterator_new(iter, entries)) < 0)
if ((error = git_config_list_dup(&config_list, b->config_list)) < 0 ||
(error = git_config_list_iterator_new(iter, config_list)) < 0)
goto out;
out:
/* Let iterator delete duplicated entries when it's done */
git_config_entries_free(entries);
/* Let iterator delete duplicated config_list when it's done */
git_config_list_free(config_list);
return error;
}
/* release the map containing the entry as an equivalent to freeing it */
static void config_snapshot_entry_free(git_config_entry *entry)
{
git_config_entries *entries = (git_config_entries *) entry->payload;
git_config_entries_free(entries);
}
static int config_snapshot_get(git_config_backend *cfg, const char *key, git_config_entry **out)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
git_config_entries *entries = NULL;
git_config_entry *entry;
git_config_list *config_list = NULL;
git_config_list_entry *entry;
int error = 0;
if (git_mutex_lock(&b->values_mutex) < 0) {
......@@ -60,19 +53,16 @@ static int config_snapshot_get(git_config_backend *cfg, const char *key, git_con
return -1;
}
entries = b->entries;
git_config_entries_incref(entries);
config_list = b->config_list;
git_config_list_incref(config_list);
git_mutex_unlock(&b->values_mutex);
if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
git_config_entries_free(entries);
if ((error = (git_config_list_get(&entry, config_list, key))) < 0) {
git_config_list_free(config_list);
return error;
}
entry->free = config_snapshot_entry_free;
entry->payload = entries;
*out = entry;
*out = &entry->base;
return 0;
}
......@@ -135,7 +125,7 @@ static void config_snapshot_free(git_config_backend *_backend)
if (backend == NULL)
return;
git_config_entries_free(backend->entries);
git_config_list_free(backend->config_list);
git_mutex_free(&backend->values_mutex);
git__free(backend);
}
......@@ -143,7 +133,7 @@ static void config_snapshot_free(git_config_backend *_backend)
static int config_snapshot_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
git_config_entries *entries = NULL;
git_config_list *config_list = NULL;
git_config_iterator *it = NULL;
git_config_entry *entry;
int error;
......@@ -152,12 +142,12 @@ static int config_snapshot_open(git_config_backend *cfg, git_config_level_t leve
GIT_UNUSED(level);
GIT_UNUSED(repo);
if ((error = git_config_entries_new(&entries)) < 0 ||
if ((error = git_config_list_new(&config_list)) < 0 ||
(error = b->source->iterator(&it, b->source)) < 0)
goto out;
while ((error = git_config_next(&entry, it)) == 0)
if ((error = git_config_entries_dup_entry(entries, entry)) < 0)
if ((error = git_config_list_dup_entry(config_list, entry)) < 0)
goto out;
if (error < 0) {
......@@ -166,12 +156,12 @@ static int config_snapshot_open(git_config_backend *cfg, git_config_level_t leve
error = 0;
}
b->entries = entries;
b->config_list = config_list;
out:
git_config_iterator_free(it);
if (error)
git_config_entries_free(entries);
git_config_list_free(config_list);
return error;
}
......
/*
* 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, 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, 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));
}
......@@ -495,6 +495,8 @@ void test_config_read__read_git_config_entry(void)
cl_assert_equal_s("core.dummy2", entry->name);
cl_assert_equal_s("42", entry->value);
cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
cl_assert_equal_s("file", entry->backend_type);
cl_assert_equal_s(cl_fixture("config/config9"), entry->origin_path);
git_config_entry_free(entry);
git_config_free(cfg);
......
......@@ -79,6 +79,7 @@ void test_config_snapshot__multivar(void)
void test_config_snapshot__includes(void)
{
git_config_entry *entry;
int i;
cl_git_mkfile("including", "[include]\npath = included");
......@@ -99,6 +100,16 @@ void test_config_snapshot__includes(void)
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
/* Ensure that the config entry is populated with origin */
cl_git_pass(git_config_get_entry(&entry, snapshot, "section.key"));
cl_assert_equal_s("section.key", entry->name);
cl_assert_equal_s("1", entry->value);
cl_assert_equal_s("file", entry->backend_type);
cl_assert_equal_s("./included", entry->origin_path);
git_config_entry_free(entry);
cl_git_pass(p_unlink("including"));
cl_git_pass(p_unlink("included"));
}
......@@ -106,6 +117,7 @@ void test_config_snapshot__includes(void)
void test_config_snapshot__snapshot(void)
{
git_config *snapshot_snapshot;
git_config_entry *entry;
int i;
cl_git_mkfile("configfile", "[section]\nkey = 1\n");
......@@ -118,22 +130,49 @@ void test_config_snapshot__snapshot(void)
cl_git_pass(git_config_get_int32(&i, snapshot_snapshot, "section.key"));
cl_assert_equal_i(i, 1);
/* Ensure that the config entry is populated with origin */
cl_git_pass(git_config_get_entry(&entry, snapshot_snapshot, "section.key"));
cl_assert_equal_s("section.key", entry->name);
cl_assert_equal_s("1", entry->value);
cl_assert_equal_s("file", entry->backend_type);
cl_assert_equal_s("configfile", entry->origin_path);
git_config_entry_free(entry);
git_config_free(snapshot_snapshot);
cl_git_pass(p_unlink("configfile"));
}
void test_config_snapshot__snapshot_from_in_memony(void)
void test_config_snapshot__snapshot_from_in_memory(void)
{
const char *configuration = "[section]\nkey = 1\n";
git_config_backend *backend;
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, 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));
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
/* Ensure that the config entry is populated with origin */
cl_git_pass(git_config_get_entry(&entry, snapshot, "section.key"));
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_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