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