summaryrefslogtreecommitdiff
path: root/src/attr_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/attr_file.c')
-rw-r--r--src/attr_file.c326
1 files changed, 198 insertions, 128 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index 695f661a8..86b3448ee 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -1,86 +1,173 @@
#include "common.h"
#include "repository.h"
#include "filebuf.h"
-#include "attr.h"
+#include "attr_file.h"
#include "git2/blob.h"
#include "git2/tree.h"
+#include "index.h"
#include <ctype.h>
-static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
-static void git_attr_rule__clear(git_attr_rule *rule);
-static bool parse_optimized_patterns(
- git_attr_fnmatch *spec,
- git_pool *pool,
- const char *pattern);
+static void attr_file_free(git_attr_file *file)
+{
+ git_attr_file__clear_rules(file);
+ git_pool_clear(&file->pool);
+ git__memzero(file, sizeof(*file));
+ git__free(file);
+}
int git_attr_file__new(
- git_attr_file **attrs_ptr,
- git_attr_file_source from,
- const char *path,
- git_pool *pool)
+ git_attr_file **out,
+ git_attr_cache_entry *ce,
+ git_attr_cache_source source)
{
- git_attr_file *attrs = NULL;
-
- attrs = git__calloc(1, sizeof(git_attr_file));
+ git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs);
- GIT_REFCOUNT_INC(attrs);
- if (pool)
- attrs->pool = pool;
- else {
- attrs->pool = git__calloc(1, sizeof(git_pool));
- if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
- goto fail;
- attrs->pool_is_allocated = true;
+ if (git_pool_init(&attrs->pool, 1, 0) < 0 ||
+ git_vector_init(&attrs->rules, 0, NULL) < 0)
+ {
+ attr_file_free(attrs);
+ return -1;
}
- if (path) {
- size_t len = strlen(path);
+ GIT_REFCOUNT_INC(attrs);
+ attrs->ce = ce;
+ attrs->source = source;
+ *out = attrs;
+ return 0;
+}
- attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
- GITERR_CHECK_ALLOC(attrs->key);
+void git_attr_file__clear_rules(git_attr_file *file)
+{
+ unsigned int i;
+ git_attr_rule *rule;
- attrs->key[0] = '0' + (char)from;
- attrs->key[1] = '#';
- memcpy(&attrs->key[2], path, len);
- attrs->key[len + 2] = '\0';
- }
+ git_vector_foreach(&file->rules, i, rule)
+ git_attr_rule__free(rule);
+ git_vector_free(&file->rules);
+}
+
+void git_attr_file__free(git_attr_file *file)
+{
+ if (!file)
+ return;
+ GIT_REFCOUNT_DEC(file, attr_file_free);
+}
+
+static int attr_file_oid_from_index(
+ git_oid *oid, git_repository *repo, const char *path)
+{
+ int error;
+ git_index *idx;
+ size_t pos;
+ const git_index_entry *entry;
- if (git_vector_init(&attrs->rules, 4, NULL) < 0)
- goto fail;
+ if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
+ (error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0)
+ return error;
- *attrs_ptr = attrs;
+ if (!(entry = git_index_get_byindex(idx, pos)))
+ return GIT_ENOTFOUND;
+
+ *oid = entry->id;
return 0;
+}
+
+int git_attr_file__load(
+ git_attr_file **out,
+ git_repository *repo,
+ git_attr_cache_entry *ce,
+ git_attr_cache_source source,
+ git_attr_cache_parser parser,
+ void *payload)
+{
+ int error = 0;
+ git_blob *blob = NULL;
+ git_buf content = GIT_BUF_INIT;
+ const char *data = NULL;
+ git_attr_file *file;
-fail:
- git_attr_file__free(attrs);
- attrs_ptr = NULL;
- return -1;
+ *out = NULL;
+
+ if (source == GIT_ATTR_CACHE__FROM_INDEX) {
+ git_oid id;
+
+ if ((error = attr_file_oid_from_index(&id, repo, ce->path)) < 0 ||
+ (error = git_blob_lookup(&blob, repo, &id)) < 0)
+ return error;
+
+ data = git_blob_rawcontent(blob);
+ } else {
+ if ((error = git_futils_readbuffer(&content, ce->fullpath)) < 0)
+ /* always return ENOTFOUND so item will just be skipped */
+ /* TODO: issue a warning once warnings API is available */
+ return GIT_ENOTFOUND;
+ data = content.ptr;
+ }
+
+ if ((error = git_attr_file__new(&file, ce, source)) < 0)
+ goto cleanup;
+
+ if (parser && (error = parser(repo, file, data, payload)) < 0)
+ git_attr_file__free(file);
+ else
+ *out = file;
+
+cleanup:
+ git_blob_free(blob);
+ git_buf_free(&content);
+
+ return error;
+}
+
+int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file)
+{
+ if (!file)
+ return 1;
+
+ if (file->source == GIT_ATTR_CACHE__FROM_INDEX) {
+ int error;
+ git_oid id;
+
+ if ((error = attr_file_oid_from_index(&id, repo, file->ce->path)) < 0)
+ return error;
+
+ return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
+ }
+
+ return git_futils_filestamp_check(
+ &file->cache_data.stamp, file->ce->fullpath);
}
+static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
+static void git_attr_rule__clear(git_attr_rule *rule);
+static bool parse_optimized_patterns(
+ git_attr_fnmatch *spec,
+ git_pool *pool,
+ const char *pattern);
+
int git_attr_file__parse_buffer(
- git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
+ git_repository *repo,
+ git_attr_file *attrs,
+ const char *data,
+ void *payload)
{
int error = 0;
- const char *scan = NULL, *context = NULL;
+ const char *scan = data, *context = NULL;
git_attr_rule *rule = NULL;
- GIT_UNUSED(parsedata);
-
- assert(buffer && attrs);
-
- scan = buffer;
+ GIT_UNUSED(payload);
/* if subdir file path, convert context for file paths */
- if (attrs->key &&
- git_path_root(attrs->key + 2) < 0 &&
- git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0)
- context = attrs->key + 2;
+ if (attrs->ce &&
+ git_path_root(attrs->ce->path) < 0 &&
+ !git__suffixcmp(attrs->ce->path, "/" GIT_ATTR_FILE))
+ context = attrs->ce->path;
while (!error && *scan) {
/* allocate rule if needed */
if (!rule) {
- if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+ if (!(rule = git__calloc(1, sizeof(*rule)))) {
error = -1;
break;
}
@@ -90,9 +177,9 @@ int git_attr_file__parse_buffer(
/* parse the next "pattern attr attr attr" line */
if (!(error = git_attr_fnmatch__parse(
- &rule->match, attrs->pool, context, &scan)) &&
+ &rule->match, &attrs->pool, context, &scan)) &&
!(error = git_attr_assignment__parse(
- repo, attrs->pool, &rule->assigns, &scan)))
+ repo, &attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
@@ -118,61 +205,6 @@ int git_attr_file__parse_buffer(
return error;
}
-int git_attr_file__new_and_load(
- git_attr_file **attrs_ptr,
- const char *path)
-{
- int error;
- git_buf content = GIT_BUF_INIT;
-
- if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
- return error;
-
- if (!(error = git_futils_readbuffer(&content, path)))
- error = git_attr_file__parse_buffer(
- NULL, NULL, git_buf_cstr(&content), *attrs_ptr);
-
- git_buf_free(&content);
-
- if (error) {
- git_attr_file__free(*attrs_ptr);
- *attrs_ptr = NULL;
- }
-
- return error;
-}
-
-void git_attr_file__clear_rules(git_attr_file *file)
-{
- unsigned int i;
- git_attr_rule *rule;
-
- git_vector_foreach(&file->rules, i, rule)
- git_attr_rule__free(rule);
-
- git_vector_free(&file->rules);
-}
-
-static void attr_file_free(git_attr_file *file)
-{
- git_attr_file__clear_rules(file);
-
- if (file->pool_is_allocated) {
- git_pool_clear(file->pool);
- git__free(file->pool);
- }
- file->pool = NULL;
-
- git__free(file);
-}
-
-void git_attr_file__free(git_attr_file *file)
-{
- if (!file)
- return;
- GIT_REFCOUNT_DEC(file, attr_file_free);
-}
-
uint32_t git_attr_file__name_hash(const char *name)
{
uint32_t h = 5381;
@@ -183,7 +215,6 @@ uint32_t git_attr_file__name_hash(const char *name)
return h;
}
-
int git_attr_file__lookup_one(
git_attr_file *file,
const git_attr_path *path,
@@ -212,25 +243,64 @@ int git_attr_file__lookup_one(
return 0;
}
+int git_attr_file__load_standalone(
+ git_attr_file **out,
+ const char *path)
+{
+ int error;
+ git_attr_file *file;
+ git_buf content = GIT_BUF_INIT;
+
+ error = git_attr_file__new(&file, NULL, GIT_ATTR_CACHE__FROM_FILE);
+ if (error < 0)
+ return error;
+
+ error = git_attr_cache_entry__new(&file->ce, NULL, path, &file->pool);
+ if (error < 0) {
+ git_attr_file__free(file);
+ return error;
+ }
+ /* because the cache entry is allocated from the file's own pool, we
+ * don't have to free it - freeing file+pool will free cache entry, too.
+ */
+
+ if (!(error = git_futils_readbuffer(&content, path))) {
+ error = git_attr_file__parse_buffer(NULL, file, content.ptr, NULL);
+ git_buf_free(&content);
+ }
+
+ if (error < 0)
+ git_attr_file__free(file);
+ else
+ *out = file;
+
+ return error;
+}
bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
const git_attr_path *path)
{
- int fnm;
- int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0;
+ const char *filename;
+ int flags = 0;
- if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
+ if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir)
return false;
- if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
- fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags);
- else if (path->is_dir)
- fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags);
- else
- fnm = p_fnmatch(match->pattern, path->basename, icase_flags);
+ if (match->flags & GIT_ATTR_FNMATCH_ICASE)
+ flags |= FNM_CASEFOLD;
- return (fnm == FNM_NOMATCH) ? false : true;
+ if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
+ filename = path->path;
+ flags |= FNM_PATHNAME;
+ } else {
+ filename = path->basename;
+
+ if (path->is_dir)
+ flags |= FNM_LEADING_DIR;
+ }
+
+ return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
}
bool git_attr_rule__match(
@@ -245,7 +315,6 @@ bool git_attr_rule__match(
return matched;
}
-
git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name)
{
@@ -344,7 +413,7 @@ void git_attr_path__free(git_attr_path *info)
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
git_pool *pool,
- const char *source,
+ const char *context,
const char **base)
{
const char *pattern, *scan;
@@ -412,21 +481,21 @@ int git_attr_fnmatch__parse(
}
if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
- source != NULL && git_path_root(pattern) < 0)
+ context != NULL && git_path_root(pattern) < 0)
{
- /* use context path minus the trailing filename */
- char *slash = strrchr(source, '/');
- size_t sourcelen = slash ? slash - source + 1 : 0;
+ /* use context path minus the trailing filename */
+ char *slash = strrchr(context, '/');
+ size_t contextlen = slash ? slash - context + 1 : 0;
/* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file
*/
spec->pattern = git_pool_malloc(
- pool, (uint32_t)(sourcelen + spec->length + 1));
+ pool, (uint32_t)(contextlen + spec->length + 1));
if (spec->pattern) {
- memcpy(spec->pattern, source, sourcelen);
- memcpy(spec->pattern + sourcelen, pattern, spec->length);
- spec->length += sourcelen;
+ memcpy(spec->pattern, context, contextlen);
+ memcpy(spec->pattern + contextlen, pattern, spec->length);
+ spec->length += contextlen;
spec->pattern[spec->length] = '\0';
}
} else {
@@ -439,6 +508,7 @@ int git_attr_fnmatch__parse(
} else {
/* strip '\' that might have be used for internal whitespace */
spec->length = git__unescape(spec->pattern);
+ /* TODO: convert remaining '\' into '/' for POSIX ??? */
}
return 0;