1251881Speter/* 2251881Speter * diff_memory.c : routines for doing diffs on in-memory data 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 24251881Speter#define WANT_MEMFUNC 25251881Speter#define WANT_STRFUNC 26251881Speter#include <apr.h> 27251881Speter#include <apr_want.h> 28251881Speter#include <apr_tables.h> 29251881Speter 30251881Speter#include <assert.h> 31251881Speter 32251881Speter#include "svn_diff.h" 33251881Speter#include "svn_pools.h" 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_string.h" 36251881Speter#include "svn_utf.h" 37251881Speter#include "diff.h" 38251881Speter#include "svn_private_config.h" 39251881Speter#include "private/svn_adler32.h" 40251881Speter#include "private/svn_diff_private.h" 41251881Speter 42251881Spetertypedef struct source_tokens_t 43251881Speter{ 44251881Speter /* A token simply is an svn_string_t pointing to 45251881Speter the data of the in-memory data source, containing 46251881Speter the raw token text, with length stored in the string */ 47251881Speter apr_array_header_t *tokens; 48251881Speter 49251881Speter /* Next token to be consumed */ 50251881Speter apr_size_t next_token; 51251881Speter 52251881Speter /* The source, containing the in-memory data to be diffed */ 53251881Speter const svn_string_t *source; 54251881Speter 55251881Speter /* The last token ends with a newline character (sequence) */ 56251881Speter svn_boolean_t ends_without_eol; 57251881Speter} source_tokens_t; 58251881Speter 59251881Spetertypedef struct diff_mem_baton_t 60251881Speter{ 61251881Speter /* The tokens for each of the sources */ 62251881Speter source_tokens_t sources[4]; 63251881Speter 64251881Speter /* Normalization buffer; we only ever compare 2 tokens at the same time */ 65251881Speter char *normalization_buf[2]; 66251881Speter 67251881Speter /* Options for normalized comparison of the data sources */ 68251881Speter const svn_diff_file_options_t *normalization_options; 69251881Speter} diff_mem_baton_t; 70251881Speter 71251881Speter 72251881Speterstatic int 73251881Speterdatasource_to_index(svn_diff_datasource_e datasource) 74251881Speter{ 75251881Speter switch (datasource) 76251881Speter { 77251881Speter case svn_diff_datasource_original: 78251881Speter return 0; 79251881Speter 80251881Speter case svn_diff_datasource_modified: 81251881Speter return 1; 82251881Speter 83251881Speter case svn_diff_datasource_latest: 84251881Speter return 2; 85251881Speter 86251881Speter case svn_diff_datasource_ancestor: 87251881Speter return 3; 88251881Speter } 89251881Speter 90251881Speter return -1; 91251881Speter} 92251881Speter 93251881Speter 94251881Speter/* Implements svn_diff_fns2_t::datasources_open */ 95251881Speterstatic svn_error_t * 96251881Speterdatasources_open(void *baton, 97251881Speter apr_off_t *prefix_lines, 98251881Speter apr_off_t *suffix_lines, 99251881Speter const svn_diff_datasource_e *datasources, 100251881Speter apr_size_t datasources_len) 101251881Speter{ 102251881Speter /* Do nothing: everything is already there and initialized to 0 */ 103251881Speter *prefix_lines = 0; 104251881Speter *suffix_lines = 0; 105251881Speter return SVN_NO_ERROR; 106251881Speter} 107251881Speter 108251881Speter 109251881Speter/* Implements svn_diff_fns2_t::datasource_close */ 110251881Speterstatic svn_error_t * 111251881Speterdatasource_close(void *baton, svn_diff_datasource_e datasource) 112251881Speter{ 113251881Speter /* Do nothing. The compare_token function needs previous datasources 114251881Speter * to stay available until all datasources are processed. 115251881Speter */ 116251881Speter 117251881Speter return SVN_NO_ERROR; 118251881Speter} 119251881Speter 120251881Speter 121251881Speter/* Implements svn_diff_fns2_t::datasource_get_next_token */ 122251881Speterstatic svn_error_t * 123251881Speterdatasource_get_next_token(apr_uint32_t *hash, void **token, void *baton, 124251881Speter svn_diff_datasource_e datasource) 125251881Speter{ 126251881Speter diff_mem_baton_t *mem_baton = baton; 127251881Speter source_tokens_t *src = &(mem_baton->sources[datasource_to_index(datasource)]); 128251881Speter 129251881Speter if ((apr_size_t)src->tokens->nelts > src->next_token) 130251881Speter { 131251881Speter /* There are actually tokens to be returned */ 132251881Speter char *buf = mem_baton->normalization_buf[0]; 133251881Speter svn_string_t *tok = (*token) 134251881Speter = APR_ARRAY_IDX(src->tokens, src->next_token, svn_string_t *); 135251881Speter apr_off_t len = tok->len; 136251881Speter svn_diff__normalize_state_t state 137251881Speter = svn_diff__normalize_state_normal; 138251881Speter 139251881Speter svn_diff__normalize_buffer(&buf, &len, &state, tok->data, 140251881Speter mem_baton->normalization_options); 141251881Speter *hash = svn__adler32(0, buf, len); 142251881Speter src->next_token++; 143251881Speter } 144251881Speter else 145251881Speter *token = NULL; 146251881Speter 147251881Speter return SVN_NO_ERROR; 148251881Speter} 149251881Speter 150251881Speter/* Implements svn_diff_fns2_t::token_compare */ 151251881Speterstatic svn_error_t * 152251881Spetertoken_compare(void *baton, void *token1, void *token2, int *result) 153251881Speter{ 154251881Speter /* Implement the same behaviour as diff_file.c:token_compare(), 155251881Speter but be simpler, because we know we'll have all data in memory */ 156251881Speter diff_mem_baton_t *btn = baton; 157251881Speter svn_string_t *t1 = token1; 158251881Speter svn_string_t *t2 = token2; 159251881Speter char *buf1 = btn->normalization_buf[0]; 160251881Speter char *buf2 = btn->normalization_buf[1]; 161251881Speter apr_off_t len1 = t1->len; 162251881Speter apr_off_t len2 = t2->len; 163251881Speter svn_diff__normalize_state_t state = svn_diff__normalize_state_normal; 164251881Speter 165251881Speter svn_diff__normalize_buffer(&buf1, &len1, &state, t1->data, 166251881Speter btn->normalization_options); 167251881Speter state = svn_diff__normalize_state_normal; 168251881Speter svn_diff__normalize_buffer(&buf2, &len2, &state, t2->data, 169251881Speter btn->normalization_options); 170251881Speter 171251881Speter if (len1 != len2) 172251881Speter *result = (len1 < len2) ? -1 : 1; 173251881Speter else 174251881Speter *result = (len1 == 0) ? 0 : memcmp(buf1, buf2, (size_t) len1); 175251881Speter 176251881Speter return SVN_NO_ERROR; 177251881Speter} 178251881Speter 179251881Speter/* Implements svn_diff_fns2_t::token_discard */ 180251881Speterstatic void 181251881Spetertoken_discard(void *baton, void *token) 182251881Speter{ 183251881Speter /* No-op, we have no use for discarded tokens... */ 184251881Speter} 185251881Speter 186251881Speter 187251881Speter/* Implements svn_diff_fns2_t::token_discard_all */ 188251881Speterstatic void 189251881Spetertoken_discard_all(void *baton) 190251881Speter{ 191251881Speter /* Do nothing. 192251881Speter Note that in the file case, this function discards all 193251881Speter tokens allocated, but we're geared toward small in-memory diffs. 194251881Speter Meaning that there's no special pool to clear. 195251881Speter */ 196251881Speter} 197251881Speter 198251881Speter 199251881Speterstatic const svn_diff_fns2_t svn_diff__mem_vtable = 200251881Speter{ 201251881Speter datasources_open, 202251881Speter datasource_close, 203251881Speter datasource_get_next_token, 204251881Speter token_compare, 205251881Speter token_discard, 206251881Speter token_discard_all 207251881Speter}; 208251881Speter 209251881Speter/* Fill SRC with the diff tokens (e.g. lines). 210251881Speter 211251881Speter TEXT is assumed to live long enough for the tokens to 212251881Speter stay valid during their lifetime: no data is copied, 213251881Speter instead, svn_string_t's are allocated pointing straight 214251881Speter into TEXT. 215251881Speter*/ 216251881Speterstatic void 217251881Speterfill_source_tokens(source_tokens_t *src, 218251881Speter const svn_string_t *text, 219251881Speter apr_pool_t *pool) 220251881Speter{ 221251881Speter const char *curp; 222251881Speter const char *endp; 223251881Speter const char *startp; 224251881Speter 225251881Speter src->tokens = apr_array_make(pool, 0, sizeof(svn_string_t *)); 226251881Speter src->next_token = 0; 227251881Speter src->source = text; 228251881Speter 229251881Speter for (startp = curp = text->data, endp = curp + text->len; 230251881Speter curp != endp; curp++) 231251881Speter { 232251881Speter if (curp != endp && *curp == '\r' && *(curp + 1) == '\n') 233251881Speter curp++; 234251881Speter 235251881Speter if (*curp == '\r' || *curp == '\n') 236251881Speter { 237251881Speter APR_ARRAY_PUSH(src->tokens, svn_string_t *) = 238251881Speter svn_string_ncreate(startp, curp - startp + 1, pool); 239251881Speter 240251881Speter startp = curp + 1; 241251881Speter } 242251881Speter } 243251881Speter 244251881Speter /* If there's anything remaining (ie last line doesn't have a newline) */ 245251881Speter if (startp != endp) 246251881Speter { 247251881Speter APR_ARRAY_PUSH(src->tokens, svn_string_t *) = 248251881Speter svn_string_ncreate(startp, endp - startp, pool); 249251881Speter src->ends_without_eol = TRUE; 250251881Speter } 251251881Speter else 252251881Speter src->ends_without_eol = FALSE; 253251881Speter} 254251881Speter 255251881Speter 256251881Speterstatic void 257251881Speteralloc_normalization_bufs(diff_mem_baton_t *btn, 258251881Speter int sources, 259251881Speter apr_pool_t *pool) 260251881Speter{ 261251881Speter apr_size_t max_len = 0; 262251881Speter apr_off_t idx; 263251881Speter int i; 264251881Speter 265251881Speter for (i = 0; i < sources; i++) 266251881Speter { 267251881Speter apr_array_header_t *tokens = btn->sources[i].tokens; 268251881Speter if (tokens->nelts > 0) 269251881Speter for (idx = 0; idx < tokens->nelts; idx++) 270251881Speter { 271251881Speter apr_size_t token_len 272251881Speter = APR_ARRAY_IDX(tokens, idx, svn_string_t *)->len; 273251881Speter max_len = (max_len < token_len) ? token_len : max_len; 274251881Speter } 275251881Speter } 276251881Speter 277251881Speter btn->normalization_buf[0] = apr_palloc(pool, max_len); 278251881Speter btn->normalization_buf[1] = apr_palloc(pool, max_len); 279251881Speter} 280251881Speter 281251881Spetersvn_error_t * 282251881Spetersvn_diff_mem_string_diff(svn_diff_t **diff, 283251881Speter const svn_string_t *original, 284251881Speter const svn_string_t *modified, 285251881Speter const svn_diff_file_options_t *options, 286251881Speter apr_pool_t *pool) 287251881Speter{ 288251881Speter diff_mem_baton_t baton; 289251881Speter 290251881Speter fill_source_tokens(&(baton.sources[0]), original, pool); 291251881Speter fill_source_tokens(&(baton.sources[1]), modified, pool); 292251881Speter alloc_normalization_bufs(&baton, 2, pool); 293251881Speter 294251881Speter baton.normalization_options = options; 295251881Speter 296251881Speter return svn_diff_diff_2(diff, &baton, &svn_diff__mem_vtable, pool); 297251881Speter} 298251881Speter 299251881Spetersvn_error_t * 300251881Spetersvn_diff_mem_string_diff3(svn_diff_t **diff, 301251881Speter const svn_string_t *original, 302251881Speter const svn_string_t *modified, 303251881Speter const svn_string_t *latest, 304251881Speter const svn_diff_file_options_t *options, 305251881Speter apr_pool_t *pool) 306251881Speter{ 307251881Speter diff_mem_baton_t baton; 308251881Speter 309251881Speter fill_source_tokens(&(baton.sources[0]), original, pool); 310251881Speter fill_source_tokens(&(baton.sources[1]), modified, pool); 311251881Speter fill_source_tokens(&(baton.sources[2]), latest, pool); 312251881Speter alloc_normalization_bufs(&baton, 3, pool); 313251881Speter 314251881Speter baton.normalization_options = options; 315251881Speter 316251881Speter return svn_diff_diff3_2(diff, &baton, &svn_diff__mem_vtable, pool); 317251881Speter} 318251881Speter 319251881Speter 320251881Spetersvn_error_t * 321251881Spetersvn_diff_mem_string_diff4(svn_diff_t **diff, 322251881Speter const svn_string_t *original, 323251881Speter const svn_string_t *modified, 324251881Speter const svn_string_t *latest, 325251881Speter const svn_string_t *ancestor, 326251881Speter const svn_diff_file_options_t *options, 327251881Speter apr_pool_t *pool) 328251881Speter{ 329251881Speter diff_mem_baton_t baton; 330251881Speter 331251881Speter fill_source_tokens(&(baton.sources[0]), original, pool); 332251881Speter fill_source_tokens(&(baton.sources[1]), modified, pool); 333251881Speter fill_source_tokens(&(baton.sources[2]), latest, pool); 334251881Speter fill_source_tokens(&(baton.sources[3]), ancestor, pool); 335251881Speter alloc_normalization_bufs(&baton, 4, pool); 336251881Speter 337251881Speter baton.normalization_options = options; 338251881Speter 339251881Speter return svn_diff_diff4_2(diff, &baton, &svn_diff__mem_vtable, pool); 340251881Speter} 341251881Speter 342251881Speter 343251881Spetertypedef enum unified_output_e 344251881Speter{ 345251881Speter unified_output_context = 0, 346251881Speter unified_output_delete, 347251881Speter unified_output_insert, 348251881Speter unified_output_skip 349251881Speter} unified_output_e; 350251881Speter 351251881Speter/* Baton for generating unified diffs */ 352251881Spetertypedef struct unified_output_baton_t 353251881Speter{ 354251881Speter svn_stream_t *output_stream; 355251881Speter const char *header_encoding; 356251881Speter source_tokens_t sources[2]; /* 0 == original; 1 == modified */ 357251881Speter apr_off_t current_token[2]; /* current token per source */ 358251881Speter 359251881Speter /* Cached markers, in header_encoding, 360251881Speter indexed using unified_output_e */ 361251881Speter const char *prefix_str[3]; 362251881Speter 363251881Speter svn_stringbuf_t *hunk; /* in-progress hunk data */ 364251881Speter apr_off_t hunk_length[2]; /* 0 == original; 1 == modified */ 365251881Speter apr_off_t hunk_start[2]; /* 0 == original; 1 == modified */ 366251881Speter 367251881Speter /* The delimiters of the hunk header, '@@' for text hunks and '##' for 368251881Speter * property hunks. */ 369251881Speter const char *hunk_delimiter; 370251881Speter /* The string to print after a line that does not end with a newline. 371251881Speter * It must start with a '\'. Typically "\ No newline at end of file". */ 372251881Speter const char *no_newline_string; 373251881Speter 374251881Speter /* Pool for allocation of temporary memory in the callbacks 375251881Speter Should be cleared on entry of each iteration of a callback */ 376251881Speter apr_pool_t *pool; 377251881Speter} output_baton_t; 378251881Speter 379251881Speter 380251881Speter/* Append tokens (lines) FIRST up to PAST_LAST 381251881Speter from token-source index TOKENS with change-type TYPE 382251881Speter to the current hunk. 383251881Speter*/ 384251881Speterstatic svn_error_t * 385251881Speteroutput_unified_token_range(output_baton_t *btn, 386251881Speter int tokens, 387251881Speter unified_output_e type, 388251881Speter apr_off_t until) 389251881Speter{ 390251881Speter source_tokens_t *source = &btn->sources[tokens]; 391251881Speter 392251881Speter if (until > source->tokens->nelts) 393251881Speter until = source->tokens->nelts; 394251881Speter 395251881Speter if (until <= btn->current_token[tokens]) 396251881Speter return SVN_NO_ERROR; 397251881Speter 398251881Speter /* Do the loop with prefix and token */ 399251881Speter while (TRUE) 400251881Speter { 401251881Speter svn_string_t *token = 402251881Speter APR_ARRAY_IDX(source->tokens, btn->current_token[tokens], 403251881Speter svn_string_t *); 404251881Speter 405251881Speter if (type != unified_output_skip) 406251881Speter { 407251881Speter svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]); 408251881Speter svn_stringbuf_appendbytes(btn->hunk, token->data, token->len); 409251881Speter } 410251881Speter 411251881Speter if (type == unified_output_context) 412251881Speter { 413251881Speter btn->hunk_length[0]++; 414251881Speter btn->hunk_length[1]++; 415251881Speter } 416251881Speter else if (type == unified_output_delete) 417251881Speter btn->hunk_length[0]++; 418251881Speter else if (type == unified_output_insert) 419251881Speter btn->hunk_length[1]++; 420251881Speter 421251881Speter /* ### TODO: Add skip processing for -p handling? */ 422251881Speter 423251881Speter btn->current_token[tokens]++; 424251881Speter if (btn->current_token[tokens] == until) 425251881Speter break; 426251881Speter } 427251881Speter 428251881Speter if (btn->current_token[tokens] == source->tokens->nelts 429251881Speter && source->ends_without_eol) 430251881Speter { 431251881Speter const char *out_str; 432251881Speter 433251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2( 434251881Speter &out_str, btn->no_newline_string, 435251881Speter btn->header_encoding, btn->pool)); 436251881Speter svn_stringbuf_appendcstr(btn->hunk, out_str); 437251881Speter } 438251881Speter 439251881Speter 440251881Speter 441251881Speter return SVN_NO_ERROR; 442251881Speter} 443251881Speter 444251881Speter/* Flush the hunk currently built up in BATON 445251881Speter into the BATON's output_stream. 446251881Speter Use the specified HUNK_DELIMITER. 447251881Speter If HUNK_DELIMITER is NULL, fall back to the default delimiter. */ 448251881Speterstatic svn_error_t * 449251881Speteroutput_unified_flush_hunk(output_baton_t *baton, 450251881Speter const char *hunk_delimiter) 451251881Speter{ 452251881Speter apr_off_t target_token; 453251881Speter apr_size_t hunk_len; 454251881Speter apr_off_t old_start; 455251881Speter apr_off_t new_start; 456251881Speter 457251881Speter if (svn_stringbuf_isempty(baton->hunk)) 458251881Speter return SVN_NO_ERROR; 459251881Speter 460251881Speter svn_pool_clear(baton->pool); 461251881Speter 462251881Speter /* Write the trailing context */ 463251881Speter target_token = baton->hunk_start[0] + baton->hunk_length[0] 464251881Speter + SVN_DIFF__UNIFIED_CONTEXT_SIZE; 465251881Speter SVN_ERR(output_unified_token_range(baton, 0 /*original*/, 466251881Speter unified_output_context, 467251881Speter target_token)); 468251881Speter if (hunk_delimiter == NULL) 469251881Speter hunk_delimiter = "@@"; 470251881Speter 471251881Speter old_start = baton->hunk_start[0]; 472251881Speter new_start = baton->hunk_start[1]; 473251881Speter 474251881Speter /* If the file is non-empty, convert the line indexes from 475251881Speter zero based to one based */ 476251881Speter if (baton->hunk_length[0]) 477251881Speter old_start++; 478251881Speter if (baton->hunk_length[1]) 479251881Speter new_start++; 480251881Speter 481251881Speter /* Write the hunk header */ 482251881Speter SVN_ERR(svn_diff__unified_write_hunk_header( 483251881Speter baton->output_stream, baton->header_encoding, hunk_delimiter, 484251881Speter old_start, baton->hunk_length[0], 485251881Speter new_start, baton->hunk_length[1], 486251881Speter NULL /* hunk_extra_context */, 487251881Speter baton->pool)); 488251881Speter 489251881Speter hunk_len = baton->hunk->len; 490251881Speter SVN_ERR(svn_stream_write(baton->output_stream, 491251881Speter baton->hunk->data, &hunk_len)); 492251881Speter 493251881Speter /* Prepare for the next hunk */ 494251881Speter baton->hunk_length[0] = 0; 495251881Speter baton->hunk_length[1] = 0; 496251881Speter baton->hunk_start[0] = 0; 497251881Speter baton->hunk_start[1] = 0; 498251881Speter svn_stringbuf_setempty(baton->hunk); 499251881Speter 500251881Speter return SVN_NO_ERROR; 501251881Speter} 502251881Speter 503251881Speter/* Implements svn_diff_output_fns_t::output_diff_modified */ 504251881Speterstatic svn_error_t * 505251881Speteroutput_unified_diff_modified(void *baton, 506251881Speter apr_off_t original_start, 507251881Speter apr_off_t original_length, 508251881Speter apr_off_t modified_start, 509251881Speter apr_off_t modified_length, 510251881Speter apr_off_t latest_start, 511251881Speter apr_off_t latest_length) 512251881Speter{ 513251881Speter output_baton_t *output_baton = baton; 514251881Speter apr_off_t context_prefix_length; 515251881Speter apr_off_t prev_context_end; 516251881Speter svn_boolean_t init_hunk = FALSE; 517251881Speter 518251881Speter if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE) 519251881Speter context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE; 520251881Speter else 521251881Speter context_prefix_length = original_start; 522251881Speter 523251881Speter /* Calculate where the previous hunk will end if we would write it now 524251881Speter (including the necessary context at the end) */ 525251881Speter if (output_baton->hunk_length[0] > 0 || output_baton->hunk_length[1] > 0) 526251881Speter { 527251881Speter prev_context_end = output_baton->hunk_start[0] 528251881Speter + output_baton->hunk_length[0] 529251881Speter + SVN_DIFF__UNIFIED_CONTEXT_SIZE; 530251881Speter } 531251881Speter else 532251881Speter { 533251881Speter prev_context_end = -1; 534251881Speter 535251881Speter if (output_baton->hunk_start[0] == 0 536251881Speter && (original_length > 0 || modified_length > 0)) 537251881Speter init_hunk = TRUE; 538251881Speter } 539251881Speter 540251881Speter /* If the changed range is far enough from the previous range, flush the current 541251881Speter hunk. */ 542251881Speter { 543251881Speter apr_off_t new_hunk_start = (original_start - context_prefix_length); 544251881Speter 545251881Speter if (output_baton->current_token[0] < new_hunk_start 546251881Speter && prev_context_end <= new_hunk_start) 547251881Speter { 548251881Speter SVN_ERR(output_unified_flush_hunk(output_baton, 549251881Speter output_baton->hunk_delimiter)); 550251881Speter init_hunk = TRUE; 551251881Speter } 552251881Speter else if (output_baton->hunk_length[0] > 0 553251881Speter || output_baton->hunk_length[1] > 0) 554251881Speter { 555251881Speter /* We extend the current hunk */ 556251881Speter 557251881Speter /* Original: Output the context preceding the changed range */ 558251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 559251881Speter unified_output_context, 560251881Speter original_start)); 561251881Speter } 562251881Speter } 563251881Speter 564251881Speter /* Original: Skip lines until we are at the beginning of the context we want 565251881Speter to display */ 566251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 567251881Speter unified_output_skip, 568251881Speter original_start - context_prefix_length)); 569251881Speter 570251881Speter if (init_hunk) 571251881Speter { 572251881Speter SVN_ERR_ASSERT(output_baton->hunk_length[0] == 0 573251881Speter && output_baton->hunk_length[1] == 0); 574251881Speter 575251881Speter output_baton->hunk_start[0] = original_start - context_prefix_length; 576251881Speter output_baton->hunk_start[1] = modified_start - context_prefix_length; 577251881Speter } 578251881Speter 579251881Speter /* Modified: Skip lines until we are at the start of the changed range */ 580251881Speter SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */, 581251881Speter unified_output_skip, 582251881Speter modified_start)); 583251881Speter 584251881Speter /* Original: Output the context preceding the changed range */ 585251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 586251881Speter unified_output_context, 587251881Speter original_start)); 588251881Speter 589251881Speter /* Both: Output the changed range */ 590251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 591251881Speter unified_output_delete, 592251881Speter original_start + original_length)); 593251881Speter SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */, 594251881Speter unified_output_insert, 595251881Speter modified_start + modified_length)); 596251881Speter 597251881Speter return SVN_NO_ERROR; 598251881Speter} 599251881Speter 600251881Speterstatic const svn_diff_output_fns_t mem_output_unified_vtable = 601251881Speter{ 602251881Speter NULL, /* output_common */ 603251881Speter output_unified_diff_modified, 604251881Speter NULL, /* output_diff_latest */ 605251881Speter NULL, /* output_diff_common */ 606251881Speter NULL /* output_conflict */ 607251881Speter}; 608251881Speter 609251881Speter 610251881Spetersvn_error_t * 611251881Spetersvn_diff_mem_string_output_unified2(svn_stream_t *output_stream, 612251881Speter svn_diff_t *diff, 613251881Speter svn_boolean_t with_diff_header, 614251881Speter const char *hunk_delimiter, 615251881Speter const char *original_header, 616251881Speter const char *modified_header, 617251881Speter const char *header_encoding, 618251881Speter const svn_string_t *original, 619251881Speter const svn_string_t *modified, 620251881Speter apr_pool_t *pool) 621251881Speter{ 622251881Speter 623251881Speter if (svn_diff_contains_diffs(diff)) 624251881Speter { 625251881Speter output_baton_t baton; 626251881Speter 627251881Speter memset(&baton, 0, sizeof(baton)); 628251881Speter baton.output_stream = output_stream; 629251881Speter baton.pool = svn_pool_create(pool); 630251881Speter baton.header_encoding = header_encoding; 631251881Speter baton.hunk = svn_stringbuf_create_empty(pool); 632251881Speter baton.hunk_delimiter = hunk_delimiter; 633251881Speter baton.no_newline_string 634251881Speter = (hunk_delimiter == NULL || strcmp(hunk_delimiter, "##") != 0) 635251881Speter ? APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR 636251881Speter : APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_PROPERTY APR_EOL_STR; 637251881Speter 638251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 639251881Speter (&(baton.prefix_str[unified_output_context]), " ", 640251881Speter header_encoding, pool)); 641251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 642251881Speter (&(baton.prefix_str[unified_output_delete]), "-", 643251881Speter header_encoding, pool)); 644251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 645251881Speter (&(baton.prefix_str[unified_output_insert]), "+", 646251881Speter header_encoding, pool)); 647251881Speter 648251881Speter fill_source_tokens(&baton.sources[0], original, pool); 649251881Speter fill_source_tokens(&baton.sources[1], modified, pool); 650251881Speter 651251881Speter if (with_diff_header) 652251881Speter { 653251881Speter SVN_ERR(svn_diff__unidiff_write_header( 654251881Speter output_stream, header_encoding, 655251881Speter original_header, modified_header, pool)); 656251881Speter } 657251881Speter 658251881Speter SVN_ERR(svn_diff_output(diff, &baton, 659251881Speter &mem_output_unified_vtable)); 660251881Speter 661251881Speter SVN_ERR(output_unified_flush_hunk(&baton, hunk_delimiter)); 662251881Speter 663251881Speter svn_pool_destroy(baton.pool); 664251881Speter } 665251881Speter 666251881Speter return SVN_NO_ERROR; 667251881Speter} 668251881Speter 669251881Spetersvn_error_t * 670251881Spetersvn_diff_mem_string_output_unified(svn_stream_t *output_stream, 671251881Speter svn_diff_t *diff, 672251881Speter const char *original_header, 673251881Speter const char *modified_header, 674251881Speter const char *header_encoding, 675251881Speter const svn_string_t *original, 676251881Speter const svn_string_t *modified, 677251881Speter apr_pool_t *pool) 678251881Speter{ 679251881Speter SVN_ERR(svn_diff_mem_string_output_unified2(output_stream, 680251881Speter diff, 681251881Speter TRUE, 682251881Speter NULL, 683251881Speter original_header, 684251881Speter modified_header, 685251881Speter header_encoding, 686251881Speter original, 687251881Speter modified, 688251881Speter pool)); 689251881Speter return SVN_NO_ERROR; 690251881Speter} 691251881Speter 692251881Speter 693251881Speter 694251881Speter/* diff3 merge output */ 695251881Speter 696251881Speter/* A stream to remember *leading* context. Note that this stream does 697251881Speter *not* copy the data that it is remembering; it just saves 698251881Speter *pointers! */ 699251881Spetertypedef struct context_saver_t { 700251881Speter svn_stream_t *stream; 701251881Speter const char *data[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; 702251881Speter apr_size_t len[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; 703251881Speter apr_size_t next_slot; 704251881Speter apr_size_t total_written; 705251881Speter} context_saver_t; 706251881Speter 707251881Speter 708251881Speterstatic svn_error_t * 709251881Spetercontext_saver_stream_write(void *baton, 710251881Speter const char *data, 711251881Speter apr_size_t *len) 712251881Speter{ 713251881Speter context_saver_t *cs = baton; 714251881Speter cs->data[cs->next_slot] = data; 715251881Speter cs->len[cs->next_slot] = *len; 716251881Speter cs->next_slot = (cs->next_slot + 1) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; 717251881Speter cs->total_written++; 718251881Speter return SVN_NO_ERROR; 719251881Speter} 720251881Speter 721251881Speter 722251881Spetertypedef struct merge_output_baton_t 723251881Speter{ 724251881Speter svn_stream_t *output_stream; 725251881Speter 726251881Speter /* Tokenized source text */ 727251881Speter source_tokens_t sources[3]; 728251881Speter apr_off_t next_token[3]; 729251881Speter 730251881Speter /* Markers for marking conflicted sections */ 731251881Speter const char *markers[4]; /* 0 = original, 1 = modified, 732251881Speter 2 = separator, 3 = latest (end) */ 733251881Speter const char *marker_eol; 734251881Speter 735251881Speter svn_diff_conflict_display_style_t conflict_style; 736251881Speter 737251881Speter /* The rest of the fields are for 738251881Speter svn_diff_conflict_display_only_conflicts only. Note that for 739251881Speter these batons, OUTPUT_STREAM is either CONTEXT_SAVER->STREAM or 740251881Speter (soon after a conflict) a "trailing context stream", never the 741251881Speter actual output stream.*/ 742251881Speter /* The actual output stream. */ 743251881Speter svn_stream_t *real_output_stream; 744251881Speter context_saver_t *context_saver; 745251881Speter /* Used to allocate context_saver and trailing context streams, and 746251881Speter for some printfs. */ 747251881Speter apr_pool_t *pool; 748251881Speter} merge_output_baton_t; 749251881Speter 750251881Speter 751251881Speterstatic svn_error_t * 752251881Speterflush_context_saver(context_saver_t *cs, 753251881Speter svn_stream_t *output_stream) 754251881Speter{ 755251881Speter int i; 756251881Speter for (i = 0; i < SVN_DIFF__UNIFIED_CONTEXT_SIZE; i++) 757251881Speter { 758251881Speter apr_size_t slot = (i + cs->next_slot) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; 759251881Speter if (cs->data[slot]) 760251881Speter { 761251881Speter apr_size_t len = cs->len[slot]; 762251881Speter SVN_ERR(svn_stream_write(output_stream, cs->data[slot], &len)); 763251881Speter } 764251881Speter } 765251881Speter return SVN_NO_ERROR; 766251881Speter} 767251881Speter 768251881Speter 769251881Speterstatic void 770251881Spetermake_context_saver(merge_output_baton_t *mob) 771251881Speter{ 772251881Speter context_saver_t *cs; 773251881Speter 774251881Speter svn_pool_clear(mob->pool); 775251881Speter cs = apr_pcalloc(mob->pool, sizeof(*cs)); 776251881Speter cs->stream = svn_stream_empty(mob->pool); 777251881Speter svn_stream_set_baton(cs->stream, cs); 778251881Speter svn_stream_set_write(cs->stream, context_saver_stream_write); 779251881Speter mob->context_saver = cs; 780251881Speter mob->output_stream = cs->stream; 781251881Speter} 782251881Speter 783251881Speter 784251881Speter/* A stream which prints SVN_DIFF__UNIFIED_CONTEXT_SIZE lines to 785251881Speter BATON->REAL_OUTPUT_STREAM, and then changes BATON->OUTPUT_STREAM to 786251881Speter a context_saver; used for *trailing* context. */ 787251881Speter 788251881Speterstruct trailing_context_printer { 789251881Speter apr_size_t lines_to_print; 790251881Speter merge_output_baton_t *mob; 791251881Speter}; 792251881Speter 793251881Speter 794251881Speterstatic svn_error_t * 795251881Spetertrailing_context_printer_write(void *baton, 796251881Speter const char *data, 797251881Speter apr_size_t *len) 798251881Speter{ 799251881Speter struct trailing_context_printer *tcp = baton; 800251881Speter SVN_ERR_ASSERT(tcp->lines_to_print > 0); 801251881Speter SVN_ERR(svn_stream_write(tcp->mob->real_output_stream, data, len)); 802251881Speter tcp->lines_to_print--; 803251881Speter if (tcp->lines_to_print == 0) 804251881Speter make_context_saver(tcp->mob); 805251881Speter return SVN_NO_ERROR; 806251881Speter} 807251881Speter 808251881Speter 809251881Speterstatic void 810251881Spetermake_trailing_context_printer(merge_output_baton_t *btn) 811251881Speter{ 812251881Speter struct trailing_context_printer *tcp; 813251881Speter svn_stream_t *s; 814251881Speter 815251881Speter svn_pool_clear(btn->pool); 816251881Speter 817251881Speter tcp = apr_pcalloc(btn->pool, sizeof(*tcp)); 818251881Speter tcp->lines_to_print = SVN_DIFF__UNIFIED_CONTEXT_SIZE; 819251881Speter tcp->mob = btn; 820251881Speter s = svn_stream_empty(btn->pool); 821251881Speter svn_stream_set_baton(s, tcp); 822251881Speter svn_stream_set_write(s, trailing_context_printer_write); 823251881Speter btn->output_stream = s; 824251881Speter} 825251881Speter 826251881Speter 827251881Speterstatic svn_error_t * 828251881Speteroutput_merge_token_range(apr_size_t *lines_printed_p, 829251881Speter merge_output_baton_t *btn, 830251881Speter int idx, apr_off_t first, 831251881Speter apr_off_t length) 832251881Speter{ 833251881Speter apr_array_header_t *tokens = btn->sources[idx].tokens; 834251881Speter apr_size_t lines_printed = 0; 835251881Speter 836251881Speter for (; length > 0 && first < tokens->nelts; length--, first++) 837251881Speter { 838251881Speter svn_string_t *token = APR_ARRAY_IDX(tokens, first, svn_string_t *); 839251881Speter apr_size_t len = token->len; 840251881Speter 841251881Speter /* Note that the trailing context printer assumes that 842251881Speter svn_stream_write is called exactly once per line. */ 843251881Speter SVN_ERR(svn_stream_write(btn->output_stream, token->data, &len)); 844251881Speter lines_printed++; 845251881Speter } 846251881Speter 847251881Speter if (lines_printed_p) 848251881Speter *lines_printed_p = lines_printed; 849251881Speter 850251881Speter return SVN_NO_ERROR; 851251881Speter} 852251881Speter 853251881Speterstatic svn_error_t * 854251881Speteroutput_marker_eol(merge_output_baton_t *btn) 855251881Speter{ 856251881Speter return svn_stream_puts(btn->output_stream, btn->marker_eol); 857251881Speter} 858251881Speter 859251881Speterstatic svn_error_t * 860251881Speteroutput_merge_marker(merge_output_baton_t *btn, int idx) 861251881Speter{ 862251881Speter SVN_ERR(svn_stream_puts(btn->output_stream, btn->markers[idx])); 863251881Speter return output_marker_eol(btn); 864251881Speter} 865251881Speter 866251881Speterstatic svn_error_t * 867251881Speteroutput_common_modified(void *baton, 868251881Speter apr_off_t original_start, apr_off_t original_length, 869251881Speter apr_off_t modified_start, apr_off_t modified_length, 870251881Speter apr_off_t latest_start, apr_off_t latest_length) 871251881Speter{ 872251881Speter return output_merge_token_range(NULL, baton, 1/*modified*/, 873251881Speter modified_start, modified_length); 874251881Speter} 875251881Speter 876251881Speterstatic svn_error_t * 877251881Speteroutput_latest(void *baton, 878251881Speter apr_off_t original_start, apr_off_t original_length, 879251881Speter apr_off_t modified_start, apr_off_t modified_length, 880251881Speter apr_off_t latest_start, apr_off_t latest_length) 881251881Speter{ 882251881Speter return output_merge_token_range(NULL, baton, 2/*latest*/, 883251881Speter latest_start, latest_length); 884251881Speter} 885251881Speter 886251881Speterstatic svn_error_t * 887251881Speteroutput_conflict(void *baton, 888251881Speter apr_off_t original_start, apr_off_t original_length, 889251881Speter apr_off_t modified_start, apr_off_t modified_length, 890251881Speter apr_off_t latest_start, apr_off_t latest_length, 891251881Speter svn_diff_t *diff); 892251881Speter 893251881Speterstatic const svn_diff_output_fns_t merge_output_vtable = 894251881Speter{ 895251881Speter output_common_modified, /* common */ 896251881Speter output_common_modified, /* modified */ 897251881Speter output_latest, 898251881Speter output_common_modified, /* output_diff_common */ 899251881Speter output_conflict 900251881Speter}; 901251881Speter 902251881Speterstatic svn_error_t * 903251881Speteroutput_conflict(void *baton, 904251881Speter apr_off_t original_start, apr_off_t original_length, 905251881Speter apr_off_t modified_start, apr_off_t modified_length, 906251881Speter apr_off_t latest_start, apr_off_t latest_length, 907251881Speter svn_diff_t *diff) 908251881Speter{ 909251881Speter merge_output_baton_t *btn = baton; 910251881Speter 911251881Speter svn_diff_conflict_display_style_t style = btn->conflict_style; 912251881Speter 913251881Speter if (style == svn_diff_conflict_display_resolved_modified_latest) 914251881Speter { 915251881Speter if (diff) 916251881Speter return svn_diff_output(diff, baton, &merge_output_vtable); 917251881Speter else 918251881Speter style = svn_diff_conflict_display_modified_latest; 919251881Speter } 920251881Speter 921251881Speter if (style == svn_diff_conflict_display_modified_latest || 922251881Speter style == svn_diff_conflict_display_modified_original_latest) 923251881Speter { 924251881Speter SVN_ERR(output_merge_marker(btn, 1/*modified*/)); 925251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 926251881Speter modified_start, modified_length)); 927251881Speter 928251881Speter if (style == svn_diff_conflict_display_modified_original_latest) 929251881Speter { 930251881Speter SVN_ERR(output_merge_marker(btn, 0/*original*/)); 931251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 0/*original*/, 932251881Speter original_start, original_length)); 933251881Speter } 934251881Speter 935251881Speter SVN_ERR(output_merge_marker(btn, 2/*separator*/)); 936251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 937251881Speter latest_start, latest_length)); 938251881Speter SVN_ERR(output_merge_marker(btn, 3/*latest (end)*/)); 939251881Speter } 940251881Speter else if (style == svn_diff_conflict_display_modified) 941251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 942251881Speter modified_start, modified_length)); 943251881Speter else if (style == svn_diff_conflict_display_latest) 944251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 945251881Speter latest_start, latest_length)); 946251881Speter else /* unknown style */ 947251881Speter SVN_ERR_MALFUNCTION(); 948251881Speter 949251881Speter return SVN_NO_ERROR; 950251881Speter} 951251881Speter 952251881Speter 953251881Speterstatic svn_error_t * 954251881Speteroutput_conflict_with_context(void *baton, 955251881Speter apr_off_t original_start, 956251881Speter apr_off_t original_length, 957251881Speter apr_off_t modified_start, 958251881Speter apr_off_t modified_length, 959251881Speter apr_off_t latest_start, 960251881Speter apr_off_t latest_length, 961251881Speter svn_diff_t *diff) 962251881Speter{ 963251881Speter merge_output_baton_t *btn = baton; 964251881Speter 965251881Speter /* Are we currently saving starting context (as opposed to printing 966251881Speter trailing context)? If so, flush it. */ 967251881Speter if (btn->output_stream == btn->context_saver->stream) 968251881Speter { 969251881Speter if (btn->context_saver->total_written > SVN_DIFF__UNIFIED_CONTEXT_SIZE) 970251881Speter SVN_ERR(svn_stream_puts(btn->real_output_stream, "@@\n")); 971251881Speter SVN_ERR(flush_context_saver(btn->context_saver, btn->real_output_stream)); 972251881Speter } 973251881Speter 974251881Speter /* Print to the real output stream. */ 975251881Speter btn->output_stream = btn->real_output_stream; 976251881Speter 977251881Speter /* Output the conflict itself. */ 978251881Speter SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, 979251881Speter (modified_length == 1 980251881Speter ? "%s (%" APR_OFF_T_FMT ")" 981251881Speter : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), 982251881Speter btn->markers[1], 983251881Speter modified_start + 1, modified_length)); 984251881Speter SVN_ERR(output_marker_eol(btn)); 985251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 986251881Speter modified_start, modified_length)); 987251881Speter 988251881Speter SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, 989251881Speter (original_length == 1 990251881Speter ? "%s (%" APR_OFF_T_FMT ")" 991251881Speter : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), 992251881Speter btn->markers[0], 993251881Speter original_start + 1, original_length)); 994251881Speter SVN_ERR(output_marker_eol(btn)); 995251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 0/*original*/, 996251881Speter original_start, original_length)); 997251881Speter 998251881Speter SVN_ERR(output_merge_marker(btn, 2/*separator*/)); 999251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 1000251881Speter latest_start, latest_length)); 1001251881Speter SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, 1002251881Speter (latest_length == 1 1003251881Speter ? "%s (%" APR_OFF_T_FMT ")" 1004251881Speter : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), 1005251881Speter btn->markers[3], 1006251881Speter latest_start + 1, latest_length)); 1007251881Speter SVN_ERR(output_marker_eol(btn)); 1008251881Speter 1009251881Speter /* Go into print-trailing-context mode instead. */ 1010251881Speter make_trailing_context_printer(btn); 1011251881Speter 1012251881Speter return SVN_NO_ERROR; 1013251881Speter} 1014251881Speter 1015251881Speter 1016251881Speterstatic const svn_diff_output_fns_t merge_only_conflicts_output_vtable = 1017251881Speter{ 1018251881Speter output_common_modified, 1019251881Speter output_common_modified, 1020251881Speter output_latest, 1021251881Speter output_common_modified, 1022251881Speter output_conflict_with_context 1023251881Speter}; 1024251881Speter 1025251881Speter 1026251881Speter/* TOKEN is the first token in the modified file. 1027251881Speter Return its line-ending, if any. */ 1028251881Speterstatic const char * 1029251881Speterdetect_eol(svn_string_t *token) 1030251881Speter{ 1031251881Speter const char *curp; 1032251881Speter 1033251881Speter if (token->len == 0) 1034251881Speter return NULL; 1035251881Speter 1036251881Speter curp = token->data + token->len - 1; 1037251881Speter if (*curp == '\r') 1038251881Speter return "\r"; 1039251881Speter else if (*curp != '\n') 1040251881Speter return NULL; 1041251881Speter else 1042251881Speter { 1043251881Speter if (token->len == 1 1044251881Speter || *(--curp) != '\r') 1045251881Speter return "\n"; 1046251881Speter else 1047251881Speter return "\r\n"; 1048251881Speter } 1049251881Speter} 1050251881Speter 1051251881Spetersvn_error_t * 1052251881Spetersvn_diff_mem_string_output_merge2(svn_stream_t *output_stream, 1053251881Speter svn_diff_t *diff, 1054251881Speter const svn_string_t *original, 1055251881Speter const svn_string_t *modified, 1056251881Speter const svn_string_t *latest, 1057251881Speter const char *conflict_original, 1058251881Speter const char *conflict_modified, 1059251881Speter const char *conflict_latest, 1060251881Speter const char *conflict_separator, 1061251881Speter svn_diff_conflict_display_style_t style, 1062251881Speter apr_pool_t *pool) 1063251881Speter{ 1064251881Speter merge_output_baton_t btn; 1065251881Speter const char *eol; 1066251881Speter svn_boolean_t conflicts_only = 1067251881Speter (style == svn_diff_conflict_display_only_conflicts); 1068251881Speter const svn_diff_output_fns_t *vtable = conflicts_only 1069251881Speter ? &merge_only_conflicts_output_vtable : &merge_output_vtable; 1070251881Speter 1071251881Speter memset(&btn, 0, sizeof(btn)); 1072251881Speter 1073251881Speter if (conflicts_only) 1074251881Speter { 1075251881Speter btn.pool = svn_pool_create(pool); 1076251881Speter make_context_saver(&btn); 1077251881Speter btn.real_output_stream = output_stream; 1078251881Speter } 1079251881Speter else 1080251881Speter btn.output_stream = output_stream; 1081251881Speter 1082251881Speter fill_source_tokens(&(btn.sources[0]), original, pool); 1083251881Speter fill_source_tokens(&(btn.sources[1]), modified, pool); 1084251881Speter fill_source_tokens(&(btn.sources[2]), latest, pool); 1085251881Speter 1086251881Speter btn.conflict_style = style; 1087251881Speter 1088251881Speter if (btn.sources[1].tokens->nelts > 0) 1089251881Speter { 1090251881Speter eol = detect_eol(APR_ARRAY_IDX(btn.sources[1].tokens, 0, svn_string_t *)); 1091251881Speter if (!eol) 1092251881Speter eol = APR_EOL_STR; /* use the platform default */ 1093251881Speter } 1094251881Speter else 1095251881Speter eol = APR_EOL_STR; /* use the platform default */ 1096251881Speter 1097251881Speter btn.marker_eol = eol; 1098251881Speter 1099251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[1], 1100251881Speter conflict_modified 1101251881Speter ? conflict_modified 1102251881Speter : "<<<<<<< (modified)", 1103251881Speter pool)); 1104251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[0], 1105251881Speter conflict_original 1106251881Speter ? conflict_original 1107251881Speter : "||||||| (original)", 1108251881Speter pool)); 1109251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[2], 1110251881Speter conflict_separator 1111251881Speter ? conflict_separator 1112251881Speter : "=======", 1113251881Speter pool)); 1114251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[3], 1115251881Speter conflict_latest 1116251881Speter ? conflict_latest 1117251881Speter : ">>>>>>> (latest)", 1118251881Speter pool)); 1119251881Speter 1120251881Speter SVN_ERR(svn_diff_output(diff, &btn, vtable)); 1121251881Speter if (conflicts_only) 1122251881Speter svn_pool_destroy(btn.pool); 1123251881Speter 1124251881Speter return SVN_NO_ERROR; 1125251881Speter} 1126251881Speter 1127251881Spetersvn_error_t * 1128251881Spetersvn_diff_mem_string_output_merge(svn_stream_t *output_stream, 1129251881Speter svn_diff_t *diff, 1130251881Speter const svn_string_t *original, 1131251881Speter const svn_string_t *modified, 1132251881Speter const svn_string_t *latest, 1133251881Speter const char *conflict_original, 1134251881Speter const char *conflict_modified, 1135251881Speter const char *conflict_latest, 1136251881Speter const char *conflict_separator, 1137251881Speter svn_boolean_t display_original_in_conflict, 1138251881Speter svn_boolean_t display_resolved_conflicts, 1139251881Speter apr_pool_t *pool) 1140251881Speter{ 1141251881Speter svn_diff_conflict_display_style_t style = 1142251881Speter svn_diff_conflict_display_modified_latest; 1143251881Speter 1144251881Speter if (display_resolved_conflicts) 1145251881Speter style = svn_diff_conflict_display_resolved_modified_latest; 1146251881Speter 1147251881Speter if (display_original_in_conflict) 1148251881Speter style = svn_diff_conflict_display_modified_original_latest; 1149251881Speter 1150251881Speter return svn_diff_mem_string_output_merge2(output_stream, 1151251881Speter diff, 1152251881Speter original, 1153251881Speter modified, 1154251881Speter latest, 1155251881Speter conflict_original, 1156251881Speter conflict_modified, 1157251881Speter conflict_latest, 1158251881Speter conflict_separator, 1159251881Speter style, 1160251881Speter pool); 1161251881Speter} 1162