mergeinfo.c revision 289166
1275680Strasz/*
2275680Strasz * mergeinfo.c:  Mergeinfo parsing and handling
3275680Strasz *
4275680Strasz * ====================================================================
5275680Strasz *    Licensed to the Apache Software Foundation (ASF) under one
6275680Strasz *    or more contributor license agreements.  See the NOTICE file
7275680Strasz *    distributed with this work for additional information
8275680Strasz *    regarding copyright ownership.  The ASF licenses this file
9275680Strasz *    to you under the Apache License, Version 2.0 (the
10275680Strasz *    "License"); you may not use this file except in compliance
11275680Strasz *    with the License.  You may obtain a copy of the License at
12275680Strasz *
13275680Strasz *      http://www.apache.org/licenses/LICENSE-2.0
14275680Strasz *
15275680Strasz *    Unless required by applicable law or agreed to in writing,
16275680Strasz *    software distributed under the License is distributed on an
17275680Strasz *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18275680Strasz *    KIND, either express or implied.  See the License for the
19275680Strasz *    specific language governing permissions and limitations
20275680Strasz *    under the License.
21275680Strasz * ====================================================================
22275680Strasz */
23275680Strasz#include <assert.h>
24275680Strasz#include <ctype.h>
25275680Strasz
26275680Strasz#include "svn_path.h"
27275680Strasz#include "svn_types.h"
28275680Strasz#include "svn_ctype.h"
29275680Strasz#include "svn_pools.h"
30275680Strasz#include "svn_sorts.h"
31275680Strasz#include "svn_error.h"
32275680Strasz#include "svn_error_codes.h"
33275680Strasz#include "svn_string.h"
34275680Strasz#include "svn_mergeinfo.h"
35275680Strasz#include "private/svn_fspath.h"
36275680Strasz#include "private/svn_mergeinfo_private.h"
37275680Strasz#include "private/svn_string_private.h"
38275680Strasz#include "private/svn_subr_private.h"
39275680Strasz#include "svn_private_config.h"
40275680Strasz#include "svn_hash.h"
41275680Strasz#include "private/svn_dep_compat.h"
42275680Strasz
43275680Strasz/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
44275680Strasz   overlapping, and their inheritability allows them to be combined, put
45275680Strasz   the result in OUTPUT and return TRUE, otherwise return FALSE.
46275680Strasz
47275680Strasz   CONSIDER_INHERITANCE determines how to account for the inheritability
48275680Strasz   of IN1 and IN2 when trying to combine ranges.  If ranges with different
49275680Strasz   inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
50275680Strasz   to happen) the result is inheritable.  If both ranges are inheritable the
51275680Strasz   result is inheritable.  Only and if both ranges are non-inheritable is
52275680Strasz   the result is non-inheritable.
53275680Strasz
54275680Strasz   Range overlapping detection algorithm from
55275680Strasz   http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
56275680Strasz*/
57275680Straszstatic svn_boolean_t
58275680Straszcombine_ranges(svn_merge_range_t *output,
59275680Strasz               const svn_merge_range_t *in1,
60275680Strasz               const svn_merge_range_t *in2,
61275680Strasz               svn_boolean_t consider_inheritance)
62275680Strasz{
63275680Strasz  if (in1->start <= in2->end && in2->start <= in1->end)
64275680Strasz    {
65275680Strasz      if (!consider_inheritance
66275680Strasz          || (consider_inheritance
67275680Strasz              && (in1->inheritable == in2->inheritable)))
68275680Strasz        {
69275680Strasz          output->start = MIN(in1->start, in2->start);
70275680Strasz          output->end = MAX(in1->end, in2->end);
71275680Strasz          output->inheritable = (in1->inheritable || in2->inheritable);
72275680Strasz          return TRUE;
73275680Strasz        }
74275680Strasz    }
75275680Strasz  return FALSE;
76275680Strasz}
77275680Strasz
78275680Strasz/* pathname -> PATHNAME */
79275680Straszstatic svn_error_t *
80275680Straszparse_pathname(const char **input,
81275680Strasz               const char *end,
82275680Strasz               const char **pathname,
83275680Strasz               apr_pool_t *pool)
84275680Strasz{
85275680Strasz  const char *curr = *input;
86275680Strasz  const char *last_colon = NULL;
87275680Strasz
88275680Strasz  /* A pathname may contain colons, so find the last colon before END
89275680Strasz     or newline.  We'll consider this the divider between the pathname
90275680Strasz     and the revisionlist. */
91275680Strasz  while (curr < end && *curr != '\n')
92275680Strasz    {
93275680Strasz      if (*curr == ':')
94275680Strasz        last_colon = curr;
95275680Strasz      curr++;
96275680Strasz    }
97275680Strasz
98275680Strasz  if (!last_colon)
99275680Strasz    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
100275680Strasz                            _("Pathname not terminated by ':'"));
101275680Strasz  if (last_colon == *input)
102275680Strasz    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
103275680Strasz                            _("No pathname preceding ':'"));
104275680Strasz
105275680Strasz  /* Tolerate relative repository paths, but convert them to absolute.
106275680Strasz     ### Efficiency?  1 string duplication here, 2 in canonicalize. */
107275680Strasz  *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
108275680Strasz                                                    last_colon - *input),
109275680Strasz                                       pool);
110275680Strasz
111277436Strasz  *input = last_colon;
112275680Strasz
113275680Strasz  return SVN_NO_ERROR;
114275680Strasz}
115275680Strasz
116275680Strasz/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
117275680Strasz * revision range.
118275680Strasz *
119275680Strasz * Note: The smallest valid value of RANGE->start is 0 because it is an
120275680Strasz * exclusive endpoint, being one less than the revision number of the first
121275680Strasz * change described by the range, and the oldest possible change is "r1" as
122275680Strasz * there cannot be a change "r0". */
123275680Strasz#define IS_VALID_FORWARD_RANGE(range) \
124275680Strasz  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
125275680Strasz
126275680Strasz/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
127275680Strasztypedef enum intersection_type_t
128275680Strasz{
129275680Strasz  /* Ranges don't intersect and don't adjoin. */
130275680Strasz  svn__no_intersection,
131275680Strasz
132275680Strasz  /* Ranges are equal. */
133275680Strasz  svn__equal_intersection,
134275680Strasz
135275680Strasz  /* Ranges adjoin but don't overlap. */
136275680Strasz  svn__adjoining_intersection,
137275680Strasz
138275680Strasz  /* Ranges overlap but neither is a subset of the other. */
139275680Strasz  svn__overlapping_intersection,
140275680Strasz
141275680Strasz  /* One range is a proper subset of the other. */
142275680Strasz  svn__proper_subset_intersection
143275680Strasz} intersection_type_t;
144275680Strasz
145275680Strasz/* Given ranges R1 and R2, both of which must be forward merge ranges,
146275680Strasz   set *INTERSECTION_TYPE to describe how the ranges intersect, if they
147275680Strasz   do at all.  The inheritance type of the ranges is not considered. */
148275680Straszstatic svn_error_t *
149275680Straszget_type_of_intersection(const svn_merge_range_t *r1,
150275680Strasz                         const svn_merge_range_t *r2,
151275680Strasz                         intersection_type_t *intersection_type)
152275680Strasz{
153275680Strasz  SVN_ERR_ASSERT(r1);
154275680Strasz  SVN_ERR_ASSERT(r2);
155275680Strasz  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
156275680Strasz  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
157275680Strasz
158275680Strasz  if (!(r1->start <= r2->end && r2->start <= r1->end))
159275680Strasz    *intersection_type = svn__no_intersection;
160275680Strasz  else if (r1->start == r2->start && r1->end == r2->end)
161275680Strasz    *intersection_type = svn__equal_intersection;
162275680Strasz  else if (r1->end == r2->start || r2->end == r1->start)
163275680Strasz    *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
614/* Return TRUE, if all ranges in RANGELIST are in ascending order and do
615 * not overlap and are not adjacent.
616 *
617 * ### Can yield false negatives: ranges of differing inheritance are
618 * allowed to be adjacent.
619 *
620 * If this returns FALSE, you probaly want to qsort() the
621 * ranges and then call svn_rangelist__combine_adjacent_ranges().
622 */
623static svn_boolean_t
624is_rangelist_normalized(svn_rangelist_t *rangelist)
625{
626  int i;
627  svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts;
628
629  for (i = 0; i < rangelist->nelts-1; ++i)
630    if (ranges[i]->end >= ranges[i+1]->start)
631      return FALSE;
632
633  return TRUE;
634}
635
636svn_error_t *
637svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
638                            apr_pool_t *scratch_pool)
639{
640  if (! is_rangelist_normalized(rangelist))
641    {
642      qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
643                  svn_sort_compare_ranges);
644
645      SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
646    }
647
648  return SVN_NO_ERROR;
649}
650
651svn_error_t *
652svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
653                                       apr_pool_t *scratch_pool)
654{
655  int i;
656  svn_merge_range_t *range, *lastrange;
657
658  lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
659
660  for (i = 1; i < rangelist->nelts; i++)
661    {
662      range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
663      if (lastrange->start <= range->end
664          && range->start <= lastrange->end)
665        {
666          /* The ranges are adjacent or intersect. */
667
668          /* svn_mergeinfo_parse promises to combine overlapping
669             ranges as long as their inheritability is the same. */
670          if (range->start < lastrange->end
671              && range->inheritable != lastrange->inheritable)
672            {
673              return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
674                                       _("Unable to parse overlapping "
675                                         "revision ranges '%s' and '%s' "
676                                         "with different inheritance "
677                                         "types"),
678                                       range_to_string(lastrange,
679                                                       scratch_pool),
680                                       range_to_string(range,
681                                                       scratch_pool));
682            }
683
684          /* Combine overlapping or adjacent ranges with the
685             same inheritability. */
686          if (lastrange->inheritable == range->inheritable)
687            {
688              lastrange->end = MAX(range->end, lastrange->end);
689              svn_sort__array_delete(rangelist, i, 1);
690              i--;
691            }
692        }
693      lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
694    }
695
696  return SVN_NO_ERROR;
697}
698
699/* revisionline -> PATHNAME COLON revisionlist */
700static svn_error_t *
701parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
702                    apr_pool_t *scratch_pool)
703{
704  const char *pathname = "";
705  apr_ssize_t klen;
706  svn_rangelist_t *existing_rangelist;
707  svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
708                                              sizeof(svn_merge_range_t *));
709
710  SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
711
712  if (*(*input) != ':')
713    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
714                            _("Pathname not terminated by ':'"));
715
716  *input = *input + 1;
717
718  SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
719
720  if (rangelist->nelts == 0)
721      return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
722                               _("Mergeinfo for '%s' maps to an "
723                                 "empty revision range"), pathname);
724  if (*input != end && *(*input) != '\n')
725    return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
726                             _("Could not find end of line in range list line "
727                               "in '%s'"), *input);
728
729  if (*input != end)
730    *input = *input + 1;
731
732  /* Sort the rangelist, combine adjacent ranges into single ranges, and
733     make sure there are no overlapping ranges.  Luckily, most data in
734     svn:mergeinfo will already be in normalized form and this will be quick.
735   */
736  SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool));
737
738  /* Handle any funky mergeinfo with relative merge source paths that
739     might exist due to issue #3547.  It's possible that this issue allowed
740     the creation of mergeinfo with path keys that differ only by a
741     leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
742     we encounter this we merge the rangelists together under a single
743     absolute path key. */
744  klen = strlen(pathname);
745  existing_rangelist = apr_hash_get(hash, pathname, klen);
746  if (existing_rangelist)
747    SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
748                                 scratch_pool, scratch_pool));
749
750  apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
751               klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
752
753  return SVN_NO_ERROR;
754}
755
756/* top -> revisionline (NEWLINE revisionline)*  */
757static svn_error_t *
758parse_top(const char **input, const char *end, svn_mergeinfo_t hash,
759          apr_pool_t *pool)
760{
761  apr_pool_t *iterpool = svn_pool_create(pool);
762
763  while (*input < end)
764    {
765      svn_pool_clear(iterpool);
766      SVN_ERR(parse_revision_line(input, end, hash, iterpool));
767    }
768  svn_pool_destroy(iterpool);
769
770  return SVN_NO_ERROR;
771}
772
773svn_error_t *
774svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo,
775                    const char *input,
776                    apr_pool_t *pool)
777{
778  svn_error_t *err;
779
780  *mergeinfo = svn_hash__make(pool);
781  err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
782
783  /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
784  if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
785    err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err,
786                            _("Could not parse mergeinfo string '%s'"),
787                            input);
788  return err;
789}
790
791/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
792   a single rangelist element in-place.
793
794   If *RANGE_INDEX is not a valid element in RANGELIST do nothing.  Otherwise
795   ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
796   subsequent ranges in RANGELIST.
797
798   If overlap is found, then remove, modify, and/or add elements to RANGELIST
799   as per the invariants for rangelists documented in svn_mergeinfo.h.  If
800   RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
801   elements if their inheritability permits -- The inheritance of intersecting
802   and adjoining ranges is handled as per svn_mergeinfo_merge2.  Upon return
803   set *RANGE_INDEX to the index of the youngest element modified, added, or
804   adjoined to RANGELIST[*RANGE_INDEX].
805
806   Note: Adjoining rangelist elements are those where the end rev of the older
807   element is equal to the start rev of the younger element.
808
809   Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
810static void
811adjust_remaining_ranges(svn_rangelist_t *rangelist,
812                        int *range_index,
813                        apr_pool_t *result_pool)
814{
815  int i;
816  int starting_index;
817  int elements_to_delete = 0;
818  svn_merge_range_t *modified_range;
819
820  if (*range_index >= rangelist->nelts)
821    return;
822
823  starting_index = *range_index + 1;
824  modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
825
826  for (i = *range_index + 1; i < rangelist->nelts; i++)
827    {
828      svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
829                                                    svn_merge_range_t *);
830
831      /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
832         RANGELIST then we are finished. */
833      if (modified_range->end < next_range->start)
834        break;
835
836      /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
837      if (modified_range->end == next_range->start)
838        {
839          if (modified_range->inheritable == next_range->inheritable)
840            {
841              /* Combine adjoining ranges with the same inheritability. */
842              modified_range->end = next_range->end;
843              elements_to_delete++;
844            }
845          else
846            {
847              /* Cannot join because inheritance differs. */
848              (*range_index)++;
849            }
850          break;
851        }
852
853      /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
854      if (modified_range->end > next_range->end)
855        {
856          /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
857             don't share the same end range. */
858          if (modified_range->inheritable
859              || (modified_range->inheritable == next_range->inheritable))
860            {
861              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
862              elements_to_delete++;
863            }
864          else
865            {
866              /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
867                 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
868                 inheritable.  This means MODIFIED_RANGE is truncated,
869                 NEXT_RANGE remains, and the portion of MODIFIED_RANGE
870                 younger than NEXT_RANGE is added as a separate range:
871                  ______________________________________________
872                 |                                              |
873                 M                 MODIFIED_RANGE               N
874                 |                 (!inhertiable)               |
875                 |______________________________________________|
876                                  |              |
877                                  O  NEXT_RANGE  P
878                                  | (inheritable)|
879                                  |______________|
880                                         |
881                                         V
882                  _______________________________________________
883                 |                |              |               |
884                 M MODIFIED_RANGE O  NEXT_RANGE  P   NEW_RANGE   N
885                 | (!inhertiable) | (inheritable)| (!inheritable)|
886                 |________________|______________|_______________|
887              */
888              svn_merge_range_t *new_modified_range =
889                apr_palloc(result_pool, sizeof(*new_modified_range));
890              new_modified_range->start = next_range->end;
891              new_modified_range->end = modified_range->end;
892              new_modified_range->inheritable = FALSE;
893              modified_range->end = next_range->start;
894              (*range_index)+=2;
895              svn_sort__array_insert(&new_modified_range, rangelist,
896                                     *range_index);
897              /* Recurse with the new range. */
898              adjust_remaining_ranges(rangelist, range_index, result_pool);
899              break;
900            }
901        }
902      else if (modified_range->end == next_range->end)
903        {
904          /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
905             the same end range. */
906          if (modified_range->inheritable
907              || (modified_range->inheritable == next_range->inheritable))
908            {
909              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
910              elements_to_delete++;
911            }
912          else
913            {
914              /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
915                 absorbed by the latter. */
916              modified_range->end = next_range->start;
917              (*range_index)++;
918            }
919          break;
920        }
921      else
922        {
923          /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
924             a proper subset of MODIFIED_RANGE, nor do the two share the
925             same end revision, i.e. they overlap. */
926          if (modified_range->inheritable == next_range->inheritable)
927            {
928              /* Combine overlapping ranges with the same inheritability. */
929              modified_range->end = next_range->end;
930              elements_to_delete++;
931            }
932          else if (modified_range->inheritable)
933            {
934              /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
935                 and NEXT_RANGE is truncated. */
936              next_range->start = modified_range->end;
937              (*range_index)++;
938            }
939          else
940            {
941              /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
942                 and MODIFIED_RANGE is truncated. */
943              modified_range->end = next_range->start;
944              (*range_index)++;
945            }
946          break;
947        }
948    }
949
950  if (elements_to_delete)
951    svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
952}
953
954svn_error_t *
955svn_rangelist_merge2(svn_rangelist_t *rangelist,
956                     const svn_rangelist_t *changes,
957                     apr_pool_t *result_pool,
958                     apr_pool_t *scratch_pool)
959{
960  int i = 0;
961  int j = 0;
962
963  /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
964  changes = svn_rangelist_dup(changes, scratch_pool);
965
966  while (i < rangelist->nelts && j < changes->nelts)
967    {
968      svn_merge_range_t *range =
969        APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
970      svn_merge_range_t *change =
971        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
972      int res = svn_sort_compare_ranges(&range, &change);
973
974      if (res == 0)
975        {
976          /* Only when merging two non-inheritable ranges is the result also
977             non-inheritable.  In all other cases ensure an inheritiable
978             result. */
979          if (range->inheritable || change->inheritable)
980            range->inheritable = TRUE;
981          i++;
982          j++;
983        }
984      else if (res < 0) /* CHANGE is younger than RANGE */
985        {
986          if (range->end < change->start)
987            {
988              /* RANGE is older than CHANGE and the two do not
989                 adjoin or overlap */
990              i++;
991            }
992          else if (range->end == change->start)
993            {
994              /* RANGE and CHANGE adjoin */
995              if (range->inheritable == change->inheritable)
996                {
997                  /* RANGE and CHANGE have the same inheritability so
998                     RANGE expands to absord CHANGE. */
999                  range->end = change->end;
1000                  adjust_remaining_ranges(rangelist, &i, result_pool);
1001                  j++;
1002                }
1003              else
1004                {
1005                  /* RANGE and CHANGE adjoin, but have different
1006                     inheritability.  Since RANGE is older, just
1007                     move on to the next RANGE. */
1008                  i++;
1009                }
1010            }
1011          else
1012            {
1013              /* RANGE and CHANGE overlap, but how? */
1014              if ((range->inheritable == change->inheritable)
1015                  || range->inheritable)
1016                {
1017                  /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
1018                      with no adjustment otherwise only the intersection is
1019                      absorbed and CHANGE is truncated. */
1020                  if (range->end >= change->end)
1021                    j++;
1022                  else
1023                    change->start = range->end;
1024                }
1025              else
1026                {
1027                  /* RANGE is non-inheritable and CHANGE is inheritable. */
1028                  if (range->start < change->start)
1029                    {
1030                      /* CHANGE absorbs intersection with RANGE and RANGE
1031                         is truncated. */
1032                      svn_merge_range_t *range_copy =
1033                        svn_merge_range_dup(range, result_pool);
1034                      range_copy->end = change->start;
1035                      range->start = change->start;
1036                      svn_sort__array_insert(&range_copy, rangelist, i++);
1037                    }
1038                  else
1039                    {
1040                      /* CHANGE and RANGE share the same start rev, but
1041                         RANGE is considered older because its end rev
1042                         is older. */
1043                      range->inheritable = TRUE;
1044                      change->start = range->end;
1045                    }
1046                }
1047            }
1048        }
1049      else /* res > 0, CHANGE is older than RANGE */
1050        {
1051          if (change->end < range->start)
1052            {
1053              /* CHANGE is older than RANGE and the two do not
1054                 adjoin or overlap, so insert a copy of CHANGE
1055                 into RANGELIST. */
1056              svn_merge_range_t *change_copy =
1057                svn_merge_range_dup(change, result_pool);
1058              svn_sort__array_insert(&change_copy, rangelist, i++);
1059              j++;
1060            }
1061          else if (change->end == range->start)
1062            {
1063              /* RANGE and CHANGE adjoin */
1064              if (range->inheritable == change->inheritable)
1065                {
1066                  /* RANGE and CHANGE have the same inheritability so we
1067                     can simply combine the two in place. */
1068                  range->start = change->start;
1069                  j++;
1070                }
1071              else
1072                {
1073                  /* RANGE and CHANGE have different inheritability so insert
1074                     a copy of CHANGE into RANGELIST. */
1075                  svn_merge_range_t *change_copy =
1076                    svn_merge_range_dup(change, result_pool);
1077                  svn_sort__array_insert(&change_copy, rangelist, i);
1078                  j++;
1079                }
1080            }
1081          else
1082            {
1083              /* RANGE and CHANGE overlap. */
1084              if (range->inheritable == change->inheritable)
1085                {
1086                  /* RANGE and CHANGE have the same inheritability so we
1087                     can simply combine the two in place... */
1088                  range->start = change->start;
1089                  if (range->end < change->end)
1090                    {
1091                      /* ...but if RANGE is expanded ensure that we don't
1092                         violate any rangelist invariants. */
1093                      range->end = change->end;
1094                      adjust_remaining_ranges(rangelist, &i, result_pool);
1095                    }
1096                  j++;
1097                }
1098              else if (range->inheritable)
1099                {
1100                  if (change->start < range->start)
1101                    {
1102                      /* RANGE is inheritable so absorbs any part of CHANGE
1103                         it overlaps.  CHANGE is truncated and the remainder
1104                         inserted into RANGELIST. */
1105                      svn_merge_range_t *change_copy =
1106                        svn_merge_range_dup(change, result_pool);
1107                      change_copy->end = range->start;
1108                      change->start = range->start;
1109                      svn_sort__array_insert(&change_copy, rangelist, i++);
1110                    }
1111                  else
1112                    {
1113                      /* CHANGE and RANGE share the same start rev, but
1114                         CHANGE is considered older because CHANGE->END is
1115                         older than RANGE->END. */
1116                      j++;
1117                    }
1118                }
1119              else
1120                {
1121                  /* RANGE is non-inheritable and CHANGE is inheritable. */
1122                  if (change->start < range->start)
1123                    {
1124                      if (change->end == range->end)
1125                        {
1126                          /* RANGE is a proper subset of CHANGE and share the
1127                             same end revision, so set RANGE equal to CHANGE. */
1128                          range->start = change->start;
1129                          range->inheritable = TRUE;
1130                          j++;
1131                        }
1132                      else if (change->end > range->end)
1133                        {
1134                          /* RANGE is a proper subset of CHANGE and CHANGE has
1135                             a younger end revision, so set RANGE equal to its
1136                             intersection with CHANGE and truncate CHANGE. */
1137                          range->start = change->start;
1138                          range->inheritable = TRUE;
1139                          change->start = range->end;
1140                        }
1141                      else
1142                        {
1143                          /* CHANGE and RANGE overlap. Set RANGE equal to its
1144                             intersection with CHANGE and take the remainder
1145                             of RANGE and insert it into RANGELIST. */
1146                          svn_merge_range_t *range_copy =
1147                            svn_merge_range_dup(range, result_pool);
1148                          range_copy->start = change->end;
1149                          range->start = change->start;
1150                          range->end = change->end;
1151                          range->inheritable = TRUE;
1152                          svn_sort__array_insert(&range_copy, rangelist, ++i);
1153                          j++;
1154                        }
1155                    }
1156                  else
1157                    {
1158                      /* CHANGE and RANGE share the same start rev, but
1159                         CHANGE is considered older because its end rev
1160                         is older.
1161
1162                         Insert the intersection of RANGE and CHANGE into
1163                         RANGELIST and then set RANGE to the non-intersecting
1164                         portion of RANGE. */
1165                      svn_merge_range_t *range_copy =
1166                        svn_merge_range_dup(range, result_pool);
1167                      range_copy->end = change->end;
1168                      range_copy->inheritable = TRUE;
1169                      range->start = change->end;
1170                      svn_sort__array_insert(&range_copy, rangelist, i++);
1171                      j++;
1172                    }
1173                }
1174            }
1175        }
1176    }
1177
1178  /* Copy any remaining elements in CHANGES into RANGELIST. */
1179  for (; j < (changes)->nelts; j++)
1180    {
1181      svn_merge_range_t *change =
1182        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
1183      svn_merge_range_t *change_copy = svn_merge_range_dup(change,
1184                                                           result_pool);
1185      svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
1186    }
1187
1188  return SVN_NO_ERROR;
1189}
1190
1191/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
1192 * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
1193static svn_boolean_t
1194range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
1195                svn_boolean_t consider_inheritance)
1196{
1197  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1198  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1199
1200  return (first->start + 1 <= second->end)
1201    && (second->start + 1 <= first->end)
1202    && (!consider_inheritance
1203        || (!(first->inheritable) == !(second->inheritable)));
1204}
1205
1206/* Return TRUE iff the forward revision range FIRST wholly contains the
1207 * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
1208 * the same inheritability. */
1209static svn_boolean_t
1210range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
1211               svn_boolean_t consider_inheritance)
1212{
1213  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1214  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1215
1216  return (first->start <= second->start) && (second->end <= first->end)
1217    && (!consider_inheritance
1218        || (!(first->inheritable) == !(second->inheritable)));
1219}
1220
1221/* Swap start and end fields of RANGE. */
1222static void
1223range_swap_endpoints(svn_merge_range_t *range)
1224{
1225  svn_revnum_t swap = range->start;
1226  range->start = range->end;
1227  range->end = swap;
1228}
1229
1230svn_error_t *
1231svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool)
1232{
1233  int i;
1234
1235  svn_sort__array_reverse(rangelist, pool);
1236
1237  for (i = 0; i < rangelist->nelts; i++)
1238    {
1239      range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
1240    }
1241
1242  return SVN_NO_ERROR;
1243}
1244
1245void
1246svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
1247                               svn_boolean_t inheritable)
1248{
1249  if (rangelist)
1250    {
1251      int i;
1252      svn_merge_range_t *range;
1253
1254      for (i = 0; i < rangelist->nelts; i++)
1255        {
1256          range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1257          range->inheritable = inheritable;
1258        }
1259    }
1260  return;
1261}
1262
1263void
1264svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
1265                               svn_boolean_t inheritable,
1266                               apr_pool_t *scratch_pool)
1267{
1268  if (mergeinfo)
1269    {
1270      apr_hash_index_t *hi;
1271
1272      for (hi = apr_hash_first(scratch_pool, mergeinfo);
1273           hi;
1274           hi = apr_hash_next(hi))
1275        {
1276          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
1277
1278          if (rangelist)
1279            svn_rangelist__set_inheritance(rangelist, inheritable);
1280        }
1281    }
1282  return;
1283}
1284
1285/* If DO_REMOVE is true, then remove any overlapping ranges described by
1286   RANGELIST1 from RANGELIST2 and place the results in *OUTPUT.  When
1287   DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
1288   the "whiteboard".
1289
1290   If DO_REMOVE is false, then capture the intersection between RANGELIST1
1291   and RANGELIST2 and place the results in *OUTPUT.  The ordering of
1292   RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
1293
1294   If CONSIDER_INHERITANCE is true, then take the inheritance of the
1295   ranges in RANGELIST1 and RANGELIST2 into account when comparing them
1296   for intersection, see the doc string for svn_rangelist_intersect().
1297
1298   If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
1299   may intersect, but the resulting intersection is non-inheritable only
1300   if both ranges were non-inheritable, e.g.:
1301
1302   RANGELIST1  RANGELIST2  CONSIDER     DO_REMOVE  *OUTPUT
1303                           INHERITANCE
1304   ----------  ------      -----------  ---------  -------
1305
1306   90-420*     1-100       TRUE         FALSE      Empty Rangelist
1307   90-420      1-100*      TRUE         FALSE      Empty Rangelist
1308   90-420      1-100       TRUE         FALSE      90-100
1309   90-420*     1-100*      TRUE         FALSE      90-100*
1310
1311   90-420*     1-100       FALSE        FALSE      90-100
1312   90-420      1-100*      FALSE        FALSE      90-100
1313   90-420      1-100       FALSE        FALSE      90-100
1314   90-420*     1-100*      FALSE        FALSE      90-100*
1315
1316   Allocate the contents of *OUTPUT in POOL. */
1317static svn_error_t *
1318rangelist_intersect_or_remove(svn_rangelist_t **output,
1319                              const svn_rangelist_t *rangelist1,
1320                              const svn_rangelist_t *rangelist2,
1321                              svn_boolean_t do_remove,
1322                              svn_boolean_t consider_inheritance,
1323                              apr_pool_t *pool)
1324{
1325  int i1, i2, lasti2;
1326  svn_merge_range_t working_elt2;
1327
1328  *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1329
1330  i1 = 0;
1331  i2 = 0;
1332  lasti2 = -1;  /* Initialized to a value that "i2" will never be. */
1333
1334  while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
1335    {
1336      svn_merge_range_t *elt1, *elt2;
1337
1338      elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
1339
1340      /* Instead of making a copy of the entire array of rangelist2
1341         elements, we just keep a copy of the current rangelist2 element
1342         that needs to be used, and modify our copy if necessary. */
1343      if (i2 != lasti2)
1344        {
1345          working_elt2 =
1346            *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
1347          lasti2 = i2;
1348        }
1349
1350      elt2 = &working_elt2;
1351
1352      /* If the rangelist2 range is contained completely in the
1353         rangelist1, we increment the rangelist2.
1354         If the ranges intersect, and match exactly, we increment both
1355         rangelist1 and rangelist2.
1356         Otherwise, we have to generate a range for the left part of
1357         the removal of rangelist1 from rangelist2, and possibly change
1358         the rangelist2 to the remaining portion of the right part of
1359         the removal, to test against. */
1360      if (range_contains(elt1, elt2, consider_inheritance))
1361        {
1362          if (!do_remove)
1363            {
1364              svn_merge_range_t tmp_range;
1365              tmp_range.start = elt2->start;
1366              tmp_range.end = elt2->end;
1367              /* The intersection of two ranges is non-inheritable only
1368                 if both ranges are non-inheritable. */
1369              tmp_range.inheritable =
1370                (elt2->inheritable || elt1->inheritable);
1371              SVN_ERR(combine_with_lastrange(&tmp_range, *output,
1372                                             consider_inheritance,
1373                                             pool));
1374            }
1375
1376          i2++;
1377
1378          if (elt2->start == elt1->start && elt2->end == elt1->end)
1379            i1++;
1380        }
1381      else if (range_intersect(elt1, elt2, consider_inheritance))
1382        {
1383          if (elt2->start < elt1->start)
1384            {
1385              /* The rangelist2 range starts before the rangelist1 range. */
1386              svn_merge_range_t tmp_range;
1387              if (do_remove)
1388                {
1389                  /* Retain the range that falls before the rangelist1
1390                     start. */
1391                  tmp_range.start = elt2->start;
1392                  tmp_range.end = elt1->start;
1393                  tmp_range.inheritable = elt2->inheritable;
1394                }
1395              else
1396                {
1397                  /* Retain the range that falls between the rangelist1
1398                     start and rangelist2 end. */
1399                  tmp_range.start = elt1->start;
1400                  tmp_range.end = MIN(elt2->end, elt1->end);
1401                  /* The intersection of two ranges is non-inheritable only
1402                     if both ranges are non-inheritable. */
1403                  tmp_range.inheritable =
1404                    (elt2->inheritable || elt1->inheritable);
1405                }
1406
1407              SVN_ERR(combine_with_lastrange(&tmp_range,
1408                                             *output, consider_inheritance,
1409                                             pool));
1410            }
1411
1412          /* Set up the rest of the rangelist2 range for further
1413             processing.  */
1414          if (elt2->end > elt1->end)
1415            {
1416              /* The rangelist2 range ends after the rangelist1 range. */
1417              if (!do_remove)
1418                {
1419                  /* Partial overlap. */
1420                  svn_merge_range_t tmp_range;
1421                  tmp_range.start = MAX(elt2->start, elt1->start);
1422                  tmp_range.end = elt1->end;
1423                  /* The intersection of two ranges is non-inheritable only
1424                     if both ranges are non-inheritable. */
1425                  tmp_range.inheritable =
1426                    (elt2->inheritable || elt1->inheritable);
1427                  SVN_ERR(combine_with_lastrange(&tmp_range,
1428                                                 *output,
1429                                                 consider_inheritance,
1430                                                 pool));
1431                }
1432
1433              working_elt2.start = elt1->end;
1434              working_elt2.end = elt2->end;
1435            }
1436          else
1437            i2++;
1438        }
1439      else  /* ranges don't intersect */
1440        {
1441          /* See which side of the rangelist2 the rangelist1 is on.  If it
1442             is on the left side, we need to move the rangelist1.
1443
1444             If it is on past the rangelist2 on the right side, we
1445             need to output the rangelist2 and increment the
1446             rangelist2.  */
1447          if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
1448            i1++;
1449          else
1450            {
1451              svn_merge_range_t *lastrange;
1452
1453              if ((*output)->nelts > 0)
1454                lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1,
1455                                          svn_merge_range_t *);
1456              else
1457                lastrange = NULL;
1458
1459              if (do_remove && !(lastrange &&
1460                                 combine_ranges(lastrange, lastrange, elt2,
1461                                                consider_inheritance)))
1462                {
1463                  lastrange = svn_merge_range_dup(elt2, pool);
1464                  APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
1465                }
1466              i2++;
1467            }
1468        }
1469    }
1470
1471  if (do_remove)
1472    {
1473      /* Copy the current rangelist2 element if we didn't hit the end
1474         of the rangelist2, and we still had it around.  This element
1475         may have been touched, so we can't just walk the rangelist2
1476         array, we have to use our copy.  This case only happens when
1477         we ran out of rangelist1 before rangelist2, *and* we had changed
1478         the rangelist2 element. */
1479      if (i2 == lasti2 && i2 < rangelist2->nelts)
1480        {
1481          SVN_ERR(combine_with_lastrange(&working_elt2, *output,
1482                                         consider_inheritance, pool));
1483          i2++;
1484        }
1485
1486      /* Copy any other remaining untouched rangelist2 elements.  */
1487      for (; i2 < rangelist2->nelts; i2++)
1488        {
1489          svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
1490                                                 svn_merge_range_t *);
1491
1492          SVN_ERR(combine_with_lastrange(elt, *output,
1493                                         consider_inheritance, pool));
1494        }
1495    }
1496
1497  return SVN_NO_ERROR;
1498}
1499
1500
1501svn_error_t *
1502svn_rangelist_intersect(svn_rangelist_t **output,
1503                        const svn_rangelist_t *rangelist1,
1504                        const svn_rangelist_t *rangelist2,
1505                        svn_boolean_t consider_inheritance,
1506                        apr_pool_t *pool)
1507{
1508  return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
1509                                       consider_inheritance, pool);
1510}
1511
1512svn_error_t *
1513svn_rangelist_remove(svn_rangelist_t **output,
1514                     const svn_rangelist_t *eraser,
1515                     const svn_rangelist_t *whiteboard,
1516                     svn_boolean_t consider_inheritance,
1517                     apr_pool_t *pool)
1518{
1519  return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
1520                                       consider_inheritance, pool);
1521}
1522
1523svn_error_t *
1524svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added,
1525                   const svn_rangelist_t *from, const svn_rangelist_t *to,
1526                   svn_boolean_t consider_inheritance,
1527                   apr_pool_t *pool)
1528{
1529  /* The following diagrams illustrate some common range delta scenarios:
1530
1531     (from)           deleted
1532     r0 <===========(=========)============[=========]===========> rHEAD
1533     [to]                                    added
1534
1535     (from)           deleted                deleted
1536     r0 <===========(=========[============]=========)===========> rHEAD
1537     [to]
1538
1539     (from)           deleted
1540     r0 <===========(=========[============)=========]===========> rHEAD
1541     [to]                                    added
1542
1543     (from)                                  deleted
1544     r0 <===========[=========(============]=========)===========> rHEAD
1545     [to]             added
1546
1547     (from)
1548     r0 <===========[=========(============)=========]===========> rHEAD
1549     [to]             added                  added
1550
1551     (from)  d                                  d             d
1552     r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
1553     [to]        a   a    a   a   a   a                   a
1554  */
1555
1556  /* The items that are present in from, but not in to, must have been
1557     deleted. */
1558  SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance,
1559                               pool));
1560  /* The items that are present in to, but not in from, must have been
1561     added.  */
1562  return svn_rangelist_remove(added, from, to, consider_inheritance, pool);
1563}
1564
1565struct mergeinfo_diff_baton
1566{
1567  svn_mergeinfo_t from;
1568  svn_mergeinfo_t to;
1569  svn_mergeinfo_t deleted;
1570  svn_mergeinfo_t added;
1571  svn_boolean_t consider_inheritance;
1572  apr_pool_t *pool;
1573};
1574
1575/* This implements the 'svn_hash_diff_func_t' interface.
1576   BATON is of type 'struct mergeinfo_diff_baton *'.
1577*/
1578static svn_error_t *
1579mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen,
1580                       enum svn_hash_diff_key_status status,
1581                       void *baton)
1582{
1583  /* hash_a is FROM mergeinfo,
1584     hash_b is TO mergeinfo. */
1585  struct mergeinfo_diff_baton *cb = baton;
1586  svn_rangelist_t *from_rangelist, *to_rangelist;
1587  const char *path = key;
1588  if (status == svn_hash_diff_key_both)
1589    {
1590      /* Record any deltas (additions or deletions). */
1591      svn_rangelist_t *deleted_rangelist, *added_rangelist;
1592      from_rangelist = apr_hash_get(cb->from, path, klen);
1593      to_rangelist = apr_hash_get(cb->to, path, klen);
1594      SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
1595                                 from_rangelist, to_rangelist,
1596                                 cb->consider_inheritance, cb->pool));
1597      if (cb->deleted && deleted_rangelist->nelts > 0)
1598        apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
1599                     klen, deleted_rangelist);
1600      if (cb->added && added_rangelist->nelts > 0)
1601        apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
1602                     klen, added_rangelist);
1603    }
1604  else if ((status == svn_hash_diff_key_a) && cb->deleted)
1605    {
1606      from_rangelist = apr_hash_get(cb->from, path, klen);
1607      apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
1608                   svn_rangelist_dup(from_rangelist, cb->pool));
1609    }
1610  else if ((status == svn_hash_diff_key_b) && cb->added)
1611    {
1612      to_rangelist = apr_hash_get(cb->to, path, klen);
1613      apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
1614                   svn_rangelist_dup(to_rangelist, cb->pool));
1615    }
1616  return SVN_NO_ERROR;
1617}
1618
1619/* Record deletions and additions of entire range lists (by path
1620   presence), and delegate to svn_rangelist_diff() for delta
1621   calculations on a specific path.  */
1622static svn_error_t *
1623walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to,
1624                             svn_mergeinfo_t deleted, svn_mergeinfo_t added,
1625                             svn_boolean_t consider_inheritance,
1626                             apr_pool_t *result_pool,
1627                             apr_pool_t *scratch_pool)
1628{
1629  struct mergeinfo_diff_baton mdb;
1630  mdb.from = from;
1631  mdb.to = to;
1632  mdb.deleted = deleted;
1633  mdb.added = added;
1634  mdb.consider_inheritance = consider_inheritance;
1635  mdb.pool = result_pool;
1636
1637  return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool);
1638}
1639
1640svn_error_t *
1641svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
1642                    svn_mergeinfo_t from, svn_mergeinfo_t to,
1643                    svn_boolean_t consider_inheritance,
1644                    apr_pool_t *result_pool,
1645                    apr_pool_t *scratch_pool)
1646{
1647  if (from && to == NULL)
1648    {
1649      *deleted = svn_mergeinfo_dup(from, result_pool);
1650      *added = svn_hash__make(result_pool);
1651    }
1652  else if (from == NULL && to)
1653    {
1654      *deleted = svn_hash__make(result_pool);
1655      *added = svn_mergeinfo_dup(to, result_pool);
1656    }
1657  else
1658    {
1659      *deleted = svn_hash__make(result_pool);
1660      *added = svn_hash__make(result_pool);
1661
1662      if (from && to)
1663        {
1664          SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
1665                                               consider_inheritance,
1666                                               result_pool, scratch_pool));
1667        }
1668    }
1669
1670  return SVN_NO_ERROR;
1671}
1672
1673svn_error_t *
1674svn_mergeinfo__equals(svn_boolean_t *is_equal,
1675                      svn_mergeinfo_t info1,
1676                      svn_mergeinfo_t info2,
1677                      svn_boolean_t consider_inheritance,
1678                      apr_pool_t *pool)
1679{
1680  apr_hash_index_t *hi;
1681
1682  *is_equal = FALSE;
1683
1684  /* special cases: at least one side has no merge info */
1685  if (info1 == NULL && info2 == NULL)
1686    {
1687      *is_equal = TRUE;
1688      return SVN_NO_ERROR;
1689    }
1690
1691  if (info1 == NULL ||  info2 == NULL)
1692    return SVN_NO_ERROR;
1693
1694  /* trivial case: different number of paths -> unequal */
1695  if (apr_hash_count(info1) != apr_hash_count(info2))
1696    return SVN_NO_ERROR;
1697
1698  /* compare range lists for all paths */
1699  for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
1700    {
1701      const char *key;
1702      apr_ssize_t key_length;
1703      svn_rangelist_t *lhs, *rhs;
1704      int i;
1705      svn_rangelist_t *deleted, *added;
1706
1707      /* get both path lists */
1708      apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
1709      rhs = apr_hash_get(info2, key, key_length);
1710
1711      /* missing on one side? */
1712      if (rhs == NULL)
1713        return SVN_NO_ERROR;
1714
1715      /* quick compare: the range lists will often be a perfect match */
1716      if (lhs->nelts == rhs->nelts)
1717        {
1718          for (i = 0; i < lhs->nelts; ++i)
1719            {
1720              svn_merge_range_t *lrange
1721                = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
1722              svn_merge_range_t *rrange
1723                = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
1724
1725              /* range mismatch? -> needs detailed comparison */
1726              if (   lrange->start != rrange->start
1727                  || lrange->end != rrange->end)
1728                break;
1729
1730              /* inheritance mismatch? -> merge info differs */
1731              if (   consider_inheritance
1732                  && lrange->inheritable != rrange->inheritable)
1733                return SVN_NO_ERROR;
1734            }
1735
1736          /* all ranges found to match -> next path */
1737          if (i == lhs->nelts)
1738            continue;
1739        }
1740
1741      /* range lists differ but there are many ways to sort and aggregate
1742         revisions into ranges. Do a full diff on them. */
1743      SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
1744                                  consider_inheritance, pool));
1745      if (deleted->nelts || added->nelts)
1746        return SVN_NO_ERROR;
1747    }
1748
1749  /* no mismatch found */
1750  *is_equal = TRUE;
1751  return SVN_NO_ERROR;
1752}
1753
1754svn_error_t *
1755svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
1756                     svn_mergeinfo_t changes,
1757                     apr_pool_t *result_pool,
1758                     apr_pool_t *scratch_pool)
1759{
1760  apr_hash_index_t *hi;
1761  apr_pool_t *iterpool;
1762
1763  if (!apr_hash_count(changes))
1764    return SVN_NO_ERROR;
1765
1766  iterpool = svn_pool_create(scratch_pool);
1767  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
1768    {
1769      const char *key;
1770      apr_ssize_t klen;
1771      svn_rangelist_t *to_insert;
1772      svn_rangelist_t *target;
1773
1774      /* get ranges to insert and the target ranges list of that insertion */
1775      apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
1776      target = apr_hash_get(mergeinfo, key, klen);
1777
1778      /* if range list exists, just expand on it.
1779       * Otherwise, add new hash entry. */
1780      if (target)
1781        {
1782          SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
1783                                       iterpool));
1784          svn_pool_clear(iterpool);
1785        }
1786      else
1787        apr_hash_set(mergeinfo, key, klen, to_insert);
1788    }
1789
1790  svn_pool_destroy(iterpool);
1791
1792  return SVN_NO_ERROR;
1793}
1794
1795svn_error_t *
1796svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat,
1797                            svn_mergeinfo_catalog_t changes_cat,
1798                            apr_pool_t *result_pool,
1799                            apr_pool_t *scratch_pool)
1800{
1801  int i = 0;
1802  int j = 0;
1803  apr_array_header_t *sorted_cat =
1804    svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths,
1805                   scratch_pool);
1806  apr_array_header_t *sorted_changes =
1807    svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths,
1808                   scratch_pool);
1809
1810  while (i < sorted_cat->nelts && j < sorted_changes->nelts)
1811    {
1812      svn_sort__item_t cat_elt, change_elt;
1813      int res;
1814
1815      cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t);
1816      change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t);
1817      res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt);
1818
1819      if (res == 0) /* Both catalogs have mergeinfo for a given path. */
1820        {
1821          svn_mergeinfo_t mergeinfo = cat_elt.value;
1822          svn_mergeinfo_t changes_mergeinfo = change_elt.value;
1823
1824          SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
1825                                       result_pool, scratch_pool));
1826          apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
1827          i++;
1828          j++;
1829        }
1830      else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
1831        {
1832          i++;
1833        }
1834      else /* Only CHANGES_CAT has mergeinfo for this path. */
1835        {
1836          apr_hash_set(mergeinfo_cat,
1837                       apr_pstrdup(result_pool, change_elt.key),
1838                       change_elt.klen,
1839                       svn_mergeinfo_dup(change_elt.value, result_pool));
1840          j++;
1841        }
1842    }
1843
1844  /* Copy back any remaining elements from the CHANGES_CAT catalog. */
1845  for (; j < sorted_changes->nelts; j++)
1846    {
1847      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j,
1848                                           svn_sort__item_t);
1849      apr_hash_set(mergeinfo_cat,
1850                   apr_pstrdup(result_pool, elt.key),
1851                   elt.klen,
1852                   svn_mergeinfo_dup(elt.value, result_pool));
1853    }
1854
1855  return SVN_NO_ERROR;
1856}
1857
1858svn_error_t *
1859svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
1860                         svn_mergeinfo_t mergeinfo1,
1861                         svn_mergeinfo_t mergeinfo2,
1862                         svn_boolean_t consider_inheritance,
1863                         apr_pool_t *result_pool,
1864                         apr_pool_t *scratch_pool)
1865{
1866  apr_hash_index_t *hi;
1867  apr_pool_t *iterpool;
1868
1869  *mergeinfo = apr_hash_make(result_pool);
1870  iterpool = svn_pool_create(scratch_pool);
1871
1872  /* ### TODO(reint): Do we care about the case when a path in one
1873     ### mergeinfo hash has inheritable mergeinfo, and in the other
1874     ### has non-inhertiable mergeinfo?  It seems like that path
1875     ### itself should really be an intersection, while child paths
1876     ### should not be... */
1877  for (hi = apr_hash_first(scratch_pool, mergeinfo1);
1878       hi; hi = apr_hash_next(hi))
1879    {
1880      const char *path = svn__apr_hash_index_key(hi);
1881      svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi);
1882      svn_rangelist_t *rangelist2;
1883
1884      svn_pool_clear(iterpool);
1885      rangelist2 = svn_hash_gets(mergeinfo2, path);
1886      if (rangelist2)
1887        {
1888          SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
1889                                          consider_inheritance, iterpool));
1890          if (rangelist2->nelts > 0)
1891            svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path),
1892                          svn_rangelist_dup(rangelist2, result_pool));
1893        }
1894    }
1895  svn_pool_destroy(iterpool);
1896  return SVN_NO_ERROR;
1897}
1898
1899svn_error_t *
1900svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
1901                      svn_mergeinfo_t eraser,
1902                      svn_mergeinfo_t whiteboard,
1903                      svn_boolean_t consider_inheritance,
1904                      apr_pool_t *result_pool,
1905                      apr_pool_t *scratch_pool)
1906{
1907  *mergeinfo = apr_hash_make(result_pool);
1908  return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
1909                                      consider_inheritance, result_pool,
1910                                      scratch_pool);
1911}
1912
1913svn_error_t *
1914svn_rangelist_to_string(svn_string_t **output,
1915                        const svn_rangelist_t *rangelist,
1916                        apr_pool_t *pool)
1917{
1918  svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
1919
1920  if (rangelist->nelts > 0)
1921    {
1922      int i;
1923      svn_merge_range_t *range;
1924
1925      /* Handle the elements that need commas at the end.  */
1926      for (i = 0; i < rangelist->nelts - 1; i++)
1927        {
1928          range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1929          svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1930          svn_stringbuf_appendcstr(buf, ",");
1931        }
1932
1933      /* Now handle the last element, which needs no comma.  */
1934      range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1935      svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1936    }
1937
1938  *output = svn_stringbuf__morph_into_string(buf);
1939
1940  return SVN_NO_ERROR;
1941}
1942
1943/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT.  If PREFIX
1944   is not NULL then prepend PREFIX to each line in OUTPUT.  If INPUT contains
1945   no elements, return the empty string.  If INPUT contains any merge source
1946   path keys that are relative then convert these to absolute paths in
1947   *OUTPUT.
1948 */
1949static svn_error_t *
1950mergeinfo_to_stringbuf(svn_stringbuf_t **output,
1951                       svn_mergeinfo_t input,
1952                       const char *prefix,
1953                       apr_pool_t *pool)
1954{
1955  *output = svn_stringbuf_create_empty(pool);
1956
1957  if (apr_hash_count(input) > 0)
1958    {
1959      apr_array_header_t *sorted =
1960        svn_sort__hash(input, svn_sort_compare_items_as_paths, pool);
1961      int i;
1962
1963      for (i = 0; i < sorted->nelts; i++)
1964        {
1965          svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t);
1966          svn_string_t *revlist;
1967
1968          SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
1969          svn_stringbuf_appendcstr(
1970            *output,
1971            apr_psprintf(pool, "%s%s%s:%s",
1972                         prefix ? prefix : "",
1973                         *((const char *) elt.key) == '/' ? "" : "/",
1974                         (const char *) elt.key,
1975                         revlist->data));
1976          if (i < sorted->nelts - 1)
1977            svn_stringbuf_appendcstr(*output, "\n");
1978        }
1979    }
1980
1981  return SVN_NO_ERROR;
1982}
1983
1984svn_error_t *
1985svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
1986                        apr_pool_t *pool)
1987{
1988  svn_stringbuf_t *mergeinfo_buf;
1989
1990  SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
1991  *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
1992  return SVN_NO_ERROR;
1993}
1994
1995svn_error_t *
1996svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
1997{
1998  apr_hash_index_t *hi;
1999
2000  for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
2001    {
2002      apr_array_header_t *rl = svn__apr_hash_index_val(hi);
2003
2004      qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges);
2005    }
2006  return SVN_NO_ERROR;
2007}
2008
2009svn_error_t *
2010svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo,
2011                                   apr_pool_t *scratch_pool)
2012{
2013  apr_hash_index_t *hi;
2014
2015  for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi))
2016    {
2017      apr_array_header_t *rl = svn__apr_hash_index_val(hi);
2018
2019      SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool));
2020    }
2021
2022  return SVN_NO_ERROR;
2023}
2024
2025svn_mergeinfo_catalog_t
2026svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog,
2027                          apr_pool_t *pool)
2028{
2029  svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool);
2030  apr_hash_index_t *hi;
2031
2032  for (hi = apr_hash_first(pool, mergeinfo_catalog);
2033       hi;
2034       hi = apr_hash_next(hi))
2035    {
2036      const char *key = svn__apr_hash_index_key(hi);
2037      svn_mergeinfo_t val = svn__apr_hash_index_val(hi);
2038
2039      svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key),
2040                    svn_mergeinfo_dup(val, pool));
2041    }
2042
2043  return new_mergeinfo_catalog;
2044}
2045
2046svn_mergeinfo_t
2047svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
2048{
2049  svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
2050  apr_hash_index_t *hi;
2051
2052  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2053    {
2054      const char *path = svn__apr_hash_index_key(hi);
2055      apr_ssize_t pathlen = svn__apr_hash_index_klen(hi);
2056      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2057
2058      apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
2059                   svn_rangelist_dup(rangelist, pool));
2060    }
2061
2062  return new_mergeinfo;
2063}
2064
2065svn_error_t *
2066svn_mergeinfo_inheritable2(svn_mergeinfo_t *output,
2067                           svn_mergeinfo_t mergeinfo,
2068                           const char *path,
2069                           svn_revnum_t start,
2070                           svn_revnum_t end,
2071                           svn_boolean_t inheritable,
2072                           apr_pool_t *result_pool,
2073                           apr_pool_t *scratch_pool)
2074{
2075  apr_hash_index_t *hi;
2076  svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
2077
2078  for (hi = apr_hash_first(scratch_pool, mergeinfo);
2079       hi;
2080       hi = apr_hash_next(hi))
2081    {
2082      const char *key = svn__apr_hash_index_key(hi);
2083      apr_ssize_t keylen = svn__apr_hash_index_klen(hi);
2084      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2085      svn_rangelist_t *inheritable_rangelist;
2086
2087      if (!path || svn_path_compare_paths(path, key) == 0)
2088        SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist,
2089                                           start, end, inheritable,
2090                                           result_pool, scratch_pool));
2091      else
2092        inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool);
2093
2094      /* Only add this rangelist if some ranges remain.  A rangelist with
2095         a path mapped to an empty rangelist is not syntactically valid */
2096      if (inheritable_rangelist->nelts)
2097        apr_hash_set(inheritable_mergeinfo,
2098                     apr_pstrmemdup(result_pool, key, keylen), keylen,
2099                     inheritable_rangelist);
2100    }
2101  *output = inheritable_mergeinfo;
2102  return SVN_NO_ERROR;
2103}
2104
2105
2106svn_error_t *
2107svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist,
2108                           const svn_rangelist_t *rangelist,
2109                           svn_revnum_t start,
2110                           svn_revnum_t end,
2111                           svn_boolean_t inheritable,
2112                           apr_pool_t *result_pool,
2113                           apr_pool_t *scratch_pool)
2114{
2115  *inheritable_rangelist = apr_array_make(result_pool, 1,
2116                                          sizeof(svn_merge_range_t *));
2117  if (rangelist->nelts)
2118    {
2119      if (!SVN_IS_VALID_REVNUM(start)
2120          || !SVN_IS_VALID_REVNUM(end)
2121          || end < start)
2122        {
2123          int i;
2124          /* We want all non-inheritable ranges removed. */
2125          for (i = 0; i < rangelist->nelts; i++)
2126            {
2127              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2128                                                       svn_merge_range_t *);
2129              if (range->inheritable == inheritable)
2130                {
2131                  svn_merge_range_t *inheritable_range =
2132                    apr_palloc(result_pool, sizeof(*inheritable_range));
2133                  inheritable_range->start = range->start;
2134                  inheritable_range->end = range->end;
2135                  inheritable_range->inheritable = TRUE;
2136                  APR_ARRAY_PUSH(*inheritable_rangelist,
2137                                 svn_merge_range_t *) = range;
2138                }
2139            }
2140        }
2141      else
2142        {
2143          /* We want only the non-inheritable ranges bound by START
2144             and END removed. */
2145          svn_rangelist_t *ranges_inheritable =
2146            svn_rangelist__initialize(start, end, inheritable, scratch_pool);
2147
2148          if (rangelist->nelts)
2149            SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
2150                                         ranges_inheritable,
2151                                         rangelist,
2152                                         TRUE,
2153                                         result_pool));
2154        }
2155    }
2156  return SVN_NO_ERROR;
2157}
2158
2159svn_boolean_t
2160svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
2161                                       apr_pool_t *pool)
2162{
2163  apr_hash_index_t *hi;
2164  svn_boolean_t removed_some_ranges = FALSE;
2165
2166  if (mergeinfo)
2167    {
2168      for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2169        {
2170          const char *path = svn__apr_hash_index_key(hi);
2171          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2172
2173          if (rangelist->nelts == 0)
2174            {
2175              svn_hash_sets(mergeinfo, path, NULL);
2176              removed_some_ranges = TRUE;
2177            }
2178        }
2179    }
2180  return removed_some_ranges;
2181}
2182
2183svn_error_t *
2184svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
2185                                          svn_mergeinfo_catalog_t in_catalog,
2186                                          const char *prefix_path,
2187                                          apr_pool_t *pool)
2188{
2189  apr_hash_index_t *hi;
2190
2191  SVN_ERR_ASSERT(prefix_path[0] == '/');
2192
2193  *out_catalog = apr_hash_make(pool);
2194
2195  for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
2196    {
2197      const char *original_path = svn__apr_hash_index_key(hi);
2198      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2199      const char *new_path;
2200
2201      new_path = svn_fspath__skip_ancestor(prefix_path, original_path);
2202      SVN_ERR_ASSERT(new_path);
2203
2204      svn_hash_sets(*out_catalog, new_path, value);
2205    }
2206
2207  return SVN_NO_ERROR;
2208}
2209
2210svn_error_t *
2211svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
2212                                     svn_mergeinfo_catalog_t in_catalog,
2213                                     const char *prefix_path,
2214                                     apr_pool_t *result_pool,
2215                                     apr_pool_t *scratch_pool)
2216{
2217  apr_hash_index_t *hi;
2218
2219  *out_catalog = apr_hash_make(result_pool);
2220
2221  for (hi = apr_hash_first(scratch_pool, in_catalog);
2222       hi;
2223       hi = apr_hash_next(hi))
2224    {
2225      const char *original_path = svn__apr_hash_index_key(hi);
2226      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2227
2228      if (original_path[0] == '/')
2229        original_path++;
2230
2231      svn_hash_sets(*out_catalog,
2232                    svn_dirent_join(prefix_path, original_path, result_pool),
2233                    value);
2234    }
2235
2236  return SVN_NO_ERROR;
2237}
2238
2239svn_error_t *
2240svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
2241                                       svn_mergeinfo_t mergeinfo,
2242                                       const char *suffix_relpath,
2243                                       apr_pool_t *result_pool,
2244                                       apr_pool_t *scratch_pool)
2245{
2246  apr_hash_index_t *hi;
2247
2248  SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath));
2249
2250  *out_mergeinfo = apr_hash_make(result_pool);
2251
2252  for (hi = apr_hash_first(scratch_pool, mergeinfo);
2253       hi;
2254       hi = apr_hash_next(hi))
2255    {
2256      const char *fspath = svn__apr_hash_index_key(hi);
2257      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2258
2259      svn_hash_sets(*out_mergeinfo,
2260                    svn_fspath__join(fspath, suffix_relpath, result_pool),
2261                    rangelist);
2262    }
2263
2264  return SVN_NO_ERROR;
2265}
2266
2267svn_rangelist_t *
2268svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool)
2269{
2270  svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts,
2271                                           sizeof(svn_merge_range_t *));
2272
2273  /* allocate target range buffer with a single operation */
2274  svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts);
2275  int i;
2276
2277  /* fill it iteratively and link it into the range list */
2278  for (i = 0; i < rangelist->nelts; i++)
2279    {
2280      memcpy(copy + i,
2281             APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
2282             sizeof(*copy));
2283      APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i;
2284    }
2285
2286  return new_rl;
2287}
2288
2289svn_merge_range_t *
2290svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool)
2291{
2292  svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range));
2293  memcpy(new_range, range, sizeof(*new_range));
2294  return new_range;
2295}
2296
2297svn_boolean_t
2298svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev)
2299{
2300  assert(SVN_IS_VALID_REVNUM(range->start));
2301  assert(SVN_IS_VALID_REVNUM(range->end));
2302  assert(range->start != range->end);
2303
2304  if (range->start < range->end)
2305    return rev > range->start && rev <= range->end;
2306  else
2307    return rev > range->end && rev <= range->start;
2308}
2309
2310svn_error_t *
2311svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
2312                                           svn_mergeinfo_catalog_t catalog,
2313                                           const char *key_prefix,
2314                                           const char *val_prefix,
2315                                           apr_pool_t *pool)
2316{
2317  svn_stringbuf_t *output_buf = NULL;
2318
2319  if (catalog && apr_hash_count(catalog))
2320    {
2321      int i;
2322      apr_array_header_t *sorted_catalog =
2323        svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
2324
2325      output_buf = svn_stringbuf_create_empty(pool);
2326      for (i = 0; i < sorted_catalog->nelts; i++)
2327        {
2328          svn_sort__item_t elt =
2329            APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t);
2330          const char *path1;
2331          svn_mergeinfo_t mergeinfo;
2332          svn_stringbuf_t *mergeinfo_output_buf;
2333
2334          path1 = elt.key;
2335          mergeinfo = elt.value;
2336          if (key_prefix)
2337            svn_stringbuf_appendcstr(output_buf, key_prefix);
2338          svn_stringbuf_appendcstr(output_buf, path1);
2339          svn_stringbuf_appendcstr(output_buf, "\n");
2340          SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo,
2341                                         val_prefix ? val_prefix : "", pool));
2342          svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf);
2343          svn_stringbuf_appendcstr(output_buf, "\n");
2344        }
2345    }
2346#if SVN_DEBUG
2347  else if (!catalog)
2348    {
2349      output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2350      svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n"));
2351    }
2352  else if (apr_hash_count(catalog) == 0)
2353    {
2354      output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2355      svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n"));
2356    }
2357#endif
2358
2359  /* If we have an output_buf, convert it to an svn_string_t;
2360     otherwise, return a new string containing only a newline
2361     character.  */
2362  if (output_buf)
2363    *output = svn_stringbuf__morph_into_string(output_buf);
2364  else
2365    *output = svn_string_create("\n", pool);
2366
2367  return SVN_NO_ERROR;
2368}
2369
2370svn_error_t *
2371svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
2372                                   svn_revnum_t *oldest_rev,
2373                                   svn_mergeinfo_t mergeinfo,
2374                                   apr_pool_t *pool)
2375{
2376  *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
2377  if (mergeinfo)
2378    {
2379      apr_hash_index_t *hi;
2380
2381      for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2382        {
2383          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2384
2385          if (rangelist->nelts)
2386            {
2387              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist,
2388                                                       rangelist->nelts - 1,
2389                                                       svn_merge_range_t *);
2390              if (!SVN_IS_VALID_REVNUM(*youngest_rev)
2391                  || (range->end > *youngest_rev))
2392                *youngest_rev = range->end;
2393
2394              range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
2395              if (!SVN_IS_VALID_REVNUM(*oldest_rev)
2396                  || (range->start < *oldest_rev))
2397                *oldest_rev = range->start;
2398            }
2399        }
2400    }
2401  return SVN_NO_ERROR;
2402}
2403
2404svn_error_t *
2405svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat,
2406                                        svn_mergeinfo_catalog_t catalog,
2407                                        svn_revnum_t youngest_rev,
2408                                        svn_revnum_t oldest_rev,
2409                                        svn_boolean_t include_range,
2410                                        apr_pool_t *result_pool,
2411                                        apr_pool_t *scratch_pool)
2412{
2413  apr_hash_index_t *hi;
2414
2415  *filtered_cat = apr_hash_make(result_pool);
2416  for (hi = apr_hash_first(scratch_pool, catalog);
2417       hi;
2418       hi = apr_hash_next(hi))
2419    {
2420      const char *path = svn__apr_hash_index_key(hi);
2421      svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
2422      svn_mergeinfo_t filtered_mergeinfo;
2423
2424      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
2425                                                        mergeinfo,
2426                                                        youngest_rev,
2427                                                        oldest_rev,
2428                                                        include_range,
2429                                                        result_pool,
2430                                                        scratch_pool));
2431      if (apr_hash_count(filtered_mergeinfo))
2432        svn_hash_sets(*filtered_cat,
2433                      apr_pstrdup(result_pool, path), filtered_mergeinfo);
2434    }
2435
2436  return SVN_NO_ERROR;
2437}
2438
2439svn_error_t *
2440svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
2441                                          svn_mergeinfo_t mergeinfo,
2442                                          svn_revnum_t youngest_rev,
2443                                          svn_revnum_t oldest_rev,
2444                                          svn_boolean_t include_range,
2445                                          apr_pool_t *result_pool,
2446                                          apr_pool_t *scratch_pool)
2447{
2448  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
2449  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
2450  SVN_ERR_ASSERT(oldest_rev < youngest_rev);
2451
2452  *filtered_mergeinfo = apr_hash_make(result_pool);
2453
2454  if (mergeinfo)
2455    {
2456      apr_hash_index_t *hi;
2457      svn_rangelist_t *filter_rangelist =
2458        svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE,
2459                                  scratch_pool);
2460
2461      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2462           hi;
2463           hi = apr_hash_next(hi))
2464        {
2465          const char *path = svn__apr_hash_index_key(hi);
2466          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2467
2468          if (rangelist->nelts)
2469            {
2470              svn_rangelist_t *new_rangelist;
2471
2472              SVN_ERR(rangelist_intersect_or_remove(
2473                        &new_rangelist, filter_rangelist, rangelist,
2474                        ! include_range, FALSE, result_pool));
2475
2476              if (new_rangelist->nelts)
2477                svn_hash_sets(*filtered_mergeinfo,
2478                              apr_pstrdup(result_pool, path), new_rangelist);
2479            }
2480        }
2481    }
2482  return SVN_NO_ERROR;
2483}
2484
2485svn_error_t *
2486svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
2487                                           svn_mergeinfo_t mergeinfo,
2488                                           svn_revnum_t offset,
2489                                           apr_pool_t *result_pool,
2490                                           apr_pool_t *scratch_pool)
2491{
2492  apr_hash_index_t *hi;
2493  *adjusted_mergeinfo = apr_hash_make(result_pool);
2494
2495  if (mergeinfo)
2496    {
2497      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2498           hi;
2499           hi = apr_hash_next(hi))
2500        {
2501          int i;
2502          const char *path = svn__apr_hash_index_key(hi);
2503          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2504          svn_rangelist_t *adjusted_rangelist =
2505            apr_array_make(result_pool, rangelist->nelts,
2506                           sizeof(svn_merge_range_t *));
2507
2508          for (i = 0; i < rangelist->nelts; i++)
2509            {
2510              svn_merge_range_t *range =
2511                APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
2512
2513              if (range->start + offset > 0 && range->end + offset > 0)
2514                {
2515                  if (range->start + offset < 0)
2516                    range->start = 0;
2517                  else
2518                    range->start = range->start + offset;
2519
2520                  if (range->end + offset < 0)
2521                    range->end = 0;
2522                  else
2523                    range->end = range->end + offset;
2524                  APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) =
2525                    range;
2526                }
2527            }
2528
2529          if (adjusted_rangelist->nelts)
2530            svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path),
2531                          adjusted_rangelist);
2532        }
2533    }
2534  return SVN_NO_ERROR;
2535}
2536
2537svn_boolean_t
2538svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
2539                                 apr_pool_t *scratch_pool)
2540{
2541  if (mergeinfo)
2542    {
2543      apr_hash_index_t *hi;
2544
2545      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2546           hi;
2547           hi = apr_hash_next(hi))
2548        {
2549          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2550          int i;
2551
2552          for (i = 0; i < rangelist->nelts; i++)
2553            {
2554              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2555                                                       svn_merge_range_t *);
2556              if (!range->inheritable)
2557                return TRUE;
2558            }
2559        }
2560    }
2561  return FALSE;
2562}
2563
2564svn_rangelist_t *
2565svn_rangelist__initialize(svn_revnum_t start,
2566                          svn_revnum_t end,
2567                          svn_boolean_t inheritable,
2568                          apr_pool_t *result_pool)
2569{
2570  svn_rangelist_t *rangelist =
2571    apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
2572  svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
2573
2574  range->start = start;
2575  range->end = end;
2576  range->inheritable = inheritable;
2577  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2578  return rangelist;
2579}
2580
2581svn_error_t *
2582svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
2583                                       const apr_array_header_t *segments,
2584                                       apr_pool_t *pool)
2585{
2586  svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
2587  int i;
2588
2589  /* Translate location segments into merge sources and ranges. */
2590  for (i = 0; i < segments->nelts; i++)
2591    {
2592      svn_location_segment_t *segment =
2593        APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
2594      svn_rangelist_t *path_ranges;
2595      svn_merge_range_t *range;
2596      const char *source_path;
2597
2598      /* No path segment?  Skip it. */
2599      if (! segment->path)
2600        continue;
2601
2602      /* Prepend a leading slash to our path. */
2603      source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
2604
2605      /* See if we already stored ranges for this path.  If not, make
2606         a new list.  */
2607      path_ranges = svn_hash_gets(mergeinfo, source_path);
2608      if (! path_ranges)
2609        path_ranges = apr_array_make(pool, 1, sizeof(range));
2610
2611      /* A svn_location_segment_t may have legitimately describe only
2612         revision 0, but there is no corresponding representation for
2613         this in a svn_merge_range_t. */
2614      if (segment->range_start == 0 && segment->range_end == 0)
2615        continue;
2616
2617      /* Build a merge range, push it onto the list of ranges, and for
2618         good measure, (re)store it in the hash. */
2619      range = apr_pcalloc(pool, sizeof(*range));
2620      range->start = MAX(segment->range_start - 1, 0);
2621      range->end = segment->range_end;
2622      range->inheritable = TRUE;
2623      APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
2624      svn_hash_sets(mergeinfo, source_path, path_ranges);
2625    }
2626
2627  *mergeinfo_p = mergeinfo;
2628  return SVN_NO_ERROR;
2629}
2630
2631svn_error_t *
2632svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
2633                          svn_mergeinfo_t merge_history,
2634                          apr_pool_t *result_pool,
2635                          apr_pool_t *scratch_pool)
2636{
2637  if (apr_hash_count(merge_history))
2638    {
2639      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2640      apr_hash_index_t *hi;
2641
2642      for (hi = apr_hash_first(scratch_pool, merge_history);
2643           hi;
2644           hi = apr_hash_next(hi))
2645        {
2646          svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi);
2647
2648          svn_pool_clear(iterpool);
2649          SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
2650                                       result_pool, iterpool));
2651        }
2652      svn_pool_destroy(iterpool);
2653    }
2654  return SVN_NO_ERROR;
2655}
2656
2657
2658const char *
2659svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
2660{
2661  switch (inherit)
2662    {
2663    case svn_mergeinfo_inherited:
2664      return "inherited";
2665    case svn_mergeinfo_nearest_ancestor:
2666      return "nearest-ancestor";
2667    default:
2668      return "explicit";
2669    }
2670}
2671
2672svn_mergeinfo_inheritance_t
2673svn_inheritance_from_word(const char *word)
2674{
2675  if (strcmp(word, "inherited") == 0)
2676    return svn_mergeinfo_inherited;
2677  if (strcmp(word, "nearest-ancestor") == 0)
2678    return svn_mergeinfo_nearest_ancestor;
2679  return svn_mergeinfo_explicit;
2680}
2681