378
385
output_unified_token_range(output_baton_t *btn,
380
387
unified_output_e type,
384
390
source_tokens_t *source = &btn->sources[tokens];
387
past_last = (past_last > source->tokens->nelts)
388
? source->tokens->nelts : past_last;
391
/* We get context from the original source, don't expect
392
to be asked to output a block which starts before
393
what we already have written. */
394
first = (first < btn->next_token) ? btn->next_token : first;
396
if (first >= past_last)
392
if (until > source->tokens->nelts)
393
until = source->tokens->nelts;
395
if (until <= btn->current_token[tokens])
397
396
return SVN_NO_ERROR;
399
398
/* Do the loop with prefix and token */
400
for (idx = first; idx < past_last; idx++)
403
= APR_ARRAY_IDX(source->tokens, idx, svn_string_t *);
404
svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]);
405
svn_stringbuf_appendbytes(btn->hunk, token->data, token->len);
401
svn_string_t *token =
402
APR_ARRAY_IDX(source->tokens, btn->current_token[tokens],
405
if (type != unified_output_skip)
407
svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]);
408
svn_stringbuf_appendbytes(btn->hunk, token->data, token->len);
407
411
if (type == unified_output_context)
412
416
else if (type == unified_output_delete)
413
417
btn->hunk_length[0]++;
418
else if (type == unified_output_insert)
415
419
btn->hunk_length[1]++;
421
/* ### TODO: Add skip processing for -p handling? */
423
btn->current_token[tokens]++;
424
if (btn->current_token[tokens] == until)
418
if (past_last == source->tokens->nelts && source->ends_without_eol)
428
if (btn->current_token[tokens] == source->tokens->nelts
429
&& source->ends_without_eol)
420
431
const char *out_str;
421
SVN_ERR(svn_utf_cstring_from_utf8_ex2
423
/* The string below is intentionally not marked for translation:
424
it's vital to correct operation of the diff(1)/patch(1)
426
APR_EOL_STR "\\ No newline at end of file" APR_EOL_STR,
427
btn->header_encoding, btn->pool));
433
SVN_ERR(svn_utf_cstring_from_utf8_ex2(
434
&out_str, btn->no_newline_string,
435
btn->header_encoding, btn->pool));
428
436
svn_stringbuf_appendcstr(btn->hunk, out_str);
432
btn->next_token = past_last;
434
441
return SVN_NO_ERROR;
453
462
/* Write the trailing context */
454
463
target_token = baton->hunk_start[0] + baton->hunk_length[0]
455
+ SVN_DIFF__UNIFIED_CONTEXT_SIZE;
464
+ SVN_DIFF__UNIFIED_CONTEXT_SIZE;
456
465
SVN_ERR(output_unified_token_range(baton, 0 /*original*/,
457
466
unified_output_context,
458
baton->next_token, target_token));
459
468
if (hunk_delimiter == NULL)
460
469
hunk_delimiter = "@@";
471
old_start = baton->hunk_start[0];
472
new_start = baton->hunk_start[1];
474
/* If the file is non-empty, convert the line indexes from
475
zero based to one based */
476
if (baton->hunk_length[0])
478
if (baton->hunk_length[1])
462
481
/* Write the hunk header */
463
if (baton->hunk_length[0] > 0)
464
/* Convert our 0-based line numbers into unidiff 1-based numbers */
465
baton->hunk_start[0]++;
466
SVN_ERR(svn_stream_printf_from_utf8(
467
baton->output_stream, baton->header_encoding,
469
/* Hunk length 1 is implied, don't show the
470
length field if we have a hunk that long */
471
(baton->hunk_length[0] == 1)
472
? ("%s -%" APR_OFF_T_FMT)
473
: ("%s -%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT),
475
baton->hunk_start[0], baton->hunk_length[0]));
477
if (baton->hunk_length[1] > 0)
478
/* Convert our 0-based line numbers into unidiff 1-based numbers */
479
baton->hunk_start[1]++;
482
/* Hunk length 1 is implied, don't show the
483
length field if we have a hunk that long */
484
if (baton->hunk_length[1] == 1)
486
SVN_ERR(svn_stream_printf_from_utf8(
487
baton->output_stream, baton->header_encoding,
489
" +%" APR_OFF_T_FMT " %s" APR_EOL_STR,
490
baton->hunk_start[1], hunk_delimiter));
494
SVN_ERR(svn_stream_printf_from_utf8(
495
baton->output_stream, baton->header_encoding,
497
" +%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT " %s" APR_EOL_STR,
498
baton->hunk_start[1], baton->hunk_length[1],
482
SVN_ERR(svn_diff__unified_write_hunk_header(
483
baton->output_stream, baton->header_encoding, hunk_delimiter,
484
old_start, baton->hunk_length[0],
485
new_start, baton->hunk_length[1],
486
NULL /* hunk_extra_context */,
502
489
hunk_len = baton->hunk->len;
503
490
SVN_ERR(svn_stream_write(baton->output_stream,
504
491
baton->hunk->data, &hunk_len));
506
baton->hunk_length[0] = baton->hunk_length[1] = 0;
493
/* Prepare for the next hunk */
494
baton->hunk_length[0] = 0;
495
baton->hunk_length[1] = 0;
496
baton->hunk_start[0] = 0;
497
baton->hunk_start[1] = 0;
507
498
svn_stringbuf_setempty(baton->hunk);
509
500
return SVN_NO_ERROR;
519
510
apr_off_t latest_start,
520
511
apr_off_t latest_length)
522
output_baton_t *btn = baton;
523
apr_off_t targ_orig, targ_mod;
525
targ_orig = original_start - SVN_DIFF__UNIFIED_CONTEXT_SIZE;
526
targ_orig = (targ_orig < 0) ? 0 : targ_orig;
527
targ_mod = modified_start;
529
/* If the changed ranges are far enough apart (no overlapping or
530
* connecting context), flush the current hunk. */
531
if (btn->next_token + SVN_DIFF__UNIFIED_CONTEXT_SIZE < targ_orig)
532
SVN_ERR(output_unified_flush_hunk(btn, btn->hunk_delimiter));
533
/* Adjust offset if it's not the first hunk. */
534
else if (btn->hunk_length[0] != 0)
535
targ_orig = btn->next_token;
537
if (btn->hunk_length[0] == 0
538
&& btn->hunk_length[1] == 0)
540
btn->hunk_start[0] = targ_orig;
541
btn->hunk_start[1] = targ_mod + targ_orig - original_start;
544
SVN_ERR(output_unified_token_range(btn, 0/*original*/,
545
unified_output_context,
546
targ_orig, original_start));
547
SVN_ERR(output_unified_token_range(btn, 0/*original*/,
513
output_baton_t *output_baton = baton;
514
apr_off_t context_prefix_length;
515
apr_off_t prev_context_end;
516
svn_boolean_t init_hunk = FALSE;
518
if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE)
519
context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE;
521
context_prefix_length = original_start;
523
/* Calculate where the previous hunk will end if we would write it now
524
(including the necessary context at the end) */
525
if (output_baton->hunk_length[0] > 0 || output_baton->hunk_length[1] > 0)
527
prev_context_end = output_baton->hunk_start[0]
528
+ output_baton->hunk_length[0]
529
+ SVN_DIFF__UNIFIED_CONTEXT_SIZE;
533
prev_context_end = -1;
535
if (output_baton->hunk_start[0] == 0
536
&& (original_length > 0 || modified_length > 0))
540
/* If the changed range is far enough from the previous range, flush the current
543
apr_off_t new_hunk_start = (original_start - context_prefix_length);
545
if (output_baton->current_token[0] < new_hunk_start
546
&& prev_context_end <= new_hunk_start)
548
SVN_ERR(output_unified_flush_hunk(output_baton,
549
output_baton->hunk_delimiter));
552
else if (output_baton->hunk_length[0] > 0
553
|| output_baton->hunk_length[1] > 0)
555
/* We extend the current hunk */
557
/* Original: Output the context preceding the changed range */
558
SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
559
unified_output_context,
564
/* Original: Skip lines until we are at the beginning of the context we want
566
SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
568
original_start - context_prefix_length));
572
SVN_ERR_ASSERT(output_baton->hunk_length[0] == 0
573
&& output_baton->hunk_length[1] == 0);
575
output_baton->hunk_start[0] = original_start - context_prefix_length;
576
output_baton->hunk_start[1] = modified_start - context_prefix_length;
579
/* Modified: Skip lines until we are at the start of the changed range */
580
SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */,
584
/* Original: Output the context preceding the changed range */
585
SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
586
unified_output_context,
589
/* Both: Output the changed range */
590
SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
548
591
unified_output_delete,
550
592
original_start + original_length));
551
return output_unified_token_range(btn, 1/*modified*/, unified_output_insert,
553
modified_start + modified_length);
593
SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */,
594
unified_output_insert,
595
modified_start + modified_length));
556
600
static const svn_diff_output_fns_t mem_output_unified_vtable =
584
628
baton.output_stream = output_stream;
585
629
baton.pool = svn_pool_create(pool);
586
630
baton.header_encoding = header_encoding;
587
baton.hunk = svn_stringbuf_create("", pool);
631
baton.hunk = svn_stringbuf_create_empty(pool);
588
632
baton.hunk_delimiter = hunk_delimiter;
633
baton.no_newline_string
634
= (hunk_delimiter == NULL || strcmp(hunk_delimiter, "##") != 0)
635
? APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR
636
: APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_PROPERTY APR_EOL_STR;
590
638
SVN_ERR(svn_utf_cstring_from_utf8_ex2
591
639
(&(baton.prefix_str[unified_output_context]), " ",
808
853
static svn_error_t *
809
854
output_marker_eol(merge_output_baton_t *btn)
811
apr_size_t len = strlen(btn->marker_eol);
812
return svn_stream_write(btn->output_stream, btn->marker_eol, &len);
856
return svn_stream_puts(btn->output_stream, btn->marker_eol);
815
859
static svn_error_t *
816
860
output_merge_marker(merge_output_baton_t *btn, int idx)
818
apr_size_t len = strlen(btn->markers[idx]);
819
SVN_ERR(svn_stream_write(btn->output_stream, btn->markers[idx], &len));
862
SVN_ERR(svn_stream_puts(btn->output_stream, btn->markers[idx]));
820
863
return output_marker_eol(btn);