text: /*
   * 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 <apr.h>
  #include <apr_pools.h>
  #include <apr_general.h>
  
  #include <assert.h>
  
  #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"
  
  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)
  {
    svn_diff_tree_processor_t *tp = apr_pcalloc(result_pool, sizeof(*tp));
  
    tp->baton        = baton;
  
    tp->dir_opened   = default_dir_opened;
    tp->dir_added    = default_dir_added;
    tp->dir_deleted  = default_dir_deleted;
    tp->dir_changed  = default_dir_changed;
    tp->dir_closed   = default_dir_closed;
  
    tp->file_opened  = default_file_opened;
    tp->file_added   = default_file_added;
    tp->file_deleted = default_file_deleted;
    tp->file_changed = default_file_changed;
    tp->file_closed  = default_file_closed;
  
    tp->node_absent  = default_node_absent;
  
    return tp;
  }
  
  struct reverse_tree_baton_t
  {
    const svn_diff_tree_processor_t *processor;
  };
  
  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;
  
    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;
  
    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;
  
    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 (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;
  
    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;
  
    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;
  
    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;
  
    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 (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;
  
    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;
  
    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,
                                          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;
  
    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;
  }
  