]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/mergeinfo.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / mergeinfo.c
1 /*
2  * mergeinfo.c:  Mergeinfo parsing and handling
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 #include <assert.h>
24 #include <ctype.h>
25
26 #include "svn_path.h"
27 #include "svn_types.h"
28 #include "svn_ctype.h"
29 #include "svn_pools.h"
30 #include "svn_sorts.h"
31 #include "svn_error.h"
32 #include "svn_error_codes.h"
33 #include "svn_string.h"
34 #include "svn_mergeinfo.h"
35 #include "private/svn_fspath.h"
36 #include "private/svn_mergeinfo_private.h"
37 #include "private/svn_sorts_private.h"
38 #include "private/svn_string_private.h"
39 #include "private/svn_subr_private.h"
40 #include "svn_private_config.h"
41 #include "svn_hash.h"
42 #include "private/svn_dep_compat.h"
43
44 /* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
45    overlapping, and their inheritability allows them to be combined, put
46    the result in OUTPUT and return TRUE, otherwise return FALSE.
47
48    CONSIDER_INHERITANCE determines how to account for the inheritability
49    of IN1 and IN2 when trying to combine ranges.  If ranges with different
50    inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
51    to happen) the result is inheritable.  If both ranges are inheritable the
52    result is inheritable.  And only if both ranges are non-inheritable
53    the result is non-inheritable.
54
55    Range overlapping detection algorithm from
56    http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
57 */
58 static svn_boolean_t
59 combine_ranges(svn_merge_range_t *output,
60                const svn_merge_range_t *in1,
61                const svn_merge_range_t *in2,
62                svn_boolean_t consider_inheritance)
63 {
64   if (in1->start <= in2->end && in2->start <= in1->end)
65     {
66       if (!consider_inheritance
67           || (consider_inheritance
68               && (in1->inheritable == in2->inheritable)))
69         {
70           output->start = MIN(in1->start, in2->start);
71           output->end = MAX(in1->end, in2->end);
72           output->inheritable = (in1->inheritable || in2->inheritable);
73           return TRUE;
74         }
75     }
76   return FALSE;
77 }
78
79 /* pathname -> PATHNAME */
80 static svn_error_t *
81 parse_pathname(const char **input,
82                const char *end,
83                const char **pathname,
84                apr_pool_t *pool)
85 {
86   const char *curr = *input;
87   const char *last_colon = NULL;
88
89   /* A pathname may contain colons, so find the last colon before END
90      or newline.  We'll consider this the divider between the pathname
91      and the revisionlist. */
92   while (curr < end && *curr != '\n')
93     {
94       if (*curr == ':')
95         last_colon = curr;
96       curr++;
97     }
98
99   if (!last_colon)
100     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
101                             _("Pathname not terminated by ':'"));
102
103   /* Tolerate relative repository paths, but convert them to absolute.
104      ### Efficiency?  1 string duplication here, 2 in canonicalize. */
105   *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
106                                                     last_colon - *input),
107                                        pool);
108
109   *input = last_colon;
110
111   return SVN_NO_ERROR;
112 }
113
114 /* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
115  * revision range.
116  *
117  * Note: The smallest valid value of RANGE->start is 0 because it is an
118  * exclusive endpoint, being one less than the revision number of the first
119  * change described by the range, and the oldest possible change is "r1" as
120  * there cannot be a change "r0". */
121 #define IS_VALID_FORWARD_RANGE(range) \
122   (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
123
124 /* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
125 typedef enum intersection_type_t
126 {
127   /* Ranges don't intersect and don't adjoin. */
128   svn__no_intersection,
129
130   /* Ranges are equal. */
131   svn__equal_intersection,
132
133   /* Ranges adjoin but don't overlap. */
134   svn__adjoining_intersection,
135
136   /* Ranges overlap but neither is a subset of the other. */
137   svn__overlapping_intersection,
138
139   /* One range is a proper subset of the other. */
140   svn__proper_subset_intersection
141 } intersection_type_t;
142
143 /* Given ranges R1 and R2, both of which must be forward merge ranges,
144    set *INTERSECTION_TYPE to describe how the ranges intersect, if they
145    do at all.  The inheritance type of the ranges is not considered. */
146 static svn_error_t *
147 get_type_of_intersection(const svn_merge_range_t *r1,
148                          const svn_merge_range_t *r2,
149                          intersection_type_t *intersection_type)
150 {
151   SVN_ERR_ASSERT(r1);
152   SVN_ERR_ASSERT(r2);
153   SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
154   SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
155
156   if (!(r1->start <= r2->end && r2->start <= r1->end))
157     *intersection_type = svn__no_intersection;
158   else if (r1->start == r2->start && r1->end == r2->end)
159     *intersection_type = svn__equal_intersection;
160   else if (r1->end == r2->start || r2->end == r1->start)
161     *intersection_type = svn__adjoining_intersection;
162   else if (r1->start <= r2->start && r1->end >= r2->end)
163     *intersection_type = svn__proper_subset_intersection;
164   else if (r2->start <= r1->start && r2->end >= r1->end)
165     *intersection_type = svn__proper_subset_intersection;
166   else
167     *intersection_type = svn__overlapping_intersection;
168
169   return SVN_NO_ERROR;
170 }
171
172 /* Modify or extend RANGELIST (a list of merge ranges) to incorporate
173    NEW_RANGE. RANGELIST is a "rangelist" as defined in svn_mergeinfo.h.
174
175    OVERVIEW
176
177    Determine the minimal set of non-overlapping merge ranges required to
178    represent the combination of RANGELIST and NEW_RANGE. The result depends
179    on whether and how NEW_RANGE overlaps any merge range[*] in RANGELIST,
180    and also on any differences in the inheritability of each range,
181    according to the rules described below. Modify RANGELIST to represent
182    this result, by adjusting the last range in it and/or appending one or
183    two more ranges.
184
185    ([*] Due to the simplifying assumption below, only the last range in
186    RANGELIST is considered.)
187
188    DETAILS
189
190    If RANGELIST is not empty assume NEW_RANGE does not intersect with any
191    range before the last one in RANGELIST.
192
193    If RANGELIST is empty or NEW_RANGE does not intersect with the lastrange
194    in RANGELIST, then append a copy of NEW_RANGE, allocated in RESULT_POOL,
195    to RANGELIST.
196
197    If NEW_RANGE intersects with the last range in RANGELIST then combine
198    these two ranges as described below:
199
200    If the intersecting ranges have the same inheritability then simply
201    combine the ranges in place.  Otherwise, if the ranges intersect but
202    differ in inheritability, then merge the ranges as dictated by
203    CONSIDER_INHERITANCE:
204
205    If CONSIDER_INHERITANCE is false then intersecting ranges are combined
206    into a single range.  The inheritability of the resulting range is
207    non-inheritable *only* if both ranges are non-inheritable, otherwise the
208    combined range is inheritable, e.g.:
209
210      Last range in        NEW_RANGE        RESULTING RANGES
211      RANGELIST
212      -------------        ---------        ----------------
213      4-10*                6-13             4-13
214      4-10                 6-13*            4-13
215      4-10*                6-13*            4-13*
216
217    If CONSIDER_INHERITANCE is true, then only the intersection between the
218    two ranges is combined, with the inheritability of the resulting range
219    non-inheritable only if both ranges were non-inheritable.  The
220    non-intersecting portions are added as separate ranges allocated in
221    RESULT_POOL, e.g.:
222
223      Last range in        NEW_RANGE        RESULTING RANGES
224      RANGELIST
225      -------------        ---------        ----------------
226      4-10*                6                4-5*, 6, 7-10*
227      4-10*                6-12             4-5*, 6-12
228
229    Note that the standard rules for rangelists still apply and overlapping
230    ranges are not allowed.  So if the above would result in overlapping
231    ranges of the same inheritance, the overlapping ranges are merged into a
232    single range, e.g.:
233
234      Last range in        NEW_RANGE        RESULTING RANGES
235      RANGELIST
236      -------------        ---------        ----------------
237      4-10                 6*               4-10 (Not 4-5, 6, 7-10)
238
239    When replacing the last range in RANGELIST, either allocate a new range in
240    RESULT_POOL or modify the existing range in place.  Any new ranges added
241    to RANGELIST are allocated in RESULT_POOL.
242 */
243 static svn_error_t *
244 combine_with_lastrange(const svn_merge_range_t *new_range,
245                        svn_rangelist_t *rangelist,
246                        svn_boolean_t consider_inheritance,
247                        apr_pool_t *result_pool)
248 {
249   svn_merge_range_t *lastrange;
250   svn_merge_range_t combined_range;
251
252   /* We don't accept a NULL RANGELIST. */
253   SVN_ERR_ASSERT(rangelist);
254
255   if (rangelist->nelts > 0)
256     lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *);
257   else
258     lastrange = NULL;
259
260   if (!lastrange)
261     {
262       /* No *LASTRANGE so push NEW_RANGE onto RANGELIST and we are done. */
263       APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
264         svn_merge_range_dup(new_range, result_pool);
265     }
266   else if (!consider_inheritance)
267     {
268       /* We are not considering inheritance so we can merge intersecting
269          ranges of different inheritability.  Of course if the ranges
270          don't intersect at all we simply push NEW_RANGE onto RANGELIST. */
271       if (combine_ranges(&combined_range, lastrange, new_range, FALSE))
272         {
273           *lastrange = combined_range;
274         }
275       else
276         {
277           APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
278             svn_merge_range_dup(new_range, result_pool);
279         }
280     }
281   else /* Considering inheritance */
282     {
283       if (combine_ranges(&combined_range, lastrange, new_range, TRUE))
284         {
285           /* Even when considering inheritance two intersection ranges
286              of the same inheritability can simply be combined. */
287           *lastrange = combined_range;
288         }
289       else
290         {
291           /* If we are here then the ranges either don't intersect or do
292              intersect but have differing inheritability.  Check for the
293              first case as that is easy to handle. */
294           intersection_type_t intersection_type;
295           svn_boolean_t sorted = FALSE;
296
297           SVN_ERR(get_type_of_intersection(new_range, lastrange,
298                                            &intersection_type));
299
300           switch (intersection_type)
301             {
302               case svn__no_intersection:
303                 /* NEW_RANGE and *LASTRANGE *really* don't intersect so
304                    just push NEW_RANGE onto RANGELIST. */
305                 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
306                   svn_merge_range_dup(new_range, result_pool);
307                 sorted = (svn_sort_compare_ranges(&lastrange,
308                                                   &new_range) < 0);
309                 break;
310
311               case svn__equal_intersection:
312                 /* They range are equal so all we do is force the
313                    inheritability of lastrange to true. */
314                 lastrange->inheritable = TRUE;
315                 sorted = TRUE;
316                 break;
317
318               case svn__adjoining_intersection:
319                 /* They adjoin but don't overlap so just push NEW_RANGE
320                    onto RANGELIST. */
321                 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
322                   svn_merge_range_dup(new_range, result_pool);
323                 sorted = (svn_sort_compare_ranges(&lastrange,
324                                                   &new_range) < 0);
325                 break;
326
327               case svn__overlapping_intersection:
328                 /* They ranges overlap but neither is a proper subset of
329                    the other.  We'll end up pusing two new ranges onto
330                    RANGELIST, the intersecting part and the part unique to
331                    NEW_RANGE.*/
332                 {
333                   svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
334                                                               result_pool);
335                   svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
336                                                               result_pool);
337
338                   /* Pop off *LASTRANGE to make our manipulations
339                      easier. */
340                   apr_array_pop(rangelist);
341
342                   /* Ensure R1 is the older range. */
343                   if (r2->start < r1->start)
344                     {
345                       /* Swap R1 and R2. */
346                       *r2 = *r1;
347                       *r1 = *new_range;
348                     }
349
350                   /* Absorb the intersecting ranges into the
351                      inheritable range. */
352                   if (r1->inheritable)
353                     r2->start = r1->end;
354                   else
355                     r1->end = r2->start;
356
357                   /* Push everything back onto RANGELIST. */
358                   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
359                   sorted = (svn_sort_compare_ranges(&lastrange,
360                                                     &r1) < 0);
361                   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
362                   if (sorted)
363                     sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
364                   break;
365                 }
366
367               default: /* svn__proper_subset_intersection */
368                 {
369                   /* One range is a proper subset of the other. */
370                   svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
371                                                               result_pool);
372                   svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
373                                                               result_pool);
374                   svn_merge_range_t *r3 = NULL;
375
376                   /* Pop off *LASTRANGE to make our manipulations
377                      easier. */
378                   apr_array_pop(rangelist);
379
380                   /* Ensure R1 is the superset. */
381                   if (r2->start < r1->start || r2->end > r1->end)
382                     {
383                       /* Swap R1 and R2. */
384                       *r2 = *r1;
385                       *r1 = *new_range;
386                     }
387
388                   if (r1->inheritable)
389                     {
390                       /* The simple case: The superset is inheritable, so
391                          just combine r1 and r2. */
392                       r1->start = MIN(r1->start, r2->start);
393                       r1->end = MAX(r1->end, r2->end);
394                       r2 = NULL;
395                     }
396                   else if (r1->start == r2->start)
397                     {
398                       svn_revnum_t tmp_revnum;
399
400                       /* *LASTRANGE and NEW_RANGE share an end point. */
401                       tmp_revnum = r1->end;
402                       r1->end = r2->end;
403                       r2->inheritable = r1->inheritable;
404                       r1->inheritable = TRUE;
405                       r2->start = r1->end;
406                       r2->end = tmp_revnum;
407                     }
408                   else if (r1->end == r2->end)
409                     {
410                       /* *LASTRANGE and NEW_RANGE share an end point. */
411                       r1->end = r2->start;
412                       r2->inheritable = TRUE;
413                     }
414                   else
415                     {
416                       /* NEW_RANGE and *LASTRANGE share neither start
417                          nor end points. */
418                       r3 = apr_pcalloc(result_pool, sizeof(*r3));
419                       r3->start = r2->end;
420                       r3->end = r1->end;
421                       r3->inheritable = r1->inheritable;
422                       r2->inheritable = TRUE;
423                       r1->end = r2->start;
424                     }
425
426                   /* Push everything back onto RANGELIST. */
427                   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
428                   sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
429                   if (r2)
430                     {
431                       APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
432                       if (sorted)
433                         sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
434                     }
435                   if (r3)
436                     {
437                       APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
438                       if (sorted)
439                         {
440                           if (r2)
441                             sorted = (svn_sort_compare_ranges(&r2,
442                                                               &r3) < 0);
443                           else
444                             sorted = (svn_sort_compare_ranges(&r1,
445                                                               &r3) < 0);
446                         }
447                     }
448                   break;
449                 }
450             }
451
452           /* Some of the above cases might have put *RANGELIST out of
453              order, so re-sort.*/
454           if (!sorted)
455             svn_sort__array(rangelist, svn_sort_compare_ranges);
456         }
457     }
458
459   return SVN_NO_ERROR;
460 }
461
462 /* Convert a single svn_merge_range_t *RANGE back into a string.  */
463 static char *
464 range_to_string(const svn_merge_range_t *range,
465                 apr_pool_t *pool)
466 {
467   const char *mark
468     = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR;
469
470   if (range->start == range->end - 1)
471     return apr_psprintf(pool, "%ld%s", range->end, mark);
472   else if (range->start - 1 == range->end)
473     return apr_psprintf(pool, "-%ld%s", range->start, mark);
474   else if (range->start < range->end)
475     return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
476   else
477     return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
478 }
479
480 /* Helper for svn_mergeinfo_parse()
481    Append revision ranges onto the array RANGELIST to represent the range
482    descriptions found in the string *INPUT.  Read only as far as a newline
483    or the position END, whichever comes first.  Set *INPUT to the position
484    after the last character of INPUT that was used.
485
486    revisionlist -> (revisionelement)(COMMA revisionelement)*
487    revisionrange -> REVISION "-" REVISION("*")
488    revisionelement -> revisionrange | REVISION("*")
489 */
490 static svn_error_t *
491 parse_rangelist(const char **input, const char *end,
492                 svn_rangelist_t *rangelist,
493                 apr_pool_t *pool)
494 {
495   const char *curr = *input;
496
497   /* Eat any leading horizontal white-space before the rangelist. */
498   while (curr < end && *curr != '\n' && isspace(*curr))
499     curr++;
500
501   if (*curr == '\n' || curr == end)
502     {
503       /* Empty range list. */
504       *input = curr;
505       return SVN_NO_ERROR;
506     }
507
508   while (curr < end && *curr != '\n')
509     {
510       /* Parse individual revisions or revision ranges. */
511       svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
512       svn_revnum_t firstrev;
513
514       SVN_ERR(svn_revnum_parse(&firstrev, curr, &curr));
515       if (*curr != '-' && *curr != '\n' && *curr != ',' && *curr != '*'
516           && curr != end)
517         return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
518                                  _("Invalid character '%c' found in revision "
519                                    "list"), *curr);
520       mrange->start = firstrev - 1;
521       mrange->end = firstrev;
522       mrange->inheritable = TRUE;
523
524       if (firstrev == 0)
525         return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
526                                  _("Invalid revision number '0' found in "
527                                    "range list"));
528
529       if (*curr == '-')
530         {
531           svn_revnum_t secondrev;
532
533           curr++;
534           SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr));
535           if (firstrev > secondrev)
536             return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
537                                      _("Unable to parse reversed revision "
538                                        "range '%ld-%ld'"),
539                                        firstrev, secondrev);
540           else if (firstrev == secondrev)
541             return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
542                                      _("Unable to parse revision range "
543                                        "'%ld-%ld' with same start and end "
544                                        "revisions"), firstrev, secondrev);
545           mrange->end = secondrev;
546         }
547
548       if (*curr == '\n' || curr == end)
549         {
550           APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
551           *input = curr;
552           return SVN_NO_ERROR;
553         }
554       else if (*curr == ',')
555         {
556           APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
557           curr++;
558         }
559       else if (*curr == '*')
560         {
561           mrange->inheritable = FALSE;
562           curr++;
563           if (*curr == ',' || *curr == '\n' || curr == end)
564             {
565               APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
566               if (*curr == ',')
567                 {
568                   curr++;
569                 }
570               else
571                 {
572                   *input = curr;
573                   return SVN_NO_ERROR;
574                 }
575             }
576           else
577             {
578               return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
579                                        _("Invalid character '%c' found in "
580                                          "range list"), *curr);
581             }
582         }
583       else
584         {
585           return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
586                                    _("Invalid character '%c' found in "
587                                      "range list"), *curr);
588         }
589
590     }
591   if (*curr != '\n')
592     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
593                             _("Range list parsing ended before hitting "
594                               "newline"));
595   *input = curr;
596   return SVN_NO_ERROR;
597 }
598
599 svn_error_t *
600 svn_rangelist__parse(svn_rangelist_t **rangelist,
601                      const char *str,
602                      apr_pool_t *result_pool)
603 {
604   const char *s = str;
605
606   *rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
607   SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool));
608   return SVN_NO_ERROR;
609 }
610
611 /* Return TRUE, if all ranges in RANGELIST are in ascending order and do
612  * not overlap and are not adjacent.
613  *
614  * ### Can yield false negatives: ranges of differing inheritance are
615  * allowed to be adjacent.
616  *
617  * If this returns FALSE, you probaly want to qsort() the
618  * ranges and then call svn_rangelist__combine_adjacent_ranges().
619  */
620 static svn_boolean_t
621 is_rangelist_normalized(svn_rangelist_t *rangelist)
622 {
623   int i;
624   svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts;
625
626   for (i = 0; i < rangelist->nelts-1; ++i)
627     if (ranges[i]->end >= ranges[i+1]->start)
628       return FALSE;
629
630   return TRUE;
631 }
632
633 svn_error_t *
634 svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
635                             apr_pool_t *scratch_pool)
636 {
637   if (! is_rangelist_normalized(rangelist))
638     {
639       svn_sort__array(rangelist, svn_sort_compare_ranges);
640
641       SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
642     }
643
644   return SVN_NO_ERROR;
645 }
646
647 svn_error_t *
648 svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
649                                        apr_pool_t *scratch_pool)
650 {
651   int i;
652   svn_merge_range_t *range, *lastrange;
653
654   lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
655
656   for (i = 1; i < rangelist->nelts; i++)
657     {
658       range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
659       if (lastrange->start <= range->end
660           && range->start <= lastrange->end)
661         {
662           /* The ranges are adjacent or intersect. */
663
664           /* svn_mergeinfo_parse promises to combine overlapping
665              ranges as long as their inheritability is the same. */
666           if (range->start < lastrange->end
667               && range->inheritable != lastrange->inheritable)
668             {
669               return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
670                                        _("Unable to parse overlapping "
671                                          "revision ranges '%s' and '%s' "
672                                          "with different inheritance "
673                                          "types"),
674                                        range_to_string(lastrange,
675                                                        scratch_pool),
676                                        range_to_string(range,
677                                                        scratch_pool));
678             }
679
680           /* Combine overlapping or adjacent ranges with the
681              same inheritability. */
682           if (lastrange->inheritable == range->inheritable)
683             {
684               lastrange->end = MAX(range->end, lastrange->end);
685               svn_sort__array_delete(rangelist, i, 1);
686               i--;
687             }
688         }
689       lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
690     }
691
692   return SVN_NO_ERROR;
693 }
694
695 /* revisionline -> PATHNAME COLON revisionlist
696  *
697  * Parse one line of mergeinfo starting at INPUT, not reading beyond END,
698  * into HASH. Allocate the new entry in HASH deeply from HASH's pool.
699  */
700 static svn_error_t *
701 parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
702                     apr_pool_t *scratch_pool)
703 {
704   const char *pathname = "";
705   apr_ssize_t klen;
706   svn_rangelist_t *existing_rangelist;
707   svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
708                                               sizeof(svn_merge_range_t *));
709
710   SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
711
712   if (*(*input) != ':')
713     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
714                             _("Pathname not terminated by ':'"));
715
716   *input = *input + 1;
717
718   SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
719
720   if (rangelist->nelts == 0)
721       return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
722                                _("Mergeinfo for '%s' maps to an "
723                                  "empty revision range"), pathname);
724   if (*input != end && *(*input) != '\n')
725     return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
726                              _("Could not find end of line in range list line "
727                                "in '%s'"), *input);
728
729   if (*input != end)
730     *input = *input + 1;
731
732   /* Sort the rangelist, combine adjacent ranges into single ranges, and
733      make sure there are no overlapping ranges.  Luckily, most data in
734      svn:mergeinfo will already be in normalized form and this will be quick.
735    */
736   SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool));
737
738   /* Handle any funky mergeinfo with relative merge source paths that
739      might exist due to issue #3547.  It's possible that this issue allowed
740      the creation of mergeinfo with path keys that differ only by a
741      leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
742      we encounter this we merge the rangelists together under a single
743      absolute path key. */
744   klen = strlen(pathname);
745   existing_rangelist = apr_hash_get(hash, pathname, klen);
746   if (existing_rangelist)
747     SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
748                                  scratch_pool, scratch_pool));
749
750   apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
751                klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
752
753   return SVN_NO_ERROR;
754 }
755
756 /* top -> revisionline (NEWLINE revisionline)*
757  *
758  * Parse mergeinfo starting at INPUT, not reading beyond END, into HASH.
759  * Allocate all the new entries in HASH deeply from HASH's pool.
760  */
761 static svn_error_t *
762 parse_top(const char **input, const char *end, svn_mergeinfo_t hash,
763           apr_pool_t *scratch_pool)
764 {
765   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
766
767   while (*input < end)
768     {
769       svn_pool_clear(iterpool);
770       SVN_ERR(parse_revision_line(input, end, hash, iterpool));
771     }
772   svn_pool_destroy(iterpool);
773
774   return SVN_NO_ERROR;
775 }
776
777 svn_error_t *
778 svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo,
779                     const char *input,
780                     apr_pool_t *pool)
781 {
782   svn_error_t *err;
783
784   *mergeinfo = svn_hash__make(pool);
785   err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
786
787   /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
788   if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
789     err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err,
790                             _("Could not parse mergeinfo string '%s'"),
791                             input);
792   return err;
793 }
794
795 /* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
796    a single rangelist element in-place.
797
798    If *RANGE_INDEX is not a valid element in RANGELIST do nothing.  Otherwise
799    ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
800    subsequent ranges in RANGELIST.
801
802    If overlap is found, then remove, modify, and/or add elements to RANGELIST
803    as per the invariants for rangelists documented in svn_mergeinfo.h.  If
804    RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
805    elements if their inheritability permits -- The inheritance of intersecting
806    and adjoining ranges is handled as per svn_mergeinfo_merge2.  Upon return
807    set *RANGE_INDEX to the index of the youngest element modified, added, or
808    adjoined to RANGELIST[*RANGE_INDEX].
809
810    Note: Adjoining rangelist elements are those where the end rev of the older
811    element is equal to the start rev of the younger element.
812
813    Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
814 static void
815 adjust_remaining_ranges(svn_rangelist_t *rangelist,
816                         int *range_index,
817                         apr_pool_t *result_pool)
818 {
819   int i;
820   int starting_index;
821   int elements_to_delete = 0;
822   svn_merge_range_t *modified_range;
823
824   if (*range_index >= rangelist->nelts)
825     return;
826
827   starting_index = *range_index + 1;
828   modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
829
830   for (i = *range_index + 1; i < rangelist->nelts; i++)
831     {
832       svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
833                                                     svn_merge_range_t *);
834
835       /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
836          RANGELIST then we are finished. */
837       if (modified_range->end < next_range->start)
838         break;
839
840       /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
841       if (modified_range->end == next_range->start)
842         {
843           if (modified_range->inheritable == next_range->inheritable)
844             {
845               /* Combine adjoining ranges with the same inheritability. */
846               modified_range->end = next_range->end;
847               elements_to_delete++;
848             }
849           else
850             {
851               /* Cannot join because inheritance differs. */
852               (*range_index)++;
853             }
854           break;
855         }
856
857       /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
858       if (modified_range->end > next_range->end)
859         {
860           /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
861              don't share the same end range. */
862           if (modified_range->inheritable
863               || (modified_range->inheritable == next_range->inheritable))
864             {
865               /* MODIFIED_RANGE absorbs NEXT_RANGE. */
866               elements_to_delete++;
867             }
868           else
869             {
870               /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
871                  MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
872                  inheritable.  This means MODIFIED_RANGE is truncated,
873                  NEXT_RANGE remains, and the portion of MODIFIED_RANGE
874                  younger than NEXT_RANGE is added as a separate range:
875                   ______________________________________________
876                  |                                              |
877                  M                 MODIFIED_RANGE               N
878                  |                 (!inheritable)               |
879                  |______________________________________________|
880                                   |              |
881                                   O  NEXT_RANGE  P
882                                   | (inheritable)|
883                                   |______________|
884                                          |
885                                          V
886                   _______________________________________________
887                  |                |              |               |
888                  M MODIFIED_RANGE O  NEXT_RANGE  P   NEW_RANGE   N
889                  | (!inheritable) | (inheritable)| (!inheritable)|
890                  |________________|______________|_______________|
891               */
892               svn_merge_range_t *new_modified_range =
893                 apr_palloc(result_pool, sizeof(*new_modified_range));
894               new_modified_range->start = next_range->end;
895               new_modified_range->end = modified_range->end;
896               new_modified_range->inheritable = FALSE;
897               modified_range->end = next_range->start;
898               (*range_index)+=2;
899               svn_sort__array_insert(rangelist, &new_modified_range,
900                                      *range_index);
901               /* Recurse with the new range. */
902               adjust_remaining_ranges(rangelist, range_index, result_pool);
903               break;
904             }
905         }
906       else if (modified_range->end == next_range->end)
907         {
908           /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
909              the same end range. */
910           if (modified_range->inheritable
911               || (modified_range->inheritable == next_range->inheritable))
912             {
913               /* MODIFIED_RANGE absorbs NEXT_RANGE. */
914               elements_to_delete++;
915             }
916           else
917             {
918               /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
919                  absorbed by the latter. */
920               modified_range->end = next_range->start;
921               (*range_index)++;
922             }
923           break;
924         }
925       else
926         {
927           /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
928              a proper subset of MODIFIED_RANGE, nor do the two share the
929              same end revision, i.e. they overlap. */
930           if (modified_range->inheritable == next_range->inheritable)
931             {
932               /* Combine overlapping ranges with the same inheritability. */
933               modified_range->end = next_range->end;
934               elements_to_delete++;
935             }
936           else if (modified_range->inheritable)
937             {
938               /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
939                  and NEXT_RANGE is truncated. */
940               next_range->start = modified_range->end;
941               (*range_index)++;
942             }
943           else
944             {
945               /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
946                  and MODIFIED_RANGE is truncated. */
947               modified_range->end = next_range->start;
948               (*range_index)++;
949             }
950           break;
951         }
952     }
953
954   if (elements_to_delete)
955     svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
956 }
957
958 svn_error_t *
959 svn_rangelist_merge2(svn_rangelist_t *rangelist,
960                      const svn_rangelist_t *changes,
961                      apr_pool_t *result_pool,
962                      apr_pool_t *scratch_pool)
963 {
964   int i = 0;
965   int j = 0;
966
967   /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
968   changes = svn_rangelist_dup(changes, scratch_pool);
969
970   while (i < rangelist->nelts && j < changes->nelts)
971     {
972       svn_merge_range_t *range =
973         APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
974       svn_merge_range_t *change =
975         APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
976       int res = svn_sort_compare_ranges(&range, &change);
977
978       if (res == 0)
979         {
980           /* Only when merging two non-inheritable ranges is the result also
981              non-inheritable.  In all other cases ensure an inheritable
982              result. */
983           if (range->inheritable || change->inheritable)
984             range->inheritable = TRUE;
985           i++;
986           j++;
987         }
988       else if (res < 0) /* CHANGE is younger than RANGE */
989         {
990           if (range->end < change->start)
991             {
992               /* RANGE is older than CHANGE and the two do not
993                  adjoin or overlap */
994               i++;
995             }
996           else if (range->end == change->start)
997             {
998               /* RANGE and CHANGE adjoin */
999               if (range->inheritable == change->inheritable)
1000                 {
1001                   /* RANGE and CHANGE have the same inheritability so
1002                      RANGE expands to absord CHANGE. */
1003                   range->end = change->end;
1004                   adjust_remaining_ranges(rangelist, &i, result_pool);
1005                   j++;
1006                 }
1007               else
1008                 {
1009                   /* RANGE and CHANGE adjoin, but have different
1010                      inheritability.  Since RANGE is older, just
1011                      move on to the next RANGE. */
1012                   i++;
1013                 }
1014             }
1015           else
1016             {
1017               /* RANGE and CHANGE overlap, but how? */
1018               if ((range->inheritable == change->inheritable)
1019                   || range->inheritable)
1020                 {
1021                   /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
1022                       with no adjustment otherwise only the intersection is
1023                       absorbed and CHANGE is truncated. */
1024                   if (range->end >= change->end)
1025                     j++;
1026                   else
1027                     change->start = range->end;
1028                 }
1029               else
1030                 {
1031                   /* RANGE is non-inheritable and CHANGE is inheritable. */
1032                   if (range->start < change->start)
1033                     {
1034                       /* CHANGE absorbs intersection with RANGE and RANGE
1035                          is truncated. */
1036                       svn_merge_range_t *range_copy =
1037                         svn_merge_range_dup(range, result_pool);
1038                       range_copy->end = change->start;
1039                       range->start = change->start;
1040                       svn_sort__array_insert(rangelist, &range_copy, i++);
1041                     }
1042                   else
1043                     {
1044                       /* CHANGE and RANGE share the same start rev, but
1045                          RANGE is considered older because its end rev
1046                          is older. */
1047                       range->inheritable = TRUE;
1048                       change->start = range->end;
1049                     }
1050                 }
1051             }
1052         }
1053       else /* res > 0, CHANGE is older than RANGE */
1054         {
1055           if (change->end < range->start)
1056             {
1057               /* CHANGE is older than RANGE and the two do not
1058                  adjoin or overlap, so insert a copy of CHANGE
1059                  into RANGELIST. */
1060               svn_merge_range_t *change_copy =
1061                 svn_merge_range_dup(change, result_pool);
1062               svn_sort__array_insert(rangelist, &change_copy, i++);
1063               j++;
1064             }
1065           else if (change->end == range->start)
1066             {
1067               /* RANGE and CHANGE adjoin */
1068               if (range->inheritable == change->inheritable)
1069                 {
1070                   /* RANGE and CHANGE have the same inheritability so we
1071                      can simply combine the two in place. */
1072                   range->start = change->start;
1073                   j++;
1074                 }
1075               else
1076                 {
1077                   /* RANGE and CHANGE have different inheritability so insert
1078                      a copy of CHANGE into RANGELIST. */
1079                   svn_merge_range_t *change_copy =
1080                     svn_merge_range_dup(change, result_pool);
1081                   svn_sort__array_insert(rangelist, &change_copy, i);
1082                   j++;
1083                 }
1084             }
1085           else
1086             {
1087               /* RANGE and CHANGE overlap. */
1088               if (range->inheritable == change->inheritable)
1089                 {
1090                   /* RANGE and CHANGE have the same inheritability so we
1091                      can simply combine the two in place... */
1092                   range->start = change->start;
1093                   if (range->end < change->end)
1094                     {
1095                       /* ...but if RANGE is expanded ensure that we don't
1096                          violate any rangelist invariants. */
1097                       range->end = change->end;
1098                       adjust_remaining_ranges(rangelist, &i, result_pool);
1099                     }
1100                   j++;
1101                 }
1102               else if (range->inheritable)
1103                 {
1104                   if (change->start < range->start)
1105                     {
1106                       /* RANGE is inheritable so absorbs any part of CHANGE
1107                          it overlaps.  CHANGE is truncated and the remainder
1108                          inserted into RANGELIST. */
1109                       svn_merge_range_t *change_copy =
1110                         svn_merge_range_dup(change, result_pool);
1111                       change_copy->end = range->start;
1112                       change->start = range->start;
1113                       svn_sort__array_insert(rangelist, &change_copy, i++);
1114                     }
1115                   else
1116                     {
1117                       /* CHANGE and RANGE share the same start rev, but
1118                          CHANGE is considered older because CHANGE->END is
1119                          older than RANGE->END. */
1120                       j++;
1121                     }
1122                 }
1123               else
1124                 {
1125                   /* RANGE is non-inheritable and CHANGE is inheritable. */
1126                   if (change->start < range->start)
1127                     {
1128                       if (change->end == range->end)
1129                         {
1130                           /* RANGE is a proper subset of CHANGE and share the
1131                              same end revision, so set RANGE equal to CHANGE. */
1132                           range->start = change->start;
1133                           range->inheritable = TRUE;
1134                           j++;
1135                         }
1136                       else if (change->end > range->end)
1137                         {
1138                           /* RANGE is a proper subset of CHANGE and CHANGE has
1139                              a younger end revision, so set RANGE equal to its
1140                              intersection with CHANGE and truncate CHANGE. */
1141                           range->start = change->start;
1142                           range->inheritable = TRUE;
1143                           change->start = range->end;
1144                         }
1145                       else
1146                         {
1147                           /* CHANGE and RANGE overlap. Set RANGE equal to its
1148                              intersection with CHANGE and take the remainder
1149                              of RANGE and insert it into RANGELIST. */
1150                           svn_merge_range_t *range_copy =
1151                             svn_merge_range_dup(range, result_pool);
1152                           range_copy->start = change->end;
1153                           range->start = change->start;
1154                           range->end = change->end;
1155                           range->inheritable = TRUE;
1156                           svn_sort__array_insert(rangelist, &range_copy, ++i);
1157                           j++;
1158                         }
1159                     }
1160                   else
1161                     {
1162                       /* CHANGE and RANGE share the same start rev, but
1163                          CHANGE is considered older because its end rev
1164                          is older.
1165
1166                          Insert the intersection of RANGE and CHANGE into
1167                          RANGELIST and then set RANGE to the non-intersecting
1168                          portion of RANGE. */
1169                       svn_merge_range_t *range_copy =
1170                         svn_merge_range_dup(range, result_pool);
1171                       range_copy->end = change->end;
1172                       range_copy->inheritable = TRUE;
1173                       range->start = change->end;
1174                       svn_sort__array_insert(rangelist, &range_copy, i++);
1175                       j++;
1176                     }
1177                 }
1178             }
1179         }
1180     }
1181
1182   /* Copy any remaining elements in CHANGES into RANGELIST. */
1183   for (; j < (changes)->nelts; j++)
1184     {
1185       svn_merge_range_t *change =
1186         APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
1187       svn_merge_range_t *change_copy = svn_merge_range_dup(change,
1188                                                            result_pool);
1189       svn_sort__array_insert(rangelist, &change_copy, rangelist->nelts);
1190     }
1191
1192   return SVN_NO_ERROR;
1193 }
1194
1195 /* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
1196  * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
1197 static svn_boolean_t
1198 range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
1199                 svn_boolean_t consider_inheritance)
1200 {
1201   SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1202   SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1203
1204   return (first->start + 1 <= second->end)
1205     && (second->start + 1 <= first->end)
1206     && (!consider_inheritance
1207         || (!(first->inheritable) == !(second->inheritable)));
1208 }
1209
1210 /* Return TRUE iff the forward revision range FIRST wholly contains the
1211  * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
1212  * the same inheritability. */
1213 static svn_boolean_t
1214 range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
1215                svn_boolean_t consider_inheritance)
1216 {
1217   SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1218   SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1219
1220   return (first->start <= second->start) && (second->end <= first->end)
1221     && (!consider_inheritance
1222         || (!(first->inheritable) == !(second->inheritable)));
1223 }
1224
1225 /* Swap start and end fields of RANGE. */
1226 static void
1227 range_swap_endpoints(svn_merge_range_t *range)
1228 {
1229   svn_revnum_t swap = range->start;
1230   range->start = range->end;
1231   range->end = swap;
1232 }
1233
1234 svn_error_t *
1235 svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool)
1236 {
1237   int i;
1238
1239   svn_sort__array_reverse(rangelist, pool);
1240
1241   for (i = 0; i < rangelist->nelts; i++)
1242     {
1243       range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
1244     }
1245
1246   return SVN_NO_ERROR;
1247 }
1248
1249 void
1250 svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
1251                                svn_boolean_t inheritable)
1252 {
1253   if (rangelist)
1254     {
1255       int i;
1256       svn_merge_range_t *range;
1257
1258       for (i = 0; i < rangelist->nelts; i++)
1259         {
1260           range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1261           range->inheritable = inheritable;
1262         }
1263     }
1264   return;
1265 }
1266
1267 void
1268 svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
1269                                svn_boolean_t inheritable,
1270                                apr_pool_t *scratch_pool)
1271 {
1272   if (mergeinfo)
1273     {
1274       apr_hash_index_t *hi;
1275
1276       for (hi = apr_hash_first(scratch_pool, mergeinfo);
1277            hi;
1278            hi = apr_hash_next(hi))
1279         {
1280           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
1281
1282           if (rangelist)
1283             svn_rangelist__set_inheritance(rangelist, inheritable);
1284         }
1285     }
1286   return;
1287 }
1288
1289 /* If DO_REMOVE is true, then remove any overlapping ranges described by
1290    RANGELIST1 from RANGELIST2 and place the results in *OUTPUT.  When
1291    DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
1292    the "whiteboard".
1293
1294    If DO_REMOVE is false, then capture the intersection between RANGELIST1
1295    and RANGELIST2 and place the results in *OUTPUT.  The ordering of
1296    RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
1297
1298    If CONSIDER_INHERITANCE is true, then take the inheritance of the
1299    ranges in RANGELIST1 and RANGELIST2 into account when comparing them
1300    for intersection, see the doc string for svn_rangelist_intersect().
1301
1302    If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
1303    may intersect, but the resulting intersection is non-inheritable only
1304    if both ranges were non-inheritable, e.g.:
1305
1306    RANGELIST1  RANGELIST2  CONSIDER     DO_REMOVE  *OUTPUT
1307                            INHERITANCE
1308    ----------  ------      -----------  ---------  -------
1309
1310    90-420*     1-100       TRUE         FALSE      Empty Rangelist
1311    90-420      1-100*      TRUE         FALSE      Empty Rangelist
1312    90-420      1-100       TRUE         FALSE      90-100
1313    90-420*     1-100*      TRUE         FALSE      90-100*
1314
1315    90-420*     1-100       FALSE        FALSE      90-100
1316    90-420      1-100*      FALSE        FALSE      90-100
1317    90-420      1-100       FALSE        FALSE      90-100
1318    90-420*     1-100*      FALSE        FALSE      90-100*
1319
1320    Allocate the contents of *OUTPUT in POOL. */
1321 static svn_error_t *
1322 rangelist_intersect_or_remove(svn_rangelist_t **output,
1323                               const svn_rangelist_t *rangelist1,
1324                               const svn_rangelist_t *rangelist2,
1325                               svn_boolean_t do_remove,
1326                               svn_boolean_t consider_inheritance,
1327                               apr_pool_t *pool)
1328 {
1329   int i1, i2, lasti2;
1330   svn_merge_range_t working_elt2;
1331
1332   *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1333
1334   i1 = 0;
1335   i2 = 0;
1336   lasti2 = -1;  /* Initialized to a value that "i2" will never be. */
1337
1338   while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
1339     {
1340       svn_merge_range_t *elt1, *elt2;
1341
1342       elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
1343
1344       /* Instead of making a copy of the entire array of rangelist2
1345          elements, we just keep a copy of the current rangelist2 element
1346          that needs to be used, and modify our copy if necessary. */
1347       if (i2 != lasti2)
1348         {
1349           working_elt2 =
1350             *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
1351           lasti2 = i2;
1352         }
1353
1354       elt2 = &working_elt2;
1355
1356       /* If the rangelist2 range is contained completely in the
1357          rangelist1, we increment the rangelist2.
1358          If the ranges intersect, and match exactly, we increment both
1359          rangelist1 and rangelist2.
1360          Otherwise, we have to generate a range for the left part of
1361          the removal of rangelist1 from rangelist2, and possibly change
1362          the rangelist2 to the remaining portion of the right part of
1363          the removal, to test against. */
1364       if (range_contains(elt1, elt2, consider_inheritance))
1365         {
1366           if (!do_remove)
1367             {
1368               svn_merge_range_t tmp_range;
1369               tmp_range.start = elt2->start;
1370               tmp_range.end = elt2->end;
1371               /* The intersection of two ranges is non-inheritable only
1372                  if both ranges are non-inheritable. */
1373               tmp_range.inheritable =
1374                 (elt2->inheritable || elt1->inheritable);
1375               SVN_ERR(combine_with_lastrange(&tmp_range, *output,
1376                                              consider_inheritance,
1377                                              pool));
1378             }
1379
1380           i2++;
1381
1382           if (elt2->start == elt1->start && elt2->end == elt1->end)
1383             i1++;
1384         }
1385       else if (range_intersect(elt1, elt2, consider_inheritance))
1386         {
1387           if (elt2->start < elt1->start)
1388             {
1389               /* The rangelist2 range starts before the rangelist1 range. */
1390               svn_merge_range_t tmp_range;
1391               if (do_remove)
1392                 {
1393                   /* Retain the range that falls before the rangelist1
1394                      start. */
1395                   tmp_range.start = elt2->start;
1396                   tmp_range.end = elt1->start;
1397                   tmp_range.inheritable = elt2->inheritable;
1398                 }
1399               else
1400                 {
1401                   /* Retain the range that falls between the rangelist1
1402                      start and rangelist2 end. */
1403                   tmp_range.start = elt1->start;
1404                   tmp_range.end = MIN(elt2->end, elt1->end);
1405                   /* The intersection of two ranges is non-inheritable only
1406                      if both ranges are non-inheritable. */
1407                   tmp_range.inheritable =
1408                     (elt2->inheritable || elt1->inheritable);
1409                 }
1410
1411               SVN_ERR(combine_with_lastrange(&tmp_range,
1412                                              *output, consider_inheritance,
1413                                              pool));
1414             }
1415
1416           /* Set up the rest of the rangelist2 range for further
1417              processing.  */
1418           if (elt2->end > elt1->end)
1419             {
1420               /* The rangelist2 range ends after the rangelist1 range. */
1421               if (!do_remove)
1422                 {
1423                   /* Partial overlap. */
1424                   svn_merge_range_t tmp_range;
1425                   tmp_range.start = MAX(elt2->start, elt1->start);
1426                   tmp_range.end = elt1->end;
1427                   /* The intersection of two ranges is non-inheritable only
1428                      if both ranges are non-inheritable. */
1429                   tmp_range.inheritable =
1430                     (elt2->inheritable || elt1->inheritable);
1431                   SVN_ERR(combine_with_lastrange(&tmp_range,
1432                                                  *output,
1433                                                  consider_inheritance,
1434                                                  pool));
1435                 }
1436
1437               working_elt2.start = elt1->end;
1438               working_elt2.end = elt2->end;
1439             }
1440           else
1441             i2++;
1442         }
1443       else  /* ranges don't intersect */
1444         {
1445           /* See which side of the rangelist2 the rangelist1 is on.  If it
1446              is on the left side, we need to move the rangelist1.
1447
1448              If it is on past the rangelist2 on the right side, we
1449              need to output the rangelist2 and increment the
1450              rangelist2.  */
1451           if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
1452             i1++;
1453           else
1454             {
1455               svn_merge_range_t *lastrange;
1456
1457               if ((*output)->nelts > 0)
1458                 lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1,
1459                                           svn_merge_range_t *);
1460               else
1461                 lastrange = NULL;
1462
1463               if (do_remove && !(lastrange &&
1464                                  combine_ranges(lastrange, lastrange, elt2,
1465                                                 consider_inheritance)))
1466                 {
1467                   lastrange = svn_merge_range_dup(elt2, pool);
1468                   APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
1469                 }
1470               i2++;
1471             }
1472         }
1473     }
1474
1475   if (do_remove)
1476     {
1477       /* Copy the current rangelist2 element if we didn't hit the end
1478          of the rangelist2, and we still had it around.  This element
1479          may have been touched, so we can't just walk the rangelist2
1480          array, we have to use our copy.  This case only happens when
1481          we ran out of rangelist1 before rangelist2, *and* we had changed
1482          the rangelist2 element. */
1483       if (i2 == lasti2 && i2 < rangelist2->nelts)
1484         {
1485           SVN_ERR(combine_with_lastrange(&working_elt2, *output,
1486                                          consider_inheritance, pool));
1487           i2++;
1488         }
1489
1490       /* Copy any other remaining untouched rangelist2 elements.  */
1491       for (; i2 < rangelist2->nelts; i2++)
1492         {
1493           svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
1494                                                  svn_merge_range_t *);
1495
1496           SVN_ERR(combine_with_lastrange(elt, *output,
1497                                          consider_inheritance, pool));
1498         }
1499     }
1500
1501   return SVN_NO_ERROR;
1502 }
1503
1504
1505 svn_error_t *
1506 svn_rangelist_intersect(svn_rangelist_t **output,
1507                         const svn_rangelist_t *rangelist1,
1508                         const svn_rangelist_t *rangelist2,
1509                         svn_boolean_t consider_inheritance,
1510                         apr_pool_t *pool)
1511 {
1512   return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
1513                                        consider_inheritance, pool);
1514 }
1515
1516 svn_error_t *
1517 svn_rangelist_remove(svn_rangelist_t **output,
1518                      const svn_rangelist_t *eraser,
1519                      const svn_rangelist_t *whiteboard,
1520                      svn_boolean_t consider_inheritance,
1521                      apr_pool_t *pool)
1522 {
1523   return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
1524                                        consider_inheritance, pool);
1525 }
1526
1527 svn_error_t *
1528 svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added,
1529                    const svn_rangelist_t *from, const svn_rangelist_t *to,
1530                    svn_boolean_t consider_inheritance,
1531                    apr_pool_t *pool)
1532 {
1533   /* The following diagrams illustrate some common range delta scenarios:
1534
1535      (from)           deleted
1536      r0 <===========(=========)============[=========]===========> rHEAD
1537      [to]                                    added
1538
1539      (from)           deleted                deleted
1540      r0 <===========(=========[============]=========)===========> rHEAD
1541      [to]
1542
1543      (from)           deleted
1544      r0 <===========(=========[============)=========]===========> rHEAD
1545      [to]                                    added
1546
1547      (from)                                  deleted
1548      r0 <===========[=========(============]=========)===========> rHEAD
1549      [to]             added
1550
1551      (from)
1552      r0 <===========[=========(============)=========]===========> rHEAD
1553      [to]             added                  added
1554
1555      (from)  d                                  d             d
1556      r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
1557      [to]        a   a    a   a   a   a                   a
1558   */
1559
1560   /* The items that are present in from, but not in to, must have been
1561      deleted. */
1562   SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance,
1563                                pool));
1564   /* The items that are present in to, but not in from, must have been
1565      added.  */
1566   return svn_rangelist_remove(added, from, to, consider_inheritance, pool);
1567 }
1568
1569 struct mergeinfo_diff_baton
1570 {
1571   svn_mergeinfo_t from;
1572   svn_mergeinfo_t to;
1573   svn_mergeinfo_t deleted;
1574   svn_mergeinfo_t added;
1575   svn_boolean_t consider_inheritance;
1576   apr_pool_t *pool;
1577 };
1578
1579 /* This implements the 'svn_hash_diff_func_t' interface.
1580    BATON is of type 'struct mergeinfo_diff_baton *'.
1581 */
1582 static svn_error_t *
1583 mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen,
1584                        enum svn_hash_diff_key_status status,
1585                        void *baton)
1586 {
1587   /* hash_a is FROM mergeinfo,
1588      hash_b is TO mergeinfo. */
1589   struct mergeinfo_diff_baton *cb = baton;
1590   svn_rangelist_t *from_rangelist, *to_rangelist;
1591   const char *path = key;
1592   if (status == svn_hash_diff_key_both)
1593     {
1594       /* Record any deltas (additions or deletions). */
1595       svn_rangelist_t *deleted_rangelist, *added_rangelist;
1596       from_rangelist = apr_hash_get(cb->from, path, klen);
1597       to_rangelist = apr_hash_get(cb->to, path, klen);
1598       SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
1599                                  from_rangelist, to_rangelist,
1600                                  cb->consider_inheritance, cb->pool));
1601       if (cb->deleted && deleted_rangelist->nelts > 0)
1602         apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
1603                      klen, deleted_rangelist);
1604       if (cb->added && added_rangelist->nelts > 0)
1605         apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
1606                      klen, added_rangelist);
1607     }
1608   else if ((status == svn_hash_diff_key_a) && cb->deleted)
1609     {
1610       from_rangelist = apr_hash_get(cb->from, path, klen);
1611       apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
1612                    svn_rangelist_dup(from_rangelist, cb->pool));
1613     }
1614   else if ((status == svn_hash_diff_key_b) && cb->added)
1615     {
1616       to_rangelist = apr_hash_get(cb->to, path, klen);
1617       apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
1618                    svn_rangelist_dup(to_rangelist, cb->pool));
1619     }
1620   return SVN_NO_ERROR;
1621 }
1622
1623 /* Record deletions and additions of entire range lists (by path
1624    presence), and delegate to svn_rangelist_diff() for delta
1625    calculations on a specific path.  */
1626 static svn_error_t *
1627 walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to,
1628                              svn_mergeinfo_t deleted, svn_mergeinfo_t added,
1629                              svn_boolean_t consider_inheritance,
1630                              apr_pool_t *result_pool,
1631                              apr_pool_t *scratch_pool)
1632 {
1633   struct mergeinfo_diff_baton mdb;
1634   mdb.from = from;
1635   mdb.to = to;
1636   mdb.deleted = deleted;
1637   mdb.added = added;
1638   mdb.consider_inheritance = consider_inheritance;
1639   mdb.pool = result_pool;
1640
1641   return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool);
1642 }
1643
1644 svn_error_t *
1645 svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
1646                     svn_mergeinfo_t from, svn_mergeinfo_t to,
1647                     svn_boolean_t consider_inheritance,
1648                     apr_pool_t *result_pool,
1649                     apr_pool_t *scratch_pool)
1650 {
1651   if (from && to == NULL)
1652     {
1653       *deleted = svn_mergeinfo_dup(from, result_pool);
1654       *added = svn_hash__make(result_pool);
1655     }
1656   else if (from == NULL && to)
1657     {
1658       *deleted = svn_hash__make(result_pool);
1659       *added = svn_mergeinfo_dup(to, result_pool);
1660     }
1661   else
1662     {
1663       *deleted = svn_hash__make(result_pool);
1664       *added = svn_hash__make(result_pool);
1665
1666       if (from && to)
1667         {
1668           SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
1669                                                consider_inheritance,
1670                                                result_pool, scratch_pool));
1671         }
1672     }
1673
1674   return SVN_NO_ERROR;
1675 }
1676
1677 svn_error_t *
1678 svn_mergeinfo__equals(svn_boolean_t *is_equal,
1679                       svn_mergeinfo_t info1,
1680                       svn_mergeinfo_t info2,
1681                       svn_boolean_t consider_inheritance,
1682                       apr_pool_t *pool)
1683 {
1684   apr_hash_index_t *hi;
1685
1686   *is_equal = FALSE;
1687
1688   /* special cases: at least one side has no merge info */
1689   if (info1 == NULL && info2 == NULL)
1690     {
1691       *is_equal = TRUE;
1692       return SVN_NO_ERROR;
1693     }
1694
1695   if (info1 == NULL ||  info2 == NULL)
1696     return SVN_NO_ERROR;
1697
1698   /* trivial case: different number of paths -> unequal */
1699   if (apr_hash_count(info1) != apr_hash_count(info2))
1700     return SVN_NO_ERROR;
1701
1702   /* compare range lists for all paths */
1703   for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
1704     {
1705       const char *key;
1706       apr_ssize_t key_length;
1707       svn_rangelist_t *lhs, *rhs;
1708       int i;
1709       svn_rangelist_t *deleted, *added;
1710
1711       /* get both path lists */
1712       apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
1713       rhs = apr_hash_get(info2, key, key_length);
1714
1715       /* missing on one side? */
1716       if (rhs == NULL)
1717         return SVN_NO_ERROR;
1718
1719       /* quick compare: the range lists will often be a perfect match */
1720       if (lhs->nelts == rhs->nelts)
1721         {
1722           for (i = 0; i < lhs->nelts; ++i)
1723             {
1724               svn_merge_range_t *lrange
1725                 = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
1726               svn_merge_range_t *rrange
1727                 = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
1728
1729               /* range mismatch? -> needs detailed comparison */
1730               if (   lrange->start != rrange->start
1731                   || lrange->end != rrange->end)
1732                 break;
1733
1734               /* inheritance mismatch? -> merge info differs */
1735               if (   consider_inheritance
1736                   && lrange->inheritable != rrange->inheritable)
1737                 return SVN_NO_ERROR;
1738             }
1739
1740           /* all ranges found to match -> next path */
1741           if (i == lhs->nelts)
1742             continue;
1743         }
1744
1745       /* range lists differ but there are many ways to sort and aggregate
1746          revisions into ranges. Do a full diff on them. */
1747       SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
1748                                   consider_inheritance, pool));
1749       if (deleted->nelts || added->nelts)
1750         return SVN_NO_ERROR;
1751     }
1752
1753   /* no mismatch found */
1754   *is_equal = TRUE;
1755   return SVN_NO_ERROR;
1756 }
1757
1758 svn_error_t *
1759 svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
1760                      svn_mergeinfo_t changes,
1761                      apr_pool_t *result_pool,
1762                      apr_pool_t *scratch_pool)
1763 {
1764   apr_hash_index_t *hi;
1765   apr_pool_t *iterpool;
1766
1767   if (!apr_hash_count(changes))
1768     return SVN_NO_ERROR;
1769
1770   iterpool = svn_pool_create(scratch_pool);
1771   for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
1772     {
1773       const char *key;
1774       apr_ssize_t klen;
1775       svn_rangelist_t *to_insert;
1776       svn_rangelist_t *target;
1777
1778       /* get ranges to insert and the target ranges list of that insertion */
1779       apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
1780       target = apr_hash_get(mergeinfo, key, klen);
1781
1782       /* if range list exists, just expand on it.
1783        * Otherwise, add new hash entry. */
1784       if (target)
1785         {
1786           SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
1787                                        iterpool));
1788           svn_pool_clear(iterpool);
1789         }
1790       else
1791         apr_hash_set(mergeinfo, key, klen, to_insert);
1792     }
1793
1794   svn_pool_destroy(iterpool);
1795
1796   return SVN_NO_ERROR;
1797 }
1798
1799 svn_error_t *
1800 svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat,
1801                             svn_mergeinfo_catalog_t changes_cat,
1802                             apr_pool_t *result_pool,
1803                             apr_pool_t *scratch_pool)
1804 {
1805   int i = 0;
1806   int j = 0;
1807   apr_array_header_t *sorted_cat =
1808     svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths,
1809                    scratch_pool);
1810   apr_array_header_t *sorted_changes =
1811     svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths,
1812                    scratch_pool);
1813
1814   while (i < sorted_cat->nelts && j < sorted_changes->nelts)
1815     {
1816       svn_sort__item_t cat_elt, change_elt;
1817       int res;
1818
1819       cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t);
1820       change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t);
1821       res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt);
1822
1823       if (res == 0) /* Both catalogs have mergeinfo for a given path. */
1824         {
1825           svn_mergeinfo_t mergeinfo = cat_elt.value;
1826           svn_mergeinfo_t changes_mergeinfo = change_elt.value;
1827
1828           SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
1829                                        result_pool, scratch_pool));
1830           apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
1831           i++;
1832           j++;
1833         }
1834       else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
1835         {
1836           i++;
1837         }
1838       else /* Only CHANGES_CAT has mergeinfo for this path. */
1839         {
1840           apr_hash_set(mergeinfo_cat,
1841                        apr_pstrdup(result_pool, change_elt.key),
1842                        change_elt.klen,
1843                        svn_mergeinfo_dup(change_elt.value, result_pool));
1844           j++;
1845         }
1846     }
1847
1848   /* Copy back any remaining elements from the CHANGES_CAT catalog. */
1849   for (; j < sorted_changes->nelts; j++)
1850     {
1851       svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j,
1852                                            svn_sort__item_t);
1853       apr_hash_set(mergeinfo_cat,
1854                    apr_pstrdup(result_pool, elt.key),
1855                    elt.klen,
1856                    svn_mergeinfo_dup(elt.value, result_pool));
1857     }
1858
1859   return SVN_NO_ERROR;
1860 }
1861
1862 svn_error_t *
1863 svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
1864                          svn_mergeinfo_t mergeinfo1,
1865                          svn_mergeinfo_t mergeinfo2,
1866                          svn_boolean_t consider_inheritance,
1867                          apr_pool_t *result_pool,
1868                          apr_pool_t *scratch_pool)
1869 {
1870   apr_hash_index_t *hi;
1871   apr_pool_t *iterpool;
1872
1873   *mergeinfo = apr_hash_make(result_pool);
1874   iterpool = svn_pool_create(scratch_pool);
1875
1876   /* ### TODO(reint): Do we care about the case when a path in one
1877      ### mergeinfo hash has inheritable mergeinfo, and in the other
1878      ### has non-inheritable mergeinfo?  It seems like that path
1879      ### itself should really be an intersection, while child paths
1880      ### should not be... */
1881   for (hi = apr_hash_first(scratch_pool, mergeinfo1);
1882        hi; hi = apr_hash_next(hi))
1883     {
1884       const char *path = apr_hash_this_key(hi);
1885       svn_rangelist_t *rangelist1 = apr_hash_this_val(hi);
1886       svn_rangelist_t *rangelist2;
1887
1888       svn_pool_clear(iterpool);
1889       rangelist2 = svn_hash_gets(mergeinfo2, path);
1890       if (rangelist2)
1891         {
1892           SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
1893                                           consider_inheritance, iterpool));
1894           if (rangelist2->nelts > 0)
1895             svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path),
1896                           svn_rangelist_dup(rangelist2, result_pool));
1897         }
1898     }
1899   svn_pool_destroy(iterpool);
1900   return SVN_NO_ERROR;
1901 }
1902
1903 svn_error_t *
1904 svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
1905                       svn_mergeinfo_t eraser,
1906                       svn_mergeinfo_t whiteboard,
1907                       svn_boolean_t consider_inheritance,
1908                       apr_pool_t *result_pool,
1909                       apr_pool_t *scratch_pool)
1910 {
1911   *mergeinfo = apr_hash_make(result_pool);
1912   return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
1913                                       consider_inheritance, result_pool,
1914                                       scratch_pool);
1915 }
1916
1917 svn_error_t *
1918 svn_rangelist_to_string(svn_string_t **output,
1919                         const svn_rangelist_t *rangelist,
1920                         apr_pool_t *pool)
1921 {
1922   svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
1923
1924   if (rangelist->nelts > 0)
1925     {
1926       int i;
1927       svn_merge_range_t *range;
1928
1929       /* Handle the elements that need commas at the end.  */
1930       for (i = 0; i < rangelist->nelts - 1; i++)
1931         {
1932           range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1933           svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1934           svn_stringbuf_appendcstr(buf, ",");
1935         }
1936
1937       /* Now handle the last element, which needs no comma.  */
1938       range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1939       svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1940     }
1941
1942   *output = svn_stringbuf__morph_into_string(buf);
1943
1944   return SVN_NO_ERROR;
1945 }
1946
1947 /* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT.  If PREFIX
1948    is not NULL then prepend PREFIX to each line in OUTPUT.  If INPUT contains
1949    no elements, return the empty string.  If INPUT contains any merge source
1950    path keys that are relative then convert these to absolute paths in
1951    *OUTPUT.
1952  */
1953 static svn_error_t *
1954 mergeinfo_to_stringbuf(svn_stringbuf_t **output,
1955                        svn_mergeinfo_t input,
1956                        const char *prefix,
1957                        apr_pool_t *pool)
1958 {
1959   *output = svn_stringbuf_create_empty(pool);
1960
1961   if (apr_hash_count(input) > 0)
1962     {
1963       apr_array_header_t *sorted =
1964         svn_sort__hash(input, svn_sort_compare_items_as_paths, pool);
1965       int i;
1966
1967       for (i = 0; i < sorted->nelts; i++)
1968         {
1969           svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t);
1970           svn_string_t *revlist;
1971
1972           SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
1973           svn_stringbuf_appendcstr(
1974             *output,
1975             apr_psprintf(pool, "%s%s%s:%s",
1976                          prefix ? prefix : "",
1977                          *((const char *) elt.key) == '/' ? "" : "/",
1978                          (const char *) elt.key,
1979                          revlist->data));
1980           if (i < sorted->nelts - 1)
1981             svn_stringbuf_appendcstr(*output, "\n");
1982         }
1983     }
1984
1985   return SVN_NO_ERROR;
1986 }
1987
1988 svn_error_t *
1989 svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
1990                         apr_pool_t *pool)
1991 {
1992   svn_stringbuf_t *mergeinfo_buf;
1993
1994   SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
1995   *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
1996   return SVN_NO_ERROR;
1997 }
1998
1999 svn_error_t *
2000 svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
2001 {
2002   apr_hash_index_t *hi;
2003
2004   for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
2005     {
2006       apr_array_header_t *rl = apr_hash_this_val(hi);
2007
2008       svn_sort__array(rl, svn_sort_compare_ranges);
2009     }
2010   return SVN_NO_ERROR;
2011 }
2012
2013 svn_error_t *
2014 svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo,
2015                                    apr_pool_t *scratch_pool)
2016 {
2017   apr_hash_index_t *hi;
2018
2019   for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi))
2020     {
2021       apr_array_header_t *rl = apr_hash_this_val(hi);
2022
2023       SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool));
2024     }
2025
2026   return SVN_NO_ERROR;
2027 }
2028
2029 svn_mergeinfo_catalog_t
2030 svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog,
2031                           apr_pool_t *pool)
2032 {
2033   svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool);
2034   apr_hash_index_t *hi;
2035
2036   for (hi = apr_hash_first(pool, mergeinfo_catalog);
2037        hi;
2038        hi = apr_hash_next(hi))
2039     {
2040       const char *key = apr_hash_this_key(hi);
2041       svn_mergeinfo_t val = apr_hash_this_val(hi);
2042
2043       svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key),
2044                     svn_mergeinfo_dup(val, pool));
2045     }
2046
2047   return new_mergeinfo_catalog;
2048 }
2049
2050 svn_mergeinfo_t
2051 svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
2052 {
2053   svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
2054   apr_hash_index_t *hi;
2055
2056   for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2057     {
2058       const char *path = apr_hash_this_key(hi);
2059       apr_ssize_t pathlen = apr_hash_this_key_len(hi);
2060       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2061
2062       apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
2063                    svn_rangelist_dup(rangelist, pool));
2064     }
2065
2066   return new_mergeinfo;
2067 }
2068
2069 svn_error_t *
2070 svn_mergeinfo_inheritable2(svn_mergeinfo_t *output,
2071                            svn_mergeinfo_t mergeinfo,
2072                            const char *path,
2073                            svn_revnum_t start,
2074                            svn_revnum_t end,
2075                            svn_boolean_t inheritable,
2076                            apr_pool_t *result_pool,
2077                            apr_pool_t *scratch_pool)
2078 {
2079   apr_hash_index_t *hi;
2080   svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
2081
2082   for (hi = apr_hash_first(scratch_pool, mergeinfo);
2083        hi;
2084        hi = apr_hash_next(hi))
2085     {
2086       const char *key = apr_hash_this_key(hi);
2087       apr_ssize_t keylen = apr_hash_this_key_len(hi);
2088       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2089       svn_rangelist_t *inheritable_rangelist;
2090
2091       if (!path || svn_path_compare_paths(path, key) == 0)
2092         SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist,
2093                                            start, end, inheritable,
2094                                            result_pool, scratch_pool));
2095       else
2096         inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool);
2097
2098       /* Only add this rangelist if some ranges remain.  A rangelist with
2099          a path mapped to an empty rangelist is not syntactically valid */
2100       if (inheritable_rangelist->nelts)
2101         apr_hash_set(inheritable_mergeinfo,
2102                      apr_pstrmemdup(result_pool, key, keylen), keylen,
2103                      inheritable_rangelist);
2104     }
2105   *output = inheritable_mergeinfo;
2106   return SVN_NO_ERROR;
2107 }
2108
2109
2110 svn_error_t *
2111 svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist,
2112                            const svn_rangelist_t *rangelist,
2113                            svn_revnum_t start,
2114                            svn_revnum_t end,
2115                            svn_boolean_t inheritable,
2116                            apr_pool_t *result_pool,
2117                            apr_pool_t *scratch_pool)
2118 {
2119   *inheritable_rangelist = apr_array_make(result_pool, 1,
2120                                           sizeof(svn_merge_range_t *));
2121   if (rangelist->nelts)
2122     {
2123       if (!SVN_IS_VALID_REVNUM(start)
2124           || !SVN_IS_VALID_REVNUM(end)
2125           || end < start)
2126         {
2127           /* We want all (non-inheritable or inheritable) ranges removed. */
2128           int i;
2129
2130           for (i = 0; i < rangelist->nelts; i++)
2131             {
2132               svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2133                                                        svn_merge_range_t *);
2134               if (range->inheritable == inheritable)
2135                 {
2136                   APR_ARRAY_PUSH(*inheritable_rangelist, svn_merge_range_t *)
2137                     = svn_merge_range_dup(range, result_pool);
2138                 }
2139             }
2140         }
2141       else
2142         {
2143           /* We want only the (non-inheritable or inheritable) ranges
2144              bound by START and END removed. */
2145           svn_rangelist_t *ranges_inheritable =
2146             svn_rangelist__initialize(start, end, inheritable, scratch_pool);
2147
2148           if (rangelist->nelts)
2149             SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
2150                                          ranges_inheritable,
2151                                          rangelist,
2152                                          TRUE,
2153                                          result_pool));
2154         }
2155     }
2156   return SVN_NO_ERROR;
2157 }
2158
2159 svn_boolean_t
2160 svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
2161                                        apr_pool_t *scratch_pool)
2162 {
2163   apr_hash_index_t *hi;
2164   svn_boolean_t removed_some_ranges = FALSE;
2165
2166   if (mergeinfo)
2167     {
2168       for (hi = apr_hash_first(scratch_pool, mergeinfo); hi;
2169            hi = apr_hash_next(hi))
2170         {
2171           const char *path = apr_hash_this_key(hi);
2172           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2173
2174           if (rangelist->nelts == 0)
2175             {
2176               svn_hash_sets(mergeinfo, path, NULL);
2177               removed_some_ranges = TRUE;
2178             }
2179         }
2180     }
2181   return removed_some_ranges;
2182 }
2183
2184 svn_error_t *
2185 svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
2186                                           svn_mergeinfo_catalog_t in_catalog,
2187                                           const char *prefix_path,
2188                                           apr_pool_t *pool)
2189 {
2190   apr_hash_index_t *hi;
2191
2192   SVN_ERR_ASSERT(prefix_path[0] == '/');
2193
2194   *out_catalog = apr_hash_make(pool);
2195
2196   for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
2197     {
2198       const char *original_path = apr_hash_this_key(hi);
2199       svn_mergeinfo_t value = apr_hash_this_val(hi);
2200       const char *new_path;
2201
2202       new_path = svn_fspath__skip_ancestor(prefix_path, original_path);
2203       SVN_ERR_ASSERT(new_path);
2204
2205       svn_hash_sets(*out_catalog, new_path, value);
2206     }
2207
2208   return SVN_NO_ERROR;
2209 }
2210
2211 svn_error_t *
2212 svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
2213                                      svn_mergeinfo_catalog_t in_catalog,
2214                                      const char *prefix_path,
2215                                      apr_pool_t *result_pool,
2216                                      apr_pool_t *scratch_pool)
2217 {
2218   apr_hash_index_t *hi;
2219
2220   *out_catalog = apr_hash_make(result_pool);
2221
2222   for (hi = apr_hash_first(scratch_pool, in_catalog);
2223        hi;
2224        hi = apr_hash_next(hi))
2225     {
2226       const char *original_path = apr_hash_this_key(hi);
2227       svn_mergeinfo_t value = apr_hash_this_val(hi);
2228
2229       if (original_path[0] == '/')
2230         original_path++;
2231
2232       svn_hash_sets(*out_catalog,
2233                     svn_dirent_join(prefix_path, original_path, result_pool),
2234                     value);
2235     }
2236
2237   return SVN_NO_ERROR;
2238 }
2239
2240 svn_error_t *
2241 svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
2242                                        svn_mergeinfo_t mergeinfo,
2243                                        const char *suffix_relpath,
2244                                        apr_pool_t *result_pool,
2245                                        apr_pool_t *scratch_pool)
2246 {
2247   apr_hash_index_t *hi;
2248
2249   SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath));
2250
2251   *out_mergeinfo = apr_hash_make(result_pool);
2252
2253   for (hi = apr_hash_first(scratch_pool, mergeinfo);
2254        hi;
2255        hi = apr_hash_next(hi))
2256     {
2257       const char *fspath = apr_hash_this_key(hi);
2258       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2259
2260       svn_hash_sets(*out_mergeinfo,
2261                     svn_fspath__join(fspath, suffix_relpath, result_pool),
2262                     rangelist);
2263     }
2264
2265   return SVN_NO_ERROR;
2266 }
2267
2268 /* Deep-copy an array of pointers to simple objects.
2269  *
2270  * Return a duplicate in POOL of the array ARRAY of pointers to objects
2271  * of size OBJECT_SIZE bytes. Duplicate each object bytewise.
2272  */
2273 static apr_array_header_t *
2274 ptr_array_dup(const apr_array_header_t *array,
2275               size_t object_size,
2276               apr_pool_t *pool)
2277 {
2278   apr_array_header_t *new_array = apr_array_make(pool, array->nelts,
2279                                                  sizeof(void *));
2280
2281   /* allocate target range buffer with a single operation */
2282   char *copy = apr_palloc(pool, object_size * array->nelts);
2283
2284   /* for efficiency, directly address source and target reference buffers */
2285   void **source = (void **)(array->elts);
2286   void **target = (void **)(new_array->elts);
2287   int i;
2288
2289   /* copy ranges iteratively and link them into the target range list */
2290   for (i = 0; i < array->nelts; i++)
2291     {
2292       target[i] = &copy[i * object_size];
2293       memcpy(target[i], source[i], object_size);
2294     }
2295   new_array->nelts = array->nelts;
2296
2297   return new_array;
2298 }
2299
2300 svn_rangelist_t *
2301 svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool)
2302 {
2303   return ptr_array_dup(rangelist, sizeof(svn_merge_range_t), pool);
2304 }
2305
2306 svn_merge_range_t *
2307 svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool)
2308 {
2309   svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range));
2310   memcpy(new_range, range, sizeof(*new_range));
2311   return new_range;
2312 }
2313
2314 svn_boolean_t
2315 svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev)
2316 {
2317   assert(SVN_IS_VALID_REVNUM(range->start));
2318   assert(SVN_IS_VALID_REVNUM(range->end));
2319   assert(range->start != range->end);
2320
2321   if (range->start < range->end)
2322     return rev > range->start && rev <= range->end;
2323   else
2324     return rev > range->end && rev <= range->start;
2325 }
2326
2327 svn_error_t *
2328 svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
2329                                            svn_mergeinfo_catalog_t catalog,
2330                                            const char *key_prefix,
2331                                            const char *val_prefix,
2332                                            apr_pool_t *pool)
2333 {
2334   svn_stringbuf_t *output_buf = NULL;
2335
2336   if (catalog && apr_hash_count(catalog))
2337     {
2338       int i;
2339       apr_array_header_t *sorted_catalog =
2340         svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
2341
2342       output_buf = svn_stringbuf_create_empty(pool);
2343       for (i = 0; i < sorted_catalog->nelts; i++)
2344         {
2345           svn_sort__item_t elt =
2346             APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t);
2347           const char *path1;
2348           svn_mergeinfo_t mergeinfo;
2349           svn_stringbuf_t *mergeinfo_output_buf;
2350
2351           path1 = elt.key;
2352           mergeinfo = elt.value;
2353           if (key_prefix)
2354             svn_stringbuf_appendcstr(output_buf, key_prefix);
2355           svn_stringbuf_appendcstr(output_buf, path1);
2356           svn_stringbuf_appendcstr(output_buf, "\n");
2357           SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo,
2358                                          val_prefix ? val_prefix : "", pool));
2359           svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf);
2360           svn_stringbuf_appendcstr(output_buf, "\n");
2361         }
2362     }
2363 #if SVN_DEBUG
2364   else if (!catalog)
2365     {
2366       output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2367       svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n"));
2368     }
2369   else if (apr_hash_count(catalog) == 0)
2370     {
2371       output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2372       svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n"));
2373     }
2374 #endif
2375
2376   /* If we have an output_buf, convert it to an svn_string_t;
2377      otherwise, return a new string containing only a newline
2378      character.  */
2379   if (output_buf)
2380     *output = svn_stringbuf__morph_into_string(output_buf);
2381   else
2382     *output = svn_string_create("\n", pool);
2383
2384   return SVN_NO_ERROR;
2385 }
2386
2387 svn_error_t *
2388 svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
2389                                    svn_revnum_t *oldest_rev,
2390                                    svn_mergeinfo_t mergeinfo,
2391                                    apr_pool_t *pool)
2392 {
2393   *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
2394   if (mergeinfo)
2395     {
2396       apr_hash_index_t *hi;
2397
2398       for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2399         {
2400           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2401
2402           if (rangelist->nelts)
2403             {
2404               svn_merge_range_t *range = APR_ARRAY_IDX(rangelist,
2405                                                        rangelist->nelts - 1,
2406                                                        svn_merge_range_t *);
2407               if (!SVN_IS_VALID_REVNUM(*youngest_rev)
2408                   || (range->end > *youngest_rev))
2409                 *youngest_rev = range->end;
2410
2411               range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
2412               if (!SVN_IS_VALID_REVNUM(*oldest_rev)
2413                   || (range->start < *oldest_rev))
2414                 *oldest_rev = range->start;
2415             }
2416         }
2417     }
2418   return SVN_NO_ERROR;
2419 }
2420
2421 svn_error_t *
2422 svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat,
2423                                         svn_mergeinfo_catalog_t catalog,
2424                                         svn_revnum_t youngest_rev,
2425                                         svn_revnum_t oldest_rev,
2426                                         svn_boolean_t include_range,
2427                                         apr_pool_t *result_pool,
2428                                         apr_pool_t *scratch_pool)
2429 {
2430   apr_hash_index_t *hi;
2431
2432   *filtered_cat = apr_hash_make(result_pool);
2433   for (hi = apr_hash_first(scratch_pool, catalog);
2434        hi;
2435        hi = apr_hash_next(hi))
2436     {
2437       const char *path = apr_hash_this_key(hi);
2438       svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
2439       svn_mergeinfo_t filtered_mergeinfo;
2440
2441       SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
2442                                                         mergeinfo,
2443                                                         youngest_rev,
2444                                                         oldest_rev,
2445                                                         include_range,
2446                                                         result_pool,
2447                                                         scratch_pool));
2448       if (apr_hash_count(filtered_mergeinfo))
2449         svn_hash_sets(*filtered_cat,
2450                       apr_pstrdup(result_pool, path), filtered_mergeinfo);
2451     }
2452
2453   return SVN_NO_ERROR;
2454 }
2455
2456 svn_error_t *
2457 svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
2458                                           svn_mergeinfo_t mergeinfo,
2459                                           svn_revnum_t youngest_rev,
2460                                           svn_revnum_t oldest_rev,
2461                                           svn_boolean_t include_range,
2462                                           apr_pool_t *result_pool,
2463                                           apr_pool_t *scratch_pool)
2464 {
2465   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
2466   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
2467   SVN_ERR_ASSERT(oldest_rev < youngest_rev);
2468
2469   *filtered_mergeinfo = apr_hash_make(result_pool);
2470
2471   if (mergeinfo)
2472     {
2473       apr_hash_index_t *hi;
2474       svn_rangelist_t *filter_rangelist =
2475         svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE,
2476                                   scratch_pool);
2477
2478       for (hi = apr_hash_first(scratch_pool, mergeinfo);
2479            hi;
2480            hi = apr_hash_next(hi))
2481         {
2482           const char *path = apr_hash_this_key(hi);
2483           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2484
2485           if (rangelist->nelts)
2486             {
2487               svn_rangelist_t *new_rangelist;
2488
2489               SVN_ERR(rangelist_intersect_or_remove(
2490                         &new_rangelist, filter_rangelist, rangelist,
2491                         ! include_range, FALSE, result_pool));
2492
2493               if (new_rangelist->nelts)
2494                 svn_hash_sets(*filtered_mergeinfo,
2495                               apr_pstrdup(result_pool, path), new_rangelist);
2496             }
2497         }
2498     }
2499   return SVN_NO_ERROR;
2500 }
2501
2502 svn_error_t *
2503 svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
2504                                            svn_mergeinfo_t mergeinfo,
2505                                            svn_revnum_t offset,
2506                                            apr_pool_t *result_pool,
2507                                            apr_pool_t *scratch_pool)
2508 {
2509   apr_hash_index_t *hi;
2510   *adjusted_mergeinfo = apr_hash_make(result_pool);
2511
2512   if (mergeinfo)
2513     {
2514       for (hi = apr_hash_first(scratch_pool, mergeinfo);
2515            hi;
2516            hi = apr_hash_next(hi))
2517         {
2518           int i;
2519           const char *path = apr_hash_this_key(hi);
2520           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2521           svn_rangelist_t *adjusted_rangelist =
2522             apr_array_make(result_pool, rangelist->nelts,
2523                            sizeof(svn_merge_range_t *));
2524
2525           for (i = 0; i < rangelist->nelts; i++)
2526             {
2527               svn_merge_range_t *range =
2528                 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
2529
2530               if (range->start + offset > 0 && range->end + offset > 0)
2531                 {
2532                   range->start = range->start + offset;
2533                   range->end = range->end + offset;
2534                   APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) =
2535                     range;
2536                 }
2537             }
2538
2539           if (adjusted_rangelist->nelts)
2540             svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path),
2541                           adjusted_rangelist);
2542         }
2543     }
2544   return SVN_NO_ERROR;
2545 }
2546
2547 svn_boolean_t
2548 svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
2549                                  apr_pool_t *scratch_pool)
2550 {
2551   if (mergeinfo)
2552     {
2553       apr_hash_index_t *hi;
2554
2555       for (hi = apr_hash_first(scratch_pool, mergeinfo);
2556            hi;
2557            hi = apr_hash_next(hi))
2558         {
2559           svn_rangelist_t *rangelist = apr_hash_this_val(hi);
2560           int i;
2561
2562           for (i = 0; i < rangelist->nelts; i++)
2563             {
2564               svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2565                                                        svn_merge_range_t *);
2566               if (!range->inheritable)
2567                 return TRUE;
2568             }
2569         }
2570     }
2571   return FALSE;
2572 }
2573
2574 svn_rangelist_t *
2575 svn_rangelist__initialize(svn_revnum_t start,
2576                           svn_revnum_t end,
2577                           svn_boolean_t inheritable,
2578                           apr_pool_t *result_pool)
2579 {
2580   svn_rangelist_t *rangelist =
2581     apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
2582   svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
2583
2584   range->start = start;
2585   range->end = end;
2586   range->inheritable = inheritable;
2587   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2588   return rangelist;
2589 }
2590
2591 svn_error_t *
2592 svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
2593                                        const apr_array_header_t *segments,
2594                                        apr_pool_t *pool)
2595 {
2596   svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
2597   int i;
2598
2599   /* Translate location segments into merge sources and ranges. */
2600   for (i = 0; i < segments->nelts; i++)
2601     {
2602       svn_location_segment_t *segment =
2603         APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
2604       svn_rangelist_t *path_ranges;
2605       svn_merge_range_t *range;
2606       const char *source_path;
2607
2608       /* No path segment?  Skip it. */
2609       if (! segment->path)
2610         continue;
2611
2612       /* Prepend a leading slash to our path. */
2613       source_path = apr_pstrcat(pool, "/", segment->path, SVN_VA_NULL);
2614
2615       /* See if we already stored ranges for this path.  If not, make
2616          a new list.  */
2617       path_ranges = svn_hash_gets(mergeinfo, source_path);
2618       if (! path_ranges)
2619         path_ranges = apr_array_make(pool, 1, sizeof(range));
2620
2621       /* A svn_location_segment_t may have legitimately describe only
2622          revision 0, but there is no corresponding representation for
2623          this in a svn_merge_range_t. */
2624       if (segment->range_start == 0 && segment->range_end == 0)
2625         continue;
2626
2627       /* Build a merge range, push it onto the list of ranges, and for
2628          good measure, (re)store it in the hash. */
2629       range = apr_pcalloc(pool, sizeof(*range));
2630       range->start = MAX(segment->range_start - 1, 0);
2631       range->end = segment->range_end;
2632       range->inheritable = TRUE;
2633       APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
2634       svn_hash_sets(mergeinfo, source_path, path_ranges);
2635     }
2636
2637   *mergeinfo_p = mergeinfo;
2638   return SVN_NO_ERROR;
2639 }
2640
2641 svn_error_t *
2642 svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
2643                           svn_mergeinfo_t merge_history,
2644                           apr_pool_t *result_pool,
2645                           apr_pool_t *scratch_pool)
2646 {
2647   if (apr_hash_count(merge_history))
2648     {
2649       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2650       apr_hash_index_t *hi;
2651
2652       for (hi = apr_hash_first(scratch_pool, merge_history);
2653            hi;
2654            hi = apr_hash_next(hi))
2655         {
2656           svn_rangelist_t *subtree_rangelist = apr_hash_this_val(hi);
2657
2658           svn_pool_clear(iterpool);
2659           SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
2660                                        result_pool, iterpool));
2661         }
2662       svn_pool_destroy(iterpool);
2663     }
2664   return SVN_NO_ERROR;
2665 }
2666
2667
2668 const char *
2669 svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
2670 {
2671   switch (inherit)
2672     {
2673     case svn_mergeinfo_inherited:
2674       return "inherited";
2675     case svn_mergeinfo_nearest_ancestor:
2676       return "nearest-ancestor";
2677     default:
2678       return "explicit";
2679     }
2680 }
2681
2682 svn_mergeinfo_inheritance_t
2683 svn_inheritance_from_word(const char *word)
2684 {
2685   if (strcmp(word, "inherited") == 0)
2686     return svn_mergeinfo_inherited;
2687   if (strcmp(word, "nearest-ancestor") == 0)
2688     return svn_mergeinfo_nearest_ancestor;
2689   return svn_mergeinfo_explicit;
2690 }