summaryrefslogtreecommitdiff
path: root/src/config_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config_file.c')
-rw-r--r--src/config_file.c421
1 files changed, 95 insertions, 326 deletions
diff --git a/src/config_file.c b/src/config_file.c
index 26bc200df..e8740d35f 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -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,