#include <apr.h>
#include <apr_general.h>
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_error.h"
#include "svn_diff.h"
#include "svn_types.h"
#include "svn_ctype.h"
#include "svn_utf.h"
#include "svn_version.h"
#include "private/svn_diff_private.h"
#include "private/svn_sorts_private.h"
#include "diff.h"
#include "svn_private_config.h"
svn_boolean_t
svn_diff_contains_conflicts(svn_diff_t *diff)
{
while (diff != NULL)
{
if (diff->type == svn_diff__type_conflict)
{
return TRUE;
}
diff = diff->next;
}
return FALSE;
}
svn_boolean_t
svn_diff_contains_diffs(svn_diff_t *diff)
{
while (diff != NULL)
{
if (diff->type != svn_diff__type_common)
{
return TRUE;
}
diff = diff->next;
}
return FALSE;
}
svn_error_t *
svn_diff_output2(svn_diff_t *diff,
void *output_baton,
const svn_diff_output_fns_t *vtable,
svn_cancel_func_t cancel_func,
void *cancel_baton)
{
svn_error_t *(*output_fn)(void *,
apr_off_t, apr_off_t,
apr_off_t, apr_off_t,
apr_off_t, apr_off_t);
while (diff != NULL)
{
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
switch (diff->type)
{
case svn_diff__type_common:
output_fn = vtable->output_common;
break;
case svn_diff__type_diff_common:
output_fn = vtable->output_diff_common;
break;
case svn_diff__type_diff_modified:
output_fn = vtable->output_diff_modified;
break;
case svn_diff__type_diff_latest:
output_fn = vtable->output_diff_latest;
break;
case svn_diff__type_conflict:
output_fn = NULL;
if (vtable->output_conflict != NULL)
{
SVN_ERR(vtable->output_conflict(output_baton,
diff->original_start, diff->original_length,
diff->modified_start, diff->modified_length,
diff->latest_start, diff->latest_length,
diff->resolved_diff));
}
break;
default:
output_fn = NULL;
break;
}
if (output_fn != NULL)
{
SVN_ERR(output_fn(output_baton,
diff->original_start, diff->original_length,
diff->modified_start, diff->modified_length,
diff->latest_start, diff->latest_length));
}
diff = diff->next;
}
return SVN_NO_ERROR;
}
void
svn_diff__normalize_buffer(char **tgt,
apr_off_t *lengthp,
svn_diff__normalize_state_t *statep,
const char *buf,
const svn_diff_file_options_t *opts)
{
const char *curp, *endp;
svn_diff__normalize_state_t state = *statep;
const char *start = buf;
apr_size_t include_len = 0;
svn_boolean_t last_skipped = FALSE;
char *tgt_newend = *tgt;
if (! opts->ignore_space && ! opts->ignore_eol_style)
{
*tgt = (char *)buf;
return;
}
#define SKIP \
do { \
if (start == curp) \
++start; \
last_skipped = TRUE; \
} while (0)
#define INCLUDE \
do { \
if (last_skipped) \
COPY_INCLUDED_SECTION; \
++include_len; \
last_skipped = FALSE; \
} while (0)
#define COPY_INCLUDED_SECTION \
do { \
if (include_len > 0) \
{ \
memmove(tgt_newend, start, include_len); \
tgt_newend += include_len; \
include_len = 0; \
} \
start = curp; \
} while (0)
#define INCLUDE_AS(x) \
do { \
if (*curp == (x)) \
INCLUDE; \
else \
{ \
INSERT((x)); \
SKIP; \
} \
} while (0)
#define INSERT(x) \
do { \
COPY_INCLUDED_SECTION; \
*tgt_newend++ = (x); \
} while (0)
for (curp = buf, endp = buf + *lengthp; curp != endp; ++curp)
{
switch (*curp)
{
case '\r':
if (opts->ignore_eol_style)
INCLUDE_AS('\n');
else
INCLUDE;
state = svn_diff__normalize_state_cr;
break;
case '\n':
if (state == svn_diff__normalize_state_cr
&& opts->ignore_eol_style)
SKIP;
else
INCLUDE;
state = svn_diff__normalize_state_normal;
break;
default:
if (svn_ctype_isspace(*curp)
&& opts->ignore_space != svn_diff_file_ignore_space_none)
{
if (state != svn_diff__normalize_state_whitespace
&& opts->ignore_space
== svn_diff_file_ignore_space_change)
INCLUDE_AS(' ');
else
SKIP;
state = svn_diff__normalize_state_whitespace;
}
else
{
INCLUDE;
state = svn_diff__normalize_state_normal;
}
}
}
if (*tgt == tgt_newend)
{
*tgt = (char *)start;
*lengthp = include_len;
}
else
{
COPY_INCLUDED_SECTION;
*lengthp = tgt_newend - *tgt;
}
*statep = state;
#undef SKIP
#undef INCLUDE
#undef INCLUDE_AS
#undef INSERT
#undef COPY_INCLUDED_SECTION
}
svn_error_t *
svn_diff__unified_append_no_newline_msg(svn_stringbuf_t *stringbuf,
const char *header_encoding,
apr_pool_t *scratch_pool)
{
const char *out_str;
SVN_ERR(svn_utf_cstring_from_utf8_ex2(
&out_str,
APR_EOL_STR
SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR,
header_encoding, scratch_pool));
svn_stringbuf_appendcstr(stringbuf, out_str);
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff__unified_write_hunk_header(svn_stream_t *output_stream,
const char *header_encoding,
const char *hunk_delimiter,
apr_off_t old_start,
apr_off_t old_length,
apr_off_t new_start,
apr_off_t new_length,
const char *hunk_extra_context,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
"%s -%" APR_OFF_T_FMT,
hunk_delimiter, old_start));
if (old_length != 1)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
",%" APR_OFF_T_FMT, old_length));
}
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
" +%" APR_OFF_T_FMT, new_start));
if (new_length != 1)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
",%" APR_OFF_T_FMT, new_length));
}
if (hunk_extra_context == NULL)
hunk_extra_context = "";
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
" %s%s%s" APR_EOL_STR,
hunk_delimiter,
hunk_extra_context[0] ? " " : "",
hunk_extra_context));
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff__unidiff_write_header(svn_stream_t *output_stream,
const char *header_encoding,
const char *old_header,
const char *new_header,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
"--- %s" APR_EOL_STR
"+++ %s" APR_EOL_STR,
old_header,
new_header));
return SVN_NO_ERROR;
}
static svn_error_t *
display_mergeinfo_diff(const char *old_mergeinfo_val,
const char *new_mergeinfo_val,
const char *encoding,
svn_stream_t *outstream,
apr_pool_t *pool)
{
apr_hash_t *old_mergeinfo_hash, *new_mergeinfo_hash, *added, *deleted;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_hash_index_t *hi;
if (old_mergeinfo_val)
SVN_ERR(svn_mergeinfo_parse(&old_mergeinfo_hash, old_mergeinfo_val, pool));
else
old_mergeinfo_hash = NULL;
if (new_mergeinfo_val)
SVN_ERR(svn_mergeinfo_parse(&new_mergeinfo_hash, new_mergeinfo_val, pool));
else
new_mergeinfo_hash = NULL;
SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, old_mergeinfo_hash,
new_mergeinfo_hash,
TRUE, pool, pool));
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
"## -0,%u +0,%u ##%s",
apr_hash_count(deleted),
apr_hash_count(added),
APR_EOL_STR));
for (hi = apr_hash_first(pool, deleted);
hi; hi = apr_hash_next(hi))
{
const char *from_path = apr_hash_this_key(hi);
svn_rangelist_t *merge_revarray = apr_hash_this_val(hi);
svn_string_t *merge_revstr;
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
iterpool));
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
_(" Reverse-merged %s:r%s%s"),
from_path, merge_revstr->data,
APR_EOL_STR));
}
for (hi = apr_hash_first(pool, added);
hi; hi = apr_hash_next(hi))
{
const char *from_path = apr_hash_this_key(hi);
svn_rangelist_t *merge_revarray = apr_hash_this_val(hi);
svn_string_t *merge_revstr;
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
iterpool));
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
_(" Merged %s:r%s%s"),
from_path, merge_revstr->data,
APR_EOL_STR));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static int
propchange_sort(const void *k1, const void *k2)
{
const svn_prop_t *propchange1 = k1;
const svn_prop_t *propchange2 = k2;
return strcmp(propchange1->name, propchange2->name);
}
svn_error_t *
svn_diff__display_prop_diffs(svn_stream_t *outstream,
const char *encoding,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
svn_boolean_t pretty_print_mergeinfo,
int context_size,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
apr_pool_t *pool = scratch_pool;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_array_header_t *changes = apr_array_copy(scratch_pool, propchanges);
int i;
svn_sort__array(changes, propchange_sort);
for (i = 0; i < changes->nelts; i++)
{
const char *action;
const svn_string_t *original_value;
const svn_prop_t *propchange
= &APR_ARRAY_IDX(changes, i, svn_prop_t);
if (original_props)
original_value = svn_hash_gets(original_props, propchange->name);
else
original_value = NULL;
if ((! (original_value || propchange->value))
|| (original_value && propchange->value
&& svn_string_compare(original_value, propchange->value)))
continue;
svn_pool_clear(iterpool);
if (! original_value)
action = "Added";
else if (! propchange->value)
action = "Deleted";
else
action = "Modified";
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
"%s: %s%s", action,
propchange->name, APR_EOL_STR));
if (pretty_print_mergeinfo
&& strcmp(propchange->name, SVN_PROP_MERGEINFO) == 0)
{
const char *orig = original_value ? original_value->data : NULL;
const char *val = propchange->value ? propchange->value->data : NULL;
svn_error_t *err = display_mergeinfo_diff(orig, val, encoding,
outstream, iterpool);
if (err && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
}
else
{
SVN_ERR(err);
continue;
}
}
{
svn_diff_t *diff;
svn_diff_file_options_t options = { 0 };
const svn_string_t *orig
= original_value ? original_value
: svn_string_create_empty(iterpool);
const svn_string_t *val
= propchange->value ? propchange->value
: svn_string_create_empty(iterpool);
SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options,
iterpool));
SVN_ERR(svn_diff_mem_string_output_unified3(
outstream, diff, FALSE , "##", NULL, NULL,
encoding, orig, val, context_size,
cancel_func, cancel_baton, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
const svn_version_t *
svn_diff_version(void)
{
SVN_VERSION_BODY;
}