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