diff options
Diffstat (limited to 'src/config_file.c')
-rw-r--r-- | src/config_file.c | 421 |
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, |