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 359289180Speter int context_size; 360289180Speter 361251881Speter /* Cached markers, in header_encoding, 362251881Speter indexed using unified_output_e */ 363251881Speter const char *prefix_str[3]; 364251881Speter 365251881Speter svn_stringbuf_t *hunk; /* in-progress hunk data */ 366251881Speter apr_off_t hunk_length[2]; /* 0 == original; 1 == modified */ 367251881Speter apr_off_t hunk_start[2]; /* 0 == original; 1 == modified */ 368251881Speter 369251881Speter /* The delimiters of the hunk header, '@@' for text hunks and '##' for 370251881Speter * property hunks. */ 371251881Speter const char *hunk_delimiter; 372251881Speter /* The string to print after a line that does not end with a newline. 373251881Speter * It must start with a '\'. Typically "\ No newline at end of file". */ 374251881Speter const char *no_newline_string; 375251881Speter 376251881Speter /* Pool for allocation of temporary memory in the callbacks 377251881Speter Should be cleared on entry of each iteration of a callback */ 378251881Speter apr_pool_t *pool; 379251881Speter} output_baton_t; 380251881Speter 381251881Speter 382251881Speter/* Append tokens (lines) FIRST up to PAST_LAST 383251881Speter from token-source index TOKENS with change-type TYPE 384251881Speter to the current hunk. 385251881Speter*/ 386251881Speterstatic svn_error_t * 387251881Speteroutput_unified_token_range(output_baton_t *btn, 388251881Speter int tokens, 389251881Speter unified_output_e type, 390251881Speter apr_off_t until) 391251881Speter{ 392251881Speter source_tokens_t *source = &btn->sources[tokens]; 393251881Speter 394251881Speter if (until > source->tokens->nelts) 395251881Speter until = source->tokens->nelts; 396251881Speter 397251881Speter if (until <= btn->current_token[tokens]) 398251881Speter return SVN_NO_ERROR; 399251881Speter 400251881Speter /* Do the loop with prefix and token */ 401251881Speter while (TRUE) 402251881Speter { 403251881Speter svn_string_t *token = 404251881Speter APR_ARRAY_IDX(source->tokens, btn->current_token[tokens], 405251881Speter svn_string_t *); 406251881Speter 407251881Speter if (type != unified_output_skip) 408251881Speter { 409251881Speter svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]); 410251881Speter svn_stringbuf_appendbytes(btn->hunk, token->data, token->len); 411251881Speter } 412251881Speter 413251881Speter if (type == unified_output_context) 414251881Speter { 415251881Speter btn->hunk_length[0]++; 416251881Speter btn->hunk_length[1]++; 417251881Speter } 418251881Speter else if (type == unified_output_delete) 419251881Speter btn->hunk_length[0]++; 420251881Speter else if (type == unified_output_insert) 421251881Speter btn->hunk_length[1]++; 422251881Speter 423251881Speter /* ### TODO: Add skip processing for -p handling? */ 424251881Speter 425251881Speter btn->current_token[tokens]++; 426251881Speter if (btn->current_token[tokens] == until) 427251881Speter break; 428251881Speter } 429251881Speter 430251881Speter if (btn->current_token[tokens] == source->tokens->nelts 431251881Speter && source->ends_without_eol) 432251881Speter { 433251881Speter const char *out_str; 434251881Speter 435251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2( 436251881Speter &out_str, btn->no_newline_string, 437251881Speter btn->header_encoding, btn->pool)); 438251881Speter svn_stringbuf_appendcstr(btn->hunk, out_str); 439251881Speter } 440251881Speter 441251881Speter 442251881Speter 443251881Speter return SVN_NO_ERROR; 444251881Speter} 445251881Speter 446251881Speter/* Flush the hunk currently built up in BATON 447251881Speter into the BATON's output_stream. 448251881Speter Use the specified HUNK_DELIMITER. 449251881Speter If HUNK_DELIMITER is NULL, fall back to the default delimiter. */ 450251881Speterstatic svn_error_t * 451251881Speteroutput_unified_flush_hunk(output_baton_t *baton, 452251881Speter const char *hunk_delimiter) 453251881Speter{ 454251881Speter apr_off_t target_token; 455251881Speter apr_size_t hunk_len; 456251881Speter apr_off_t old_start; 457251881Speter apr_off_t new_start; 458251881Speter 459251881Speter if (svn_stringbuf_isempty(baton->hunk)) 460251881Speter return SVN_NO_ERROR; 461251881Speter 462251881Speter svn_pool_clear(baton->pool); 463251881Speter 464251881Speter /* Write the trailing context */ 465251881Speter target_token = baton->hunk_start[0] + baton->hunk_length[0] 466289180Speter + baton->context_size; 467251881Speter SVN_ERR(output_unified_token_range(baton, 0 /*original*/, 468251881Speter unified_output_context, 469251881Speter target_token)); 470251881Speter if (hunk_delimiter == NULL) 471251881Speter hunk_delimiter = "@@"; 472251881Speter 473251881Speter old_start = baton->hunk_start[0]; 474251881Speter new_start = baton->hunk_start[1]; 475251881Speter 476251881Speter /* If the file is non-empty, convert the line indexes from 477251881Speter zero based to one based */ 478251881Speter if (baton->hunk_length[0]) 479251881Speter old_start++; 480251881Speter if (baton->hunk_length[1]) 481251881Speter new_start++; 482251881Speter 483251881Speter /* Write the hunk header */ 484251881Speter SVN_ERR(svn_diff__unified_write_hunk_header( 485251881Speter baton->output_stream, baton->header_encoding, hunk_delimiter, 486251881Speter old_start, baton->hunk_length[0], 487251881Speter new_start, baton->hunk_length[1], 488251881Speter NULL /* hunk_extra_context */, 489251881Speter baton->pool)); 490251881Speter 491251881Speter hunk_len = baton->hunk->len; 492251881Speter SVN_ERR(svn_stream_write(baton->output_stream, 493251881Speter baton->hunk->data, &hunk_len)); 494251881Speter 495251881Speter /* Prepare for the next hunk */ 496251881Speter baton->hunk_length[0] = 0; 497251881Speter baton->hunk_length[1] = 0; 498251881Speter baton->hunk_start[0] = 0; 499251881Speter baton->hunk_start[1] = 0; 500251881Speter svn_stringbuf_setempty(baton->hunk); 501251881Speter 502251881Speter return SVN_NO_ERROR; 503251881Speter} 504251881Speter 505251881Speter/* Implements svn_diff_output_fns_t::output_diff_modified */ 506251881Speterstatic svn_error_t * 507251881Speteroutput_unified_diff_modified(void *baton, 508251881Speter apr_off_t original_start, 509251881Speter apr_off_t original_length, 510251881Speter apr_off_t modified_start, 511251881Speter apr_off_t modified_length, 512251881Speter apr_off_t latest_start, 513251881Speter apr_off_t latest_length) 514251881Speter{ 515251881Speter output_baton_t *output_baton = baton; 516251881Speter apr_off_t context_prefix_length; 517251881Speter apr_off_t prev_context_end; 518251881Speter svn_boolean_t init_hunk = FALSE; 519251881Speter 520289180Speter if (original_start > output_baton->context_size) 521289180Speter context_prefix_length = output_baton->context_size; 522251881Speter else 523251881Speter context_prefix_length = original_start; 524251881Speter 525251881Speter /* Calculate where the previous hunk will end if we would write it now 526251881Speter (including the necessary context at the end) */ 527251881Speter if (output_baton->hunk_length[0] > 0 || output_baton->hunk_length[1] > 0) 528251881Speter { 529251881Speter prev_context_end = output_baton->hunk_start[0] 530251881Speter + output_baton->hunk_length[0] 531289180Speter + output_baton->context_size; 532251881Speter } 533251881Speter else 534251881Speter { 535251881Speter prev_context_end = -1; 536251881Speter 537251881Speter if (output_baton->hunk_start[0] == 0 538251881Speter && (original_length > 0 || modified_length > 0)) 539251881Speter init_hunk = TRUE; 540251881Speter } 541251881Speter 542251881Speter /* If the changed range is far enough from the previous range, flush the current 543251881Speter hunk. */ 544251881Speter { 545251881Speter apr_off_t new_hunk_start = (original_start - context_prefix_length); 546251881Speter 547251881Speter if (output_baton->current_token[0] < new_hunk_start 548251881Speter && prev_context_end <= new_hunk_start) 549251881Speter { 550251881Speter SVN_ERR(output_unified_flush_hunk(output_baton, 551251881Speter output_baton->hunk_delimiter)); 552251881Speter init_hunk = TRUE; 553251881Speter } 554251881Speter else if (output_baton->hunk_length[0] > 0 555251881Speter || output_baton->hunk_length[1] > 0) 556251881Speter { 557251881Speter /* We extend the current hunk */ 558251881Speter 559251881Speter /* Original: Output the context preceding the changed range */ 560251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 561251881Speter unified_output_context, 562251881Speter original_start)); 563251881Speter } 564251881Speter } 565251881Speter 566251881Speter /* Original: Skip lines until we are at the beginning of the context we want 567251881Speter to display */ 568251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 569251881Speter unified_output_skip, 570251881Speter original_start - context_prefix_length)); 571251881Speter 572251881Speter if (init_hunk) 573251881Speter { 574251881Speter SVN_ERR_ASSERT(output_baton->hunk_length[0] == 0 575251881Speter && output_baton->hunk_length[1] == 0); 576251881Speter 577251881Speter output_baton->hunk_start[0] = original_start - context_prefix_length; 578251881Speter output_baton->hunk_start[1] = modified_start - context_prefix_length; 579251881Speter } 580251881Speter 581251881Speter /* Modified: Skip lines until we are at the start of the changed range */ 582251881Speter SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */, 583251881Speter unified_output_skip, 584251881Speter modified_start)); 585251881Speter 586251881Speter /* Original: Output the context preceding the changed range */ 587251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 588251881Speter unified_output_context, 589251881Speter original_start)); 590251881Speter 591251881Speter /* Both: Output the changed range */ 592251881Speter SVN_ERR(output_unified_token_range(output_baton, 0 /* original */, 593251881Speter unified_output_delete, 594251881Speter original_start + original_length)); 595251881Speter SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */, 596251881Speter unified_output_insert, 597251881Speter modified_start + modified_length)); 598251881Speter 599251881Speter return SVN_NO_ERROR; 600251881Speter} 601251881Speter 602251881Speterstatic const svn_diff_output_fns_t mem_output_unified_vtable = 603251881Speter{ 604251881Speter NULL, /* output_common */ 605251881Speter output_unified_diff_modified, 606251881Speter NULL, /* output_diff_latest */ 607251881Speter NULL, /* output_diff_common */ 608251881Speter NULL /* output_conflict */ 609251881Speter}; 610251881Speter 611251881Speter 612251881Spetersvn_error_t * 613289180Spetersvn_diff_mem_string_output_unified3(svn_stream_t *output_stream, 614251881Speter svn_diff_t *diff, 615251881Speter svn_boolean_t with_diff_header, 616251881Speter const char *hunk_delimiter, 617251881Speter const char *original_header, 618251881Speter const char *modified_header, 619251881Speter const char *header_encoding, 620251881Speter const svn_string_t *original, 621251881Speter const svn_string_t *modified, 622289180Speter int context_size, 623289180Speter svn_cancel_func_t cancel_func, 624289180Speter void *cancel_baton, 625289180Speter apr_pool_t *scratch_pool) 626251881Speter{ 627251881Speter 628251881Speter if (svn_diff_contains_diffs(diff)) 629251881Speter { 630251881Speter output_baton_t baton; 631251881Speter 632251881Speter memset(&baton, 0, sizeof(baton)); 633251881Speter baton.output_stream = output_stream; 634289180Speter baton.pool = svn_pool_create(scratch_pool); 635251881Speter baton.header_encoding = header_encoding; 636289180Speter baton.hunk = svn_stringbuf_create_empty(scratch_pool); 637251881Speter baton.hunk_delimiter = hunk_delimiter; 638251881Speter baton.no_newline_string 639251881Speter = (hunk_delimiter == NULL || strcmp(hunk_delimiter, "##") != 0) 640251881Speter ? APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR 641251881Speter : APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_PROPERTY APR_EOL_STR; 642289180Speter baton.context_size = context_size >= 0 ? context_size 643289180Speter : SVN_DIFF__UNIFIED_CONTEXT_SIZE; 644251881Speter 645251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 646251881Speter (&(baton.prefix_str[unified_output_context]), " ", 647289180Speter header_encoding, scratch_pool)); 648251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 649251881Speter (&(baton.prefix_str[unified_output_delete]), "-", 650289180Speter header_encoding, scratch_pool)); 651251881Speter SVN_ERR(svn_utf_cstring_from_utf8_ex2 652251881Speter (&(baton.prefix_str[unified_output_insert]), "+", 653289180Speter header_encoding, scratch_pool)); 654251881Speter 655289180Speter fill_source_tokens(&baton.sources[0], original, scratch_pool); 656289180Speter fill_source_tokens(&baton.sources[1], modified, scratch_pool); 657251881Speter 658251881Speter if (with_diff_header) 659251881Speter { 660251881Speter SVN_ERR(svn_diff__unidiff_write_header( 661251881Speter output_stream, header_encoding, 662289180Speter original_header, modified_header, scratch_pool)); 663251881Speter } 664251881Speter 665289180Speter SVN_ERR(svn_diff_output2(diff, &baton, 666289180Speter &mem_output_unified_vtable, 667289180Speter cancel_func, cancel_baton)); 668251881Speter 669251881Speter SVN_ERR(output_unified_flush_hunk(&baton, hunk_delimiter)); 670251881Speter 671251881Speter svn_pool_destroy(baton.pool); 672251881Speter } 673251881Speter 674251881Speter return SVN_NO_ERROR; 675251881Speter} 676251881Speter 677251881Speter 678251881Speter 679251881Speter 680251881Speter/* diff3 merge output */ 681251881Speter 682251881Speter/* A stream to remember *leading* context. Note that this stream does 683251881Speter *not* copy the data that it is remembering; it just saves 684251881Speter *pointers! */ 685251881Spetertypedef struct context_saver_t { 686251881Speter svn_stream_t *stream; 687289180Speter int context_size; 688289180Speter const char **data; /* const char *data[context_size] */ 689289180Speter apr_size_t *len; /* apr_size_t len[context_size] */ 690251881Speter apr_size_t next_slot; 691251881Speter apr_size_t total_written; 692251881Speter} context_saver_t; 693251881Speter 694251881Speter 695251881Speterstatic svn_error_t * 696251881Spetercontext_saver_stream_write(void *baton, 697251881Speter const char *data, 698251881Speter apr_size_t *len) 699251881Speter{ 700251881Speter context_saver_t *cs = baton; 701251881Speter cs->data[cs->next_slot] = data; 702251881Speter cs->len[cs->next_slot] = *len; 703289180Speter cs->next_slot = (cs->next_slot + 1) % cs->context_size; 704251881Speter cs->total_written++; 705251881Speter return SVN_NO_ERROR; 706251881Speter} 707251881Speter 708251881Speter 709251881Spetertypedef struct merge_output_baton_t 710251881Speter{ 711251881Speter svn_stream_t *output_stream; 712251881Speter 713251881Speter /* Tokenized source text */ 714251881Speter source_tokens_t sources[3]; 715251881Speter apr_off_t next_token[3]; 716251881Speter 717251881Speter /* Markers for marking conflicted sections */ 718251881Speter const char *markers[4]; /* 0 = original, 1 = modified, 719251881Speter 2 = separator, 3 = latest (end) */ 720251881Speter const char *marker_eol; 721251881Speter 722251881Speter svn_diff_conflict_display_style_t conflict_style; 723289180Speter int context_size; 724251881Speter 725289180Speter /* cancel support */ 726289180Speter svn_cancel_func_t cancel_func; 727289180Speter void *cancel_baton; 728289180Speter 729251881Speter /* The rest of the fields are for 730251881Speter svn_diff_conflict_display_only_conflicts only. Note that for 731251881Speter these batons, OUTPUT_STREAM is either CONTEXT_SAVER->STREAM or 732251881Speter (soon after a conflict) a "trailing context stream", never the 733251881Speter actual output stream.*/ 734251881Speter /* The actual output stream. */ 735251881Speter svn_stream_t *real_output_stream; 736251881Speter context_saver_t *context_saver; 737251881Speter /* Used to allocate context_saver and trailing context streams, and 738251881Speter for some printfs. */ 739251881Speter apr_pool_t *pool; 740251881Speter} merge_output_baton_t; 741251881Speter 742251881Speter 743251881Speterstatic svn_error_t * 744251881Speterflush_context_saver(context_saver_t *cs, 745251881Speter svn_stream_t *output_stream) 746251881Speter{ 747251881Speter int i; 748289180Speter for (i = 0; i < cs->context_size; i++) 749251881Speter { 750289180Speter apr_size_t slot = (i + cs->next_slot) % cs->context_size; 751251881Speter if (cs->data[slot]) 752251881Speter { 753251881Speter apr_size_t len = cs->len[slot]; 754251881Speter SVN_ERR(svn_stream_write(output_stream, cs->data[slot], &len)); 755251881Speter } 756251881Speter } 757251881Speter return SVN_NO_ERROR; 758251881Speter} 759251881Speter 760251881Speter 761251881Speterstatic void 762251881Spetermake_context_saver(merge_output_baton_t *mob) 763251881Speter{ 764251881Speter context_saver_t *cs; 765251881Speter 766289180Speter assert(mob->context_size > 0); /* Or nothing to save */ 767289180Speter 768251881Speter svn_pool_clear(mob->pool); 769251881Speter cs = apr_pcalloc(mob->pool, sizeof(*cs)); 770251881Speter cs->stream = svn_stream_empty(mob->pool); 771251881Speter svn_stream_set_baton(cs->stream, cs); 772251881Speter svn_stream_set_write(cs->stream, context_saver_stream_write); 773251881Speter mob->context_saver = cs; 774251881Speter mob->output_stream = cs->stream; 775289180Speter cs->context_size = mob->context_size; 776289180Speter cs->data = apr_pcalloc(mob->pool, sizeof(*cs->data) * cs->context_size); 777289180Speter cs->len = apr_pcalloc(mob->pool, sizeof(*cs->len) * cs->context_size); 778251881Speter} 779251881Speter 780251881Speter 781289180Speter/* A stream which prints LINES_TO_PRINT (based on context_size) lines to 782251881Speter BATON->REAL_OUTPUT_STREAM, and then changes BATON->OUTPUT_STREAM to 783251881Speter a context_saver; used for *trailing* context. */ 784251881Speter 785251881Speterstruct trailing_context_printer { 786251881Speter apr_size_t lines_to_print; 787251881Speter merge_output_baton_t *mob; 788251881Speter}; 789251881Speter 790251881Speter 791251881Speterstatic svn_error_t * 792251881Spetertrailing_context_printer_write(void *baton, 793251881Speter const char *data, 794251881Speter apr_size_t *len) 795251881Speter{ 796251881Speter struct trailing_context_printer *tcp = baton; 797251881Speter SVN_ERR_ASSERT(tcp->lines_to_print > 0); 798251881Speter SVN_ERR(svn_stream_write(tcp->mob->real_output_stream, data, len)); 799251881Speter tcp->lines_to_print--; 800251881Speter if (tcp->lines_to_print == 0) 801251881Speter make_context_saver(tcp->mob); 802251881Speter return SVN_NO_ERROR; 803251881Speter} 804251881Speter 805251881Speter 806251881Speterstatic void 807251881Spetermake_trailing_context_printer(merge_output_baton_t *btn) 808251881Speter{ 809251881Speter struct trailing_context_printer *tcp; 810251881Speter svn_stream_t *s; 811251881Speter 812251881Speter svn_pool_clear(btn->pool); 813251881Speter 814251881Speter tcp = apr_pcalloc(btn->pool, sizeof(*tcp)); 815289180Speter tcp->lines_to_print = btn->context_size; 816251881Speter tcp->mob = btn; 817251881Speter s = svn_stream_empty(btn->pool); 818251881Speter svn_stream_set_baton(s, tcp); 819251881Speter svn_stream_set_write(s, trailing_context_printer_write); 820251881Speter btn->output_stream = s; 821251881Speter} 822251881Speter 823251881Speter 824251881Speterstatic svn_error_t * 825251881Speteroutput_merge_token_range(apr_size_t *lines_printed_p, 826251881Speter merge_output_baton_t *btn, 827251881Speter int idx, apr_off_t first, 828251881Speter apr_off_t length) 829251881Speter{ 830251881Speter apr_array_header_t *tokens = btn->sources[idx].tokens; 831251881Speter apr_size_t lines_printed = 0; 832251881Speter 833251881Speter for (; length > 0 && first < tokens->nelts; length--, first++) 834251881Speter { 835251881Speter svn_string_t *token = APR_ARRAY_IDX(tokens, first, svn_string_t *); 836251881Speter apr_size_t len = token->len; 837251881Speter 838251881Speter /* Note that the trailing context printer assumes that 839251881Speter svn_stream_write is called exactly once per line. */ 840251881Speter SVN_ERR(svn_stream_write(btn->output_stream, token->data, &len)); 841251881Speter lines_printed++; 842251881Speter } 843251881Speter 844251881Speter if (lines_printed_p) 845251881Speter *lines_printed_p = lines_printed; 846251881Speter 847251881Speter return SVN_NO_ERROR; 848251881Speter} 849251881Speter 850251881Speterstatic svn_error_t * 851251881Speteroutput_marker_eol(merge_output_baton_t *btn) 852251881Speter{ 853251881Speter return svn_stream_puts(btn->output_stream, btn->marker_eol); 854251881Speter} 855251881Speter 856251881Speterstatic svn_error_t * 857251881Speteroutput_merge_marker(merge_output_baton_t *btn, int idx) 858251881Speter{ 859251881Speter SVN_ERR(svn_stream_puts(btn->output_stream, btn->markers[idx])); 860251881Speter return output_marker_eol(btn); 861251881Speter} 862251881Speter 863251881Speterstatic svn_error_t * 864251881Speteroutput_common_modified(void *baton, 865251881Speter apr_off_t original_start, apr_off_t original_length, 866251881Speter apr_off_t modified_start, apr_off_t modified_length, 867251881Speter apr_off_t latest_start, apr_off_t latest_length) 868251881Speter{ 869251881Speter return output_merge_token_range(NULL, baton, 1/*modified*/, 870251881Speter modified_start, modified_length); 871251881Speter} 872251881Speter 873251881Speterstatic svn_error_t * 874251881Speteroutput_latest(void *baton, 875251881Speter apr_off_t original_start, apr_off_t original_length, 876251881Speter apr_off_t modified_start, apr_off_t modified_length, 877251881Speter apr_off_t latest_start, apr_off_t latest_length) 878251881Speter{ 879251881Speter return output_merge_token_range(NULL, baton, 2/*latest*/, 880251881Speter latest_start, latest_length); 881251881Speter} 882251881Speter 883251881Speterstatic svn_error_t * 884251881Speteroutput_conflict(void *baton, 885251881Speter apr_off_t original_start, apr_off_t original_length, 886251881Speter apr_off_t modified_start, apr_off_t modified_length, 887251881Speter apr_off_t latest_start, apr_off_t latest_length, 888251881Speter svn_diff_t *diff); 889251881Speter 890251881Speterstatic const svn_diff_output_fns_t merge_output_vtable = 891251881Speter{ 892251881Speter output_common_modified, /* common */ 893251881Speter output_common_modified, /* modified */ 894251881Speter output_latest, 895251881Speter output_common_modified, /* output_diff_common */ 896251881Speter output_conflict 897251881Speter}; 898251881Speter 899251881Speterstatic svn_error_t * 900251881Speteroutput_conflict(void *baton, 901251881Speter apr_off_t original_start, apr_off_t original_length, 902251881Speter apr_off_t modified_start, apr_off_t modified_length, 903251881Speter apr_off_t latest_start, apr_off_t latest_length, 904251881Speter svn_diff_t *diff) 905251881Speter{ 906251881Speter merge_output_baton_t *btn = baton; 907251881Speter 908251881Speter svn_diff_conflict_display_style_t style = btn->conflict_style; 909251881Speter 910251881Speter if (style == svn_diff_conflict_display_resolved_modified_latest) 911251881Speter { 912251881Speter if (diff) 913289180Speter return svn_diff_output2(diff, baton, &merge_output_vtable, 914289180Speter btn->cancel_func, btn->cancel_baton); 915251881Speter else 916251881Speter style = svn_diff_conflict_display_modified_latest; 917251881Speter } 918251881Speter 919251881Speter if (style == svn_diff_conflict_display_modified_latest || 920251881Speter style == svn_diff_conflict_display_modified_original_latest) 921251881Speter { 922251881Speter SVN_ERR(output_merge_marker(btn, 1/*modified*/)); 923251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 924251881Speter modified_start, modified_length)); 925251881Speter 926251881Speter if (style == svn_diff_conflict_display_modified_original_latest) 927251881Speter { 928251881Speter SVN_ERR(output_merge_marker(btn, 0/*original*/)); 929251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 0/*original*/, 930251881Speter original_start, original_length)); 931251881Speter } 932251881Speter 933251881Speter SVN_ERR(output_merge_marker(btn, 2/*separator*/)); 934251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 935251881Speter latest_start, latest_length)); 936251881Speter SVN_ERR(output_merge_marker(btn, 3/*latest (end)*/)); 937251881Speter } 938251881Speter else if (style == svn_diff_conflict_display_modified) 939251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 940251881Speter modified_start, modified_length)); 941251881Speter else if (style == svn_diff_conflict_display_latest) 942251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 943251881Speter latest_start, latest_length)); 944251881Speter else /* unknown style */ 945251881Speter SVN_ERR_MALFUNCTION(); 946251881Speter 947251881Speter return SVN_NO_ERROR; 948251881Speter} 949251881Speter 950289180Speterstatic svn_error_t * 951289180Speteroutput_conflict_with_context_marker(merge_output_baton_t *btn, 952289180Speter const char *label, 953289180Speter apr_off_t start, 954289180Speter apr_off_t length) 955289180Speter{ 956289180Speter if (length == 1) 957289180Speter SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, 958289180Speter "%s (%" APR_OFF_T_FMT ")", 959289180Speter label, start + 1)); 960289180Speter else 961289180Speter SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, 962289180Speter "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")", 963289180Speter label, start + 1, length)); 964251881Speter 965289180Speter SVN_ERR(output_marker_eol(btn)); 966289180Speter 967289180Speter return SVN_NO_ERROR; 968289180Speter} 969289180Speter 970251881Speterstatic svn_error_t * 971251881Speteroutput_conflict_with_context(void *baton, 972251881Speter apr_off_t original_start, 973251881Speter apr_off_t original_length, 974251881Speter apr_off_t modified_start, 975251881Speter apr_off_t modified_length, 976251881Speter apr_off_t latest_start, 977251881Speter apr_off_t latest_length, 978251881Speter svn_diff_t *diff) 979251881Speter{ 980251881Speter merge_output_baton_t *btn = baton; 981251881Speter 982251881Speter /* Are we currently saving starting context (as opposed to printing 983251881Speter trailing context)? If so, flush it. */ 984251881Speter if (btn->output_stream == btn->context_saver->stream) 985251881Speter { 986289180Speter if (btn->context_saver->total_written > btn->context_size) 987251881Speter SVN_ERR(svn_stream_puts(btn->real_output_stream, "@@\n")); 988251881Speter SVN_ERR(flush_context_saver(btn->context_saver, btn->real_output_stream)); 989251881Speter } 990251881Speter 991251881Speter /* Print to the real output stream. */ 992251881Speter btn->output_stream = btn->real_output_stream; 993251881Speter 994251881Speter /* Output the conflict itself. */ 995289180Speter SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[1], 996289180Speter modified_start, 997289180Speter modified_length)); 998251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, 999251881Speter modified_start, modified_length)); 1000251881Speter 1001289180Speter SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[0], 1002289180Speter original_start, 1003289180Speter original_length)); 1004251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 0/*original*/, 1005251881Speter original_start, original_length)); 1006251881Speter 1007251881Speter SVN_ERR(output_merge_marker(btn, 2/*separator*/)); 1008251881Speter SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, 1009251881Speter latest_start, latest_length)); 1010289180Speter SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[3], 1011289180Speter latest_start, 1012289180Speter latest_length)); 1013251881Speter 1014251881Speter /* Go into print-trailing-context mode instead. */ 1015251881Speter make_trailing_context_printer(btn); 1016251881Speter 1017251881Speter return SVN_NO_ERROR; 1018251881Speter} 1019251881Speter 1020251881Speter 1021251881Speterstatic const svn_diff_output_fns_t merge_only_conflicts_output_vtable = 1022251881Speter{ 1023251881Speter output_common_modified, 1024251881Speter output_common_modified, 1025251881Speter output_latest, 1026251881Speter output_common_modified, 1027251881Speter output_conflict_with_context 1028251881Speter}; 1029251881Speter 1030251881Speter 1031251881Speter/* TOKEN is the first token in the modified file. 1032251881Speter Return its line-ending, if any. */ 1033251881Speterstatic const char * 1034251881Speterdetect_eol(svn_string_t *token) 1035251881Speter{ 1036251881Speter const char *curp; 1037251881Speter 1038251881Speter if (token->len == 0) 1039251881Speter return NULL; 1040251881Speter 1041251881Speter curp = token->data + token->len - 1; 1042251881Speter if (*curp == '\r') 1043251881Speter return "\r"; 1044251881Speter else if (*curp != '\n') 1045251881Speter return NULL; 1046251881Speter else 1047251881Speter { 1048251881Speter if (token->len == 1 1049251881Speter || *(--curp) != '\r') 1050251881Speter return "\n"; 1051251881Speter else 1052251881Speter return "\r\n"; 1053251881Speter } 1054251881Speter} 1055251881Speter 1056251881Spetersvn_error_t * 1057289180Spetersvn_diff_mem_string_output_merge3(svn_stream_t *output_stream, 1058251881Speter svn_diff_t *diff, 1059251881Speter const svn_string_t *original, 1060251881Speter const svn_string_t *modified, 1061251881Speter const svn_string_t *latest, 1062251881Speter const char *conflict_original, 1063251881Speter const char *conflict_modified, 1064251881Speter const char *conflict_latest, 1065251881Speter const char *conflict_separator, 1066251881Speter svn_diff_conflict_display_style_t style, 1067289180Speter svn_cancel_func_t cancel_func, 1068289180Speter void *cancel_baton, 1069289180Speter apr_pool_t *scratch_pool) 1070251881Speter{ 1071251881Speter merge_output_baton_t btn; 1072251881Speter const char *eol; 1073251881Speter svn_boolean_t conflicts_only = 1074251881Speter (style == svn_diff_conflict_display_only_conflicts); 1075251881Speter const svn_diff_output_fns_t *vtable = conflicts_only 1076251881Speter ? &merge_only_conflicts_output_vtable : &merge_output_vtable; 1077251881Speter 1078251881Speter memset(&btn, 0, sizeof(btn)); 1079289180Speter btn.context_size = SVN_DIFF__UNIFIED_CONTEXT_SIZE; 1080251881Speter 1081251881Speter if (conflicts_only) 1082251881Speter { 1083289180Speter btn.pool = svn_pool_create(scratch_pool); 1084251881Speter make_context_saver(&btn); 1085251881Speter btn.real_output_stream = output_stream; 1086251881Speter } 1087251881Speter else 1088251881Speter btn.output_stream = output_stream; 1089251881Speter 1090289180Speter fill_source_tokens(&(btn.sources[0]), original, scratch_pool); 1091289180Speter fill_source_tokens(&(btn.sources[1]), modified, scratch_pool); 1092289180Speter fill_source_tokens(&(btn.sources[2]), latest, scratch_pool); 1093251881Speter 1094251881Speter btn.conflict_style = style; 1095251881Speter 1096251881Speter if (btn.sources[1].tokens->nelts > 0) 1097251881Speter { 1098251881Speter eol = detect_eol(APR_ARRAY_IDX(btn.sources[1].tokens, 0, svn_string_t *)); 1099251881Speter if (!eol) 1100251881Speter eol = APR_EOL_STR; /* use the platform default */ 1101251881Speter } 1102251881Speter else 1103251881Speter eol = APR_EOL_STR; /* use the platform default */ 1104251881Speter 1105251881Speter btn.marker_eol = eol; 1106289180Speter btn.cancel_func = cancel_func; 1107289180Speter btn.cancel_baton = cancel_baton; 1108251881Speter 1109251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[1], 1110251881Speter conflict_modified 1111251881Speter ? conflict_modified 1112251881Speter : "<<<<<<< (modified)", 1113289180Speter scratch_pool)); 1114251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[0], 1115251881Speter conflict_original 1116251881Speter ? conflict_original 1117251881Speter : "||||||| (original)", 1118289180Speter scratch_pool)); 1119251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[2], 1120251881Speter conflict_separator 1121251881Speter ? conflict_separator 1122251881Speter : "=======", 1123289180Speter scratch_pool)); 1124251881Speter SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[3], 1125251881Speter conflict_latest 1126251881Speter ? conflict_latest 1127251881Speter : ">>>>>>> (latest)", 1128289180Speter scratch_pool)); 1129251881Speter 1130289180Speter SVN_ERR(svn_diff_output2(diff, &btn, vtable, cancel_func, cancel_baton)); 1131251881Speter if (conflicts_only) 1132251881Speter svn_pool_destroy(btn.pool); 1133251881Speter 1134251881Speter return SVN_NO_ERROR; 1135251881Speter} 1136