]>
Commit | Line | Data |
---|---|---|
44b1ff4c | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
44b1ff4c VM |
3 | * |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
8 | #include "common.h" | |
9 | #include "fileops.h" | |
10 | #include "hash.h" | |
11 | #include "filter.h" | |
c5266eba | 12 | #include "repository.h" |
0646634e | 13 | #include "global.h" |
2a7d224f | 14 | #include "git2/sys/filter.h" |
c5266eba | 15 | #include "git2/config.h" |
9587895f | 16 | #include "blob.h" |
974774c7 | 17 | #include "attr_file.h" |
2a7d224f | 18 | #include "array.h" |
44b1ff4c | 19 | |
570ba25c RB |
20 | struct git_filter_source { |
21 | git_repository *repo; | |
22 | const char *path; | |
23 | git_oid oid; /* zero if unknown (which is likely) */ | |
24 | uint16_t filemode; /* zero if unknown */ | |
2a7d224f | 25 | git_filter_mode_t mode; |
45c53eb6 | 26 | uint32_t options; |
570ba25c RB |
27 | }; |
28 | ||
85d54812 RB |
29 | typedef struct { |
30 | git_filter *filter; | |
31 | void *payload; | |
32 | } git_filter_entry; | |
33 | ||
34 | struct git_filter_list { | |
35 | git_array_t(git_filter_entry) filters; | |
85d54812 RB |
36 | git_filter_source source; |
37 | char path[GIT_FLEX_ARRAY]; | |
38 | }; | |
39 | ||
40 | typedef struct { | |
5623e627 | 41 | char *filter_name; |
85d54812 | 42 | git_filter *filter; |
974774c7 | 43 | int priority; |
29e92d38 | 44 | int initialized; |
974774c7 RB |
45 | size_t nattrs, nmatches; |
46 | char *attrdata; | |
47 | const char *attrs[GIT_FLEX_ARRAY]; | |
85d54812 RB |
48 | } git_filter_def; |
49 | ||
974774c7 RB |
50 | static int filter_def_priority_cmp(const void *a, const void *b) |
51 | { | |
52 | int pa = ((const git_filter_def *)a)->priority; | |
53 | int pb = ((const git_filter_def *)b)->priority; | |
54 | return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; | |
55 | } | |
56 | ||
0646634e RB |
57 | struct filter_registry { |
58 | git_vector filters; | |
974774c7 RB |
59 | }; |
60 | ||
0646634e RB |
61 | static struct filter_registry *git__filter_registry = NULL; |
62 | ||
63 | static void filter_registry_shutdown(void) | |
64 | { | |
65 | struct filter_registry *reg = NULL; | |
66 | size_t pos; | |
67 | git_filter_def *fdef; | |
68 | ||
69 | if ((reg = git__swap(git__filter_registry, NULL)) == NULL) | |
70 | return; | |
71 | ||
72 | git_vector_foreach(®->filters, pos, fdef) { | |
9cfce273 | 73 | if (fdef->filter && fdef->filter->shutdown) { |
0646634e RB |
74 | fdef->filter->shutdown(fdef->filter); |
75 | fdef->initialized = false; | |
76 | } | |
77 | ||
5623e627 | 78 | git__free(fdef->filter_name); |
0646634e RB |
79 | git__free(fdef->attrdata); |
80 | git__free(fdef); | |
81 | } | |
82 | ||
83 | git_vector_free(®->filters); | |
84 | git__free(reg); | |
85 | } | |
86 | ||
87 | static int filter_registry_initialize(void) | |
88 | { | |
89 | int error = 0; | |
90 | struct filter_registry *reg; | |
91 | ||
92 | if (git__filter_registry) | |
93 | return 0; | |
94 | ||
95 | reg = git__calloc(1, sizeof(struct filter_registry)); | |
96 | GITERR_CHECK_ALLOC(reg); | |
97 | ||
98 | if ((error = git_vector_init( | |
99 | ®->filters, 2, filter_def_priority_cmp)) < 0) | |
100 | goto cleanup; | |
101 | ||
102 | reg = git__compare_and_swap(&git__filter_registry, NULL, reg); | |
103 | if (reg != NULL) | |
104 | goto cleanup; | |
105 | ||
106 | git__on_shutdown(filter_registry_shutdown); | |
107 | ||
4b11f25a RB |
108 | /* try to register both default filters */ |
109 | { | |
110 | git_filter *crlf = git_crlf_filter_new(); | |
111 | git_filter *ident = git_ident_filter_new(); | |
112 | ||
113 | if (crlf && git_filter_register( | |
114 | GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0) | |
115 | crlf = NULL; | |
116 | if (ident && git_filter_register( | |
117 | GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) | |
118 | ident = NULL; | |
119 | ||
120 | if (!crlf || !ident) | |
121 | return -1; | |
122 | } | |
123 | ||
124 | return 0; | |
0646634e RB |
125 | |
126 | cleanup: | |
127 | git_vector_free(®->filters); | |
128 | git__free(reg); | |
129 | return error; | |
130 | } | |
131 | ||
974774c7 RB |
132 | static int filter_def_scan_attrs( |
133 | git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) | |
134 | { | |
135 | const char *start, *scan = attr_str; | |
136 | int has_eq; | |
137 | ||
138 | *nattr = *nmatch = 0; | |
139 | ||
140 | if (!scan) | |
141 | return 0; | |
142 | ||
143 | while (*scan) { | |
144 | while (git__isspace(*scan)) scan++; | |
145 | ||
146 | for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) { | |
147 | if (*scan == '=') | |
148 | has_eq = 1; | |
149 | } | |
150 | ||
151 | if (scan > start) { | |
152 | (*nattr)++; | |
4b11f25a | 153 | if (has_eq || *start == '-' || *start == '+' || *start == '!') |
974774c7 RB |
154 | (*nmatch)++; |
155 | ||
156 | if (has_eq) | |
157 | git_buf_putc(attrs, '='); | |
158 | git_buf_put(attrs, start, scan - start); | |
159 | git_buf_putc(attrs, '\0'); | |
160 | } | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static void filter_def_set_attrs(git_filter_def *fdef) | |
167 | { | |
168 | char *scan = fdef->attrdata; | |
169 | size_t i; | |
170 | ||
171 | for (i = 0; i < fdef->nattrs; ++i) { | |
172 | const char *name, *value; | |
173 | ||
174 | switch (*scan) { | |
175 | case '=': | |
176 | name = scan + 1; | |
177 | for (scan++; *scan != '='; scan++) /* find '=' */; | |
178 | *scan++ = '\0'; | |
179 | value = scan; | |
180 | break; | |
181 | case '-': | |
182 | name = scan + 1; value = git_attr__false; break; | |
183 | case '+': | |
184 | name = scan + 1; value = git_attr__true; break; | |
185 | case '!': | |
186 | name = scan + 1; value = git_attr__unset; break; | |
187 | default: | |
188 | name = scan; value = NULL; break; | |
189 | } | |
190 | ||
191 | fdef->attrs[i] = name; | |
192 | fdef->attrs[i + fdef->nattrs] = value; | |
193 | ||
194 | scan += strlen(scan) + 1; | |
195 | } | |
196 | } | |
197 | ||
0646634e RB |
198 | static int filter_def_name_key_check(const void *key, const void *fdef) |
199 | { | |
200 | const char *name = | |
201 | fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; | |
40cb40fa RB |
202 | return name ? git__strcmp(key, name) : -1; |
203 | } | |
204 | ||
205 | static int filter_def_filter_key_check(const void *key, const void *fdef) | |
206 | { | |
207 | const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL; | |
208 | return (key == filter) ? 0 : -1; | |
0646634e RB |
209 | } |
210 | ||
211 | static int filter_registry_find(size_t *pos, const char *name) | |
212 | { | |
213 | return git_vector_search2( | |
214 | pos, &git__filter_registry->filters, filter_def_name_key_check, name); | |
215 | } | |
216 | ||
217 | static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) | |
218 | { | |
219 | git_filter_def *fdef = NULL; | |
220 | ||
221 | if (!filter_registry_find(pos, name)) | |
222 | fdef = git_vector_get(&git__filter_registry->filters, *pos); | |
223 | ||
224 | return fdef; | |
225 | } | |
226 | ||
974774c7 RB |
227 | int git_filter_register( |
228 | const char *name, git_filter *filter, int priority) | |
229 | { | |
230 | git_filter_def *fdef; | |
392702ee | 231 | size_t nattr = 0, nmatch = 0, alloc_len; |
974774c7 RB |
232 | git_buf attrs = GIT_BUF_INIT; |
233 | ||
5623e627 AGO |
234 | assert(name && filter); |
235 | ||
0646634e RB |
236 | if (filter_registry_initialize() < 0) |
237 | return -1; | |
238 | ||
239 | if (!filter_registry_find(NULL, name)) { | |
974774c7 RB |
240 | giterr_set( |
241 | GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); | |
eefc32d5 | 242 | return GIT_EEXISTS; |
974774c7 RB |
243 | } |
244 | ||
245 | if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) | |
246 | return -1; | |
247 | ||
f1453c59 ET |
248 | GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2); |
249 | GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *)); | |
250 | GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def)); | |
392702ee ET |
251 | |
252 | fdef = git__calloc(1, alloc_len); | |
974774c7 RB |
253 | GITERR_CHECK_ALLOC(fdef); |
254 | ||
5623e627 AGO |
255 | fdef->filter_name = git__strdup(name); |
256 | GITERR_CHECK_ALLOC(fdef->filter_name); | |
257 | ||
974774c7 RB |
258 | fdef->filter = filter; |
259 | fdef->priority = priority; | |
260 | fdef->nattrs = nattr; | |
261 | fdef->nmatches = nmatch; | |
262 | fdef->attrdata = git_buf_detach(&attrs); | |
263 | ||
264 | filter_def_set_attrs(fdef); | |
265 | ||
0646634e | 266 | if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { |
5623e627 | 267 | git__free(fdef->filter_name); |
974774c7 RB |
268 | git__free(fdef->attrdata); |
269 | git__free(fdef); | |
270 | return -1; | |
271 | } | |
272 | ||
0646634e | 273 | git_vector_sort(&git__filter_registry->filters); |
29e92d38 RB |
274 | return 0; |
275 | } | |
276 | ||
974774c7 RB |
277 | int git_filter_unregister(const char *name) |
278 | { | |
279 | size_t pos; | |
280 | git_filter_def *fdef; | |
281 | ||
5623e627 AGO |
282 | assert(name); |
283 | ||
974774c7 | 284 | /* cannot unregister default filters */ |
eefc32d5 | 285 | if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { |
974774c7 RB |
286 | giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); |
287 | return -1; | |
288 | } | |
289 | ||
0646634e | 290 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { |
974774c7 RB |
291 | giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); |
292 | return GIT_ENOTFOUND; | |
293 | } | |
294 | ||
0646634e | 295 | (void)git_vector_remove(&git__filter_registry->filters, pos); |
974774c7 | 296 | |
29e92d38 | 297 | if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { |
974774c7 | 298 | fdef->filter->shutdown(fdef->filter); |
29e92d38 RB |
299 | fdef->initialized = false; |
300 | } | |
974774c7 | 301 | |
5623e627 | 302 | git__free(fdef->filter_name); |
974774c7 RB |
303 | git__free(fdef->attrdata); |
304 | git__free(fdef); | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
0646634e RB |
309 | static int filter_initialize(git_filter_def *fdef) |
310 | { | |
311 | int error = 0; | |
312 | ||
313 | if (!fdef->initialized && | |
314 | fdef->filter && | |
315 | fdef->filter->initialize && | |
316 | (error = fdef->filter->initialize(fdef->filter)) < 0) | |
317 | { | |
318 | /* auto-unregister if initialize fails */ | |
319 | git_filter_unregister(fdef->filter_name); | |
320 | return error; | |
321 | } | |
322 | ||
323 | fdef->initialized = true; | |
324 | return 0; | |
325 | } | |
326 | ||
974774c7 RB |
327 | git_filter *git_filter_lookup(const char *name) |
328 | { | |
329 | size_t pos; | |
0646634e RB |
330 | git_filter_def *fdef; |
331 | ||
332 | if (filter_registry_initialize() < 0) | |
333 | return NULL; | |
29e92d38 | 334 | |
0646634e | 335 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL) |
29e92d38 RB |
336 | return NULL; |
337 | ||
338 | if (!fdef->initialized && filter_initialize(fdef) < 0) | |
339 | return NULL; | |
340 | ||
341 | return fdef->filter; | |
974774c7 RB |
342 | } |
343 | ||
4b11f25a RB |
344 | void git_filter_free(git_filter *filter) |
345 | { | |
346 | git__free(filter); | |
347 | } | |
348 | ||
570ba25c RB |
349 | git_repository *git_filter_source_repo(const git_filter_source *src) |
350 | { | |
351 | return src->repo; | |
352 | } | |
353 | ||
354 | const char *git_filter_source_path(const git_filter_source *src) | |
355 | { | |
356 | return src->path; | |
357 | } | |
358 | ||
359 | uint16_t git_filter_source_filemode(const git_filter_source *src) | |
360 | { | |
361 | return src->filemode; | |
362 | } | |
363 | ||
364 | const git_oid *git_filter_source_id(const git_filter_source *src) | |
365 | { | |
366 | return git_oid_iszero(&src->oid) ? NULL : &src->oid; | |
367 | } | |
368 | ||
2a7d224f RB |
369 | git_filter_mode_t git_filter_source_mode(const git_filter_source *src) |
370 | { | |
371 | return src->mode; | |
372 | } | |
373 | ||
45c53eb6 | 374 | uint32_t git_filter_source_options(const git_filter_source *src) |
5269008c RB |
375 | { |
376 | return src->options; | |
377 | } | |
378 | ||
40cb40fa | 379 | static int filter_list_new( |
2a7d224f | 380 | git_filter_list **out, const git_filter_source *src) |
27950fa3 | 381 | { |
85d54812 | 382 | git_filter_list *fl = NULL; |
f1453c59 | 383 | size_t pathlen = src->path ? strlen(src->path) : 0, alloclen; |
85d54812 | 384 | |
f1453c59 ET |
385 | GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen); |
386 | GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); | |
392702ee | 387 | |
f1453c59 | 388 | fl = git__calloc(1, alloclen); |
85d54812 RB |
389 | GITERR_CHECK_ALLOC(fl); |
390 | ||
85d54812 RB |
391 | if (src->path) |
392 | memcpy(fl->path, src->path, pathlen); | |
393 | fl->source.repo = src->repo; | |
394 | fl->source.path = fl->path; | |
2a7d224f | 395 | fl->source.mode = src->mode; |
5269008c | 396 | fl->source.options = src->options; |
85d54812 RB |
397 | |
398 | *out = fl; | |
399 | return 0; | |
400 | } | |
401 | ||
974774c7 | 402 | static int filter_list_check_attributes( |
9f779aac ET |
403 | const char ***out, |
404 | git_repository *repo, | |
405 | git_attr_session *attr_session, | |
406 | git_filter_def *fdef, | |
407 | const git_filter_source *src) | |
974774c7 RB |
408 | { |
409 | int error; | |
410 | size_t i; | |
411 | const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); | |
412 | GITERR_CHECK_ALLOC(strs); | |
413 | ||
9f779aac ET |
414 | error = git_attr_get_many_with_session( |
415 | strs, repo, attr_session, 0, src->path, fdef->nattrs, fdef->attrs); | |
974774c7 RB |
416 | |
417 | /* if no values were found but no matches are needed, it's okay! */ | |
418 | if (error == GIT_ENOTFOUND && !fdef->nmatches) { | |
419 | giterr_clear(); | |
e399c7ee | 420 | git__free((void *)strs); |
974774c7 RB |
421 | return 0; |
422 | } | |
423 | ||
424 | for (i = 0; !error && i < fdef->nattrs; ++i) { | |
425 | const char *want = fdef->attrs[fdef->nattrs + i]; | |
426 | git_attr_t want_type, found_type; | |
427 | ||
428 | if (!want) | |
429 | continue; | |
430 | ||
431 | want_type = git_attr_value(want); | |
432 | found_type = git_attr_value(strs[i]); | |
433 | ||
434 | if (want_type != found_type || | |
435 | (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i]))) | |
436 | error = GIT_ENOTFOUND; | |
437 | } | |
438 | ||
439 | if (error) | |
e399c7ee | 440 | git__free((void *)strs); |
974774c7 RB |
441 | else |
442 | *out = strs; | |
443 | ||
444 | return error; | |
445 | } | |
446 | ||
40cb40fa | 447 | int git_filter_list_new( |
5269008c RB |
448 | git_filter_list **out, |
449 | git_repository *repo, | |
450 | git_filter_mode_t mode, | |
45c53eb6 | 451 | uint32_t options) |
40cb40fa RB |
452 | { |
453 | git_filter_source src = { 0 }; | |
454 | src.repo = repo; | |
455 | src.path = NULL; | |
456 | src.mode = mode; | |
5269008c | 457 | src.options = options; |
40cb40fa RB |
458 | return filter_list_new(out, &src); |
459 | } | |
460 | ||
9f779aac | 461 | int git_filter_list__load_with_attr_session( |
85d54812 RB |
462 | git_filter_list **filters, |
463 | git_repository *repo, | |
9f779aac | 464 | git_attr_session *attr_session, |
4b11f25a | 465 | git_blob *blob, /* can be NULL */ |
85d54812 | 466 | const char *path, |
5269008c | 467 | git_filter_mode_t mode, |
45c53eb6 | 468 | uint32_t options) |
85d54812 RB |
469 | { |
470 | int error = 0; | |
471 | git_filter_list *fl = NULL; | |
472 | git_filter_source src = { 0 }; | |
473 | git_filter_entry *fe; | |
974774c7 RB |
474 | size_t idx; |
475 | git_filter_def *fdef; | |
85d54812 | 476 | |
0646634e | 477 | if (filter_registry_initialize() < 0) |
85d54812 RB |
478 | return -1; |
479 | ||
480 | src.repo = repo; | |
481 | src.path = path; | |
2a7d224f | 482 | src.mode = mode; |
5269008c | 483 | src.options = options; |
4b11f25a RB |
484 | if (blob) |
485 | git_oid_cpy(&src.oid, git_blob_id(blob)); | |
85d54812 | 486 | |
0646634e | 487 | git_vector_foreach(&git__filter_registry->filters, idx, fdef) { |
974774c7 | 488 | const char **values = NULL; |
85d54812 | 489 | void *payload = NULL; |
85d54812 RB |
490 | |
491 | if (!fdef || !fdef->filter) | |
492 | continue; | |
27950fa3 | 493 | |
974774c7 | 494 | if (fdef->nattrs > 0) { |
9f779aac ET |
495 | error = filter_list_check_attributes( |
496 | &values, repo, attr_session, fdef, &src); | |
497 | ||
974774c7 RB |
498 | if (error == GIT_ENOTFOUND) { |
499 | error = 0; | |
500 | continue; | |
501 | } else if (error < 0) | |
502 | break; | |
503 | } | |
504 | ||
29e92d38 RB |
505 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) |
506 | break; | |
507 | ||
85d54812 | 508 | if (fdef->filter->check) |
974774c7 | 509 | error = fdef->filter->check( |
2a7d224f | 510 | fdef->filter, &payload, &src, values); |
974774c7 | 511 | |
e399c7ee | 512 | git__free((void *)values); |
85d54812 | 513 | |
eefc32d5 | 514 | if (error == GIT_PASSTHROUGH) |
85d54812 RB |
515 | error = 0; |
516 | else if (error < 0) | |
517 | break; | |
518 | else { | |
40cb40fa | 519 | if (!fl && (error = filter_list_new(&fl, &src)) < 0) |
85d54812 RB |
520 | return error; |
521 | ||
522 | fe = git_array_alloc(fl->filters); | |
523 | GITERR_CHECK_ALLOC(fe); | |
524 | fe->filter = fdef->filter; | |
525 | fe->payload = payload; | |
526 | } | |
527 | } | |
528 | ||
529 | if (error && fl != NULL) { | |
530 | git_array_clear(fl->filters); | |
531 | git__free(fl); | |
532 | fl = NULL; | |
533 | } | |
534 | ||
535 | *filters = fl; | |
536 | return error; | |
537 | } | |
538 | ||
9f779aac ET |
539 | int git_filter_list_load( |
540 | git_filter_list **filters, | |
541 | git_repository *repo, | |
542 | git_blob *blob, /* can be NULL */ | |
543 | const char *path, | |
544 | git_filter_mode_t mode, | |
545 | uint32_t options) | |
546 | { | |
547 | return git_filter_list__load_with_attr_session( | |
548 | filters, repo, NULL, blob, path, mode, options); | |
549 | } | |
550 | ||
85d54812 RB |
551 | void git_filter_list_free(git_filter_list *fl) |
552 | { | |
553 | uint32_t i; | |
554 | ||
555 | if (!fl) | |
556 | return; | |
557 | ||
558 | for (i = 0; i < git_array_size(fl->filters); ++i) { | |
559 | git_filter_entry *fe = git_array_get(fl->filters, i); | |
560 | if (fe->filter->cleanup) | |
561 | fe->filter->cleanup(fe->filter, fe->payload); | |
27950fa3 VM |
562 | } |
563 | ||
85d54812 RB |
564 | git_array_clear(fl->filters); |
565 | git__free(fl); | |
27950fa3 VM |
566 | } |
567 | ||
40cb40fa RB |
568 | int git_filter_list_push( |
569 | git_filter_list *fl, git_filter *filter, void *payload) | |
570 | { | |
571 | int error = 0; | |
572 | size_t pos; | |
573 | git_filter_def *fdef; | |
574 | git_filter_entry *fe; | |
575 | ||
576 | assert(fl && filter); | |
577 | ||
578 | if (git_vector_search2( | |
579 | &pos, &git__filter_registry->filters, | |
580 | filter_def_filter_key_check, filter) < 0) { | |
581 | giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); | |
582 | return -1; | |
583 | } | |
584 | ||
585 | fdef = git_vector_get(&git__filter_registry->filters, pos); | |
586 | ||
587 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) | |
588 | return error; | |
589 | ||
590 | fe = git_array_alloc(fl->filters); | |
591 | GITERR_CHECK_ALLOC(fe); | |
592 | fe->filter = filter; | |
593 | fe->payload = payload; | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
b47349b8 RB |
598 | size_t git_filter_list_length(const git_filter_list *fl) |
599 | { | |
600 | return fl ? git_array_size(fl->filters) : 0; | |
601 | } | |
602 | ||
2a7d224f | 603 | static int filter_list_out_buffer_from_raw( |
a9f51e43 | 604 | git_buf *out, const void *ptr, size_t size) |
2a7d224f | 605 | { |
a9f51e43 RB |
606 | if (git_buf_is_allocated(out)) |
607 | git_buf_free(out); | |
608 | ||
609 | if (!size) { | |
610 | git_buf_init(out, 0); | |
611 | } else { | |
612 | out->ptr = (char *)ptr; | |
613 | out->asize = 0; | |
614 | out->size = size; | |
615 | } | |
2a7d224f | 616 | |
2a7d224f RB |
617 | return 0; |
618 | } | |
619 | ||
620 | int git_filter_list_apply_to_data( | |
a9f51e43 | 621 | git_buf *tgt, git_filter_list *fl, git_buf *src) |
44b1ff4c | 622 | { |
85d54812 RB |
623 | int error = 0; |
624 | uint32_t i; | |
a9f51e43 | 625 | git_buf *dbuffer[2], local = GIT_BUF_INIT; |
2a7d224f | 626 | unsigned int si = 0; |
44b1ff4c | 627 | |
1e4976cb RB |
628 | git_buf_sanitize(tgt); |
629 | git_buf_sanitize(src); | |
630 | ||
2a7d224f RB |
631 | if (!fl) |
632 | return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size); | |
44b1ff4c | 633 | |
2a7d224f RB |
634 | dbuffer[0] = src; |
635 | dbuffer[1] = tgt; | |
44b1ff4c | 636 | |
2a7d224f | 637 | /* if `src` buffer is reallocable, then use it, otherwise copy it */ |
a9f51e43 RB |
638 | if (!git_buf_is_allocated(src)) { |
639 | if (git_buf_set(&local, src->ptr, src->size) < 0) | |
2a7d224f RB |
640 | return -1; |
641 | dbuffer[0] = &local; | |
642 | } | |
44b1ff4c | 643 | |
85d54812 | 644 | for (i = 0; i < git_array_size(fl->filters); ++i) { |
2a7d224f | 645 | unsigned int di = 1 - si; |
37f9e409 | 646 | uint32_t fidx = (fl->source.mode == GIT_FILTER_TO_WORKTREE) ? |
2a7d224f RB |
647 | i : git_array_size(fl->filters) - 1 - i; |
648 | git_filter_entry *fe = git_array_get(fl->filters, fidx); | |
44b1ff4c | 649 | |
2a7d224f | 650 | dbuffer[di]->size = 0; |
974774c7 | 651 | |
ce49c7a8 | 652 | /* Apply the filter from dbuffer[src] to the other buffer; |
44b1ff4c VM |
653 | * if the filtering is canceled by the user mid-filter, |
654 | * we skip to the next filter without changing the source | |
655 | * of the double buffering (so that the text goes through | |
656 | * cleanly). | |
657 | */ | |
85d54812 | 658 | |
2a7d224f RB |
659 | error = fe->filter->apply( |
660 | fe->filter, &fe->payload, dbuffer[di], dbuffer[si], &fl->source); | |
85d54812 | 661 | |
eefc32d5 RB |
662 | if (error == GIT_PASSTHROUGH) { |
663 | /* PASSTHROUGH means filter decided not to process the buffer */ | |
2a7d224f | 664 | error = 0; |
eefc32d5 | 665 | } else if (!error) { |
1e4976cb | 666 | git_buf_sanitize(dbuffer[di]); /* force NUL termination */ |
2a7d224f | 667 | si = di; /* swap buffers */ |
eefc32d5 | 668 | } else { |
2a7d224f | 669 | tgt->size = 0; |
424222f4 | 670 | goto cleanup; |
85d54812 | 671 | } |
44b1ff4c VM |
672 | } |
673 | ||
674 | /* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */ | |
a9f51e43 RB |
675 | if (si != 1) |
676 | git_buf_swap(dbuffer[0], dbuffer[1]); | |
2a7d224f | 677 | |
424222f4 | 678 | cleanup: |
a9f51e43 | 679 | git_buf_free(&local); /* don't leak if we allocated locally */ |
44b1ff4c | 680 | |
424222f4 | 681 | return error; |
44b1ff4c | 682 | } |
2a7d224f RB |
683 | |
684 | int git_filter_list_apply_to_file( | |
a9f51e43 | 685 | git_buf *out, |
2a7d224f RB |
686 | git_filter_list *filters, |
687 | git_repository *repo, | |
688 | const char *path) | |
689 | { | |
690 | int error; | |
691 | const char *base = repo ? git_repository_workdir(repo) : NULL; | |
692 | git_buf abspath = GIT_BUF_INIT, raw = GIT_BUF_INIT; | |
693 | ||
694 | if (!(error = git_path_join_unrooted(&abspath, path, base, NULL)) && | |
695 | !(error = git_futils_readbuffer(&raw, abspath.ptr))) | |
696 | { | |
a9f51e43 | 697 | error = git_filter_list_apply_to_data(out, filters, &raw); |
2a7d224f | 698 | |
a9f51e43 | 699 | git_buf_free(&raw); |
2a7d224f RB |
700 | } |
701 | ||
702 | git_buf_free(&abspath); | |
703 | return error; | |
704 | } | |
705 | ||
706 | int git_filter_list_apply_to_blob( | |
a9f51e43 | 707 | git_buf *out, |
2a7d224f RB |
708 | git_filter_list *filters, |
709 | git_blob *blob) | |
710 | { | |
71379313 RB |
711 | git_buf in = GIT_BUF_INIT; |
712 | git_off_t rawsize = git_blob_rawsize(blob); | |
713 | ||
714 | if (!git__is_sizet(rawsize)) { | |
715 | giterr_set(GITERR_OS, "Blob is too large to filter"); | |
716 | return -1; | |
717 | } | |
718 | ||
719 | in.ptr = (char *)git_blob_rawcontent(blob); | |
720 | in.asize = 0; | |
721 | in.size = (size_t)rawsize; | |
2a7d224f | 722 | |
4b11f25a RB |
723 | if (filters) |
724 | git_oid_cpy(&filters->source.oid, git_blob_id(blob)); | |
725 | ||
2a7d224f RB |
726 | return git_filter_list_apply_to_data(out, filters, &in); |
727 | } |