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] = ©[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