1251881Speter/*
2251881Speter * mergeinfo.c:  Mergeinfo parsing and handling
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter#include <assert.h>
24251881Speter#include <ctype.h>
25251881Speter
26251881Speter#include "svn_path.h"
27251881Speter#include "svn_types.h"
28251881Speter#include "svn_ctype.h"
29251881Speter#include "svn_pools.h"
30251881Speter#include "svn_sorts.h"
31251881Speter#include "svn_error.h"
32251881Speter#include "svn_error_codes.h"
33251881Speter#include "svn_string.h"
34251881Speter#include "svn_mergeinfo.h"
35251881Speter#include "private/svn_fspath.h"
36251881Speter#include "private/svn_mergeinfo_private.h"
37251881Speter#include "private/svn_string_private.h"
38251881Speter#include "private/svn_subr_private.h"
39251881Speter#include "svn_private_config.h"
40251881Speter#include "svn_hash.h"
41251881Speter#include "private/svn_dep_compat.h"
42251881Speter
43251881Speter/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
44251881Speter   overlapping, and their inheritability allows them to be combined, put
45251881Speter   the result in OUTPUT and return TRUE, otherwise return FALSE.
46251881Speter
47251881Speter   CONSIDER_INHERITANCE determines how to account for the inheritability
48251881Speter   of IN1 and IN2 when trying to combine ranges.  If ranges with different
49251881Speter   inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
50251881Speter   to happen) the result is inheritable.  If both ranges are inheritable the
51251881Speter   result is inheritable.  Only and if both ranges are non-inheritable is
52251881Speter   the result is non-inheritable.
53251881Speter
54251881Speter   Range overlapping detection algorithm from
55251881Speter   http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
56251881Speter*/
57251881Speterstatic svn_boolean_t
58251881Spetercombine_ranges(svn_merge_range_t *output,
59251881Speter               const svn_merge_range_t *in1,
60251881Speter               const svn_merge_range_t *in2,
61251881Speter               svn_boolean_t consider_inheritance)
62251881Speter{
63251881Speter  if (in1->start <= in2->end && in2->start <= in1->end)
64251881Speter    {
65251881Speter      if (!consider_inheritance
66251881Speter          || (consider_inheritance
67251881Speter              && (in1->inheritable == in2->inheritable)))
68251881Speter        {
69251881Speter          output->start = MIN(in1->start, in2->start);
70251881Speter          output->end = MAX(in1->end, in2->end);
71251881Speter          output->inheritable = (in1->inheritable || in2->inheritable);
72251881Speter          return TRUE;
73251881Speter        }
74251881Speter    }
75251881Speter  return FALSE;
76251881Speter}
77251881Speter
78251881Speter/* pathname -> PATHNAME */
79251881Speterstatic svn_error_t *
80251881Speterparse_pathname(const char **input,
81251881Speter               const char *end,
82251881Speter               const char **pathname,
83251881Speter               apr_pool_t *pool)
84251881Speter{
85251881Speter  const char *curr = *input;
86251881Speter  const char *last_colon = NULL;
87251881Speter
88251881Speter  /* A pathname may contain colons, so find the last colon before END
89251881Speter     or newline.  We'll consider this the divider between the pathname
90251881Speter     and the revisionlist. */
91251881Speter  while (curr < end && *curr != '\n')
92251881Speter    {
93251881Speter      if (*curr == ':')
94251881Speter        last_colon = curr;
95251881Speter      curr++;
96251881Speter    }
97251881Speter
98251881Speter  if (!last_colon)
99251881Speter    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
100251881Speter                            _("Pathname not terminated by ':'"));
101251881Speter  if (last_colon == *input)
102251881Speter    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
103251881Speter                            _("No pathname preceding ':'"));
104251881Speter
105251881Speter  /* Tolerate relative repository paths, but convert them to absolute.
106251881Speter     ### Efficiency?  1 string duplication here, 2 in canonicalize. */
107251881Speter  *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
108251881Speter                                                    last_colon - *input),
109251881Speter                                       pool);
110251881Speter
111251881Speter  *input = last_colon;
112251881Speter
113251881Speter  return SVN_NO_ERROR;
114251881Speter}
115251881Speter
116251881Speter/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
117251881Speter * revision range.
118251881Speter *
119251881Speter * Note: The smallest valid value of RANGE->start is 0 because it is an
120251881Speter * exclusive endpoint, being one less than the revision number of the first
121251881Speter * change described by the range, and the oldest possible change is "r1" as
122251881Speter * there cannot be a change "r0". */
123251881Speter#define IS_VALID_FORWARD_RANGE(range) \
124251881Speter  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
125251881Speter
126251881Speter/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
127251881Spetertypedef enum intersection_type_t
128251881Speter{
129251881Speter  /* Ranges don't intersect and don't adjoin. */
130251881Speter  svn__no_intersection,
131251881Speter
132251881Speter  /* Ranges are equal. */
133251881Speter  svn__equal_intersection,
134251881Speter
135251881Speter  /* Ranges adjoin but don't overlap. */
136251881Speter  svn__adjoining_intersection,
137251881Speter
138251881Speter  /* Ranges overlap but neither is a subset of the other. */
139251881Speter  svn__overlapping_intersection,
140251881Speter
141251881Speter  /* One range is a proper subset of the other. */
142251881Speter  svn__proper_subset_intersection
143251881Speter} intersection_type_t;
144251881Speter
145251881Speter/* Given ranges R1 and R2, both of which must be forward merge ranges,
146251881Speter   set *INTERSECTION_TYPE to describe how the ranges intersect, if they
147251881Speter   do at all.  The inheritance type of the ranges is not considered. */
148251881Speterstatic svn_error_t *
149251881Speterget_type_of_intersection(const svn_merge_range_t *r1,
150251881Speter                         const svn_merge_range_t *r2,
151251881Speter                         intersection_type_t *intersection_type)
152251881Speter{
153251881Speter  SVN_ERR_ASSERT(r1);
154251881Speter  SVN_ERR_ASSERT(r2);
155251881Speter  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
156251881Speter  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
157251881Speter
158251881Speter  if (!(r1->start <= r2->end && r2->start <= r1->end))
159251881Speter    *intersection_type = svn__no_intersection;
160251881Speter  else if (r1->start == r2->start && r1->end == r2->end)
161251881Speter    *intersection_type = svn__equal_intersection;
162251881Speter  else if (r1->end == r2->start || r2->end == r1->start)
163251881Speter    *intersection_type = svn__adjoining_intersection;
164251881Speter  else if (r1->start <= r2->start && r1->end >= r2->end)
165251881Speter    *intersection_type = svn__proper_subset_intersection;
166251881Speter  else if (r2->start <= r1->start && r2->end >= r1->end)
167251881Speter    *intersection_type = svn__proper_subset_intersection;
168251881Speter  else
169251881Speter    *intersection_type = svn__overlapping_intersection;
170251881Speter
171251881Speter  return SVN_NO_ERROR;
172251881Speter}
173251881Speter
174251881Speter/* Modify or extend RANGELIST (a list of merge ranges) to incorporate
175251881Speter   NEW_RANGE. RANGELIST is a "rangelist" as defined in svn_mergeinfo.h.
176251881Speter
177251881Speter   OVERVIEW
178251881Speter
179251881Speter   Determine the minimal set of non-overlapping merge ranges required to
180251881Speter   represent the combination of RANGELIST and NEW_RANGE. The result depends
181251881Speter   on whether and how NEW_RANGE overlaps any merge range[*] in RANGELIST,
182251881Speter   and also on any differences in the inheritability of each range,
183251881Speter   according to the rules described below. Modify RANGELIST to represent
184251881Speter   this result, by adjusting the last range in it and/or appending one or
185251881Speter   two more ranges.
186251881Speter
187251881Speter   ([*] Due to the simplifying assumption below, only the last range in
188251881Speter   RANGELIST is considered.)
189251881Speter
190251881Speter   DETAILS
191251881Speter
192251881Speter   If RANGELIST is not empty assume NEW_RANGE does not intersect with any
193251881Speter   range before the last one in RANGELIST.
194251881Speter
195251881Speter   If RANGELIST is empty or NEW_RANGE does not intersect with the lastrange
196251881Speter   in RANGELIST, then append a copy of NEW_RANGE, allocated in RESULT_POOL,
197251881Speter   to RANGELIST.
198251881Speter
199251881Speter   If NEW_RANGE intersects with the last range in RANGELIST then combine
200251881Speter   these two ranges as described below:
201251881Speter
202251881Speter   If the intersecting ranges have the same inheritability then simply
203251881Speter   combine the ranges in place.  Otherwise, if the ranges intersect but
204251881Speter   differ in inheritability, then merge the ranges as dictated by
205251881Speter   CONSIDER_INHERITANCE:
206251881Speter
207251881Speter   If CONSIDER_INHERITANCE is false then intersecting ranges are combined
208251881Speter   into a single range.  The inheritability of the resulting range is
209251881Speter   non-inheritable *only* if both ranges are non-inheritable, otherwise the
210251881Speter   combined range is inheritable, e.g.:
211251881Speter
212251881Speter     Last range in        NEW_RANGE        RESULTING RANGES
213251881Speter     RANGELIST
214251881Speter     -------------        ---------        ----------------
215251881Speter     4-10*                6-13             4-13
216251881Speter     4-10                 6-13*            4-13
217251881Speter     4-10*                6-13*            4-13*
218251881Speter
219251881Speter   If CONSIDER_INHERITANCE is true, then only the intersection between the
220251881Speter   two ranges is combined, with the inheritability of the resulting range
221251881Speter   non-inheritable only if both ranges were non-inheritable.  The
222251881Speter   non-intersecting portions are added as separate ranges allocated in
223251881Speter   RESULT_POOL, e.g.:
224251881Speter
225251881Speter     Last range in        NEW_RANGE        RESULTING RANGES
226251881Speter     RANGELIST
227251881Speter     -------------        ---------        ----------------
228251881Speter     4-10*                6                4-5*, 6, 7-10*
229251881Speter     4-10*                6-12             4-5*, 6-12
230251881Speter
231251881Speter   Note that the standard rules for rangelists still apply and overlapping
232251881Speter   ranges are not allowed.  So if the above would result in overlapping
233251881Speter   ranges of the same inheritance, the overlapping ranges are merged into a
234251881Speter   single range, e.g.:
235251881Speter
236251881Speter     Last range in        NEW_RANGE        RESULTING RANGES
237251881Speter     RANGELIST
238251881Speter     -------------        ---------        ----------------
239251881Speter     4-10                 6*               4-10 (Not 4-5, 6, 7-10)
240251881Speter
241251881Speter   When replacing the last range in RANGELIST, either allocate a new range in
242251881Speter   RESULT_POOL or modify the existing range in place.  Any new ranges added
243251881Speter   to RANGELIST are allocated in RESULT_POOL.
244251881Speter*/
245251881Speterstatic svn_error_t *
246251881Spetercombine_with_lastrange(const svn_merge_range_t *new_range,
247251881Speter                       svn_rangelist_t *rangelist,
248251881Speter                       svn_boolean_t consider_inheritance,
249251881Speter                       apr_pool_t *result_pool)
250251881Speter{
251251881Speter  svn_merge_range_t *lastrange;
252251881Speter  svn_merge_range_t combined_range;
253251881Speter
254251881Speter  /* We don't accept a NULL RANGELIST. */
255251881Speter  SVN_ERR_ASSERT(rangelist);
256251881Speter
257251881Speter  if (rangelist->nelts > 0)
258251881Speter    lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *);
259251881Speter  else
260251881Speter    lastrange = NULL;
261251881Speter
262251881Speter  if (!lastrange)
263251881Speter    {
264251881Speter      /* No *LASTRANGE so push NEW_RANGE onto RANGELIST and we are done. */
265251881Speter      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
266251881Speter        svn_merge_range_dup(new_range, result_pool);
267251881Speter    }
268251881Speter  else if (!consider_inheritance)
269251881Speter    {
270251881Speter      /* We are not considering inheritance so we can merge intersecting
271251881Speter         ranges of different inheritability.  Of course if the ranges
272251881Speter         don't intersect at all we simply push NEW_RANGE only RANGELIST. */
273251881Speter      if (combine_ranges(&combined_range, lastrange, new_range, FALSE))
274251881Speter        {
275251881Speter          *lastrange = combined_range;
276251881Speter        }
277251881Speter      else
278251881Speter        {
279251881Speter          APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
280251881Speter            svn_merge_range_dup(new_range, result_pool);
281251881Speter        }
282251881Speter    }
283251881Speter  else /* Considering inheritance */
284251881Speter    {
285251881Speter      if (combine_ranges(&combined_range, lastrange, new_range, TRUE))
286251881Speter        {
287251881Speter          /* Even when considering inheritance two intersection ranges
288251881Speter             of the same inheritability can simply be combined. */
289251881Speter          *lastrange = combined_range;
290251881Speter        }
291251881Speter      else
292251881Speter        {
293251881Speter          /* If we are here then the ranges either don't intersect or do
294251881Speter             intersect but have differing inheritability.  Check for the
295251881Speter             first case as that is easy to handle. */
296251881Speter          intersection_type_t intersection_type;
297251881Speter          svn_boolean_t sorted = FALSE;
298251881Speter
299251881Speter          SVN_ERR(get_type_of_intersection(new_range, lastrange,
300251881Speter                                           &intersection_type));
301251881Speter
302251881Speter          switch (intersection_type)
303251881Speter            {
304251881Speter              case svn__no_intersection:
305251881Speter                /* NEW_RANGE and *LASTRANGE *really* don't intersect so
306251881Speter                   just push NEW_RANGE only RANGELIST. */
307251881Speter                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
308251881Speter                  svn_merge_range_dup(new_range, result_pool);
309251881Speter                sorted = (svn_sort_compare_ranges(&lastrange,
310251881Speter                                                  &new_range) < 0);
311251881Speter                break;
312251881Speter
313251881Speter              case svn__equal_intersection:
314251881Speter                /* They range are equal so all we do is force the
315251881Speter                   inheritability of lastrange to true. */
316251881Speter                lastrange->inheritable = TRUE;
317251881Speter                sorted = TRUE;
318251881Speter                break;
319251881Speter
320251881Speter              case svn__adjoining_intersection:
321251881Speter                /* They adjoin but don't overlap so just push NEW_RANGE
322251881Speter                   onto RANGELIST. */
323251881Speter                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
324251881Speter                  svn_merge_range_dup(new_range, result_pool);
325251881Speter                sorted = (svn_sort_compare_ranges(&lastrange,
326251881Speter                                                  &new_range) < 0);
327251881Speter                break;
328251881Speter
329251881Speter              case svn__overlapping_intersection:
330251881Speter                /* They ranges overlap but neither is a proper subset of
331251881Speter                   the other.  We'll end up pusing two new ranges onto
332251881Speter                   RANGELIST, the intersecting part and the part unique to
333251881Speter                   NEW_RANGE.*/
334251881Speter                {
335251881Speter                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
336251881Speter                                                              result_pool);
337251881Speter                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
338251881Speter                                                              result_pool);
339251881Speter
340251881Speter                  /* Pop off *LASTRANGE to make our manipulations
341251881Speter                     easier. */
342251881Speter                  apr_array_pop(rangelist);
343251881Speter
344251881Speter                  /* Ensure R1 is the older range. */
345251881Speter                  if (r2->start < r1->start)
346251881Speter                    {
347251881Speter                      /* Swap R1 and R2. */
348251881Speter                      *r2 = *r1;
349251881Speter                      *r1 = *new_range;
350251881Speter                    }
351251881Speter
352251881Speter                  /* Absorb the intersecting ranges into the
353251881Speter                     inheritable range. */
354251881Speter                  if (r1->inheritable)
355251881Speter                    r2->start = r1->end;
356251881Speter                  else
357251881Speter                    r1->end = r2->start;
358251881Speter
359251881Speter                  /* Push everything back onto RANGELIST. */
360251881Speter                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
361251881Speter                  sorted = (svn_sort_compare_ranges(&lastrange,
362251881Speter                                                    &r1) < 0);
363251881Speter                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
364251881Speter                  if (sorted)
365251881Speter                    sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
366251881Speter                  break;
367251881Speter                }
368251881Speter
369251881Speter              default: /* svn__proper_subset_intersection */
370251881Speter                {
371251881Speter                  /* One range is a proper subset of the other. */
372251881Speter                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
373251881Speter                                                              result_pool);
374251881Speter                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
375251881Speter                                                              result_pool);
376251881Speter                  svn_merge_range_t *r3 = NULL;
377251881Speter
378251881Speter                  /* Pop off *LASTRANGE to make our manipulations
379251881Speter                     easier. */
380251881Speter                  apr_array_pop(rangelist);
381251881Speter
382251881Speter                  /* Ensure R1 is the superset. */
383251881Speter                  if (r2->start < r1->start || r2->end > r1->end)
384251881Speter                    {
385251881Speter                      /* Swap R1 and R2. */
386251881Speter                      *r2 = *r1;
387251881Speter                      *r1 = *new_range;
388251881Speter                    }
389251881Speter
390251881Speter                  if (r1->inheritable)
391251881Speter                    {
392251881Speter                      /* The simple case: The superset is inheritable, so
393251881Speter                         just combine r1 and r2. */
394251881Speter                      r1->start = MIN(r1->start, r2->start);
395251881Speter                      r1->end = MAX(r1->end, r2->end);
396251881Speter                      r2 = NULL;
397251881Speter                    }
398251881Speter                  else if (r1->start == r2->start)
399251881Speter                    {
400251881Speter                      svn_revnum_t tmp_revnum;
401251881Speter
402251881Speter                      /* *LASTRANGE and NEW_RANGE share an end point. */
403251881Speter                      tmp_revnum = r1->end;
404251881Speter                      r1->end = r2->end;
405251881Speter                      r2->inheritable = r1->inheritable;
406251881Speter                      r1->inheritable = TRUE;
407251881Speter                      r2->start = r1->end;
408251881Speter                      r2->end = tmp_revnum;
409251881Speter                    }
410251881Speter                  else if (r1->end == r2->end)
411251881Speter                    {
412251881Speter                      /* *LASTRANGE and NEW_RANGE share an end point. */
413251881Speter                      r1->end = r2->start;
414251881Speter                      r2->inheritable = TRUE;
415251881Speter                    }
416251881Speter                  else
417251881Speter                    {
418251881Speter                      /* NEW_RANGE and *LASTRANGE share neither start
419251881Speter                         nor end points. */
420251881Speter                      r3 = apr_pcalloc(result_pool, sizeof(*r3));
421251881Speter                      r3->start = r2->end;
422251881Speter                      r3->end = r1->end;
423251881Speter                      r3->inheritable = r1->inheritable;
424251881Speter                      r2->inheritable = TRUE;
425251881Speter                      r1->end = r2->start;
426251881Speter                    }
427251881Speter
428251881Speter                  /* Push everything back onto RANGELIST. */
429251881Speter                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
430251881Speter                  sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
431251881Speter                  if (r2)
432251881Speter                    {
433251881Speter                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
434251881Speter                      if (sorted)
435251881Speter                        sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
436251881Speter                    }
437251881Speter                  if (r3)
438251881Speter                    {
439251881Speter                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
440251881Speter                      if (sorted)
441251881Speter                        {
442251881Speter                          if (r2)
443251881Speter                            sorted = (svn_sort_compare_ranges(&r2,
444251881Speter                                                              &r3) < 0);
445251881Speter                          else
446251881Speter                            sorted = (svn_sort_compare_ranges(&r1,
447251881Speter                                                              &r3) < 0);
448251881Speter                        }
449251881Speter                    }
450251881Speter                  break;
451251881Speter                }
452251881Speter            }
453251881Speter
454251881Speter          /* Some of the above cases might have put *RANGELIST out of
455251881Speter             order, so re-sort.*/
456251881Speter          if (!sorted)
457251881Speter            qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
458251881Speter                  svn_sort_compare_ranges);
459251881Speter        }
460251881Speter    }
461251881Speter
462251881Speter  return SVN_NO_ERROR;
463251881Speter}
464251881Speter
465251881Speter/* Convert a single svn_merge_range_t *RANGE back into a string.  */
466251881Speterstatic char *
467251881Speterrange_to_string(const svn_merge_range_t *range,
468251881Speter                apr_pool_t *pool)
469251881Speter{
470251881Speter  const char *mark
471251881Speter    = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR;
472251881Speter
473251881Speter  if (range->start == range->end - 1)
474251881Speter    return apr_psprintf(pool, "%ld%s", range->end, mark);
475251881Speter  else if (range->start - 1 == range->end)
476251881Speter    return apr_psprintf(pool, "-%ld%s", range->start, mark);
477251881Speter  else if (range->start < range->end)
478251881Speter    return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
479251881Speter  else
480251881Speter    return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
481251881Speter}
482251881Speter
483251881Speter/* Helper for svn_mergeinfo_parse()
484251881Speter   Append revision ranges onto the array RANGELIST to represent the range
485251881Speter   descriptions found in the string *INPUT.  Read only as far as a newline
486251881Speter   or the position END, whichever comes first.  Set *INPUT to the position
487251881Speter   after the last character of INPUT that was used.
488251881Speter
489251881Speter   revisionlist -> (revisionelement)(COMMA revisionelement)*
490251881Speter   revisionrange -> REVISION "-" REVISION("*")
491251881Speter   revisionelement -> revisionrange | REVISION("*")
492251881Speter*/
493251881Speterstatic svn_error_t *
494251881Speterparse_rangelist(const char **input, const char *end,
495251881Speter                svn_rangelist_t *rangelist,
496251881Speter                apr_pool_t *pool)
497251881Speter{
498251881Speter  const char *curr = *input;
499251881Speter
500251881Speter  /* Eat any leading horizontal white-space before the rangelist. */
501251881Speter  while (curr < end && *curr != '\n' && isspace(*curr))
502251881Speter    curr++;
503251881Speter
504251881Speter  if (*curr == '\n' || curr == end)
505251881Speter    {
506251881Speter      /* Empty range list. */
507251881Speter      *input = curr;
508251881Speter      return SVN_NO_ERROR;
509251881Speter    }
510251881Speter
511251881Speter  while (curr < end && *curr != '\n')
512251881Speter    {
513251881Speter      /* Parse individual revisions or revision ranges. */
514251881Speter      svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
515251881Speter      svn_revnum_t firstrev;
516251881Speter
517251881Speter      SVN_ERR(svn_revnum_parse(&firstrev, curr, &curr));
518251881Speter      if (*curr != '-' && *curr != '\n' && *curr != ',' && *curr != '*'
519251881Speter          && curr != end)
520251881Speter        return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
521251881Speter                                 _("Invalid character '%c' found in revision "
522251881Speter                                   "list"), *curr);
523251881Speter      mrange->start = firstrev - 1;
524251881Speter      mrange->end = firstrev;
525251881Speter      mrange->inheritable = TRUE;
526251881Speter
527251881Speter      if (firstrev == 0)
528251881Speter        return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
529251881Speter                                 _("Invalid revision number '0' found in "
530251881Speter                                   "range list"));
531251881Speter
532251881Speter      if (*curr == '-')
533251881Speter        {
534251881Speter          svn_revnum_t secondrev;
535251881Speter
536251881Speter          curr++;
537251881Speter          SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr));
538251881Speter          if (firstrev > secondrev)
539251881Speter            return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
540251881Speter                                     _("Unable to parse reversed revision "
541251881Speter                                       "range '%ld-%ld'"),
542251881Speter                                       firstrev, secondrev);
543251881Speter          else if (firstrev == secondrev)
544251881Speter            return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
545251881Speter                                     _("Unable to parse revision range "
546251881Speter                                       "'%ld-%ld' with same start and end "
547251881Speter                                       "revisions"), firstrev, secondrev);
548251881Speter          mrange->end = secondrev;
549251881Speter        }
550251881Speter
551251881Speter      if (*curr == '\n' || curr == end)
552251881Speter        {
553251881Speter          APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
554251881Speter          *input = curr;
555251881Speter          return SVN_NO_ERROR;
556251881Speter        }
557251881Speter      else if (*curr == ',')
558251881Speter        {
559251881Speter          APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
560251881Speter          curr++;
561251881Speter        }
562251881Speter      else if (*curr == '*')
563251881Speter        {
564251881Speter          mrange->inheritable = FALSE;
565251881Speter          curr++;
566251881Speter          if (*curr == ',' || *curr == '\n' || curr == end)
567251881Speter            {
568251881Speter              APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
569251881Speter              if (*curr == ',')
570251881Speter                {
571251881Speter                  curr++;
572251881Speter                }
573251881Speter              else
574251881Speter                {
575251881Speter                  *input = curr;
576251881Speter                  return SVN_NO_ERROR;
577251881Speter                }
578251881Speter            }
579251881Speter          else
580251881Speter            {
581251881Speter              return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
582251881Speter                                       _("Invalid character '%c' found in "
583251881Speter                                         "range list"), *curr);
584251881Speter            }
585251881Speter        }
586251881Speter      else
587251881Speter        {
588251881Speter          return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
589251881Speter                                   _("Invalid character '%c' found in "
590251881Speter                                     "range list"), *curr);
591251881Speter        }
592251881Speter
593251881Speter    }
594251881Speter  if (*curr != '\n')
595251881Speter    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
596251881Speter                            _("Range list parsing ended before hitting "
597251881Speter                              "newline"));
598251881Speter  *input = curr;
599251881Speter  return SVN_NO_ERROR;
600251881Speter}
601251881Speter
602251881Spetersvn_error_t *
603251881Spetersvn_rangelist__parse(svn_rangelist_t **rangelist,
604251881Speter                     const char *str,
605251881Speter                     apr_pool_t *result_pool)
606251881Speter{
607251881Speter  const char *s = str;
608251881Speter
609251881Speter  *rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
610251881Speter  SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool));
611251881Speter  return SVN_NO_ERROR;
612251881Speter}
613251881Speter
614251881Spetersvn_error_t *
615251881Spetersvn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
616251881Speter                                       apr_pool_t *scratch_pool)
617251881Speter{
618251881Speter  int i;
619251881Speter  svn_merge_range_t *range, *lastrange;
620251881Speter
621251881Speter  lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
622251881Speter
623251881Speter  for (i = 1; i < rangelist->nelts; i++)
624251881Speter    {
625251881Speter      range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
626251881Speter      if (lastrange->start <= range->end
627251881Speter          && range->start <= lastrange->end)
628251881Speter        {
629251881Speter          /* The ranges are adjacent or intersect. */
630251881Speter
631251881Speter          /* svn_mergeinfo_parse promises to combine overlapping
632251881Speter             ranges as long as their inheritability is the same. */
633251881Speter          if (range->start < lastrange->end
634251881Speter              && range->inheritable != lastrange->inheritable)
635251881Speter            {
636251881Speter              return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
637251881Speter                                       _("Unable to parse overlapping "
638251881Speter                                         "revision ranges '%s' and '%s' "
639251881Speter                                         "with different inheritance "
640251881Speter                                         "types"),
641251881Speter                                       range_to_string(lastrange,
642251881Speter                                                       scratch_pool),
643251881Speter                                       range_to_string(range,
644251881Speter                                                       scratch_pool));
645251881Speter            }
646251881Speter
647251881Speter          /* Combine overlapping or adjacent ranges with the
648251881Speter             same inheritability. */
649251881Speter          if (lastrange->inheritable == range->inheritable)
650251881Speter            {
651251881Speter              lastrange->end = MAX(range->end, lastrange->end);
652251881Speter              svn_sort__array_delete(rangelist, i, 1);
653251881Speter              i--;
654251881Speter            }
655251881Speter        }
656251881Speter      lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
657251881Speter    }
658251881Speter
659251881Speter  return SVN_NO_ERROR;
660251881Speter}
661251881Speter
662251881Speter/* revisionline -> PATHNAME COLON revisionlist */
663251881Speterstatic svn_error_t *
664251881Speterparse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
665251881Speter                    apr_pool_t *scratch_pool)
666251881Speter{
667251881Speter  const char *pathname = "";
668251881Speter  apr_ssize_t klen;
669251881Speter  svn_rangelist_t *existing_rangelist;
670251881Speter  svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
671251881Speter                                              sizeof(svn_merge_range_t *));
672251881Speter
673251881Speter  SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
674251881Speter
675251881Speter  if (*(*input) != ':')
676251881Speter    return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
677251881Speter                            _("Pathname not terminated by ':'"));
678251881Speter
679251881Speter  *input = *input + 1;
680251881Speter
681251881Speter  SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
682251881Speter
683251881Speter  if (rangelist->nelts == 0)
684251881Speter      return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
685251881Speter                               _("Mergeinfo for '%s' maps to an "
686251881Speter                                 "empty revision range"), pathname);
687251881Speter  if (*input != end && *(*input) != '\n')
688251881Speter    return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
689251881Speter                             _("Could not find end of line in range list line "
690251881Speter                               "in '%s'"), *input);
691251881Speter
692251881Speter  if (*input != end)
693251881Speter    *input = *input + 1;
694251881Speter
695251881Speter  /* Sort the rangelist, combine adjacent ranges into single ranges,
696251881Speter     and make sure there are no overlapping ranges. */
697251881Speter  if (rangelist->nelts > 1)
698251881Speter    {
699251881Speter      qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
700251881Speter            svn_sort_compare_ranges);
701251881Speter
702251881Speter      SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
703251881Speter    }
704251881Speter
705251881Speter  /* Handle any funky mergeinfo with relative merge source paths that
706251881Speter     might exist due to issue #3547.  It's possible that this issue allowed
707251881Speter     the creation of mergeinfo with path keys that differ only by a
708251881Speter     leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
709251881Speter     we encounter this we merge the rangelists together under a single
710251881Speter     absolute path key. */
711251881Speter  klen = strlen(pathname);
712251881Speter  existing_rangelist = apr_hash_get(hash, pathname, klen);
713251881Speter  if (existing_rangelist)
714251881Speter    SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
715251881Speter                                 scratch_pool, scratch_pool));
716251881Speter
717251881Speter  apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
718251881Speter               klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
719251881Speter
720251881Speter  return SVN_NO_ERROR;
721251881Speter}
722251881Speter
723251881Speter/* top -> revisionline (NEWLINE revisionline)*  */
724251881Speterstatic svn_error_t *
725251881Speterparse_top(const char **input, const char *end, svn_mergeinfo_t hash,
726251881Speter          apr_pool_t *pool)
727251881Speter{
728251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
729251881Speter
730251881Speter  while (*input < end)
731251881Speter    {
732251881Speter      svn_pool_clear(iterpool);
733251881Speter      SVN_ERR(parse_revision_line(input, end, hash, iterpool));
734251881Speter    }
735251881Speter  svn_pool_destroy(iterpool);
736251881Speter
737251881Speter  return SVN_NO_ERROR;
738251881Speter}
739251881Speter
740251881Spetersvn_error_t *
741251881Spetersvn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo,
742251881Speter                    const char *input,
743251881Speter                    apr_pool_t *pool)
744251881Speter{
745251881Speter  svn_error_t *err;
746251881Speter
747251881Speter  *mergeinfo = svn_hash__make(pool);
748251881Speter  err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
749251881Speter
750251881Speter  /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
751251881Speter  if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
752251881Speter    err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err,
753251881Speter                            _("Could not parse mergeinfo string '%s'"),
754251881Speter                            input);
755251881Speter  return err;
756251881Speter}
757251881Speter
758251881Speter/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
759251881Speter   a single rangelist element in-place.
760251881Speter
761251881Speter   If *RANGE_INDEX is not a valid element in RANGELIST do nothing.  Otherwise
762251881Speter   ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
763251881Speter   subsequent ranges in RANGELIST.
764251881Speter
765251881Speter   If overlap is found, then remove, modify, and/or add elements to RANGELIST
766251881Speter   as per the invariants for rangelists documented in svn_mergeinfo.h.  If
767251881Speter   RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
768251881Speter   elements if their inheritability permits -- The inheritance of intersecting
769251881Speter   and adjoining ranges is handled as per svn_mergeinfo_merge2.  Upon return
770251881Speter   set *RANGE_INDEX to the index of the youngest element modified, added, or
771251881Speter   adjoined to RANGELIST[*RANGE_INDEX].
772251881Speter
773251881Speter   Note: Adjoining rangelist elements are those where the end rev of the older
774251881Speter   element is equal to the start rev of the younger element.
775251881Speter
776251881Speter   Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
777251881Speterstatic void
778251881Speteradjust_remaining_ranges(svn_rangelist_t *rangelist,
779251881Speter                        int *range_index,
780251881Speter                        apr_pool_t *result_pool)
781251881Speter{
782251881Speter  int i;
783251881Speter  int starting_index;
784251881Speter  int elements_to_delete = 0;
785251881Speter  svn_merge_range_t *modified_range;
786251881Speter
787251881Speter  if (*range_index >= rangelist->nelts)
788251881Speter    return;
789251881Speter
790251881Speter  starting_index = *range_index + 1;
791251881Speter  modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
792251881Speter
793251881Speter  for (i = *range_index + 1; i < rangelist->nelts; i++)
794251881Speter    {
795251881Speter      svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
796251881Speter                                                    svn_merge_range_t *);
797251881Speter
798251881Speter      /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
799251881Speter         RANGELIST then we are finished. */
800251881Speter      if (modified_range->end < next_range->start)
801251881Speter        break;
802251881Speter
803251881Speter      /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
804251881Speter      if (modified_range->end == next_range->start)
805251881Speter        {
806251881Speter          if (modified_range->inheritable == next_range->inheritable)
807251881Speter            {
808251881Speter              /* Combine adjoining ranges with the same inheritability. */
809251881Speter              modified_range->end = next_range->end;
810251881Speter              elements_to_delete++;
811251881Speter            }
812251881Speter          else
813251881Speter            {
814251881Speter              /* Cannot join because inheritance differs. */
815251881Speter              (*range_index)++;
816251881Speter            }
817251881Speter          break;
818251881Speter        }
819251881Speter
820251881Speter      /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
821251881Speter      if (modified_range->end > next_range->end)
822251881Speter        {
823251881Speter          /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
824251881Speter             don't share the same end range. */
825251881Speter          if (modified_range->inheritable
826251881Speter              || (modified_range->inheritable == next_range->inheritable))
827251881Speter            {
828251881Speter              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
829251881Speter              elements_to_delete++;
830251881Speter            }
831251881Speter          else
832251881Speter            {
833251881Speter              /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
834251881Speter                 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
835251881Speter                 inheritable.  This means MODIFIED_RANGE is truncated,
836251881Speter                 NEXT_RANGE remains, and the portion of MODIFIED_RANGE
837251881Speter                 younger than NEXT_RANGE is added as a separate range:
838251881Speter                  ______________________________________________
839251881Speter                 |                                              |
840251881Speter                 M                 MODIFIED_RANGE               N
841251881Speter                 |                 (!inhertiable)               |
842251881Speter                 |______________________________________________|
843251881Speter                                  |              |
844251881Speter                                  O  NEXT_RANGE  P
845251881Speter                                  | (inheritable)|
846251881Speter                                  |______________|
847251881Speter                                         |
848251881Speter                                         V
849251881Speter                  _______________________________________________
850251881Speter                 |                |              |               |
851251881Speter                 M MODIFIED_RANGE O  NEXT_RANGE  P   NEW_RANGE   N
852251881Speter                 | (!inhertiable) | (inheritable)| (!inheritable)|
853251881Speter                 |________________|______________|_______________|
854251881Speter              */
855251881Speter              svn_merge_range_t *new_modified_range =
856251881Speter                apr_palloc(result_pool, sizeof(*new_modified_range));
857251881Speter              new_modified_range->start = next_range->end;
858251881Speter              new_modified_range->end = modified_range->end;
859251881Speter              new_modified_range->inheritable = FALSE;
860251881Speter              modified_range->end = next_range->start;
861251881Speter              (*range_index)+=2;
862251881Speter              svn_sort__array_insert(&new_modified_range, rangelist,
863251881Speter                                     *range_index);
864251881Speter              /* Recurse with the new range. */
865251881Speter              adjust_remaining_ranges(rangelist, range_index, result_pool);
866251881Speter              break;
867251881Speter            }
868251881Speter        }
869251881Speter      else if (modified_range->end == next_range->end)
870251881Speter        {
871251881Speter          /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
872251881Speter             the same end range. */
873251881Speter          if (modified_range->inheritable
874251881Speter              || (modified_range->inheritable == next_range->inheritable))
875251881Speter            {
876251881Speter              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
877251881Speter              elements_to_delete++;
878251881Speter            }
879251881Speter          else
880251881Speter            {
881251881Speter              /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
882251881Speter                 absorbed by the latter. */
883251881Speter              modified_range->end = next_range->start;
884251881Speter              (*range_index)++;
885251881Speter            }
886251881Speter          break;
887251881Speter        }
888251881Speter      else
889251881Speter        {
890251881Speter          /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
891251881Speter             a proper subset of MODIFIED_RANGE, nor do the two share the
892251881Speter             same end revision, i.e. they overlap. */
893251881Speter          if (modified_range->inheritable == next_range->inheritable)
894251881Speter            {
895251881Speter              /* Combine overlapping ranges with the same inheritability. */
896251881Speter              modified_range->end = next_range->end;
897251881Speter              elements_to_delete++;
898251881Speter            }
899251881Speter          else if (modified_range->inheritable)
900251881Speter            {
901251881Speter              /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
902251881Speter                 and NEXT_RANGE is truncated. */
903251881Speter              next_range->start = modified_range->end;
904251881Speter              (*range_index)++;
905251881Speter            }
906251881Speter          else
907251881Speter            {
908251881Speter              /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
909251881Speter                 and MODIFIED_RANGE is truncated. */
910251881Speter              modified_range->end = next_range->start;
911251881Speter              (*range_index)++;
912251881Speter            }
913251881Speter          break;
914251881Speter        }
915251881Speter    }
916251881Speter
917251881Speter  if (elements_to_delete)
918251881Speter    svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
919251881Speter}
920251881Speter
921251881Spetersvn_error_t *
922251881Spetersvn_rangelist_merge2(svn_rangelist_t *rangelist,
923251881Speter                     const svn_rangelist_t *changes,
924251881Speter                     apr_pool_t *result_pool,
925251881Speter                     apr_pool_t *scratch_pool)
926251881Speter{
927251881Speter  int i = 0;
928251881Speter  int j = 0;
929251881Speter
930251881Speter  /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
931251881Speter  changes = svn_rangelist_dup(changes, scratch_pool);
932251881Speter
933251881Speter  while (i < rangelist->nelts && j < changes->nelts)
934251881Speter    {
935251881Speter      svn_merge_range_t *range =
936251881Speter        APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
937251881Speter      svn_merge_range_t *change =
938251881Speter        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
939251881Speter      int res = svn_sort_compare_ranges(&range, &change);
940251881Speter
941251881Speter      if (res == 0)
942251881Speter        {
943251881Speter          /* Only when merging two non-inheritable ranges is the result also
944251881Speter             non-inheritable.  In all other cases ensure an inheritiable
945251881Speter             result. */
946251881Speter          if (range->inheritable || change->inheritable)
947251881Speter            range->inheritable = TRUE;
948251881Speter          i++;
949251881Speter          j++;
950251881Speter        }
951251881Speter      else if (res < 0) /* CHANGE is younger than RANGE */
952251881Speter        {
953251881Speter          if (range->end < change->start)
954251881Speter            {
955251881Speter              /* RANGE is older than CHANGE and the two do not
956251881Speter                 adjoin or overlap */
957251881Speter              i++;
958251881Speter            }
959251881Speter          else if (range->end == change->start)
960251881Speter            {
961251881Speter              /* RANGE and CHANGE adjoin */
962251881Speter              if (range->inheritable == change->inheritable)
963251881Speter                {
964251881Speter                  /* RANGE and CHANGE have the same inheritability so
965251881Speter                     RANGE expands to absord CHANGE. */
966251881Speter                  range->end = change->end;
967251881Speter                  adjust_remaining_ranges(rangelist, &i, result_pool);
968251881Speter                  j++;
969251881Speter                }
970251881Speter              else
971251881Speter                {
972251881Speter                  /* RANGE and CHANGE adjoin, but have different
973251881Speter                     inheritability.  Since RANGE is older, just
974251881Speter                     move on to the next RANGE. */
975251881Speter                  i++;
976251881Speter                }
977251881Speter            }
978251881Speter          else
979251881Speter            {
980251881Speter              /* RANGE and CHANGE overlap, but how? */
981251881Speter              if ((range->inheritable == change->inheritable)
982251881Speter                  || range->inheritable)
983251881Speter                {
984251881Speter                  /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
985251881Speter                      with no adjustment otherwise only the intersection is
986251881Speter                      absorbed and CHANGE is truncated. */
987251881Speter                  if (range->end >= change->end)
988251881Speter                    j++;
989251881Speter                  else
990251881Speter                    change->start = range->end;
991251881Speter                }
992251881Speter              else
993251881Speter                {
994251881Speter                  /* RANGE is non-inheritable and CHANGE is inheritable. */
995251881Speter                  if (range->start < change->start)
996251881Speter                    {
997251881Speter                      /* CHANGE absorbs intersection with RANGE and RANGE
998251881Speter                         is truncated. */
999251881Speter                      svn_merge_range_t *range_copy =
1000251881Speter                        svn_merge_range_dup(range, result_pool);
1001251881Speter                      range_copy->end = change->start;
1002251881Speter                      range->start = change->start;
1003251881Speter                      svn_sort__array_insert(&range_copy, rangelist, i++);
1004251881Speter                    }
1005251881Speter                  else
1006251881Speter                    {
1007251881Speter                      /* CHANGE and RANGE share the same start rev, but
1008251881Speter                         RANGE is considered older because its end rev
1009251881Speter                         is older. */
1010251881Speter                      range->inheritable = TRUE;
1011251881Speter                      change->start = range->end;
1012251881Speter                    }
1013251881Speter                }
1014251881Speter            }
1015251881Speter        }
1016251881Speter      else /* res > 0, CHANGE is older than RANGE */
1017251881Speter        {
1018251881Speter          if (change->end < range->start)
1019251881Speter            {
1020251881Speter              /* CHANGE is older than RANGE and the two do not
1021251881Speter                 adjoin or overlap, so insert a copy of CHANGE
1022251881Speter                 into RANGELIST. */
1023251881Speter              svn_merge_range_t *change_copy =
1024251881Speter                svn_merge_range_dup(change, result_pool);
1025251881Speter              svn_sort__array_insert(&change_copy, rangelist, i++);
1026251881Speter              j++;
1027251881Speter            }
1028251881Speter          else if (change->end == range->start)
1029251881Speter            {
1030251881Speter              /* RANGE and CHANGE adjoin */
1031251881Speter              if (range->inheritable == change->inheritable)
1032251881Speter                {
1033251881Speter                  /* RANGE and CHANGE have the same inheritability so we
1034251881Speter                     can simply combine the two in place. */
1035251881Speter                  range->start = change->start;
1036251881Speter                  j++;
1037251881Speter                }
1038251881Speter              else
1039251881Speter                {
1040251881Speter                  /* RANGE and CHANGE have different inheritability so insert
1041251881Speter                     a copy of CHANGE into RANGELIST. */
1042251881Speter                  svn_merge_range_t *change_copy =
1043251881Speter                    svn_merge_range_dup(change, result_pool);
1044251881Speter                  svn_sort__array_insert(&change_copy, rangelist, i);
1045251881Speter                  j++;
1046251881Speter                }
1047251881Speter            }
1048251881Speter          else
1049251881Speter            {
1050251881Speter              /* RANGE and CHANGE overlap. */
1051251881Speter              if (range->inheritable == change->inheritable)
1052251881Speter                {
1053251881Speter                  /* RANGE and CHANGE have the same inheritability so we
1054251881Speter                     can simply combine the two in place... */
1055251881Speter                  range->start = change->start;
1056251881Speter                  if (range->end < change->end)
1057251881Speter                    {
1058251881Speter                      /* ...but if RANGE is expanded ensure that we don't
1059251881Speter                         violate any rangelist invariants. */
1060251881Speter                      range->end = change->end;
1061251881Speter                      adjust_remaining_ranges(rangelist, &i, result_pool);
1062251881Speter                    }
1063251881Speter                  j++;
1064251881Speter                }
1065251881Speter              else if (range->inheritable)
1066251881Speter                {
1067251881Speter                  if (change->start < range->start)
1068251881Speter                    {
1069251881Speter                      /* RANGE is inheritable so absorbs any part of CHANGE
1070251881Speter                         it overlaps.  CHANGE is truncated and the remainder
1071251881Speter                         inserted into RANGELIST. */
1072251881Speter                      svn_merge_range_t *change_copy =
1073251881Speter                        svn_merge_range_dup(change, result_pool);
1074251881Speter                      change_copy->end = range->start;
1075251881Speter                      change->start = range->start;
1076251881Speter                      svn_sort__array_insert(&change_copy, rangelist, i++);
1077251881Speter                    }
1078251881Speter                  else
1079251881Speter                    {
1080251881Speter                      /* CHANGE and RANGE share the same start rev, but
1081251881Speter                         CHANGE is considered older because CHANGE->END is
1082251881Speter                         older than RANGE->END. */
1083251881Speter                      j++;
1084251881Speter                    }
1085251881Speter                }
1086251881Speter              else
1087251881Speter                {
1088251881Speter                  /* RANGE is non-inheritable and CHANGE is inheritable. */
1089251881Speter                  if (change->start < range->start)
1090251881Speter                    {
1091251881Speter                      if (change->end == range->end)
1092251881Speter                        {
1093251881Speter                          /* RANGE is a proper subset of CHANGE and share the
1094251881Speter                             same end revision, so set RANGE equal to CHANGE. */
1095251881Speter                          range->start = change->start;
1096251881Speter                          range->inheritable = TRUE;
1097251881Speter                          j++;
1098251881Speter                        }
1099251881Speter                      else if (change->end > range->end)
1100251881Speter                        {
1101251881Speter                          /* RANGE is a proper subset of CHANGE and CHANGE has
1102251881Speter                             a younger end revision, so set RANGE equal to its
1103251881Speter                             intersection with CHANGE and truncate CHANGE. */
1104251881Speter                          range->start = change->start;
1105251881Speter                          range->inheritable = TRUE;
1106251881Speter                          change->start = range->end;
1107251881Speter                        }
1108251881Speter                      else
1109251881Speter                        {
1110251881Speter                          /* CHANGE and RANGE overlap. Set RANGE equal to its
1111251881Speter                             intersection with CHANGE and take the remainder
1112251881Speter                             of RANGE and insert it into RANGELIST. */
1113251881Speter                          svn_merge_range_t *range_copy =
1114251881Speter                            svn_merge_range_dup(range, result_pool);
1115251881Speter                          range_copy->start = change->end;
1116251881Speter                          range->start = change->start;
1117251881Speter                          range->end = change->end;
1118251881Speter                          range->inheritable = TRUE;
1119251881Speter                          svn_sort__array_insert(&range_copy, rangelist, ++i);
1120251881Speter                          j++;
1121251881Speter                        }
1122251881Speter                    }
1123251881Speter                  else
1124251881Speter                    {
1125251881Speter                      /* CHANGE and RANGE share the same start rev, but
1126251881Speter                         CHANGE is considered older because its end rev
1127251881Speter                         is older.
1128251881Speter
1129251881Speter                         Insert the intersection of RANGE and CHANGE into
1130251881Speter                         RANGELIST and then set RANGE to the non-intersecting
1131251881Speter                         portion of RANGE. */
1132251881Speter                      svn_merge_range_t *range_copy =
1133251881Speter                        svn_merge_range_dup(range, result_pool);
1134251881Speter                      range_copy->end = change->end;
1135251881Speter                      range_copy->inheritable = TRUE;
1136251881Speter                      range->start = change->end;
1137251881Speter                      svn_sort__array_insert(&range_copy, rangelist, i++);
1138251881Speter                      j++;
1139251881Speter                    }
1140251881Speter                }
1141251881Speter            }
1142251881Speter        }
1143251881Speter    }
1144251881Speter
1145251881Speter  /* Copy any remaining elements in CHANGES into RANGELIST. */
1146251881Speter  for (; j < (changes)->nelts; j++)
1147251881Speter    {
1148251881Speter      svn_merge_range_t *change =
1149251881Speter        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
1150251881Speter      svn_merge_range_t *change_copy = svn_merge_range_dup(change,
1151251881Speter                                                           result_pool);
1152251881Speter      svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
1153251881Speter    }
1154251881Speter
1155251881Speter  return SVN_NO_ERROR;
1156251881Speter}
1157251881Speter
1158251881Speter/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
1159251881Speter * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
1160251881Speterstatic svn_boolean_t
1161251881Speterrange_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
1162251881Speter                svn_boolean_t consider_inheritance)
1163251881Speter{
1164251881Speter  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1165251881Speter  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1166251881Speter
1167251881Speter  return (first->start + 1 <= second->end)
1168251881Speter    && (second->start + 1 <= first->end)
1169251881Speter    && (!consider_inheritance
1170251881Speter        || (!(first->inheritable) == !(second->inheritable)));
1171251881Speter}
1172251881Speter
1173251881Speter/* Return TRUE iff the forward revision range FIRST wholly contains the
1174251881Speter * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
1175251881Speter * the same inheritability. */
1176251881Speterstatic svn_boolean_t
1177251881Speterrange_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
1178251881Speter               svn_boolean_t consider_inheritance)
1179251881Speter{
1180251881Speter  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1181251881Speter  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1182251881Speter
1183251881Speter  return (first->start <= second->start) && (second->end <= first->end)
1184251881Speter    && (!consider_inheritance
1185251881Speter        || (!(first->inheritable) == !(second->inheritable)));
1186251881Speter}
1187251881Speter
1188251881Speter/* Swap start and end fields of RANGE. */
1189251881Speterstatic void
1190251881Speterrange_swap_endpoints(svn_merge_range_t *range)
1191251881Speter{
1192251881Speter  svn_revnum_t swap = range->start;
1193251881Speter  range->start = range->end;
1194251881Speter  range->end = swap;
1195251881Speter}
1196251881Speter
1197251881Spetersvn_error_t *
1198251881Spetersvn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool)
1199251881Speter{
1200251881Speter  int i;
1201251881Speter
1202251881Speter  svn_sort__array_reverse(rangelist, pool);
1203251881Speter
1204251881Speter  for (i = 0; i < rangelist->nelts; i++)
1205251881Speter    {
1206251881Speter      range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
1207251881Speter    }
1208251881Speter
1209251881Speter  return SVN_NO_ERROR;
1210251881Speter}
1211251881Speter
1212251881Spetervoid
1213251881Spetersvn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
1214251881Speter                               svn_boolean_t inheritable)
1215251881Speter{
1216251881Speter  if (rangelist)
1217251881Speter    {
1218251881Speter      int i;
1219251881Speter      svn_merge_range_t *range;
1220251881Speter
1221251881Speter      for (i = 0; i < rangelist->nelts; i++)
1222251881Speter        {
1223251881Speter          range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1224251881Speter          range->inheritable = inheritable;
1225251881Speter        }
1226251881Speter    }
1227251881Speter  return;
1228251881Speter}
1229251881Speter
1230251881Spetervoid
1231251881Spetersvn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
1232251881Speter                               svn_boolean_t inheritable,
1233251881Speter                               apr_pool_t *scratch_pool)
1234251881Speter{
1235251881Speter  if (mergeinfo)
1236251881Speter    {
1237251881Speter      apr_hash_index_t *hi;
1238251881Speter
1239251881Speter      for (hi = apr_hash_first(scratch_pool, mergeinfo);
1240251881Speter           hi;
1241251881Speter           hi = apr_hash_next(hi))
1242251881Speter        {
1243251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
1244251881Speter
1245251881Speter          if (rangelist)
1246251881Speter            svn_rangelist__set_inheritance(rangelist, inheritable);
1247251881Speter        }
1248251881Speter    }
1249251881Speter  return;
1250251881Speter}
1251251881Speter
1252251881Speter/* If DO_REMOVE is true, then remove any overlapping ranges described by
1253251881Speter   RANGELIST1 from RANGELIST2 and place the results in *OUTPUT.  When
1254251881Speter   DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
1255251881Speter   the "whiteboard".
1256251881Speter
1257251881Speter   If DO_REMOVE is false, then capture the intersection between RANGELIST1
1258251881Speter   and RANGELIST2 and place the results in *OUTPUT.  The ordering of
1259251881Speter   RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
1260251881Speter
1261251881Speter   If CONSIDER_INHERITANCE is true, then take the inheritance of the
1262251881Speter   ranges in RANGELIST1 and RANGELIST2 into account when comparing them
1263251881Speter   for intersection, see the doc string for svn_rangelist_intersect().
1264251881Speter
1265251881Speter   If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
1266251881Speter   may intersect, but the resulting intersection is non-inheritable only
1267251881Speter   if both ranges were non-inheritable, e.g.:
1268251881Speter
1269251881Speter   RANGELIST1  RANGELIST2  CONSIDER     DO_REMOVE  *OUTPUT
1270251881Speter                           INHERITANCE
1271251881Speter   ----------  ------      -----------  ---------  -------
1272251881Speter
1273251881Speter   90-420*     1-100       TRUE         FALSE      Empty Rangelist
1274251881Speter   90-420      1-100*      TRUE         FALSE      Empty Rangelist
1275251881Speter   90-420      1-100       TRUE         FALSE      90-100
1276251881Speter   90-420*     1-100*      TRUE         FALSE      90-100*
1277251881Speter
1278251881Speter   90-420*     1-100       FALSE        FALSE      90-100
1279251881Speter   90-420      1-100*      FALSE        FALSE      90-100
1280251881Speter   90-420      1-100       FALSE        FALSE      90-100
1281251881Speter   90-420*     1-100*      FALSE        FALSE      90-100*
1282251881Speter
1283251881Speter   Allocate the contents of *OUTPUT in POOL. */
1284251881Speterstatic svn_error_t *
1285251881Speterrangelist_intersect_or_remove(svn_rangelist_t **output,
1286251881Speter                              const svn_rangelist_t *rangelist1,
1287251881Speter                              const svn_rangelist_t *rangelist2,
1288251881Speter                              svn_boolean_t do_remove,
1289251881Speter                              svn_boolean_t consider_inheritance,
1290251881Speter                              apr_pool_t *pool)
1291251881Speter{
1292251881Speter  int i1, i2, lasti2;
1293251881Speter  svn_merge_range_t working_elt2;
1294251881Speter
1295251881Speter  *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1296251881Speter
1297251881Speter  i1 = 0;
1298251881Speter  i2 = 0;
1299251881Speter  lasti2 = -1;  /* Initialized to a value that "i2" will never be. */
1300251881Speter
1301251881Speter  while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
1302251881Speter    {
1303251881Speter      svn_merge_range_t *elt1, *elt2;
1304251881Speter
1305251881Speter      elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
1306251881Speter
1307251881Speter      /* Instead of making a copy of the entire array of rangelist2
1308251881Speter         elements, we just keep a copy of the current rangelist2 element
1309251881Speter         that needs to be used, and modify our copy if necessary. */
1310251881Speter      if (i2 != lasti2)
1311251881Speter        {
1312251881Speter          working_elt2 =
1313251881Speter            *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
1314251881Speter          lasti2 = i2;
1315251881Speter        }
1316251881Speter
1317251881Speter      elt2 = &working_elt2;
1318251881Speter
1319251881Speter      /* If the rangelist2 range is contained completely in the
1320251881Speter         rangelist1, we increment the rangelist2.
1321251881Speter         If the ranges intersect, and match exactly, we increment both
1322251881Speter         rangelist1 and rangelist2.
1323251881Speter         Otherwise, we have to generate a range for the left part of
1324251881Speter         the removal of rangelist1 from rangelist2, and possibly change
1325251881Speter         the rangelist2 to the remaining portion of the right part of
1326251881Speter         the removal, to test against. */
1327251881Speter      if (range_contains(elt1, elt2, consider_inheritance))
1328251881Speter        {
1329251881Speter          if (!do_remove)
1330251881Speter            {
1331251881Speter              svn_merge_range_t tmp_range;
1332251881Speter              tmp_range.start = elt2->start;
1333251881Speter              tmp_range.end = elt2->end;
1334251881Speter              /* The intersection of two ranges is non-inheritable only
1335251881Speter                 if both ranges are non-inheritable. */
1336251881Speter              tmp_range.inheritable =
1337251881Speter                (elt2->inheritable || elt1->inheritable);
1338251881Speter              SVN_ERR(combine_with_lastrange(&tmp_range, *output,
1339251881Speter                                             consider_inheritance,
1340251881Speter                                             pool));
1341251881Speter            }
1342251881Speter
1343251881Speter          i2++;
1344251881Speter
1345251881Speter          if (elt2->start == elt1->start && elt2->end == elt1->end)
1346251881Speter            i1++;
1347251881Speter        }
1348251881Speter      else if (range_intersect(elt1, elt2, consider_inheritance))
1349251881Speter        {
1350251881Speter          if (elt2->start < elt1->start)
1351251881Speter            {
1352251881Speter              /* The rangelist2 range starts before the rangelist1 range. */
1353251881Speter              svn_merge_range_t tmp_range;
1354251881Speter              if (do_remove)
1355251881Speter                {
1356251881Speter                  /* Retain the range that falls before the rangelist1
1357251881Speter                     start. */
1358251881Speter                  tmp_range.start = elt2->start;
1359251881Speter                  tmp_range.end = elt1->start;
1360251881Speter                  tmp_range.inheritable = elt2->inheritable;
1361251881Speter                }
1362251881Speter              else
1363251881Speter                {
1364251881Speter                  /* Retain the range that falls between the rangelist1
1365251881Speter                     start and rangelist2 end. */
1366251881Speter                  tmp_range.start = elt1->start;
1367251881Speter                  tmp_range.end = MIN(elt2->end, elt1->end);
1368251881Speter                  /* The intersection of two ranges is non-inheritable only
1369251881Speter                     if both ranges are non-inheritable. */
1370251881Speter                  tmp_range.inheritable =
1371251881Speter                    (elt2->inheritable || elt1->inheritable);
1372251881Speter                }
1373251881Speter
1374251881Speter              SVN_ERR(combine_with_lastrange(&tmp_range,
1375251881Speter                                             *output, consider_inheritance,
1376251881Speter                                             pool));
1377251881Speter            }
1378251881Speter
1379251881Speter          /* Set up the rest of the rangelist2 range for further
1380251881Speter             processing.  */
1381251881Speter          if (elt2->end > elt1->end)
1382251881Speter            {
1383251881Speter              /* The rangelist2 range ends after the rangelist1 range. */
1384251881Speter              if (!do_remove)
1385251881Speter                {
1386251881Speter                  /* Partial overlap. */
1387251881Speter                  svn_merge_range_t tmp_range;
1388251881Speter                  tmp_range.start = MAX(elt2->start, elt1->start);
1389251881Speter                  tmp_range.end = elt1->end;
1390251881Speter                  /* The intersection of two ranges is non-inheritable only
1391251881Speter                     if both ranges are non-inheritable. */
1392251881Speter                  tmp_range.inheritable =
1393251881Speter                    (elt2->inheritable || elt1->inheritable);
1394251881Speter                  SVN_ERR(combine_with_lastrange(&tmp_range,
1395251881Speter                                                 *output,
1396251881Speter                                                 consider_inheritance,
1397251881Speter                                                 pool));
1398251881Speter                }
1399251881Speter
1400251881Speter              working_elt2.start = elt1->end;
1401251881Speter              working_elt2.end = elt2->end;
1402251881Speter            }
1403251881Speter          else
1404251881Speter            i2++;
1405251881Speter        }
1406251881Speter      else  /* ranges don't intersect */
1407251881Speter        {
1408251881Speter          /* See which side of the rangelist2 the rangelist1 is on.  If it
1409251881Speter             is on the left side, we need to move the rangelist1.
1410251881Speter
1411251881Speter             If it is on past the rangelist2 on the right side, we
1412251881Speter             need to output the rangelist2 and increment the
1413251881Speter             rangelist2.  */
1414251881Speter          if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
1415251881Speter            i1++;
1416251881Speter          else
1417251881Speter            {
1418251881Speter              svn_merge_range_t *lastrange;
1419251881Speter
1420251881Speter              if ((*output)->nelts > 0)
1421251881Speter                lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1,
1422251881Speter                                          svn_merge_range_t *);
1423251881Speter              else
1424251881Speter                lastrange = NULL;
1425251881Speter
1426251881Speter              if (do_remove && !(lastrange &&
1427251881Speter                                 combine_ranges(lastrange, lastrange, elt2,
1428251881Speter                                                consider_inheritance)))
1429251881Speter                {
1430251881Speter                  lastrange = svn_merge_range_dup(elt2, pool);
1431251881Speter                  APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
1432251881Speter                }
1433251881Speter              i2++;
1434251881Speter            }
1435251881Speter        }
1436251881Speter    }
1437251881Speter
1438251881Speter  if (do_remove)
1439251881Speter    {
1440251881Speter      /* Copy the current rangelist2 element if we didn't hit the end
1441251881Speter         of the rangelist2, and we still had it around.  This element
1442251881Speter         may have been touched, so we can't just walk the rangelist2
1443251881Speter         array, we have to use our copy.  This case only happens when
1444251881Speter         we ran out of rangelist1 before rangelist2, *and* we had changed
1445251881Speter         the rangelist2 element. */
1446251881Speter      if (i2 == lasti2 && i2 < rangelist2->nelts)
1447251881Speter        {
1448251881Speter          SVN_ERR(combine_with_lastrange(&working_elt2, *output,
1449251881Speter                                         consider_inheritance, pool));
1450251881Speter          i2++;
1451251881Speter        }
1452251881Speter
1453251881Speter      /* Copy any other remaining untouched rangelist2 elements.  */
1454251881Speter      for (; i2 < rangelist2->nelts; i2++)
1455251881Speter        {
1456251881Speter          svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
1457251881Speter                                                 svn_merge_range_t *);
1458251881Speter
1459251881Speter          SVN_ERR(combine_with_lastrange(elt, *output,
1460251881Speter                                         consider_inheritance, pool));
1461251881Speter        }
1462251881Speter    }
1463251881Speter
1464251881Speter  return SVN_NO_ERROR;
1465251881Speter}
1466251881Speter
1467251881Speter
1468251881Spetersvn_error_t *
1469251881Spetersvn_rangelist_intersect(svn_rangelist_t **output,
1470251881Speter                        const svn_rangelist_t *rangelist1,
1471251881Speter                        const svn_rangelist_t *rangelist2,
1472251881Speter                        svn_boolean_t consider_inheritance,
1473251881Speter                        apr_pool_t *pool)
1474251881Speter{
1475251881Speter  return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
1476251881Speter                                       consider_inheritance, pool);
1477251881Speter}
1478251881Speter
1479251881Spetersvn_error_t *
1480251881Spetersvn_rangelist_remove(svn_rangelist_t **output,
1481251881Speter                     const svn_rangelist_t *eraser,
1482251881Speter                     const svn_rangelist_t *whiteboard,
1483251881Speter                     svn_boolean_t consider_inheritance,
1484251881Speter                     apr_pool_t *pool)
1485251881Speter{
1486251881Speter  return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
1487251881Speter                                       consider_inheritance, pool);
1488251881Speter}
1489251881Speter
1490251881Spetersvn_error_t *
1491251881Spetersvn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added,
1492251881Speter                   const svn_rangelist_t *from, const svn_rangelist_t *to,
1493251881Speter                   svn_boolean_t consider_inheritance,
1494251881Speter                   apr_pool_t *pool)
1495251881Speter{
1496251881Speter  /* The following diagrams illustrate some common range delta scenarios:
1497251881Speter
1498251881Speter     (from)           deleted
1499251881Speter     r0 <===========(=========)============[=========]===========> rHEAD
1500251881Speter     [to]                                    added
1501251881Speter
1502251881Speter     (from)           deleted                deleted
1503251881Speter     r0 <===========(=========[============]=========)===========> rHEAD
1504251881Speter     [to]
1505251881Speter
1506251881Speter     (from)           deleted
1507251881Speter     r0 <===========(=========[============)=========]===========> rHEAD
1508251881Speter     [to]                                    added
1509251881Speter
1510251881Speter     (from)                                  deleted
1511251881Speter     r0 <===========[=========(============]=========)===========> rHEAD
1512251881Speter     [to]             added
1513251881Speter
1514251881Speter     (from)
1515251881Speter     r0 <===========[=========(============)=========]===========> rHEAD
1516251881Speter     [to]             added                  added
1517251881Speter
1518251881Speter     (from)  d                                  d             d
1519251881Speter     r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
1520251881Speter     [to]        a   a    a   a   a   a                   a
1521251881Speter  */
1522251881Speter
1523251881Speter  /* The items that are present in from, but not in to, must have been
1524251881Speter     deleted. */
1525251881Speter  SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance,
1526251881Speter                               pool));
1527251881Speter  /* The items that are present in to, but not in from, must have been
1528251881Speter     added.  */
1529251881Speter  return svn_rangelist_remove(added, from, to, consider_inheritance, pool);
1530251881Speter}
1531251881Speter
1532251881Speterstruct mergeinfo_diff_baton
1533251881Speter{
1534251881Speter  svn_mergeinfo_t from;
1535251881Speter  svn_mergeinfo_t to;
1536251881Speter  svn_mergeinfo_t deleted;
1537251881Speter  svn_mergeinfo_t added;
1538251881Speter  svn_boolean_t consider_inheritance;
1539251881Speter  apr_pool_t *pool;
1540251881Speter};
1541251881Speter
1542251881Speter/* This implements the 'svn_hash_diff_func_t' interface.
1543251881Speter   BATON is of type 'struct mergeinfo_diff_baton *'.
1544251881Speter*/
1545251881Speterstatic svn_error_t *
1546251881Spetermergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen,
1547251881Speter                       enum svn_hash_diff_key_status status,
1548251881Speter                       void *baton)
1549251881Speter{
1550251881Speter  /* hash_a is FROM mergeinfo,
1551251881Speter     hash_b is TO mergeinfo. */
1552251881Speter  struct mergeinfo_diff_baton *cb = baton;
1553251881Speter  svn_rangelist_t *from_rangelist, *to_rangelist;
1554251881Speter  const char *path = key;
1555251881Speter  if (status == svn_hash_diff_key_both)
1556251881Speter    {
1557251881Speter      /* Record any deltas (additions or deletions). */
1558251881Speter      svn_rangelist_t *deleted_rangelist, *added_rangelist;
1559251881Speter      from_rangelist = apr_hash_get(cb->from, path, klen);
1560251881Speter      to_rangelist = apr_hash_get(cb->to, path, klen);
1561251881Speter      SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
1562251881Speter                                 from_rangelist, to_rangelist,
1563251881Speter                                 cb->consider_inheritance, cb->pool));
1564251881Speter      if (cb->deleted && deleted_rangelist->nelts > 0)
1565251881Speter        apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
1566251881Speter                     klen, deleted_rangelist);
1567251881Speter      if (cb->added && added_rangelist->nelts > 0)
1568251881Speter        apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
1569251881Speter                     klen, added_rangelist);
1570251881Speter    }
1571251881Speter  else if ((status == svn_hash_diff_key_a) && cb->deleted)
1572251881Speter    {
1573251881Speter      from_rangelist = apr_hash_get(cb->from, path, klen);
1574251881Speter      apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
1575251881Speter                   svn_rangelist_dup(from_rangelist, cb->pool));
1576251881Speter    }
1577251881Speter  else if ((status == svn_hash_diff_key_b) && cb->added)
1578251881Speter    {
1579251881Speter      to_rangelist = apr_hash_get(cb->to, path, klen);
1580251881Speter      apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
1581251881Speter                   svn_rangelist_dup(to_rangelist, cb->pool));
1582251881Speter    }
1583251881Speter  return SVN_NO_ERROR;
1584251881Speter}
1585251881Speter
1586251881Speter/* Record deletions and additions of entire range lists (by path
1587251881Speter   presence), and delegate to svn_rangelist_diff() for delta
1588251881Speter   calculations on a specific path.  */
1589251881Speterstatic svn_error_t *
1590251881Speterwalk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to,
1591251881Speter                             svn_mergeinfo_t deleted, svn_mergeinfo_t added,
1592251881Speter                             svn_boolean_t consider_inheritance,
1593251881Speter                             apr_pool_t *result_pool,
1594251881Speter                             apr_pool_t *scratch_pool)
1595251881Speter{
1596251881Speter  struct mergeinfo_diff_baton mdb;
1597251881Speter  mdb.from = from;
1598251881Speter  mdb.to = to;
1599251881Speter  mdb.deleted = deleted;
1600251881Speter  mdb.added = added;
1601251881Speter  mdb.consider_inheritance = consider_inheritance;
1602251881Speter  mdb.pool = result_pool;
1603251881Speter
1604251881Speter  return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool);
1605251881Speter}
1606251881Speter
1607251881Spetersvn_error_t *
1608251881Spetersvn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
1609251881Speter                    svn_mergeinfo_t from, svn_mergeinfo_t to,
1610251881Speter                    svn_boolean_t consider_inheritance,
1611251881Speter                    apr_pool_t *result_pool,
1612251881Speter                    apr_pool_t *scratch_pool)
1613251881Speter{
1614251881Speter  if (from && to == NULL)
1615251881Speter    {
1616251881Speter      *deleted = svn_mergeinfo_dup(from, result_pool);
1617251881Speter      *added = svn_hash__make(result_pool);
1618251881Speter    }
1619251881Speter  else if (from == NULL && to)
1620251881Speter    {
1621251881Speter      *deleted = svn_hash__make(result_pool);
1622251881Speter      *added = svn_mergeinfo_dup(to, result_pool);
1623251881Speter    }
1624251881Speter  else
1625251881Speter    {
1626251881Speter      *deleted = svn_hash__make(result_pool);
1627251881Speter      *added = svn_hash__make(result_pool);
1628251881Speter
1629251881Speter      if (from && to)
1630251881Speter        {
1631251881Speter          SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
1632251881Speter                                               consider_inheritance,
1633251881Speter                                               result_pool, scratch_pool));
1634251881Speter        }
1635251881Speter    }
1636251881Speter
1637251881Speter  return SVN_NO_ERROR;
1638251881Speter}
1639251881Speter
1640251881Spetersvn_error_t *
1641251881Spetersvn_mergeinfo__equals(svn_boolean_t *is_equal,
1642251881Speter                      svn_mergeinfo_t info1,
1643251881Speter                      svn_mergeinfo_t info2,
1644251881Speter                      svn_boolean_t consider_inheritance,
1645251881Speter                      apr_pool_t *pool)
1646251881Speter{
1647251881Speter  apr_hash_index_t *hi;
1648251881Speter
1649251881Speter  *is_equal = FALSE;
1650251881Speter
1651251881Speter  /* special cases: at least one side has no merge info */
1652251881Speter  if (info1 == NULL && info2 == NULL)
1653251881Speter    {
1654251881Speter      *is_equal = TRUE;
1655251881Speter      return SVN_NO_ERROR;
1656251881Speter    }
1657251881Speter
1658251881Speter  if (info1 == NULL ||  info2 == NULL)
1659251881Speter    return SVN_NO_ERROR;
1660251881Speter
1661251881Speter  /* trivial case: different number of paths -> unequal */
1662251881Speter  if (apr_hash_count(info1) != apr_hash_count(info2))
1663251881Speter    return SVN_NO_ERROR;
1664251881Speter
1665251881Speter  /* compare range lists for all paths */
1666251881Speter  for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
1667251881Speter    {
1668251881Speter      const char *key;
1669251881Speter      apr_ssize_t key_length;
1670251881Speter      svn_rangelist_t *lhs, *rhs;
1671251881Speter      int i;
1672251881Speter      svn_rangelist_t *deleted, *added;
1673251881Speter
1674251881Speter      /* get both path lists */
1675251881Speter      apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
1676251881Speter      rhs = apr_hash_get(info2, key, key_length);
1677251881Speter
1678251881Speter      /* missing on one side? */
1679251881Speter      if (rhs == NULL)
1680251881Speter        return SVN_NO_ERROR;
1681251881Speter
1682251881Speter      /* quick compare: the range lists will often be a perfect match */
1683251881Speter      if (lhs->nelts == rhs->nelts)
1684251881Speter        {
1685251881Speter          for (i = 0; i < lhs->nelts; ++i)
1686251881Speter            {
1687251881Speter              svn_merge_range_t *lrange
1688251881Speter                = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
1689251881Speter              svn_merge_range_t *rrange
1690251881Speter                = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
1691251881Speter
1692251881Speter              /* range mismatch? -> needs detailed comparison */
1693251881Speter              if (   lrange->start != rrange->start
1694251881Speter                  || lrange->end != rrange->end)
1695251881Speter                break;
1696251881Speter
1697251881Speter              /* inheritance mismatch? -> merge info differs */
1698251881Speter              if (   consider_inheritance
1699251881Speter                  && lrange->inheritable != rrange->inheritable)
1700251881Speter                return SVN_NO_ERROR;
1701251881Speter            }
1702251881Speter
1703251881Speter          /* all ranges found to match -> next path */
1704251881Speter          if (i == lhs->nelts)
1705251881Speter            continue;
1706251881Speter        }
1707251881Speter
1708251881Speter      /* range lists differ but there are many ways to sort and aggregate
1709251881Speter         revisions into ranges. Do a full diff on them. */
1710251881Speter      SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
1711251881Speter                                  consider_inheritance, pool));
1712251881Speter      if (deleted->nelts || added->nelts)
1713251881Speter        return SVN_NO_ERROR;
1714251881Speter    }
1715251881Speter
1716251881Speter  /* no mismatch found */
1717251881Speter  *is_equal = TRUE;
1718251881Speter  return SVN_NO_ERROR;
1719251881Speter}
1720251881Speter
1721251881Spetersvn_error_t *
1722251881Spetersvn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
1723251881Speter                     svn_mergeinfo_t changes,
1724251881Speter                     apr_pool_t *result_pool,
1725251881Speter                     apr_pool_t *scratch_pool)
1726251881Speter{
1727251881Speter  apr_hash_index_t *hi;
1728251881Speter  apr_pool_t *iterpool;
1729251881Speter
1730251881Speter  if (!apr_hash_count(changes))
1731251881Speter    return SVN_NO_ERROR;
1732251881Speter
1733251881Speter  iterpool = svn_pool_create(scratch_pool);
1734251881Speter  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
1735251881Speter    {
1736251881Speter      const char *key;
1737251881Speter      apr_ssize_t klen;
1738251881Speter      svn_rangelist_t *to_insert;
1739251881Speter      svn_rangelist_t *target;
1740251881Speter
1741251881Speter      /* get ranges to insert and the target ranges list of that insertion */
1742251881Speter      apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
1743251881Speter      target = apr_hash_get(mergeinfo, key, klen);
1744251881Speter
1745251881Speter      /* if range list exists, just expand on it.
1746251881Speter       * Otherwise, add new hash entry. */
1747251881Speter      if (target)
1748251881Speter        {
1749251881Speter          SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
1750251881Speter                                       iterpool));
1751251881Speter          svn_pool_clear(iterpool);
1752251881Speter        }
1753251881Speter      else
1754251881Speter        apr_hash_set(mergeinfo, key, klen, to_insert);
1755251881Speter    }
1756251881Speter
1757251881Speter  svn_pool_destroy(iterpool);
1758251881Speter
1759251881Speter  return SVN_NO_ERROR;
1760251881Speter}
1761251881Speter
1762251881Spetersvn_error_t *
1763251881Spetersvn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat,
1764251881Speter                            svn_mergeinfo_catalog_t changes_cat,
1765251881Speter                            apr_pool_t *result_pool,
1766251881Speter                            apr_pool_t *scratch_pool)
1767251881Speter{
1768251881Speter  int i = 0;
1769251881Speter  int j = 0;
1770251881Speter  apr_array_header_t *sorted_cat =
1771251881Speter    svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths,
1772251881Speter                   scratch_pool);
1773251881Speter  apr_array_header_t *sorted_changes =
1774251881Speter    svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths,
1775251881Speter                   scratch_pool);
1776251881Speter
1777251881Speter  while (i < sorted_cat->nelts && j < sorted_changes->nelts)
1778251881Speter    {
1779251881Speter      svn_sort__item_t cat_elt, change_elt;
1780251881Speter      int res;
1781251881Speter
1782251881Speter      cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t);
1783251881Speter      change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t);
1784251881Speter      res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt);
1785251881Speter
1786251881Speter      if (res == 0) /* Both catalogs have mergeinfo for a given path. */
1787251881Speter        {
1788251881Speter          svn_mergeinfo_t mergeinfo = cat_elt.value;
1789251881Speter          svn_mergeinfo_t changes_mergeinfo = change_elt.value;
1790251881Speter
1791251881Speter          SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
1792251881Speter                                       result_pool, scratch_pool));
1793251881Speter          apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
1794251881Speter          i++;
1795251881Speter          j++;
1796251881Speter        }
1797251881Speter      else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
1798251881Speter        {
1799251881Speter          i++;
1800251881Speter        }
1801251881Speter      else /* Only CHANGES_CAT has mergeinfo for this path. */
1802251881Speter        {
1803251881Speter          apr_hash_set(mergeinfo_cat,
1804251881Speter                       apr_pstrdup(result_pool, change_elt.key),
1805251881Speter                       change_elt.klen,
1806251881Speter                       svn_mergeinfo_dup(change_elt.value, result_pool));
1807251881Speter          j++;
1808251881Speter        }
1809251881Speter    }
1810251881Speter
1811251881Speter  /* Copy back any remaining elements from the CHANGES_CAT catalog. */
1812251881Speter  for (; j < sorted_changes->nelts; j++)
1813251881Speter    {
1814251881Speter      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j,
1815251881Speter                                           svn_sort__item_t);
1816251881Speter      apr_hash_set(mergeinfo_cat,
1817251881Speter                   apr_pstrdup(result_pool, elt.key),
1818251881Speter                   elt.klen,
1819251881Speter                   svn_mergeinfo_dup(elt.value, result_pool));
1820251881Speter    }
1821251881Speter
1822251881Speter  return SVN_NO_ERROR;
1823251881Speter}
1824251881Speter
1825251881Spetersvn_error_t *
1826251881Spetersvn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
1827251881Speter                         svn_mergeinfo_t mergeinfo1,
1828251881Speter                         svn_mergeinfo_t mergeinfo2,
1829251881Speter                         svn_boolean_t consider_inheritance,
1830251881Speter                         apr_pool_t *result_pool,
1831251881Speter                         apr_pool_t *scratch_pool)
1832251881Speter{
1833251881Speter  apr_hash_index_t *hi;
1834251881Speter  apr_pool_t *iterpool;
1835251881Speter
1836251881Speter  *mergeinfo = apr_hash_make(result_pool);
1837251881Speter  iterpool = svn_pool_create(scratch_pool);
1838251881Speter
1839251881Speter  /* ### TODO(reint): Do we care about the case when a path in one
1840251881Speter     ### mergeinfo hash has inheritable mergeinfo, and in the other
1841251881Speter     ### has non-inhertiable mergeinfo?  It seems like that path
1842251881Speter     ### itself should really be an intersection, while child paths
1843251881Speter     ### should not be... */
1844251881Speter  for (hi = apr_hash_first(scratch_pool, mergeinfo1);
1845251881Speter       hi; hi = apr_hash_next(hi))
1846251881Speter    {
1847251881Speter      const char *path = svn__apr_hash_index_key(hi);
1848251881Speter      svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi);
1849251881Speter      svn_rangelist_t *rangelist2;
1850251881Speter
1851251881Speter      svn_pool_clear(iterpool);
1852251881Speter      rangelist2 = svn_hash_gets(mergeinfo2, path);
1853251881Speter      if (rangelist2)
1854251881Speter        {
1855251881Speter          SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
1856251881Speter                                          consider_inheritance, iterpool));
1857251881Speter          if (rangelist2->nelts > 0)
1858251881Speter            svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path),
1859251881Speter                          svn_rangelist_dup(rangelist2, result_pool));
1860251881Speter        }
1861251881Speter    }
1862251881Speter  svn_pool_destroy(iterpool);
1863251881Speter  return SVN_NO_ERROR;
1864251881Speter}
1865251881Speter
1866251881Spetersvn_error_t *
1867251881Spetersvn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
1868251881Speter                      svn_mergeinfo_t eraser,
1869251881Speter                      svn_mergeinfo_t whiteboard,
1870251881Speter                      svn_boolean_t consider_inheritance,
1871251881Speter                      apr_pool_t *result_pool,
1872251881Speter                      apr_pool_t *scratch_pool)
1873251881Speter{
1874251881Speter  *mergeinfo = apr_hash_make(result_pool);
1875251881Speter  return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
1876251881Speter                                      consider_inheritance, result_pool,
1877251881Speter                                      scratch_pool);
1878251881Speter}
1879251881Speter
1880251881Spetersvn_error_t *
1881251881Spetersvn_rangelist_to_string(svn_string_t **output,
1882251881Speter                        const svn_rangelist_t *rangelist,
1883251881Speter                        apr_pool_t *pool)
1884251881Speter{
1885251881Speter  svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
1886251881Speter
1887251881Speter  if (rangelist->nelts > 0)
1888251881Speter    {
1889251881Speter      int i;
1890251881Speter      svn_merge_range_t *range;
1891251881Speter
1892251881Speter      /* Handle the elements that need commas at the end.  */
1893251881Speter      for (i = 0; i < rangelist->nelts - 1; i++)
1894251881Speter        {
1895251881Speter          range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1896251881Speter          svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1897251881Speter          svn_stringbuf_appendcstr(buf, ",");
1898251881Speter        }
1899251881Speter
1900251881Speter      /* Now handle the last element, which needs no comma.  */
1901251881Speter      range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1902251881Speter      svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1903251881Speter    }
1904251881Speter
1905251881Speter  *output = svn_stringbuf__morph_into_string(buf);
1906251881Speter
1907251881Speter  return SVN_NO_ERROR;
1908251881Speter}
1909251881Speter
1910251881Speter/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT.  If PREFIX
1911251881Speter   is not NULL then prepend PREFIX to each line in OUTPUT.  If INPUT contains
1912251881Speter   no elements, return the empty string.  If INPUT contains any merge source
1913251881Speter   path keys that are relative then convert these to absolute paths in
1914251881Speter   *OUTPUT.
1915251881Speter */
1916251881Speterstatic svn_error_t *
1917251881Spetermergeinfo_to_stringbuf(svn_stringbuf_t **output,
1918251881Speter                       svn_mergeinfo_t input,
1919251881Speter                       const char *prefix,
1920251881Speter                       apr_pool_t *pool)
1921251881Speter{
1922251881Speter  *output = svn_stringbuf_create_empty(pool);
1923251881Speter
1924251881Speter  if (apr_hash_count(input) > 0)
1925251881Speter    {
1926251881Speter      apr_array_header_t *sorted =
1927251881Speter        svn_sort__hash(input, svn_sort_compare_items_as_paths, pool);
1928251881Speter      int i;
1929251881Speter
1930251881Speter      for (i = 0; i < sorted->nelts; i++)
1931251881Speter        {
1932251881Speter          svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t);
1933251881Speter          svn_string_t *revlist;
1934251881Speter
1935251881Speter          SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
1936251881Speter          svn_stringbuf_appendcstr(
1937251881Speter            *output,
1938251881Speter            apr_psprintf(pool, "%s%s%s:%s",
1939251881Speter                         prefix ? prefix : "",
1940251881Speter                         *((const char *) elt.key) == '/' ? "" : "/",
1941251881Speter                         (const char *) elt.key,
1942251881Speter                         revlist->data));
1943251881Speter          if (i < sorted->nelts - 1)
1944251881Speter            svn_stringbuf_appendcstr(*output, "\n");
1945251881Speter        }
1946251881Speter    }
1947251881Speter
1948251881Speter  return SVN_NO_ERROR;
1949251881Speter}
1950251881Speter
1951251881Spetersvn_error_t *
1952251881Spetersvn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
1953251881Speter                        apr_pool_t *pool)
1954251881Speter{
1955251881Speter  svn_stringbuf_t *mergeinfo_buf;
1956251881Speter
1957251881Speter  SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
1958251881Speter  *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
1959251881Speter  return SVN_NO_ERROR;
1960251881Speter}
1961251881Speter
1962251881Spetersvn_error_t *
1963251881Spetersvn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
1964251881Speter{
1965251881Speter  apr_hash_index_t *hi;
1966251881Speter
1967251881Speter  for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
1968251881Speter    {
1969251881Speter      apr_array_header_t *rl = svn__apr_hash_index_val(hi);
1970251881Speter
1971251881Speter      qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges);
1972251881Speter    }
1973251881Speter  return SVN_NO_ERROR;
1974251881Speter}
1975251881Speter
1976251881Spetersvn_mergeinfo_catalog_t
1977251881Spetersvn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog,
1978251881Speter                          apr_pool_t *pool)
1979251881Speter{
1980251881Speter  svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool);
1981251881Speter  apr_hash_index_t *hi;
1982251881Speter
1983251881Speter  for (hi = apr_hash_first(pool, mergeinfo_catalog);
1984251881Speter       hi;
1985251881Speter       hi = apr_hash_next(hi))
1986251881Speter    {
1987251881Speter      const char *key = svn__apr_hash_index_key(hi);
1988251881Speter      svn_mergeinfo_t val = svn__apr_hash_index_val(hi);
1989251881Speter
1990251881Speter      svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key),
1991251881Speter                    svn_mergeinfo_dup(val, pool));
1992251881Speter    }
1993251881Speter
1994251881Speter  return new_mergeinfo_catalog;
1995251881Speter}
1996251881Speter
1997251881Spetersvn_mergeinfo_t
1998251881Spetersvn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
1999251881Speter{
2000251881Speter  svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
2001251881Speter  apr_hash_index_t *hi;
2002251881Speter
2003251881Speter  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2004251881Speter    {
2005251881Speter      const char *path = svn__apr_hash_index_key(hi);
2006251881Speter      apr_ssize_t pathlen = svn__apr_hash_index_klen(hi);
2007251881Speter      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2008251881Speter
2009251881Speter      apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
2010251881Speter                   svn_rangelist_dup(rangelist, pool));
2011251881Speter    }
2012251881Speter
2013251881Speter  return new_mergeinfo;
2014251881Speter}
2015251881Speter
2016251881Spetersvn_error_t *
2017251881Spetersvn_mergeinfo_inheritable2(svn_mergeinfo_t *output,
2018251881Speter                           svn_mergeinfo_t mergeinfo,
2019251881Speter                           const char *path,
2020251881Speter                           svn_revnum_t start,
2021251881Speter                           svn_revnum_t end,
2022251881Speter                           svn_boolean_t inheritable,
2023251881Speter                           apr_pool_t *result_pool,
2024251881Speter                           apr_pool_t *scratch_pool)
2025251881Speter{
2026251881Speter  apr_hash_index_t *hi;
2027251881Speter  svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
2028251881Speter
2029251881Speter  for (hi = apr_hash_first(scratch_pool, mergeinfo);
2030251881Speter       hi;
2031251881Speter       hi = apr_hash_next(hi))
2032251881Speter    {
2033251881Speter      const char *key = svn__apr_hash_index_key(hi);
2034251881Speter      apr_ssize_t keylen = svn__apr_hash_index_klen(hi);
2035251881Speter      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2036251881Speter      svn_rangelist_t *inheritable_rangelist;
2037251881Speter
2038251881Speter      if (!path || svn_path_compare_paths(path, key) == 0)
2039251881Speter        SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist,
2040251881Speter                                           start, end, inheritable,
2041251881Speter                                           result_pool, scratch_pool));
2042251881Speter      else
2043251881Speter        inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool);
2044251881Speter
2045251881Speter      /* Only add this rangelist if some ranges remain.  A rangelist with
2046251881Speter         a path mapped to an empty rangelist is not syntactically valid */
2047251881Speter      if (inheritable_rangelist->nelts)
2048251881Speter        apr_hash_set(inheritable_mergeinfo,
2049251881Speter                     apr_pstrmemdup(result_pool, key, keylen), keylen,
2050251881Speter                     inheritable_rangelist);
2051251881Speter    }
2052251881Speter  *output = inheritable_mergeinfo;
2053251881Speter  return SVN_NO_ERROR;
2054251881Speter}
2055251881Speter
2056251881Speter
2057251881Spetersvn_error_t *
2058251881Spetersvn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist,
2059251881Speter                           const svn_rangelist_t *rangelist,
2060251881Speter                           svn_revnum_t start,
2061251881Speter                           svn_revnum_t end,
2062251881Speter                           svn_boolean_t inheritable,
2063251881Speter                           apr_pool_t *result_pool,
2064251881Speter                           apr_pool_t *scratch_pool)
2065251881Speter{
2066251881Speter  *inheritable_rangelist = apr_array_make(result_pool, 1,
2067251881Speter                                          sizeof(svn_merge_range_t *));
2068251881Speter  if (rangelist->nelts)
2069251881Speter    {
2070251881Speter      if (!SVN_IS_VALID_REVNUM(start)
2071251881Speter          || !SVN_IS_VALID_REVNUM(end)
2072251881Speter          || end < start)
2073251881Speter        {
2074251881Speter          int i;
2075251881Speter          /* We want all non-inheritable ranges removed. */
2076251881Speter          for (i = 0; i < rangelist->nelts; i++)
2077251881Speter            {
2078251881Speter              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2079251881Speter                                                       svn_merge_range_t *);
2080251881Speter              if (range->inheritable == inheritable)
2081251881Speter                {
2082251881Speter                  svn_merge_range_t *inheritable_range =
2083251881Speter                    apr_palloc(result_pool, sizeof(*inheritable_range));
2084251881Speter                  inheritable_range->start = range->start;
2085251881Speter                  inheritable_range->end = range->end;
2086251881Speter                  inheritable_range->inheritable = TRUE;
2087251881Speter                  APR_ARRAY_PUSH(*inheritable_rangelist,
2088251881Speter                                 svn_merge_range_t *) = range;
2089251881Speter                }
2090251881Speter            }
2091251881Speter        }
2092251881Speter      else
2093251881Speter        {
2094251881Speter          /* We want only the non-inheritable ranges bound by START
2095251881Speter             and END removed. */
2096251881Speter          svn_rangelist_t *ranges_inheritable =
2097251881Speter            svn_rangelist__initialize(start, end, inheritable, scratch_pool);
2098251881Speter
2099251881Speter          if (rangelist->nelts)
2100251881Speter            SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
2101251881Speter                                         ranges_inheritable,
2102251881Speter                                         rangelist,
2103251881Speter                                         TRUE,
2104251881Speter                                         result_pool));
2105251881Speter        }
2106251881Speter    }
2107251881Speter  return SVN_NO_ERROR;
2108251881Speter}
2109251881Speter
2110251881Spetersvn_boolean_t
2111251881Spetersvn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
2112251881Speter                                       apr_pool_t *pool)
2113251881Speter{
2114251881Speter  apr_hash_index_t *hi;
2115251881Speter  svn_boolean_t removed_some_ranges = FALSE;
2116251881Speter
2117251881Speter  if (mergeinfo)
2118251881Speter    {
2119251881Speter      for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2120251881Speter        {
2121251881Speter          const char *path = svn__apr_hash_index_key(hi);
2122251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2123251881Speter
2124251881Speter          if (rangelist->nelts == 0)
2125251881Speter            {
2126251881Speter              svn_hash_sets(mergeinfo, path, NULL);
2127251881Speter              removed_some_ranges = TRUE;
2128251881Speter            }
2129251881Speter        }
2130251881Speter    }
2131251881Speter  return removed_some_ranges;
2132251881Speter}
2133251881Speter
2134251881Spetersvn_error_t *
2135251881Spetersvn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
2136251881Speter                                          svn_mergeinfo_catalog_t in_catalog,
2137251881Speter                                          const char *prefix_path,
2138251881Speter                                          apr_pool_t *pool)
2139251881Speter{
2140251881Speter  apr_hash_index_t *hi;
2141251881Speter
2142251881Speter  SVN_ERR_ASSERT(prefix_path[0] == '/');
2143251881Speter
2144251881Speter  *out_catalog = apr_hash_make(pool);
2145251881Speter
2146251881Speter  for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
2147251881Speter    {
2148251881Speter      const char *original_path = svn__apr_hash_index_key(hi);
2149251881Speter      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2150251881Speter      const char *new_path;
2151251881Speter
2152251881Speter      new_path = svn_fspath__skip_ancestor(prefix_path, original_path);
2153251881Speter      SVN_ERR_ASSERT(new_path);
2154251881Speter
2155251881Speter      svn_hash_sets(*out_catalog, new_path, value);
2156251881Speter    }
2157251881Speter
2158251881Speter  return SVN_NO_ERROR;
2159251881Speter}
2160251881Speter
2161251881Spetersvn_error_t *
2162251881Spetersvn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
2163251881Speter                                     svn_mergeinfo_catalog_t in_catalog,
2164251881Speter                                     const char *prefix_path,
2165251881Speter                                     apr_pool_t *result_pool,
2166251881Speter                                     apr_pool_t *scratch_pool)
2167251881Speter{
2168251881Speter  apr_hash_index_t *hi;
2169251881Speter
2170251881Speter  *out_catalog = apr_hash_make(result_pool);
2171251881Speter
2172251881Speter  for (hi = apr_hash_first(scratch_pool, in_catalog);
2173251881Speter       hi;
2174251881Speter       hi = apr_hash_next(hi))
2175251881Speter    {
2176251881Speter      const char *original_path = svn__apr_hash_index_key(hi);
2177251881Speter      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2178251881Speter
2179251881Speter      if (original_path[0] == '/')
2180251881Speter        original_path++;
2181251881Speter
2182251881Speter      svn_hash_sets(*out_catalog,
2183251881Speter                    svn_dirent_join(prefix_path, original_path, result_pool),
2184251881Speter                    value);
2185251881Speter    }
2186251881Speter
2187251881Speter  return SVN_NO_ERROR;
2188251881Speter}
2189251881Speter
2190251881Spetersvn_error_t *
2191251881Spetersvn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
2192251881Speter                                       svn_mergeinfo_t mergeinfo,
2193251881Speter                                       const char *suffix_relpath,
2194251881Speter                                       apr_pool_t *result_pool,
2195251881Speter                                       apr_pool_t *scratch_pool)
2196251881Speter{
2197251881Speter  apr_hash_index_t *hi;
2198251881Speter
2199251881Speter  SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath));
2200251881Speter
2201251881Speter  *out_mergeinfo = apr_hash_make(result_pool);
2202251881Speter
2203251881Speter  for (hi = apr_hash_first(scratch_pool, mergeinfo);
2204251881Speter       hi;
2205251881Speter       hi = apr_hash_next(hi))
2206251881Speter    {
2207251881Speter      const char *fspath = svn__apr_hash_index_key(hi);
2208251881Speter      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2209251881Speter
2210251881Speter      svn_hash_sets(*out_mergeinfo,
2211251881Speter                    svn_fspath__join(fspath, suffix_relpath, result_pool),
2212251881Speter                    rangelist);
2213251881Speter    }
2214251881Speter
2215251881Speter  return SVN_NO_ERROR;
2216251881Speter}
2217251881Speter
2218251881Spetersvn_rangelist_t *
2219251881Spetersvn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool)
2220251881Speter{
2221251881Speter  svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts,
2222251881Speter                                           sizeof(svn_merge_range_t *));
2223251881Speter
2224251881Speter  /* allocate target range buffer with a single operation */
2225251881Speter  svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts);
2226251881Speter  int i;
2227251881Speter
2228251881Speter  /* fill it iteratively and link it into the range list */
2229251881Speter  for (i = 0; i < rangelist->nelts; i++)
2230251881Speter    {
2231251881Speter      memcpy(copy + i,
2232251881Speter             APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
2233251881Speter             sizeof(*copy));
2234251881Speter      APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i;
2235251881Speter    }
2236251881Speter
2237251881Speter  return new_rl;
2238251881Speter}
2239251881Speter
2240251881Spetersvn_merge_range_t *
2241251881Spetersvn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool)
2242251881Speter{
2243251881Speter  svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range));
2244251881Speter  memcpy(new_range, range, sizeof(*new_range));
2245251881Speter  return new_range;
2246251881Speter}
2247251881Speter
2248251881Spetersvn_boolean_t
2249251881Spetersvn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev)
2250251881Speter{
2251251881Speter  assert(SVN_IS_VALID_REVNUM(range->start));
2252251881Speter  assert(SVN_IS_VALID_REVNUM(range->end));
2253251881Speter  assert(range->start != range->end);
2254251881Speter
2255251881Speter  if (range->start < range->end)
2256251881Speter    return rev > range->start && rev <= range->end;
2257251881Speter  else
2258251881Speter    return rev > range->end && rev <= range->start;
2259251881Speter}
2260251881Speter
2261251881Spetersvn_error_t *
2262251881Spetersvn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
2263251881Speter                                           svn_mergeinfo_catalog_t catalog,
2264251881Speter                                           const char *key_prefix,
2265251881Speter                                           const char *val_prefix,
2266251881Speter                                           apr_pool_t *pool)
2267251881Speter{
2268251881Speter  svn_stringbuf_t *output_buf = NULL;
2269251881Speter
2270251881Speter  if (catalog && apr_hash_count(catalog))
2271251881Speter    {
2272251881Speter      int i;
2273251881Speter      apr_array_header_t *sorted_catalog =
2274251881Speter        svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
2275251881Speter
2276251881Speter      output_buf = svn_stringbuf_create_empty(pool);
2277251881Speter      for (i = 0; i < sorted_catalog->nelts; i++)
2278251881Speter        {
2279251881Speter          svn_sort__item_t elt =
2280251881Speter            APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t);
2281251881Speter          const char *path1;
2282251881Speter          svn_mergeinfo_t mergeinfo;
2283251881Speter          svn_stringbuf_t *mergeinfo_output_buf;
2284251881Speter
2285251881Speter          path1 = elt.key;
2286251881Speter          mergeinfo = elt.value;
2287251881Speter          if (key_prefix)
2288251881Speter            svn_stringbuf_appendcstr(output_buf, key_prefix);
2289251881Speter          svn_stringbuf_appendcstr(output_buf, path1);
2290251881Speter          svn_stringbuf_appendcstr(output_buf, "\n");
2291251881Speter          SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo,
2292251881Speter                                         val_prefix ? val_prefix : "", pool));
2293251881Speter          svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf);
2294251881Speter          svn_stringbuf_appendcstr(output_buf, "\n");
2295251881Speter        }
2296251881Speter    }
2297251881Speter#if SVN_DEBUG
2298251881Speter  else if (!catalog)
2299251881Speter    {
2300251881Speter      output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2301251881Speter      svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n"));
2302251881Speter    }
2303251881Speter  else if (apr_hash_count(catalog) == 0)
2304251881Speter    {
2305251881Speter      output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2306251881Speter      svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n"));
2307251881Speter    }
2308251881Speter#endif
2309251881Speter
2310251881Speter  /* If we have an output_buf, convert it to an svn_string_t;
2311251881Speter     otherwise, return a new string containing only a newline
2312251881Speter     character.  */
2313251881Speter  if (output_buf)
2314251881Speter    *output = svn_stringbuf__morph_into_string(output_buf);
2315251881Speter  else
2316251881Speter    *output = svn_string_create("\n", pool);
2317251881Speter
2318251881Speter  return SVN_NO_ERROR;
2319251881Speter}
2320251881Speter
2321251881Spetersvn_error_t *
2322251881Spetersvn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
2323251881Speter                                   svn_revnum_t *oldest_rev,
2324251881Speter                                   svn_mergeinfo_t mergeinfo,
2325251881Speter                                   apr_pool_t *pool)
2326251881Speter{
2327251881Speter  *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
2328251881Speter  if (mergeinfo)
2329251881Speter    {
2330251881Speter      apr_hash_index_t *hi;
2331251881Speter
2332251881Speter      for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2333251881Speter        {
2334251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2335251881Speter
2336251881Speter          if (rangelist->nelts)
2337251881Speter            {
2338251881Speter              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist,
2339251881Speter                                                       rangelist->nelts - 1,
2340251881Speter                                                       svn_merge_range_t *);
2341251881Speter              if (!SVN_IS_VALID_REVNUM(*youngest_rev)
2342251881Speter                  || (range->end > *youngest_rev))
2343251881Speter                *youngest_rev = range->end;
2344251881Speter
2345251881Speter              range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
2346251881Speter              if (!SVN_IS_VALID_REVNUM(*oldest_rev)
2347251881Speter                  || (range->start < *oldest_rev))
2348251881Speter                *oldest_rev = range->start;
2349251881Speter            }
2350251881Speter        }
2351251881Speter    }
2352251881Speter  return SVN_NO_ERROR;
2353251881Speter}
2354251881Speter
2355251881Spetersvn_error_t *
2356251881Spetersvn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat,
2357251881Speter                                        svn_mergeinfo_catalog_t catalog,
2358251881Speter                                        svn_revnum_t youngest_rev,
2359251881Speter                                        svn_revnum_t oldest_rev,
2360251881Speter                                        svn_boolean_t include_range,
2361251881Speter                                        apr_pool_t *result_pool,
2362251881Speter                                        apr_pool_t *scratch_pool)
2363251881Speter{
2364251881Speter  apr_hash_index_t *hi;
2365251881Speter
2366251881Speter  *filtered_cat = apr_hash_make(result_pool);
2367251881Speter  for (hi = apr_hash_first(scratch_pool, catalog);
2368251881Speter       hi;
2369251881Speter       hi = apr_hash_next(hi))
2370251881Speter    {
2371251881Speter      const char *path = svn__apr_hash_index_key(hi);
2372251881Speter      svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
2373251881Speter      svn_mergeinfo_t filtered_mergeinfo;
2374251881Speter
2375251881Speter      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
2376251881Speter                                                        mergeinfo,
2377251881Speter                                                        youngest_rev,
2378251881Speter                                                        oldest_rev,
2379251881Speter                                                        include_range,
2380251881Speter                                                        result_pool,
2381251881Speter                                                        scratch_pool));
2382251881Speter      if (apr_hash_count(filtered_mergeinfo))
2383251881Speter        svn_hash_sets(*filtered_cat,
2384251881Speter                      apr_pstrdup(result_pool, path), filtered_mergeinfo);
2385251881Speter    }
2386251881Speter
2387251881Speter  return SVN_NO_ERROR;
2388251881Speter}
2389251881Speter
2390251881Spetersvn_error_t *
2391251881Spetersvn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
2392251881Speter                                          svn_mergeinfo_t mergeinfo,
2393251881Speter                                          svn_revnum_t youngest_rev,
2394251881Speter                                          svn_revnum_t oldest_rev,
2395251881Speter                                          svn_boolean_t include_range,
2396251881Speter                                          apr_pool_t *result_pool,
2397251881Speter                                          apr_pool_t *scratch_pool)
2398251881Speter{
2399251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
2400251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
2401251881Speter  SVN_ERR_ASSERT(oldest_rev < youngest_rev);
2402251881Speter
2403251881Speter  *filtered_mergeinfo = apr_hash_make(result_pool);
2404251881Speter
2405251881Speter  if (mergeinfo)
2406251881Speter    {
2407251881Speter      apr_hash_index_t *hi;
2408251881Speter      svn_rangelist_t *filter_rangelist =
2409251881Speter        svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE,
2410251881Speter                                  scratch_pool);
2411251881Speter
2412251881Speter      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2413251881Speter           hi;
2414251881Speter           hi = apr_hash_next(hi))
2415251881Speter        {
2416251881Speter          const char *path = svn__apr_hash_index_key(hi);
2417251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2418251881Speter
2419251881Speter          if (rangelist->nelts)
2420251881Speter            {
2421251881Speter              svn_rangelist_t *new_rangelist;
2422251881Speter
2423251881Speter              SVN_ERR(rangelist_intersect_or_remove(
2424251881Speter                        &new_rangelist, filter_rangelist, rangelist,
2425251881Speter                        ! include_range, FALSE, result_pool));
2426251881Speter
2427251881Speter              if (new_rangelist->nelts)
2428251881Speter                svn_hash_sets(*filtered_mergeinfo,
2429251881Speter                              apr_pstrdup(result_pool, path), new_rangelist);
2430251881Speter            }
2431251881Speter        }
2432251881Speter    }
2433251881Speter  return SVN_NO_ERROR;
2434251881Speter}
2435251881Speter
2436251881Spetersvn_error_t *
2437251881Spetersvn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
2438251881Speter                                           svn_mergeinfo_t mergeinfo,
2439251881Speter                                           svn_revnum_t offset,
2440251881Speter                                           apr_pool_t *result_pool,
2441251881Speter                                           apr_pool_t *scratch_pool)
2442251881Speter{
2443251881Speter  apr_hash_index_t *hi;
2444251881Speter  *adjusted_mergeinfo = apr_hash_make(result_pool);
2445251881Speter
2446251881Speter  if (mergeinfo)
2447251881Speter    {
2448251881Speter      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2449251881Speter           hi;
2450251881Speter           hi = apr_hash_next(hi))
2451251881Speter        {
2452251881Speter          int i;
2453251881Speter          const char *path = svn__apr_hash_index_key(hi);
2454251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2455251881Speter          svn_rangelist_t *adjusted_rangelist =
2456251881Speter            apr_array_make(result_pool, rangelist->nelts,
2457251881Speter                           sizeof(svn_merge_range_t *));
2458251881Speter
2459251881Speter          for (i = 0; i < rangelist->nelts; i++)
2460251881Speter            {
2461251881Speter              svn_merge_range_t *range =
2462251881Speter                APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
2463251881Speter
2464251881Speter              if (range->start + offset > 0 && range->end + offset > 0)
2465251881Speter                {
2466251881Speter                  if (range->start + offset < 0)
2467251881Speter                    range->start = 0;
2468251881Speter                  else
2469251881Speter                    range->start = range->start + offset;
2470251881Speter
2471251881Speter                  if (range->end + offset < 0)
2472251881Speter                    range->end = 0;
2473251881Speter                  else
2474251881Speter                    range->end = range->end + offset;
2475251881Speter                  APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) =
2476251881Speter                    range;
2477251881Speter                }
2478251881Speter            }
2479251881Speter
2480251881Speter          if (adjusted_rangelist->nelts)
2481251881Speter            svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path),
2482251881Speter                          adjusted_rangelist);
2483251881Speter        }
2484251881Speter    }
2485251881Speter  return SVN_NO_ERROR;
2486251881Speter}
2487251881Speter
2488251881Spetersvn_boolean_t
2489251881Spetersvn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
2490251881Speter                                 apr_pool_t *scratch_pool)
2491251881Speter{
2492251881Speter  if (mergeinfo)
2493251881Speter    {
2494251881Speter      apr_hash_index_t *hi;
2495251881Speter
2496251881Speter      for (hi = apr_hash_first(scratch_pool, mergeinfo);
2497251881Speter           hi;
2498251881Speter           hi = apr_hash_next(hi))
2499251881Speter        {
2500251881Speter          svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2501251881Speter          int i;
2502251881Speter
2503251881Speter          for (i = 0; i < rangelist->nelts; i++)
2504251881Speter            {
2505251881Speter              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2506251881Speter                                                       svn_merge_range_t *);
2507251881Speter              if (!range->inheritable)
2508251881Speter                return TRUE;
2509251881Speter            }
2510251881Speter        }
2511251881Speter    }
2512251881Speter  return FALSE;
2513251881Speter}
2514251881Speter
2515251881Spetersvn_rangelist_t *
2516251881Spetersvn_rangelist__initialize(svn_revnum_t start,
2517251881Speter                          svn_revnum_t end,
2518251881Speter                          svn_boolean_t inheritable,
2519251881Speter                          apr_pool_t *result_pool)
2520251881Speter{
2521251881Speter  svn_rangelist_t *rangelist =
2522251881Speter    apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
2523251881Speter  svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
2524251881Speter
2525251881Speter  range->start = start;
2526251881Speter  range->end = end;
2527251881Speter  range->inheritable = inheritable;
2528251881Speter  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2529251881Speter  return rangelist;
2530251881Speter}
2531251881Speter
2532251881Spetersvn_error_t *
2533251881Spetersvn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
2534251881Speter                                       const apr_array_header_t *segments,
2535251881Speter                                       apr_pool_t *pool)
2536251881Speter{
2537251881Speter  svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
2538251881Speter  int i;
2539251881Speter
2540251881Speter  /* Translate location segments into merge sources and ranges. */
2541251881Speter  for (i = 0; i < segments->nelts; i++)
2542251881Speter    {
2543251881Speter      svn_location_segment_t *segment =
2544251881Speter        APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
2545251881Speter      svn_rangelist_t *path_ranges;
2546251881Speter      svn_merge_range_t *range;
2547251881Speter      const char *source_path;
2548251881Speter
2549251881Speter      /* No path segment?  Skip it. */
2550251881Speter      if (! segment->path)
2551251881Speter        continue;
2552251881Speter
2553251881Speter      /* Prepend a leading slash to our path. */
2554251881Speter      source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
2555251881Speter
2556251881Speter      /* See if we already stored ranges for this path.  If not, make
2557251881Speter         a new list.  */
2558251881Speter      path_ranges = svn_hash_gets(mergeinfo, source_path);
2559251881Speter      if (! path_ranges)
2560251881Speter        path_ranges = apr_array_make(pool, 1, sizeof(range));
2561251881Speter
2562251881Speter      /* A svn_location_segment_t may have legitimately describe only
2563251881Speter         revision 0, but there is no corresponding representation for
2564251881Speter         this in a svn_merge_range_t. */
2565251881Speter      if (segment->range_start == 0 && segment->range_end == 0)
2566251881Speter        continue;
2567251881Speter
2568251881Speter      /* Build a merge range, push it onto the list of ranges, and for
2569251881Speter         good measure, (re)store it in the hash. */
2570251881Speter      range = apr_pcalloc(pool, sizeof(*range));
2571251881Speter      range->start = MAX(segment->range_start - 1, 0);
2572251881Speter      range->end = segment->range_end;
2573251881Speter      range->inheritable = TRUE;
2574251881Speter      APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
2575251881Speter      svn_hash_sets(mergeinfo, source_path, path_ranges);
2576251881Speter    }
2577251881Speter
2578251881Speter  *mergeinfo_p = mergeinfo;
2579251881Speter  return SVN_NO_ERROR;
2580251881Speter}
2581251881Speter
2582251881Spetersvn_error_t *
2583251881Spetersvn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
2584251881Speter                          svn_mergeinfo_t merge_history,
2585251881Speter                          apr_pool_t *result_pool,
2586251881Speter                          apr_pool_t *scratch_pool)
2587251881Speter{
2588251881Speter  if (apr_hash_count(merge_history))
2589251881Speter    {
2590251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2591251881Speter      apr_hash_index_t *hi;
2592251881Speter
2593251881Speter      for (hi = apr_hash_first(scratch_pool, merge_history);
2594251881Speter           hi;
2595251881Speter           hi = apr_hash_next(hi))
2596251881Speter        {
2597251881Speter          svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi);
2598251881Speter
2599251881Speter          svn_pool_clear(iterpool);
2600251881Speter          SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
2601251881Speter                                       result_pool, iterpool));
2602251881Speter        }
2603251881Speter      svn_pool_destroy(iterpool);
2604251881Speter    }
2605251881Speter  return SVN_NO_ERROR;
2606251881Speter}
2607251881Speter
2608251881Speter
2609251881Speterconst char *
2610251881Spetersvn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
2611251881Speter{
2612251881Speter  switch (inherit)
2613251881Speter    {
2614251881Speter    case svn_mergeinfo_inherited:
2615251881Speter      return "inherited";
2616251881Speter    case svn_mergeinfo_nearest_ancestor:
2617251881Speter      return "nearest-ancestor";
2618251881Speter    default:
2619251881Speter      return "explicit";
2620251881Speter    }
2621251881Speter}
2622251881Speter
2623251881Spetersvn_mergeinfo_inheritance_t
2624251881Spetersvn_inheritance_from_word(const char *word)
2625251881Speter{
2626251881Speter  if (strcmp(word, "inherited") == 0)
2627251881Speter    return svn_mergeinfo_inherited;
2628251881Speter  if (strcmp(word, "nearest-ancestor") == 0)
2629251881Speter    return svn_mergeinfo_nearest_ancestor;
2630251881Speter  return svn_mergeinfo_explicit;
2631251881Speter}
2632