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