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.
14 #include "repository.h"
16 #include "git2/sys/filter.h"
17 #include "git2/config.h"
19 #include "attr_file.h"
23 struct git_filter_source
{
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
;
33 const char *filter_name
;
38 struct git_filter_list
{
39 git_array_t(git_filter_entry
) filters
;
40 git_filter_source source
;
42 char path
[GIT_FLEX_ARRAY
];
50 size_t nattrs
, nmatches
;
52 const char *attrs
[GIT_FLEX_ARRAY
];
55 static int filter_def_priority_cmp(const void *a
, const void *b
)
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;
62 struct git_filter_registry
{
67 static struct git_filter_registry filter_registry
;
69 static void git_filter_global_shutdown(void);
72 static int filter_def_scan_attrs(
73 git_str
*attrs
, size_t *nattr
, size_t *nmatch
, const char *attr_str
)
75 const char *start
, *scan
= attr_str
;
84 while (git__isspace(*scan
)) scan
++;
86 for (start
= scan
, has_eq
= 0; *scan
&& !git__isspace(*scan
); ++scan
) {
93 if (has_eq
|| *start
== '-' || *start
== '+' || *start
== '!')
97 git_str_putc(attrs
, '=');
98 git_str_put(attrs
, start
, scan
- start
);
99 git_str_putc(attrs
, '\0');
106 static void filter_def_set_attrs(git_filter_def
*fdef
)
108 char *scan
= fdef
->attrdata
;
111 for (i
= 0; i
< fdef
->nattrs
; ++i
) {
112 const char *name
, *value
;
117 for (scan
++; *scan
!= '='; scan
++) /* find '=' */;
122 name
= scan
+ 1; value
= git_attr__false
; break;
124 name
= scan
+ 1; value
= git_attr__true
; break;
126 name
= scan
+ 1; value
= git_attr__unset
; break;
128 name
= scan
; value
= NULL
; break;
131 fdef
->attrs
[i
] = name
;
132 fdef
->attrs
[i
+ fdef
->nattrs
] = value
;
134 scan
+= strlen(scan
) + 1;
138 static int filter_def_name_key_check(const void *key
, const void *fdef
)
141 fdef
? ((const git_filter_def
*)fdef
)->filter_name
: NULL
;
142 return name
? git__strcmp(key
, name
) : -1;
145 static int filter_def_filter_key_check(const void *key
, const void *fdef
)
147 const void *filter
= fdef
? ((const git_filter_def
*)fdef
)->filter
: NULL
;
148 return (key
== filter
) ? 0 : -1;
151 /* Note: callers must lock the registry before calling this function */
152 static int filter_registry_insert(
153 const char *name
, git_filter
*filter
, int priority
)
155 git_filter_def
*fdef
;
156 size_t nattr
= 0, nmatch
= 0, alloc_len
;
157 git_str attrs
= GIT_STR_INIT
;
159 if (filter_def_scan_attrs(&attrs
, &nattr
, &nmatch
, filter
->attributes
) < 0)
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
));
166 fdef
= git__calloc(1, alloc_len
);
167 GIT_ERROR_CHECK_ALLOC(fdef
);
169 fdef
->filter_name
= git__strdup(name
);
170 GIT_ERROR_CHECK_ALLOC(fdef
->filter_name
);
172 fdef
->filter
= filter
;
173 fdef
->priority
= priority
;
174 fdef
->nattrs
= nattr
;
175 fdef
->nmatches
= nmatch
;
176 fdef
->attrdata
= git_str_detach(&attrs
);
178 filter_def_set_attrs(fdef
);
180 if (git_vector_insert(&filter_registry
.filters
, fdef
) < 0) {
181 git__free(fdef
->filter_name
);
182 git__free(fdef
->attrdata
);
187 git_vector_sort(&filter_registry
.filters
);
191 int git_filter_global_init(void)
193 git_filter
*crlf
= NULL
, *ident
= NULL
;
196 if (git_rwlock_init(&filter_registry
.lock
) < 0)
199 if ((error
= git_vector_init(&filter_registry
.filters
, 2,
200 filter_def_priority_cmp
)) < 0)
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)
212 error
= git_runtime_shutdown_register(git_filter_global_shutdown
);
216 git_filter_free(crlf
);
217 git_filter_free(ident
);
223 static void git_filter_global_shutdown(void)
226 git_filter_def
*fdef
;
228 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0)
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;
237 git__free(fdef
->filter_name
);
238 git__free(fdef
->attrdata
);
242 git_vector_free(&filter_registry
.filters
);
244 git_rwlock_wrunlock(&filter_registry
.lock
);
245 git_rwlock_free(&filter_registry
.lock
);
248 /* Note: callers must lock the registry before calling this function */
249 static int filter_registry_find(size_t *pos
, const char *name
)
251 return git_vector_search2(
252 pos
, &filter_registry
.filters
, filter_def_name_key_check
, name
);
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
)
258 git_filter_def
*fdef
= NULL
;
260 if (!filter_registry_find(pos
, name
))
261 fdef
= git_vector_get(&filter_registry
.filters
, *pos
);
267 int git_filter_register(
268 const char *name
, git_filter
*filter
, int priority
)
272 GIT_ASSERT_ARG(name
);
273 GIT_ASSERT_ARG(filter
);
275 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0) {
276 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
280 if (!filter_registry_find(NULL
, name
)) {
282 GIT_ERROR_FILTER
, "attempt to reregister existing filter '%s'", name
);
287 error
= filter_registry_insert(name
, filter
, priority
);
290 git_rwlock_wrunlock(&filter_registry
.lock
);
294 int git_filter_unregister(const char *name
)
297 git_filter_def
*fdef
;
300 GIT_ASSERT_ARG(name
);
302 /* cannot unregister default filters */
303 if (!strcmp(GIT_FILTER_CRLF
, name
) || !strcmp(GIT_FILTER_IDENT
, name
)) {
304 git_error_set(GIT_ERROR_FILTER
, "cannot unregister filter '%s'", name
);
308 if (git_rwlock_wrlock(&filter_registry
.lock
) < 0) {
309 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
313 if ((fdef
= filter_registry_lookup(&pos
, name
)) == NULL
) {
314 git_error_set(GIT_ERROR_FILTER
, "cannot find filter '%s' to unregister", name
);
315 error
= GIT_ENOTFOUND
;
319 git_vector_remove(&filter_registry
.filters
, pos
);
321 if (fdef
->initialized
&& fdef
->filter
&& fdef
->filter
->shutdown
) {
322 fdef
->filter
->shutdown(fdef
->filter
);
323 fdef
->initialized
= false;
326 git__free(fdef
->filter_name
);
327 git__free(fdef
->attrdata
);
331 git_rwlock_wrunlock(&filter_registry
.lock
);
335 static int filter_initialize(git_filter_def
*fdef
)
339 if (!fdef
->initialized
&& fdef
->filter
&& fdef
->filter
->initialize
) {
340 if ((error
= fdef
->filter
->initialize(fdef
->filter
)) < 0)
344 fdef
->initialized
= true;
348 git_filter
*git_filter_lookup(const char *name
)
351 git_filter_def
*fdef
;
352 git_filter
*filter
= NULL
;
354 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
355 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
359 if ((fdef
= filter_registry_lookup(&pos
, name
)) == NULL
||
360 (!fdef
->initialized
&& filter_initialize(fdef
) < 0))
363 filter
= fdef
->filter
;
366 git_rwlock_rdunlock(&filter_registry
.lock
);
370 void git_filter_free(git_filter
*filter
)
375 git_repository
*git_filter_source_repo(const git_filter_source
*src
)
380 const char *git_filter_source_path(const git_filter_source
*src
)
385 uint16_t git_filter_source_filemode(const git_filter_source
*src
)
387 return src
->filemode
;
390 const git_oid
*git_filter_source_id(const git_filter_source
*src
)
392 return git_oid_is_zero(&src
->oid
) ? NULL
: &src
->oid
;
395 git_filter_mode_t
git_filter_source_mode(const git_filter_source
*src
)
400 uint32_t git_filter_source_flags(const git_filter_source
*src
)
402 return src
->options
.flags
;
405 static int filter_list_new(
406 git_filter_list
**out
, const git_filter_source
*src
)
408 git_filter_list
*fl
= NULL
;
409 size_t pathlen
= src
->path
? strlen(src
->path
) : 0, alloclen
;
411 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_filter_list
), pathlen
);
412 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
414 fl
= git__calloc(1, alloclen
);
415 GIT_ERROR_CHECK_ALLOC(fl
);
418 memcpy(fl
->path
, src
->path
, pathlen
);
419 fl
->source
.repo
= src
->repo
;
420 fl
->source
.path
= fl
->path
;
421 fl
->source
.mode
= src
->mode
;
423 memcpy(&fl
->source
.options
, &src
->options
, sizeof(git_filter_options
));
429 static int filter_list_check_attributes(
431 git_repository
*repo
,
432 git_filter_session
*filter_session
,
433 git_filter_def
*fdef
,
434 const git_filter_source
*src
)
436 const char **strs
= git__calloc(fdef
->nattrs
, sizeof(const char *));
437 git_attr_options attr_opts
= GIT_ATTR_OPTIONS_INIT
;
441 GIT_ERROR_CHECK_ALLOC(strs
);
443 if ((src
->options
.flags
& GIT_FILTER_NO_SYSTEM_ATTRIBUTES
) != 0)
444 attr_opts
.flags
|= GIT_ATTR_CHECK_NO_SYSTEM
;
446 if ((src
->options
.flags
& GIT_FILTER_ATTRIBUTES_FROM_HEAD
) != 0)
447 attr_opts
.flags
|= GIT_ATTR_CHECK_INCLUDE_HEAD
;
449 if ((src
->options
.flags
& GIT_FILTER_ATTRIBUTES_FROM_COMMIT
) != 0) {
450 attr_opts
.flags
|= GIT_ATTR_CHECK_INCLUDE_COMMIT
;
452 #ifndef GIT_DEPRECATE_HARD
453 if (src
->options
.commit_id
)
454 git_oid_cpy(&attr_opts
.attr_commit_id
, src
->options
.commit_id
);
457 git_oid_cpy(&attr_opts
.attr_commit_id
, &src
->options
.attr_commit_id
);
460 error
= git_attr_get_many_with_session(
461 strs
, repo
, filter_session
->attr_session
, &attr_opts
, src
->path
, fdef
->nattrs
, fdef
->attrs
);
463 /* if no values were found but no matches are needed, it's okay! */
464 if (error
== GIT_ENOTFOUND
&& !fdef
->nmatches
) {
466 git__free((void *)strs
);
470 for (i
= 0; !error
&& i
< fdef
->nattrs
; ++i
) {
471 const char *want
= fdef
->attrs
[fdef
->nattrs
+ i
];
472 git_attr_value_t want_type
, found_type
;
477 want_type
= git_attr_value(want
);
478 found_type
= git_attr_value(strs
[i
]);
480 if (want_type
!= found_type
)
481 error
= GIT_ENOTFOUND
;
482 else if (want_type
== GIT_ATTR_VALUE_STRING
&&
483 strcmp(want
, strs
[i
]) &&
485 error
= GIT_ENOTFOUND
;
489 git__free((void *)strs
);
496 int git_filter_list_new(
497 git_filter_list
**out
,
498 git_repository
*repo
,
499 git_filter_mode_t mode
,
502 git_filter_source src
= { 0 };
506 src
.options
.flags
= flags
;
507 return filter_list_new(out
, &src
);
510 int git_filter_list__load(
511 git_filter_list
**filters
,
512 git_repository
*repo
,
513 git_blob
*blob
, /* can be NULL */
515 git_filter_mode_t mode
,
516 git_filter_session
*filter_session
)
519 git_filter_list
*fl
= NULL
;
520 git_filter_source src
= { 0 };
521 git_filter_entry
*fe
;
523 git_filter_def
*fdef
;
525 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
526 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
534 memcpy(&src
.options
, &filter_session
->options
, sizeof(git_filter_options
));
537 git_oid_cpy(&src
.oid
, git_blob_id(blob
));
539 git_vector_foreach(&filter_registry
.filters
, idx
, fdef
) {
540 const char **values
= NULL
;
541 void *payload
= NULL
;
543 if (!fdef
|| !fdef
->filter
)
546 if (fdef
->nattrs
> 0) {
547 error
= filter_list_check_attributes(
549 filter_session
, fdef
, &src
);
551 if (error
== GIT_ENOTFOUND
) {
554 } else if (error
< 0)
558 if (!fdef
->initialized
&& (error
= filter_initialize(fdef
)) < 0)
561 if (fdef
->filter
->check
)
562 error
= fdef
->filter
->check(
563 fdef
->filter
, &payload
, &src
, values
);
565 git__free((void *)values
);
567 if (error
== GIT_PASSTHROUGH
)
573 if ((error
= filter_list_new(&fl
, &src
)) < 0)
576 fl
->temp_buf
= filter_session
->temp_buf
;
579 fe
= git_array_alloc(fl
->filters
);
580 GIT_ERROR_CHECK_ALLOC(fe
);
582 fe
->filter
= fdef
->filter
;
583 fe
->filter_name
= fdef
->filter_name
;
584 fe
->payload
= payload
;
588 git_rwlock_rdunlock(&filter_registry
.lock
);
590 if (error
&& fl
!= NULL
) {
591 git_array_clear(fl
->filters
);
600 int git_filter_list_load_ext(
601 git_filter_list
**filters
,
602 git_repository
*repo
,
603 git_blob
*blob
, /* can be NULL */
605 git_filter_mode_t mode
,
606 git_filter_options
*opts
)
608 git_filter_session filter_session
= GIT_FILTER_SESSION_INIT
;
611 memcpy(&filter_session
.options
, opts
, sizeof(git_filter_options
));
613 return git_filter_list__load(
614 filters
, repo
, blob
, path
, mode
, &filter_session
);
617 int git_filter_list_load(
618 git_filter_list
**filters
,
619 git_repository
*repo
,
620 git_blob
*blob
, /* can be NULL */
622 git_filter_mode_t mode
,
625 git_filter_session filter_session
= GIT_FILTER_SESSION_INIT
;
627 filter_session
.options
.flags
= flags
;
629 return git_filter_list__load(
630 filters
, repo
, blob
, path
, mode
, &filter_session
);
633 void git_filter_list_free(git_filter_list
*fl
)
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
);
646 git_array_clear(fl
->filters
);
650 int git_filter_list_contains(
656 GIT_ASSERT_ARG(name
);
661 for (i
= 0; i
< fl
->filters
.size
; i
++) {
662 if (strcmp(fl
->filters
.ptr
[i
].filter_name
, name
) == 0)
669 int git_filter_list_push(
670 git_filter_list
*fl
, git_filter
*filter
, void *payload
)
674 git_filter_def
*fdef
= NULL
;
675 git_filter_entry
*fe
;
678 GIT_ASSERT_ARG(filter
);
680 if (git_rwlock_rdlock(&filter_registry
.lock
) < 0) {
681 git_error_set(GIT_ERROR_OS
, "failed to lock filter registry");
685 if (git_vector_search2(
686 &pos
, &filter_registry
.filters
,
687 filter_def_filter_key_check
, filter
) == 0)
688 fdef
= git_vector_get(&filter_registry
.filters
, pos
);
690 git_rwlock_rdunlock(&filter_registry
.lock
);
693 git_error_set(GIT_ERROR_FILTER
, "cannot use an unregistered filter");
697 if (!fdef
->initialized
&& (error
= filter_initialize(fdef
)) < 0)
700 fe
= git_array_alloc(fl
->filters
);
701 GIT_ERROR_CHECK_ALLOC(fe
);
703 fe
->payload
= payload
;
708 size_t git_filter_list_length(const git_filter_list
*fl
)
710 return fl
? git_array_size(fl
->filters
) : 0;
714 git_writestream parent
;
719 static int buf_stream_write(
720 git_writestream
*s
, const char *buffer
, size_t len
)
722 struct buf_stream
*buf_stream
= (struct buf_stream
*)s
;
723 GIT_ASSERT_ARG(buf_stream
);
724 GIT_ASSERT(buf_stream
->complete
== 0);
726 return git_str_put(buf_stream
->target
, buffer
, len
);
729 static int buf_stream_close(git_writestream
*s
)
731 struct buf_stream
*buf_stream
= (struct buf_stream
*)s
;
732 GIT_ASSERT_ARG(buf_stream
);
734 GIT_ASSERT(buf_stream
->complete
== 0);
735 buf_stream
->complete
= 1;
740 static void buf_stream_free(git_writestream
*s
)
745 static void buf_stream_init(struct buf_stream
*writer
, git_str
*target
)
747 memset(writer
, 0, sizeof(struct buf_stream
));
749 writer
->parent
.write
= buf_stream_write
;
750 writer
->parent
.close
= buf_stream_close
;
751 writer
->parent
.free
= buf_stream_free
;
752 writer
->target
= target
;
754 git_str_clear(target
);
757 int git_filter_list_apply_to_buffer(
759 git_filter_list
*filters
,
763 GIT_BUF_WRAP_PRIVATE(out
, git_filter_list__apply_to_buffer
, filters
, in
, in_len
);
766 int git_filter_list__apply_to_buffer(
768 git_filter_list
*filters
,
772 struct buf_stream writer
;
775 buf_stream_init(&writer
, out
);
777 if ((error
= git_filter_list_stream_buffer(filters
,
778 in
, in_len
, &writer
.parent
)) < 0)
781 GIT_ASSERT(writer
.complete
);
785 int git_filter_list__convert_buf(
787 git_filter_list
*filters
,
792 if (!filters
|| git_filter_list_length(filters
) == 0) {
793 git_str_swap(out
, in
);
798 error
= git_filter_list__apply_to_buffer(out
, filters
,
807 int git_filter_list_apply_to_file(
809 git_filter_list
*filters
,
810 git_repository
*repo
,
813 GIT_BUF_WRAP_PRIVATE(out
, git_filter_list__apply_to_file
, filters
, repo
, path
);
816 int git_filter_list__apply_to_file(
818 git_filter_list
*filters
,
819 git_repository
*repo
,
822 struct buf_stream writer
;
825 buf_stream_init(&writer
, out
);
827 if ((error
= git_filter_list_stream_file(
828 filters
, repo
, path
, &writer
.parent
)) < 0)
831 GIT_ASSERT(writer
.complete
);
835 static int buf_from_blob(git_str
*out
, git_blob
*blob
)
837 git_object_size_t rawsize
= git_blob_rawsize(blob
);
839 if (!git__is_sizet(rawsize
)) {
840 git_error_set(GIT_ERROR_OS
, "blob is too large to filter");
844 git_str_attach_notowned(out
, git_blob_rawcontent(blob
), (size_t)rawsize
);
848 int git_filter_list_apply_to_blob(
850 git_filter_list
*filters
,
853 GIT_BUF_WRAP_PRIVATE(out
, git_filter_list__apply_to_blob
, filters
, blob
);
856 int git_filter_list__apply_to_blob(
858 git_filter_list
*filters
,
861 struct buf_stream writer
;
864 buf_stream_init(&writer
, out
);
866 if ((error
= git_filter_list_stream_blob(
867 filters
, blob
, &writer
.parent
)) < 0)
870 GIT_ASSERT(writer
.complete
);
874 struct buffered_stream
{
875 git_writestream parent
;
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
*);
879 const git_filter_source
*source
;
884 git_writestream
*target
;
887 static int buffered_stream_write(
888 git_writestream
*s
, const char *buffer
, size_t len
)
890 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
891 GIT_ASSERT_ARG(buffered_stream
);
893 return git_str_put(&buffered_stream
->input
, buffer
, len
);
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;
907 static int buffered_stream_close(git_writestream
*s
)
909 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
911 git_error_state error_state
= {0};
914 GIT_ASSERT_ARG(buffered_stream
);
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
;
921 BUF_TO_STRUCT(&legacy_output
, buffered_stream
->output
);
922 BUF_TO_STRUCT(&legacy_input
, &buffered_stream
->input
);
924 error
= buffered_stream
->legacy_write_fn(
925 buffered_stream
->filter
,
926 buffered_stream
->payload
,
929 buffered_stream
->source
);
931 STRUCT_TO_BUF(buffered_stream
->output
, &legacy_output
);
932 STRUCT_TO_BUF(&buffered_stream
->input
, &legacy_input
);
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
);
942 if (error
== GIT_PASSTHROUGH
) {
943 writebuf
= &buffered_stream
->input
;
944 } else if (error
== 0) {
945 writebuf
= buffered_stream
->output
;
947 /* close stream before erroring out taking care
948 * to preserve the original error */
949 git_error_state_capture(&error_state
, error
);
950 buffered_stream
->target
->close(buffered_stream
->target
);
951 git_error_state_restore(&error_state
);
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
);
962 static void buffered_stream_free(git_writestream
*s
)
964 struct buffered_stream
*buffered_stream
= (struct buffered_stream
*)s
;
966 if (buffered_stream
) {
967 git_str_dispose(&buffered_stream
->input
);
968 git_str_dispose(&buffered_stream
->temp_buf
);
969 git__free(buffered_stream
);
973 int git_filter_buffered_stream_new(
974 git_writestream
**out
,
976 int (*write_fn
)(git_filter
*, void **, git_str
*, const git_str
*, const git_filter_source
*),
979 const git_filter_source
*source
,
980 git_writestream
*target
)
982 struct buffered_stream
*buffered_stream
= git__calloc(1, sizeof(struct buffered_stream
));
983 GIT_ERROR_CHECK_ALLOC(buffered_stream
);
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
;
996 git_str_clear(temp_buf
);
998 *out
= (git_writestream
*)buffered_stream
;
1002 #ifndef GIT_DEPRECATE_HARD
1003 static int buffered_legacy_stream_new(
1004 git_writestream
**out
,
1006 int (*legacy_write_fn
)(git_filter
*, void **, git_buf
*, const git_buf
*, const git_filter_source
*),
1009 const git_filter_source
*source
,
1010 git_writestream
*target
)
1012 struct buffered_stream
*buffered_stream
= git__calloc(1, sizeof(struct buffered_stream
));
1013 GIT_ERROR_CHECK_ALLOC(buffered_stream
);
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
;
1026 git_str_clear(temp_buf
);
1028 *out
= (git_writestream
*)buffered_stream
;
1033 static int setup_stream(
1034 git_writestream
**out
,
1035 git_filter_entry
*fe
,
1036 git_filter_list
*filters
,
1037 git_writestream
*last_stream
)
1039 #ifndef GIT_DEPRECATE_HARD
1040 GIT_ASSERT(fe
->filter
->stream
|| fe
->filter
->apply
);
1043 * If necessary, create a stream that proxies the traditional
1046 if (!fe
->filter
->stream
) {
1047 /* Create a stream that proxies the one-shot apply */
1048 return buffered_legacy_stream_new(out
,
1049 fe
->filter
, fe
->filter
->apply
, filters
->temp_buf
,
1050 &fe
->payload
, &filters
->source
, last_stream
);
1054 GIT_ASSERT(fe
->filter
->stream
);
1055 return fe
->filter
->stream(out
, fe
->filter
,
1056 &fe
->payload
, &filters
->source
, last_stream
);
1059 static int stream_list_init(
1060 git_writestream
**out
,
1061 git_vector
*streams
,
1062 git_filter_list
*filters
,
1063 git_writestream
*target
)
1065 git_writestream
*last_stream
= target
;
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
;
1081 git_filter_entry
*fe
= git_array_get(filters
->filters
, filter_idx
);
1082 git_writestream
*filter_stream
;
1084 error
= setup_stream(&filter_stream
, fe
, filters
, last_stream
);
1089 git_vector_insert(streams
, filter_stream
);
1090 last_stream
= filter_stream
;
1095 last_stream
->close(last_stream
);
1102 static void filter_streams_free(git_vector
*streams
)
1104 git_writestream
*stream
;
1107 git_vector_foreach(streams
, i
, stream
)
1108 stream
->free(stream
);
1109 git_vector_free(streams
);
1112 int git_filter_list_stream_file(
1113 git_filter_list
*filters
,
1114 git_repository
*repo
,
1116 git_writestream
*target
)
1118 char buf
[GIT_BUFSIZE_FILTERIO
];
1119 git_str abspath
= GIT_STR_INIT
;
1120 const char *base
= repo
? git_repository_workdir(repo
) : NULL
;
1121 git_vector filter_streams
= GIT_VECTOR_INIT
;
1122 git_writestream
*stream_start
;
1124 int fd
= -1, error
, initialized
= 0;
1126 if ((error
= stream_list_init(
1127 &stream_start
, &filter_streams
, filters
, target
)) < 0 ||
1128 (error
= git_fs_path_join_unrooted(&abspath
, path
, base
, NULL
)) < 0 ||
1129 (error
= git_path_validate_str_length(repo
, &abspath
)) < 0)
1134 if ((fd
= git_futils_open_ro(abspath
.ptr
)) < 0) {
1139 while ((readlen
= p_read(fd
, buf
, sizeof(buf
))) > 0) {
1140 if ((error
= stream_start
->write(stream_start
, buf
, readlen
)) < 0)
1149 error
|= stream_start
->close(stream_start
);
1153 filter_streams_free(&filter_streams
);
1154 git_str_dispose(&abspath
);
1158 int git_filter_list_stream_buffer(
1159 git_filter_list
*filters
,
1162 git_writestream
*target
)
1164 git_vector filter_streams
= GIT_VECTOR_INIT
;
1165 git_writestream
*stream_start
;
1166 int error
, initialized
= 0;
1168 if ((error
= stream_list_init(&stream_start
, &filter_streams
, filters
, target
)) < 0)
1172 if ((error
= stream_start
->write(stream_start
, buffer
, len
)) < 0)
1177 error
|= stream_start
->close(stream_start
);
1179 filter_streams_free(&filter_streams
);
1183 int git_filter_list_stream_blob(
1184 git_filter_list
*filters
,
1186 git_writestream
*target
)
1188 git_str in
= GIT_STR_INIT
;
1190 if (buf_from_blob(&in
, blob
) < 0)
1194 git_oid_cpy(&filters
->source
.oid
, git_blob_id(blob
));
1196 return git_filter_list_stream_buffer(filters
, in
.ptr
, in
.size
, target
);
1199 int git_filter_init(git_filter
*filter
, unsigned int version
)
1201 GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter
, version
, git_filter
, GIT_FILTER_INIT
);
1205 #ifndef GIT_DEPRECATE_HARD
1207 int git_filter_list_stream_data(
1208 git_filter_list
*filters
,
1210 git_writestream
*target
)
1212 return git_filter_list_stream_buffer(filters
, data
->ptr
, data
->size
, target
);
1215 int git_filter_list_apply_to_data(
1216 git_buf
*tgt
, git_filter_list
*filters
, git_buf
*src
)
1218 return git_filter_list_apply_to_buffer(tgt
, filters
, src
->ptr
, src
->size
);