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