/* * diff_tree.c : default diff tree processor * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== */ #include #include #include #include #include "svn_dirent_uri.h" #include "svn_error.h" #include "svn_io.h" #include "svn_pools.h" #include "svn_props.h" #include "svn_types.h" #include "private/svn_diff_tree.h" #include "svn_private_config.h" typedef struct tree_processor_t { svn_diff_tree_processor_t tp; /* void *future_extension */ } tree_processor_t; static svn_error_t * default_dir_opened(void **new_dir_baton, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *parent_dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { *new_dir_baton = NULL; return SVN_NO_ERROR; } static svn_error_t * default_dir_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->dir_closed(relpath, NULL, right_source, dir_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_dir_deleted(const char *relpath, const svn_diff_source_t *left_source, /*const*/ apr_hash_t *left_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->dir_closed(relpath, left_source, NULL, dir_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_dir_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, const apr_array_header_t *prop_changes, void *dir_baton, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->dir_closed(relpath, left_source, right_source, dir_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_dir_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } static svn_error_t * default_file_opened(void **new_file_baton, svn_boolean_t *skip, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { *new_file_baton = dir_baton; return SVN_NO_ERROR; } static svn_error_t * default_file_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, const char *copyfrom_file, const char *right_file, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->file_closed(relpath, NULL, right_source, file_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_file_deleted(const char *relpath, const svn_diff_source_t *left_source, const char *left_file, /*const*/ apr_hash_t *left_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->file_closed(relpath, left_source, NULL, file_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_file_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const char *left_file, const char *right_file, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, svn_boolean_t file_modified, const apr_array_header_t *prop_changes, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { SVN_ERR(processor->file_closed(relpath, left_source, right_source, file_baton, processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * default_file_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } static svn_error_t * default_node_absent(const char *relpath, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } svn_diff_tree_processor_t * svn_diff__tree_processor_create(void *baton, apr_pool_t *result_pool) { tree_processor_t *wrapper; wrapper = apr_pcalloc(result_pool, sizeof(*wrapper)); wrapper->tp.baton = baton; wrapper->tp.dir_opened = default_dir_opened; wrapper->tp.dir_added = default_dir_added; wrapper->tp.dir_deleted = default_dir_deleted; wrapper->tp.dir_changed = default_dir_changed; wrapper->tp.dir_closed = default_dir_closed; wrapper->tp.file_opened = default_file_opened; wrapper->tp.file_added = default_file_added; wrapper->tp.file_deleted = default_file_deleted; wrapper->tp.file_changed = default_file_changed; wrapper->tp.file_closed = default_file_closed; wrapper->tp.node_absent = default_node_absent; return &wrapper->tp; } struct reverse_tree_baton_t { const svn_diff_tree_processor_t *processor; const char *prefix_relpath; }; static svn_error_t * reverse_dir_opened(void **new_dir_baton, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *parent_dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->dir_opened(new_dir_baton, skip, skip_children, relpath, right_source, left_source, NULL /* copyfrom */, parent_dir_baton, rb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_dir_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->dir_deleted(relpath, right_source, right_props, dir_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_dir_deleted(const char *relpath, const svn_diff_source_t *left_source, /*const*/ apr_hash_t *left_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->dir_added(relpath, NULL, left_source, NULL, left_props, dir_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_dir_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, const apr_array_header_t *prop_changes, void *dir_baton, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; apr_array_header_t *reversed_prop_changes = NULL; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); if (prop_changes) { SVN_ERR_ASSERT(left_props != NULL && right_props != NULL); SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props, scratch_pool)); } SVN_ERR(rb->processor->dir_changed(relpath, right_source, left_source, right_props, left_props, reversed_prop_changes, dir_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_dir_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->dir_closed(relpath, right_source, left_source, dir_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_file_opened(void **new_file_baton, svn_boolean_t *skip, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->file_opened(new_file_baton, skip, relpath, right_source, left_source, NULL /* copy_from */, dir_baton, rb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_file_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, const char *copyfrom_file, const char *right_file, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->file_deleted(relpath, right_source, right_file, right_props, file_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_file_deleted(const char *relpath, const svn_diff_source_t *left_source, const char *left_file, /*const*/ apr_hash_t *left_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->file_added(relpath, NULL /* copyfrom src */, left_source, NULL /* copyfrom file */, left_file, NULL /* copyfrom props */, left_props, file_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_file_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const char *left_file, const char *right_file, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, svn_boolean_t file_modified, const apr_array_header_t *prop_changes, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; apr_array_header_t *reversed_prop_changes = NULL; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); if (prop_changes) { SVN_ERR_ASSERT(left_props != NULL && right_props != NULL); SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props, scratch_pool)); } SVN_ERR(rb->processor->file_changed(relpath, right_source, left_source, right_file, left_file, right_props, left_props, file_modified, reversed_prop_changes, file_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_file_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->file_closed(relpath, right_source, left_source, file_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * reverse_node_absent(const char *relpath, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct reverse_tree_baton_t *rb = processor->baton; if (rb->prefix_relpath) relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool); SVN_ERR(rb->processor->node_absent(relpath, dir_baton, rb->processor, scratch_pool)); return SVN_NO_ERROR; } const svn_diff_tree_processor_t * svn_diff__tree_processor_reverse_create(const svn_diff_tree_processor_t * processor, const char *prefix_relpath, apr_pool_t *result_pool) { struct reverse_tree_baton_t *rb; svn_diff_tree_processor_t *reverse; rb = apr_pcalloc(result_pool, sizeof(*rb)); rb->processor = processor; if (prefix_relpath) rb->prefix_relpath = apr_pstrdup(result_pool, prefix_relpath); reverse = svn_diff__tree_processor_create(rb, result_pool); reverse->dir_opened = reverse_dir_opened; reverse->dir_added = reverse_dir_added; reverse->dir_deleted = reverse_dir_deleted; reverse->dir_changed = reverse_dir_changed; reverse->dir_closed = reverse_dir_closed; reverse->file_opened = reverse_file_opened; reverse->file_added = reverse_file_added; reverse->file_deleted = reverse_file_deleted; reverse->file_changed = reverse_file_changed; reverse->file_closed = reverse_file_closed; reverse->node_absent = reverse_node_absent; return reverse; } struct filter_tree_baton_t { const svn_diff_tree_processor_t *processor; const char *prefix_relpath; }; static svn_error_t * filter_dir_opened(void **new_dir_baton, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *parent_dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); if (! relpath) { /* Skip work for this, but NOT for DESCENDANTS */ *skip = TRUE; return SVN_NO_ERROR; } SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children, relpath, left_source, right_source, copyfrom_source, parent_dir_baton, fb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_dir_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->dir_added(relpath, copyfrom_source, right_source, copyfrom_props, right_props, dir_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_dir_deleted(const char *relpath, const svn_diff_source_t *left_source, /*const*/ apr_hash_t *left_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->dir_deleted(relpath, left_source, left_props, dir_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_dir_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, const apr_array_header_t *prop_changes, void *dir_baton, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->dir_changed(relpath, left_source, right_source, left_props, right_props, prop_changes, dir_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_dir_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->dir_closed(relpath, left_source, right_source, dir_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_file_opened(void **new_file_baton, svn_boolean_t *skip, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); if (! relpath) { *skip = TRUE; return SVN_NO_ERROR; } SVN_ERR(fb->processor->file_opened(new_file_baton, skip, relpath, left_source, right_source, copyfrom_source, dir_baton, fb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_file_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, const char *copyfrom_file, const char *right_file, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->file_added(relpath, copyfrom_source, right_source, copyfrom_file, right_file, copyfrom_props, right_props, file_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_file_deleted(const char *relpath, const svn_diff_source_t *left_source, const char *left_file, /*const*/ apr_hash_t *left_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->file_deleted(relpath, left_source, left_file, left_props, file_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_file_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const char *left_file, const char *right_file, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, svn_boolean_t file_modified, const apr_array_header_t *prop_changes, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->file_changed(relpath, left_source, right_source, left_file, right_file, left_props, right_props, file_modified, prop_changes, file_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_file_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->file_closed(relpath, left_source, right_source, file_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * filter_node_absent(const char *relpath, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct filter_tree_baton_t *fb = processor->baton; relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath); assert(relpath != NULL); /* Driver error */ SVN_ERR(fb->processor->node_absent(relpath, dir_baton, fb->processor, scratch_pool)); return SVN_NO_ERROR; } const svn_diff_tree_processor_t * svn_diff__tree_processor_filter_create(const svn_diff_tree_processor_t * processor, const char *prefix_relpath, apr_pool_t *result_pool) { struct filter_tree_baton_t *fb; svn_diff_tree_processor_t *filter; fb = apr_pcalloc(result_pool, sizeof(*fb)); fb->processor = processor; if (prefix_relpath) fb->prefix_relpath = apr_pstrdup(result_pool, prefix_relpath); filter = svn_diff__tree_processor_create(fb, result_pool); filter->dir_opened = filter_dir_opened; filter->dir_added = filter_dir_added; filter->dir_deleted = filter_dir_deleted; filter->dir_changed = filter_dir_changed; filter->dir_closed = filter_dir_closed; filter->file_opened = filter_file_opened; filter->file_added = filter_file_added; filter->file_deleted = filter_file_deleted; filter->file_changed = filter_file_changed; filter->file_closed = filter_file_closed; filter->node_absent = filter_node_absent; return filter; } struct copy_as_changed_baton_t { const svn_diff_tree_processor_t *processor; }; static svn_error_t * copy_as_changed_dir_opened(void **new_dir_baton, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *parent_dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; if (!left_source && copyfrom_source) { assert(right_source != NULL); left_source = copyfrom_source; copyfrom_source = NULL; } SVN_ERR(cb->processor->dir_opened(new_dir_baton, skip, skip_children, relpath, left_source, right_source, copyfrom_source, parent_dir_baton, cb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_dir_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; if (copyfrom_source) { apr_array_header_t *propchanges; SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props, scratch_pool)); SVN_ERR(cb->processor->dir_changed(relpath, copyfrom_source, right_source, copyfrom_props, right_props, propchanges, dir_baton, cb->processor, scratch_pool)); } else { SVN_ERR(cb->processor->dir_added(relpath, copyfrom_source, right_source, copyfrom_props, right_props, dir_baton, cb->processor, scratch_pool)); } return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_dir_deleted(const char *relpath, const svn_diff_source_t *left_source, /*const*/ apr_hash_t *left_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->dir_deleted(relpath, left_source, left_props, dir_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_dir_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, const apr_array_header_t *prop_changes, void *dir_baton, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->dir_changed(relpath, left_source, right_source, left_props, right_props, prop_changes, dir_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_dir_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->dir_closed(relpath, left_source, right_source, dir_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_file_opened(void **new_file_baton, svn_boolean_t *skip, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; if (!left_source && copyfrom_source) { assert(right_source != NULL); left_source = copyfrom_source; copyfrom_source = NULL; } SVN_ERR(cb->processor->file_opened(new_file_baton, skip, relpath, left_source, right_source, copyfrom_source, dir_baton, cb->processor, result_pool, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_file_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, const char *copyfrom_file, const char *right_file, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; if (copyfrom_source) { apr_array_header_t *propchanges; svn_boolean_t same; SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props, scratch_pool)); /* "" is sometimes a marker for just modified (E.g. no-textdeltas), and it is certainly not a file */ if (*copyfrom_file && *right_file) { SVN_ERR(svn_io_files_contents_same_p(&same, copyfrom_file, right_file, scratch_pool)); } else same = FALSE; SVN_ERR(cb->processor->file_changed(relpath, copyfrom_source, right_source, copyfrom_file, right_file, copyfrom_props, right_props, !same, propchanges, file_baton, cb->processor, scratch_pool)); } else { SVN_ERR(cb->processor->file_added(relpath, copyfrom_source, right_source, copyfrom_file, right_file, copyfrom_props, right_props, file_baton, cb->processor, scratch_pool)); } return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_file_deleted(const char *relpath, const svn_diff_source_t *left_source, const char *left_file, /*const*/ apr_hash_t *left_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->file_deleted(relpath, left_source, left_file, left_props, file_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_file_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const char *left_file, const char *right_file, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, svn_boolean_t file_modified, const apr_array_header_t *prop_changes, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->file_changed(relpath, left_source, right_source, left_file, right_file, left_props, right_props, file_modified, prop_changes, file_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_file_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->file_closed(relpath, left_source, right_source, file_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * copy_as_changed_node_absent(const char *relpath, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct copy_as_changed_baton_t *cb = processor->baton; SVN_ERR(cb->processor->node_absent(relpath, dir_baton, cb->processor, scratch_pool)); return SVN_NO_ERROR; } const svn_diff_tree_processor_t * svn_diff__tree_processor_copy_as_changed_create( const svn_diff_tree_processor_t * processor, apr_pool_t *result_pool) { struct copy_as_changed_baton_t *cb; svn_diff_tree_processor_t *filter; cb = apr_pcalloc(result_pool, sizeof(*cb)); cb->processor = processor; filter = svn_diff__tree_processor_create(cb, result_pool); filter->dir_opened = copy_as_changed_dir_opened; filter->dir_added = copy_as_changed_dir_added; filter->dir_deleted = copy_as_changed_dir_deleted; filter->dir_changed = copy_as_changed_dir_changed; filter->dir_closed = copy_as_changed_dir_closed; filter->file_opened = copy_as_changed_file_opened; filter->file_added = copy_as_changed_file_added; filter->file_deleted = copy_as_changed_file_deleted; filter->file_changed = copy_as_changed_file_changed; filter->file_closed = copy_as_changed_file_closed; filter->node_absent = copy_as_changed_node_absent; return filter; } /* Processor baton for the tee tree processor */ struct tee_baton_t { const svn_diff_tree_processor_t *p1; const svn_diff_tree_processor_t *p2; }; /* Wrapper baton for file and directory batons in the tee processor */ struct tee_node_baton_t { void *baton1; void *baton2; }; static svn_error_t * tee_dir_opened(void **new_dir_baton, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *parent_dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *pb = parent_dir_baton; struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb)); SVN_ERR(tb->p1->dir_opened(&(nb->baton1), skip, skip_children, relpath, left_source, right_source, copyfrom_source, pb ? pb->baton1 : NULL, tb->p1, result_pool, scratch_pool)); SVN_ERR(tb->p2->dir_opened(&(nb->baton2), skip, skip_children, relpath, left_source, right_source, copyfrom_source, pb ? pb->baton2 : NULL, tb->p2, result_pool, scratch_pool)); *new_dir_baton = nb; return SVN_NO_ERROR; } static svn_error_t * tee_dir_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *db = dir_baton; SVN_ERR(tb->p1->dir_added(relpath, copyfrom_source, right_source, copyfrom_props, right_props, db->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->dir_added(relpath, copyfrom_source, right_source, copyfrom_props, right_props, db->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_dir_deleted(const char *relpath, const svn_diff_source_t *left_source, /*const*/ apr_hash_t *left_props, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *db = dir_baton; SVN_ERR(tb->p1->dir_deleted(relpath, left_source, left_props, db->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->dir_deleted(relpath, left_source, left_props, db->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_dir_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, const apr_array_header_t *prop_changes, void *dir_baton, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *db = dir_baton; SVN_ERR(tb->p1->dir_changed(relpath, left_source, right_source, left_props, right_props, prop_changes, db->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->dir_changed(relpath, left_source, right_source, left_props, right_props, prop_changes, db->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_dir_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *db = dir_baton; SVN_ERR(tb->p1->dir_closed(relpath, left_source, right_source, db->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->dir_closed(relpath, left_source, right_source, db->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_file_opened(void **new_file_baton, svn_boolean_t *skip, const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *pb = dir_baton; struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb)); SVN_ERR(tb->p1->file_opened(&(nb->baton1), skip, relpath, left_source, right_source, copyfrom_source, pb ? pb->baton1 : NULL, tb->p1, result_pool, scratch_pool)); SVN_ERR(tb->p2->file_opened(&(nb->baton2), skip, relpath, left_source, right_source, copyfrom_source, pb ? pb->baton2 : NULL, tb->p2, result_pool, scratch_pool)); *new_file_baton = nb; return SVN_NO_ERROR; } static svn_error_t * tee_file_added(const char *relpath, const svn_diff_source_t *copyfrom_source, const svn_diff_source_t *right_source, const char *copyfrom_file, const char *right_file, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *fb = file_baton; SVN_ERR(tb->p1->file_added(relpath, copyfrom_source, right_source, copyfrom_file, right_file, copyfrom_props, right_props, fb->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->file_added(relpath, copyfrom_source, right_source, copyfrom_file, right_file, copyfrom_props, right_props, fb->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_file_deleted(const char *relpath, const svn_diff_source_t *left_source, const char *left_file, /*const*/ apr_hash_t *left_props, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *fb = file_baton; SVN_ERR(tb->p1->file_deleted(relpath, left_source, left_file, left_props, fb->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->file_deleted(relpath, left_source, left_file, left_props, fb->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_file_changed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, const char *left_file, const char *right_file, /*const*/ apr_hash_t *left_props, /*const*/ apr_hash_t *right_props, svn_boolean_t file_modified, const apr_array_header_t *prop_changes, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *fb = file_baton; SVN_ERR(tb->p1->file_changed(relpath, left_source, right_source, left_file, right_file, left_props, right_props, file_modified, prop_changes, fb->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->file_changed(relpath, left_source, right_source, left_file, right_file, left_props, right_props, file_modified, prop_changes, fb->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_file_closed(const char *relpath, const svn_diff_source_t *left_source, const svn_diff_source_t *right_source, void *file_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *fb = file_baton; SVN_ERR(tb->p1->file_closed(relpath, left_source, right_source, fb->baton1, tb->p1, scratch_pool)); SVN_ERR(tb->p2->file_closed(relpath, left_source, right_source, fb->baton2, tb->p2, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * tee_node_absent(const char *relpath, void *dir_baton, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { struct tee_baton_t *tb = processor->baton; struct tee_node_baton_t *db = dir_baton; SVN_ERR(tb->p1->node_absent(relpath, db ? db->baton1 : NULL, tb->p1, scratch_pool)); SVN_ERR(tb->p2->node_absent(relpath, db ? db->baton2 : NULL, tb->p2, scratch_pool)); return SVN_NO_ERROR; } const svn_diff_tree_processor_t * svn_diff__tree_processor_tee_create(const svn_diff_tree_processor_t *processor1, const svn_diff_tree_processor_t *processor2, apr_pool_t *result_pool) { struct tee_baton_t *tb = apr_pcalloc(result_pool, sizeof(*tb)); svn_diff_tree_processor_t *tee; tb->p1 = processor1; tb->p2 = processor2; tee = svn_diff__tree_processor_create(tb, result_pool); tee->dir_opened = tee_dir_opened; tee->dir_added = tee_dir_added; tee->dir_deleted = tee_dir_deleted; tee->dir_changed = tee_dir_changed; tee->dir_closed = tee_dir_closed; tee->file_opened = tee_file_opened; tee->file_added = tee_file_added; tee->file_deleted = tee_file_deleted; tee->file_changed = tee_file_changed; tee->file_closed = tee_file_closed; tee->node_absent = tee_node_absent; return tee; } svn_diff_source_t * svn_diff__source_create(svn_revnum_t revision, apr_pool_t *result_pool) { svn_diff_source_t *src = apr_pcalloc(result_pool, sizeof(*src)); src->revision = revision; return src; }