-#include "common.h"
+/*
+ * 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 "attrcache.h"
+
#include "repository.h"
#include "attr_file.h"
#include "config.h"
#include "sysdir.h"
#include "ignore.h"
+#include "path.h"
GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
{
GIT_UNUSED(cache); /* avoid warning if threading is off */
if (git_mutex_lock(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "unable to get attr cache lock");
+ git_error_set(GIT_ERROR_OS, "unable to get attr cache lock");
return -1;
}
return 0;
GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
git_attr_cache *cache, const char *path)
{
- khiter_t pos = git_strmap_lookup_index(cache->files, path);
-
- if (git_strmap_valid_index(cache->files, pos))
- return git_strmap_value_at(cache->files, pos);
- else
- return NULL;
+ return git_strmap_get(cache->files, path);
}
int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
+ git_repository *repo,
const char *base,
const char *path,
git_pool *pool)
{
+ git_str fullpath_str = GIT_STR_INIT;
size_t baselen = 0, pathlen = strlen(path);
size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
git_attr_file_entry *ce;
- if (base != NULL && git_path_root(path) < 0) {
+ if (base != NULL && git_fs_path_root(path) < 0) {
baselen = strlen(base);
cachesize += baselen;
cachesize++;
}
- ce = git_pool_mallocz(pool, (uint32_t)cachesize);
- GITERR_CHECK_ALLOC(ce);
+ ce = git_pool_mallocz(pool, cachesize);
+ GIT_ERROR_CHECK_ALLOC(ce);
if (baselen) {
memcpy(ce->fullpath, base, baselen);
}
memcpy(&ce->fullpath[baselen], path, pathlen);
+ fullpath_str.ptr = ce->fullpath;
+ fullpath_str.size = pathlen + baselen;
+
+ if (git_path_validate_str_length(repo, &fullpath_str) < 0)
+ return -1;
+
ce->path = &ce->fullpath[baselen];
*out = ce;
static int attr_cache_make_entry(
git_attr_file_entry **out, git_repository *repo, const char *path)
{
- int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
+ int error;
- error = git_attr_cache__alloc_file_entry(
- &entry, git_repository_workdir(repo), path, &cache->pool);
+ if ((error = git_attr_cache__alloc_file_entry(&entry, repo,
+ git_repository_workdir(repo), path, &cache->pool)) < 0)
+ return error;
- if (!error) {
- git_strmap_insert(cache->files, entry->path, entry, &error);
- if (error > 0)
- error = 0;
- }
+ if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0)
+ return error;
*out = entry;
return error;
* Replace the existing value if another thread has
* created it in the meantime.
*/
- old = git__swap(entry->file[file->source], file);
+ old = git_atomic_swap(entry->file[file->source.type], file);
if (old) {
GIT_REFCOUNT_OWN(old, NULL);
{
int error = 0;
git_attr_file_entry *entry;
- git_attr_file *old = NULL;
+ git_attr_file *oldfile = NULL;
if (!file)
return 0;
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
- old = git__compare_and_swap(&entry->file[file->source], file, NULL);
+ oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
attr_cache_unlock(cache);
- if (old) {
- GIT_REFCOUNT_OWN(old, NULL);
- git_attr_file__free(old);
+ if (oldfile == file) {
+ GIT_REFCOUNT_OWN(file, NULL);
+ git_attr_file__free(file);
}
return error;
git_attr_file_entry **out_entry,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename)
+ git_attr_file_source *source)
{
int error = 0;
- git_buf path = GIT_BUF_INIT;
- const char *wd = git_repository_workdir(repo), *relfile;
+ git_str path = GIT_STR_INIT;
+ const char *wd = git_repository_workdir(repo);
+ const char *filename;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL;
/* join base and path as needed */
- if (base != NULL && git_path_root(filename) < 0) {
- git_buf *p = attr_session ? &attr_session->tmp : &path;
+ if (source->base != NULL && git_fs_path_root(source->filename) < 0) {
+ git_str *p = attr_session ? &attr_session->tmp : &path;
- if (git_buf_joinpath(p, base, filename) < 0)
+ if (git_str_joinpath(p, source->base, source->filename) < 0 ||
+ git_path_validate_str_length(repo, p) < 0)
return -1;
filename = p->ptr;
+ } else {
+ filename = source->filename;
}
- relfile = filename;
- if (wd && !git__prefixcmp(relfile, wd))
- relfile += strlen(wd);
+ if (wd && !git__prefixcmp(filename, wd))
+ filename += strlen(wd);
/* check cache for existing entry */
if ((error = attr_cache_lock(cache)) < 0)
goto cleanup;
- entry = attr_cache_lookup_entry(cache, relfile);
- if (!entry)
- error = attr_cache_make_entry(&entry, repo, relfile);
- else if (entry->file[source] != NULL) {
- file = entry->file[source];
+ entry = attr_cache_lookup_entry(cache, filename);
+
+ if (!entry) {
+ error = attr_cache_make_entry(&entry, repo, filename);
+ } else if (entry->file[source->type] != NULL) {
+ file = entry->file[source->type];
GIT_REFCOUNT_INC(file);
}
*out_file = file;
*out_entry = entry;
- git_buf_free(&path);
+ git_str_dispose(&path);
return error;
}
git_attr_file **out,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
- git_attr_file_parser parser)
+ git_attr_file_source *source,
+ git_attr_file_parser parser,
+ bool allow_macros)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL, *updated = NULL;
- if ((error = attr_cache_lookup(
- &file, &entry, repo, attr_session, source, base, filename)) < 0)
+ if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0)
return error;
/* load file if we don't have one or if existing one is out of date */
- if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
- error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser);
+ if (!file ||
+ (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0)
+ error = git_attr_file__load(&updated, repo, attr_session,
+ entry, source, parser,
+ allow_macros);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
- if ((error = attr_cache_upsert(cache, updated)) < 0)
+ if ((error = attr_cache_upsert(cache, updated)) < 0) {
git_attr_file__free(updated);
- else {
+ } else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
}
/* no error if file simply doesn't exist */
if (error == GIT_ENOTFOUND) {
- giterr_clear();
+ git_error_clear();
error = 0;
}
}
bool git_attr_cache__is_cached(
git_repository *repo,
- git_attr_file_source source,
+ git_attr_file_source_t source_type,
const char *filename)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
- git_strmap *files;
- khiter_t pos;
git_attr_file_entry *entry;
+ git_strmap *files;
if (!cache || !(files = cache->files))
return false;
- pos = git_strmap_lookup_index(files, filename);
- if (!git_strmap_valid_index(files, pos))
+ if ((entry = git_strmap_get(files, filename)) == NULL)
return false;
- entry = git_strmap_value_at(files, pos);
-
- return entry && (entry->file[source] != NULL);
+ return entry && (entry->file[source_type] != NULL);
}
static int attr_cache__lookup_path(
char **out, git_config *cfg, const char *key, const char *fallback)
{
- git_buf buf = GIT_BUF_INIT;
+ git_str buf = GIT_STR_INIT;
int error;
git_config_entry *entry = NULL;
const char *cfgval = entry->value;
/* expand leading ~/ as needed */
- if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
- !git_sysdir_find_global_file(&buf, &cfgval[2]))
- *out = git_buf_detach(&buf);
- else if (cfgval)
+ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
+ if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
+ *out = git_str_detach(&buf);
+ } else if (cfgval) {
*out = git__strdup(cfgval);
+ }
+ }
+ else if (!git_sysdir_find_xdg_file(&buf, fallback)) {
+ *out = git_str_detach(&buf);
}
- else if (!git_sysdir_find_xdg_file(&buf, fallback))
- *out = git_buf_detach(&buf);
git_config_entry_free(entry);
- git_buf_free(&buf);
+ git_str_dispose(&buf);
return error;
}
git_strmap_foreach_value(cache->files, entry, {
for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
- if ((file = git__swap(entry->file[i], NULL)) != NULL) {
+ if ((file = git_atomic_swap(entry->file[i], NULL)) != NULL) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
return 0;
cache = git__calloc(1, sizeof(git_attr_cache));
- GITERR_CHECK_ALLOC(cache);
+ GIT_ERROR_CHECK_ALLOC(cache);
/* set up lock */
if (git_mutex_init(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "unable to initialize lock for attr cache");
+ git_error_set(GIT_ERROR_OS, "unable to initialize lock for attr cache");
git__free(cache);
return -1;
}
/* allocate hashtable for attribute and ignore file contents,
* hashtable for attribute macros, and string pool
*/
- if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
- (ret = git_strmap_alloc(&cache->macros)) < 0)
+ if ((ret = git_strmap_new(&cache->files)) < 0 ||
+ (ret = git_strmap_new(&cache->macros)) < 0 ||
+ (ret = git_pool_init(&cache->pool, 1)) < 0)
goto cancel;
- git_pool_init(&cache->pool, 1);
-
- cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
- if (cache)
+ if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg);
/* insert default macros */
- return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
+ return git_attr_add_macro(repo, "binary", "-diff -merge -text -crlf");
cancel:
attr_cache__free(cache);
return ret;
}
-void git_attr_cache_flush(git_repository *repo)
+int git_attr_cache_flush(git_repository *repo)
{
git_attr_cache *cache;
/* this could be done less expensively, but for now, we'll just free
* the entire attrcache and let the next use reinitialize it...
*/
- if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
+ if (repo && (cache = git_atomic_swap(repo->attrcache, NULL)) != NULL)
attr_cache__free(cache);
+
+ return 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
- git_strmap *macros = cache->macros;
- int error;
-
- /* TODO: generate warning log if (macro->assigns.length == 0) */
- if (macro->assigns.length == 0)
- return 0;
+ git_attr_rule *preexisting;
+ bool locked = false;
+ int error = 0;
- if (attr_cache_lock(cache) < 0) {
- giterr_set(GITERR_OS, "unable to get attr cache lock");
- error = -1;
- } else {
- git_strmap_insert(macros, macro->match.pattern, macro, &error);
- git_mutex_unlock(&cache->lock);
+ /*
+ * Callers assume that if we return success, that the
+ * macro will have been adopted by the attributes cache.
+ * Thus, we have to free the macro here if it's not being
+ * added to the cache.
+ *
+ * TODO: generate warning log if (macro->assigns.length == 0)
+ */
+ if (macro->assigns.length == 0) {
+ git_attr_rule__free(macro);
+ goto out;
}
- return (error < 0) ? -1 : 0;
+ if ((error = attr_cache_lock(cache)) < 0)
+ goto out;
+ locked = true;
+
+ if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL)
+ git_attr_rule__free(preexisting);
+
+ if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0)
+ goto out;
+
+out:
+ if (locked)
+ attr_cache_unlock(cache);
+ return error;
}
git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
- khiter_t pos;
-
- pos = git_strmap_lookup_index(macros, name);
- if (!git_strmap_valid_index(macros, pos))
- return NULL;
-
- return (git_attr_rule *)git_strmap_value_at(macros, pos);
+ return git_strmap_get(macros, name);
}
-