1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
/*
* 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 "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
#include "repository.h"
#include "git2/config.h"
#include "blob.h"
struct git_filter_source {
git_repository *repo;
const char *path;
git_oid oid; /* zero if unknown (which is likely) */
uint16_t filemode; /* zero if unknown */
};
typedef struct {
git_filter *filter;
void *payload;
} git_filter_entry;
struct git_filter_list {
git_array_t(git_filter_entry) filters;
git_filter_mode_t mode;
git_filter_source source;
char path[GIT_FLEX_ARRAY];
};
typedef struct {
const char *filter_name;
git_filter *filter;
} git_filter_def;
static git_array_t(git_filter_def) filter_registry = GIT_ARRAY_INIT;
git_repository *git_filter_source_repo(const git_filter_source *src)
{
return src->repo;
}
const char *git_filter_source_path(const git_filter_source *src)
{
return src->path;
}
uint16_t git_filter_source_filemode(const git_filter_source *src)
{
return src->filemode;
}
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)
{
git_filter_list *fl = NULL;
size_t pathlen = src->path ? strlen(src->path) : 0;
fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1);
GITERR_CHECK_ALLOC(fl);
fl->mode = mode;
if (src->path)
memcpy(fl->path, src->path, pathlen);
fl->source.repo = src->repo;
fl->source.path = fl->path;
*out = fl;
return 0;
}
int git_filter_list_load(
git_filter_list **filters,
git_repository *repo,
const char *path,
git_filter_mode_t mode)
{
int error = 0;
git_filter_list *fl = NULL;
git_filter_source src = { 0 };
git_filter_entry *fe;
uint32_t f;
if (filter_load_defaults() < 0)
return -1;
src.repo = repo;
src.path = path;
for (f = 0; f < git_array_size(filter_registry); ++f) {
void *payload = NULL;
git_filter_def *fdef = git_array_get(filter_registry, f);
if (!fdef || !fdef->filter)
continue;
if (fdef->filter->check)
error = fdef->filter->check(fdef->filter, &payload, mode, &src);
if (error == GIT_ENOTFOUND)
error = 0;
else if (error < 0)
break;
else {
if (!fl && (error = git_filter_list_new(&fl, mode, &src)) < 0)
return error;
fe = git_array_alloc(fl->filters);
GITERR_CHECK_ALLOC(fe);
fe->filter = fdef->filter;
fe->payload = payload;
}
}
if (error && fl != NULL) {
git_array_clear(fl->filters);
git__free(fl);
fl = NULL;
}
*filters = fl;
return error;
}
void git_filter_list_free(git_filter_list *fl)
{
uint32_t i;
if (!fl)
return;
for (i = 0; i < git_array_size(fl->filters); ++i) {
git_filter_entry *fe = git_array_get(fl->filters, i);
if (fe->filter->cleanup)
fe->filter->cleanup(fe->filter, fe->payload);
}
git_array_clear(fl->filters);
git__free(fl);
}
int git_filter_list_apply(
git_buf *dest,
git_buf *source,
git_filter_list *fl)
{
int error = 0;
uint32_t i;
unsigned int src;
git_buf *dbuffer[2];
if (!fl) {
git_buf_swap(dest, source);
return 0;
}
dbuffer[0] = source;
dbuffer[1] = dest;
src = 0;
/* Pre-grow the destination buffer to more or less the size
* we expect it to have */
if (git_buf_grow(dest, git_buf_len(source)) < 0)
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]);
/* 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
* of the double buffering (so that the text goes through
* cleanly).
*/
{
git_buffer srcb = GIT_BUFFER_FROM_BUF(dbuffer[src]);
git_buffer dstb = GIT_BUFFER_FROM_BUF(dbuffer[dst]);
error = fe->filter->apply(
fe->filter, &fe->payload, fl->mode, &dstb, &srcb, &fl->source);
if (error == GIT_ENOTFOUND)
error = 0;
else if (error < 0) {
git_buf_clear(dest);
return error;
}
else {
git_buf_from_buffer(dbuffer[src], &srcb);
git_buf_from_buffer(dbuffer[dst], &dstb);
src = dst;
}
}
if (git_buf_oom(dbuffer[dst]))
return -1;
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
if (src != 1)
git_buf_swap(dest, source);
return 0;
}