]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_diff/diff_tree.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_diff / diff_tree.c
1 /*
2  * diff_tree.c :  default diff tree processor
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include <apr.h>
25 #include <apr_pools.h>
26 #include <apr_general.h>
27
28 #include <assert.h>
29
30 #include "svn_dirent_uri.h"
31 #include "svn_error.h"
32 #include "svn_io.h"
33 #include "svn_pools.h"
34 #include "svn_props.h"
35 #include "svn_types.h"
36
37 #include "private/svn_diff_tree.h"
38 #include "svn_private_config.h"
39
40 static svn_error_t *
41 default_dir_opened(void **new_dir_baton,
42                    svn_boolean_t *skip,
43                    svn_boolean_t *skip_children,
44                    const char *relpath,
45                    const svn_diff_source_t *left_source,
46                    const svn_diff_source_t *right_source,
47                    const svn_diff_source_t *copyfrom_source,
48                    void *parent_dir_baton,
49                    const svn_diff_tree_processor_t *processor,
50                    apr_pool_t *result_pool,
51                    apr_pool_t *scratch_pool)
52 {
53   *new_dir_baton = NULL;
54   return SVN_NO_ERROR;
55 }
56
57 static svn_error_t *
58 default_dir_added(const char *relpath,
59                   const svn_diff_source_t *copyfrom_source,
60                   const svn_diff_source_t *right_source,
61                   /*const*/ apr_hash_t *copyfrom_props,
62                   /*const*/ apr_hash_t *right_props,
63                   void *dir_baton,
64                   const svn_diff_tree_processor_t *processor,
65                   apr_pool_t *scratch_pool)
66 {
67   SVN_ERR(processor->dir_closed(relpath, NULL, right_source,
68                                 dir_baton, processor,
69                                 scratch_pool));
70
71   return SVN_NO_ERROR;
72 }
73
74 static svn_error_t *
75 default_dir_deleted(const char *relpath,
76                     const svn_diff_source_t *left_source,
77                     /*const*/ apr_hash_t *left_props,
78                     void *dir_baton,
79                     const svn_diff_tree_processor_t *processor,
80                     apr_pool_t *scratch_pool)
81 {
82   SVN_ERR(processor->dir_closed(relpath, left_source, NULL,
83                                 dir_baton, processor,
84                                 scratch_pool));
85   return SVN_NO_ERROR;
86 }
87
88 static svn_error_t *
89 default_dir_changed(const char *relpath,
90                     const svn_diff_source_t *left_source,
91                     const svn_diff_source_t *right_source,
92                     /*const*/ apr_hash_t *left_props,
93                     /*const*/ apr_hash_t *right_props,
94                     const apr_array_header_t *prop_changes,
95                     void *dir_baton,
96                     const struct svn_diff_tree_processor_t *processor,
97                     apr_pool_t *scratch_pool)
98 {
99   SVN_ERR(processor->dir_closed(relpath,
100                                 left_source, right_source,
101                                 dir_baton,
102                                 processor, scratch_pool));
103   return SVN_NO_ERROR;
104 }
105
106 static svn_error_t *
107 default_dir_closed(const char *relpath,
108                    const svn_diff_source_t *left_source,
109                    const svn_diff_source_t *right_source,
110                    void *dir_baton,
111                    const svn_diff_tree_processor_t *processor,
112                    apr_pool_t *scratch_pool)
113 {
114   return SVN_NO_ERROR;
115 }
116
117 static svn_error_t *
118 default_file_opened(void **new_file_baton,
119                     svn_boolean_t *skip,
120                     const char *relpath,
121                     const svn_diff_source_t *left_source,
122                     const svn_diff_source_t *right_source,
123                     const svn_diff_source_t *copyfrom_source,
124                     void *dir_baton,
125                     const svn_diff_tree_processor_t *processor,
126                     apr_pool_t *result_pool,
127                     apr_pool_t *scratch_pool)
128 {
129   *new_file_baton = dir_baton;
130   return SVN_NO_ERROR;
131 }
132
133 static svn_error_t *
134 default_file_added(const char *relpath,
135                    const svn_diff_source_t *copyfrom_source,
136                    const svn_diff_source_t *right_source,
137                    const char *copyfrom_file,
138                    const char *right_file,
139                    /*const*/ apr_hash_t *copyfrom_props,
140                    /*const*/ apr_hash_t *right_props,
141                    void *file_baton,
142                    const svn_diff_tree_processor_t *processor,
143                    apr_pool_t *scratch_pool)
144 {
145   SVN_ERR(processor->file_closed(relpath,
146                                  NULL, right_source,
147                                  file_baton, processor, scratch_pool));
148   return SVN_NO_ERROR;
149 }
150
151 static svn_error_t *
152 default_file_deleted(const char *relpath,
153                      const svn_diff_source_t *left_source,
154                      const char *left_file,
155                      /*const*/ apr_hash_t *left_props,
156                      void *file_baton,
157                      const svn_diff_tree_processor_t *processor,
158                      apr_pool_t *scratch_pool)
159 {
160   SVN_ERR(processor->file_closed(relpath,
161                                  left_source, NULL,
162                                  file_baton, processor, scratch_pool));
163   return SVN_NO_ERROR;
164 }
165
166 static svn_error_t *
167 default_file_changed(const char *relpath,
168                      const svn_diff_source_t *left_source,
169                      const svn_diff_source_t *right_source,
170                      const char *left_file,
171                      const char *right_file,
172                      /*const*/ apr_hash_t *left_props,
173                      /*const*/ apr_hash_t *right_props,
174                      svn_boolean_t file_modified,
175                      const apr_array_header_t *prop_changes,
176                      void *file_baton,
177                      const svn_diff_tree_processor_t *processor,
178                      apr_pool_t *scratch_pool)
179 {
180   SVN_ERR(processor->file_closed(relpath,
181                                  left_source, right_source,
182                                  file_baton, processor, scratch_pool));
183   return SVN_NO_ERROR;
184 }
185
186 static svn_error_t *
187 default_file_closed(const char *relpath,
188                     const svn_diff_source_t *left_source,
189                     const svn_diff_source_t *right_source,
190                     void *file_baton,
191                     const svn_diff_tree_processor_t *processor,
192                     apr_pool_t *scratch_pool)
193 {
194   return SVN_NO_ERROR;
195 }
196
197 static svn_error_t *
198 default_node_absent(const char *relpath,
199                     void *dir_baton,
200                     const svn_diff_tree_processor_t *processor,
201                     apr_pool_t *scratch_pool)
202 {
203   return SVN_NO_ERROR;
204 }
205
206 svn_diff_tree_processor_t *
207 svn_diff__tree_processor_create(void *baton,
208                                 apr_pool_t *result_pool)
209 {
210   svn_diff_tree_processor_t *tp = apr_pcalloc(result_pool, sizeof(*tp));
211
212   tp->baton        = baton;
213
214   tp->dir_opened   = default_dir_opened;
215   tp->dir_added    = default_dir_added;
216   tp->dir_deleted  = default_dir_deleted;
217   tp->dir_changed  = default_dir_changed;
218   tp->dir_closed   = default_dir_closed;
219
220   tp->file_opened  = default_file_opened;
221   tp->file_added   = default_file_added;
222   tp->file_deleted = default_file_deleted;
223   tp->file_changed = default_file_changed;
224   tp->file_closed  = default_file_closed;
225
226   tp->node_absent  = default_node_absent;
227
228   return tp;
229 }
230
231 struct reverse_tree_baton_t
232 {
233   const svn_diff_tree_processor_t *processor;
234 };
235
236 static svn_error_t *
237 reverse_dir_opened(void **new_dir_baton,
238                    svn_boolean_t *skip,
239                    svn_boolean_t *skip_children,
240                    const char *relpath,
241                    const svn_diff_source_t *left_source,
242                    const svn_diff_source_t *right_source,
243                    const svn_diff_source_t *copyfrom_source,
244                    void *parent_dir_baton,
245                    const svn_diff_tree_processor_t *processor,
246                    apr_pool_t *result_pool,
247                    apr_pool_t *scratch_pool)
248 {
249   struct reverse_tree_baton_t *rb = processor->baton;
250
251   SVN_ERR(rb->processor->dir_opened(new_dir_baton, skip, skip_children,
252                                     relpath,
253                                     right_source, left_source,
254                                     NULL /* copyfrom */,
255                                     parent_dir_baton,
256                                     rb->processor,
257                                     result_pool, scratch_pool));
258   return SVN_NO_ERROR;
259 }
260
261 static svn_error_t *
262 reverse_dir_added(const char *relpath,
263                   const svn_diff_source_t *copyfrom_source,
264                   const svn_diff_source_t *right_source,
265                   /*const*/ apr_hash_t *copyfrom_props,
266                   /*const*/ apr_hash_t *right_props,
267                   void *dir_baton,
268                   const svn_diff_tree_processor_t *processor,
269                   apr_pool_t *scratch_pool)
270 {
271   struct reverse_tree_baton_t *rb = processor->baton;
272
273   SVN_ERR(rb->processor->dir_deleted(relpath,
274                                      right_source,
275                                      right_props,
276                                      dir_baton,
277                                      rb->processor,
278                                      scratch_pool));
279
280   return SVN_NO_ERROR;
281 }
282
283 static svn_error_t *
284 reverse_dir_deleted(const char *relpath,
285                     const svn_diff_source_t *left_source,
286                     /*const*/ apr_hash_t *left_props,
287                     void *dir_baton,
288                     const svn_diff_tree_processor_t *processor,
289                     apr_pool_t *scratch_pool)
290 {
291   struct reverse_tree_baton_t *rb = processor->baton;
292
293   SVN_ERR(rb->processor->dir_added(relpath,
294                                    NULL,
295                                    left_source,
296                                    NULL,
297                                    left_props,
298                                    dir_baton,
299                                    rb->processor,
300                                    scratch_pool));
301   return SVN_NO_ERROR;
302 }
303
304 static svn_error_t *
305 reverse_dir_changed(const char *relpath,
306                     const svn_diff_source_t *left_source,
307                     const svn_diff_source_t *right_source,
308                     /*const*/ apr_hash_t *left_props,
309                     /*const*/ apr_hash_t *right_props,
310                     const apr_array_header_t *prop_changes,
311                     void *dir_baton,
312                     const struct svn_diff_tree_processor_t *processor,
313                     apr_pool_t *scratch_pool)
314 {
315   struct reverse_tree_baton_t *rb = processor->baton;
316   apr_array_header_t *reversed_prop_changes = NULL;
317
318   if (prop_changes)
319     {
320       SVN_ERR_ASSERT(left_props != NULL && right_props != NULL);
321       SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props,
322                              scratch_pool));
323     }
324
325   SVN_ERR(rb->processor->dir_changed(relpath,
326                                      right_source,
327                                      left_source,
328                                      right_props,
329                                      left_props,
330                                      reversed_prop_changes,
331                                      dir_baton,
332                                      rb->processor,
333                                      scratch_pool));
334   return SVN_NO_ERROR;
335 }
336
337 static svn_error_t *
338 reverse_dir_closed(const char *relpath,
339                    const svn_diff_source_t *left_source,
340                    const svn_diff_source_t *right_source,
341                    void *dir_baton,
342                    const svn_diff_tree_processor_t *processor,
343                    apr_pool_t *scratch_pool)
344 {
345   struct reverse_tree_baton_t *rb = processor->baton;
346
347   SVN_ERR(rb->processor->dir_closed(relpath,
348                                     right_source,
349                                     left_source,
350                                     dir_baton,
351                                     rb->processor,
352                                     scratch_pool));
353   return SVN_NO_ERROR;
354 }
355
356 static svn_error_t *
357 reverse_file_opened(void **new_file_baton,
358                     svn_boolean_t *skip,
359                     const char *relpath,
360                     const svn_diff_source_t *left_source,
361                     const svn_diff_source_t *right_source,
362                     const svn_diff_source_t *copyfrom_source,
363                     void *dir_baton,
364                     const svn_diff_tree_processor_t *processor,
365                     apr_pool_t *result_pool,
366                     apr_pool_t *scratch_pool)
367 {
368   struct reverse_tree_baton_t *rb = processor->baton;
369
370   SVN_ERR(rb->processor->file_opened(new_file_baton,
371                                      skip,
372                                      relpath,
373                                      right_source,
374                                      left_source,
375                                      NULL /* copy_from */,
376                                      dir_baton,
377                                      rb->processor,
378                                      result_pool,
379                                      scratch_pool));
380   return SVN_NO_ERROR;
381 }
382
383 static svn_error_t *
384 reverse_file_added(const char *relpath,
385                    const svn_diff_source_t *copyfrom_source,
386                    const svn_diff_source_t *right_source,
387                    const char *copyfrom_file,
388                    const char *right_file,
389                    /*const*/ apr_hash_t *copyfrom_props,
390                    /*const*/ apr_hash_t *right_props,
391                    void *file_baton,
392                    const svn_diff_tree_processor_t *processor,
393                    apr_pool_t *scratch_pool)
394 {
395   struct reverse_tree_baton_t *rb = processor->baton;
396
397   SVN_ERR(rb->processor->file_deleted(relpath,
398                                       right_source,
399                                       right_file,
400                                       right_props,
401                                       file_baton,
402                                       rb->processor,
403                                       scratch_pool));
404   return SVN_NO_ERROR;
405 }
406
407 static svn_error_t *
408 reverse_file_deleted(const char *relpath,
409                      const svn_diff_source_t *left_source,
410                      const char *left_file,
411                      /*const*/ apr_hash_t *left_props,
412                      void *file_baton,
413                      const svn_diff_tree_processor_t *processor,
414                      apr_pool_t *scratch_pool)
415 {
416   struct reverse_tree_baton_t *rb = processor->baton;
417
418   SVN_ERR(rb->processor->file_added(relpath,
419                                     NULL /* copyfrom src */,
420                                     left_source,
421                                     NULL /* copyfrom file */,
422                                     left_file,
423                                     NULL /* copyfrom props */,
424                                     left_props,
425                                     file_baton,
426                                     rb->processor,
427                                     scratch_pool));
428   return SVN_NO_ERROR;
429 }
430
431 static svn_error_t *
432 reverse_file_changed(const char *relpath,
433                      const svn_diff_source_t *left_source,
434                      const svn_diff_source_t *right_source,
435                      const char *left_file,
436                      const char *right_file,
437                      /*const*/ apr_hash_t *left_props,
438                      /*const*/ apr_hash_t *right_props,
439                      svn_boolean_t file_modified,
440                      const apr_array_header_t *prop_changes,
441                      void *file_baton,
442                      const svn_diff_tree_processor_t *processor,
443                      apr_pool_t *scratch_pool)
444 {
445   struct reverse_tree_baton_t *rb = processor->baton;
446   apr_array_header_t *reversed_prop_changes = NULL;
447
448   if (prop_changes)
449     {
450       SVN_ERR_ASSERT(left_props != NULL && right_props != NULL);
451       SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props,
452                              scratch_pool));
453     }
454
455   SVN_ERR(rb->processor->file_changed(relpath,
456                                       right_source,
457                                       left_source,
458                                       right_file,
459                                       left_file,
460                                       right_props,
461                                       left_props,
462                                       file_modified,
463                                       reversed_prop_changes,
464                                       file_baton,
465                                       rb->processor,
466                                       scratch_pool));
467   return SVN_NO_ERROR;
468 }
469
470 static svn_error_t *
471 reverse_file_closed(const char *relpath,
472                     const svn_diff_source_t *left_source,
473                     const svn_diff_source_t *right_source,
474                     void *file_baton,
475                     const svn_diff_tree_processor_t *processor,
476                     apr_pool_t *scratch_pool)
477 {
478   struct reverse_tree_baton_t *rb = processor->baton;
479
480   SVN_ERR(rb->processor->file_closed(relpath,
481                                      right_source,
482                                      left_source,
483                                      file_baton,
484                                      rb->processor,
485                                      scratch_pool));
486
487   return SVN_NO_ERROR;
488 }
489
490 static svn_error_t *
491 reverse_node_absent(const char *relpath,
492                     void *dir_baton,
493                     const svn_diff_tree_processor_t *processor,
494                     apr_pool_t *scratch_pool)
495 {
496   struct reverse_tree_baton_t *rb = processor->baton;
497
498   SVN_ERR(rb->processor->node_absent(relpath,
499                                     dir_baton,
500                                     rb->processor,
501                                     scratch_pool));
502   return SVN_NO_ERROR;
503 }
504
505
506 const svn_diff_tree_processor_t *
507 svn_diff__tree_processor_reverse_create(const svn_diff_tree_processor_t * processor,
508                                         apr_pool_t *result_pool)
509 {
510   struct reverse_tree_baton_t *rb;
511   svn_diff_tree_processor_t *reverse;
512
513   rb = apr_pcalloc(result_pool, sizeof(*rb));
514   rb->processor = processor;
515
516   reverse = svn_diff__tree_processor_create(rb, result_pool);
517
518   reverse->dir_opened   = reverse_dir_opened;
519   reverse->dir_added    = reverse_dir_added;
520   reverse->dir_deleted  = reverse_dir_deleted;
521   reverse->dir_changed  = reverse_dir_changed;
522   reverse->dir_closed   = reverse_dir_closed;
523
524   reverse->file_opened   = reverse_file_opened;
525   reverse->file_added    = reverse_file_added;
526   reverse->file_deleted  = reverse_file_deleted;
527   reverse->file_changed  = reverse_file_changed;
528   reverse->file_closed   = reverse_file_closed;
529
530   reverse->node_absent   = reverse_node_absent;
531
532   return reverse;
533 }
534
535 struct filter_tree_baton_t
536 {
537   const svn_diff_tree_processor_t *processor;
538   const char *prefix_relpath;
539 };
540
541 static svn_error_t *
542 filter_dir_opened(void **new_dir_baton,
543                   svn_boolean_t *skip,
544                   svn_boolean_t *skip_children,
545                   const char *relpath,
546                   const svn_diff_source_t *left_source,
547                   const svn_diff_source_t *right_source,
548                   const svn_diff_source_t *copyfrom_source,
549                   void *parent_dir_baton,
550                   const svn_diff_tree_processor_t *processor,
551                   apr_pool_t *result_pool,
552                   apr_pool_t *scratch_pool)
553 {
554   struct filter_tree_baton_t *fb = processor->baton;
555
556   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
557
558   if (! relpath)
559     {
560       /* Skip work for this, but NOT for DESCENDANTS */
561       *skip = TRUE;
562       return SVN_NO_ERROR;
563     }
564
565   SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
566                                     relpath,
567                                     left_source, right_source,
568                                     copyfrom_source,
569                                     parent_dir_baton,
570                                     fb->processor,
571                                     result_pool, scratch_pool));
572   return SVN_NO_ERROR;
573 }
574
575 static svn_error_t *
576 filter_dir_added(const char *relpath,
577                  const svn_diff_source_t *copyfrom_source,
578                  const svn_diff_source_t *right_source,
579                  /*const*/ apr_hash_t *copyfrom_props,
580                  /*const*/ apr_hash_t *right_props,
581                  void *dir_baton,
582                  const svn_diff_tree_processor_t *processor,
583                  apr_pool_t *scratch_pool)
584 {
585   struct filter_tree_baton_t *fb = processor->baton;
586
587   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
588   assert(relpath != NULL); /* Driver error */
589
590   SVN_ERR(fb->processor->dir_added(relpath,
591                                    copyfrom_source,
592                                    right_source,
593                                    copyfrom_props,
594                                    right_props,
595                                    dir_baton,
596                                    fb->processor,
597                                    scratch_pool));
598
599   return SVN_NO_ERROR;
600 }
601
602 static svn_error_t *
603 filter_dir_deleted(const char *relpath,
604                    const svn_diff_source_t *left_source,
605                    /*const*/ apr_hash_t *left_props,
606                    void *dir_baton,
607                    const svn_diff_tree_processor_t *processor,
608                    apr_pool_t *scratch_pool)
609 {
610   struct filter_tree_baton_t *fb = processor->baton;
611
612   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
613   assert(relpath != NULL); /* Driver error */
614
615   SVN_ERR(fb->processor->dir_deleted(relpath,
616                                      left_source,
617                                      left_props,
618                                      dir_baton,
619                                      fb->processor,
620                                      scratch_pool));
621
622   return SVN_NO_ERROR;
623 }
624
625 static svn_error_t *
626 filter_dir_changed(const char *relpath,
627                    const svn_diff_source_t *left_source,
628                    const svn_diff_source_t *right_source,
629                    /*const*/ apr_hash_t *left_props,
630                    /*const*/ apr_hash_t *right_props,
631                    const apr_array_header_t *prop_changes,
632                    void *dir_baton,
633                    const struct svn_diff_tree_processor_t *processor,
634                    apr_pool_t *scratch_pool)
635 {
636   struct filter_tree_baton_t *fb = processor->baton;
637
638   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
639   assert(relpath != NULL); /* Driver error */
640
641   SVN_ERR(fb->processor->dir_changed(relpath,
642                                      left_source,
643                                      right_source,
644                                      left_props,
645                                      right_props,
646                                      prop_changes,
647                                      dir_baton,
648                                      fb->processor,
649                                      scratch_pool));
650   return SVN_NO_ERROR;
651 }
652
653 static svn_error_t *
654 filter_dir_closed(const char *relpath,
655                   const svn_diff_source_t *left_source,
656                   const svn_diff_source_t *right_source,
657                   void *dir_baton,
658                   const svn_diff_tree_processor_t *processor,
659                   apr_pool_t *scratch_pool)
660 {
661   struct filter_tree_baton_t *fb = processor->baton;
662
663   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
664   assert(relpath != NULL); /* Driver error */
665
666   SVN_ERR(fb->processor->dir_closed(relpath,
667                                     left_source,
668                                     right_source,
669                                     dir_baton,
670                                     fb->processor,
671                                     scratch_pool));
672   return SVN_NO_ERROR;
673 }
674
675 static svn_error_t *
676 filter_file_opened(void **new_file_baton,
677                    svn_boolean_t *skip,
678                    const char *relpath,
679                    const svn_diff_source_t *left_source,
680                    const svn_diff_source_t *right_source,
681                    const svn_diff_source_t *copyfrom_source,
682                    void *dir_baton,
683                    const svn_diff_tree_processor_t *processor,
684                    apr_pool_t *result_pool,
685                    apr_pool_t *scratch_pool)
686 {
687   struct filter_tree_baton_t *fb = processor->baton;
688
689   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
690
691   if (! relpath)
692     {
693       *skip = TRUE;
694       return SVN_NO_ERROR;
695     }
696
697   SVN_ERR(fb->processor->file_opened(new_file_baton,
698                                      skip,
699                                      relpath,
700                                      left_source,
701                                      right_source,
702                                      copyfrom_source,
703                                      dir_baton,
704                                      fb->processor,
705                                      result_pool,
706                                      scratch_pool));
707   return SVN_NO_ERROR;
708 }
709
710 static svn_error_t *
711 filter_file_added(const char *relpath,
712                   const svn_diff_source_t *copyfrom_source,
713                   const svn_diff_source_t *right_source,
714                   const char *copyfrom_file,
715                   const char *right_file,
716                   /*const*/ apr_hash_t *copyfrom_props,
717                   /*const*/ apr_hash_t *right_props,
718                   void *file_baton,
719                   const svn_diff_tree_processor_t *processor,
720                   apr_pool_t *scratch_pool)
721 {
722   struct filter_tree_baton_t *fb = processor->baton;
723
724   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
725   assert(relpath != NULL); /* Driver error */
726
727   SVN_ERR(fb->processor->file_added(relpath,
728                                     copyfrom_source,
729                                     right_source,
730                                     copyfrom_file,
731                                     right_file,
732                                     copyfrom_props,
733                                     right_props,
734                                     file_baton,
735                                     fb->processor,
736                                     scratch_pool));
737   return SVN_NO_ERROR;
738 }
739
740 static svn_error_t *
741 filter_file_deleted(const char *relpath,
742                     const svn_diff_source_t *left_source,
743                     const char *left_file,
744                     /*const*/ apr_hash_t *left_props,
745                     void *file_baton,
746                     const svn_diff_tree_processor_t *processor,
747                     apr_pool_t *scratch_pool)
748 {
749   struct filter_tree_baton_t *fb = processor->baton;
750
751   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
752   assert(relpath != NULL); /* Driver error */
753
754   SVN_ERR(fb->processor->file_deleted(relpath,
755                                       left_source,
756                                       left_file,
757                                       left_props,
758                                       file_baton,
759                                       fb->processor,
760                                       scratch_pool));
761
762   return SVN_NO_ERROR;
763 }
764
765 static svn_error_t *
766 filter_file_changed(const char *relpath,
767                     const svn_diff_source_t *left_source,
768                     const svn_diff_source_t *right_source,
769                     const char *left_file,
770                     const char *right_file,
771                     /*const*/ apr_hash_t *left_props,
772                     /*const*/ apr_hash_t *right_props,
773                     svn_boolean_t file_modified,
774                     const apr_array_header_t *prop_changes,
775                     void *file_baton,
776                     const svn_diff_tree_processor_t *processor,
777                     apr_pool_t *scratch_pool)
778 {
779   struct filter_tree_baton_t *fb = processor->baton;
780
781   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
782   assert(relpath != NULL); /* Driver error */
783
784   SVN_ERR(fb->processor->file_changed(relpath,
785                                       left_source,
786                                       right_source,
787                                       left_file,
788                                       right_file,
789                                       left_props,
790                                       right_props,
791                                       file_modified,
792                                       prop_changes,
793                                       file_baton,
794                                       fb->processor,
795                                       scratch_pool));
796   return SVN_NO_ERROR;
797 }
798
799 static svn_error_t *
800 filter_file_closed(const char *relpath,
801                    const svn_diff_source_t *left_source,
802                    const svn_diff_source_t *right_source,
803                    void *file_baton,
804                    const svn_diff_tree_processor_t *processor,
805                    apr_pool_t *scratch_pool)
806 {
807   struct filter_tree_baton_t *fb = processor->baton;
808
809   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
810   assert(relpath != NULL); /* Driver error */
811
812   SVN_ERR(fb->processor->file_closed(relpath,
813                                      left_source,
814                                      right_source,
815                                      file_baton,
816                                      fb->processor,
817                                      scratch_pool));
818
819   return SVN_NO_ERROR;
820 }
821
822 static svn_error_t *
823 filter_node_absent(const char *relpath,
824                    void *dir_baton,
825                    const svn_diff_tree_processor_t *processor,
826                    apr_pool_t *scratch_pool)
827 {
828   struct filter_tree_baton_t *fb = processor->baton;
829
830   relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
831   assert(relpath != NULL); /* Driver error */
832
833   SVN_ERR(fb->processor->node_absent(relpath,
834                                     dir_baton,
835                                     fb->processor,
836                                     scratch_pool));
837   return SVN_NO_ERROR;
838 }
839
840
841 const svn_diff_tree_processor_t *
842 svn_diff__tree_processor_filter_create(const svn_diff_tree_processor_t * processor,
843                                         const char *prefix_relpath,
844                                         apr_pool_t *result_pool)
845 {
846   struct filter_tree_baton_t *fb;
847   svn_diff_tree_processor_t *filter;
848
849   fb = apr_pcalloc(result_pool, sizeof(*fb));
850   fb->processor = processor;
851   if (prefix_relpath)
852     fb->prefix_relpath = apr_pstrdup(result_pool, prefix_relpath);
853
854   filter = svn_diff__tree_processor_create(fb, result_pool);
855
856   filter->dir_opened   = filter_dir_opened;
857   filter->dir_added    = filter_dir_added;
858   filter->dir_deleted  = filter_dir_deleted;
859   filter->dir_changed  = filter_dir_changed;
860   filter->dir_closed   = filter_dir_closed;
861
862   filter->file_opened   = filter_file_opened;
863   filter->file_added    = filter_file_added;
864   filter->file_deleted  = filter_file_deleted;
865   filter->file_changed  = filter_file_changed;
866   filter->file_closed   = filter_file_closed;
867
868   filter->node_absent   = filter_node_absent;
869
870   return filter;
871 }
872
873 struct copy_as_changed_baton_t
874 {
875   const svn_diff_tree_processor_t *processor;
876 };
877
878 static svn_error_t *
879 copy_as_changed_dir_opened(void **new_dir_baton,
880                            svn_boolean_t *skip,
881                            svn_boolean_t *skip_children,
882                            const char *relpath,
883                            const svn_diff_source_t *left_source,
884                            const svn_diff_source_t *right_source,
885                            const svn_diff_source_t *copyfrom_source,
886                            void *parent_dir_baton,
887                            const svn_diff_tree_processor_t *processor,
888                            apr_pool_t *result_pool,
889                            apr_pool_t *scratch_pool)
890 {
891   struct copy_as_changed_baton_t *cb = processor->baton;
892
893   if (!left_source && copyfrom_source)
894     {
895       assert(right_source != NULL);
896
897       left_source = copyfrom_source;
898       copyfrom_source = NULL;
899     }
900
901   SVN_ERR(cb->processor->dir_opened(new_dir_baton, skip, skip_children,
902                                     relpath,
903                                     left_source, right_source,
904                                     copyfrom_source,
905                                     parent_dir_baton,
906                                     cb->processor,
907                                     result_pool, scratch_pool));
908   return SVN_NO_ERROR;
909 }
910
911 static svn_error_t *
912 copy_as_changed_dir_added(const char *relpath,
913                           const svn_diff_source_t *copyfrom_source,
914                           const svn_diff_source_t *right_source,
915                           /*const*/ apr_hash_t *copyfrom_props,
916                           /*const*/ apr_hash_t *right_props,
917                           void *dir_baton,
918                           const svn_diff_tree_processor_t *processor,
919                           apr_pool_t *scratch_pool)
920 {
921   struct copy_as_changed_baton_t *cb = processor->baton;
922
923   if (copyfrom_source)
924     {
925       apr_array_header_t *propchanges;
926       SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props,
927                              scratch_pool));
928       SVN_ERR(cb->processor->dir_changed(relpath,
929                                          copyfrom_source,
930                                          right_source,
931                                          copyfrom_props,
932                                          right_props,
933                                          propchanges,
934                                          dir_baton,
935                                          cb->processor,
936                                          scratch_pool));
937     }
938   else
939     {
940       SVN_ERR(cb->processor->dir_added(relpath,
941                                        copyfrom_source,
942                                        right_source,
943                                        copyfrom_props,
944                                        right_props,
945                                        dir_baton,
946                                        cb->processor,
947                                        scratch_pool));
948     }
949
950   return SVN_NO_ERROR;
951 }
952
953 static svn_error_t *
954 copy_as_changed_dir_deleted(const char *relpath,
955                             const svn_diff_source_t *left_source,
956                             /*const*/ apr_hash_t *left_props,
957                             void *dir_baton,
958                             const svn_diff_tree_processor_t *processor,
959                             apr_pool_t *scratch_pool)
960 {
961   struct copy_as_changed_baton_t *cb = processor->baton;
962
963   SVN_ERR(cb->processor->dir_deleted(relpath,
964                                      left_source,
965                                      left_props,
966                                      dir_baton,
967                                      cb->processor,
968                                      scratch_pool));
969
970   return SVN_NO_ERROR;
971 }
972
973 static svn_error_t *
974 copy_as_changed_dir_changed(const char *relpath,
975                             const svn_diff_source_t *left_source,
976                             const svn_diff_source_t *right_source,
977                             /*const*/ apr_hash_t *left_props,
978                             /*const*/ apr_hash_t *right_props,
979                             const apr_array_header_t *prop_changes,
980                             void *dir_baton,
981                             const struct svn_diff_tree_processor_t *processor,
982                             apr_pool_t *scratch_pool)
983 {
984   struct copy_as_changed_baton_t *cb = processor->baton;
985
986   SVN_ERR(cb->processor->dir_changed(relpath,
987                                      left_source,
988                                      right_source,
989                                      left_props,
990                                      right_props,
991                                      prop_changes,
992                                      dir_baton,
993                                      cb->processor,
994                                      scratch_pool));
995   return SVN_NO_ERROR;
996 }
997
998 static svn_error_t *
999 copy_as_changed_dir_closed(const char *relpath,
1000                            const svn_diff_source_t *left_source,
1001                            const svn_diff_source_t *right_source,
1002                            void *dir_baton,
1003                            const svn_diff_tree_processor_t *processor,
1004                            apr_pool_t *scratch_pool)
1005 {
1006   struct copy_as_changed_baton_t *cb = processor->baton;
1007
1008   SVN_ERR(cb->processor->dir_closed(relpath,
1009                                     left_source,
1010                                     right_source,
1011                                     dir_baton,
1012                                     cb->processor,
1013                                     scratch_pool));
1014   return SVN_NO_ERROR;
1015 }
1016
1017 static svn_error_t *
1018 copy_as_changed_file_opened(void **new_file_baton,
1019                             svn_boolean_t *skip,
1020                             const char *relpath,
1021                             const svn_diff_source_t *left_source,
1022                             const svn_diff_source_t *right_source,
1023                             const svn_diff_source_t *copyfrom_source,
1024                             void *dir_baton,
1025                             const svn_diff_tree_processor_t *processor,
1026                             apr_pool_t *result_pool,
1027                             apr_pool_t *scratch_pool)
1028 {
1029   struct copy_as_changed_baton_t *cb = processor->baton;
1030
1031   if (!left_source && copyfrom_source)
1032     {
1033       assert(right_source != NULL);
1034
1035       left_source = copyfrom_source;
1036       copyfrom_source = NULL;
1037     }
1038
1039   SVN_ERR(cb->processor->file_opened(new_file_baton,
1040                                      skip,
1041                                      relpath,
1042                                      left_source,
1043                                      right_source,
1044                                      copyfrom_source,
1045                                      dir_baton,
1046                                      cb->processor,
1047                                      result_pool,
1048                                      scratch_pool));
1049   return SVN_NO_ERROR;
1050 }
1051
1052 static svn_error_t *
1053 copy_as_changed_file_added(const char *relpath,
1054                            const svn_diff_source_t *copyfrom_source,
1055                            const svn_diff_source_t *right_source,
1056                            const char *copyfrom_file,
1057                            const char *right_file,
1058                            /*const*/ apr_hash_t *copyfrom_props,
1059                            /*const*/ apr_hash_t *right_props,
1060                            void *file_baton,
1061                            const svn_diff_tree_processor_t *processor,
1062                            apr_pool_t *scratch_pool)
1063 {
1064   struct copy_as_changed_baton_t *cb = processor->baton;
1065
1066   if (copyfrom_source)
1067     {
1068       apr_array_header_t *propchanges;
1069       svn_boolean_t same;
1070       SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props,
1071                              scratch_pool));
1072
1073       /* "" is sometimes a marker for just modified (E.g. no-textdeltas),
1074          and it is certainly not a file */
1075       if (*copyfrom_file && *right_file)
1076         {
1077           SVN_ERR(svn_io_files_contents_same_p(&same, copyfrom_file,
1078                                                right_file, scratch_pool));
1079         }
1080       else
1081         same = FALSE;
1082
1083       SVN_ERR(cb->processor->file_changed(relpath,
1084                                           copyfrom_source,
1085                                           right_source,
1086                                           copyfrom_file,
1087                                           right_file,
1088                                           copyfrom_props,
1089                                           right_props,
1090                                           !same,
1091                                           propchanges,
1092                                           file_baton,
1093                                           cb->processor,
1094                                           scratch_pool));
1095     }
1096   else
1097     {
1098       SVN_ERR(cb->processor->file_added(relpath,
1099                                         copyfrom_source,
1100                                         right_source,
1101                                         copyfrom_file,
1102                                         right_file,
1103                                         copyfrom_props,
1104                                         right_props,
1105                                         file_baton,
1106                                         cb->processor,
1107                                         scratch_pool));
1108     }
1109   return SVN_NO_ERROR;
1110 }
1111
1112 static svn_error_t *
1113 copy_as_changed_file_deleted(const char *relpath,
1114                              const svn_diff_source_t *left_source,
1115                              const char *left_file,
1116                              /*const*/ apr_hash_t *left_props,
1117                              void *file_baton,
1118                              const svn_diff_tree_processor_t *processor,
1119                              apr_pool_t *scratch_pool)
1120 {
1121   struct copy_as_changed_baton_t *cb = processor->baton;
1122
1123   SVN_ERR(cb->processor->file_deleted(relpath,
1124                                       left_source,
1125                                       left_file,
1126                                       left_props,
1127                                       file_baton,
1128                                       cb->processor,
1129                                       scratch_pool));
1130
1131   return SVN_NO_ERROR;
1132 }
1133
1134 static svn_error_t *
1135 copy_as_changed_file_changed(const char *relpath,
1136                              const svn_diff_source_t *left_source,
1137                              const svn_diff_source_t *right_source,
1138                              const char *left_file,
1139                              const char *right_file,
1140                              /*const*/ apr_hash_t *left_props,
1141                              /*const*/ apr_hash_t *right_props,
1142                              svn_boolean_t file_modified,
1143                              const apr_array_header_t *prop_changes,
1144                              void *file_baton,
1145                              const svn_diff_tree_processor_t *processor,
1146                              apr_pool_t *scratch_pool)
1147 {
1148   struct copy_as_changed_baton_t *cb = processor->baton;
1149
1150   SVN_ERR(cb->processor->file_changed(relpath,
1151                                       left_source,
1152                                       right_source,
1153                                       left_file,
1154                                       right_file,
1155                                       left_props,
1156                                       right_props,
1157                                       file_modified,
1158                                       prop_changes,
1159                                       file_baton,
1160                                       cb->processor,
1161                                       scratch_pool));
1162   return SVN_NO_ERROR;
1163 }
1164
1165 static svn_error_t *
1166 copy_as_changed_file_closed(const char *relpath,
1167                             const svn_diff_source_t *left_source,
1168                             const svn_diff_source_t *right_source,
1169                             void *file_baton,
1170                             const svn_diff_tree_processor_t *processor,
1171                             apr_pool_t *scratch_pool)
1172 {
1173   struct copy_as_changed_baton_t *cb = processor->baton;
1174
1175   SVN_ERR(cb->processor->file_closed(relpath,
1176                                      left_source,
1177                                      right_source,
1178                                      file_baton,
1179                                      cb->processor,
1180                                      scratch_pool));
1181
1182   return SVN_NO_ERROR;
1183 }
1184
1185 static svn_error_t *
1186 copy_as_changed_node_absent(const char *relpath,
1187                             void *dir_baton,
1188                             const svn_diff_tree_processor_t *processor,
1189                             apr_pool_t *scratch_pool)
1190 {
1191   struct copy_as_changed_baton_t *cb = processor->baton;
1192
1193   SVN_ERR(cb->processor->node_absent(relpath,
1194                                     dir_baton,
1195                                     cb->processor,
1196                                     scratch_pool));
1197   return SVN_NO_ERROR;
1198 }
1199
1200
1201 const svn_diff_tree_processor_t *
1202 svn_diff__tree_processor_copy_as_changed_create(
1203                         const svn_diff_tree_processor_t * processor,
1204                         apr_pool_t *result_pool)
1205 {
1206   struct copy_as_changed_baton_t *cb;
1207   svn_diff_tree_processor_t *filter;
1208
1209   cb = apr_pcalloc(result_pool, sizeof(*cb));
1210   cb->processor = processor;
1211
1212   filter = svn_diff__tree_processor_create(cb, result_pool);
1213   filter->dir_opened   = copy_as_changed_dir_opened;
1214   filter->dir_added    = copy_as_changed_dir_added;
1215   filter->dir_deleted  = copy_as_changed_dir_deleted;
1216   filter->dir_changed  = copy_as_changed_dir_changed;
1217   filter->dir_closed   = copy_as_changed_dir_closed;
1218
1219   filter->file_opened   = copy_as_changed_file_opened;
1220   filter->file_added    = copy_as_changed_file_added;
1221   filter->file_deleted  = copy_as_changed_file_deleted;
1222   filter->file_changed  = copy_as_changed_file_changed;
1223   filter->file_closed   = copy_as_changed_file_closed;
1224
1225   filter->node_absent   = copy_as_changed_node_absent;
1226
1227   return filter;
1228 }
1229
1230
1231 /* Processor baton for the tee tree processor */
1232 struct tee_baton_t
1233 {
1234   const svn_diff_tree_processor_t *p1;
1235   const svn_diff_tree_processor_t *p2;
1236 };
1237
1238 /* Wrapper baton for file and directory batons in the tee processor */
1239 struct tee_node_baton_t
1240 {
1241   void *baton1;
1242   void *baton2;
1243 };
1244
1245 static svn_error_t *
1246 tee_dir_opened(void **new_dir_baton,
1247                svn_boolean_t *skip,
1248                svn_boolean_t *skip_children,
1249                const char *relpath,
1250                const svn_diff_source_t *left_source,
1251                const svn_diff_source_t *right_source,
1252                const svn_diff_source_t *copyfrom_source,
1253                void *parent_dir_baton,
1254                const svn_diff_tree_processor_t *processor,
1255                apr_pool_t *result_pool,
1256                apr_pool_t *scratch_pool)
1257 {
1258   struct tee_baton_t *tb = processor->baton;
1259   struct tee_node_baton_t *pb = parent_dir_baton;
1260   struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb));
1261
1262   SVN_ERR(tb->p1->dir_opened(&(nb->baton1),
1263                              skip,
1264                              skip_children,
1265                              relpath,
1266                              left_source,
1267                              right_source,
1268                              copyfrom_source,
1269                              pb ? pb->baton1 : NULL,
1270                              tb->p1,
1271                              result_pool,
1272                              scratch_pool));
1273
1274   SVN_ERR(tb->p2->dir_opened(&(nb->baton2),
1275                              skip,
1276                              skip_children,
1277                              relpath,
1278                              left_source,
1279                              right_source,
1280                              copyfrom_source,
1281                              pb ? pb->baton2 : NULL,
1282                              tb->p2,
1283                              result_pool,
1284                              scratch_pool));
1285
1286   *new_dir_baton = nb;
1287
1288   return SVN_NO_ERROR;
1289 }
1290
1291 static svn_error_t *
1292 tee_dir_added(const char *relpath,
1293               const svn_diff_source_t *copyfrom_source,
1294               const svn_diff_source_t *right_source,
1295               /*const*/ apr_hash_t *copyfrom_props,
1296               /*const*/ apr_hash_t *right_props,
1297               void *dir_baton,
1298               const svn_diff_tree_processor_t *processor,
1299               apr_pool_t *scratch_pool)
1300 {
1301   struct tee_baton_t *tb = processor->baton;
1302   struct tee_node_baton_t *db = dir_baton;
1303
1304   SVN_ERR(tb->p1->dir_added(relpath,
1305                             copyfrom_source,
1306                             right_source,
1307                             copyfrom_props,
1308                             right_props,
1309                             db->baton1,
1310                             tb->p1,
1311                             scratch_pool));
1312
1313   SVN_ERR(tb->p2->dir_added(relpath,
1314                             copyfrom_source,
1315                             right_source,
1316                             copyfrom_props,
1317                             right_props,
1318                             db->baton2,
1319                             tb->p2,
1320                             scratch_pool));
1321
1322   return SVN_NO_ERROR;
1323 }
1324
1325 static svn_error_t *
1326 tee_dir_deleted(const char *relpath,
1327                 const svn_diff_source_t *left_source,
1328                 /*const*/ apr_hash_t *left_props,
1329                 void *dir_baton,
1330                 const svn_diff_tree_processor_t *processor,
1331                 apr_pool_t *scratch_pool)
1332 {
1333   struct tee_baton_t *tb = processor->baton;
1334   struct tee_node_baton_t *db = dir_baton;
1335
1336   SVN_ERR(tb->p1->dir_deleted(relpath,
1337                               left_source,
1338                               left_props,
1339                               db->baton1,
1340                               tb->p1,
1341                               scratch_pool));
1342
1343   SVN_ERR(tb->p2->dir_deleted(relpath,
1344                               left_source,
1345                               left_props,
1346                               db->baton2,
1347                               tb->p2,
1348                               scratch_pool));
1349
1350   return SVN_NO_ERROR;
1351 }
1352
1353 static svn_error_t *
1354 tee_dir_changed(const char *relpath,
1355                     const svn_diff_source_t *left_source,
1356                     const svn_diff_source_t *right_source,
1357                     /*const*/ apr_hash_t *left_props,
1358                     /*const*/ apr_hash_t *right_props,
1359                     const apr_array_header_t *prop_changes,
1360                     void *dir_baton,
1361                     const struct svn_diff_tree_processor_t *processor,
1362                     apr_pool_t *scratch_pool)
1363 {
1364   struct tee_baton_t *tb = processor->baton;
1365   struct tee_node_baton_t *db = dir_baton;
1366
1367   SVN_ERR(tb->p1->dir_changed(relpath,
1368                               left_source,
1369                               right_source,
1370                               left_props,
1371                               right_props,
1372                               prop_changes,
1373                               db->baton1,
1374                               tb->p1,
1375                               scratch_pool));
1376
1377   SVN_ERR(tb->p2->dir_changed(relpath,
1378                               left_source,
1379                               right_source,
1380                               left_props,
1381                               right_props,
1382                               prop_changes,
1383                               db->baton2,
1384                               tb->p2,
1385                               scratch_pool));
1386   return SVN_NO_ERROR;
1387 }
1388
1389 static svn_error_t *
1390 tee_dir_closed(const char *relpath,
1391                const svn_diff_source_t *left_source,
1392                const svn_diff_source_t *right_source,
1393                void *dir_baton,
1394                const svn_diff_tree_processor_t *processor,
1395                apr_pool_t *scratch_pool)
1396 {
1397   struct tee_baton_t *tb = processor->baton;
1398   struct tee_node_baton_t *db = dir_baton;
1399
1400   SVN_ERR(tb->p1->dir_closed(relpath,
1401                              left_source,
1402                              right_source,
1403                              db->baton1,
1404                              tb->p1,
1405                              scratch_pool));
1406
1407   SVN_ERR(tb->p2->dir_closed(relpath,
1408                              left_source,
1409                              right_source,
1410                              db->baton2,
1411                              tb->p2,
1412                              scratch_pool));
1413   return SVN_NO_ERROR;
1414 }
1415
1416 static svn_error_t *
1417 tee_file_opened(void **new_file_baton,
1418                 svn_boolean_t *skip,
1419                 const char *relpath,
1420                 const svn_diff_source_t *left_source,
1421                 const svn_diff_source_t *right_source,
1422                 const svn_diff_source_t *copyfrom_source,
1423                 void *dir_baton,
1424                 const svn_diff_tree_processor_t *processor,
1425                 apr_pool_t *result_pool,
1426                 apr_pool_t *scratch_pool)
1427 {
1428   struct tee_baton_t *tb = processor->baton;
1429   struct tee_node_baton_t *pb = dir_baton;
1430   struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb));
1431
1432   SVN_ERR(tb->p1->file_opened(&(nb->baton1),
1433                               skip,
1434                               relpath,
1435                               left_source,
1436                               right_source,
1437                               copyfrom_source,
1438                               pb ? pb->baton1 : NULL,
1439                               tb->p1,
1440                               result_pool,
1441                               scratch_pool));
1442
1443   SVN_ERR(tb->p2->file_opened(&(nb->baton2),
1444                               skip,
1445                               relpath,
1446                               left_source,
1447                               right_source,
1448                               copyfrom_source,
1449                               pb ? pb->baton2 : NULL,
1450                               tb->p2,
1451                               result_pool,
1452                               scratch_pool));
1453
1454   *new_file_baton = nb;
1455
1456   return SVN_NO_ERROR;
1457 }
1458
1459 static svn_error_t *
1460 tee_file_added(const char *relpath,
1461                const svn_diff_source_t *copyfrom_source,
1462                const svn_diff_source_t *right_source,
1463                const char *copyfrom_file,
1464                const char *right_file,
1465                /*const*/ apr_hash_t *copyfrom_props,
1466                /*const*/ apr_hash_t *right_props,
1467                void *file_baton,
1468                const svn_diff_tree_processor_t *processor,
1469                apr_pool_t *scratch_pool)
1470 {
1471   struct tee_baton_t *tb = processor->baton;
1472   struct tee_node_baton_t *fb = file_baton;
1473
1474   SVN_ERR(tb->p1->file_added(relpath,
1475                              copyfrom_source,
1476                              right_source,
1477                              copyfrom_file,
1478                              right_file,
1479                              copyfrom_props,
1480                              right_props,
1481                              fb->baton1,
1482                              tb->p1,
1483                              scratch_pool));
1484
1485   SVN_ERR(tb->p2->file_added(relpath,
1486                              copyfrom_source,
1487                              right_source,
1488                              copyfrom_file,
1489                              right_file,
1490                              copyfrom_props,
1491                              right_props,
1492                              fb->baton2,
1493                              tb->p2,
1494                              scratch_pool));
1495   return SVN_NO_ERROR;
1496 }
1497
1498 static svn_error_t *
1499 tee_file_deleted(const char *relpath,
1500                  const svn_diff_source_t *left_source,
1501                  const char *left_file,
1502                  /*const*/ apr_hash_t *left_props,
1503                  void *file_baton,
1504                  const svn_diff_tree_processor_t *processor,
1505                  apr_pool_t *scratch_pool)
1506 {
1507   struct tee_baton_t *tb = processor->baton;
1508   struct tee_node_baton_t *fb = file_baton;
1509
1510   SVN_ERR(tb->p1->file_deleted(relpath,
1511                                left_source,
1512                                left_file,
1513                                left_props,
1514                                fb->baton1,
1515                                tb->p1,
1516                                scratch_pool));
1517
1518   SVN_ERR(tb->p2->file_deleted(relpath,
1519                                left_source,
1520                                left_file,
1521                                left_props,
1522                                fb->baton2,
1523                                tb->p2,
1524                                scratch_pool));
1525   return SVN_NO_ERROR;
1526 }
1527
1528 static svn_error_t *
1529 tee_file_changed(const char *relpath,
1530                  const svn_diff_source_t *left_source,
1531                  const svn_diff_source_t *right_source,
1532                  const char *left_file,
1533                  const char *right_file,
1534                  /*const*/ apr_hash_t *left_props,
1535                  /*const*/ apr_hash_t *right_props,
1536                  svn_boolean_t file_modified,
1537                  const apr_array_header_t *prop_changes,
1538                  void *file_baton,
1539                  const svn_diff_tree_processor_t *processor,
1540                  apr_pool_t *scratch_pool)
1541 {
1542   struct tee_baton_t *tb = processor->baton;
1543   struct tee_node_baton_t *fb = file_baton;
1544
1545   SVN_ERR(tb->p1->file_changed(relpath,
1546                                left_source,
1547                                right_source,
1548                                left_file,
1549                                right_file,
1550                                left_props,
1551                                right_props,
1552                                file_modified,
1553                                prop_changes,
1554                                fb->baton1,
1555                                tb->p1,
1556                                scratch_pool));
1557
1558   SVN_ERR(tb->p2->file_changed(relpath,
1559                                left_source,
1560                                right_source,
1561                                left_file,
1562                                right_file,
1563                                left_props,
1564                                right_props,
1565                                file_modified,
1566                                prop_changes,
1567                                fb->baton2,
1568                                tb->p2,
1569                                scratch_pool));
1570   return SVN_NO_ERROR;
1571 }
1572
1573 static svn_error_t *
1574 tee_file_closed(const char *relpath,
1575                     const svn_diff_source_t *left_source,
1576                     const svn_diff_source_t *right_source,
1577                     void *file_baton,
1578                     const svn_diff_tree_processor_t *processor,
1579                     apr_pool_t *scratch_pool)
1580 {
1581   struct tee_baton_t *tb = processor->baton;
1582   struct tee_node_baton_t *fb = file_baton;
1583
1584   SVN_ERR(tb->p1->file_closed(relpath,
1585                               left_source,
1586                               right_source,
1587                               fb->baton1,
1588                               tb->p1,
1589                               scratch_pool));
1590
1591   SVN_ERR(tb->p2->file_closed(relpath,
1592                               left_source,
1593                               right_source,
1594                               fb->baton2,
1595                               tb->p2,
1596                               scratch_pool));
1597
1598   return SVN_NO_ERROR;
1599 }
1600
1601 static svn_error_t *
1602 tee_node_absent(const char *relpath,
1603                 void *dir_baton,
1604                 const svn_diff_tree_processor_t *processor,
1605                 apr_pool_t *scratch_pool)
1606 {
1607   struct tee_baton_t *tb = processor->baton;
1608   struct tee_node_baton_t *db = dir_baton;
1609
1610   SVN_ERR(tb->p1->node_absent(relpath,
1611                               db ? db->baton1 : NULL,
1612                               tb->p1,
1613                               scratch_pool));
1614
1615   SVN_ERR(tb->p2->node_absent(relpath,
1616                               db ? db->baton2 : NULL,
1617                               tb->p2,
1618                               scratch_pool));
1619
1620   return SVN_NO_ERROR;
1621 }
1622
1623 const svn_diff_tree_processor_t *
1624 svn_diff__tree_processor_tee_create(const svn_diff_tree_processor_t *processor1,
1625                                     const svn_diff_tree_processor_t *processor2,
1626                                     apr_pool_t *result_pool)
1627 {
1628   struct tee_baton_t *tb = apr_pcalloc(result_pool, sizeof(*tb));
1629   svn_diff_tree_processor_t *tee;
1630   tb->p1 = processor1;
1631   tb->p2 = processor2;
1632
1633   tee = svn_diff__tree_processor_create(tb, result_pool);
1634
1635   tee->dir_opened    = tee_dir_opened;
1636   tee->dir_added     = tee_dir_added;
1637   tee->dir_deleted   = tee_dir_deleted;
1638   tee->dir_changed   = tee_dir_changed;
1639   tee->dir_closed    = tee_dir_closed;
1640   tee->file_opened   = tee_file_opened;
1641   tee->file_added    = tee_file_added;
1642   tee->file_deleted  = tee_file_deleted;
1643   tee->file_changed  = tee_file_changed;
1644   tee->file_closed   = tee_file_closed;
1645   tee->node_absent   = tee_node_absent;
1646
1647   return tee;
1648 }
1649
1650 svn_diff_source_t *
1651 svn_diff__source_create(svn_revnum_t revision,
1652                         apr_pool_t *result_pool)
1653 {
1654   svn_diff_source_t *src = apr_pcalloc(result_pool, sizeof(*src));
1655
1656   src->revision = revision;
1657   return src;
1658 }