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