summaryrefslogtreecommitdiff
path: root/src/crlf.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-08-28 16:44:04 -0700
committerRussell Belfer <rb@github.com>2013-09-17 09:30:06 -0700
commit85d5481206a932d747b2d5587b6d4c7f69993ba6 (patch)
treea66d55c92d70668509efce5b231517467a3f8b7e /src/crlf.c
parent0cf77103b218ad3622aff34f3296db1bdd5f0df9 (diff)
downloadlibgit2-85d5481206a932d747b2d5587b6d4c7f69993ba6.tar.gz
Create public filter object and use it
This creates include/sys/filter.h with a basic definition of a git_filter and then converts the internal code to use it. There are related internal objects (git_filter_list) that we will want to publish at some point, but this is a first step.
Diffstat (limited to 'src/crlf.c')
-rw-r--r--src/crlf.c171
1 files changed, 101 insertions, 70 deletions
diff --git a/src/crlf.c b/src/crlf.c
index fbb3ba2dd..2177bff98 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -19,13 +19,11 @@
struct crlf_attrs {
int crlf_action;
int eol;
+ int auto_crlf;
};
struct crlf_filter {
git_filter f;
- struct crlf_attrs attrs;
- git_repository *repo;
- char path[GIT_FLEX_ARRAY];
};
static int check_crlf(const char *value)
@@ -76,7 +74,8 @@ 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)
+static int crlf_load_attributes(
+ struct crlf_attrs *ca, git_repository *repo, const char *path)
{
#define NUM_CONV_ATTRS 3
@@ -108,9 +107,8 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
return -1;
}
-static int has_cr_in_index(git_filter *self)
+static int has_cr_in_index(git_repository *repo, const char *path)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
git_index *index;
const git_index_entry *entry;
git_blob *blob;
@@ -118,19 +116,19 @@ static int has_cr_in_index(git_filter *self)
git_off_t blobsize;
bool found_cr;
- if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+ if (git_repository_index__weakptr(&index, repo) < 0) {
giterr_clear();
return false;
}
- if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
- !(entry = git_index_get_bypath(index, filter->path, 1)))
+ if (!(entry = git_index_get_bypath(index, path, 0)) &&
+ !(entry = git_index_get_bypath(index, path, 1)))
return false;
if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
return true;
- if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+ if (git_blob_lookup(&blob, repo, &entry->oid) < 0)
return false;
blobcontent = git_blob_rawcontent(blob);
@@ -147,26 +145,26 @@ static int has_cr_in_index(git_filter *self)
}
static int crlf_apply_to_odb(
- git_filter *self, git_buf *dest, const git_buf *source)
+ struct crlf_attrs *ca,
+ git_buffer *to,
+ const git_buffer *from,
+ const git_filter_source *src)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
-
- assert(self && dest && source);
+ const git_buf from_buf = GIT_BUF_FROM_BUFFER(from);
+ git_buf to_buf = GIT_BUF_FROM_BUFFER(to);
/* Empty file? Nothing to do */
- if (git_buf_len(source) == 0)
+ if (!git_buf_len(&from_buf))
return 0;
/* Heuristics to see if we can skip the conversion.
* Straight from Core Git.
*/
- if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
- filter->attrs.crlf_action == GIT_CRLF_GUESS) {
-
+ if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
git_buf_text_stats stats;
/* Check heuristics for binary vs text... */
- if (git_buf_text_gather_stats(&stats, source, false))
+ if (git_buf_text_gather_stats(&stats, &from_buf, false))
return -1;
/*
@@ -175,28 +173,34 @@ static int crlf_apply_to_odb(
* stuff?
*/
if (stats.cr != stats.crlf)
- return -1;
+ return GIT_ENOTFOUND;
- if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+ if (ca->crlf_action == GIT_CRLF_GUESS) {
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
*/
- if (has_cr_in_index(self))
- return -1;
+ if (has_cr_in_index(src->repo, src->path))
+ return GIT_ENOTFOUND;
}
if (!stats.cr)
- return -1;
+ return GIT_ENOTFOUND;
}
/* Actually drop the carriage returns */
- return git_buf_text_crlf_to_lf(dest, source);
+ if (git_buf_text_crlf_to_lf(&to_buf, &from_buf) < 0)
+ return -1;
+
+ /* Overwrite "to" buffer in case data was resized */
+ git_buffer_from_buf(to, &to_buf);
+
+ return 0;
}
-static const char *line_ending(struct crlf_filter *filter)
+static const char *line_ending(struct crlf_attrs *ca)
{
- switch (filter->attrs.crlf_action) {
+ switch (ca->crlf_action) {
case GIT_CRLF_BINARY:
case GIT_CRLF_INPUT:
return "\n";
@@ -213,7 +217,7 @@ static const char *line_ending(struct crlf_filter *filter)
goto line_ending_error;
}
- switch (filter->attrs.eol) {
+ switch (ca->eol) {
case GIT_EOL_UNSET:
return GIT_EOL_NATIVE == GIT_EOL_CRLF
? "\r\n"
@@ -235,44 +239,58 @@ line_ending_error:
}
static int crlf_apply_to_workdir(
- git_filter *self, git_buf *tgt, const git_buf *src)
+ struct crlf_attrs *ca, git_buffer *to, const git_buffer *from)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
+ const git_buf from_buf = GIT_BUF_FROM_BUFFER(from);
+ git_buf to_buf = GIT_BUF_FROM_BUFFER(to);
const char *workdir_ending = NULL;
- assert(self && tgt && src);
-
/* Empty file? Nothing to do. */
- if (git_buf_len(src) == 0)
- return -1;
+ if (git_buf_len(&from_buf) == 0)
+ return 0;
/* Determine proper line ending */
- workdir_ending = line_ending(filter);
+ workdir_ending = line_ending(ca);
if (!workdir_ending)
return -1;
if (!strcmp("\n", workdir_ending)) {
- if (git_buf_find(src, '\r') < 0)
+ if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf)
+ return GIT_ENOTFOUND;
+
+ if (git_buf_find(&from_buf, '\r') < 0)
+ return GIT_ENOTFOUND;
+
+ if (git_buf_text_crlf_to_lf(&to_buf, &from_buf) < 0)
+ return -1;
+ } else {
+ /* only other supported option is lf->crlf conversion */
+ assert(!strcmp("\r\n", workdir_ending));
+
+ if (git_buf_text_lf_to_crlf(&to_buf, &from_buf) < 0)
return -1;
- return git_buf_text_crlf_to_lf(tgt, src);
}
- /* only other supported option is lf->crlf conversion */
- assert(!strcmp("\r\n", workdir_ending));
- return git_buf_text_lf_to_crlf(tgt, src);
+ /* Overwrite "to" buffer in case data was resized */
+ git_buffer_from_buf(to, &to_buf);
+
+ return 0;
}
-static int find_and_add_filter(
- git_vector *filters, git_repository *repo, const char *path,
- int (*apply)(struct git_filter *self, git_buf *tgt, const git_buf *src))
+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)
{
- struct crlf_attrs ca;
- struct crlf_filter *filter;
- size_t pathlen;
int error;
+ struct crlf_attrs ca;
+
+ GIT_UNUSED(self);
+ GIT_UNUSED(mode);
/* Load gitattributes for the path */
- if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
+ if ((error = crlf_load_attributes(&ca, src->repo, src->path)) < 0)
return error;
/*
@@ -282,41 +300,54 @@ static int find_and_add_filter(
ca.crlf_action = crlf_input_action(&ca);
if (ca.crlf_action == GIT_CRLF_BINARY)
- return 0;
+ return GIT_ENOTFOUND;
if (ca.crlf_action == GIT_CRLF_GUESS) {
- int auto_crlf;
-
- if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+ if ((error = git_repository__cvar(
+ &ca.auto_crlf, src->repo, GIT_CVAR_AUTO_CRLF)) < 0)
return error;
- if (auto_crlf == GIT_AUTO_CRLF_FALSE)
- return 0;
+ if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return GIT_ENOTFOUND;
}
- /* If we're good, we create a new filter object and push it
- * into the filters array */
- pathlen = strlen(path);
- filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
- GITERR_CHECK_ALLOC(filter);
+ *payload = git__malloc(sizeof(ca));
+ GITERR_CHECK_ALLOC(*payload);
+ memcpy(*payload, &ca, sizeof(ca));
- filter->f.apply = apply;
- filter->f.do_free = NULL;
- memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
- filter->repo = repo;
- memcpy(filter->path, path, pathlen + 1);
+ return 0;
+}
+
+static int crlf_apply(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_filter_mode_t mode,
+ git_buffer *to,
+ const git_buffer *from,
+ const git_filter_source *src)
+{
+ GIT_UNUSED(self);
- return git_vector_insert(filters, filter);
+ if (mode == GIT_FILTER_SMUDGE)
+ return crlf_apply_to_workdir(*payload, to, from);
+ else
+ return crlf_apply_to_odb(*payload, to, from, src);
}
-int git_filter_add__crlf_to_odb(
- git_vector *filters, git_repository *repo, const char *path)
+static void crlf_cleanup(
+ git_filter *self,
+ void *payload)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
+ GIT_UNUSED(self);
+ git__free(payload);
}
-int git_filter_add__crlf_to_workdir(
- git_vector *filters, git_repository *repo, const char *path)
+git_filter *git_crlf_filter_new(void)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
+ struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
+ f->f.version = GIT_FILTER_VERSION;
+ f->f.check = crlf_check;
+ f->f.apply = crlf_apply;
+ f->f.cleanup = crlf_cleanup;
+ return (git_filter *)f;
}