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 @@ ...@@ -12,7 +12,7 @@
#include "git2/sys/config.h" #include "git2/sys/config.h"
#include "vector.h" #include "vector.h"
#include "buf_text.h" #include "buf_text.h"
#include "config_file.h" #include "config_backend.h"
#include "transaction.h" #include "transaction.h"
#if GIT_WIN32 #if GIT_WIN32
# include <windows.h> # include <windows.h>
...@@ -31,30 +31,30 @@ void git_config_entry_free(git_config_entry *entry) ...@@ -31,30 +31,30 @@ void git_config_entry_free(git_config_entry *entry)
typedef struct { typedef struct {
git_refcount rc; git_refcount rc;
git_config_backend *file; git_config_backend *backend;
git_config_level_t level; 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; backend = internal->backend;
file->free(file); backend->free(backend);
git__free(internal); git__free(internal);
} }
static void config_free(git_config *cfg) static void config_free(git_config *cfg)
{ {
size_t i; size_t i;
file_internal *internal; backend_internal *internal;
for (i = 0; i < cfg->files.length; ++i) { for (i = 0; i < cfg->backends.length; ++i) {
internal = git_vector_get(&cfg->files, i); internal = git_vector_get(&cfg->backends, i);
GIT_REFCOUNT_DEC(internal, file_internal_free); GIT_REFCOUNT_DEC(internal, backend_internal_free);
} }
git_vector_free(&cfg->files); git_vector_free(&cfg->backends);
git__memzero(cfg, sizeof(*cfg)); git__memzero(cfg, sizeof(*cfg));
git__free(cfg); git__free(cfg);
...@@ -70,8 +70,8 @@ void git_config_free(git_config *cfg) ...@@ -70,8 +70,8 @@ void git_config_free(git_config *cfg)
static int config_backend_cmp(const void *a, const void *b) static int config_backend_cmp(const void *a, const void *b)
{ {
const file_internal *bk_a = (const file_internal *)(a); const backend_internal *bk_a = (const backend_internal *)(a);
const file_internal *bk_b = (const file_internal *)(b); const backend_internal *bk_b = (const backend_internal *)(b);
return bk_b->level - bk_a->level; return bk_b->level - bk_a->level;
} }
...@@ -85,7 +85,7 @@ int git_config_new(git_config **out) ...@@ -85,7 +85,7 @@ int git_config_new(git_config **out)
memset(cfg, 0x0, sizeof(git_config)); 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); git__free(cfg);
return -1; return -1;
} }
...@@ -114,7 +114,7 @@ int git_config_add_file_ondisk( ...@@ -114,7 +114,7 @@ int git_config_add_file_ondisk(
return -1; return -1;
} }
if (git_config_file__ondisk(&file, path) < 0) if (git_config_backend_from_file(&file, path) < 0)
return -1; return -1;
if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) { 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) ...@@ -151,7 +151,7 @@ int git_config_snapshot(git_config **out, git_config *in)
{ {
int error = 0; int error = 0;
size_t i; size_t i;
file_internal *internal; backend_internal *internal;
git_config *config; git_config *config;
*out = NULL; *out = NULL;
...@@ -159,10 +159,10 @@ int git_config_snapshot(git_config **out, git_config *in) ...@@ -159,10 +159,10 @@ int git_config_snapshot(git_config **out, git_config *in)
if (git_config_new(&config) < 0) if (git_config_new(&config) < 0)
return -1; return -1;
git_vector_foreach(&in->files, i, internal) { git_vector_foreach(&in->backends, i, internal) {
git_config_backend *b; git_config_backend *b;
if ((error = internal->file->snapshot(&b, internal->file)) < 0) if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
break; break;
if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) { 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) ...@@ -179,24 +179,24 @@ int git_config_snapshot(git_config **out, git_config *in)
return error; return error;
} }
static int find_internal_file_by_level( static int find_backend_by_level(
file_internal **internal_out, backend_internal **out,
const git_config *cfg, const git_config *cfg,
git_config_level_t level) git_config_level_t level)
{ {
int pos = -1; int pos = -1;
file_internal *internal; backend_internal *internal;
size_t i; size_t i;
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
* which has the highest level. As config files are stored in a vector * which has the highest level. As config backends are stored in a vector
* sorted by decreasing order of level, getting the file at position 0 * sorted by decreasing order of level, getting the backend at position 0
* will do the job. * will do the job.
*/ */
if (level == GIT_CONFIG_HIGHEST_LEVEL) { if (level == GIT_CONFIG_HIGHEST_LEVEL) {
pos = 0; pos = 0;
} else { } else {
git_vector_foreach(&cfg->files, i, internal) { git_vector_foreach(&cfg->backends, i, internal) {
if (internal->level == level) if (internal->level == level)
pos = (int)i; pos = (int)i;
} }
...@@ -204,34 +204,34 @@ static int find_internal_file_by_level( ...@@ -204,34 +204,34 @@ static int find_internal_file_by_level(
if (pos == -1) { if (pos == -1) {
giterr_set(GITERR_CONFIG, 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; return GIT_ENOTFOUND;
} }
*internal_out = git_vector_get(&cfg->files, pos); *out = git_vector_get(&cfg->backends, pos);
return 0; return 0;
} }
static int duplicate_level(void **old_raw, void *new_raw) 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); 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; return GIT_EEXISTS;
} }
static void try_remove_existing_file_internal( static void try_remove_existing_backend(
git_config *cfg, git_config *cfg,
git_config_level_t level) git_config_level_t level)
{ {
int pos = -1; int pos = -1;
file_internal *internal; backend_internal *internal;
size_t i; size_t i;
git_vector_foreach(&cfg->files, i, internal) { git_vector_foreach(&cfg->backends, i, internal) {
if (internal->level == level) if (internal->level == level)
pos = (int)i; pos = (int)i;
} }
...@@ -239,32 +239,32 @@ static void try_remove_existing_file_internal( ...@@ -239,32 +239,32 @@ static void try_remove_existing_file_internal(
if (pos == -1) if (pos == -1)
return; 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; return;
GIT_REFCOUNT_DEC(internal, file_internal_free); GIT_REFCOUNT_DEC(internal, backend_internal_free);
} }
static int git_config__add_internal( static int git_config__add_internal(
git_config *cfg, git_config *cfg,
file_internal *internal, backend_internal *internal,
git_config_level_t level, git_config_level_t level,
int force) int force)
{ {
int result; int result;
/* delete existing config file for level if it exists */ /* delete existing config backend for level if it exists */
if (force) 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) internal, &duplicate_level)) < 0)
return result; return result;
git_vector_sort(&cfg->files); git_vector_sort(&cfg->backends);
internal->file->cfg = cfg; internal->backend->cfg = cfg;
GIT_REFCOUNT_INC(internal); GIT_REFCOUNT_INC(internal);
...@@ -285,10 +285,10 @@ int git_config_open_level( ...@@ -285,10 +285,10 @@ int git_config_open_level(
git_config_level_t level) git_config_level_t level)
{ {
git_config *cfg; git_config *cfg;
file_internal *internal; backend_internal *internal;
int res; 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; return res;
if ((res = git_config_new(&cfg)) < 0) if ((res = git_config_new(&cfg)) < 0)
...@@ -306,27 +306,27 @@ int git_config_open_level( ...@@ -306,27 +306,27 @@ int git_config_open_level(
int git_config_add_backend( int git_config_add_backend(
git_config *cfg, git_config *cfg,
git_config_backend *file, git_config_backend *backend,
git_config_level_t level, git_config_level_t level,
const git_repository *repo, const git_repository *repo,
int force) int force)
{ {
file_internal *internal; backend_internal *internal;
int result; 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; return result;
internal = git__malloc(sizeof(file_internal)); internal = git__malloc(sizeof(backend_internal));
GITERR_CHECK_ALLOC(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; internal->level = level;
if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) { if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
...@@ -351,11 +351,11 @@ typedef struct { ...@@ -351,11 +351,11 @@ typedef struct {
static int find_next_backend(size_t *out, const git_config *cfg, size_t i) 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) { for (; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1); internal = git_vector_get(&cfg->backends, i - 1);
if (!internal || !internal->file) if (!internal || !internal->backend)
continue; continue;
*out = i; *out = i;
...@@ -368,7 +368,7 @@ static int find_next_backend(size_t *out, const git_config *cfg, size_t 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) static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
{ {
all_iter *iter = (all_iter *) _iter; all_iter *iter = (all_iter *) _iter;
file_internal *internal; backend_internal *internal;
git_config_backend *backend; git_config_backend *backend;
size_t i; size_t i;
int error = 0; int error = 0;
...@@ -385,8 +385,8 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter) ...@@ -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) if (find_next_backend(&i, iter->cfg, iter->i) < 0)
return GIT_ITEROVER; return GIT_ITEROVER;
internal = git_vector_get(&iter->cfg->files, i - 1); internal = git_vector_get(&iter->cfg->backends, i - 1);
backend = internal->file; backend = internal->backend;
iter->i = i - 1; iter->i = i - 1;
if (iter->current) if (iter->current)
...@@ -461,7 +461,7 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg) ...@@ -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.free = all_iter_free;
iter->parent.next = all_iter_next; iter->parent.next = all_iter_next;
iter->i = cfg->files.length; iter->i = cfg->backends.length;
iter->cfg = cfg; iter->cfg = cfg;
*out = (git_config_iterator *) iter; *out = (git_config_iterator *) iter;
...@@ -488,7 +488,7 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf ...@@ -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.next = all_iter_glob_next;
iter->parent.free = all_iter_glob_free; iter->parent.free = all_iter_glob_free;
iter->i = cfg->files.length; iter->i = cfg->backends.length;
iter->cfg = cfg; iter->cfg = cfg;
*out = (git_config_iterator *) iter; *out = (git_config_iterator *) iter;
...@@ -592,38 +592,38 @@ static int get_backend_for_use(git_config_backend **out, ...@@ -592,38 +592,38 @@ static int get_backend_for_use(git_config_backend **out,
git_config *cfg, const char *name, backend_use use) git_config *cfg, const char *name, backend_use use)
{ {
size_t i; size_t i;
file_internal *f; backend_internal *backend;
*out = NULL; *out = NULL;
if (git_vector_length(&cfg->files) == 0) { if (git_vector_length(&cfg->backends) == 0) {
giterr_set(GITERR_CONFIG, 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); uses[use], name);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
git_vector_foreach(&cfg->files, i, f) { git_vector_foreach(&cfg->backends, i, backend) {
if (!f->file->readonly) { if (!backend->backend->readonly) {
*out = f->file; *out = backend->backend;
return 0; return 0;
} }
} }
giterr_set(GITERR_CONFIG, 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); uses[use], name);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
int git_config_delete_entry(git_config *cfg, const char *name) 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 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) 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) ...@@ -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 git_config_set_string(git_config *cfg, const char *name, const char *value)
{ {
int error; int error;
git_config_backend *file; git_config_backend *backend;
if (!value) { if (!value) {
giterr_set(GITERR_CONFIG, "the value to set cannot be NULL"); giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
return -1; 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; return GIT_ENOTFOUND;
error = file->set(file, name, value); error = backend->set(backend, name, value);
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL) if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg)); git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
...@@ -722,7 +722,7 @@ static int get_entry( ...@@ -722,7 +722,7 @@ static int get_entry(
const char *key = name; const char *key = name;
char *normalized = NULL; char *normalized = NULL;
size_t i; size_t i;
file_internal *internal; backend_internal *internal;
*out = NULL; *out = NULL;
...@@ -733,11 +733,11 @@ static int get_entry( ...@@ -733,11 +733,11 @@ static int get_entry(
} }
res = GIT_ENOTFOUND; res = GIT_ENOTFOUND;
git_vector_foreach(&cfg->files, i, internal) { git_vector_foreach(&cfg->backends, i, internal) {
if (!internal || !internal->file) if (!internal || !internal->backend)
continue; continue;
res = internal->file->get(internal->file, key, out); res = internal->backend->get(internal->backend, key, out);
if (res != GIT_ENOTFOUND) if (res != GIT_ENOTFOUND)
break; break;
} }
...@@ -835,13 +835,13 @@ int git_config_get_bool(int *out, const git_config *cfg, const char *name) ...@@ -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) static int is_readonly(const git_config *cfg)
{ {
size_t i; size_t i;
file_internal *internal; backend_internal *internal;
git_vector_foreach(&cfg->files, i, internal) { git_vector_foreach(&cfg->backends, i, internal) {
if (!internal || !internal->file) if (!internal || !internal->backend)
continue; continue;
if (!internal->file->readonly) if (!internal->backend->readonly)
return 0; return 0;
} }
...@@ -1058,22 +1058,22 @@ on_error: ...@@ -1058,22 +1058,22 @@ on_error:
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) 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 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) 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 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) int git_config_next(git_config_entry **entry, git_config_iterator *iter)
...@@ -1179,17 +1179,17 @@ int git_config_open_default(git_config **out) ...@@ -1179,17 +1179,17 @@ int git_config_open_default(git_config **out)
int git_config_lock(git_transaction **out, git_config *cfg) int git_config_lock(git_transaction **out, git_config *cfg)
{ {
int error; int error;
git_config_backend *file; git_config_backend *backend;
file_internal *internal; backend_internal *internal;
internal = git_vector_get(&cfg->files, 0); internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->file) { if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
return -1; return -1;
} }
file = internal->file; backend = internal->backend;
if ((error = file->lock(file)) < 0) if ((error = backend->lock(backend)) < 0)
return error; return error;
return git_transaction_config_new(out, cfg); return git_transaction_config_new(out, cfg);
...@@ -1197,18 +1197,18 @@ int git_config_lock(git_transaction **out, git_config *cfg) ...@@ -1197,18 +1197,18 @@ int git_config_lock(git_transaction **out, git_config *cfg)
int git_config_unlock(git_config *cfg, int commit) int git_config_unlock(git_config *cfg, int commit)
{ {
git_config_backend *file; git_config_backend *backend;
file_internal *internal; backend_internal *internal;
internal = git_vector_get(&cfg->files, 0); internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->file) { if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
return -1; 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) ...@@ -1376,6 +1376,30 @@ int git_config_parse_path(git_buf *out, const char *value)
return git_buf_sets(out, 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 */ /* Take something the user gave us and make it nice for our hash function */
int git_config__normalize_name(const char *in, char **out) int git_config__normalize_name(const char *in, char **out)
{ {
...@@ -1393,8 +1417,8 @@ 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; goto invalid;
/* Validate and downcase up to first dot and after last dot */ /* Validate and downcase up to first dot and after last dot */
if (git_config_file_normalize_section(name, fdot) < 0 || if (normalize_section(name, fdot) < 0 ||
git_config_file_normalize_section(ldot + 1, NULL) < 0) normalize_section(ldot + 1, NULL) < 0)
goto invalid; goto invalid;
/* If there is a middle range, make sure it doesn't have newlines */ /* If there is a middle range, make sure it doesn't have newlines */
...@@ -1466,8 +1490,7 @@ int git_config_rename_section( ...@@ -1466,8 +1490,7 @@ int git_config_rename_section(
goto cleanup; goto cleanup;
if (new_section_name != NULL && if (new_section_name != NULL &&
(error = git_config_file_normalize_section( (error = normalize_section(replace.ptr, strchr(replace.ptr, '.'))) < 0)
replace.ptr, strchr(replace.ptr, '.'))) < 0)
{ {
giterr_set( giterr_set(
GITERR_CONFIG, "invalid config section '%s'", new_section_name); GITERR_CONFIG, "invalid config section '%s'", new_section_name);
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
struct git_config { struct git_config {
git_refcount rc; git_refcount rc;
git_vector files; git_vector backends;
}; };
extern int git_config__global_location(git_buf *buf); extern int git_config__global_location(git_buf *buf);
...@@ -34,19 +34,6 @@ extern int git_config_rename_section( ...@@ -34,19 +34,6 @@ extern int git_config_rename_section(
const char *old_section_name, /* eg "branch.dummy" */ const char *old_section_name, /* eg "branch.dummy" */
const char *new_section_name); /* NULL to drop the old section */ 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); 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 */ /* internal only: does not normalize key and sets out to NULL if not found */
......
...@@ -12,36 +12,57 @@ ...@@ -12,36 +12,57 @@
#include "git2/sys/config.h" #include "git2/sys/config.h"
#include "git2/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); 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) if (cfg)
cfg->free(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) git_config_entry **out, git_config_backend *cfg, const char *name)
{ {
return cfg->get(cfg, name, out); 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) git_config_backend *cfg, const char *name, const char *value)
{ {
return cfg->set(cfg, name, 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) git_config_backend *cfg, const char *name)
{ {
return cfg->del(cfg, name); return cfg->del(cfg, name);
} }
GIT_INLINE(int) git_config_file_foreach( GIT_INLINE(int) git_config_backend_foreach(
git_config_backend *cfg, git_config_backend *cfg,
int (*fn)(const git_config_entry *entry, void *data), int (*fn)(const git_config_entry *entry, void *data),
void *data) void *data)
...@@ -49,25 +70,14 @@ GIT_INLINE(int) git_config_file_foreach( ...@@ -49,25 +70,14 @@ GIT_INLINE(int) git_config_file_foreach(
return git_config_backend_foreach_match(cfg, NULL, fn, data); return git_config_backend_foreach_match(cfg, NULL, fn, data);
} }
GIT_INLINE(int) git_config_file_foreach_match( GIT_INLINE(int) git_config_backend_lock(git_config_backend *cfg)
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)
{ {
return cfg->lock(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); return cfg->unlock(cfg, success);
} }
extern int git_config_file_normalize_section(char *start, char *end);
#endif #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 @@ ...@@ -5,9 +5,8 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "config_file.h"
#include "config.h" #include "config.h"
#include "filebuf.h" #include "filebuf.h"
#include "sysdir.h" #include "sysdir.h"
#include "buffer.h" #include "buffer.h"
...@@ -18,36 +17,20 @@ ...@@ -18,36 +17,20 @@
#include "strmap.h" #include "strmap.h"
#include "array.h" #include "array.h"
#include "config_parse.h" #include "config_parse.h"
#include "config_entries.h"
#include <ctype.h> #include <ctype.h>
#include <sys/types.h> #include <sys/types.h>
#include <regex.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 */ /* Max depth for [include] directives */
#define MAX_INCLUDE_DEPTH 10 #define MAX_INCLUDE_DEPTH 10
typedef struct { typedef struct {
git_atomic refcount;
git_strmap *map;
config_entry_list *list;
} diskfile_entries;
typedef struct {
git_config_backend parent; git_config_backend parent;
/* mutex to coordinate accessing the values */ /* mutex to coordinate accessing the values */
git_mutex values_mutex; git_mutex values_mutex;
diskfile_entries *entries; git_config_entries *entries;
const git_repository *repo; const git_repository *repo;
git_config_level_t level; git_config_level_t level;
} diskfile_header; } diskfile_header;
...@@ -73,16 +56,15 @@ typedef struct { ...@@ -73,16 +56,15 @@ typedef struct {
typedef struct { typedef struct {
const git_repository *repo; const git_repository *repo;
const char *file_path; const char *file_path;
diskfile_entries *entries; git_config_entries *entries;
git_config_level_t level; git_config_level_t level;
unsigned int depth; unsigned int depth;
} diskfile_parse_state; } 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 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); 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_snapshot(git_config_backend **out, git_config_backend *in);
static int config_error_readonly(void) static int config_error_readonly(void)
...@@ -91,126 +73,14 @@ static int config_error_readonly(void) ...@@ -91,126 +73,14 @@ static int config_error_readonly(void)
return -1; 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 * 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 * refcount. This is its own function to make sure we use the mutex to
* avoid the map pointer from changing under us. * 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) { if (git_mutex_lock(&h->values_mutex) < 0) {
giterr_set(GITERR_OS, "failed to lock config backend"); giterr_set(GITERR_OS, "failed to lock config backend");
...@@ -218,31 +88,13 @@ static diskfile_entries *diskfile_entries_take(diskfile_header *h) ...@@ -218,31 +88,13 @@ static diskfile_entries *diskfile_entries_take(diskfile_header *h)
} }
entries = h->entries; entries = h->entries;
git_atomic_inc(&entries->refcount); git_config_entries_incref(entries);
git_mutex_unlock(&h->values_mutex); git_mutex_unlock(&h->values_mutex);
return entries; 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) static void config_file_clear(struct config_file *file)
{ {
struct config_file *include; struct config_file *include;
...@@ -267,14 +119,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level, const ...@@ -267,14 +119,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level, const
b->header.level = level; b->header.level = level;
b->header.repo = repo; 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; return res;
if (!git_path_exists(b->file.path)) if (!git_path_exists(b->file.path))
return 0; return 0;
if (res < 0 || (res = config_read(b->header.entries, repo, &b->file, level, 0)) < 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; b->header.entries = NULL;
} }
...@@ -316,7 +168,7 @@ out: ...@@ -316,7 +168,7 @@ out:
static int config_refresh(git_config_backend *cfg) static int config_refresh(git_config_backend *cfg)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *entries = NULL, *tmp; git_config_entries *entries = NULL, *tmp;
git_config_file *include; git_config_file *include;
int error, modified; int error, modified;
uint32_t i; uint32_t i;
...@@ -331,7 +183,7 @@ static int config_refresh(git_config_backend *cfg) ...@@ -331,7 +183,7 @@ static int config_refresh(git_config_backend *cfg)
if (!modified) if (!modified)
return 0; return 0;
if ((error = diskfile_entries_alloc(&entries)) < 0) if ((error = git_config_entries_new(&entries)) < 0)
goto out; goto out;
/* Reparse the current configuration */ /* Reparse the current configuration */
...@@ -355,7 +207,7 @@ static int config_refresh(git_config_backend *cfg) ...@@ -355,7 +207,7 @@ static int config_refresh(git_config_backend *cfg)
git_mutex_unlock(&b->header.values_mutex); git_mutex_unlock(&b->header.values_mutex);
out: out:
diskfile_entries_free(entries); git_config_entries_free(entries);
return (error == GIT_ENOTFOUND) ? 0 : error; return (error == GIT_ENOTFOUND) ? 0 : error;
} }
...@@ -368,133 +220,80 @@ static void backend_free(git_config_backend *_backend) ...@@ -368,133 +220,80 @@ static void backend_free(git_config_backend *_backend)
return; return;
config_file_clear(&backend->file); 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_mutex_free(&backend->header.values_mutex);
git__free(backend); 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( static int config_iterator_new(
git_config_iterator **iter, git_config_iterator **iter,
struct git_config_backend* backend) struct git_config_backend* backend)
{ {
diskfile_header *h;
git_config_file_iter *it;
git_config_backend *snapshot;
diskfile_header *bh = (diskfile_header *) backend; diskfile_header *bh = (diskfile_header *) backend;
git_config_entries *entries;
int error; int error;
if ((error = config_snapshot(&snapshot, backend)) < 0) if ((error = git_config_entries_dup(&entries, bh->entries)) < 0)
return error;
if ((error = snapshot->open(snapshot, bh->level, bh->repo)) < 0)
return error; return error;
it = git__calloc(1, sizeof(git_config_file_iter)); if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
GITERR_CHECK_ALLOC(it); goto out;
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;
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) static int config_set(git_config_backend *cfg, const char *name, const char *value)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *entries; git_config_entries *entries;
git_strmap *entry_map; git_config_entry *existing;
char *key, *esc_value = NULL; char *key, *esc_value = NULL;
khiter_t pos; int error;
int rval, ret;
if ((rval = git_config__normalize_name(name, &key)) < 0) if ((error = git_config__normalize_name(name, &key)) < 0)
return rval; return error;
if ((entries = diskfile_entries_take(&b->header)) == NULL) if ((entries = diskfile_entries_take(&b->header)) == NULL)
return -1; return -1;
entry_map = entries->map;
/* /* Check whether we'd be modifying an included or multivar key */
* Try to find it in the existing values and update it if it if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
* only has one value. if (error != GIT_ENOTFOUND)
*/
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;
goto out; 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 */ /* don't update if old and new values already match */
if ((!existing->entry->value && !value) || error = 0;
(existing->entry->value && value && goto out;
!strcmp(existing->entry->value, value))) {
ret = 0;
goto out;
}
} }
/* No early returns due to sanity checks, let's write it out and refresh */ /* No early returns due to sanity checks, let's write it out and refresh */
if (value) { if (value) {
esc_value = escape_value(value); esc_value = escape_value(value);
GITERR_CHECK_ALLOC(esc_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; goto out;
ret = config_refresh(cfg); error = config_refresh(cfg);
out: out:
diskfile_entries_free(entries); git_config_entries_free(entries);
git__free(esc_value); git__free(esc_value);
git__free(key); git__free(key);
return ret; return error;
} }
/* release the map containing the entry as an equivalent to freeing it */ /* release the map containing the entry as an equivalent to freeing it */
static void free_diskfile_entry(git_config_entry *entry) static void free_diskfile_entry(git_config_entry *entry)
{ {
diskfile_entries *map = (diskfile_entries *) entry->payload; git_config_entries *entries = (git_config_entries *) entry->payload;
diskfile_entries_free(map); git_config_entries_free(entries);
} }
/* /*
...@@ -503,10 +302,8 @@ static void free_diskfile_entry(git_config_entry *entry) ...@@ -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) static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
{ {
diskfile_header *h = (diskfile_header *)cfg; diskfile_header *h = (diskfile_header *)cfg;
diskfile_entries *entries; git_config_entries *entries = NULL;
git_strmap *entry_map; git_config_entry *entry;
khiter_t pos;
config_entry_list *var;
int error = 0; int error = 0;
if (!h->parent.readonly && ((error = config_refresh(cfg)) < 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 ...@@ -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) if ((entries = diskfile_entries_take(h)) == NULL)
return -1; return -1;
entry_map = entries->map;
pos = git_strmap_lookup_index(entry_map, key);
/* no error message; the config system will write one */ if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
if (!git_strmap_valid_index(entry_map, pos)) { git_config_entries_free(entries);
diskfile_entries_free(entries); return error;
return GIT_ENOTFOUND;
} }
var = git_strmap_value_at(entry_map, pos); entry->free = free_diskfile_entry;
*out = var->last->entry; entry->payload = entries;
(*out)->free = free_diskfile_entry; *out = entry;
(*out)->payload = entries;
return error; return 0;
} }
static int config_set_multivar( static int config_set_multivar(
...@@ -567,79 +359,61 @@ out: ...@@ -567,79 +359,61 @@ out:
static int config_delete(git_config_backend *cfg, const char *name) static int config_delete(git_config_backend *cfg, const char *name)
{ {
config_entry_list *var;
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *map; git_config_entries *entries = NULL;
git_strmap *entry_map; git_config_entry *entry;
char *key; char *key = NULL;
int result; int error;
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);
if (!git_strmap_valid_index(entry_map, pos)) { if ((error = git_config__normalize_name(name, &key)) < 0)
diskfile_entries_free(map); goto out;
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
}
var = git_strmap_value_at(entry_map, pos); if ((entries = diskfile_entries_take(&b->header)) == NULL)
diskfile_entries_free(map); goto out;
if (var->entry->include_depth) { /* Check whether we'd be modifying an included or multivar key */
giterr_set(GITERR_CONFIG, "cannot delete included variable"); if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
return -1; if (error == GIT_ENOTFOUND)
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
goto out;
} }
if (var->next != NULL) { if ((error = config_write(b, name, entry->name, NULL, NULL)) < 0)
giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete"); goto out;
return -1;
}
if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0) if ((error = config_refresh(cfg)) < 0)
return result; 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) static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
diskfile_entries *map; git_config_entries *entries = NULL;
git_strmap *entry_map; git_config_entry *entry = NULL;
char *key; regex_t preg = { 0 };
regex_t preg; char *key = NULL;
int result; int result;
khiter_t pos;
if ((result = git_config__normalize_name(name, &key)) < 0) if ((result = git_config__normalize_name(name, &key)) < 0)
return result; goto out;
if ((map = diskfile_entries_take(&b->header)) == NULL)
return -1;
entry_map = b->header.entries->map;
pos = git_strmap_lookup_index(entry_map, key);
if (!git_strmap_valid_index(entry_map, pos)) { if ((entries = diskfile_entries_take(&b->header)) == NULL) {
diskfile_entries_free(map); result = -1;
git__free(key); goto out;
giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
} }
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 = p_regcomp(&preg, regexp, REG_EXTENDED)) != 0) {
if (result != 0) {
giterr_set_regex(&preg, result); giterr_set_regex(&preg, result);
result = -1; result = -1;
goto out; goto out;
...@@ -648,21 +422,16 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con ...@@ -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) if ((result = config_write(b, name, key, &preg, NULL)) < 0)
goto out; goto out;
result = config_refresh(cfg); if ((result = config_refresh(cfg)) < 0)
goto out;
out: out:
git_config_entries_free(entries);
git__free(key); git__free(key);
regfree(&preg); regfree(&preg);
return result; 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) static int config_lock(git_config_backend *_cfg)
{ {
diskfile_backend *cfg = (diskfile_backend *) _cfg; diskfile_backend *cfg = (diskfile_backend *) _cfg;
...@@ -699,7 +468,7 @@ static int config_unlock(git_config_backend *_cfg, int success) ...@@ -699,7 +468,7 @@ static int config_unlock(git_config_backend *_cfg, int success)
return error; 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; diskfile_backend *backend;
...@@ -789,7 +558,7 @@ static void backend_readonly_free(git_config_backend *_backend) ...@@ -789,7 +558,7 @@ static void backend_readonly_free(git_config_backend *_backend)
if (backend == NULL) if (backend == NULL)
return; return;
diskfile_entries_free(backend->header.entries); git_config_entries_free(backend->header.entries);
git_mutex_free(&backend->header.values_mutex); git_mutex_free(&backend->header.values_mutex);
git__free(backend); git__free(backend);
} }
...@@ -799,7 +568,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve ...@@ -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_readonly_backend *b = (diskfile_readonly_backend *) cfg;
diskfile_backend *src = b->snapshot_from; diskfile_backend *src = b->snapshot_from;
diskfile_header *src_header = &src->header; diskfile_header *src_header = &src->header;
diskfile_entries *entries; git_config_entries *entries;
int error; int error;
if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0) 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 ...@@ -816,7 +585,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
return 0; 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; diskfile_readonly_backend *backend;
...@@ -826,7 +595,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in) ...@@ -826,7 +595,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
git_mutex_init(&backend->header.values_mutex); 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.readonly = 1;
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
...@@ -1062,7 +831,7 @@ static int read_on_variable( ...@@ -1062,7 +831,7 @@ static int read_on_variable(
entry->level = parse_data->level; entry->level = parse_data->level;
entry->include_depth = parse_data->depth; 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; return result;
result = 0; result = 0;
...@@ -1079,7 +848,7 @@ static int read_on_variable( ...@@ -1079,7 +848,7 @@ static int read_on_variable(
} }
static int config_read( static int config_read(
diskfile_entries *entries, git_config_entries *entries,
const git_repository *repo, const git_repository *repo,
git_config_file *file, git_config_file *file,
git_config_level_t level, 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 @@ ...@@ -11,10 +11,14 @@
#include <ctype.h> #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) 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)", 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 @@ ...@@ -12,8 +12,8 @@
#include "oid.h" #include "oid.h"
#include "parse.h" #include "parse.h"
static const char *git_config_escapes = "ntb\"\\"; extern const char *git_config_escapes;
static const char *git_config_escaped = "\n\t\b\"\\"; extern const char *git_config_escaped;
typedef struct config_file { typedef struct config_file {
git_oid checksum; git_oid checksum;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include "buf_text.h" #include "buf_text.h"
#include "vector.h" #include "vector.h"
#include "posix.h" #include "posix.h"
#include "config_file.h" #include "config_backend.h"
#include "config.h" #include "config.h"
#include "repository.h" #include "repository.h"
#include "tree.h" #include "tree.h"
...@@ -199,13 +199,15 @@ out: ...@@ -199,13 +199,15 @@ out:
*/ */
static void free_submodule_names(git_strmap *names) static void free_submodule_names(git_strmap *names)
{ {
git_buf *name; const char *key;
char *value;
if (names == NULL) if (names == NULL)
return; return;
git_strmap_foreach_value(names, name, { git_strmap_foreach(names, key, value, {
git__free(name); git__free((char *) key);
git__free(value);
}); });
git_strmap_free(names); git_strmap_free(names);
...@@ -257,7 +259,7 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf ...@@ -257,7 +259,7 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf
if (!isvalid) if (!isvalid)
continue; 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) { if (rval < 0) {
giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table"); giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
error = -1; error = -1;
...@@ -333,9 +335,9 @@ int git_submodule_lookup( ...@@ -333,9 +335,9 @@ int git_submodule_lookup(
mods = open_gitmodules(repo, GITMODULES_EXISTING); mods = open_gitmodules(repo, GITMODULES_EXISTING);
if (mods) 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) { if (error < 0) {
git_submodule_free(sm); git_submodule_free(sm);
...@@ -792,11 +794,11 @@ int git_submodule_add_setup( ...@@ -792,11 +794,11 @@ int git_submodule_add_setup(
} }
if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 || 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; goto cleanup;
if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 || 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; goto cleanup;
git_buf_clear(&name); git_buf_clear(&name);
...@@ -834,7 +836,7 @@ cleanup: ...@@ -834,7 +836,7 @@ cleanup:
if (out != NULL) if (out != NULL)
*out = sm; *out = sm;
git_config_file_free(mods); git_config_backend_free(mods);
git_repository_free(subrepo); git_repository_free(subrepo);
git_buf_dispose(&real_url); git_buf_dispose(&real_url);
git_buf_dispose(&name); git_buf_dispose(&name);
...@@ -1033,14 +1035,14 @@ static int write_var(git_repository *repo, const char *name, const char *var, co ...@@ -1033,14 +1035,14 @@ static int write_var(git_repository *repo, const char *name, const char *var, co
goto cleanup; goto cleanup;
if (val) if (val)
error = git_config_file_set_string(mods, key.ptr, val); error = git_config_backend_set_string(mods, key.ptr, val);
else else
error = git_config_file_delete(mods, key.ptr); error = git_config_backend_delete(mods, key.ptr);
git_buf_dispose(&key); git_buf_dispose(&key);
cleanup: cleanup:
git_config_file_free(mods); git_config_backend_free(mods);
return error; return error;
} }
...@@ -2070,12 +2072,12 @@ static git_config_backend *open_gitmodules( ...@@ -2070,12 +2072,12 @@ static git_config_backend *open_gitmodules(
return NULL; return NULL;
if (okay_to_create || git_path_isfile(path.ptr)) { if (okay_to_create || git_path_isfile(path.ptr)) {
/* git_config_file__ondisk should only fail if OOM */ /* git_config_backend_from_file should only fail if OOM */
if (git_config_file__ondisk(&mods, path.ptr) < 0) if (git_config_backend_from_file(&mods, path.ptr) < 0)
mods = NULL; mods = NULL;
/* open should only fail here if the file is malformed */ /* open should only fail here if the file is malformed */
else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) { else if (git_config_backend_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
git_config_file_free(mods); git_config_backend_free(mods);
mods = NULL; 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 "clar_libgit2.h"
#include "config_file.h" #include "config_backend.h"
#include "config.h" #include "config.h"
#include "path.h" #include "path.h"
...@@ -20,7 +20,7 @@ void test_config_readonly__writing_to_readonly_fails(void) ...@@ -20,7 +20,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
{ {
git_config_backend *backend; 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; backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0)); 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) ...@@ -32,11 +32,11 @@ void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
{ {
git_config_backend *backend; 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; backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0)); 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_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz")); 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) ...@@ -50,11 +50,11 @@ void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
{ {
git_config_backend *backend; 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; backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)); 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_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz")); cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
#include "buffer.h" #include "buffer.h"
#include "fileops.h" #include "fileops.h"
#include "git2/sys/config.h" #include "git2/sys/config.h"
#include "config_file.h"
#include "config.h" #include "config.h"
void test_config_write__initialize(void) 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