2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
13 #include "repository.h"
15 #include "git2/sys/filter.h"
16 #include "git2/config.h"
18 #include "attr_file.h"
21 struct git_filter_source
{
24 git_oid oid
; /* zero if unknown (which is likely) */
25 uint16_t filemode
; /* zero if unknown */
26 git_filter_mode_t mode
;
27 git_filter_options options
;
31 const char *filter_name
;
36 struct git_filter_list
{
37 git_array_t(git_filter_entry
) filters
;
38 git_filter_source source
;
40 char path
[GIT_FLEX_ARRAY
];
48 size_t nattrs
, nmatches
;
50 const char *attrs
[GIT_FLEX_ARRAY
];
53 static int filter_def_priority_cmp(const void *a
, const void *b
)
55 int pa
= ((const git_filter_def
*)a
)->priority
;
56 int pb
= ((const git_filter_def
*)b
)->priority
;
57 return (pa
< pb
) ? -1 : (pa
> pb
) ? 1 : 0;
60 struct git_filter_registry
{
65 static struct git_filter_registry filter_registry
;
67 static void git_filter_global_shutdown(void);
70 static int filter_def_scan_attrs(
71 git_buf
*attrs
, size_t *nattr
, size_t *nmatch
, const char *attr_str
)
73 const char *start
, *scan
= attr_str
;
82 while (git__isspace(*scan
)) scan
++;
84 for (start
= scan
, has_eq
= 0; *scan
&& !git__isspace(*scan
); ++scan
) {
91 if (has_eq
|| *start
== '-' || *start
== '+' || *start
== '!')
95 git_buf_putc(attrs
, '=');
96 git_buf_put(attrs
, start
, scan
- start
);
97 git_buf_putc(attrs
, '\0');
104 static void filter_def_set_attrs(git_filter_def
*fdef
)
106 char *scan
= fdef
->attrdata
;
109 for (i
= 0; i
< fdef
->nattrs
; ++i
) {
110 const char *name
, *value
;
115 for (scan
++; *scan
!= '='; scan
++) /* find '=' */;
120 name
= scan
+ 1; value
= git_attr__false
; break;
122 name
= scan
+ 1; value
= git_attr__true
; break;
124 name
= scan
+ 1; value
= git_attr__unset
; break;
126 name
= scan
; value
= NULL
; break;
129 fdef
->attrs
[i
] = name
;
130 fdef
->attrs
[i
+ fdef
->nattrs
] = value
;
132 scan
+= strlen(scan
) + 1;
136 static int filter_def_name_key_check(const void *key
, const void *fdef
)
139 fdef
? ((const git_filter_def
*)fdef
)->filter_name
: NULL
;
140 return name
? git__strcmp(key
, name
) : -1;
143 static int filter_def_filter_key_check(const void *key
, const void *fdef
)
145 const void *filter
= fdef
? ((const git_filter_def
*)fdef
)->filter
: NULL
;
146 return (key
== filter
) ? 0 : -1;
149 /* Note: callers must lock the registry before calling this function */
150 static int filter_registry_insert(
151 const char *name
, git_filter
*filter
, int priority
)
153 git_filter_def
*fdef
;
154 size_t nattr
= 0, nmatch
= 0, alloc_len
;
155 git_buf attrs
= GIT_BUF_INIT
;
157 if (filter_def_scan_attrs(&attrs
, &nattr
, &nmatch
, filter
->attributes
) < 0)
160 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len
, nattr
, 2);
161 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len
, alloc_len
, sizeof(char *));
162 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, sizeof(git_filter_def
));
164 fdef
= git__calloc(1, alloc_len
);
165 GIT_ERROR_CHECK_ALLOC(fdef
);
167 fdef
->filter_name
= git__strdup(name
);
168 GIT_ERROR_CHECK_ALLOC(fdef
->filter_name
);
170 fdef
->filter
= filter
;
171 fdef
->priority
= priority
;
172 fdef
->nattrs
= nattr
;
173 fdef
->nmatches
= nmatch
;
174 fdef
->attrdata
= git_buf_detach(&attrs
);
176 filter_def_set_attrs(fdef
);
178 if (git_vector_insert(&filter_registry
.filters
, fdef
) < 0) {
179 git__free(fdef
->filter_name
);
180 git__free(fdef
->attrdata
);
185 git_vector_sort(&filter_registry
.filters
);
189 int git_filter_global_init(void)
191 git_filter
*crlf
= NULL
, *ident
= NULL
;
194 if (git_rwlock_init(&filter_registry
.lock
) < 0)
197 if ((error
= git_vector_init(&filter_registry
.filters
, 2,
198 filter_def_priority_cmp
)) < 0)
201 if ((crlf
= git_crlf_filter_new()) == NULL
||
202 filter_registry_insert(
203 GIT_FILTER_CRLF
, crlf
, GIT_FILTER_CRLF_PRIORITY
) < 0 ||
204 (ident
= git_ident_filter_new()) == NULL
||
205 filter_registry_insert(
206 GIT_FILTER_IDENT
, ident
, GIT_FILTER_IDENT_PRIORITY
) < 0)
210 error
= git_runtime_shutdown_register(git_filter_global_shutdown
);
214 git_filter_free(crlf
);
215 git_filter_free(ident
);
221 static void git_filter_global_shutdown(void)
224 git_filter_def
*fdef
;
226 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0)
229 git_vector_foreach(&filter_registry
.filters
, pos
, fdef
) {
230 if (fdef
->filter
&& fdef
->filter
->shutdown
) {
231 fdef
->filter
->shutdown(fdef
->filter
);
232 fdef
->initialized
= false;
235 git__free(fdef
->filter_name
);
236 git__free(fdef
->attrdata
);
240 git_vector_free(&filter_registry
.filters
);
242 git_rwlock_wrunlock(&filter_registry
.lock
);
243 git_rwlock_free(&filter_registry
.lock
);
246 /* Note: callers must lock the registry before calling this function */
247 static int filter_registry_find(size_t *pos
, const char *name
)
249 return git_vector_search2(
250 pos
, &filter_registry
.filters
, filter_def_name_key_check
, name
);
253 /* Note: callers must lock the registry before calling this function */
254 static git_filter_def
*filter_registry_lookup(size_t *pos
, const char *name
)
256 git_filter_def
*fdef
= NULL
;
258 if (!filter_registry_find(pos
, name
))
259 fdef
= git_vector_get(&filter_registry
.filters
, *pos
);
265 int git_filter_register(
266 const char *name
, git_filter
*filter
, int priority
)
270 GIT_ASSERT_ARG(name
);
271 GIT_ASSERT_ARG(filter
);
273 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0) {
274 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
278 if (!filter_registry_find(NULL
, name
)) {
280 GIT_ERROR_FILTER
, "attempt to reregister existing filter '%s'", name
);
285 error
= filter_registry_insert(name
, filter
, priority
);
288 git_rwlock_wrunlock(&filter_registry
.lock
);
292 int git_filter_unregister(const char *name
)
295 git_filter_def
*fdef
;
298 GIT_ASSERT_ARG(name
);
300 /* cannot unregister default filters */
301 if (!strcmp(GIT_FILTER_CRLF
, name
) || !strcmp(GIT_FILTER_IDENT
, name
)) {
302 git_error_set(GIT_ERROR_FILTER
, "cannot unregister filter '%s'", name
);
306 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0) {
307 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
311 if ((fdef
= filter_registry_lookup(&pos
, name
)) == NULL
) {
312 git_error_set(GIT_ERROR_FILTER
, "cannot find filter '%s' to unregister", name
);
313 error
= GIT_ENOTFOUND
;
317 git_vector_remove(&filter_registry
.filters
, pos
);
319 if (fdef
->initialized
&& fdef
->filter
&& fdef
->filter
->shutdown
) {
320 fdef
->filter
->shutdown(fdef
->filter
);
321 fdef
->initialized
= false;
324 git__free(fdef
->filter_name
);
325 git__free(fdef
->attrdata
);
329 git_rwlock_wrunlock(&filter_registry
.lock
);
333 static int filter_initialize(git_filter_def
*fdef
)
337 if (!fdef
->initialized
&& fdef
->filter
&& fdef
->filter
->initialize
) {
338 if ((error
= fdef
->filter
->initialize(fdef
->filter
)) < 0)
342 fdef
->initialized
= true;
346 git_filter
*git_filter_lookup(const char *name
)
349 git_filter_def
*fdef
;
350 git_filter
*filter
= NULL
;
352 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
353 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
357 if ((fdef
= filter_registry_lookup(&pos
, name
)) == NULL
||
358 (!fdef
->initialized
&& filter_initialize(fdef
) < 0))
361 filter
= fdef
->filter
;
364 git_rwlock_rdunlock(&filter_registry
.lock
);
368 void git_filter_free(git_filter
*filter
)
373 git_repository
*git_filter_source_repo(const git_filter_source
*src
)
378 const char *git_filter_source_path(const git_filter_source
*src
)
383 uint16_t git_filter_source_filemode(const git_filter_source
*src
)
385 return src
->filemode
;
388 const git_oid
*git_filter_source_id(const git_filter_source
*src
)
390 return git_oid_is_zero(&src
->oid
) ? NULL
: &src
->oid
;
393 git_filter_mode_t
git_filter_source_mode(const git_filter_source
*src
)
398 uint32_t git_filter_source_flags(const git_filter_source
*src
)
400 return src
->options
.flags
;
403 static int filter_list_new(
404 git_filter_list
**out
, const git_filter_source
*src
)
406 git_filter_list
*fl
= NULL
;
407 size_t pathlen
= src
->path
? strlen(src
->path
) : 0, alloclen
;
409 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_filter_list
), pathlen
);
410 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
412 fl
= git__calloc(1, alloclen
);
413 GIT_ERROR_CHECK_ALLOC(fl
);
416 memcpy(fl
->path
, src
->path
, pathlen
);
417 fl
->source
.repo
= src
->repo
;
418 fl
->source
.path
= fl
->path
;
419 fl
->source
.mode
= src
->mode
;
421 memcpy(&fl
->source
.options
, &src
->options
, sizeof(git_filter_options
));
427 static int filter_list_check_attributes(
429 git_repository
*repo
,
430 git_filter_session
*filter_session
,
431 git_filter_def
*fdef
,
432 const git_filter_source
*src
)
434 const char **strs
= git__calloc(fdef
->nattrs
, sizeof(const char *));
435 git_attr_options attr_opts
= GIT_ATTR_OPTIONS_INIT
;
439 GIT_ERROR_CHECK_ALLOC(strs
);
441 if ((src
->options
.flags
& GIT_FILTER_NO_SYSTEM_ATTRIBUTES
) != 0)
442 attr_opts
.flags
|= GIT_ATTR_CHECK_NO_SYSTEM
;
444 if ((src
->options
.flags
& GIT_FILTER_ATTRIBUTES_FROM_HEAD
) != 0)
445 attr_opts
.flags
|= GIT_ATTR_CHECK_INCLUDE_HEAD
;
447 if ((src
->options
.flags
& GIT_FILTER_ATTRIBUTES_FROM_COMMIT
) != 0) {
448 attr_opts
.flags
|= GIT_ATTR_CHECK_INCLUDE_COMMIT
;
450 #ifndef GIT_DEPRECATE_HARD
451 if (src
->options
.commit_id
)
452 git_oid_cpy(&attr_opts
.attr_commit_id
, src
->options
.commit_id
);
455 git_oid_cpy(&attr_opts
.attr_commit_id
, &src
->options
.attr_commit_id
);
458 error
= git_attr_get_many_with_session(
459 strs
, repo
, filter_session
->attr_session
, &attr_opts
, src
->path
, fdef
->nattrs
, fdef
->attrs
);
461 /* if no values were found but no matches are needed, it's okay! */
462 if (error
== GIT_ENOTFOUND
&& !fdef
->nmatches
) {
464 git__free((void *)strs
);
468 for (i
= 0; !error
&& i
< fdef
->nattrs
; ++i
) {
469 const char *want
= fdef
->attrs
[fdef
->nattrs
+ i
];
470 git_attr_value_t want_type
, found_type
;
475 want_type
= git_attr_value(want
);
476 found_type
= git_attr_value(strs
[i
]);
478 if (want_type
!= found_type
)
479 error
= GIT_ENOTFOUND
;
480 else if (want_type
== GIT_ATTR_VALUE_STRING
&&
481 strcmp(want
, strs
[i
]) &&
483 error
= GIT_ENOTFOUND
;
487 git__free((void *)strs
);
494 int git_filter_list_new(
495 git_filter_list
**out
,
496 git_repository
*repo
,
497 git_filter_mode_t mode
,
500 git_filter_source src
= { 0 };
504 src
.options
.flags
= flags
;
505 return filter_list_new(out
, &src
);
508 int git_filter_list__load(
509 git_filter_list
**filters
,
510 git_repository
*repo
,
511 git_blob
*blob
, /* can be NULL */
513 git_filter_mode_t mode
,
514 git_filter_session
*filter_session
)
517 git_filter_list
*fl
= NULL
;
518 git_filter_source src
= { 0 };
519 git_filter_entry
*fe
;
521 git_filter_def
*fdef
;
523 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
524 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
532 memcpy(&src
.options
, &filter_session
->options
, sizeof(git_filter_options
));
535 git_oid_cpy(&src
.oid
, git_blob_id(blob
));
537 git_vector_foreach(&filter_registry
.filters
, idx
, fdef
) {
538 const char **values
= NULL
;
539 void *payload
= NULL
;
541 if (!fdef
|| !fdef
->filter
)
544 if (fdef
->nattrs
> 0) {
545 error
= filter_list_check_attributes(
547 filter_session
, fdef
, &src
);
549 if (error
== GIT_ENOTFOUND
) {
552 } else if (error
< 0)
556 if (!fdef
->initialized
&& (error
= filter_initialize(fdef
)) < 0)
559 if (fdef
->filter
->check
)
560 error
= fdef
->filter
->check(
561 fdef
->filter
, &payload
, &src
, values
);
563 git__free((void *)values
);
565 if (error
== GIT_PASSTHROUGH
)
571 if ((error
= filter_list_new(&fl
, &src
)) < 0)
574 fl
->temp_buf
= filter_session
->temp_buf
;
577 fe
= git_array_alloc(fl
->filters
);
578 GIT_ERROR_CHECK_ALLOC(fe
);
580 fe
->filter
= fdef
->filter
;
581 fe
->filter_name
= fdef
->filter_name
;
582 fe
->payload
= payload
;
586 git_rwlock_rdunlock(&filter_registry
.lock
);
588 if (error
&& fl
!= NULL
) {
589 git_array_clear(fl
->filters
);
598 int git_filter_list_load_ext(
599 git_filter_list
**filters
,
600 git_repository
*repo
,
601 git_blob
*blob
, /* can be NULL */
603 git_filter_mode_t mode
,
604 git_filter_options
*opts
)
606 git_filter_session filter_session
= GIT_FILTER_SESSION_INIT
;
609 memcpy(&filter_session
.options
, opts
, sizeof(git_filter_options
));
611 return git_filter_list__load(
612 filters
, repo
, blob
, path
, mode
, &filter_session
);
615 int git_filter_list_load(
616 git_filter_list
**filters
,
617 git_repository
*repo
,
618 git_blob
*blob
, /* can be NULL */
620 git_filter_mode_t mode
,
623 git_filter_session filter_session
= GIT_FILTER_SESSION_INIT
;
625 filter_session
.options
.flags
= flags
;
627 return git_filter_list__load(
628 filters
, repo
, blob
, path
, mode
, &filter_session
);
631 void git_filter_list_free(git_filter_list
*fl
)
638 for (i
= 0; i
< git_array_size(fl
->filters
); ++i
) {
639 git_filter_entry
*fe
= git_array_get(fl
->filters
, i
);
640 if (fe
->filter
->cleanup
)
641 fe
->filter
->cleanup(fe
->filter
, fe
->payload
);
644 git_array_clear(fl
->filters
);
648 int git_filter_list_contains(
654 GIT_ASSERT_ARG(name
);
659 for (i
= 0; i
< fl
->filters
.size
; i
++) {
660 if (strcmp(fl
->filters
.ptr
[i
].filter_name
, name
) == 0)
667 int git_filter_list_push(
668 git_filter_list
*fl
, git_filter
*filter
, void *payload
)
672 git_filter_def
*fdef
= NULL
;
673 git_filter_entry
*fe
;
676 GIT_ASSERT_ARG(filter
);
678 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
679 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
683 if (git_vector_search2(
684 &pos
, &filter_registry
.filters
,
685 filter_def_filter_key_check
, filter
) == 0)
686 fdef
= git_vector_get(&filter_registry
.filters
, pos
);
688 git_rwlock_rdunlock(&filter_registry
.lock
);
691 git_error_set(GIT_ERROR_FILTER
, "cannot use an unregistered filter");
695 if (!fdef
->initialized
&& (error
= filter_initialize(fdef
)) < 0)
698 fe
= git_array_alloc(fl
->filters
);
699 GIT_ERROR_CHECK_ALLOC(fe
);
701 fe
->payload
= payload
;
706 size_t git_filter_list_length(const git_filter_list
*fl
)
708 return fl
? git_array_size(fl
->filters
) : 0;
712 git_writestream parent
;
717 static int buf_stream_write(
718 git_writestream
*s
, const char *buffer
, size_t len
)
720 struct buf_stream
*buf_stream
= (struct buf_stream
*)s
;
721 GIT_ASSERT_ARG(buf_stream
);
722 GIT_ASSERT(buf_stream
->complete
== 0);
724 return git_buf_put(buf_stream
->target
, buffer
, len
);
727 static int buf_stream_close(git_writestream
*s
)
729 struct buf_stream
*buf_stream
= (struct buf_stream
*)s
;
730 GIT_ASSERT_ARG(buf_stream
);
732 GIT_ASSERT(buf_stream
->complete
== 0);
733 buf_stream
->complete
= 1;
738 static void buf_stream_free(git_writestream
*s
)
743 static void buf_stream_init(struct buf_stream
*writer
, git_buf
*target
)
745 memset(writer
, 0, sizeof(struct buf_stream
));
747 writer
->parent
.write
= buf_stream_write
;
748 writer
->parent
.close
= buf_stream_close
;
749 writer
->parent
.free
= buf_stream_free
;
750 writer
->target
= target
;
752 git_buf_clear(target
);
755 int git_filter_list_apply_to_buffer(
757 git_filter_list
*filters
,
761 struct buf_stream writer
;
764 if ((error
= git_buf_sanitize(out
)) < 0)
767 buf_stream_init(&writer
, out
);
769 if ((error
= git_filter_list_stream_buffer(filters
,
770 in
, in_len
, &writer
.parent
)) < 0)
773 GIT_ASSERT(writer
.complete
);
777 int git_filter_list__convert_buf(
779 git_filter_list
*filters
,
784 if (!filters
|| git_filter_list_length(filters
) == 0) {
785 git_buf_swap(out
, in
);
790 error
= git_filter_list_apply_to_buffer(out
, filters
,
799 int git_filter_list_apply_to_file(
801 git_filter_list
*filters
,
802 git_repository
*repo
,
805 struct buf_stream writer
;
808 buf_stream_init(&writer
, out
);
810 if ((error
= git_filter_list_stream_file(
811 filters
, repo
, path
, &writer
.parent
)) < 0)
814 GIT_ASSERT(writer
.complete
);
818 static int buf_from_blob(git_buf
*out
, git_blob
*blob
)
820 git_object_size_t rawsize
= git_blob_rawsize(blob
);
822 if (!git__is_sizet(rawsize
)) {
823 git_error_set(GIT_ERROR_OS
, "blob is too large to filter");
827 git_buf_attach_notowned(out
, git_blob_rawcontent(blob
), (size_t)rawsize
);
831 int git_filter_list_apply_to_blob(
833 git_filter_list
*filters
,
836 struct buf_stream writer
;
839 buf_stream_init(&writer
, out
);
841 if ((error
= git_filter_list_stream_blob(
842 filters
, blob
, &writer
.parent
)) < 0)
845 GIT_ASSERT(writer
.complete
);
849 struct buffered_stream
{
850 git_writestream parent
;
852 int (*write_fn
)(git_filter
*, void **, git_buf
*, const git_buf
*, const git_filter_source
*);
853 const git_filter_source
*source
;
858 git_writestream
*target
;
861 static int buffered_stream_write(
862 git_writestream
*s
, const char *buffer
, size_t len
)
864 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
865 GIT_ASSERT_ARG(buffered_stream
);
867 return git_buf_put(&buffered_stream
->input
, buffer
, len
);
870 static int buffered_stream_close(git_writestream
*s
)
872 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
874 git_error_state error_state
= {0};
877 GIT_ASSERT_ARG(buffered_stream
);
879 error
= buffered_stream
->write_fn(
880 buffered_stream
->filter
,
881 buffered_stream
->payload
,
882 buffered_stream
->output
,
883 &buffered_stream
->input
,
884 buffered_stream
->source
);
886 if (error
== GIT_PASSTHROUGH
) {
887 writebuf
= &buffered_stream
->input
;
888 } else if (error
== 0) {
889 if ((error
= git_buf_sanitize(buffered_stream
->output
)) < 0)
892 writebuf
= buffered_stream
->output
;
894 /* close stream before erroring out taking care
895 * to preserve the original error */
896 git_error_state_capture(&error_state
, error
);
897 buffered_stream
->target
->close(buffered_stream
->target
);
898 git_error_state_restore(&error_state
);
902 if ((error
= buffered_stream
->target
->write(
903 buffered_stream
->target
, writebuf
->ptr
, writebuf
->size
)) == 0)
904 error
= buffered_stream
->target
->close(buffered_stream
->target
);
909 static void buffered_stream_free(git_writestream
*s
)
911 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
913 if (buffered_stream
) {
914 git_buf_dispose(&buffered_stream
->input
);
915 git_buf_dispose(&buffered_stream
->temp_buf
);
916 git__free(buffered_stream
);
920 int git_filter_buffered_stream_new(
921 git_writestream
**out
,
923 int (*write_fn
)(git_filter
*, void **, git_buf
*, const git_buf
*, const git_filter_source
*),
926 const git_filter_source
*source
,
927 git_writestream
*target
)
929 struct buffered_stream
*buffered_stream
= git__calloc(1, sizeof(struct buffered_stream
));
930 GIT_ERROR_CHECK_ALLOC(buffered_stream
);
932 buffered_stream
->parent
.write
= buffered_stream_write
;
933 buffered_stream
->parent
.close
= buffered_stream_close
;
934 buffered_stream
->parent
.free
= buffered_stream_free
;
935 buffered_stream
->filter
= filter
;
936 buffered_stream
->write_fn
= write_fn
;
937 buffered_stream
->output
= temp_buf
? temp_buf
: &buffered_stream
->temp_buf
;
938 buffered_stream
->payload
= payload
;
939 buffered_stream
->source
= source
;
940 buffered_stream
->target
= target
;
943 git_buf_clear(temp_buf
);
945 *out
= (git_writestream
*)buffered_stream
;
949 static int setup_stream(
950 git_writestream
**out
,
951 git_filter_entry
*fe
,
952 git_filter_list
*filters
,
953 git_writestream
*last_stream
)
955 #ifndef GIT_DEPRECATE_HARD
956 GIT_ASSERT(fe
->filter
->stream
|| fe
->filter
->apply
);
959 * If necessary, create a stream that proxies the traditional
962 if (!fe
->filter
->stream
) {
963 /* Create a stream that proxies the one-shot apply */
964 return git_filter_buffered_stream_new(out
,
965 fe
->filter
, fe
->filter
->apply
, filters
->temp_buf
,
966 &fe
->payload
, &filters
->source
, last_stream
);
970 GIT_ASSERT(fe
->filter
->stream
);
971 return fe
->filter
->stream(out
, fe
->filter
,
972 &fe
->payload
, &filters
->source
, last_stream
);
975 static int stream_list_init(
976 git_writestream
**out
,
978 git_filter_list
*filters
,
979 git_writestream
*target
)
981 git_writestream
*last_stream
= target
;
992 /* Create filters last to first to get the chaining direction */
993 for (i
= 0; i
< git_array_size(filters
->filters
); ++i
) {
994 size_t filter_idx
= (filters
->source
.mode
== GIT_FILTER_TO_WORKTREE
) ?
995 git_array_size(filters
->filters
) - 1 - i
: i
;
997 git_filter_entry
*fe
= git_array_get(filters
->filters
, filter_idx
);
998 git_writestream
*filter_stream
;
1000 error
= setup_stream(&filter_stream
, fe
, filters
, last_stream
);
1005 git_vector_insert(streams
, filter_stream
);
1006 last_stream
= filter_stream
;
1011 last_stream
->close(last_stream
);
1018 static void filter_streams_free(git_vector
*streams
)
1020 git_writestream
*stream
;
1023 git_vector_foreach(streams
, i
, stream
)
1024 stream
->free(stream
);
1025 git_vector_free(streams
);
1028 int git_filter_list_stream_file(
1029 git_filter_list
*filters
,
1030 git_repository
*repo
,
1032 git_writestream
*target
)
1034 char buf
[FILTERIO_BUFSIZE
];
1035 git_buf abspath
= GIT_BUF_INIT
;
1036 const char *base
= repo
? git_repository_workdir(repo
) : NULL
;
1037 git_vector filter_streams
= GIT_VECTOR_INIT
;
1038 git_writestream
*stream_start
;
1040 int fd
= -1, error
, initialized
= 0;
1042 if ((error
= stream_list_init(
1043 &stream_start
, &filter_streams
, filters
, target
)) < 0 ||
1044 (error
= git_path_join_unrooted(&abspath
, path
, base
, NULL
)) < 0 ||
1045 (error
= git_path_validate_workdir_buf(repo
, &abspath
)) < 0)
1050 if ((fd
= git_futils_open_ro(abspath
.ptr
)) < 0) {
1055 while ((readlen
= p_read(fd
, buf
, sizeof(buf
))) > 0) {
1056 if ((error
= stream_start
->write(stream_start
, buf
, readlen
)) < 0)
1065 error
|= stream_start
->close(stream_start
);
1069 filter_streams_free(&filter_streams
);
1070 git_buf_dispose(&abspath
);
1074 int git_filter_list_stream_buffer(
1075 git_filter_list
*filters
,
1078 git_writestream
*target
)
1080 git_vector filter_streams
= GIT_VECTOR_INIT
;
1081 git_writestream
*stream_start
;
1082 int error
, initialized
= 0;
1084 if ((error
= stream_list_init(&stream_start
, &filter_streams
, filters
, target
)) < 0)
1088 if ((error
= stream_start
->write(stream_start
, buffer
, len
)) < 0)
1093 error
|= stream_start
->close(stream_start
);
1095 filter_streams_free(&filter_streams
);
1099 int git_filter_list_stream_blob(
1100 git_filter_list
*filters
,
1102 git_writestream
*target
)
1104 git_buf in
= GIT_BUF_INIT
;
1106 if (buf_from_blob(&in
, blob
) < 0)
1110 git_oid_cpy(&filters
->source
.oid
, git_blob_id(blob
));
1112 return git_filter_list_stream_buffer(filters
, in
.ptr
, in
.size
, target
);
1115 int git_filter_init(git_filter
*filter
, unsigned int version
)
1117 GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter
, version
, git_filter
, GIT_FILTER_INIT
);
1121 #ifndef GIT_DEPRECATE_HARD
1123 int git_filter_list_stream_data(
1124 git_filter_list
*filters
,
1126 git_writestream
*target
)
1130 if ((error
= git_buf_sanitize(data
)) < 0)
1133 return git_filter_list_stream_buffer(filters
, data
->ptr
, data
->size
, target
);
1136 int git_filter_list_apply_to_data(
1137 git_buf
*tgt
, git_filter_list
*filters
, git_buf
*src
)
1141 if ((error
= git_buf_sanitize(src
)) < 0)
1144 return git_filter_list_apply_to_buffer(tgt
, filters
, src
->ptr
, src
->size
);