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