#include <stdlib.h>
#include <string.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_file_io.h>
#include <apr_strings.h>
#include <apr_general.h>
#include "svn_types.h"
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_error.h"
#include "svn_props.h"
#include "svn_io.h"
#include "svn_hash.h"
#include "svn_mergeinfo.h"
#include "svn_wc.h"
#include "svn_utf.h"
#include "svn_diff.h"
#include "svn_sorts.h"
#include "private/svn_wc_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_skel.h"
#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
#include "wc.h"
#include "props.h"
#include "translate.h"
#include "workqueue.h"
#include "conflicts.h"
#include "svn_private_config.h"
static svn_error_t *
diff_mergeinfo_props(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
const svn_string_t *from_prop_val,
const svn_string_t *to_prop_val, apr_pool_t *pool)
{
if (svn_string_compare(from_prop_val, to_prop_val))
{
*deleted = apr_hash_make(pool);
*added = apr_hash_make(pool);
}
else
{
svn_mergeinfo_t from, to;
SVN_ERR(svn_mergeinfo_parse(&from, from_prop_val->data, pool));
SVN_ERR(svn_mergeinfo_parse(&to, to_prop_val->data, pool));
SVN_ERR(svn_mergeinfo_diff2(deleted, added, from, to,
TRUE, pool, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
combine_mergeinfo_props(const svn_string_t **output,
const svn_string_t *prop_val1,
const svn_string_t *prop_val2,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t mergeinfo1, mergeinfo2;
svn_string_t *mergeinfo_string;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo1, prop_val1->data, scratch_pool));
SVN_ERR(svn_mergeinfo_parse(&mergeinfo2, prop_val2->data, scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(mergeinfo1, mergeinfo2, scratch_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo1, result_pool));
*output = mergeinfo_string;
return SVN_NO_ERROR;
}
static svn_error_t *
combine_forked_mergeinfo_props(const svn_string_t **output,
const svn_string_t *from_prop_val,
const svn_string_t *working_prop_val,
const svn_string_t *to_prop_val,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t from_mergeinfo, l_deleted, l_added, r_deleted, r_added;
svn_string_t *mergeinfo_string;
SVN_ERR(diff_mergeinfo_props(&l_deleted, &l_added, from_prop_val,
working_prop_val, scratch_pool));
SVN_ERR(diff_mergeinfo_props(&r_deleted, &r_added, from_prop_val,
to_prop_val, scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(l_deleted, r_deleted,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(l_added, r_added,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo_parse(&from_mergeinfo, from_prop_val->data,
scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(from_mergeinfo, l_added,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo_remove2(&from_mergeinfo, l_deleted, from_mergeinfo,
TRUE, scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, from_mergeinfo,
result_pool));
*output = mergeinfo_string;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_merge_props3(svn_wc_notify_state_t *state,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
apr_hash_t *baseprops,
const apr_array_header_t *propchanges,
svn_boolean_t dry_run,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
int i;
svn_wc__db_status_t status;
svn_node_kind_t kind;
apr_hash_t *pristine_props = NULL;
apr_hash_t *actual_props;
apr_hash_t *new_actual_props;
svn_boolean_t had_props, props_mod;
svn_boolean_t have_base;
svn_boolean_t conflicted;
svn_skel_t *work_items = NULL;
svn_skel_t *conflict_skel = NULL;
svn_wc__db_t *db = wc_ctx->db;
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, &conflicted, NULL,
&had_props, &props_mod, &have_base, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded)
{
return svn_error_createf(
SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else if (status != svn_wc__db_status_normal
&& status != svn_wc__db_status_added
&& status != svn_wc__db_status_incomplete)
{
return svn_error_createf(
SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("The node '%s' does not have properties in this state."),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else if (conflicted)
{
svn_boolean_t text_conflicted;
svn_boolean_t prop_conflicted;
svn_boolean_t tree_conflicted;
SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted,
&prop_conflicted,
&tree_conflicted,
db, local_abspath,
scratch_pool));
if (text_conflicted || prop_conflicted || tree_conflicted)
{
return svn_error_createf(
SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Can't merge into conflicted node '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
}
for (i = propchanges->nelts; i--; )
{
const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
if (!svn_wc_is_normal_prop(change->name))
return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
_("The property '%s' may not be merged "
"into '%s'."),
change->name,
svn_dirent_local_style(local_abspath,
scratch_pool));
}
if (had_props)
SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath,
scratch_pool, scratch_pool));
if (pristine_props == NULL)
pristine_props = apr_hash_make(scratch_pool);
if (props_mod)
SVN_ERR(svn_wc__get_actual_props(&actual_props, db, local_abspath,
scratch_pool, scratch_pool));
else
actual_props = pristine_props;
SVN_ERR(svn_wc__merge_props(&conflict_skel, state,
&new_actual_props,
db, local_abspath,
baseprops ,
pristine_props,
actual_props,
propchanges,
scratch_pool, scratch_pool));
if (dry_run)
{
return SVN_NO_ERROR;
}
{
const char *dir_abspath;
if (kind == svn_node_dir)
dir_abspath = local_abspath;
else
dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
}
if (conflict_skel)
{
svn_skel_t *work_item;
SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel,
left_version,
right_version,
scratch_pool,
scratch_pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
}
SVN_ERR_ASSERT(new_actual_props != NULL);
SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, new_actual_props,
svn_wc__has_magic_property(propchanges),
conflict_skel,
work_items,
scratch_pool));
if (work_items != NULL)
SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
scratch_pool));
if (conflict_skel && conflict_func)
{
svn_boolean_t prop_conflicted;
SVN_ERR(svn_wc__conflict_invoke_resolver(db, local_abspath, kind,
conflict_skel,
NULL ,
conflict_func, conflict_baton,
cancel_func, cancel_baton,
scratch_pool));
SVN_ERR(svn_wc__internal_conflicted_p(
NULL, &prop_conflicted, NULL,
wc_ctx->db, local_abspath, scratch_pool));
if (! prop_conflicted)
*state = svn_wc_notify_state_merged;
}
return SVN_NO_ERROR;
}
static svn_stringbuf_t *
generate_conflict_message(const char *propname,
const svn_string_t *original,
const svn_string_t *mine,
const svn_string_t *incoming,
const svn_string_t *incoming_base,
apr_pool_t *result_pool)
{
if (incoming_base == NULL)
{
SVN_ERR_ASSERT_NO_RETURN(incoming != NULL);
if (mine)
{
SVN_ERR_ASSERT_NO_RETURN(!svn_string_compare(mine, incoming));
return svn_stringbuf_createf(result_pool,
_("Trying to add new property '%s'\n"
"but the property already exists.\n"),
propname);
}
SVN_ERR_ASSERT_NO_RETURN(original != NULL);
return svn_stringbuf_createf(result_pool,
_("Trying to add new property '%s'\n"
"but the property has been locally "
"deleted.\n"),
propname);
}
if (incoming == NULL)
{
SVN_ERR_ASSERT_NO_RETURN(incoming_base != NULL);
if (original == NULL && mine != NULL)
return svn_stringbuf_createf(result_pool,
_("Trying to delete property '%s'\n"
"but the property has been locally "
"added.\n"),
propname);
SVN_ERR_ASSERT_NO_RETURN(original != NULL);
if (svn_string_compare(original, incoming_base))
{
if (mine)
return svn_stringbuf_createf(result_pool,
_("Trying to delete property '%s'\n"
"but the property has been locally "
"modified.\n"),
propname);
}
else if (mine == NULL)
{
return svn_stringbuf_createf(result_pool,
_("Trying to delete property '%s'\n"
"but the property has been locally "
"deleted and had a different "
"value.\n"),
propname);
}
SVN_ERR_ASSERT_NO_RETURN(!svn_string_compare(original, incoming_base));
return svn_stringbuf_createf(result_pool,
_("Trying to delete property '%s'\n"
"but the local property value is "
"different.\n"),
propname);
}
SVN_ERR_ASSERT_NO_RETURN(!mine || !svn_string_compare(mine, incoming_base));
if (original && mine && svn_string_compare(original, mine))
{
SVN_ERR_ASSERT_NO_RETURN(!svn_string_compare(original, incoming_base));
return svn_stringbuf_createf(result_pool,
_("Trying to change property '%s'\n"
"but the local property value conflicts "
"with the incoming change.\n"),
propname);
}
if (original && mine)
return svn_stringbuf_createf(result_pool,
_("Trying to change property '%s'\n"
"but the property has already been locally "
"changed to a different value.\n"),
propname);
if (original)
return svn_stringbuf_createf(result_pool,
_("Trying to change property '%s'\nbut "
"the property has been locally deleted.\n"),
propname);
if (mine)
return svn_stringbuf_createf(result_pool,
_("Trying to change property '%s'\nbut the "
"property has been locally added with a "
"different value.\n"),
propname);
return svn_stringbuf_createf(result_pool,
_("Trying to change property '%s'\nbut "
"the property does not exist locally.\n"),
propname);
}
static const svn_string_t *
maybe_prop_value(const svn_skel_t *skel,
apr_pool_t *result_pool)
{
if (skel->children == NULL)
return NULL;
return svn_string_ncreate(skel->children->data,
skel->children->len,
result_pool);
}
static svn_error_t *
prop_conflict_new(const svn_string_t **conflict_desc,
const char *propname,
const svn_string_t *original,
const svn_string_t *mine,
const svn_string_t *incoming,
const svn_string_t *incoming_base,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_diff_t *diff;
svn_diff_file_options_t *diff_opts;
svn_stringbuf_t *buf;
svn_boolean_t incoming_base_is_binary;
svn_boolean_t mine_is_binary;
svn_boolean_t incoming_is_binary;
buf = generate_conflict_message(propname, original, mine, incoming,
incoming_base, scratch_pool);
if (mine == NULL)
mine = svn_string_create_empty(scratch_pool);
if (incoming == NULL)
incoming = svn_string_create_empty(scratch_pool);
if (incoming_base == NULL)
incoming_base = svn_string_create_empty(scratch_pool);
incoming_base_is_binary = svn_io_is_binary_data(incoming_base->data,
incoming_base->len);
mine_is_binary = svn_io_is_binary_data(mine->data, mine->len);
incoming_is_binary = svn_io_is_binary_data(incoming->data, incoming->len);
if (!(incoming_base_is_binary || mine_is_binary || incoming_is_binary))
{
diff_opts = svn_diff_file_options_create(scratch_pool);
diff_opts->ignore_space = svn_diff_file_ignore_space_none;
diff_opts->ignore_eol_style = FALSE;
diff_opts->show_c_function = FALSE;
SVN_ERR(svn_diff_mem_string_diff3(&diff, incoming_base, mine, incoming,
diff_opts, scratch_pool));
if (svn_diff_contains_conflicts(diff))
{
svn_stream_t *stream;
svn_diff_conflict_display_style_t style;
const char *mine_marker = _("<<<<<<< (local property value)");
const char *incoming_marker = _(">>>>>>> (incoming 'changed to' value)");
const char *incoming_base_marker = _("||||||| (incoming 'changed from' value)");
const char *separator = "=======";
svn_string_t *incoming_base_ascii =
svn_string_create(svn_utf_cstring_from_utf8_fuzzy(incoming_base->data,
scratch_pool),
scratch_pool);
svn_string_t *mine_ascii =
svn_string_create(svn_utf_cstring_from_utf8_fuzzy(mine->data,
scratch_pool),
scratch_pool);
svn_string_t *incoming_ascii =
svn_string_create(svn_utf_cstring_from_utf8_fuzzy(incoming->data,
scratch_pool),
scratch_pool);
style = svn_diff_conflict_display_modified_original_latest;
stream = svn_stream_from_stringbuf(buf, scratch_pool);
SVN_ERR(svn_stream_skip(stream, buf->len));
SVN_ERR(svn_diff_mem_string_output_merge3(stream, diff,
incoming_base_ascii,
mine_ascii,
incoming_ascii,
incoming_base_marker, mine_marker,
incoming_marker, separator,
style,
cancel_func, cancel_baton,
scratch_pool));
SVN_ERR(svn_stream_close(stream));
*conflict_desc = svn_string_create_from_buf(buf, result_pool);
return SVN_NO_ERROR;
}
}
if (mine->len > 0)
{
svn_stringbuf_appendcstr(buf, _("Local property value:\n"));
if (mine_is_binary)
svn_stringbuf_appendcstr(buf, _("Cannot display: property value is "
"binary data\n"));
else
svn_stringbuf_appendbytes(buf, mine->data, mine->len);
svn_stringbuf_appendcstr(buf, "\n");
}
if (incoming->len > 0)
{
svn_stringbuf_appendcstr(buf, _("Incoming property value:\n"));
if (incoming_is_binary)
svn_stringbuf_appendcstr(buf, _("Cannot display: property value is "
"binary data\n"));
else
svn_stringbuf_appendbytes(buf, incoming->data, incoming->len);
svn_stringbuf_appendcstr(buf, "\n");
}
*conflict_desc = svn_string_create_from_buf(buf, result_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
prop_conflict_from_skel(const svn_string_t **conflict_desc,
const svn_skel_t *skel,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_string_t *original;
const svn_string_t *mine;
const svn_string_t *incoming;
const svn_string_t *incoming_base;
const char *propname;
skel = skel->children->next;
propname = apr_pstrmemdup(scratch_pool, skel->data, skel->len);
original = maybe_prop_value(skel->next, scratch_pool);
mine = maybe_prop_value(skel->next->next, scratch_pool);
incoming = maybe_prop_value(skel->next->next->next, scratch_pool);
incoming_base = maybe_prop_value(skel->next->next->next->next, scratch_pool);
return svn_error_trace(prop_conflict_new(conflict_desc,
propname,
original, mine,
incoming, incoming_base,
cancel_func, cancel_baton,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc__create_prejfile(const char **tmp_prejfile_abspath,
svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *prop_conflict_data,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *tempdir_abspath;
svn_stream_t *stream;
const char *temp_abspath;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const svn_skel_t *scan;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tempdir_abspath,
db, local_abspath,
iterpool, iterpool));
SVN_ERR(svn_stream_open_unique(&stream, &temp_abspath,
tempdir_abspath, svn_io_file_del_none,
scratch_pool, iterpool));
if (prop_conflict_data)
{
for (scan = prop_conflict_data->children->next;
scan != NULL; scan = scan->next)
{
const svn_string_t *conflict_desc;
svn_pool_clear(iterpool);
SVN_ERR(prop_conflict_from_skel(&conflict_desc, scan,
cancel_func, cancel_baton,
iterpool, iterpool));
SVN_ERR(svn_stream_puts(stream, conflict_desc->data));
}
}
else
{
svn_wc_operation_t operation;
apr_hash_index_t *hi;
apr_hash_t *old_props;
apr_hash_t *mine_props;
apr_hash_t *their_original_props;
apr_hash_t *their_props;
apr_hash_t *conflicted_props;
svn_skel_t *conflicts;
SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, NULL,
db, local_abspath,
conflicts,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
&mine_props,
&their_original_props,
&their_props,
&conflicted_props,
db, local_abspath,
conflicts,
scratch_pool,
scratch_pool));
if (operation == svn_wc_operation_merge)
SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
scratch_pool, scratch_pool));
else
old_props = their_original_props;
for (hi = apr_hash_first(scratch_pool, conflicted_props);
hi;
hi = apr_hash_next(hi))
{
const svn_string_t *conflict_desc;
const char *propname = apr_hash_this_key(hi);
const svn_string_t *old_value;
const svn_string_t *mine_value;
const svn_string_t *their_value;
const svn_string_t *their_original_value;
svn_pool_clear(iterpool);
old_value = old_props ? svn_hash_gets(old_props, propname) : NULL;
mine_value = mine_props ? svn_hash_gets(mine_props, propname) : NULL;
their_value = their_props ? svn_hash_gets(their_props, propname)
: NULL;
their_original_value = their_original_props
? svn_hash_gets(their_original_props, propname)
: NULL;
SVN_ERR(prop_conflict_new(&conflict_desc,
propname, old_value, mine_value,
their_value, their_original_value,
cancel_func, cancel_baton,
iterpool, iterpool));
SVN_ERR(svn_stream_puts(stream, conflict_desc->data));
}
}
SVN_ERR(svn_stream_close(stream));
svn_pool_destroy(iterpool);
*tmp_prejfile_abspath = apr_pstrdup(result_pool, temp_abspath);
return SVN_NO_ERROR;
}
static void
set_prop_merge_state(svn_wc_notify_state_t *state,
svn_wc_notify_state_t new_value)
{
static char ordering[] =
{ svn_wc_notify_state_unknown,
svn_wc_notify_state_unchanged,
svn_wc_notify_state_inapplicable,
svn_wc_notify_state_changed,
svn_wc_notify_state_merged,
svn_wc_notify_state_obstructed,
svn_wc_notify_state_conflicted };
int state_pos = 0, i;
if (! state)
return;
for (i = 0; i < sizeof(ordering); i++)
{
if (*state == ordering[i])
{
state_pos = i;
break;
}
}
for (i = 0; i <= state_pos; i++)
{
if (new_value == ordering[i])
return;
}
*state = new_value;
}
static svn_error_t *
apply_single_prop_add(const svn_string_t **result_val,
svn_boolean_t *conflict_remains,
svn_boolean_t *did_merge,
const char *propname,
const svn_string_t *pristine_val,
const svn_string_t *new_val,
const svn_string_t *working_val,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*conflict_remains = FALSE;
if (working_val)
{
if (svn_string_compare(working_val, new_val))
*did_merge = TRUE;
else
{
svn_boolean_t merged_prop = FALSE;
if (strcmp(propname, SVN_PROP_MERGEINFO) == 0)
{
const svn_string_t *merged_val;
svn_error_t *err = combine_mergeinfo_props(&merged_val,
working_val,
new_val,
result_pool,
scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
merged_prop = TRUE;
*result_val = merged_val;
*did_merge = TRUE;
}
}
if (!merged_prop)
*conflict_remains = TRUE;
}
}
else if (pristine_val)
*conflict_remains = TRUE;
else
*result_val = new_val;
return SVN_NO_ERROR;
}
static svn_error_t *
apply_single_prop_delete(const svn_string_t **result_val,
svn_boolean_t *conflict_remains,
svn_boolean_t *did_merge,
const svn_string_t *base_val,
const svn_string_t *old_val,
const svn_string_t *working_val)
{
*conflict_remains = FALSE;
if (! base_val)
{
if (working_val
&& !svn_string_compare(working_val, old_val))
{
*conflict_remains = TRUE;
}
else
{
*result_val = NULL;
if (old_val)
*did_merge = TRUE;
}
}
else if (svn_string_compare(base_val, old_val))
{
if (working_val)
{
if (svn_string_compare(working_val, old_val))
*result_val = NULL;
else
*conflict_remains = TRUE;
}
else
*did_merge = TRUE;
}
else
*conflict_remains = TRUE;
return SVN_NO_ERROR;
}
static svn_error_t *
apply_single_mergeinfo_prop_change(const svn_string_t **result_val,
svn_boolean_t *conflict_remains,
svn_boolean_t *did_merge,
const svn_string_t *base_val,
const svn_string_t *old_val,
const svn_string_t *new_val,
const svn_string_t *working_val,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if ((working_val && ! base_val)
|| (! working_val && base_val)
|| (working_val && base_val
&& !svn_string_compare(working_val, base_val)))
{
if (working_val)
{
if (svn_string_compare(working_val, new_val))
*did_merge = TRUE;
else
{
SVN_ERR(combine_forked_mergeinfo_props(&new_val, old_val,
working_val,
new_val,
result_pool,
scratch_pool));
*result_val = new_val;
*did_merge = TRUE;
}
}
else
{
*conflict_remains = TRUE;
}
}
else if (! working_val)
{
svn_mergeinfo_t deleted_mergeinfo, added_mergeinfo;
svn_string_t *mergeinfo_string;
SVN_ERR(diff_mergeinfo_props(&deleted_mergeinfo,
&added_mergeinfo,
old_val, new_val, scratch_pool));
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string,
added_mergeinfo, result_pool));
*result_val = mergeinfo_string;
}
else
{
if (svn_string_compare(old_val, base_val))
*result_val = new_val;
else
{
SVN_ERR(combine_forked_mergeinfo_props(&new_val, old_val,
working_val,
new_val, result_pool,
scratch_pool));
*result_val = new_val;
*did_merge = TRUE;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
apply_single_generic_prop_change(const svn_string_t **result_val,
svn_boolean_t *conflict_remains,
svn_boolean_t *did_merge,
const svn_string_t *old_val,
const svn_string_t *new_val,
const svn_string_t *working_val)
{
SVN_ERR_ASSERT(old_val != NULL);
if (working_val && new_val
&& svn_string_compare(working_val, new_val))
{
if (! old_val || ! svn_string_compare(old_val, new_val))
*did_merge = TRUE;
}
else if (working_val && old_val
&& svn_string_compare(working_val, old_val))
{
*result_val = new_val;
}
else
{
*conflict_remains = TRUE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
apply_single_prop_change(const svn_string_t **result_val,
svn_boolean_t *conflict_remains,
svn_boolean_t *did_merge,
const char *propname,
const svn_string_t *base_val,
const svn_string_t *old_val,
const svn_string_t *new_val,
const svn_string_t *working_val,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t merged_prop = FALSE;
*conflict_remains = FALSE;
if (strcmp(propname, SVN_PROP_MERGEINFO) == 0)
{
svn_error_t *err = apply_single_mergeinfo_prop_change(result_val,
conflict_remains,
did_merge,
base_val,
old_val,
new_val,
working_val,
result_pool,
scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
merged_prop = TRUE;
}
}
if (!merged_prop)
{
SVN_ERR(apply_single_generic_prop_change(result_val, conflict_remains,
did_merge,
old_val, new_val, working_val));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__merge_props(svn_skel_t **conflict_skel,
svn_wc_notify_state_t *state,
apr_hash_t **new_actual_props,
svn_wc__db_t *db,
const char *local_abspath,
apr_hash_t *server_baseprops,
apr_hash_t *pristine_props,
apr_hash_t *actual_props,
const apr_array_header_t *propchanges,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool;
int i;
apr_hash_t *conflict_props = NULL;
apr_hash_t *their_props;
SVN_ERR_ASSERT(pristine_props != NULL);
SVN_ERR_ASSERT(actual_props != NULL);
*new_actual_props = apr_hash_copy(result_pool, actual_props);
if (!server_baseprops)
server_baseprops = pristine_props;
their_props = apr_hash_copy(scratch_pool, server_baseprops);
if (state)
{
*state = svn_wc_notify_state_unchanged;
}
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < propchanges->nelts; i++)
{
const svn_prop_t *incoming_change
= &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
const char *propname = incoming_change->name;
const svn_string_t *base_val
= svn_hash_gets(pristine_props, propname);
const svn_string_t *from_val
= svn_hash_gets(server_baseprops, propname);
const svn_string_t *to_val
= incoming_change->value;
const svn_string_t *working_val
= svn_hash_gets(actual_props, propname);
const svn_string_t *result_val;
svn_boolean_t conflict_remains;
svn_boolean_t did_merge = FALSE;
svn_pool_clear(iterpool);
to_val = svn_string_dup(to_val, result_pool);
svn_hash_sets(their_props, propname, to_val);
set_prop_merge_state(state, svn_wc_notify_state_changed);
result_val = working_val;
if (! from_val)
SVN_ERR(apply_single_prop_add(&result_val, &conflict_remains,
&did_merge, propname,
base_val, to_val, working_val,
result_pool, iterpool));
else if (! to_val)
SVN_ERR(apply_single_prop_delete(&result_val, &conflict_remains,
&did_merge,
base_val, from_val, working_val));
else
SVN_ERR(apply_single_prop_change(&result_val, &conflict_remains,
&did_merge, propname,
base_val, from_val, to_val, working_val,
result_pool, iterpool));
if (result_val != working_val)
svn_hash_sets(*new_actual_props, propname, result_val);
if (did_merge)
set_prop_merge_state(state, svn_wc_notify_state_merged);
if (conflict_remains)
{
set_prop_merge_state(state, svn_wc_notify_state_conflicted);
if (!conflict_props)
conflict_props = apr_hash_make(scratch_pool);
svn_hash_sets(conflict_props, propname, "");
}
}
svn_pool_destroy(iterpool);
if (conflict_props != NULL)
{
if (!*conflict_skel)
*conflict_skel = svn_wc__conflict_skel_create(result_pool);
SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(*conflict_skel,
db, local_abspath,
NULL ,
actual_props,
server_baseprops,
their_props,
conflict_props,
result_pool,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
wcprop_set(svn_wc__db_t *db,
const char *local_abspath,
const char *name,
const svn_string_t *value,
apr_pool_t *scratch_pool)
{
apr_hash_t *prophash;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_base_get_dav_cache(&prophash, db, local_abspath,
scratch_pool, scratch_pool));
if (prophash == NULL)
prophash = apr_hash_make(scratch_pool);
svn_hash_sets(prophash, name, value);
return svn_error_trace(svn_wc__db_base_set_dav_cache(db, local_abspath,
prophash,
scratch_pool));
}
svn_error_t *
svn_wc__get_actual_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(props != NULL);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
return svn_error_trace(svn_wc__db_read_props(props, db, local_abspath,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc_prop_list2(apr_hash_t **props,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_wc__get_actual_props(props,
wc_ctx->db,
local_abspath,
result_pool,
scratch_pool));
}
struct propname_filter_baton_t {
svn_wc__proplist_receiver_t receiver_func;
void *receiver_baton;
const char *propname;
};
static svn_error_t *
propname_filter_receiver(void *baton,
const char *local_abspath,
apr_hash_t *props,
apr_pool_t *scratch_pool)
{
struct propname_filter_baton_t *pfb = baton;
const svn_string_t *propval = svn_hash_gets(props, pfb->propname);
if (propval)
{
props = apr_hash_make(scratch_pool);
svn_hash_sets(props, pfb->propname, propval);
SVN_ERR(pfb->receiver_func(pfb->receiver_baton, local_abspath, props,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__prop_list_recursive(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *propname,
svn_depth_t depth,
svn_boolean_t pristine,
const apr_array_header_t *changelists,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__proplist_receiver_t receiver = receiver_func;
void *baton = receiver_baton;
struct propname_filter_baton_t pfb;
pfb.receiver_func = receiver_func;
pfb.receiver_baton = receiver_baton;
pfb.propname = propname;
SVN_ERR_ASSERT(receiver_func);
if (propname)
{
baton = &pfb;
receiver = propname_filter_receiver;
}
switch (depth)
{
case svn_depth_empty:
{
apr_hash_t *props;
apr_hash_t *changelist_hash = NULL;
if (changelists && changelists->nelts)
SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash,
changelists, scratch_pool));
if (!svn_wc__internal_changelist_match(wc_ctx->db, local_abspath,
changelist_hash, scratch_pool))
break;
if (pristine)
SVN_ERR(svn_wc__db_read_pristine_props(&props, wc_ctx->db,
local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_read_props(&props, wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
if (props && apr_hash_count(props) > 0)
SVN_ERR(receiver(baton, local_abspath, props, scratch_pool));
}
break;
case svn_depth_files:
case svn_depth_immediates:
case svn_depth_infinity:
{
SVN_ERR(svn_wc__db_read_props_streamily(wc_ctx->db, local_abspath,
depth, pristine,
changelists, receiver, baton,
cancel_func, cancel_baton,
scratch_pool));
}
break;
default:
SVN_ERR_MALFUNCTION();
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__prop_retrieve_recursive(apr_hash_t **values,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__db_prop_retrieve_recursive(values,
wc_ctx->db,
local_abspath,
propname,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc_get_pristine_props(apr_hash_t **props,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
SVN_ERR_ASSERT(props != NULL);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
err = svn_wc__db_read_pristine_props(props, wc_ctx->db, local_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
*props = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_prop_get2(const svn_string_t **value,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
enum svn_prop_kind kind = svn_property_kind2(name);
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (kind == svn_prop_entry_kind)
{
return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
_("Property '%s' is an entry property"), name);
}
err = svn_wc__internal_propget(value, wc_ctx->db, local_abspath, name,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
*value = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__internal_propget(const svn_string_t **value,
svn_wc__db_t *db,
const char *local_abspath,
const char *name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *prophash = NULL;
enum svn_prop_kind kind = svn_property_kind2(name);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(kind != svn_prop_entry_kind);
if (kind == svn_prop_wc_kind)
{
SVN_ERR_W(svn_wc__db_base_get_dav_cache(&prophash, db, local_abspath,
result_pool, scratch_pool),
_("Failed to load properties"));
}
else
{
SVN_ERR_W(svn_wc__get_actual_props(&prophash, db, local_abspath,
result_pool, scratch_pool),
_("Failed to load properties"));
}
if (prophash)
*value = svn_hash_gets(prophash, name);
else
*value = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
validate_prop_against_node_kind(const char *name,
const char *path,
svn_node_kind_t node_kind,
apr_pool_t *pool)
{
const char *path_display
= svn_path_is_url(path) ? path : svn_dirent_local_style(path, pool);
switch (node_kind)
{
case svn_node_dir:
if (! svn_prop_is_known_svn_dir_prop(name)
&& svn_prop_is_known_svn_file_prop(name))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Cannot set '%s' on a directory ('%s')"),
name, path_display);
break;
case svn_node_file:
if (! svn_prop_is_known_svn_file_prop(name)
&& svn_prop_is_known_svn_dir_prop(name))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Cannot set '%s' on a file ('%s')"),
name,
path_display);
break;
default:
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("'%s' is not a file or directory"),
path_display);
}
return SVN_NO_ERROR;
}
struct getter_baton {
const svn_string_t *mime_type;
const char *local_abspath;
};
static svn_error_t *
get_file_for_validation(const svn_string_t **mime_type,
svn_stream_t *stream,
void *baton,
apr_pool_t *pool)
{
struct getter_baton *gb = baton;
if (mime_type)
*mime_type = gb->mime_type;
if (stream)
{
svn_stream_t *read_stream;
SVN_ERR(svn_stream_open_readonly(&read_stream, gb->local_abspath,
pool, pool));
SVN_ERR(svn_stream_copy3(read_stream, svn_stream_disown(stream, pool),
NULL, NULL, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
validate_eol_prop_against_file(const char *path,
svn_wc_canonicalize_svn_prop_get_file_t getter,
void *getter_baton,
apr_pool_t *pool)
{
svn_stream_t *translating_stream;
svn_error_t *err;
const svn_string_t *mime_type;
const char *path_display
= svn_path_is_url(path) ? path : svn_dirent_local_style(path, pool);
SVN_ERR(getter(&mime_type, NULL, getter_baton, pool));
if (mime_type && svn_mime_type_is_binary(mime_type->data))
return svn_error_createf
(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Can't set '%s': "
"file '%s' has binary mime type property"),
SVN_PROP_EOL_STYLE, path_display);
translating_stream = svn_subst_stream_translated(svn_stream_empty(pool),
"", FALSE, NULL, FALSE,
pool);
err = getter(NULL, translating_stream, getter_baton, pool);
err = svn_error_compose_create(err, svn_stream_close(translating_stream));
if (err && err->apr_err == SVN_ERR_IO_INCONSISTENT_EOL)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
_("File '%s' has inconsistent newlines"),
path_display);
return svn_error_trace(err);
}
static svn_error_t *
do_propset(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
const char *name,
const svn_string_t *value,
svn_boolean_t skip_checks,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
apr_hash_t *prophash;
svn_wc_notify_action_t notify_action;
svn_skel_t *work_item = NULL;
svn_boolean_t clear_recorded_info = FALSE;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_W(svn_wc__db_read_props(&prophash, db, local_abspath,
scratch_pool, scratch_pool),
_("Failed to load current properties"));
if (value && svn_prop_is_svn_prop(name))
{
const svn_string_t *new_value;
struct getter_baton gb;
gb.mime_type = svn_hash_gets(prophash, SVN_PROP_MIME_TYPE);
gb.local_abspath = local_abspath;
SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, name, value,
local_abspath, kind,
skip_checks,
get_file_for_validation, &gb,
scratch_pool));
value = new_value;
}
if (kind == svn_node_file
&& (strcmp(name, SVN_PROP_EXECUTABLE) == 0
|| strcmp(name, SVN_PROP_NEEDS_LOCK) == 0))
{
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
scratch_pool, scratch_pool));
}
if (kind == svn_node_file && strcmp(name, SVN_PROP_KEYWORDS) == 0)
{
svn_string_t *old_value = svn_hash_gets(prophash, SVN_PROP_KEYWORDS);
apr_hash_t *old_keywords, *new_keywords;
if (old_value)
SVN_ERR(svn_wc__expand_keywords(&old_keywords,
db, local_abspath, NULL,
old_value->data, TRUE,
scratch_pool, scratch_pool));
else
old_keywords = apr_hash_make(scratch_pool);
if (value)
SVN_ERR(svn_wc__expand_keywords(&new_keywords,
db, local_abspath, NULL,
value->data, TRUE,
scratch_pool, scratch_pool));
else
new_keywords = apr_hash_make(scratch_pool);
if (svn_subst_keywords_differ2(old_keywords, new_keywords, FALSE,
scratch_pool))
{
clear_recorded_info = TRUE;
}
}
else if (kind == svn_node_file && strcmp(name, SVN_PROP_EOL_STYLE) == 0)
{
svn_string_t *old_value = svn_hash_gets(prophash, SVN_PROP_EOL_STYLE);
if (((value == NULL) != (old_value == NULL))
|| (value && ! svn_string_compare(value, old_value)))
{
clear_recorded_info = TRUE;
}
}
if (svn_hash_gets(prophash, name) == NULL)
{
if (value == NULL)
notify_action = svn_wc_notify_property_deleted_nonexistent;
else
notify_action = svn_wc_notify_property_added;
}
else
{
if (value == NULL)
notify_action = svn_wc_notify_property_deleted;
else
notify_action = svn_wc_notify_property_modified;
}
svn_hash_sets(prophash, name, value);
SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, prophash,
clear_recorded_info, NULL, work_item,
scratch_pool));
if (work_item)
SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
if (notify_func)
{
svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
notify_action,
scratch_pool);
notify->prop_name = name;
notify->kind = kind;
(*notify_func)(notify_baton, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
struct propset_walk_baton
{
const char *propname;
const svn_string_t *propval;
svn_wc__db_t *db;
svn_boolean_t force;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
};
static svn_error_t *
propset_walk_cb(const char *local_abspath,
svn_node_kind_t kind,
void *walk_baton,
apr_pool_t *scratch_pool)
{
struct propset_walk_baton *wb = walk_baton;
svn_error_t *err;
err = do_propset(wb->db, local_abspath, kind, wb->propname, wb->propval,
wb->force, wb->notify_func, wb->notify_baton, scratch_pool);
if (err && (err->apr_err == SVN_ERR_ILLEGAL_TARGET
|| err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
}
return svn_error_trace(err);
}
svn_error_t *
svn_wc_prop_set4(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *name,
const svn_string_t *value,
svn_depth_t depth,
svn_boolean_t skip_checks,
const apr_array_header_t *changelist_filter,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
enum svn_prop_kind prop_kind = svn_property_kind2(name);
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_wc__db_t *db = wc_ctx->db;
if (prop_kind == svn_prop_entry_kind)
return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
_("Property '%s' is an entry property"), name);
if (prop_kind == svn_prop_wc_kind)
{
SVN_ERR_ASSERT(depth == svn_depth_empty);
return svn_error_trace(wcprop_set(wc_ctx->db, local_abspath,
name, value, scratch_pool));
}
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
if (status != svn_wc__db_status_normal
&& status != svn_wc__db_status_added
&& status != svn_wc__db_status_incomplete)
{
return svn_error_createf(SVN_ERR_WC_INVALID_SCHEDULE, NULL,
_("Can't set properties on '%s':"
" invalid status for updating properties."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
{
const char *dir_abspath;
if (kind == svn_node_dir)
dir_abspath = local_abspath;
else
dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
}
if (depth == svn_depth_empty || kind != svn_node_dir)
{
apr_hash_t *changelist_hash = NULL;
if (changelist_filter && changelist_filter->nelts)
SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
scratch_pool));
if (!svn_wc__internal_changelist_match(wc_ctx->db, local_abspath,
changelist_hash, scratch_pool))
return SVN_NO_ERROR;
SVN_ERR(do_propset(wc_ctx->db, local_abspath,
kind == svn_node_dir
? svn_node_dir
: svn_node_file,
name, value, skip_checks,
notify_func, notify_baton, scratch_pool));
}
else
{
struct propset_walk_baton wb;
wb.propname = name;
wb.propval = value;
wb.db = wc_ctx->db;
wb.force = skip_checks;
wb.notify_func = notify_func;
wb.notify_baton = notify_baton;
SVN_ERR(svn_wc__internal_walk_children(wc_ctx->db, local_abspath,
FALSE, changelist_filter,
propset_walk_cb, &wb,
depth,
cancel_func, cancel_baton,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
ensure_prop_is_regular_kind(const char *name)
{
enum svn_prop_kind prop_kind = svn_property_kind2(name);
if (prop_kind == svn_prop_entry_kind)
return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
_("Property '%s' is an entry property"), name);
if (prop_kind == svn_prop_wc_kind)
return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
_("Property '%s' is a WC property, not "
"a regular property"), name);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__canonicalize_props(apr_hash_t **prepared_props,
const char *local_abspath,
svn_node_kind_t node_kind,
const apr_hash_t *props,
svn_boolean_t skip_some_checks,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_string_t *mime_type;
struct getter_baton gb;
apr_hash_index_t *hi;
*prepared_props = apr_hash_make(result_pool);
mime_type = svn_hash_gets((apr_hash_t *)props, SVN_PROP_MIME_TYPE);
if (mime_type)
{
SVN_ERR(svn_wc_canonicalize_svn_prop(
&mime_type, SVN_PROP_MIME_TYPE, mime_type,
local_abspath, node_kind, skip_some_checks,
NULL, NULL, scratch_pool));
svn_hash_sets(*prepared_props, SVN_PROP_MIME_TYPE, mime_type);
}
gb.mime_type = mime_type;
gb.local_abspath = local_abspath;
for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)props); hi;
hi = apr_hash_next(hi))
{
const char *name = apr_hash_this_key(hi);
const svn_string_t *value = apr_hash_this_val(hi);
if (strcmp(name, SVN_PROP_MIME_TYPE) == 0)
continue;
SVN_ERR(ensure_prop_is_regular_kind(name));
SVN_ERR(svn_wc_canonicalize_svn_prop(
&value, name, value,
local_abspath, node_kind, skip_some_checks,
get_file_for_validation, &gb, scratch_pool));
svn_hash_sets(*prepared_props, name, value);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_canonicalize_svn_prop(const svn_string_t **propval_p,
const char *propname,
const svn_string_t *propval,
const char *path,
svn_node_kind_t kind,
svn_boolean_t skip_some_checks,
svn_wc_canonicalize_svn_prop_get_file_t getter,
void *getter_baton,
apr_pool_t *pool)
{
svn_stringbuf_t *new_value = NULL;
static const svn_string_t boolean_value
= SVN__STATIC_STRING(SVN_PROP_BOOLEAN_TRUE);
SVN_ERR(validate_prop_against_node_kind(propname, path, kind, pool));
if (!skip_some_checks && (strcmp(propname, SVN_PROP_EOL_STYLE) == 0))
{
svn_subst_eol_style_t eol_style;
const char *ignored_eol;
new_value = svn_stringbuf_create_from_string(propval, pool);
svn_stringbuf_strip_whitespace(new_value);
svn_subst_eol_style_from_value(&eol_style, &ignored_eol, new_value->data);
if (eol_style == svn_subst_eol_style_unknown)
return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
_("Unrecognized line ending style '%s' for '%s'"),
new_value->data,
svn_dirent_local_style(path, pool));
SVN_ERR(validate_eol_prop_against_file(path, getter, getter_baton,
pool));
}
else if (!skip_some_checks && (strcmp(propname, SVN_PROP_MIME_TYPE) == 0))
{
new_value = svn_stringbuf_create_from_string(propval, pool);
svn_stringbuf_strip_whitespace(new_value);
SVN_ERR(svn_mime_type_validate(new_value->data, pool));
}
else if (strcmp(propname, SVN_PROP_IGNORE) == 0
|| strcmp(propname, SVN_PROP_EXTERNALS) == 0
|| strcmp(propname, SVN_PROP_INHERITABLE_IGNORES) == 0
|| strcmp(propname, SVN_PROP_INHERITABLE_AUTO_PROPS) == 0)
{
if (propval->len == 0
|| propval->data[propval->len - 1] != '\n')
{
new_value = svn_stringbuf_create_from_string(propval, pool);
svn_stringbuf_appendbyte(new_value, '\n');
}
if (strcmp(propname, SVN_PROP_EXTERNALS) == 0)
{
apr_array_header_t *externals = NULL;
apr_array_header_t *duplicate_targets = NULL;
SVN_ERR(svn_wc_parse_externals_description3(&externals, path,
propval->data, FALSE,
pool));
SVN_ERR(svn_wc__externals_find_target_dups(&duplicate_targets,
externals,
pool,
pool));
if (duplicate_targets && duplicate_targets->nelts > 0)
{
const char *more_str = "";
if (duplicate_targets->nelts > 1)
{
more_str = apr_psprintf(pool,
_(" (%d more duplicate targets found)"),
duplicate_targets->nelts - 1);
}
return svn_error_createf(
SVN_ERR_WC_DUPLICATE_EXTERNALS_TARGET, NULL,
_("Invalid %s property on '%s': "
"target '%s' appears more than once%s"),
SVN_PROP_EXTERNALS,
svn_dirent_local_style(path, pool),
APR_ARRAY_IDX(duplicate_targets, 0, const char*),
more_str);
}
}
}
else if (strcmp(propname, SVN_PROP_KEYWORDS) == 0)
{
new_value = svn_stringbuf_create_from_string(propval, pool);
svn_stringbuf_strip_whitespace(new_value);
}
else if (svn_prop_is_boolean(propname))
{
propval = &boolean_value;
}
else if (strcmp(propname, SVN_PROP_MERGEINFO) == 0)
{
apr_hash_t *mergeinfo;
svn_string_t *new_value_str;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, propval->data, pool));
if (kind != svn_node_dir
&& svn_mergeinfo__is_noninheritable(mergeinfo, pool))
return svn_error_createf(
SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Cannot set non-inheritable mergeinfo on a non-directory ('%s')"),
svn_dirent_local_style(path, pool));
SVN_ERR(svn_mergeinfo_to_string(&new_value_str, mergeinfo, pool));
propval = new_value_str;
}
if (new_value)
*propval_p = svn_stringbuf__morph_into_string(new_value);
else
*propval_p = propval;
return SVN_NO_ERROR;
}
svn_boolean_t
svn_wc_is_normal_prop(const char *name)
{
enum svn_prop_kind kind = svn_property_kind2(name);
return (kind == svn_prop_regular_kind);
}
svn_boolean_t
svn_wc_is_wc_prop(const char *name)
{
enum svn_prop_kind kind = svn_property_kind2(name);
return (kind == svn_prop_wc_kind);
}
svn_boolean_t
svn_wc_is_entry_prop(const char *name)
{
enum svn_prop_kind kind = svn_property_kind2(name);
return (kind == svn_prop_entry_kind);
}
svn_error_t *
svn_wc__props_modified(svn_boolean_t *modified_p,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, modified_p, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_props_modified_p2(svn_boolean_t *modified_p,
svn_wc_context_t* wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__props_modified(modified_p,
wc_ctx->db,
local_abspath,
scratch_pool));
}
svn_error_t *
svn_wc__internal_propdiff(apr_array_header_t **propchanges,
apr_hash_t **original_props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *baseprops;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_read_pristine_props(&baseprops, db, local_abspath,
result_pool, scratch_pool));
if (original_props != NULL)
*original_props = baseprops;
if (propchanges != NULL)
{
apr_hash_t *actual_props;
if (baseprops == NULL)
baseprops = apr_hash_make(scratch_pool);
SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
result_pool, scratch_pool));
SVN_ERR(svn_prop_diffs(propchanges, actual_props, baseprops,
result_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_prop_diffs2(apr_array_header_t **propchanges,
apr_hash_t **original_props,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_wc__internal_propdiff(propchanges,
original_props, wc_ctx->db, local_abspath,
result_pool, scratch_pool));
}
svn_boolean_t
svn_wc__has_magic_property(const apr_array_header_t *properties)
{
int i;
for (i = 0; i < properties->nelts; i++)
{
const svn_prop_t *property = &APR_ARRAY_IDX(properties, i, svn_prop_t);
if (strcmp(property->name, SVN_PROP_EXECUTABLE) == 0
|| strcmp(property->name, SVN_PROP_KEYWORDS) == 0
|| strcmp(property->name, SVN_PROP_EOL_STYLE) == 0
|| strcmp(property->name, SVN_PROP_SPECIAL) == 0
|| strcmp(property->name, SVN_PROP_NEEDS_LOCK) == 0)
return TRUE;
}
return FALSE;
}
svn_error_t *
svn_wc__get_iprops(apr_array_header_t **inherited_props,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__db_read_inherited_props(inherited_props, NULL,
wc_ctx->db, local_abspath,
propname,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc__get_cached_iprop_children(apr_hash_t **iprop_paths,
svn_depth_t depth,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__db_get_children_with_cached_iprops(iprop_paths,
depth,
local_abspath,
wc_ctx->db,
result_pool,
scratch_pool));
return SVN_NO_ERROR;
}