diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attr.c | 8 | ||||
-rw-r--r-- | src/crlf.c | 55 | ||||
-rw-r--r-- | src/filter.c | 263 |
3 files changed, 264 insertions, 62 deletions
diff --git a/src/attr.c b/src/attr.c index 6cdff29f9..7946db4d6 100644 --- a/src/attr.c +++ b/src/attr.c @@ -26,7 +26,6 @@ git_attr_t git_attr_value(const char *attr) return GIT_ATTR_VALUE_T; } - static int collect_attr_files( git_repository *repo, uint32_t flags, @@ -103,8 +102,6 @@ int git_attr_get_many( attr_get_many_info *info = NULL; size_t num_found = 0; - memset((void *)values, 0, sizeof(const char *) * num_attr); - if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; @@ -141,6 +138,11 @@ int git_attr_get_many( } } + for (k = 0; k < num_attr; k++) { + if (!info[k].found) + values[k] = NULL; + } + cleanup: git_vector_free(&files); git_attr_path__free(&path); diff --git a/src/crlf.c b/src/crlf.c index cfc2d1eb1..1242450d8 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -74,39 +74,6 @@ static int crlf_input_action(struct crlf_attrs *ca) return ca->crlf_action; } -static int crlf_load_attributes( - struct crlf_attrs *ca, git_repository *repo, const char *path) -{ -#define NUM_CONV_ATTRS 3 - - static const char *attr_names[NUM_CONV_ATTRS] = { - "crlf", "eol", "text", - }; - - const char *attr_vals[NUM_CONV_ATTRS]; - int error; - - error = git_attr_get_many(attr_vals, - repo, 0, path, NUM_CONV_ATTRS, attr_names); - - if (error == GIT_ENOTFOUND) { - ca->crlf_action = GIT_CRLF_GUESS; - ca->eol = GIT_EOL_UNSET; - return 0; - } - - if (error == 0) { - ca->crlf_action = check_crlf(attr_vals[2]); /* text */ - if (ca->crlf_action == GIT_CRLF_GUESS) - ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */ - - ca->eol = check_eol(attr_vals[1]); /* eol */ - return 0; - } - - return -1; -} - static int has_cr_in_index(const git_filter_source *src) { git_repository *repo = git_filter_source_repo(src); @@ -283,7 +250,8 @@ static int crlf_check( git_filter *self, void **payload, /* points to NULL ptr on entry, may be set */ git_filter_mode_t mode, - const git_filter_source *src) + const git_filter_source *src, + const char **attr_values) { int error; struct crlf_attrs ca; @@ -291,11 +259,16 @@ static int crlf_check( GIT_UNUSED(self); GIT_UNUSED(mode); - /* Load gitattributes for the path */ - error = crlf_load_attributes( - &ca, git_filter_source_repo(src), git_filter_source_path(src)); - if (error < 0) - return error; + if (!attr_values) { + ca.crlf_action = GIT_CRLF_GUESS; + ca.eol = GIT_EOL_UNSET; + } else { + ca.crlf_action = check_crlf(attr_values[2]); /* text */ + if (ca.crlf_action == GIT_CRLF_GUESS) + ca.crlf_action = check_crlf(attr_values[0]); /* clrf */ + ca.eol = check_eol(attr_values[1]); /* eol */ + } + ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT; /* * Use the core Git logic to see if we should perform CRLF for this file @@ -350,7 +323,11 @@ static void crlf_cleanup( git_filter *git_crlf_filter_new(void) { struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter)); + f->f.version = GIT_FILTER_VERSION; + f->f.attributes = "crlf eol text"; + f->f.initialize = NULL; + f->f.shutdown = NULL; f->f.check = crlf_check; f->f.apply = crlf_apply; f->f.cleanup = crlf_cleanup; diff --git a/src/filter.c b/src/filter.c index 3d4c6d6ce..7fbc20a41 100644 --- a/src/filter.c +++ b/src/filter.c @@ -12,6 +12,7 @@ #include "repository.h" #include "git2/config.h" #include "blob.h" +#include "attr_file.h" struct git_filter_source { git_repository *repo; @@ -35,9 +36,187 @@ struct git_filter_list { typedef struct { const char *filter_name; git_filter *filter; + int priority; + size_t nattrs, nmatches; + char *attrdata; + const char *attrs[GIT_FLEX_ARRAY]; } git_filter_def; -static git_array_t(git_filter_def) filter_registry = GIT_ARRAY_INIT; +static int filter_def_priority_cmp(const void *a, const void *b) +{ + int pa = ((const git_filter_def *)a)->priority; + int pb = ((const git_filter_def *)b)->priority; + return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; +} + +static git_vector git__filter_registry = { + 0, filter_def_priority_cmp, NULL, 0, 0 +}; + +static int filter_def_scan_attrs( + git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) +{ + const char *start, *scan = attr_str; + int has_eq; + + *nattr = *nmatch = 0; + + if (!scan) + return 0; + + while (*scan) { + while (git__isspace(*scan)) scan++; + + for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) { + if (*scan == '=') + has_eq = 1; + } + + if (scan > start) { + (*nattr)++; + if (has_eq || *scan == '-' || *scan == '+' || *scan == '!') + (*nmatch)++; + + if (has_eq) + git_buf_putc(attrs, '='); + git_buf_put(attrs, start, scan - start); + git_buf_putc(attrs, '\0'); + } + } + + return 0; +} + +static void filter_def_set_attrs(git_filter_def *fdef) +{ + char *scan = fdef->attrdata; + size_t i; + + for (i = 0; i < fdef->nattrs; ++i) { + const char *name, *value; + + switch (*scan) { + case '=': + name = scan + 1; + for (scan++; *scan != '='; scan++) /* find '=' */; + *scan++ = '\0'; + value = scan; + break; + case '-': + name = scan + 1; value = git_attr__false; break; + case '+': + name = scan + 1; value = git_attr__true; break; + case '!': + name = scan + 1; value = git_attr__unset; break; + default: + name = scan; value = NULL; break; + } + + fdef->attrs[i] = name; + fdef->attrs[i + fdef->nattrs] = value; + + scan += strlen(scan) + 1; + } +} + +int git_filter_register( + const char *name, git_filter *filter, int priority) +{ + git_filter_def *fdef; + size_t nattr = 0, nmatch = 0; + git_buf attrs = GIT_BUF_INIT; + + if (git_filter_lookup(name) != NULL) { + giterr_set( + GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); + return -1; + } + + if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) + return -1; + + fdef = git__calloc( + sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1); + GITERR_CHECK_ALLOC(fdef); + + fdef->filter_name = name; + fdef->filter = filter; + fdef->priority = priority; + fdef->nattrs = nattr; + fdef->nmatches = nmatch; + fdef->attrdata = git_buf_detach(&attrs); + + filter_def_set_attrs(fdef); + + if (git_vector_insert(&git__filter_registry, fdef) < 0) { + git__free(fdef->attrdata); + git__free(fdef); + return -1; + } + + git_vector_sort(&git__filter_registry); + return 0; +} + +static int filter_def_name_key_check(const void *key, const void *fdef) +{ + const char *name = + fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; + return name ? -1 : git__strcmp(key, name); +} + +static git_filter_def *filter_find_by_name(size_t *pos, const char *name) +{ + git_filter_def *fdef = NULL; + + if (!git_vector_search2( + pos, &git__filter_registry, filter_def_name_key_check, name)) + fdef = git_vector_get(&git__filter_registry, *pos); + + return fdef; +} + +int git_filter_unregister(const char *name) +{ + size_t pos; + git_filter_def *fdef; + + /* cannot unregister default filters */ + if (!strcmp(GIT_FILTER_CRLF, name)) { + giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); + return -1; + } + + if ((fdef = filter_find_by_name(&pos, name)) == NULL) { + giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); + return GIT_ENOTFOUND; + } + + (void)git_vector_remove(&git__filter_registry, pos); + + if (fdef->filter->shutdown) + fdef->filter->shutdown(fdef->filter); + + git__free(fdef->attrdata); + git__free(fdef); + + return 0; +} + +git_filter *git_filter_lookup(const char *name) +{ + size_t pos; + git_filter_def *fdef = filter_find_by_name(&pos, name); + return fdef ? fdef->filter : NULL; +} + +static int filter_load_defaults(void) +{ + if (!git_vector_length(&git__filter_registry)) + return git_filter_register(GIT_FILTER_CRLF, git_crlf_filter_new(), 0); + + return 0; +} git_repository *git_filter_source_repo(const git_filter_source *src) { @@ -59,20 +238,6 @@ const git_oid *git_filter_source_id(const git_filter_source *src) return git_oid_iszero(&src->oid) ? NULL : &src->oid; } -static int filter_load_defaults(void) -{ - if (!git_array_size(filter_registry)) { - git_filter_def *fdef = git_array_alloc(filter_registry); - GITERR_CHECK_ALLOC(fdef); - - fdef->filter_name = GIT_FILTER_CRLF; - fdef->filter = git_crlf_filter_new(); - GITERR_CHECK_ALLOC(fdef->filter); - } - - return 0; -} - static int git_filter_list_new( git_filter_list **out, git_filter_mode_t mode, const git_filter_source *src) { @@ -92,6 +257,47 @@ static int git_filter_list_new( return 0; } +static int filter_list_check_attributes( + const char ***out, git_filter_def *fdef, const git_filter_source *src) +{ + int error; + size_t i; + const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); + GITERR_CHECK_ALLOC(strs); + + error = git_attr_get_many( + strs, src->repo, 0, src->path, fdef->nattrs, fdef->attrs); + + /* if no values were found but no matches are needed, it's okay! */ + if (error == GIT_ENOTFOUND && !fdef->nmatches) { + giterr_clear(); + git__free(strs); + return 0; + } + + for (i = 0; !error && i < fdef->nattrs; ++i) { + const char *want = fdef->attrs[fdef->nattrs + i]; + git_attr_t want_type, found_type; + + if (!want) + continue; + + want_type = git_attr_value(want); + found_type = git_attr_value(strs[i]); + + if (want_type != found_type || + (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i]))) + error = GIT_ENOTFOUND; + } + + if (error) + git__free(strs); + else + *out = strs; + + return error; +} + int git_filter_list_load( git_filter_list **filters, git_repository *repo, @@ -102,7 +308,8 @@ int git_filter_list_load( git_filter_list *fl = NULL; git_filter_source src = { 0 }; git_filter_entry *fe; - uint32_t f; + size_t idx; + git_filter_def *fdef; if (filter_load_defaults() < 0) return -1; @@ -110,15 +317,27 @@ int git_filter_list_load( src.repo = repo; src.path = path; - for (f = 0; f < git_array_size(filter_registry); ++f) { + git_vector_foreach(&git__filter_registry, idx, fdef) { + const char **values = NULL; void *payload = NULL; - git_filter_def *fdef = git_array_get(filter_registry, f); if (!fdef || !fdef->filter) continue; + if (fdef->nattrs > 0) { + error = filter_list_check_attributes(&values, fdef, &src); + if (error == GIT_ENOTFOUND) { + error = 0; + continue; + } else if (error < 0) + break; + } + if (fdef->filter->check) - error = fdef->filter->check(fdef->filter, &payload, mode, &src); + error = fdef->filter->check( + fdef->filter, &payload, mode, &src, values); + + git__free(values); if (error == GIT_ENOTFOUND) error = 0; @@ -171,6 +390,7 @@ int git_filter_list_apply( uint32_t i; unsigned int src; git_buf *dbuffer[2]; + git_filter_entry *fe; if (!fl) { git_buf_swap(dest, source); @@ -188,11 +408,14 @@ int git_filter_list_apply( return -1; for (i = 0; i < git_array_size(fl->filters); ++i) { - git_filter_entry *fe = git_array_get(fl->filters, i); unsigned int dst = 1 - src; git_buf_clear(dbuffer[dst]); + fe = git_array_get( + fl->filters, (fl->mode == GIT_FILTER_TO_ODB) ? + i : git_array_size(fl->filters) - 1 - i); + /* Apply the filter from dbuffer[src] to the other buffer; * if the filtering is canceled by the user mid-filter, * we skip to the next filter without changing the source |