diff options
Diffstat (limited to 'src/attr_file.c')
-rw-r--r-- | src/attr_file.c | 326 |
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; |