]>
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; |
795eaccd | 26 | uint32_t flags; |
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 | 36 | git_filter_source source; |
646364e7 | 37 | git_buf *temp_buf; |
85d54812 RB |
38 | char path[GIT_FLEX_ARRAY]; |
39 | }; | |
40 | ||
41 | typedef struct { | |
5623e627 | 42 | char *filter_name; |
85d54812 | 43 | git_filter *filter; |
974774c7 | 44 | int priority; |
29e92d38 | 45 | int initialized; |
974774c7 RB |
46 | size_t nattrs, nmatches; |
47 | char *attrdata; | |
48 | const char *attrs[GIT_FLEX_ARRAY]; | |
85d54812 RB |
49 | } git_filter_def; |
50 | ||
974774c7 RB |
51 | static int filter_def_priority_cmp(const void *a, const void *b) |
52 | { | |
53 | int pa = ((const git_filter_def *)a)->priority; | |
54 | int pb = ((const git_filter_def *)b)->priority; | |
55 | return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; | |
56 | } | |
57 | ||
0646634e RB |
58 | struct filter_registry { |
59 | git_vector filters; | |
974774c7 RB |
60 | }; |
61 | ||
0646634e RB |
62 | static struct filter_registry *git__filter_registry = NULL; |
63 | ||
64 | static void filter_registry_shutdown(void) | |
65 | { | |
66 | struct filter_registry *reg = NULL; | |
67 | size_t pos; | |
68 | git_filter_def *fdef; | |
69 | ||
70 | if ((reg = git__swap(git__filter_registry, NULL)) == NULL) | |
71 | return; | |
72 | ||
73 | git_vector_foreach(®->filters, pos, fdef) { | |
9cfce273 | 74 | if (fdef->filter && fdef->filter->shutdown) { |
0646634e RB |
75 | fdef->filter->shutdown(fdef->filter); |
76 | fdef->initialized = false; | |
77 | } | |
78 | ||
5623e627 | 79 | git__free(fdef->filter_name); |
0646634e RB |
80 | git__free(fdef->attrdata); |
81 | git__free(fdef); | |
82 | } | |
83 | ||
84 | git_vector_free(®->filters); | |
85 | git__free(reg); | |
86 | } | |
87 | ||
88 | static int filter_registry_initialize(void) | |
89 | { | |
90 | int error = 0; | |
91 | struct filter_registry *reg; | |
92 | ||
93 | if (git__filter_registry) | |
94 | return 0; | |
95 | ||
96 | reg = git__calloc(1, sizeof(struct filter_registry)); | |
97 | GITERR_CHECK_ALLOC(reg); | |
98 | ||
99 | if ((error = git_vector_init( | |
100 | ®->filters, 2, filter_def_priority_cmp)) < 0) | |
101 | goto cleanup; | |
102 | ||
103 | reg = git__compare_and_swap(&git__filter_registry, NULL, reg); | |
104 | if (reg != NULL) | |
105 | goto cleanup; | |
106 | ||
107 | git__on_shutdown(filter_registry_shutdown); | |
108 | ||
4b11f25a RB |
109 | /* try to register both default filters */ |
110 | { | |
111 | git_filter *crlf = git_crlf_filter_new(); | |
112 | git_filter *ident = git_ident_filter_new(); | |
113 | ||
114 | if (crlf && git_filter_register( | |
115 | GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0) | |
116 | crlf = NULL; | |
117 | if (ident && git_filter_register( | |
118 | GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) | |
119 | ident = NULL; | |
120 | ||
121 | if (!crlf || !ident) | |
122 | return -1; | |
123 | } | |
124 | ||
125 | return 0; | |
0646634e RB |
126 | |
127 | cleanup: | |
128 | git_vector_free(®->filters); | |
129 | git__free(reg); | |
130 | return error; | |
131 | } | |
132 | ||
974774c7 RB |
133 | static int filter_def_scan_attrs( |
134 | git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) | |
135 | { | |
136 | const char *start, *scan = attr_str; | |
137 | int has_eq; | |
138 | ||
139 | *nattr = *nmatch = 0; | |
140 | ||
141 | if (!scan) | |
142 | return 0; | |
143 | ||
144 | while (*scan) { | |
145 | while (git__isspace(*scan)) scan++; | |
146 | ||
147 | for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) { | |
148 | if (*scan == '=') | |
149 | has_eq = 1; | |
150 | } | |
151 | ||
152 | if (scan > start) { | |
153 | (*nattr)++; | |
4b11f25a | 154 | if (has_eq || *start == '-' || *start == '+' || *start == '!') |
974774c7 RB |
155 | (*nmatch)++; |
156 | ||
157 | if (has_eq) | |
158 | git_buf_putc(attrs, '='); | |
159 | git_buf_put(attrs, start, scan - start); | |
160 | git_buf_putc(attrs, '\0'); | |
161 | } | |
162 | } | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | static void filter_def_set_attrs(git_filter_def *fdef) | |
168 | { | |
169 | char *scan = fdef->attrdata; | |
170 | size_t i; | |
171 | ||
172 | for (i = 0; i < fdef->nattrs; ++i) { | |
173 | const char *name, *value; | |
174 | ||
175 | switch (*scan) { | |
176 | case '=': | |
177 | name = scan + 1; | |
178 | for (scan++; *scan != '='; scan++) /* find '=' */; | |
179 | *scan++ = '\0'; | |
180 | value = scan; | |
181 | break; | |
182 | case '-': | |
183 | name = scan + 1; value = git_attr__false; break; | |
184 | case '+': | |
185 | name = scan + 1; value = git_attr__true; break; | |
186 | case '!': | |
187 | name = scan + 1; value = git_attr__unset; break; | |
188 | default: | |
189 | name = scan; value = NULL; break; | |
190 | } | |
191 | ||
192 | fdef->attrs[i] = name; | |
193 | fdef->attrs[i + fdef->nattrs] = value; | |
194 | ||
195 | scan += strlen(scan) + 1; | |
196 | } | |
197 | } | |
198 | ||
0646634e RB |
199 | static int filter_def_name_key_check(const void *key, const void *fdef) |
200 | { | |
201 | const char *name = | |
202 | fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; | |
40cb40fa RB |
203 | return name ? git__strcmp(key, name) : -1; |
204 | } | |
205 | ||
206 | static int filter_def_filter_key_check(const void *key, const void *fdef) | |
207 | { | |
208 | const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL; | |
209 | return (key == filter) ? 0 : -1; | |
0646634e RB |
210 | } |
211 | ||
212 | static int filter_registry_find(size_t *pos, const char *name) | |
213 | { | |
214 | return git_vector_search2( | |
215 | pos, &git__filter_registry->filters, filter_def_name_key_check, name); | |
216 | } | |
217 | ||
218 | static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) | |
219 | { | |
220 | git_filter_def *fdef = NULL; | |
221 | ||
222 | if (!filter_registry_find(pos, name)) | |
223 | fdef = git_vector_get(&git__filter_registry->filters, *pos); | |
224 | ||
225 | return fdef; | |
226 | } | |
227 | ||
974774c7 RB |
228 | int git_filter_register( |
229 | const char *name, git_filter *filter, int priority) | |
230 | { | |
231 | git_filter_def *fdef; | |
392702ee | 232 | size_t nattr = 0, nmatch = 0, alloc_len; |
974774c7 RB |
233 | git_buf attrs = GIT_BUF_INIT; |
234 | ||
5623e627 AGO |
235 | assert(name && filter); |
236 | ||
0646634e RB |
237 | if (filter_registry_initialize() < 0) |
238 | return -1; | |
239 | ||
240 | if (!filter_registry_find(NULL, name)) { | |
974774c7 RB |
241 | giterr_set( |
242 | GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); | |
eefc32d5 | 243 | return GIT_EEXISTS; |
974774c7 RB |
244 | } |
245 | ||
246 | if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) | |
247 | return -1; | |
248 | ||
f1453c59 ET |
249 | GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2); |
250 | GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *)); | |
251 | GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def)); | |
392702ee ET |
252 | |
253 | fdef = git__calloc(1, alloc_len); | |
974774c7 RB |
254 | GITERR_CHECK_ALLOC(fdef); |
255 | ||
5623e627 AGO |
256 | fdef->filter_name = git__strdup(name); |
257 | GITERR_CHECK_ALLOC(fdef->filter_name); | |
258 | ||
974774c7 RB |
259 | fdef->filter = filter; |
260 | fdef->priority = priority; | |
261 | fdef->nattrs = nattr; | |
262 | fdef->nmatches = nmatch; | |
263 | fdef->attrdata = git_buf_detach(&attrs); | |
264 | ||
265 | filter_def_set_attrs(fdef); | |
266 | ||
0646634e | 267 | if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { |
5623e627 | 268 | git__free(fdef->filter_name); |
974774c7 RB |
269 | git__free(fdef->attrdata); |
270 | git__free(fdef); | |
271 | return -1; | |
272 | } | |
273 | ||
0646634e | 274 | git_vector_sort(&git__filter_registry->filters); |
29e92d38 RB |
275 | return 0; |
276 | } | |
277 | ||
974774c7 RB |
278 | int git_filter_unregister(const char *name) |
279 | { | |
280 | size_t pos; | |
281 | git_filter_def *fdef; | |
282 | ||
5623e627 AGO |
283 | assert(name); |
284 | ||
974774c7 | 285 | /* cannot unregister default filters */ |
eefc32d5 | 286 | if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { |
974774c7 RB |
287 | giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); |
288 | return -1; | |
289 | } | |
290 | ||
0646634e | 291 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { |
974774c7 RB |
292 | giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); |
293 | return GIT_ENOTFOUND; | |
294 | } | |
295 | ||
0646634e | 296 | (void)git_vector_remove(&git__filter_registry->filters, pos); |
974774c7 | 297 | |
29e92d38 | 298 | if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { |
974774c7 | 299 | fdef->filter->shutdown(fdef->filter); |
29e92d38 RB |
300 | fdef->initialized = false; |
301 | } | |
974774c7 | 302 | |
5623e627 | 303 | git__free(fdef->filter_name); |
974774c7 RB |
304 | git__free(fdef->attrdata); |
305 | git__free(fdef); | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
0646634e RB |
310 | static int filter_initialize(git_filter_def *fdef) |
311 | { | |
312 | int error = 0; | |
313 | ||
314 | if (!fdef->initialized && | |
315 | fdef->filter && | |
316 | fdef->filter->initialize && | |
317 | (error = fdef->filter->initialize(fdef->filter)) < 0) | |
318 | { | |
319 | /* auto-unregister if initialize fails */ | |
320 | git_filter_unregister(fdef->filter_name); | |
321 | return error; | |
322 | } | |
323 | ||
324 | fdef->initialized = true; | |
325 | return 0; | |
326 | } | |
327 | ||
974774c7 RB |
328 | git_filter *git_filter_lookup(const char *name) |
329 | { | |
330 | size_t pos; | |
0646634e RB |
331 | git_filter_def *fdef; |
332 | ||
333 | if (filter_registry_initialize() < 0) | |
334 | return NULL; | |
29e92d38 | 335 | |
0646634e | 336 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL) |
29e92d38 RB |
337 | return NULL; |
338 | ||
339 | if (!fdef->initialized && filter_initialize(fdef) < 0) | |
340 | return NULL; | |
341 | ||
342 | return fdef->filter; | |
974774c7 RB |
343 | } |
344 | ||
4b11f25a RB |
345 | void git_filter_free(git_filter *filter) |
346 | { | |
347 | git__free(filter); | |
348 | } | |
349 | ||
570ba25c RB |
350 | git_repository *git_filter_source_repo(const git_filter_source *src) |
351 | { | |
352 | return src->repo; | |
353 | } | |
354 | ||
355 | const char *git_filter_source_path(const git_filter_source *src) | |
356 | { | |
357 | return src->path; | |
358 | } | |
359 | ||
360 | uint16_t git_filter_source_filemode(const git_filter_source *src) | |
361 | { | |
362 | return src->filemode; | |
363 | } | |
364 | ||
365 | const git_oid *git_filter_source_id(const git_filter_source *src) | |
366 | { | |
367 | return git_oid_iszero(&src->oid) ? NULL : &src->oid; | |
368 | } | |
369 | ||
2a7d224f RB |
370 | git_filter_mode_t git_filter_source_mode(const git_filter_source *src) |
371 | { | |
372 | return src->mode; | |
373 | } | |
374 | ||
795eaccd | 375 | uint32_t git_filter_source_flags(const git_filter_source *src) |
5269008c | 376 | { |
795eaccd | 377 | return src->flags; |
5269008c RB |
378 | } |
379 | ||
40cb40fa | 380 | static int filter_list_new( |
2a7d224f | 381 | git_filter_list **out, const git_filter_source *src) |
27950fa3 | 382 | { |
85d54812 | 383 | git_filter_list *fl = NULL; |
f1453c59 | 384 | size_t pathlen = src->path ? strlen(src->path) : 0, alloclen; |
85d54812 | 385 | |
f1453c59 ET |
386 | GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen); |
387 | GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); | |
392702ee | 388 | |
f1453c59 | 389 | fl = git__calloc(1, alloclen); |
85d54812 RB |
390 | GITERR_CHECK_ALLOC(fl); |
391 | ||
85d54812 RB |
392 | if (src->path) |
393 | memcpy(fl->path, src->path, pathlen); | |
394 | fl->source.repo = src->repo; | |
395 | fl->source.path = fl->path; | |
2a7d224f | 396 | fl->source.mode = src->mode; |
795eaccd | 397 | fl->source.flags = src->flags; |
85d54812 RB |
398 | |
399 | *out = fl; | |
400 | return 0; | |
401 | } | |
402 | ||
974774c7 | 403 | static int filter_list_check_attributes( |
9f779aac ET |
404 | const char ***out, |
405 | git_repository *repo, | |
406 | git_attr_session *attr_session, | |
407 | git_filter_def *fdef, | |
408 | const git_filter_source *src) | |
974774c7 RB |
409 | { |
410 | int error; | |
411 | size_t i; | |
412 | const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); | |
413 | GITERR_CHECK_ALLOC(strs); | |
414 | ||
9f779aac ET |
415 | error = git_attr_get_many_with_session( |
416 | strs, repo, attr_session, 0, src->path, fdef->nattrs, fdef->attrs); | |
974774c7 RB |
417 | |
418 | /* if no values were found but no matches are needed, it's okay! */ | |
419 | if (error == GIT_ENOTFOUND && !fdef->nmatches) { | |
420 | giterr_clear(); | |
e399c7ee | 421 | git__free((void *)strs); |
974774c7 RB |
422 | return 0; |
423 | } | |
424 | ||
425 | for (i = 0; !error && i < fdef->nattrs; ++i) { | |
426 | const char *want = fdef->attrs[fdef->nattrs + i]; | |
427 | git_attr_t want_type, found_type; | |
428 | ||
429 | if (!want) | |
430 | continue; | |
431 | ||
432 | want_type = git_attr_value(want); | |
433 | found_type = git_attr_value(strs[i]); | |
434 | ||
435 | if (want_type != found_type || | |
436 | (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i]))) | |
437 | error = GIT_ENOTFOUND; | |
438 | } | |
439 | ||
440 | if (error) | |
e399c7ee | 441 | git__free((void *)strs); |
974774c7 RB |
442 | else |
443 | *out = strs; | |
444 | ||
445 | return error; | |
446 | } | |
447 | ||
40cb40fa | 448 | int git_filter_list_new( |
5269008c RB |
449 | git_filter_list **out, |
450 | git_repository *repo, | |
451 | git_filter_mode_t mode, | |
795eaccd | 452 | uint32_t flags) |
40cb40fa RB |
453 | { |
454 | git_filter_source src = { 0 }; | |
455 | src.repo = repo; | |
456 | src.path = NULL; | |
457 | src.mode = mode; | |
795eaccd | 458 | src.flags = flags; |
40cb40fa RB |
459 | return filter_list_new(out, &src); |
460 | } | |
461 | ||
d05218b0 | 462 | int git_filter_list__load_ext( |
85d54812 RB |
463 | git_filter_list **filters, |
464 | git_repository *repo, | |
4b11f25a | 465 | git_blob *blob, /* can be NULL */ |
85d54812 | 466 | const char *path, |
5269008c | 467 | git_filter_mode_t mode, |
d05218b0 | 468 | git_filter_options *filter_opts) |
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; |
d05218b0 ET |
483 | src.flags = filter_opts->flags; |
484 | ||
4b11f25a RB |
485 | if (blob) |
486 | git_oid_cpy(&src.oid, git_blob_id(blob)); | |
85d54812 | 487 | |
0646634e | 488 | git_vector_foreach(&git__filter_registry->filters, idx, fdef) { |
974774c7 | 489 | const char **values = NULL; |
85d54812 | 490 | void *payload = NULL; |
85d54812 RB |
491 | |
492 | if (!fdef || !fdef->filter) | |
493 | continue; | |
27950fa3 | 494 | |
974774c7 | 495 | if (fdef->nattrs > 0) { |
9f779aac | 496 | error = filter_list_check_attributes( |
d05218b0 | 497 | &values, repo, filter_opts->attr_session, fdef, &src); |
9f779aac | 498 | |
974774c7 RB |
499 | if (error == GIT_ENOTFOUND) { |
500 | error = 0; | |
501 | continue; | |
502 | } else if (error < 0) | |
503 | break; | |
504 | } | |
505 | ||
29e92d38 RB |
506 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) |
507 | break; | |
508 | ||
85d54812 | 509 | if (fdef->filter->check) |
974774c7 | 510 | error = fdef->filter->check( |
2a7d224f | 511 | fdef->filter, &payload, &src, values); |
974774c7 | 512 | |
e399c7ee | 513 | git__free((void *)values); |
85d54812 | 514 | |
eefc32d5 | 515 | if (error == GIT_PASSTHROUGH) |
85d54812 RB |
516 | error = 0; |
517 | else if (error < 0) | |
518 | break; | |
519 | else { | |
9c9aa1ba ET |
520 | if (!fl) { |
521 | if ((error = filter_list_new(&fl, &src)) < 0) | |
522 | return error; | |
523 | ||
524 | fl->temp_buf = filter_opts->temp_buf; | |
525 | } | |
85d54812 RB |
526 | |
527 | fe = git_array_alloc(fl->filters); | |
528 | GITERR_CHECK_ALLOC(fe); | |
529 | fe->filter = fdef->filter; | |
530 | fe->payload = payload; | |
531 | } | |
532 | } | |
533 | ||
534 | if (error && fl != NULL) { | |
535 | git_array_clear(fl->filters); | |
536 | git__free(fl); | |
537 | fl = NULL; | |
538 | } | |
539 | ||
540 | *filters = fl; | |
541 | return error; | |
542 | } | |
543 | ||
9f779aac ET |
544 | int git_filter_list_load( |
545 | git_filter_list **filters, | |
546 | git_repository *repo, | |
547 | git_blob *blob, /* can be NULL */ | |
548 | const char *path, | |
549 | git_filter_mode_t mode, | |
795eaccd | 550 | uint32_t flags) |
9f779aac | 551 | { |
d05218b0 ET |
552 | git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; |
553 | ||
554 | filter_opts.flags = flags; | |
555 | ||
556 | return git_filter_list__load_ext( | |
557 | filters, repo, blob, path, mode, &filter_opts); | |
9f779aac ET |
558 | } |
559 | ||
85d54812 RB |
560 | void git_filter_list_free(git_filter_list *fl) |
561 | { | |
562 | uint32_t i; | |
563 | ||
564 | if (!fl) | |
565 | return; | |
566 | ||
567 | for (i = 0; i < git_array_size(fl->filters); ++i) { | |
568 | git_filter_entry *fe = git_array_get(fl->filters, i); | |
569 | if (fe->filter->cleanup) | |
570 | fe->filter->cleanup(fe->filter, fe->payload); | |
27950fa3 VM |
571 | } |
572 | ||
85d54812 RB |
573 | git_array_clear(fl->filters); |
574 | git__free(fl); | |
27950fa3 VM |
575 | } |
576 | ||
40cb40fa RB |
577 | int git_filter_list_push( |
578 | git_filter_list *fl, git_filter *filter, void *payload) | |
579 | { | |
580 | int error = 0; | |
581 | size_t pos; | |
582 | git_filter_def *fdef; | |
583 | git_filter_entry *fe; | |
584 | ||
585 | assert(fl && filter); | |
586 | ||
587 | if (git_vector_search2( | |
588 | &pos, &git__filter_registry->filters, | |
589 | filter_def_filter_key_check, filter) < 0) { | |
590 | giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); | |
591 | return -1; | |
592 | } | |
593 | ||
594 | fdef = git_vector_get(&git__filter_registry->filters, pos); | |
595 | ||
596 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) | |
597 | return error; | |
598 | ||
599 | fe = git_array_alloc(fl->filters); | |
600 | GITERR_CHECK_ALLOC(fe); | |
601 | fe->filter = filter; | |
602 | fe->payload = payload; | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
b47349b8 RB |
607 | size_t git_filter_list_length(const git_filter_list *fl) |
608 | { | |
609 | return fl ? git_array_size(fl->filters) : 0; | |
610 | } | |
611 | ||
5555696f | 612 | struct buf_stream { |
f7c0125f | 613 | git_writestream parent; |
5555696f ET |
614 | git_buf *target; |
615 | bool complete; | |
616 | }; | |
617 | ||
618 | static int buf_stream_write( | |
b75f15aa | 619 | git_writestream *s, const char *buffer, size_t len) |
44b1ff4c | 620 | { |
5555696f ET |
621 | struct buf_stream *buf_stream = (struct buf_stream *)s; |
622 | assert(buf_stream); | |
44b1ff4c | 623 | |
5555696f | 624 | assert(buf_stream->complete == 0); |
1e4976cb | 625 | |
5555696f ET |
626 | return git_buf_put(buf_stream->target, buffer, len); |
627 | } | |
44b1ff4c | 628 | |
b75f15aa | 629 | static int buf_stream_close(git_writestream *s) |
5555696f ET |
630 | { |
631 | struct buf_stream *buf_stream = (struct buf_stream *)s; | |
632 | assert(buf_stream); | |
44b1ff4c | 633 | |
5555696f ET |
634 | assert(buf_stream->complete == 0); |
635 | buf_stream->complete = 1; | |
44b1ff4c | 636 | |
5555696f ET |
637 | return 0; |
638 | } | |
85d54812 | 639 | |
b75f15aa | 640 | static void buf_stream_free(git_writestream *s) |
5555696f ET |
641 | { |
642 | GIT_UNUSED(s); | |
643 | } | |
85d54812 | 644 | |
5555696f ET |
645 | static void buf_stream_init(struct buf_stream *writer, git_buf *target) |
646 | { | |
647 | memset(writer, 0, sizeof(struct buf_stream)); | |
44b1ff4c | 648 | |
f7c0125f ET |
649 | writer->parent.write = buf_stream_write; |
650 | writer->parent.close = buf_stream_close; | |
651 | writer->parent.free = buf_stream_free; | |
5555696f | 652 | writer->target = target; |
2a7d224f | 653 | |
5555696f ET |
654 | git_buf_clear(target); |
655 | } | |
44b1ff4c | 656 | |
5555696f ET |
657 | int git_filter_list_apply_to_data( |
658 | git_buf *tgt, git_filter_list *filters, git_buf *src) | |
659 | { | |
660 | struct buf_stream writer; | |
661 | int error; | |
662 | ||
663 | git_buf_sanitize(tgt); | |
664 | git_buf_sanitize(src); | |
665 | ||
d4cf1675 ET |
666 | if (!filters) { |
667 | git_buf_attach_notowned(tgt, src->ptr, src->size); | |
668 | return 0; | |
669 | } | |
5555696f ET |
670 | |
671 | buf_stream_init(&writer, tgt); | |
672 | ||
673 | if ((error = git_filter_list_stream_data(filters, src, | |
b75f15aa | 674 | (git_writestream *)&writer)) < 0) |
5555696f ET |
675 | return error; |
676 | ||
677 | assert(writer.complete); | |
424222f4 | 678 | return error; |
44b1ff4c | 679 | } |
2a7d224f RB |
680 | |
681 | int git_filter_list_apply_to_file( | |
a9f51e43 | 682 | git_buf *out, |
2a7d224f RB |
683 | git_filter_list *filters, |
684 | git_repository *repo, | |
685 | const char *path) | |
686 | { | |
5555696f | 687 | struct buf_stream writer; |
2a7d224f | 688 | int error; |
2a7d224f | 689 | |
5555696f | 690 | buf_stream_init(&writer, out); |
2a7d224f | 691 | |
5555696f | 692 | if ((error = git_filter_list_stream_file( |
b75f15aa | 693 | filters, repo, path, (git_writestream *)&writer)) < 0) |
5555696f | 694 | return error; |
2a7d224f | 695 | |
5555696f | 696 | assert(writer.complete); |
2a7d224f RB |
697 | return error; |
698 | } | |
699 | ||
fbdc9db3 ET |
700 | static int buf_from_blob(git_buf *out, git_blob *blob) |
701 | { | |
702 | git_off_t rawsize = git_blob_rawsize(blob); | |
703 | ||
704 | if (!git__is_sizet(rawsize)) { | |
705 | giterr_set(GITERR_OS, "Blob is too large to filter"); | |
706 | return -1; | |
707 | } | |
708 | ||
d4cf1675 | 709 | git_buf_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize); |
fbdc9db3 ET |
710 | return 0; |
711 | } | |
712 | ||
2a7d224f | 713 | int git_filter_list_apply_to_blob( |
a9f51e43 | 714 | git_buf *out, |
2a7d224f RB |
715 | git_filter_list *filters, |
716 | git_blob *blob) | |
717 | { | |
5555696f ET |
718 | struct buf_stream writer; |
719 | int error; | |
71379313 | 720 | |
5555696f | 721 | buf_stream_init(&writer, out); |
fbdc9db3 | 722 | |
5555696f | 723 | if ((error = git_filter_list_stream_blob( |
b75f15aa | 724 | filters, blob, (git_writestream *)&writer)) < 0) |
5555696f | 725 | return error; |
fbdc9db3 | 726 | |
5555696f ET |
727 | assert(writer.complete); |
728 | return error; | |
fbdc9db3 ET |
729 | } |
730 | ||
731 | struct proxy_stream { | |
f7c0125f | 732 | git_writestream parent; |
fbdc9db3 ET |
733 | git_filter *filter; |
734 | const git_filter_source *source; | |
735 | void **payload; | |
736 | git_buf input; | |
646364e7 ET |
737 | git_buf temp_buf; |
738 | git_buf *output; | |
b75f15aa | 739 | git_writestream *target; |
fbdc9db3 ET |
740 | }; |
741 | ||
742 | static int proxy_stream_write( | |
b75f15aa | 743 | git_writestream *s, const char *buffer, size_t len) |
fbdc9db3 ET |
744 | { |
745 | struct proxy_stream *proxy_stream = (struct proxy_stream *)s; | |
746 | assert(proxy_stream); | |
747 | ||
748 | return git_buf_put(&proxy_stream->input, buffer, len); | |
749 | } | |
750 | ||
b75f15aa | 751 | static int proxy_stream_close(git_writestream *s) |
fbdc9db3 ET |
752 | { |
753 | struct proxy_stream *proxy_stream = (struct proxy_stream *)s; | |
754 | git_buf *writebuf; | |
755 | int error; | |
756 | ||
757 | assert(proxy_stream); | |
758 | ||
759 | error = proxy_stream->filter->apply( | |
760 | proxy_stream->filter, | |
761 | proxy_stream->payload, | |
646364e7 | 762 | proxy_stream->output, |
fbdc9db3 ET |
763 | &proxy_stream->input, |
764 | proxy_stream->source); | |
765 | ||
766 | if (error == GIT_PASSTHROUGH) { | |
767 | writebuf = &proxy_stream->input; | |
768 | } else if (error == 0) { | |
646364e7 ET |
769 | git_buf_sanitize(proxy_stream->output); |
770 | writebuf = proxy_stream->output; | |
fbdc9db3 ET |
771 | } else { |
772 | return error; | |
773 | } | |
774 | ||
775 | if ((error = proxy_stream->target->write( | |
776 | proxy_stream->target, writebuf->ptr, writebuf->size)) == 0) | |
777 | error = proxy_stream->target->close(proxy_stream->target); | |
778 | ||
779 | return error; | |
780 | } | |
781 | ||
b75f15aa | 782 | static void proxy_stream_free(git_writestream *s) |
fbdc9db3 ET |
783 | { |
784 | struct proxy_stream *proxy_stream = (struct proxy_stream *)s; | |
785 | assert(proxy_stream); | |
786 | ||
787 | git_buf_free(&proxy_stream->input); | |
646364e7 | 788 | git_buf_free(&proxy_stream->temp_buf); |
fbdc9db3 ET |
789 | git__free(proxy_stream); |
790 | } | |
791 | ||
792 | static int proxy_stream_init( | |
b75f15aa | 793 | git_writestream **out, |
fbdc9db3 | 794 | git_filter *filter, |
646364e7 | 795 | git_buf *temp_buf, |
fbdc9db3 ET |
796 | void **payload, |
797 | const git_filter_source *source, | |
b75f15aa | 798 | git_writestream *target) |
fbdc9db3 ET |
799 | { |
800 | struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream)); | |
801 | GITERR_CHECK_ALLOC(proxy_stream); | |
802 | ||
f7c0125f ET |
803 | proxy_stream->parent.write = proxy_stream_write; |
804 | proxy_stream->parent.close = proxy_stream_close; | |
805 | proxy_stream->parent.free = proxy_stream_free; | |
fbdc9db3 ET |
806 | proxy_stream->filter = filter; |
807 | proxy_stream->payload = payload; | |
808 | proxy_stream->source = source; | |
809 | proxy_stream->target = target; | |
646364e7 | 810 | proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf; |
fbdc9db3 | 811 | |
b75f15aa | 812 | *out = (git_writestream *)proxy_stream; |
fbdc9db3 ET |
813 | return 0; |
814 | } | |
815 | ||
816 | static int stream_list_init( | |
b75f15aa | 817 | git_writestream **out, |
fbdc9db3 ET |
818 | git_vector *streams, |
819 | git_filter_list *filters, | |
b75f15aa | 820 | git_writestream *target) |
fbdc9db3 | 821 | { |
b75f15aa | 822 | git_writestream *last_stream = target; |
fbdc9db3 ET |
823 | size_t i; |
824 | int error = 0; | |
825 | ||
826 | *out = NULL; | |
827 | ||
828 | if (!filters) { | |
829 | *out = target; | |
830 | return 0; | |
831 | } | |
832 | ||
833 | /* Create filters last to first to get the chaining direction */ | |
834 | for (i = 0; i < git_array_size(filters->filters); ++i) { | |
835 | size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ? | |
836 | git_array_size(filters->filters) - 1 - i : i; | |
837 | git_filter_entry *fe = git_array_get(filters->filters, filter_idx); | |
b75f15aa | 838 | git_writestream *filter_stream; |
fbdc9db3 ET |
839 | |
840 | assert(fe->filter->stream || fe->filter->apply); | |
841 | ||
5555696f ET |
842 | /* If necessary, create a stream that proxies the traditional |
843 | * application. | |
844 | */ | |
646364e7 ET |
845 | if (fe->filter->stream) |
846 | error = fe->filter->stream(&filter_stream, fe->filter, | |
5555696f | 847 | &fe->payload, &filters->source, last_stream); |
646364e7 ET |
848 | else |
849 | /* Create a stream that proxies the one-shot apply */ | |
850 | error = proxy_stream_init(&filter_stream, fe->filter, | |
851 | filters->temp_buf, &fe->payload, &filters->source, | |
852 | last_stream); | |
fbdc9db3 | 853 | |
5555696f ET |
854 | if (error < 0) |
855 | return error; | |
856 | ||
857 | git_vector_insert(streams, filter_stream); | |
fbdc9db3 | 858 | last_stream = filter_stream; |
71379313 RB |
859 | } |
860 | ||
fbdc9db3 ET |
861 | *out = last_stream; |
862 | return 0; | |
863 | } | |
864 | ||
865 | void stream_list_free(git_vector *streams) | |
866 | { | |
b75f15aa | 867 | git_writestream *stream; |
fbdc9db3 ET |
868 | size_t i; |
869 | ||
870 | git_vector_foreach(streams, i, stream) | |
871 | stream->free(stream); | |
5555696f | 872 | git_vector_free(streams); |
fbdc9db3 ET |
873 | } |
874 | ||
875 | #define STREAM_BUFSIZE 10240 | |
876 | ||
fbdc9db3 ET |
877 | int git_filter_list_stream_file( |
878 | git_filter_list *filters, | |
fbdc9db3 ET |
879 | git_repository *repo, |
880 | const char *path, | |
b75f15aa | 881 | git_writestream *target) |
fbdc9db3 ET |
882 | { |
883 | char buf[STREAM_BUFSIZE]; | |
5555696f | 884 | git_buf abspath = GIT_BUF_INIT; |
fbdc9db3 ET |
885 | const char *base = repo ? git_repository_workdir(repo) : NULL; |
886 | git_vector filter_streams = GIT_VECTOR_INIT; | |
b75f15aa | 887 | git_writestream *stream_start; |
fbdc9db3 ET |
888 | ssize_t readlen; |
889 | int fd, error; | |
890 | ||
891 | if ((error = stream_list_init( | |
892 | &stream_start, &filter_streams, filters, target)) < 0 || | |
893 | (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0) | |
894 | goto done; | |
895 | ||
896 | if ((fd = git_futils_open_ro(path)) < 0) { | |
897 | error = fd; | |
898 | goto done; | |
899 | } | |
900 | ||
901 | while ((readlen = p_read(fd, buf, STREAM_BUFSIZE)) > 0) { | |
5555696f | 902 | if ((error = stream_start->write(stream_start, buf, readlen)) < 0) |
fbdc9db3 ET |
903 | goto done; |
904 | } | |
905 | ||
906 | if (!readlen) | |
907 | error = stream_start->close(stream_start); | |
908 | else if (readlen < 0) | |
909 | error = readlen; | |
910 | ||
911 | p_close(fd); | |
912 | ||
913 | done: | |
914 | stream_list_free(&filter_streams); | |
915 | git_buf_free(&abspath); | |
916 | return error; | |
917 | } | |
918 | ||
919 | int git_filter_list_stream_data( | |
920 | git_filter_list *filters, | |
921 | git_buf *data, | |
b75f15aa | 922 | git_writestream *target) |
fbdc9db3 ET |
923 | { |
924 | git_vector filter_streams = GIT_VECTOR_INIT; | |
b75f15aa | 925 | git_writestream *stream_start; |
fbdc9db3 ET |
926 | int error = 0; |
927 | ||
5555696f ET |
928 | git_buf_sanitize(data); |
929 | ||
fbdc9db3 ET |
930 | if ((error = stream_list_init( |
931 | &stream_start, &filter_streams, filters, target)) == 0 && | |
932 | (error = | |
933 | stream_start->write(stream_start, data->ptr, data->size)) == 0) | |
934 | error = stream_start->close(stream_start); | |
935 | ||
936 | stream_list_free(&filter_streams); | |
937 | return error; | |
938 | } | |
939 | ||
940 | int git_filter_list_stream_blob( | |
941 | git_filter_list *filters, | |
942 | git_blob *blob, | |
b75f15aa | 943 | git_writestream *target) |
fbdc9db3 ET |
944 | { |
945 | git_buf in = GIT_BUF_INIT; | |
946 | ||
947 | if (buf_from_blob(&in, blob) < 0) | |
948 | return -1; | |
2a7d224f | 949 | |
4b11f25a RB |
950 | if (filters) |
951 | git_oid_cpy(&filters->source.oid, git_blob_id(blob)); | |
952 | ||
fbdc9db3 | 953 | return git_filter_list_stream_data(filters, &in, target); |
2a7d224f | 954 | } |