Unverified Commit 0530d7d9 by Carlos Martín Nieto Committed by GitHub

Merge pull request #4767 from pks-t/pks/config-mem

In-memory configuration
parents ba1cd495 2be39cef
......@@ -12,7 +12,7 @@
#include "git2/sys/config.h"
#include "vector.h"
#include "buf_text.h"
#include "config_file.h"
#include "config_backend.h"
#include "transaction.h"
#if GIT_WIN32
# include <windows.h>
......@@ -31,30 +31,30 @@ void git_config_entry_free(git_config_entry *entry)
typedef struct {
git_refcount rc;
git_config_backend *file;
git_config_backend *backend;
git_config_level_t level;
} file_internal;
} backend_internal;
static void file_internal_free(file_internal *internal)
static void backend_internal_free(backend_internal *internal)
{
git_config_backend *file;
git_config_backend *backend;
file = internal->file;
file->free(file);
backend = internal->backend;
backend->free(backend);
git__free(internal);
}
static void config_free(git_config *cfg)
{
size_t i;
file_internal *internal;
backend_internal *internal;
for (i = 0; i < cfg->files.length; ++i) {
internal = git_vector_get(&cfg->files, i);
GIT_REFCOUNT_DEC(internal, file_internal_free);
for (i = 0; i < cfg->backends.length; ++i) {
internal = git_vector_get(&cfg->backends, i);
GIT_REFCOUNT_DEC(internal, backend_internal_free);
}
git_vector_free(&cfg->files);
git_vector_free(&cfg->backends);
git__memzero(cfg, sizeof(*cfg));
git__free(cfg);
......@@ -70,8 +70,8 @@ void git_config_free(git_config *cfg)
static int config_backend_cmp(const void *a, const void *b)
{
const file_internal *bk_a = (const file_internal *)(a);
const file_internal *bk_b = (const file_internal *)(b);
const backend_internal *bk_a = (const backend_internal *)(a);
const backend_internal *bk_b = (const backend_internal *)(b);
return bk_b->level - bk_a->level;
}
......@@ -85,7 +85,7 @@ int git_config_new(git_config **out)
memset(cfg, 0x0, sizeof(git_config));
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
git__free(cfg);
return -1;
}
......@@ -114,7 +114,7 @@ int git_config_add_file_ondisk(
return -1;
}
if (git_config_file__ondisk(&file, path) < 0)
if (git_config_backend_from_file(&file, path) < 0)
return -1;
if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
......@@ -151,7 +151,7 @@ int git_config_snapshot(git_config **out, git_config *in)
{
int error = 0;
size_t i;
file_internal *internal;
backend_internal *internal;
git_config *config;
*out = NULL;
......@@ -159,10 +159,10 @@ int git_config_snapshot(git_config **out, git_config *in)
if (git_config_new(&config) < 0)
return -1;
git_vector_foreach(&in->files, i, internal) {
git_vector_foreach(&in->backends, i, internal) {
git_config_backend *b;
if ((error = internal->file->snapshot(&b, internal->file)) < 0)
if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
break;
if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
......@@ -179,24 +179,24 @@ int git_config_snapshot(git_config **out, git_config *in)
return error;
}
static int find_internal_file_by_level(
file_internal **internal_out,
static int find_backend_by_level(
backend_internal **out,
const git_config *cfg,
git_config_level_t level)
{
int pos = -1;
file_internal *internal;
backend_internal *internal;
size_t i;
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
* which has the highest level. As config files are stored in a vector
* sorted by decreasing order of level, getting the file at position 0
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
* which has the highest level. As config backends are stored in a vector
* sorted by decreasing order of level, getting the backend at position 0
* will do the job.
*/
if (level == GIT_CONFIG_HIGHEST_LEVEL) {
pos = 0;
} else {
git_vector_foreach(&cfg->files, i, internal) {
git_vector_foreach(&cfg->backends, i, internal) {
if (internal->level == level)
pos = (int)i;
}
......@@ -204,34 +204,34 @@ static int find_internal_file_by_level(
if (pos == -1) {
giterr_set(GITERR_CONFIG,
"no config file exists for the given level '%i'", (int)level);
"no configuration exists for the given level '%i'", (int)level);
return GIT_ENOTFOUND;
}
*internal_out = git_vector_get(&cfg->files, pos);
*out = git_vector_get(&cfg->backends, pos);
return 0;
}
static int duplicate_level(void **old_raw, void *new_raw)
{
file_internal **old = (file_internal **)old_raw;
backend_internal **old = (backend_internal **)old_raw;
GIT_UNUSED(new_raw);
giterr_set(GITERR_CONFIG, "a file with the same level (%i) has already been added to the config", (int)(*old)->level);
giterr_set(GITERR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level);
return GIT_EEXISTS;
}
static void try_remove_existing_file_internal(
static void try_remove_existing_backend(
git_config *cfg,
git_config_level_t level)
{
int pos = -1;
file_internal *internal;
backend_internal *internal;
size_t i;
git_vector_foreach(&cfg->files, i, internal) {
git_vector_foreach(&cfg->backends, i, internal) {
if (internal->level == level)
pos = (int)i;
}
......@@ -239,32 +239,32 @@ static void try_remove_existing_file_internal(
if (pos == -1)
return;
internal = git_vector_get(&cfg->files, pos);
internal = git_vector_get(&cfg->backends, pos);
if (git_vector_remove(&cfg->files, pos) < 0)
if (git_vector_remove(&cfg->backends, pos) < 0)
return;
GIT_REFCOUNT_DEC(internal, file_internal_free);
GIT_REFCOUNT_DEC(internal, backend_internal_free);
}
static int git_config__add_internal(
git_config *cfg,
file_internal *internal,
backend_internal *internal,
git_config_level_t level,
int force)
{
int result;
/* delete existing config file for level if it exists */
/* delete existing config backend for level if it exists */
if (force)
try_remove_existing_file_internal(cfg, level);
try_remove_existing_backend(cfg, level);
if ((result = git_vector_insert_sorted(&cfg->files,
if ((result = git_vector_insert_sorted(&cfg->backends,
internal, &duplicate_level)) < 0)
return result;
git_vector_sort(&cfg->files);
internal->file->cfg = cfg;
git_vector_sort(&cfg->backends);
internal->backend->cfg = cfg;
GIT_REFCOUNT_INC(internal);
......@@ -285,10 +285,10 @@ int git_config_open_level(
git_config_level_t level)
{
git_config *cfg;
file_internal *internal;
backend_internal *internal;
int res;
if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0)
if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0)
return res;
if ((res = git_config_new(&cfg)) < 0)
......@@ -306,27 +306,27 @@ int git_config_open_level(
int git_config_add_backend(
git_config *cfg,
git_config_backend *file,
git_config_backend *backend,
git_config_level_t level,
const git_repository *repo,
int force)
{
file_internal *internal;
backend_internal *internal;
int result;
assert(cfg && file);
assert(cfg && backend);
GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
GITERR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
if ((result = file->open(file, level, repo)) < 0)
if ((result = backend->open(backend, level, repo)) < 0)
return result;
internal = git__malloc(sizeof(file_internal));
internal = git__malloc(sizeof(backend_internal));
GITERR_CHECK_ALLOC(internal);
memset(internal, 0x0, sizeof(file_internal));
memset(internal, 0x0, sizeof(backend_internal));
internal->file = file;
internal->backend = backend;
internal->level = level;
if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
......@@ -351,11 +351,11 @@ typedef struct {
static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
{
file_internal *internal;
backend_internal *internal;
for (; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1);
if (!internal || !internal->file)
internal = git_vector_get(&cfg->backends, i - 1);
if (!internal || !internal->backend)
continue;
*out = i;
......@@ -368,7 +368,7 @@ static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
{
all_iter *iter = (all_iter *) _iter;
file_internal *internal;
backend_internal *internal;
git_config_backend *backend;
size_t i;
int error = 0;
......@@ -385,8 +385,8 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
if (find_next_backend(&i, iter->cfg, iter->i) < 0)
return GIT_ITEROVER;
internal = git_vector_get(&iter->cfg->files, i - 1);
backend = internal->file;
internal = git_vector_get(&iter->cfg->backends, i - 1);
backend = internal->backend;
iter->i = i - 1;
if (iter->current)
......@@ -461,7 +461,7 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
iter->parent.free = all_iter_free;
iter->parent.next = all_iter_next;
iter->i = cfg->files.length;
iter->i = cfg->backends.length;
iter->cfg = cfg;
*out = (git_config_iterator *) iter;
......@@ -488,7 +488,7 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
iter->parent.next = all_iter_glob_next;
iter->parent.free = all_iter_glob_free;
iter->i = cfg->files.length;
iter->i = cfg->backends.length;
iter->cfg = cfg;
*out = (git_config_iterator *) iter;
......@@ -592,38 +592,38 @@ static int get_backend_for_use(git_config_backend **out,
git_config *cfg, const char *name, backend_use use)
{
size_t i;
file_internal *f;
backend_internal *backend;
*out = NULL;
if (git_vector_length(&cfg->files) == 0) {
if (git_vector_length(&cfg->backends) == 0) {
giterr_set(GITERR_CONFIG,
"cannot %s value for '%s' when no config files exist",
"cannot %s value for '%s' when no config backends exist",
uses[use], name);
return GIT_ENOTFOUND;
}
git_vector_foreach(&cfg->files, i, f) {
if (!f->file->readonly) {
*out = f->file;
git_vector_foreach(&cfg->backends, i, backend) {
if (!backend->backend->readonly) {
*out = backend->backend;
return 0;
}
}
giterr_set(GITERR_CONFIG,
"cannot %s value for '%s' when all config files are readonly",
"cannot %s value for '%s' when all config backends are readonly",
uses[use], name);
return GIT_ENOTFOUND;
}
int git_config_delete_entry(git_config *cfg, const char *name)
{
git_config_backend *file;
git_config_backend *backend;
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
return GIT_ENOTFOUND;
return file->del(file, name);
return backend->del(backend, name);
}
int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
......@@ -646,17 +646,17 @@ int git_config_set_bool(git_config *cfg, const char *name, int value)
int git_config_set_string(git_config *cfg, const char *name, const char *value)
{
int error;
git_config_backend *file;
git_config_backend *backend;
if (!value) {
giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
return -1;
}
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0)
if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0)
return GIT_ENOTFOUND;
error = file->set(file, name, value);
error = backend->set(backend, name, value);
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
......@@ -722,7 +722,7 @@ static int get_entry(
const char *key = name;
char *normalized = NULL;
size_t i;
file_internal *internal;
backend_internal *internal;
*out = NULL;
......@@ -733,11 +733,11 @@ static int get_entry(
}
res = GIT_ENOTFOUND;
git_vector_foreach(&cfg->files, i, internal) {
if (!internal || !internal->file)
git_vector_foreach(&cfg->backends, i, internal) {
if (!internal || !internal->backend)
continue;
res = internal->file->get(internal->file, key, out);
res = internal->backend->get(internal->backend, key, out);
if (res != GIT_ENOTFOUND)
break;
}
......@@ -835,13 +835,13 @@ int git_config_get_bool(int *out, const git_config *cfg, const char *name)
static int is_readonly(const git_config *cfg)
{
size_t i;
file_internal *internal;
backend_internal *internal;
git_vector_foreach(&cfg->files, i, internal) {
if (!internal || !internal->file)
git_vector_foreach(&cfg->backends, i, internal) {
if (!internal || !internal->backend)
continue;
if (!internal->file->readonly)
if (!internal->backend->readonly)
return 0;
}
......@@ -1058,22 +1058,22 @@ on_error:
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
{
git_config_backend *file;
git_config_backend *backend;
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
return GIT_ENOTFOUND;
return file->set_multivar(file, name, regexp, value);
return backend->set_multivar(backend, name, regexp, value);
}
int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
{
git_config_backend *file;
git_config_backend *backend;
if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
return GIT_ENOTFOUND;
return file->del_multivar(file, name, regexp);
return backend->del_multivar(backend, name, regexp);
}
int git_config_next(git_config_entry **entry, git_config_iterator *iter)
......@@ -1179,17 +1179,17 @@ int git_config_open_default(git_config **out)
int git_config_lock(git_transaction **out, git_config *cfg)
{
int error;
git_config_backend *file;
file_internal *internal;
git_config_backend *backend;
backend_internal *internal;
internal = git_vector_get(&cfg->files, 0);
if (!internal || !internal->file) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
return -1;
}
file = internal->file;
backend = internal->backend;
if ((error = file->lock(file)) < 0)
if ((error = backend->lock(backend)) < 0)
return error;
return git_transaction_config_new(out, cfg);
......@@ -1197,18 +1197,18 @@ int git_config_lock(git_transaction **out, git_config *cfg)
int git_config_unlock(git_config *cfg, int commit)
{
git_config_backend *file;
file_internal *internal;
git_config_backend *backend;
backend_internal *internal;
internal = git_vector_get(&cfg->files, 0);
if (!internal || !internal->file) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
return -1;
}
file = internal->file;
backend = internal->backend;
return file->unlock(file, commit);
return backend->unlock(backend, commit);
}
/***********
......@@ -1376,6 +1376,30 @@ int git_config_parse_path(git_buf *out, const char *value)
return git_buf_sets(out, value);
}
static int normalize_section(char *start, char *end)
{
char *scan;
if (start == end)
return GIT_EINVALIDSPEC;
/* Validate and downcase range */
for (scan = start; *scan; ++scan) {
if (end && scan >= end)
break;
if (isalnum(*scan))
*scan = (char)git__tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
}
if (scan == start)
return GIT_EINVALIDSPEC;
return 0;
}
/* Take something the user gave us and make it nice for our hash function */
int git_config__normalize_name(const char *in, char **out)
{
......@@ -1393,8 +1417,8 @@ int git_config__normalize_name(const char *in, char **out)
goto invalid;
/* Validate and downcase up to first dot and after last dot */
if (git_config_file_normalize_section(name, fdot) < 0 ||
git_config_file_normalize_section(ldot + 1, NULL) < 0)
if (normalize_section(name, fdot) < 0 ||
normalize_section(ldot + 1, NULL) < 0)
goto invalid;
/* If there is a middle range, make sure it doesn't have newlines */
......@@ -1466,8 +1490,7 @@ int git_config_rename_section(
goto cleanup;
if (new_section_name != NULL &&
(error = git_config_file_normalize_section(
replace.ptr, strchr(replace.ptr, '.'))) < 0)
(error = normalize_section(replace.ptr, strchr(replace.ptr, '.'))) < 0)
{
giterr_set(
GITERR_CONFIG, "invalid config section '%s'", new_section_name);
......
......@@ -24,7 +24,7 @@
struct git_config {
git_refcount rc;
git_vector files;
git_vector backends;
};
extern int git_config__global_location(git_buf *buf);
......@@ -34,19 +34,6 @@ extern int git_config_rename_section(
const char *old_section_name, /* eg "branch.dummy" */
const char *new_section_name); /* NULL to drop the old section */
/**
* Create a configuration file backend for ondisk files
*
* These are the normal `.gitconfig` files that Core Git
* processes. Note that you first have to add this file to a
* configuration object before you can query it for configuration
* variables.
*
* @param out the new backend
* @param path where the config file is located
*/
extern int git_config_file__ondisk(git_config_backend **out, const char *path);
extern int git_config__normalize_name(const char *in, char **out);
/* internal only: does not normalize key and sets out to NULL if not found */
......
......@@ -12,36 +12,57 @@
#include "git2/sys/config.h"
#include "git2/config.h"
GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
/**
* Create a configuration file backend for ondisk files
*
* These are the normal `.gitconfig` files that Core Git
* processes. Note that you first have to add this file to a
* configuration object before you can query it for configuration
* variables.
*
* @param out the new backend
* @param path where the config file is located
*/
extern int git_config_backend_from_file(git_config_backend **out, const char *path);
/**
* Create an in-memory configuration file backend
*
* @param out the new backend
* @param cfg the configuration that is to be parsed
*/
extern int git_config_backend_from_string(git_config_backend **out, const char *cfg);
GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
{
return cfg->open(cfg, level, repo);
}
GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
GIT_INLINE(void) git_config_backend_free(git_config_backend *cfg)
{
if (cfg)
cfg->free(cfg);
}
GIT_INLINE(int) git_config_file_get_string(
GIT_INLINE(int) git_config_backend_get_string(
git_config_entry **out, git_config_backend *cfg, const char *name)
{
return cfg->get(cfg, name, out);
}
GIT_INLINE(int) git_config_file_set_string(
GIT_INLINE(int) git_config_backend_set_string(
git_config_backend *cfg, const char *name, const char *value)
{
return cfg->set(cfg, name, value);
}
GIT_INLINE(int) git_config_file_delete(
GIT_INLINE(int) git_config_backend_delete(
git_config_backend *cfg, const char *name)
{
return cfg->del(cfg, name);
}
GIT_INLINE(int) git_config_file_foreach(
GIT_INLINE(int) git_config_backend_foreach(
git_config_backend *cfg,
int (*fn)(const git_config_entry *entry, void *data),
void *data)
......@@ -49,25 +70,14 @@ GIT_INLINE(int) git_config_file_foreach(
return git_config_backend_foreach_match(cfg, NULL, fn, data);
}
GIT_INLINE(int) git_config_file_foreach_match(
git_config_backend *cfg,
const char *regexp,
int (*fn)(const git_config_entry *entry, void *data),
void *data)
{
return git_config_backend_foreach_match(cfg, regexp, fn, data);
}
GIT_INLINE(int) git_config_file_lock(git_config_backend *cfg)
GIT_INLINE(int) git_config_backend_lock(git_config_backend *cfg)
{
return cfg->lock(cfg);
}
GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success)
GIT_INLINE(int) git_config_backend_unlock(git_config_backend *cfg, int success)
{
return cfg->unlock(cfg, success);
}
extern int git_config_file_normalize_section(char *start, char *end);
#endif
/*
* 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 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;
};
static void config_entry_list_free(config_entry_list *list)
{
config_entry_list *next;
while (list != NULL) {
next = list->next;
git__free((char*) list->entry->name);
git__free((char *) list->entry->value);
git__free(list->entry);
git__free(list);
list = next;
};
}
static void config_entry_list_append(config_entry_list **list, config_entry_list *entry)
{
if (*list)
(*list)->last->next = entry;
else
*list = entry;
(*list)->last = entry;
}
int git_config_entries_new(git_config_entries **out)
{
git_config_entries *entries;
int error;
entries = git__calloc(1, sizeof(git_config_entries));
GITERR_CHECK_ALLOC(entries);
GIT_REFCOUNT_INC(entries);
if ((error = git_strmap_alloc(&entries->map)) < 0)
git__free(entries);
else
*out = entries;
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) {
git_config_entry *dup;
dup = git__calloc(1, sizeof(git_config_entry));
dup->name = git__strdup(head->entry->name);
GITERR_CHECK_ALLOC(dup->name);
if (head->entry->value) {
dup->value = git__strdup(head->entry->value);
GITERR_CHECK_ALLOC(dup->value);
}
dup->level = head->entry->level;
dup->include_depth = head->entry->include_depth;
if ((error = git_config_entries_append(result, dup)) < 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;
git_strmap_foreach_value(entries->map, list, config_entry_list_free(list));
git_strmap_free(entries->map);
list = entries->list;
while (list != NULL) {
next = list->next;
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)
{
git_strmap_iter pos;
config_entry_list *existing, *var;
int error = 0;
var = git__calloc(1, sizeof(config_entry_list));
GITERR_CHECK_ALLOC(var);
var->entry = entry;
pos = git_strmap_lookup_index(entries->map, entry->name);
if (!git_strmap_valid_index(entries->map, pos)) {
/*
* We only ever inspect `last` from the first config
* entry in a multivar. In case where this new entry is
* the first one in the entry map, it will also be the
* last one at the time of adding it, which is
* why we set `last` here to itself. Otherwise we
* do not have to set `last` and leave it set to
* `NULL`.
*/
var->last = var;
git_strmap_insert(entries->map, entry->name, var, &error);
if (error > 0)
error = 0;
} else {
existing = git_strmap_value_at(entries->map, pos);
config_entry_list_append(&existing, var);
}
var = git__calloc(1, sizeof(config_entry_list));
GITERR_CHECK_ALLOC(var);
var->entry = entry;
config_entry_list_append(&entries->list, var);
return error;
}
int config_entry_get(config_entry_list **out, git_config_entries *entries, const char *key)
{
khiter_t pos;
pos = git_strmap_lookup_index(entries->map, key);
/* no error message; the config system will write one */
if (!git_strmap_valid_index(entries->map, pos))
return GIT_ENOTFOUND;
*out = git_strmap_value_at(entries->map, pos);
return 0;
}
int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key)
{
config_entry_list *entry;
int error;
if ((error = config_entry_get(&entry, entries, key)) < 0)
return error;
*out = entry->last->entry;
return 0;
}
int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key)
{
config_entry_list *entry;
int error;
if ((error = config_entry_get(&entry, entries, key)) < 0)
return error;
if (entry->next != NULL) {
giterr_set(GITERR_CONFIG, "entry is not unique due to being a multivar");
return -1;
}
if (entry->entry->include_depth) {
giterr_set(GITERR_CONFIG, "entry is not unique due to being included");
return -1;
}
*out = entry->entry;
return 0;
}
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);
}
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));
GITERR_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);
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);
......@@ -5,9 +5,8 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "config_file.h"
#include "config.h"
#include "filebuf.h"
#include "sysdir.h"
#include "buffer.h"
......@@ -18,36 +17,20 @@
#include "strmap.h"
#include "array.h"
#include "config_parse.h"
#include "config_entries.h"
#include <ctype.h>
#include <sys/types.h>
#include <regex.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_file_iter {
git_config_iterator parent;
config_entry_list *head;
} git_config_file_iter;
/* Max depth for [include] directives */
#define MAX_INCLUDE_DEPTH 10
typedef struct {
git_atomic refcount;
git_strmap *map;
config_entry_list *list;
} diskfile_entries;
typedef struct {
git_config_backend parent;
/* mutex to coordinate accessing the values */
git_mutex values_mutex;
diskfile_entries *entries;
git_config_entries *entries;
const git_repository *repo;
git_config_level_t level;
} diskfile_header;
......@@ -73,16 +56,15 @@ typedef struct {
typedef struct {
const git_repository *repo;
const char *file_path;
diskfile_entries *entries;
git_config_entries *entries;
git_config_level_t level;
unsigned int depth;
} diskfile_parse_state;
static int config_read(diskfile_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
static int config_read(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
static int config_snapshot(git_config_backend **out, git_config_backend *in);
static int config_error_readonly(void)
......@@ -91,126 +73,14 @@ static int config_error_readonly(void)
return -1;
}
static void config_entry_list_free(config_entry_list *list)
{
config_entry_list *next;
while (list != NULL) {
next = list->next;
git__free((char*) list->entry->name);
git__free((char *) list->entry->value);
git__free(list->entry);
git__free(list);
list = next;
};
}
int git_config_file_normalize_section(char *start, char *end)
{
char *scan;
if (start == end)
return GIT_EINVALIDSPEC;
/* Validate and downcase range */
for (scan = start; *scan; ++scan) {
if (end && scan >= end)
break;
if (isalnum(*scan))
*scan = (char)git__tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
}
if (scan == start)
return GIT_EINVALIDSPEC;
return 0;
}
static void config_entry_list_append(config_entry_list **list, config_entry_list *entry)
{
if (*list)
(*list)->last->next = entry;
else
*list = entry;
(*list)->last = entry;
}
/* Add or append the new config option */
static int diskfile_entries_append(diskfile_entries *entries, git_config_entry *entry)
{
git_strmap_iter pos;
config_entry_list *existing, *var;
int error = 0;
var = git__calloc(1, sizeof(config_entry_list));
GITERR_CHECK_ALLOC(var);
var->entry = entry;
pos = git_strmap_lookup_index(entries->map, entry->name);
if (!git_strmap_valid_index(entries->map, pos)) {
/*
* We only ever inspect `last` from the first config
* entry in a multivar. In case where this new entry is
* the first one in the entry map, it will also be the
* last one at the time of adding it, which is
* why we set `last` here to itself. Otherwise we
* do not have to set `last` and leave it set to
* `NULL`.
*/
var->last = var;
git_strmap_insert(entries->map, entry->name, var, &error);
if (error > 0)
error = 0;
} else {
existing = git_strmap_value_at(entries->map, pos);
config_entry_list_append(&existing, var);
}
var = git__calloc(1, sizeof(config_entry_list));
GITERR_CHECK_ALLOC(var);
var->entry = entry;
config_entry_list_append(&entries->list, var);
return error;
}
static void diskfile_entries_free(diskfile_entries *entries)
{
config_entry_list *list = NULL, *next;
if (!entries)
return;
if (git_atomic_dec(&entries->refcount) != 0)
return;
git_strmap_foreach_value(entries->map, list, config_entry_list_free(list));
git_strmap_free(entries->map);
list = entries->list;
while (list != NULL) {
next = list->next;
git__free(list);
list = next;
}
git__free(entries);
}
/**
* Take the current values map from the backend and increase its
* refcount. This is its own function to make sure we use the mutex to
* avoid the map pointer from changing under us.
*/
static diskfile_entries *diskfile_entries_take(diskfile_header *h)
static git_config_entries *diskfile_entries_take(diskfile_header *h)
{
diskfile_entries *entries;
git_config_entries *entries;
if (git_mutex_lock(&h->values_mutex) < 0) {
giterr_set(GITERR_OS, "failed to lock config backend");
......@@ -218,31 +88,13 @@ static diskfile_entries *diskfile_entries_take(diskfile_header *h)
}
entries = h->entries;
git_atomic_inc(&entries->refcount);
git_config_entries_incref(entries);
git_mutex_unlock(&h->values_mutex);
return entries;
}
static int diskfile_entries_alloc(diskfile_entries **out)
{
diskfile_entries *entries;
int error;
entries = git__calloc(1, sizeof(diskfile_entries));
GITERR_CHECK_ALLOC(entries);
git_atomic_set(&entries->refcount, 1);
if ((error = git_strmap_alloc(&entries->map)) < 0)
git__free(entries);
else
*out = entries;
return error;
}
static void config_file_clear(struct config_file *file)
{
struct config_file *include;
......@@ -267,14 +119,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level, const
b->header.level = level;
b->header.repo = repo;
if ((res = diskfile_entries_alloc(&b->header.entries)) < 0)
if ((res = git_config_entries_new(&b->header.entries)) < 0)
return res;
if (!git_path_exists(b->file.path))
return 0;
if (res < 0 || (res = config_read(b->header.entries, repo, &b->file, level, 0)) < 0) {
diskfile_entries_free(b->header.entries);
git_config_entries_free(b->header.entries);
b->header.entries = NULL;
}
......@@ -316,7 +168,7 @@ out:
static int config_refresh(git_config_backend *cfg)
{
diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *entries = NULL, *tmp;
git_config_entries *entries = NULL, *tmp;
git_config_file *include;
int error, modified;
uint32_t i;
......@@ -331,7 +183,7 @@ static int config_refresh(git_config_backend *cfg)
if (!modified)
return 0;
if ((error = diskfile_entries_alloc(&entries)) < 0)
if ((error = git_config_entries_new(&entries)) < 0)
goto out;
/* Reparse the current configuration */
......@@ -355,7 +207,7 @@ static int config_refresh(git_config_backend *cfg)
git_mutex_unlock(&b->header.values_mutex);
out:
diskfile_entries_free(entries);
git_config_entries_free(entries);
return (error == GIT_ENOTFOUND) ? 0 : error;
}
......@@ -368,133 +220,80 @@ static void backend_free(git_config_backend *_backend)
return;
config_file_clear(&backend->file);
diskfile_entries_free(backend->header.entries);
git_config_entries_free(backend->header.entries);
git_mutex_free(&backend->header.values_mutex);
git__free(backend);
}
static void config_iterator_free(
git_config_iterator* iter)
{
iter->backend->free(iter->backend);
git__free(iter);
}
static int config_iterator_next(
git_config_entry **entry,
git_config_iterator *iter)
{
git_config_file_iter *it = (git_config_file_iter *) iter;
if (!it->head)
return GIT_ITEROVER;
*entry = it->head->entry;
it->head = it->head->next;
return 0;
}
static int config_iterator_new(
git_config_iterator **iter,
struct git_config_backend* backend)
{
diskfile_header *h;
git_config_file_iter *it;
git_config_backend *snapshot;
diskfile_header *bh = (diskfile_header *) backend;
git_config_entries *entries;
int error;
if ((error = config_snapshot(&snapshot, backend)) < 0)
return error;
if ((error = snapshot->open(snapshot, bh->level, bh->repo)) < 0)
if ((error = git_config_entries_dup(&entries, bh->entries)) < 0)
return error;
it = git__calloc(1, sizeof(git_config_file_iter));
GITERR_CHECK_ALLOC(it);
h = (diskfile_header *)snapshot;
it->parent.backend = snapshot;
it->head = h->entries->list;
it->parent.next = config_iterator_next;
it->parent.free = config_iterator_free;
*iter = (git_config_iterator *) it;
if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
goto out;
return 0;
out:
/* Let iterator delete duplicated entries when it's done */
git_config_entries_free(entries);
return error;
}
static int config_set(git_config_backend *cfg, const char *name, const char *value)
{
diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *entries;
git_strmap *entry_map;
git_config_entries *entries;
git_config_entry *existing;
char *key, *esc_value = NULL;
khiter_t pos;
int rval, ret;
int error;
if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval;
if ((error = git_config__normalize_name(name, &key)) < 0)
return error;
if ((entries = diskfile_entries_take(&b->header)) == NULL)
return -1;
entry_map = entries->map;
/*
* Try to find it in the existing values and update it if it
* only has one value.
*/
pos = git_strmap_lookup_index(entry_map, key);
if (git_strmap_valid_index(entry_map, pos)) {
config_entry_list *existing = git_strmap_value_at(entry_map, pos);
if (existing->next != NULL) {
giterr_set(GITERR_CONFIG, "multivar incompatible with simple set");
ret = -1;
goto out;
}
if (existing->entry->include_depth) {
giterr_set(GITERR_CONFIG, "modifying included variable is not supported");
ret = -1;
/* Check whether we'd be modifying an included or multivar key */
if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
}
error = 0;
} else if ((!existing->value && !value) ||
(existing->value && value && !strcmp(existing->value, value))) {
/* don't update if old and new values already match */
if ((!existing->entry->value && !value) ||
(existing->entry->value && value &&
!strcmp(existing->entry->value, value))) {
ret = 0;
goto out;
}
error = 0;
goto out;
}
/* No early returns due to sanity checks, let's write it out and refresh */
if (value) {
esc_value = escape_value(value);
GITERR_CHECK_ALLOC(esc_value);
}
if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
if ((error = config_write(b, name, key, NULL, esc_value)) < 0)
goto out;
ret = config_refresh(cfg);
error = config_refresh(cfg);
out:
diskfile_entries_free(entries);
git_config_entries_free(entries);
git__free(esc_value);
git__free(key);
return ret;
return error;
}
/* release the map containing the entry as an equivalent to freeing it */
static void free_diskfile_entry(git_config_entry *entry)
{
diskfile_entries *map = (diskfile_entries *) entry->payload;
diskfile_entries_free(map);
git_config_entries *entries = (git_config_entries *) entry->payload;
git_config_entries_free(entries);
}
/*
......@@ -503,10 +302,8 @@ static void free_diskfile_entry(git_config_entry *entry)
static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
{
diskfile_header *h = (diskfile_header *)cfg;
diskfile_entries *entries;
git_strmap *entry_map;
khiter_t pos;
config_entry_list *var;
git_config_entries *entries = NULL;
git_config_entry *entry;
int error = 0;
if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
......@@ -514,22 +311,17 @@ static int config_get(git_config_backend *cfg, const char *key, git_config_entry
if ((entries = diskfile_entries_take(h)) == NULL)
return -1;
entry_map = entries->map;
pos = git_strmap_lookup_index(entry_map, key);
/* no error message; the config system will write one */
if (!git_strmap_valid_index(entry_map, pos)) {
diskfile_entries_free(entries);
return GIT_ENOTFOUND;
if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
git_config_entries_free(entries);
return error;
}
var = git_strmap_value_at(entry_map, pos);
*out = var->last->entry;
(*out)->free = free_diskfile_entry;
(*out)->payload = entries;
entry->free = free_diskfile_entry;
entry->payload = entries;
*out = entry;
return error;
return 0;
}
static int config_set_multivar(
......@@ -567,79 +359,61 @@ out:
static int config_delete(git_config_backend *cfg, const char *name)
{
config_entry_list *var;
diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *map;
git_strmap *entry_map;
char *key;
int result;
khiter_t pos;
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
if ((map = diskfile_entries_take(&b->header)) == NULL)
return -1;
entry_map = b->header.entries->map;
pos = git_strmap_lookup_index(entry_map, key);
git__free(key);
git_config_entries *entries = NULL;
git_config_entry *entry;
char *key = NULL;
int error;
if (!git_strmap_valid_index(entry_map, pos)) {
diskfile_entries_free(map);
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
}
if ((error = git_config__normalize_name(name, &key)) < 0)
goto out;
var = git_strmap_value_at(entry_map, pos);
diskfile_entries_free(map);
if ((entries = diskfile_entries_take(&b->header)) == NULL)
goto out;
if (var->entry->include_depth) {
giterr_set(GITERR_CONFIG, "cannot delete included variable");
return -1;
/* Check whether we'd be modifying an included or multivar key */
if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
if (error == GIT_ENOTFOUND)
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
goto out;
}
if (var->next != NULL) {
giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
return -1;
}
if ((error = config_write(b, name, entry->name, NULL, NULL)) < 0)
goto out;
if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
return result;
if ((error = config_refresh(cfg)) < 0)
goto out;
return config_refresh(cfg);
out:
git_config_entries_free(entries);
git__free(key);
return error;
}
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
{
diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *map;
git_strmap *entry_map;
char *key;
regex_t preg;
git_config_entries *entries = NULL;
git_config_entry *entry = NULL;
regex_t preg = { 0 };
char *key = NULL;
int result;
khiter_t pos;
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
if ((map = diskfile_entries_take(&b->header)) == NULL)
return -1;
entry_map = b->header.entries->map;
pos = git_strmap_lookup_index(entry_map, key);
goto out;
if (!git_strmap_valid_index(entry_map, pos)) {
diskfile_entries_free(map);
git__free(key);
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
if ((entries = diskfile_entries_take(&b->header)) == NULL) {
result = -1;
goto out;
}
diskfile_entries_free(map);
if ((result = git_config_entries_get(&entry, entries, key)) < 0) {
if (result == GIT_ENOTFOUND)
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
goto out;
}
result = p_regcomp(&preg, regexp, REG_EXTENDED);
if (result != 0) {
if ((result = p_regcomp(&preg, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&preg, result);
result = -1;
goto out;
......@@ -648,21 +422,16 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
if ((result = config_write(b, name, key, &preg, NULL)) < 0)
goto out;
result = config_refresh(cfg);
if ((result = config_refresh(cfg)) < 0)
goto out;
out:
git_config_entries_free(entries);
git__free(key);
regfree(&preg);
return result;
}
static int config_snapshot(git_config_backend **out, git_config_backend *in)
{
diskfile_backend *b = (diskfile_backend *) in;
return git_config_file__snapshot(out, b);
}
static int config_lock(git_config_backend *_cfg)
{
diskfile_backend *cfg = (diskfile_backend *) _cfg;
......@@ -699,7 +468,7 @@ static int config_unlock(git_config_backend *_cfg, int success)
return error;
}
int git_config_file__ondisk(git_config_backend **out, const char *path)
int git_config_backend_from_file(git_config_backend **out, const char *path)
{
diskfile_backend *backend;
......@@ -789,7 +558,7 @@ static void backend_readonly_free(git_config_backend *_backend)
if (backend == NULL)
return;
diskfile_entries_free(backend->header.entries);
git_config_entries_free(backend->header.entries);
git_mutex_free(&backend->header.values_mutex);
git__free(backend);
}
......@@ -799,7 +568,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
diskfile_backend *src = b->snapshot_from;
diskfile_header *src_header = &src->header;
diskfile_entries *entries;
git_config_entries *entries;
int error;
if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
......@@ -816,7 +585,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
return 0;
}
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
static int config_snapshot(git_config_backend **out, git_config_backend *in)
{
diskfile_readonly_backend *backend;
......@@ -826,7 +595,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
git_mutex_init(&backend->header.values_mutex);
backend->snapshot_from = in;
backend->snapshot_from = (diskfile_backend *) in;
backend->header.parent.readonly = 1;
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
......@@ -1062,7 +831,7 @@ static int read_on_variable(
entry->level = parse_data->level;
entry->include_depth = parse_data->depth;
if ((result = diskfile_entries_append(parse_data->entries, entry)) < 0)
if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
return result;
result = 0;
......@@ -1079,7 +848,7 @@ static int read_on_variable(
}
static int config_read(
diskfile_entries *entries,
git_config_entries *entries,
const git_repository *repo,
git_config_file *file,
git_config_level_t level,
......
/*
* 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.h"
#include "config_backend.h"
#include "config_parse.h"
#include "config_entries.h"
typedef struct {
git_config_backend parent;
git_config_entries *entries;
git_buf cfg;
} config_memory_backend;
typedef struct {
git_config_entries *entries;
git_config_level_t level;
} config_memory_parse_data;
static int config_error_readonly(void)
{
giterr_set(GITERR_CONFIG, "this backend is read-only");
return -1;
}
static int read_variable_cb(
git_config_parser *reader,
const char *current_section,
const char *var_name,
const char *var_value,
const char *line,
size_t line_len,
void *payload)
{
config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
git_buf buf = GIT_BUF_INIT;
git_config_entry *entry;
const char *c;
int result;
GIT_UNUSED(reader);
GIT_UNUSED(line);
GIT_UNUSED(line_len);
if (current_section) {
/* TODO: Once warnings land, we should likely warn
* here. Git appears to warn in most cases if it sees
* un-namespaced config options.
*/
git_buf_puts(&buf, current_section);
git_buf_putc(&buf, '.');
}
for (c = var_name; *c; c++)
git_buf_putc(&buf, git__tolower(*c));
if (git_buf_oom(&buf))
return -1;
entry = git__calloc(1, sizeof(git_config_entry));
GITERR_CHECK_ALLOC(entry);
entry->name = git_buf_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)
return result;
return result;
}
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;
config_memory_parse_data parse_data;
git_config_parser reader;
GIT_UNUSED(repo);
if (memory_backend->cfg.size == 0)
return 0;
git_parse_ctx_init(&reader.ctx, memory_backend->cfg.ptr, memory_backend->cfg.size);
reader.file = NULL;
parse_data.entries = memory_backend->entries;
parse_data.level = level;
return git_config_parse(&reader, NULL, read_variable_cb, NULL, NULL, &parse_data);
}
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);
}
static int config_memory_iterator(
git_config_iterator **iter,
git_config_backend *backend)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
git_config_entries *entries;
int error;
if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0)
goto out;
if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
goto out;
out:
/* Let iterator delete duplicated entries when it's done */
git_config_entries_free(entries);
return error;
}
static int config_memory_set(git_config_backend *backend, const char *name, const char *value)
{
GIT_UNUSED(backend);
GIT_UNUSED(name);
GIT_UNUSED(value);
return config_error_readonly();
}
static int config_memory_set_multivar(
git_config_backend *backend, const char *name, const char *regexp, const char *value)
{
GIT_UNUSED(backend);
GIT_UNUSED(name);
GIT_UNUSED(regexp);
GIT_UNUSED(value);
return config_error_readonly();
}
static int config_memory_delete(git_config_backend *backend, const char *name)
{
GIT_UNUSED(backend);
GIT_UNUSED(name);
return config_error_readonly();
}
static int config_memory_delete_multivar(git_config_backend *backend, const char *name, const char *regexp)
{
GIT_UNUSED(backend);
GIT_UNUSED(name);
GIT_UNUSED(regexp);
return config_error_readonly();
}
static int config_memory_lock(git_config_backend *backend)
{
GIT_UNUSED(backend);
return config_error_readonly();
}
static int config_memory_unlock(git_config_backend *backend, int success)
{
GIT_UNUSED(backend);
GIT_UNUSED(success);
return config_error_readonly();
}
static int config_memory_snapshot(git_config_backend **out, git_config_backend *backend)
{
GIT_UNUSED(out);
GIT_UNUSED(backend);
giterr_set(GITERR_CONFIG, "this backend does not support snapshots");
return -1;
}
static void config_memory_free(git_config_backend *_backend)
{
config_memory_backend *backend = (config_memory_backend *)_backend;
if (backend == NULL)
return;
git_config_entries_free(backend->entries);
git_buf_dispose(&backend->cfg);
git__free(backend);
}
int git_config_backend_from_string(git_config_backend **out, const char *cfg)
{
config_memory_backend *backend;
backend = git__calloc(1, sizeof(config_memory_backend));
GITERR_CHECK_ALLOC(backend);
if (git_config_entries_new(&backend->entries) < 0) {
git__free(backend);
return -1;
}
if (git_buf_sets(&backend->cfg, cfg) < 0) {
git_config_entries_free(backend->entries);
git__free(backend);
return -1;
}
backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
backend->parent.readonly = 1;
backend->parent.open = config_memory_open;
backend->parent.get = config_memory_get;
backend->parent.set = config_memory_set;
backend->parent.set_multivar = config_memory_set_multivar;
backend->parent.del = config_memory_delete;
backend->parent.del_multivar = config_memory_delete_multivar;
backend->parent.iterator = config_memory_iterator;
backend->parent.lock = config_memory_lock;
backend->parent.unlock = config_memory_unlock;
backend->parent.snapshot = config_memory_snapshot;
backend->parent.free = config_memory_free;
*out = (git_config_backend *)backend;
return 0;
}
......@@ -11,10 +11,14 @@
#include <ctype.h>
const char *git_config_escapes = "ntb\"\\";
const char *git_config_escaped = "\n\t\b\"\\";
static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
{
const char *file = reader->file ? reader->file->path : "in-memory";
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
error_str, reader->file->path, reader->ctx.line_num, col);
error_str, file, reader->ctx.line_num, col);
}
......
......@@ -12,8 +12,8 @@
#include "oid.h"
#include "parse.h"
static const char *git_config_escapes = "ntb\"\\";
static const char *git_config_escaped = "\n\t\b\"\\";
extern const char *git_config_escapes;
extern const char *git_config_escaped;
typedef struct config_file {
git_oid checksum;
......
......@@ -15,7 +15,7 @@
#include "buf_text.h"
#include "vector.h"
#include "posix.h"
#include "config_file.h"
#include "config_backend.h"
#include "config.h"
#include "repository.h"
#include "tree.h"
......@@ -199,13 +199,15 @@ out:
*/
static void free_submodule_names(git_strmap *names)
{
git_buf *name;
const char *key;
char *value;
if (names == NULL)
return;
git_strmap_foreach_value(names, name, {
git__free(name);
git_strmap_foreach(names, key, value, {
git__free((char *) key);
git__free(value);
});
git_strmap_free(names);
......@@ -257,7 +259,7 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf
if (!isvalid)
continue;
git_strmap_insert(names, entry->value, git_buf_detach(&buf), &rval);
git_strmap_insert(names, git__strdup(entry->value), git_buf_detach(&buf), &rval);
if (rval < 0) {
giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
error = -1;
......@@ -333,9 +335,9 @@ int git_submodule_lookup(
mods = open_gitmodules(repo, GITMODULES_EXISTING);
if (mods)
error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
error = git_config_backend_foreach_match(mods, pattern, find_by_path, &data);
git_config_file_free(mods);
git_config_backend_free(mods);
if (error < 0) {
git_submodule_free(sm);
......@@ -792,11 +794,11 @@ int git_submodule_add_setup(
}
if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
(error = git_config_file_set_string(mods, name.ptr, path)) < 0)
(error = git_config_backend_set_string(mods, name.ptr, path)) < 0)
goto cleanup;
if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
(error = git_config_file_set_string(mods, name.ptr, url)) < 0)
(error = git_config_backend_set_string(mods, name.ptr, url)) < 0)
goto cleanup;
git_buf_clear(&name);
......@@ -834,7 +836,7 @@ cleanup:
if (out != NULL)
*out = sm;
git_config_file_free(mods);
git_config_backend_free(mods);
git_repository_free(subrepo);
git_buf_dispose(&real_url);
git_buf_dispose(&name);
......@@ -1033,14 +1035,14 @@ static int write_var(git_repository *repo, const char *name, const char *var, co
goto cleanup;
if (val)
error = git_config_file_set_string(mods, key.ptr, val);
error = git_config_backend_set_string(mods, key.ptr, val);
else
error = git_config_file_delete(mods, key.ptr);
error = git_config_backend_delete(mods, key.ptr);
git_buf_dispose(&key);
cleanup:
git_config_file_free(mods);
git_config_backend_free(mods);
return error;
}
......@@ -2070,12 +2072,12 @@ static git_config_backend *open_gitmodules(
return NULL;
if (okay_to_create || git_path_isfile(path.ptr)) {
/* git_config_file__ondisk should only fail if OOM */
if (git_config_file__ondisk(&mods, path.ptr) < 0)
/* git_config_backend_from_file should only fail if OOM */
if (git_config_backend_from_file(&mods, path.ptr) < 0)
mods = NULL;
/* open should only fail here if the file is malformed */
else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
git_config_file_free(mods);
else if (git_config_backend_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
git_config_backend_free(mods);
mods = NULL;
}
}
......
#include "clar_libgit2.h"
#include "config_backend.h"
static git_config_backend *backend;
void test_config_memory__initialize(void)
{
backend = NULL;
}
void test_config_memory__cleanup(void)
{
git_config_backend_free(backend);
}
static void assert_config_contains(git_config_backend *backend,
const char *name, const char *value)
{
git_config_entry *entry;
cl_git_pass(git_config_backend_get_string(&entry, backend, name));
cl_assert_equal_s(entry->value, value);
}
struct expected_entry {
const char *name;
const char *value;
int seen;
};
static int contains_all_cb(const git_config_entry *entry, void *payload)
{
struct expected_entry *entries = (struct expected_entry *) payload;
int i;
for (i = 0; entries[i].name; i++) {
if (strcmp(entries[i].name, entry->name) ||
strcmp(entries[i].value , entry->value))
continue;
if (entries[i].seen)
cl_fail("Entry seen more than once");
entries[i].seen = 1;
return 0;
}
cl_fail("Unexpected entry");
return -1;
}
static void assert_config_contains_all(git_config_backend *backend,
struct expected_entry *entries)
{
int i;
cl_git_pass(git_config_backend_foreach(backend, contains_all_cb, entries));
for (i = 0; entries[i].name; i++)
cl_assert(entries[i].seen);
}
static void setup_backend(const char *cfg)
{
cl_git_pass(git_config_backend_from_string(&backend, cfg));
cl_git_pass(git_config_backend_open(backend, 0, NULL));
}
void test_config_memory__write_operations_fail(void)
{
setup_backend("");
cl_git_fail(git_config_backend_set_string(backend, "general.foo", "var"));
cl_git_fail(git_config_backend_delete(backend, "general.foo"));
cl_git_fail(git_config_backend_lock(backend));
cl_git_fail(git_config_backend_unlock(backend, 0));
}
void test_config_memory__simple(void)
{
setup_backend(
"[general]\n"
"foo=bar\n");
assert_config_contains(backend, "general.foo", "bar");
}
void test_config_memory__malformed_fails_to_open(void)
{
cl_git_pass(git_config_backend_from_string(&backend,
"[general\n"
"foo=bar\n"));
cl_git_fail(git_config_backend_open(backend, 0, NULL));
}
void test_config_memory__multiple_vars(void)
{
setup_backend(
"[general]\n"
"foo=bar\n"
"key=value\n");
assert_config_contains(backend, "general.foo", "bar");
assert_config_contains(backend, "general.key", "value");
}
void test_config_memory__multiple_sections(void)
{
setup_backend(
"[general]\n"
"foo=bar\n"
"\n"
"[other]\n"
"key=value\n");
assert_config_contains(backend, "general.foo", "bar");
assert_config_contains(backend, "other.key", "value");
}
void test_config_memory__multivar_gets_correct_string(void)
{
setup_backend(
"[general]\n"
"foo=bar1\n"
"foo=bar2\n");
assert_config_contains(backend, "general.foo", "bar2");
}
void test_config_memory__foreach_sees_multivar(void)
{
struct expected_entry entries[] = {
{ "general.foo", "bar1", 0 },
{ "general.foo", "bar2", 0 },
{ NULL, NULL, 0 },
};
setup_backend(
"[general]\n"
"foo=bar1\n"
"foo=bar2\n");
assert_config_contains_all(backend, entries);
}
#include "clar_libgit2.h"
#include "config_file.h"
#include "config_backend.h"
#include "config.h"
#include "path.h"
......@@ -20,7 +20,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
{
git_config_backend *backend;
cl_git_pass(git_config_file__ondisk(&backend, "global"));
cl_git_pass(git_config_backend_from_file(&backend, "global"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
......@@ -32,11 +32,11 @@ void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
{
git_config_backend *backend;
cl_git_pass(git_config_file__ondisk(&backend, "global"));
cl_git_pass(git_config_backend_from_file(&backend, "global"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "local"));
cl_git_pass(git_config_backend_from_file(&backend, "local"));
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
......@@ -50,11 +50,11 @@ void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
{
git_config_backend *backend;
cl_git_pass(git_config_file__ondisk(&backend, "local"));
cl_git_pass(git_config_backend_from_file(&backend, "local"));
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "global"));
cl_git_pass(git_config_backend_from_file(&backend, "global"));
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
......
......@@ -2,7 +2,6 @@
#include "buffer.h"
#include "fileops.h"
#include "git2/sys/config.h"
#include "config_file.h"
#include "config.h"
void test_config_write__initialize(void)
......
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