]>
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 | ||
eae0bfdc PP |
8 | #include "filter.h" |
9 | ||
e579e0f7 | 10 | #include "buf.h" |
44b1ff4c | 11 | #include "common.h" |
22a2d3d5 | 12 | #include "futils.h" |
44b1ff4c | 13 | #include "hash.h" |
c5266eba | 14 | #include "repository.h" |
c25aa7cd | 15 | #include "runtime.h" |
2a7d224f | 16 | #include "git2/sys/filter.h" |
c5266eba | 17 | #include "git2/config.h" |
9587895f | 18 | #include "blob.h" |
974774c7 | 19 | #include "attr_file.h" |
2a7d224f | 20 | #include "array.h" |
e579e0f7 | 21 | #include "path.h" |
44b1ff4c | 22 | |
570ba25c | 23 | struct git_filter_source { |
c25aa7cd PP |
24 | git_repository *repo; |
25 | const char *path; | |
26 | git_oid oid; /* zero if unknown (which is likely) */ | |
27 | uint16_t filemode; /* zero if unknown */ | |
28 | git_filter_mode_t mode; | |
29 | git_filter_options options; | |
570ba25c RB |
30 | }; |
31 | ||
85d54812 | 32 | typedef struct { |
2eecc288 | 33 | const char *filter_name; |
85d54812 RB |
34 | git_filter *filter; |
35 | void *payload; | |
36 | } git_filter_entry; | |
37 | ||
38 | struct git_filter_list { | |
39 | git_array_t(git_filter_entry) filters; | |
85d54812 | 40 | git_filter_source source; |
e579e0f7 | 41 | git_str *temp_buf; |
85d54812 RB |
42 | char path[GIT_FLEX_ARRAY]; |
43 | }; | |
44 | ||
45 | typedef struct { | |
5623e627 | 46 | char *filter_name; |
85d54812 | 47 | git_filter *filter; |
974774c7 | 48 | int priority; |
29e92d38 | 49 | int initialized; |
974774c7 RB |
50 | size_t nattrs, nmatches; |
51 | char *attrdata; | |
52 | const char *attrs[GIT_FLEX_ARRAY]; | |
85d54812 RB |
53 | } git_filter_def; |
54 | ||
974774c7 RB |
55 | static int filter_def_priority_cmp(const void *a, const void *b) |
56 | { | |
57 | int pa = ((const git_filter_def *)a)->priority; | |
58 | int pb = ((const git_filter_def *)b)->priority; | |
59 | return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; | |
60 | } | |
61 | ||
2ed855a9 ET |
62 | struct git_filter_registry { |
63 | git_rwlock lock; | |
0646634e | 64 | git_vector filters; |
974774c7 RB |
65 | }; |
66 | ||
2ed855a9 | 67 | static struct git_filter_registry filter_registry; |
0646634e | 68 | |
2ed855a9 | 69 | static void git_filter_global_shutdown(void); |
4b11f25a | 70 | |
0646634e | 71 | |
974774c7 | 72 | static int filter_def_scan_attrs( |
e579e0f7 | 73 | git_str *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) |
974774c7 RB |
74 | { |
75 | const char *start, *scan = attr_str; | |
76 | int has_eq; | |
77 | ||
78 | *nattr = *nmatch = 0; | |
79 | ||
80 | if (!scan) | |
81 | return 0; | |
82 | ||
83 | while (*scan) { | |
84 | while (git__isspace(*scan)) scan++; | |
85 | ||
86 | for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) { | |
87 | if (*scan == '=') | |
88 | has_eq = 1; | |
89 | } | |
90 | ||
91 | if (scan > start) { | |
92 | (*nattr)++; | |
4b11f25a | 93 | if (has_eq || *start == '-' || *start == '+' || *start == '!') |
974774c7 RB |
94 | (*nmatch)++; |
95 | ||
96 | if (has_eq) | |
e579e0f7 MB |
97 | git_str_putc(attrs, '='); |
98 | git_str_put(attrs, start, scan - start); | |
99 | git_str_putc(attrs, '\0'); | |
974774c7 RB |
100 | } |
101 | } | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static void filter_def_set_attrs(git_filter_def *fdef) | |
107 | { | |
108 | char *scan = fdef->attrdata; | |
109 | size_t i; | |
110 | ||
111 | for (i = 0; i < fdef->nattrs; ++i) { | |
112 | const char *name, *value; | |
113 | ||
114 | switch (*scan) { | |
115 | case '=': | |
116 | name = scan + 1; | |
117 | for (scan++; *scan != '='; scan++) /* find '=' */; | |
118 | *scan++ = '\0'; | |
119 | value = scan; | |
120 | break; | |
121 | case '-': | |
122 | name = scan + 1; value = git_attr__false; break; | |
123 | case '+': | |
124 | name = scan + 1; value = git_attr__true; break; | |
125 | case '!': | |
126 | name = scan + 1; value = git_attr__unset; break; | |
127 | default: | |
128 | name = scan; value = NULL; break; | |
129 | } | |
130 | ||
131 | fdef->attrs[i] = name; | |
132 | fdef->attrs[i + fdef->nattrs] = value; | |
133 | ||
134 | scan += strlen(scan) + 1; | |
135 | } | |
136 | } | |
137 | ||
0646634e RB |
138 | static int filter_def_name_key_check(const void *key, const void *fdef) |
139 | { | |
140 | const char *name = | |
141 | fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; | |
40cb40fa RB |
142 | return name ? git__strcmp(key, name) : -1; |
143 | } | |
144 | ||
145 | static int filter_def_filter_key_check(const void *key, const void *fdef) | |
146 | { | |
147 | const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL; | |
148 | return (key == filter) ? 0 : -1; | |
0646634e RB |
149 | } |
150 | ||
2ed855a9 ET |
151 | /* Note: callers must lock the registry before calling this function */ |
152 | static int filter_registry_insert( | |
974774c7 RB |
153 | const char *name, git_filter *filter, int priority) |
154 | { | |
155 | git_filter_def *fdef; | |
392702ee | 156 | size_t nattr = 0, nmatch = 0, alloc_len; |
e579e0f7 | 157 | git_str attrs = GIT_STR_INIT; |
974774c7 | 158 | |
974774c7 RB |
159 | if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) |
160 | return -1; | |
161 | ||
ac3d33df JK |
162 | GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2); |
163 | GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *)); | |
164 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def)); | |
392702ee ET |
165 | |
166 | fdef = git__calloc(1, alloc_len); | |
ac3d33df | 167 | GIT_ERROR_CHECK_ALLOC(fdef); |
974774c7 | 168 | |
5623e627 | 169 | fdef->filter_name = git__strdup(name); |
ac3d33df | 170 | GIT_ERROR_CHECK_ALLOC(fdef->filter_name); |
5623e627 | 171 | |
974774c7 RB |
172 | fdef->filter = filter; |
173 | fdef->priority = priority; | |
174 | fdef->nattrs = nattr; | |
175 | fdef->nmatches = nmatch; | |
e579e0f7 | 176 | fdef->attrdata = git_str_detach(&attrs); |
974774c7 RB |
177 | |
178 | filter_def_set_attrs(fdef); | |
179 | ||
2ed855a9 | 180 | if (git_vector_insert(&filter_registry.filters, fdef) < 0) { |
5623e627 | 181 | git__free(fdef->filter_name); |
974774c7 RB |
182 | git__free(fdef->attrdata); |
183 | git__free(fdef); | |
184 | return -1; | |
185 | } | |
186 | ||
2ed855a9 | 187 | git_vector_sort(&filter_registry.filters); |
29e92d38 RB |
188 | return 0; |
189 | } | |
190 | ||
2ed855a9 ET |
191 | int git_filter_global_init(void) |
192 | { | |
193 | git_filter *crlf = NULL, *ident = NULL; | |
194 | int error = 0; | |
195 | ||
196 | if (git_rwlock_init(&filter_registry.lock) < 0) | |
197 | return -1; | |
198 | ||
199 | if ((error = git_vector_init(&filter_registry.filters, 2, | |
200 | filter_def_priority_cmp)) < 0) | |
201 | goto done; | |
202 | ||
203 | if ((crlf = git_crlf_filter_new()) == NULL || | |
204 | filter_registry_insert( | |
205 | GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 || | |
206 | (ident = git_ident_filter_new()) == NULL || | |
207 | filter_registry_insert( | |
208 | GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) | |
209 | error = -1; | |
210 | ||
c25aa7cd PP |
211 | if (!error) |
212 | error = git_runtime_shutdown_register(git_filter_global_shutdown); | |
2ed855a9 ET |
213 | |
214 | done: | |
215 | if (error) { | |
216 | git_filter_free(crlf); | |
217 | git_filter_free(ident); | |
218 | } | |
219 | ||
220 | return error; | |
221 | } | |
222 | ||
223 | static void git_filter_global_shutdown(void) | |
224 | { | |
225 | size_t pos; | |
226 | git_filter_def *fdef; | |
227 | ||
228 | if (git_rwlock_wrlock(&filter_registry.lock) < 0) | |
229 | return; | |
230 | ||
231 | git_vector_foreach(&filter_registry.filters, pos, fdef) { | |
232 | if (fdef->filter && fdef->filter->shutdown) { | |
233 | fdef->filter->shutdown(fdef->filter); | |
234 | fdef->initialized = false; | |
235 | } | |
236 | ||
237 | git__free(fdef->filter_name); | |
238 | git__free(fdef->attrdata); | |
239 | git__free(fdef); | |
240 | } | |
241 | ||
242 | git_vector_free(&filter_registry.filters); | |
243 | ||
244 | git_rwlock_wrunlock(&filter_registry.lock); | |
245 | git_rwlock_free(&filter_registry.lock); | |
246 | } | |
247 | ||
248 | /* Note: callers must lock the registry before calling this function */ | |
249 | static int filter_registry_find(size_t *pos, const char *name) | |
250 | { | |
251 | return git_vector_search2( | |
252 | pos, &filter_registry.filters, filter_def_name_key_check, name); | |
253 | } | |
254 | ||
255 | /* Note: callers must lock the registry before calling this function */ | |
256 | static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) | |
257 | { | |
258 | git_filter_def *fdef = NULL; | |
259 | ||
260 | if (!filter_registry_find(pos, name)) | |
261 | fdef = git_vector_get(&filter_registry.filters, *pos); | |
262 | ||
263 | return fdef; | |
264 | } | |
265 | ||
266 | ||
267 | int git_filter_register( | |
268 | const char *name, git_filter *filter, int priority) | |
269 | { | |
270 | int error; | |
271 | ||
c25aa7cd PP |
272 | GIT_ASSERT_ARG(name); |
273 | GIT_ASSERT_ARG(filter); | |
2ed855a9 ET |
274 | |
275 | if (git_rwlock_wrlock(&filter_registry.lock) < 0) { | |
ac3d33df | 276 | git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); |
2ed855a9 ET |
277 | return -1; |
278 | } | |
279 | ||
280 | if (!filter_registry_find(NULL, name)) { | |
ac3d33df JK |
281 | git_error_set( |
282 | GIT_ERROR_FILTER, "attempt to reregister existing filter '%s'", name); | |
2ed855a9 ET |
283 | error = GIT_EEXISTS; |
284 | goto done; | |
285 | } | |
286 | ||
287 | error = filter_registry_insert(name, filter, priority); | |
288 | ||
289 | done: | |
290 | git_rwlock_wrunlock(&filter_registry.lock); | |
291 | return error; | |
292 | } | |
293 | ||
974774c7 RB |
294 | int git_filter_unregister(const char *name) |
295 | { | |
296 | size_t pos; | |
297 | git_filter_def *fdef; | |
2ed855a9 | 298 | int error = 0; |
974774c7 | 299 | |
c25aa7cd | 300 | GIT_ASSERT_ARG(name); |
5623e627 | 301 | |
974774c7 | 302 | /* cannot unregister default filters */ |
eefc32d5 | 303 | if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { |
ac3d33df | 304 | git_error_set(GIT_ERROR_FILTER, "cannot unregister filter '%s'", name); |
974774c7 RB |
305 | return -1; |
306 | } | |
307 | ||
2ed855a9 | 308 | if (git_rwlock_wrlock(&filter_registry.lock) < 0) { |
ac3d33df | 309 | git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); |
2ed855a9 ET |
310 | return -1; |
311 | } | |
312 | ||
0646634e | 313 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { |
ac3d33df | 314 | git_error_set(GIT_ERROR_FILTER, "cannot find filter '%s' to unregister", name); |
2ed855a9 ET |
315 | error = GIT_ENOTFOUND; |
316 | goto done; | |
974774c7 RB |
317 | } |
318 | ||
2ed855a9 | 319 | git_vector_remove(&filter_registry.filters, pos); |
974774c7 | 320 | |
29e92d38 | 321 | if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { |
974774c7 | 322 | fdef->filter->shutdown(fdef->filter); |
29e92d38 RB |
323 | fdef->initialized = false; |
324 | } | |
974774c7 | 325 | |
5623e627 | 326 | git__free(fdef->filter_name); |
974774c7 RB |
327 | git__free(fdef->attrdata); |
328 | git__free(fdef); | |
329 | ||
2ed855a9 ET |
330 | done: |
331 | git_rwlock_wrunlock(&filter_registry.lock); | |
332 | return error; | |
974774c7 RB |
333 | } |
334 | ||
0646634e RB |
335 | static int filter_initialize(git_filter_def *fdef) |
336 | { | |
337 | int error = 0; | |
338 | ||
2ed855a9 ET |
339 | if (!fdef->initialized && fdef->filter && fdef->filter->initialize) { |
340 | if ((error = fdef->filter->initialize(fdef->filter)) < 0) | |
341 | return error; | |
0646634e RB |
342 | } |
343 | ||
344 | fdef->initialized = true; | |
345 | return 0; | |
346 | } | |
347 | ||
974774c7 RB |
348 | git_filter *git_filter_lookup(const char *name) |
349 | { | |
350 | size_t pos; | |
0646634e | 351 | git_filter_def *fdef; |
2ed855a9 | 352 | git_filter *filter = NULL; |
0646634e | 353 | |
2ed855a9 | 354 | if (git_rwlock_rdlock(&filter_registry.lock) < 0) { |
ac3d33df | 355 | git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); |
0646634e | 356 | return NULL; |
2ed855a9 | 357 | } |
29e92d38 | 358 | |
2ed855a9 ET |
359 | if ((fdef = filter_registry_lookup(&pos, name)) == NULL || |
360 | (!fdef->initialized && filter_initialize(fdef) < 0)) | |
361 | goto done; | |
29e92d38 | 362 | |
2ed855a9 | 363 | filter = fdef->filter; |
29e92d38 | 364 | |
2ed855a9 ET |
365 | done: |
366 | git_rwlock_rdunlock(&filter_registry.lock); | |
367 | return filter; | |
974774c7 RB |
368 | } |
369 | ||
4b11f25a RB |
370 | void git_filter_free(git_filter *filter) |
371 | { | |
372 | git__free(filter); | |
373 | } | |
374 | ||
570ba25c RB |
375 | git_repository *git_filter_source_repo(const git_filter_source *src) |
376 | { | |
377 | return src->repo; | |
378 | } | |
379 | ||
380 | const char *git_filter_source_path(const git_filter_source *src) | |
381 | { | |
382 | return src->path; | |
383 | } | |
384 | ||
385 | uint16_t git_filter_source_filemode(const git_filter_source *src) | |
386 | { | |
387 | return src->filemode; | |
388 | } | |
389 | ||
390 | const git_oid *git_filter_source_id(const git_filter_source *src) | |
391 | { | |
22a2d3d5 | 392 | return git_oid_is_zero(&src->oid) ? NULL : &src->oid; |
570ba25c RB |
393 | } |
394 | ||
2a7d224f RB |
395 | git_filter_mode_t git_filter_source_mode(const git_filter_source *src) |
396 | { | |
397 | return src->mode; | |
398 | } | |
399 | ||
795eaccd | 400 | uint32_t git_filter_source_flags(const git_filter_source *src) |
5269008c | 401 | { |
c25aa7cd | 402 | return src->options.flags; |
5269008c RB |
403 | } |
404 | ||
40cb40fa | 405 | static int filter_list_new( |
2a7d224f | 406 | git_filter_list **out, const git_filter_source *src) |
27950fa3 | 407 | { |
85d54812 | 408 | git_filter_list *fl = NULL; |
f1453c59 | 409 | size_t pathlen = src->path ? strlen(src->path) : 0, alloclen; |
85d54812 | 410 | |
ac3d33df JK |
411 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen); |
412 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); | |
392702ee | 413 | |
f1453c59 | 414 | fl = git__calloc(1, alloclen); |
ac3d33df | 415 | GIT_ERROR_CHECK_ALLOC(fl); |
85d54812 | 416 | |
85d54812 RB |
417 | if (src->path) |
418 | memcpy(fl->path, src->path, pathlen); | |
419 | fl->source.repo = src->repo; | |
420 | fl->source.path = fl->path; | |
2a7d224f | 421 | fl->source.mode = src->mode; |
c25aa7cd PP |
422 | |
423 | memcpy(&fl->source.options, &src->options, sizeof(git_filter_options)); | |
85d54812 RB |
424 | |
425 | *out = fl; | |
426 | return 0; | |
427 | } | |
428 | ||
974774c7 | 429 | static int filter_list_check_attributes( |
9f779aac ET |
430 | const char ***out, |
431 | git_repository *repo, | |
c25aa7cd | 432 | git_filter_session *filter_session, |
9f779aac ET |
433 | git_filter_def *fdef, |
434 | const git_filter_source *src) | |
974774c7 | 435 | { |
974774c7 | 436 | const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); |
c25aa7cd | 437 | git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT; |
22a2d3d5 UG |
438 | size_t i; |
439 | int error; | |
440 | ||
ac3d33df | 441 | GIT_ERROR_CHECK_ALLOC(strs); |
974774c7 | 442 | |
c25aa7cd PP |
443 | if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) |
444 | attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM; | |
445 | ||
446 | if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0) | |
447 | attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD; | |
448 | ||
449 | if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) { | |
450 | attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT; | |
22a2d3d5 | 451 | |
c25aa7cd PP |
452 | #ifndef GIT_DEPRECATE_HARD |
453 | if (src->options.commit_id) | |
454 | git_oid_cpy(&attr_opts.attr_commit_id, src->options.commit_id); | |
455 | else | |
456 | #endif | |
457 | git_oid_cpy(&attr_opts.attr_commit_id, &src->options.attr_commit_id); | |
458 | } | |
22a2d3d5 | 459 | |
9f779aac | 460 | error = git_attr_get_many_with_session( |
c25aa7cd | 461 | strs, repo, filter_session->attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs); |
974774c7 RB |
462 | |
463 | /* if no values were found but no matches are needed, it's okay! */ | |
464 | if (error == GIT_ENOTFOUND && !fdef->nmatches) { | |
ac3d33df | 465 | git_error_clear(); |
e399c7ee | 466 | git__free((void *)strs); |
974774c7 RB |
467 | return 0; |
468 | } | |
469 | ||
470 | for (i = 0; !error && i < fdef->nattrs; ++i) { | |
471 | const char *want = fdef->attrs[fdef->nattrs + i]; | |
22a2d3d5 | 472 | git_attr_value_t want_type, found_type; |
974774c7 RB |
473 | |
474 | if (!want) | |
475 | continue; | |
476 | ||
477 | want_type = git_attr_value(want); | |
478 | found_type = git_attr_value(strs[i]); | |
479 | ||
63924435 ET |
480 | if (want_type != found_type) |
481 | error = GIT_ENOTFOUND; | |
22a2d3d5 | 482 | else if (want_type == GIT_ATTR_VALUE_STRING && |
63924435 ET |
483 | strcmp(want, strs[i]) && |
484 | strcmp(want, "*")) | |
974774c7 RB |
485 | error = GIT_ENOTFOUND; |
486 | } | |
487 | ||
488 | if (error) | |
e399c7ee | 489 | git__free((void *)strs); |
974774c7 RB |
490 | else |
491 | *out = strs; | |
492 | ||
493 | return error; | |
494 | } | |
495 | ||
40cb40fa | 496 | int git_filter_list_new( |
5269008c RB |
497 | git_filter_list **out, |
498 | git_repository *repo, | |
499 | git_filter_mode_t mode, | |
795eaccd | 500 | uint32_t flags) |
40cb40fa RB |
501 | { |
502 | git_filter_source src = { 0 }; | |
503 | src.repo = repo; | |
504 | src.path = NULL; | |
505 | src.mode = mode; | |
c25aa7cd | 506 | src.options.flags = flags; |
40cb40fa RB |
507 | return filter_list_new(out, &src); |
508 | } | |
509 | ||
c25aa7cd | 510 | int git_filter_list__load( |
85d54812 RB |
511 | git_filter_list **filters, |
512 | git_repository *repo, | |
4b11f25a | 513 | git_blob *blob, /* can be NULL */ |
85d54812 | 514 | const char *path, |
5269008c | 515 | git_filter_mode_t mode, |
c25aa7cd | 516 | git_filter_session *filter_session) |
85d54812 RB |
517 | { |
518 | int error = 0; | |
519 | git_filter_list *fl = NULL; | |
520 | git_filter_source src = { 0 }; | |
521 | git_filter_entry *fe; | |
974774c7 RB |
522 | size_t idx; |
523 | git_filter_def *fdef; | |
85d54812 | 524 | |
2ed855a9 | 525 | if (git_rwlock_rdlock(&filter_registry.lock) < 0) { |
ac3d33df | 526 | git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); |
85d54812 | 527 | return -1; |
2ed855a9 | 528 | } |
85d54812 RB |
529 | |
530 | src.repo = repo; | |
531 | src.path = path; | |
2a7d224f | 532 | src.mode = mode; |
c25aa7cd PP |
533 | |
534 | memcpy(&src.options, &filter_session->options, sizeof(git_filter_options)); | |
d05218b0 | 535 | |
4b11f25a RB |
536 | if (blob) |
537 | git_oid_cpy(&src.oid, git_blob_id(blob)); | |
85d54812 | 538 | |
2ed855a9 | 539 | git_vector_foreach(&filter_registry.filters, idx, fdef) { |
974774c7 | 540 | const char **values = NULL; |
85d54812 | 541 | void *payload = NULL; |
85d54812 RB |
542 | |
543 | if (!fdef || !fdef->filter) | |
544 | continue; | |
27950fa3 | 545 | |
974774c7 | 546 | if (fdef->nattrs > 0) { |
9f779aac | 547 | error = filter_list_check_attributes( |
c25aa7cd PP |
548 | &values, repo, |
549 | filter_session, fdef, &src); | |
9f779aac | 550 | |
974774c7 RB |
551 | if (error == GIT_ENOTFOUND) { |
552 | error = 0; | |
553 | continue; | |
554 | } else if (error < 0) | |
555 | break; | |
556 | } | |
557 | ||
29e92d38 RB |
558 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) |
559 | break; | |
560 | ||
85d54812 | 561 | if (fdef->filter->check) |
974774c7 | 562 | error = fdef->filter->check( |
2a7d224f | 563 | fdef->filter, &payload, &src, values); |
974774c7 | 564 | |
e399c7ee | 565 | git__free((void *)values); |
85d54812 | 566 | |
eefc32d5 | 567 | if (error == GIT_PASSTHROUGH) |
85d54812 RB |
568 | error = 0; |
569 | else if (error < 0) | |
570 | break; | |
571 | else { | |
9c9aa1ba ET |
572 | if (!fl) { |
573 | if ((error = filter_list_new(&fl, &src)) < 0) | |
2ed855a9 | 574 | break; |
9c9aa1ba | 575 | |
c25aa7cd | 576 | fl->temp_buf = filter_session->temp_buf; |
9c9aa1ba | 577 | } |
85d54812 RB |
578 | |
579 | fe = git_array_alloc(fl->filters); | |
ac3d33df | 580 | GIT_ERROR_CHECK_ALLOC(fe); |
2eecc288 ET |
581 | |
582 | fe->filter = fdef->filter; | |
583 | fe->filter_name = fdef->filter_name; | |
85d54812 RB |
584 | fe->payload = payload; |
585 | } | |
586 | } | |
587 | ||
2ed855a9 ET |
588 | git_rwlock_rdunlock(&filter_registry.lock); |
589 | ||
85d54812 RB |
590 | if (error && fl != NULL) { |
591 | git_array_clear(fl->filters); | |
592 | git__free(fl); | |
593 | fl = NULL; | |
594 | } | |
595 | ||
596 | *filters = fl; | |
597 | return error; | |
598 | } | |
599 | ||
c25aa7cd PP |
600 | int git_filter_list_load_ext( |
601 | git_filter_list **filters, | |
602 | git_repository *repo, | |
603 | git_blob *blob, /* can be NULL */ | |
604 | const char *path, | |
605 | git_filter_mode_t mode, | |
606 | git_filter_options *opts) | |
607 | { | |
608 | git_filter_session filter_session = GIT_FILTER_SESSION_INIT; | |
609 | ||
610 | if (opts) | |
611 | memcpy(&filter_session.options, opts, sizeof(git_filter_options)); | |
612 | ||
613 | return git_filter_list__load( | |
614 | filters, repo, blob, path, mode, &filter_session); | |
615 | } | |
616 | ||
9f779aac ET |
617 | int git_filter_list_load( |
618 | git_filter_list **filters, | |
619 | git_repository *repo, | |
620 | git_blob *blob, /* can be NULL */ | |
621 | const char *path, | |
622 | git_filter_mode_t mode, | |
795eaccd | 623 | uint32_t flags) |
9f779aac | 624 | { |
c25aa7cd | 625 | git_filter_session filter_session = GIT_FILTER_SESSION_INIT; |
d05218b0 | 626 | |
c25aa7cd | 627 | filter_session.options.flags = flags; |
d05218b0 | 628 | |
c25aa7cd PP |
629 | return git_filter_list__load( |
630 | filters, repo, blob, path, mode, &filter_session); | |
9f779aac ET |
631 | } |
632 | ||
85d54812 RB |
633 | void git_filter_list_free(git_filter_list *fl) |
634 | { | |
635 | uint32_t i; | |
636 | ||
637 | if (!fl) | |
638 | return; | |
639 | ||
640 | for (i = 0; i < git_array_size(fl->filters); ++i) { | |
641 | git_filter_entry *fe = git_array_get(fl->filters, i); | |
642 | if (fe->filter->cleanup) | |
643 | fe->filter->cleanup(fe->filter, fe->payload); | |
27950fa3 VM |
644 | } |
645 | ||
85d54812 RB |
646 | git_array_clear(fl->filters); |
647 | git__free(fl); | |
27950fa3 VM |
648 | } |
649 | ||
2eecc288 ET |
650 | int git_filter_list_contains( |
651 | git_filter_list *fl, | |
652 | const char *name) | |
653 | { | |
654 | size_t i; | |
655 | ||
c25aa7cd | 656 | GIT_ASSERT_ARG(name); |
2eecc288 ET |
657 | |
658 | if (!fl) | |
659 | return 0; | |
660 | ||
661 | for (i = 0; i < fl->filters.size; i++) { | |
662 | if (strcmp(fl->filters.ptr[i].filter_name, name) == 0) | |
663 | return 1; | |
664 | } | |
665 | ||
666 | return 0; | |
667 | } | |
668 | ||
40cb40fa RB |
669 | int git_filter_list_push( |
670 | git_filter_list *fl, git_filter *filter, void *payload) | |
671 | { | |
672 | int error = 0; | |
673 | size_t pos; | |
2ed855a9 | 674 | git_filter_def *fdef = NULL; |
40cb40fa RB |
675 | git_filter_entry *fe; |
676 | ||
c25aa7cd PP |
677 | GIT_ASSERT_ARG(fl); |
678 | GIT_ASSERT_ARG(filter); | |
40cb40fa | 679 | |
2ed855a9 | 680 | if (git_rwlock_rdlock(&filter_registry.lock) < 0) { |
ac3d33df | 681 | git_error_set(GIT_ERROR_OS, "failed to lock filter registry"); |
2ed855a9 ET |
682 | return -1; |
683 | } | |
684 | ||
40cb40fa | 685 | if (git_vector_search2( |
2ed855a9 ET |
686 | &pos, &filter_registry.filters, |
687 | filter_def_filter_key_check, filter) == 0) | |
688 | fdef = git_vector_get(&filter_registry.filters, pos); | |
689 | ||
690 | git_rwlock_rdunlock(&filter_registry.lock); | |
691 | ||
692 | if (fdef == NULL) { | |
ac3d33df | 693 | git_error_set(GIT_ERROR_FILTER, "cannot use an unregistered filter"); |
40cb40fa RB |
694 | return -1; |
695 | } | |
696 | ||
40cb40fa RB |
697 | if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) |
698 | return error; | |
699 | ||
700 | fe = git_array_alloc(fl->filters); | |
ac3d33df | 701 | GIT_ERROR_CHECK_ALLOC(fe); |
40cb40fa RB |
702 | fe->filter = filter; |
703 | fe->payload = payload; | |
704 | ||
705 | return 0; | |
706 | } | |
707 | ||
b47349b8 RB |
708 | size_t git_filter_list_length(const git_filter_list *fl) |
709 | { | |
710 | return fl ? git_array_size(fl->filters) : 0; | |
711 | } | |
712 | ||
5555696f | 713 | struct buf_stream { |
f7c0125f | 714 | git_writestream parent; |
e579e0f7 | 715 | git_str *target; |
5555696f ET |
716 | bool complete; |
717 | }; | |
718 | ||
719 | static int buf_stream_write( | |
b75f15aa | 720 | git_writestream *s, const char *buffer, size_t len) |
44b1ff4c | 721 | { |
5555696f | 722 | struct buf_stream *buf_stream = (struct buf_stream *)s; |
c25aa7cd PP |
723 | GIT_ASSERT_ARG(buf_stream); |
724 | GIT_ASSERT(buf_stream->complete == 0); | |
1e4976cb | 725 | |
e579e0f7 | 726 | return git_str_put(buf_stream->target, buffer, len); |
5555696f | 727 | } |
44b1ff4c | 728 | |
b75f15aa | 729 | static int buf_stream_close(git_writestream *s) |
5555696f ET |
730 | { |
731 | struct buf_stream *buf_stream = (struct buf_stream *)s; | |
c25aa7cd | 732 | GIT_ASSERT_ARG(buf_stream); |
44b1ff4c | 733 | |
c25aa7cd | 734 | GIT_ASSERT(buf_stream->complete == 0); |
5555696f | 735 | buf_stream->complete = 1; |
44b1ff4c | 736 | |
5555696f ET |
737 | return 0; |
738 | } | |
85d54812 | 739 | |
b75f15aa | 740 | static void buf_stream_free(git_writestream *s) |
5555696f ET |
741 | { |
742 | GIT_UNUSED(s); | |
743 | } | |
85d54812 | 744 | |
e579e0f7 | 745 | static void buf_stream_init(struct buf_stream *writer, git_str *target) |
5555696f ET |
746 | { |
747 | memset(writer, 0, sizeof(struct buf_stream)); | |
44b1ff4c | 748 | |
f7c0125f ET |
749 | writer->parent.write = buf_stream_write; |
750 | writer->parent.close = buf_stream_close; | |
751 | writer->parent.free = buf_stream_free; | |
5555696f | 752 | writer->target = target; |
2a7d224f | 753 | |
e579e0f7 | 754 | git_str_clear(target); |
5555696f | 755 | } |
44b1ff4c | 756 | |
c25aa7cd PP |
757 | int git_filter_list_apply_to_buffer( |
758 | git_buf *out, | |
759 | git_filter_list *filters, | |
760 | const char *in, | |
761 | size_t in_len) | |
e579e0f7 MB |
762 | { |
763 | GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_buffer, filters, in, in_len); | |
764 | } | |
765 | ||
766 | int git_filter_list__apply_to_buffer( | |
767 | git_str *out, | |
768 | git_filter_list *filters, | |
769 | const char *in, | |
770 | size_t in_len) | |
5555696f ET |
771 | { |
772 | struct buf_stream writer; | |
773 | int error; | |
774 | ||
c25aa7cd PP |
775 | buf_stream_init(&writer, out); |
776 | ||
777 | if ((error = git_filter_list_stream_buffer(filters, | |
778 | in, in_len, &writer.parent)) < 0) | |
779 | return error; | |
780 | ||
781 | GIT_ASSERT(writer.complete); | |
782 | return error; | |
783 | } | |
784 | ||
785 | int git_filter_list__convert_buf( | |
e579e0f7 | 786 | git_str *out, |
c25aa7cd | 787 | git_filter_list *filters, |
e579e0f7 | 788 | git_str *in) |
c25aa7cd PP |
789 | { |
790 | int error; | |
791 | ||
792 | if (!filters || git_filter_list_length(filters) == 0) { | |
e579e0f7 MB |
793 | git_str_swap(out, in); |
794 | git_str_dispose(in); | |
d4cf1675 ET |
795 | return 0; |
796 | } | |
5555696f | 797 | |
e579e0f7 | 798 | error = git_filter_list__apply_to_buffer(out, filters, |
c25aa7cd | 799 | in->ptr, in->size); |
5555696f | 800 | |
c25aa7cd | 801 | if (!error) |
e579e0f7 | 802 | git_str_dispose(in); |
5555696f | 803 | |
424222f4 | 804 | return error; |
44b1ff4c | 805 | } |
2a7d224f RB |
806 | |
807 | int git_filter_list_apply_to_file( | |
a9f51e43 | 808 | git_buf *out, |
2a7d224f RB |
809 | git_filter_list *filters, |
810 | git_repository *repo, | |
811 | const char *path) | |
e579e0f7 MB |
812 | { |
813 | GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_file, filters, repo, path); | |
814 | } | |
815 | ||
816 | int git_filter_list__apply_to_file( | |
817 | git_str *out, | |
818 | git_filter_list *filters, | |
819 | git_repository *repo, | |
820 | const char *path) | |
2a7d224f | 821 | { |
5555696f | 822 | struct buf_stream writer; |
2a7d224f | 823 | int error; |
2a7d224f | 824 | |
5555696f | 825 | buf_stream_init(&writer, out); |
2a7d224f | 826 | |
5555696f | 827 | if ((error = git_filter_list_stream_file( |
69f0032b | 828 | filters, repo, path, &writer.parent)) < 0) |
5555696f | 829 | return error; |
2a7d224f | 830 | |
c25aa7cd | 831 | GIT_ASSERT(writer.complete); |
2a7d224f RB |
832 | return error; |
833 | } | |
834 | ||
e579e0f7 | 835 | static int buf_from_blob(git_str *out, git_blob *blob) |
fbdc9db3 | 836 | { |
22a2d3d5 | 837 | git_object_size_t rawsize = git_blob_rawsize(blob); |
fbdc9db3 ET |
838 | |
839 | if (!git__is_sizet(rawsize)) { | |
ac3d33df | 840 | git_error_set(GIT_ERROR_OS, "blob is too large to filter"); |
fbdc9db3 ET |
841 | return -1; |
842 | } | |
843 | ||
e579e0f7 | 844 | git_str_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize); |
fbdc9db3 ET |
845 | return 0; |
846 | } | |
847 | ||
2a7d224f | 848 | int git_filter_list_apply_to_blob( |
a9f51e43 | 849 | git_buf *out, |
2a7d224f RB |
850 | git_filter_list *filters, |
851 | git_blob *blob) | |
e579e0f7 MB |
852 | { |
853 | GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_blob, filters, blob); | |
854 | } | |
855 | ||
856 | int git_filter_list__apply_to_blob( | |
857 | git_str *out, | |
858 | git_filter_list *filters, | |
859 | git_blob *blob) | |
2a7d224f | 860 | { |
5555696f ET |
861 | struct buf_stream writer; |
862 | int error; | |
71379313 | 863 | |
5555696f | 864 | buf_stream_init(&writer, out); |
fbdc9db3 | 865 | |
5555696f | 866 | if ((error = git_filter_list_stream_blob( |
69f0032b | 867 | filters, blob, &writer.parent)) < 0) |
5555696f | 868 | return error; |
fbdc9db3 | 869 | |
c25aa7cd | 870 | GIT_ASSERT(writer.complete); |
5555696f | 871 | return error; |
fbdc9db3 ET |
872 | } |
873 | ||
c25aa7cd | 874 | struct buffered_stream { |
f7c0125f | 875 | git_writestream parent; |
fbdc9db3 | 876 | git_filter *filter; |
e579e0f7 MB |
877 | int (*write_fn)(git_filter *, void **, git_str *, const git_str *, const git_filter_source *); |
878 | int (*legacy_write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *); | |
fbdc9db3 ET |
879 | const git_filter_source *source; |
880 | void **payload; | |
e579e0f7 MB |
881 | git_str input; |
882 | git_str temp_buf; | |
883 | git_str *output; | |
b75f15aa | 884 | git_writestream *target; |
fbdc9db3 ET |
885 | }; |
886 | ||
c25aa7cd | 887 | static int buffered_stream_write( |
b75f15aa | 888 | git_writestream *s, const char *buffer, size_t len) |
fbdc9db3 | 889 | { |
c25aa7cd PP |
890 | struct buffered_stream *buffered_stream = (struct buffered_stream *)s; |
891 | GIT_ASSERT_ARG(buffered_stream); | |
fbdc9db3 | 892 | |
e579e0f7 | 893 | return git_str_put(&buffered_stream->input, buffer, len); |
fbdc9db3 ET |
894 | } |
895 | ||
ad5611d8 TR |
896 | #ifndef GIT_DEPRECATE_HARD |
897 | # define BUF_TO_STRUCT(b, s) \ | |
898 | (b)->ptr = (s)->ptr; \ | |
899 | (b)->size = (s)->size; \ | |
900 | (b)->reserved = (s)->asize; | |
901 | # define STRUCT_TO_BUF(s, b) \ | |
902 | (s)->ptr = (b)->ptr; \ | |
903 | (s)->size = (b)->size; \ | |
904 | (s)->asize = (b)->reserved; | |
905 | #endif | |
906 | ||
c25aa7cd | 907 | static int buffered_stream_close(git_writestream *s) |
fbdc9db3 | 908 | { |
c25aa7cd | 909 | struct buffered_stream *buffered_stream = (struct buffered_stream *)s; |
e579e0f7 | 910 | git_str *writebuf; |
ac3d33df | 911 | git_error_state error_state = {0}; |
fbdc9db3 ET |
912 | int error; |
913 | ||
c25aa7cd | 914 | GIT_ASSERT_ARG(buffered_stream); |
fbdc9db3 | 915 | |
ad5611d8 TR |
916 | #ifndef GIT_DEPRECATE_HARD |
917 | if (buffered_stream->write_fn == NULL) { | |
918 | git_buf legacy_output = GIT_BUF_INIT, | |
919 | legacy_input = GIT_BUF_INIT; | |
920 | ||
921 | BUF_TO_STRUCT(&legacy_output, buffered_stream->output); | |
922 | BUF_TO_STRUCT(&legacy_input, &buffered_stream->input); | |
923 | ||
924 | error = buffered_stream->legacy_write_fn( | |
925 | buffered_stream->filter, | |
926 | buffered_stream->payload, | |
927 | &legacy_output, | |
928 | &legacy_input, | |
929 | buffered_stream->source); | |
930 | ||
931 | STRUCT_TO_BUF(buffered_stream->output, &legacy_output); | |
932 | STRUCT_TO_BUF(&buffered_stream->input, &legacy_input); | |
933 | } else | |
934 | #endif | |
c25aa7cd PP |
935 | error = buffered_stream->write_fn( |
936 | buffered_stream->filter, | |
937 | buffered_stream->payload, | |
938 | buffered_stream->output, | |
939 | &buffered_stream->input, | |
940 | buffered_stream->source); | |
fbdc9db3 ET |
941 | |
942 | if (error == GIT_PASSTHROUGH) { | |
c25aa7cd | 943 | writebuf = &buffered_stream->input; |
fbdc9db3 | 944 | } else if (error == 0) { |
c25aa7cd | 945 | writebuf = buffered_stream->output; |
fbdc9db3 | 946 | } else { |
ac3d33df JK |
947 | /* close stream before erroring out taking care |
948 | * to preserve the original error */ | |
949 | git_error_state_capture(&error_state, error); | |
c25aa7cd | 950 | buffered_stream->target->close(buffered_stream->target); |
ac3d33df | 951 | git_error_state_restore(&error_state); |
fbdc9db3 ET |
952 | return error; |
953 | } | |
954 | ||
c25aa7cd PP |
955 | if ((error = buffered_stream->target->write( |
956 | buffered_stream->target, writebuf->ptr, writebuf->size)) == 0) | |
957 | error = buffered_stream->target->close(buffered_stream->target); | |
fbdc9db3 ET |
958 | |
959 | return error; | |
960 | } | |
961 | ||
c25aa7cd | 962 | static void buffered_stream_free(git_writestream *s) |
fbdc9db3 | 963 | { |
c25aa7cd | 964 | struct buffered_stream *buffered_stream = (struct buffered_stream *)s; |
fbdc9db3 | 965 | |
c25aa7cd | 966 | if (buffered_stream) { |
e579e0f7 MB |
967 | git_str_dispose(&buffered_stream->input); |
968 | git_str_dispose(&buffered_stream->temp_buf); | |
c25aa7cd PP |
969 | git__free(buffered_stream); |
970 | } | |
fbdc9db3 ET |
971 | } |
972 | ||
c25aa7cd | 973 | int git_filter_buffered_stream_new( |
b75f15aa | 974 | git_writestream **out, |
fbdc9db3 | 975 | git_filter *filter, |
e579e0f7 MB |
976 | int (*write_fn)(git_filter *, void **, git_str *, const git_str *, const git_filter_source *), |
977 | git_str *temp_buf, | |
fbdc9db3 ET |
978 | void **payload, |
979 | const git_filter_source *source, | |
b75f15aa | 980 | git_writestream *target) |
fbdc9db3 | 981 | { |
c25aa7cd PP |
982 | struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream)); |
983 | GIT_ERROR_CHECK_ALLOC(buffered_stream); | |
984 | ||
985 | buffered_stream->parent.write = buffered_stream_write; | |
986 | buffered_stream->parent.close = buffered_stream_close; | |
987 | buffered_stream->parent.free = buffered_stream_free; | |
988 | buffered_stream->filter = filter; | |
989 | buffered_stream->write_fn = write_fn; | |
990 | buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf; | |
991 | buffered_stream->payload = payload; | |
992 | buffered_stream->source = source; | |
993 | buffered_stream->target = target; | |
fbdc9db3 | 994 | |
669ae274 | 995 | if (temp_buf) |
e579e0f7 | 996 | git_str_clear(temp_buf); |
669ae274 | 997 | |
c25aa7cd | 998 | *out = (git_writestream *)buffered_stream; |
fbdc9db3 ET |
999 | return 0; |
1000 | } | |
1001 | ||
e579e0f7 MB |
1002 | #ifndef GIT_DEPRECATE_HARD |
1003 | static int buffered_legacy_stream_new( | |
1004 | git_writestream **out, | |
1005 | git_filter *filter, | |
1006 | int (*legacy_write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *), | |
1007 | git_str *temp_buf, | |
1008 | void **payload, | |
1009 | const git_filter_source *source, | |
1010 | git_writestream *target) | |
1011 | { | |
1012 | struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream)); | |
1013 | GIT_ERROR_CHECK_ALLOC(buffered_stream); | |
1014 | ||
1015 | buffered_stream->parent.write = buffered_stream_write; | |
1016 | buffered_stream->parent.close = buffered_stream_close; | |
1017 | buffered_stream->parent.free = buffered_stream_free; | |
1018 | buffered_stream->filter = filter; | |
1019 | buffered_stream->legacy_write_fn = legacy_write_fn; | |
1020 | buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf; | |
1021 | buffered_stream->payload = payload; | |
1022 | buffered_stream->source = source; | |
1023 | buffered_stream->target = target; | |
1024 | ||
1025 | if (temp_buf) | |
1026 | git_str_clear(temp_buf); | |
1027 | ||
1028 | *out = (git_writestream *)buffered_stream; | |
1029 | return 0; | |
1030 | } | |
1031 | #endif | |
1032 | ||
c25aa7cd PP |
1033 | static int setup_stream( |
1034 | git_writestream **out, | |
1035 | git_filter_entry *fe, | |
1036 | git_filter_list *filters, | |
1037 | git_writestream *last_stream) | |
1038 | { | |
1039 | #ifndef GIT_DEPRECATE_HARD | |
1040 | GIT_ASSERT(fe->filter->stream || fe->filter->apply); | |
1041 | ||
1042 | /* | |
1043 | * If necessary, create a stream that proxies the traditional | |
1044 | * application. | |
1045 | */ | |
1046 | if (!fe->filter->stream) { | |
1047 | /* Create a stream that proxies the one-shot apply */ | |
e579e0f7 | 1048 | return buffered_legacy_stream_new(out, |
c25aa7cd PP |
1049 | fe->filter, fe->filter->apply, filters->temp_buf, |
1050 | &fe->payload, &filters->source, last_stream); | |
1051 | } | |
1052 | #endif | |
1053 | ||
1054 | GIT_ASSERT(fe->filter->stream); | |
1055 | return fe->filter->stream(out, fe->filter, | |
1056 | &fe->payload, &filters->source, last_stream); | |
1057 | } | |
1058 | ||
fbdc9db3 | 1059 | static int stream_list_init( |
b75f15aa | 1060 | git_writestream **out, |
fbdc9db3 ET |
1061 | git_vector *streams, |
1062 | git_filter_list *filters, | |
b75f15aa | 1063 | git_writestream *target) |
fbdc9db3 | 1064 | { |
b75f15aa | 1065 | git_writestream *last_stream = target; |
fbdc9db3 ET |
1066 | size_t i; |
1067 | int error = 0; | |
1068 | ||
1069 | *out = NULL; | |
1070 | ||
1071 | if (!filters) { | |
1072 | *out = target; | |
1073 | return 0; | |
1074 | } | |
1075 | ||
1076 | /* Create filters last to first to get the chaining direction */ | |
1077 | for (i = 0; i < git_array_size(filters->filters); ++i) { | |
1078 | size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ? | |
1079 | git_array_size(filters->filters) - 1 - i : i; | |
c25aa7cd | 1080 | |
fbdc9db3 | 1081 | git_filter_entry *fe = git_array_get(filters->filters, filter_idx); |
b75f15aa | 1082 | git_writestream *filter_stream; |
a78441bc | 1083 | |
c25aa7cd | 1084 | error = setup_stream(&filter_stream, fe, filters, last_stream); |
fbdc9db3 | 1085 | |
5555696f | 1086 | if (error < 0) |
cf07db2f | 1087 | goto out; |
5555696f ET |
1088 | |
1089 | git_vector_insert(streams, filter_stream); | |
fbdc9db3 | 1090 | last_stream = filter_stream; |
71379313 RB |
1091 | } |
1092 | ||
cf07db2f PS |
1093 | out: |
1094 | if (error) | |
1095 | last_stream->close(last_stream); | |
1096 | else | |
1097 | *out = last_stream; | |
1098 | ||
1099 | return error; | |
fbdc9db3 ET |
1100 | } |
1101 | ||
22a2d3d5 | 1102 | static void filter_streams_free(git_vector *streams) |
fbdc9db3 | 1103 | { |
b75f15aa | 1104 | git_writestream *stream; |
fbdc9db3 ET |
1105 | size_t i; |
1106 | ||
1107 | git_vector_foreach(streams, i, stream) | |
1108 | stream->free(stream); | |
5555696f | 1109 | git_vector_free(streams); |
fbdc9db3 ET |
1110 | } |
1111 | ||
fbdc9db3 ET |
1112 | int git_filter_list_stream_file( |
1113 | git_filter_list *filters, | |
fbdc9db3 ET |
1114 | git_repository *repo, |
1115 | const char *path, | |
b75f15aa | 1116 | git_writestream *target) |
fbdc9db3 | 1117 | { |
ad5611d8 | 1118 | char buf[GIT_BUFSIZE_FILTERIO]; |
e579e0f7 | 1119 | git_str abspath = GIT_STR_INIT; |
fbdc9db3 ET |
1120 | const char *base = repo ? git_repository_workdir(repo) : NULL; |
1121 | git_vector filter_streams = GIT_VECTOR_INIT; | |
b75f15aa | 1122 | git_writestream *stream_start; |
fbdc9db3 | 1123 | ssize_t readlen; |
cf07db2f | 1124 | int fd = -1, error, initialized = 0; |
fbdc9db3 ET |
1125 | |
1126 | if ((error = stream_list_init( | |
1127 | &stream_start, &filter_streams, filters, target)) < 0 || | |
e579e0f7 MB |
1128 | (error = git_fs_path_join_unrooted(&abspath, path, base, NULL)) < 0 || |
1129 | (error = git_path_validate_str_length(repo, &abspath)) < 0) | |
fbdc9db3 | 1130 | goto done; |
c25aa7cd | 1131 | |
cf07db2f | 1132 | initialized = 1; |
fbdc9db3 | 1133 | |
6a2edc5a | 1134 | if ((fd = git_futils_open_ro(abspath.ptr)) < 0) { |
fbdc9db3 ET |
1135 | error = fd; |
1136 | goto done; | |
1137 | } | |
1138 | ||
7dd22538 | 1139 | while ((readlen = p_read(fd, buf, sizeof(buf))) > 0) { |
5555696f | 1140 | if ((error = stream_start->write(stream_start, buf, readlen)) < 0) |
fbdc9db3 ET |
1141 | goto done; |
1142 | } | |
1143 | ||
cf07db2f | 1144 | if (readlen < 0) |
ac3d33df | 1145 | error = -1; |
fbdc9db3 | 1146 | |
fbdc9db3 | 1147 | done: |
cf07db2f PS |
1148 | if (initialized) |
1149 | error |= stream_start->close(stream_start); | |
1150 | ||
0137aba5 CMN |
1151 | if (fd >= 0) |
1152 | p_close(fd); | |
22a2d3d5 | 1153 | filter_streams_free(&filter_streams); |
e579e0f7 | 1154 | git_str_dispose(&abspath); |
fbdc9db3 ET |
1155 | return error; |
1156 | } | |
1157 | ||
c25aa7cd | 1158 | int git_filter_list_stream_buffer( |
fbdc9db3 | 1159 | git_filter_list *filters, |
c25aa7cd PP |
1160 | const char *buffer, |
1161 | size_t len, | |
b75f15aa | 1162 | git_writestream *target) |
fbdc9db3 ET |
1163 | { |
1164 | git_vector filter_streams = GIT_VECTOR_INIT; | |
b75f15aa | 1165 | git_writestream *stream_start; |
cf07db2f | 1166 | int error, initialized = 0; |
fbdc9db3 | 1167 | |
4de7f3bf CMN |
1168 | if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0) |
1169 | goto out; | |
cf07db2f | 1170 | initialized = 1; |
fbdc9db3 | 1171 | |
c25aa7cd | 1172 | if ((error = stream_start->write(stream_start, buffer, len)) < 0) |
cf07db2f | 1173 | goto out; |
4de7f3bf CMN |
1174 | |
1175 | out: | |
cf07db2f PS |
1176 | if (initialized) |
1177 | error |= stream_start->close(stream_start); | |
1178 | ||
22a2d3d5 | 1179 | filter_streams_free(&filter_streams); |
cf07db2f | 1180 | return error; |
fbdc9db3 ET |
1181 | } |
1182 | ||
1183 | int git_filter_list_stream_blob( | |
1184 | git_filter_list *filters, | |
1185 | git_blob *blob, | |
b75f15aa | 1186 | git_writestream *target) |
fbdc9db3 | 1187 | { |
e579e0f7 | 1188 | git_str in = GIT_STR_INIT; |
fbdc9db3 ET |
1189 | |
1190 | if (buf_from_blob(&in, blob) < 0) | |
1191 | return -1; | |
2a7d224f | 1192 | |
4b11f25a RB |
1193 | if (filters) |
1194 | git_oid_cpy(&filters->source.oid, git_blob_id(blob)); | |
1195 | ||
c25aa7cd | 1196 | return git_filter_list_stream_buffer(filters, in.ptr, in.size, target); |
2a7d224f | 1197 | } |
a78441bc MM |
1198 | |
1199 | int git_filter_init(git_filter *filter, unsigned int version) | |
1200 | { | |
1201 | GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT); | |
1202 | return 0; | |
1203 | } | |
c25aa7cd PP |
1204 | |
1205 | #ifndef GIT_DEPRECATE_HARD | |
1206 | ||
1207 | int git_filter_list_stream_data( | |
1208 | git_filter_list *filters, | |
1209 | git_buf *data, | |
1210 | git_writestream *target) | |
1211 | { | |
c25aa7cd PP |
1212 | return git_filter_list_stream_buffer(filters, data->ptr, data->size, target); |
1213 | } | |
1214 | ||
1215 | int git_filter_list_apply_to_data( | |
1216 | git_buf *tgt, git_filter_list *filters, git_buf *src) | |
1217 | { | |
c25aa7cd PP |
1218 | return git_filter_list_apply_to_buffer(tgt, filters, src->ptr, src->size); |
1219 | } | |
1220 | ||
1221 | #endif |