1251881Speter/* 2251881Speter * conflicts.c: Tree conflicts. 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#include "cl-conflicts.h" 25251881Speter#include "svn_hash.h" 26251881Speter#include "svn_xml.h" 27251881Speter#include "svn_dirent_uri.h" 28251881Speter#include "svn_path.h" 29251881Speter#include "private/svn_token.h" 30251881Speter 31251881Speter#include "cl.h" 32251881Speter 33251881Speter#include "svn_private_config.h" 34251881Speter 35251881Speter 36251881Speter/* A map for svn_wc_conflict_action_t values to XML strings */ 37251881Speterstatic const svn_token_map_t map_conflict_action_xml[] = 38251881Speter{ 39251881Speter { "edit", svn_wc_conflict_action_edit }, 40251881Speter { "delete", svn_wc_conflict_action_delete }, 41251881Speter { "add", svn_wc_conflict_action_add }, 42251881Speter { "replace", svn_wc_conflict_action_replace }, 43251881Speter { NULL, 0 } 44251881Speter}; 45251881Speter 46251881Speter/* A map for svn_wc_conflict_reason_t values to XML strings */ 47251881Speterstatic const svn_token_map_t map_conflict_reason_xml[] = 48251881Speter{ 49251881Speter { "edit", svn_wc_conflict_reason_edited }, 50251881Speter { "delete", svn_wc_conflict_reason_deleted }, 51251881Speter { "missing", svn_wc_conflict_reason_missing }, 52251881Speter { "obstruction", svn_wc_conflict_reason_obstructed }, 53251881Speter { "add", svn_wc_conflict_reason_added }, 54251881Speter { "replace", svn_wc_conflict_reason_replaced }, 55251881Speter { "unversioned", svn_wc_conflict_reason_unversioned }, 56251881Speter { "moved-away", svn_wc_conflict_reason_moved_away }, 57251881Speter { "moved-here", svn_wc_conflict_reason_moved_here }, 58251881Speter { NULL, 0 } 59251881Speter}; 60251881Speter 61251881Speterstatic const svn_token_map_t map_conflict_kind_xml[] = 62251881Speter{ 63251881Speter { "text", svn_wc_conflict_kind_text }, 64251881Speter { "property", svn_wc_conflict_kind_property }, 65251881Speter { "tree", svn_wc_conflict_kind_tree }, 66251881Speter { NULL, 0 } 67251881Speter}; 68251881Speter 69251881Speter/* Return a localised string representation of the local part of a conflict; 70251881Speter NULL for non-localised odd cases. */ 71251881Speterstatic const char * 72251881Speterlocal_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason) 73251881Speter{ 74251881Speter switch (kind) 75251881Speter { 76251881Speter case svn_node_file: 77251881Speter switch (reason) 78251881Speter { 79251881Speter case svn_wc_conflict_reason_edited: 80251881Speter return _("local file edit"); 81251881Speter case svn_wc_conflict_reason_obstructed: 82251881Speter return _("local file obstruction"); 83251881Speter case svn_wc_conflict_reason_deleted: 84251881Speter return _("local file delete"); 85251881Speter case svn_wc_conflict_reason_missing: 86251881Speter return _("local file missing"); 87251881Speter case svn_wc_conflict_reason_unversioned: 88251881Speter return _("local file unversioned"); 89251881Speter case svn_wc_conflict_reason_added: 90251881Speter return _("local file add"); 91251881Speter case svn_wc_conflict_reason_replaced: 92251881Speter return _("local file replace"); 93251881Speter case svn_wc_conflict_reason_moved_away: 94251881Speter return _("local file moved away"); 95251881Speter case svn_wc_conflict_reason_moved_here: 96251881Speter return _("local file moved here"); 97251881Speter } 98251881Speter break; 99251881Speter case svn_node_dir: 100251881Speter switch (reason) 101251881Speter { 102251881Speter case svn_wc_conflict_reason_edited: 103251881Speter return _("local dir edit"); 104251881Speter case svn_wc_conflict_reason_obstructed: 105251881Speter return _("local dir obstruction"); 106251881Speter case svn_wc_conflict_reason_deleted: 107251881Speter return _("local dir delete"); 108251881Speter case svn_wc_conflict_reason_missing: 109251881Speter return _("local dir missing"); 110251881Speter case svn_wc_conflict_reason_unversioned: 111251881Speter return _("local dir unversioned"); 112251881Speter case svn_wc_conflict_reason_added: 113251881Speter return _("local dir add"); 114251881Speter case svn_wc_conflict_reason_replaced: 115251881Speter return _("local dir replace"); 116251881Speter case svn_wc_conflict_reason_moved_away: 117251881Speter return _("local dir moved away"); 118251881Speter case svn_wc_conflict_reason_moved_here: 119251881Speter return _("local dir moved here"); 120251881Speter } 121251881Speter break; 122251881Speter case svn_node_symlink: 123251881Speter case svn_node_none: 124251881Speter case svn_node_unknown: 125251881Speter break; 126251881Speter } 127251881Speter return NULL; 128251881Speter} 129251881Speter 130251881Speter/* Return a localised string representation of the incoming part of a 131251881Speter conflict; NULL for non-localised odd cases. */ 132251881Speterstatic const char * 133251881Speterincoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action) 134251881Speter{ 135251881Speter switch (kind) 136251881Speter { 137251881Speter case svn_node_file: 138251881Speter switch (action) 139251881Speter { 140251881Speter case svn_wc_conflict_action_edit: 141251881Speter return _("incoming file edit"); 142251881Speter case svn_wc_conflict_action_add: 143251881Speter return _("incoming file add"); 144251881Speter case svn_wc_conflict_action_delete: 145251881Speter return _("incoming file delete"); 146251881Speter case svn_wc_conflict_action_replace: 147251881Speter return _("incoming file replace"); 148251881Speter } 149251881Speter break; 150251881Speter case svn_node_dir: 151251881Speter switch (action) 152251881Speter { 153251881Speter case svn_wc_conflict_action_edit: 154251881Speter return _("incoming dir edit"); 155251881Speter case svn_wc_conflict_action_add: 156251881Speter return _("incoming dir add"); 157251881Speter case svn_wc_conflict_action_delete: 158251881Speter return _("incoming dir delete"); 159251881Speter case svn_wc_conflict_action_replace: 160251881Speter return _("incoming dir replace"); 161251881Speter } 162251881Speter break; 163251881Speter case svn_node_symlink: 164251881Speter case svn_node_none: 165251881Speter case svn_node_unknown: 166251881Speter break; 167251881Speter } 168251881Speter return NULL; 169251881Speter} 170251881Speter 171251881Speter/* Return a localised string representation of the operation part of a 172251881Speter conflict. */ 173251881Speterstatic const char * 174251881Speteroperation_str(svn_wc_operation_t operation) 175251881Speter{ 176251881Speter switch (operation) 177251881Speter { 178251881Speter case svn_wc_operation_update: return _("upon update"); 179251881Speter case svn_wc_operation_switch: return _("upon switch"); 180251881Speter case svn_wc_operation_merge: return _("upon merge"); 181251881Speter case svn_wc_operation_none: return _("upon none"); 182251881Speter } 183251881Speter SVN_ERR_MALFUNCTION_NO_RETURN(); 184251881Speter return NULL; 185251881Speter} 186251881Speter 187251881Spetersvn_error_t * 188251881Spetersvn_cl__get_human_readable_prop_conflict_description( 189251881Speter const char **desc, 190251881Speter const svn_wc_conflict_description2_t *conflict, 191251881Speter apr_pool_t *pool) 192251881Speter{ 193251881Speter const char *reason_str, *action_str; 194251881Speter 195251881Speter /* We provide separately translatable strings for the values that we 196251881Speter * know about, and a fall-back in case any other values occur. */ 197251881Speter switch (conflict->reason) 198251881Speter { 199251881Speter case svn_wc_conflict_reason_edited: 200251881Speter reason_str = _("local edit"); 201251881Speter break; 202251881Speter case svn_wc_conflict_reason_added: 203251881Speter reason_str = _("local add"); 204251881Speter break; 205251881Speter case svn_wc_conflict_reason_deleted: 206251881Speter reason_str = _("local delete"); 207251881Speter break; 208251881Speter case svn_wc_conflict_reason_obstructed: 209251881Speter reason_str = _("local obstruction"); 210251881Speter break; 211251881Speter default: 212251881Speter reason_str = apr_psprintf(pool, _("local %s"), 213251881Speter svn_token__to_word(map_conflict_reason_xml, 214251881Speter conflict->reason)); 215251881Speter break; 216251881Speter } 217251881Speter switch (conflict->action) 218251881Speter { 219251881Speter case svn_wc_conflict_action_edit: 220251881Speter action_str = _("incoming edit"); 221251881Speter break; 222251881Speter case svn_wc_conflict_action_add: 223251881Speter action_str = _("incoming add"); 224251881Speter break; 225251881Speter case svn_wc_conflict_action_delete: 226251881Speter action_str = _("incoming delete"); 227251881Speter break; 228251881Speter default: 229251881Speter action_str = apr_psprintf(pool, _("incoming %s"), 230251881Speter svn_token__to_word(map_conflict_action_xml, 231251881Speter conflict->action)); 232251881Speter break; 233251881Speter } 234251881Speter SVN_ERR_ASSERT(reason_str && action_str); 235251881Speter *desc = apr_psprintf(pool, _("%s, %s %s"), 236251881Speter reason_str, action_str, 237251881Speter operation_str(conflict->operation)); 238251881Speter return SVN_NO_ERROR; 239251881Speter} 240251881Speter 241251881Spetersvn_error_t * 242251881Spetersvn_cl__get_human_readable_tree_conflict_description( 243251881Speter const char **desc, 244251881Speter const svn_wc_conflict_description2_t *conflict, 245251881Speter apr_pool_t *pool) 246251881Speter{ 247251881Speter const char *action, *reason, *operation; 248251881Speter svn_node_kind_t incoming_kind; 249251881Speter 250251881Speter /* Determine the node kind of the incoming change. */ 251251881Speter incoming_kind = svn_node_unknown; 252251881Speter if (conflict->action == svn_wc_conflict_action_edit || 253251881Speter conflict->action == svn_wc_conflict_action_delete) 254251881Speter { 255251881Speter /* Change is acting on 'src_left' version of the node. */ 256251881Speter if (conflict->src_left_version) 257251881Speter incoming_kind = conflict->src_left_version->node_kind; 258251881Speter } 259251881Speter else if (conflict->action == svn_wc_conflict_action_add || 260251881Speter conflict->action == svn_wc_conflict_action_replace) 261251881Speter { 262251881Speter /* Change is acting on 'src_right' version of the node. 263251881Speter * 264251881Speter * ### For 'replace', the node kind is ambiguous. However, src_left 265251881Speter * ### is NULL for replace, so we must use src_right. */ 266251881Speter if (conflict->src_right_version) 267251881Speter incoming_kind = conflict->src_right_version->node_kind; 268251881Speter } 269251881Speter 270251881Speter reason = local_reason_str(conflict->node_kind, conflict->reason); 271251881Speter action = incoming_action_str(incoming_kind, conflict->action); 272251881Speter operation = operation_str(conflict->operation); 273251881Speter SVN_ERR_ASSERT(operation); 274251881Speter 275251881Speter if (action && reason) 276251881Speter { 277251881Speter *desc = apr_psprintf(pool, _("%s, %s %s"), 278251881Speter reason, action, operation); 279251881Speter } 280251881Speter else 281251881Speter { 282251881Speter /* A catch-all message for very rare or nominally impossible cases. 283251881Speter It will not be pretty, but is closer to an internal error than 284251881Speter an ordinary user-facing string. */ 285251881Speter *desc = apr_psprintf(pool, _("local: %s %s incoming: %s %s %s"), 286251881Speter svn_node_kind_to_word(conflict->node_kind), 287251881Speter svn_token__to_word(map_conflict_reason_xml, 288251881Speter conflict->reason), 289251881Speter svn_node_kind_to_word(incoming_kind), 290251881Speter svn_token__to_word(map_conflict_action_xml, 291251881Speter conflict->action), 292251881Speter operation); 293251881Speter } 294251881Speter return SVN_NO_ERROR; 295251881Speter} 296251881Speter 297251881Speter 298251881Speter/* Helper for svn_cl__append_tree_conflict_info_xml(). 299251881Speter * Appends the attributes of the given VERSION to ATT_HASH. 300251881Speter * SIDE is the content of the version tag's side="..." attribute, 301251881Speter * currently one of "source-left" or "source-right".*/ 302251881Speterstatic svn_error_t * 303251881Speteradd_conflict_version_xml(svn_stringbuf_t **pstr, 304251881Speter const char *side, 305251881Speter const svn_wc_conflict_version_t *version, 306251881Speter apr_pool_t *pool) 307251881Speter{ 308251881Speter apr_hash_t *att_hash = apr_hash_make(pool); 309251881Speter 310251881Speter 311251881Speter svn_hash_sets(att_hash, "side", side); 312251881Speter 313251881Speter if (version->repos_url) 314251881Speter svn_hash_sets(att_hash, "repos-url", version->repos_url); 315251881Speter 316251881Speter if (version->path_in_repos) 317251881Speter svn_hash_sets(att_hash, "path-in-repos", version->path_in_repos); 318251881Speter 319251881Speter if (SVN_IS_VALID_REVNUM(version->peg_rev)) 320251881Speter svn_hash_sets(att_hash, "revision", apr_ltoa(pool, version->peg_rev)); 321251881Speter 322251881Speter if (version->node_kind != svn_node_unknown) 323251881Speter svn_hash_sets(att_hash, "kind", 324251881Speter svn_cl__node_kind_str_xml(version->node_kind)); 325251881Speter 326251881Speter svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing, 327251881Speter "version", att_hash); 328251881Speter return SVN_NO_ERROR; 329251881Speter} 330251881Speter 331251881Speter 332251881Speterstatic svn_error_t * 333251881Speterappend_tree_conflict_info_xml(svn_stringbuf_t *str, 334251881Speter const svn_wc_conflict_description2_t *conflict, 335251881Speter apr_pool_t *pool) 336251881Speter{ 337251881Speter apr_hash_t *att_hash = apr_hash_make(pool); 338251881Speter const char *tmp; 339251881Speter 340251881Speter svn_hash_sets(att_hash, "victim", 341251881Speter svn_dirent_basename(conflict->local_abspath, pool)); 342251881Speter 343251881Speter svn_hash_sets(att_hash, "kind", 344251881Speter svn_cl__node_kind_str_xml(conflict->node_kind)); 345251881Speter 346251881Speter svn_hash_sets(att_hash, "operation", 347251881Speter svn_cl__operation_str_xml(conflict->operation, pool)); 348251881Speter 349251881Speter tmp = svn_token__to_word(map_conflict_action_xml, conflict->action); 350251881Speter svn_hash_sets(att_hash, "action", tmp); 351251881Speter 352251881Speter tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason); 353251881Speter svn_hash_sets(att_hash, "reason", tmp); 354251881Speter 355251881Speter /* Open the tree-conflict tag. */ 356251881Speter svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal, 357251881Speter "tree-conflict", att_hash); 358251881Speter 359251881Speter /* Add child tags for OLDER_VERSION and THEIR_VERSION. */ 360251881Speter 361251881Speter if (conflict->src_left_version) 362251881Speter SVN_ERR(add_conflict_version_xml(&str, 363251881Speter "source-left", 364251881Speter conflict->src_left_version, 365251881Speter pool)); 366251881Speter 367251881Speter if (conflict->src_right_version) 368251881Speter SVN_ERR(add_conflict_version_xml(&str, 369251881Speter "source-right", 370251881Speter conflict->src_right_version, 371251881Speter pool)); 372251881Speter 373251881Speter svn_xml_make_close_tag(&str, pool, "tree-conflict"); 374251881Speter 375251881Speter return SVN_NO_ERROR; 376251881Speter} 377251881Speter 378251881Spetersvn_error_t * 379251881Spetersvn_cl__append_conflict_info_xml(svn_stringbuf_t *str, 380251881Speter const svn_wc_conflict_description2_t *conflict, 381251881Speter apr_pool_t *scratch_pool) 382251881Speter{ 383251881Speter apr_hash_t *att_hash; 384251881Speter const char *kind; 385251881Speter if (conflict->kind == svn_wc_conflict_kind_tree) 386251881Speter { 387251881Speter /* Uses other element type */ 388251881Speter return svn_error_trace( 389251881Speter append_tree_conflict_info_xml(str, conflict, scratch_pool)); 390251881Speter } 391251881Speter 392251881Speter att_hash = apr_hash_make(scratch_pool); 393251881Speter 394251881Speter svn_hash_sets(att_hash, "operation", 395251881Speter svn_cl__operation_str_xml(conflict->operation, scratch_pool)); 396251881Speter 397251881Speter 398251881Speter kind = svn_token__to_word(map_conflict_kind_xml, conflict->kind); 399251881Speter svn_hash_sets(att_hash, "type", kind); 400251881Speter 401251881Speter svn_hash_sets(att_hash, "operation", 402251881Speter svn_cl__operation_str_xml(conflict->operation, scratch_pool)); 403251881Speter 404251881Speter 405251881Speter /* "<conflict>" */ 406251881Speter svn_xml_make_open_tag_hash(&str, scratch_pool, 407251881Speter svn_xml_normal, "conflict", att_hash); 408251881Speter 409251881Speter if (conflict->src_left_version) 410251881Speter SVN_ERR(add_conflict_version_xml(&str, 411251881Speter "source-left", 412251881Speter conflict->src_left_version, 413251881Speter scratch_pool)); 414251881Speter 415251881Speter if (conflict->src_right_version) 416251881Speter SVN_ERR(add_conflict_version_xml(&str, 417251881Speter "source-right", 418251881Speter conflict->src_right_version, 419251881Speter scratch_pool)); 420251881Speter 421251881Speter switch (conflict->kind) 422251881Speter { 423251881Speter case svn_wc_conflict_kind_text: 424251881Speter /* "<prev-base-file> xx </prev-base-file>" */ 425251881Speter svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-base-file", 426251881Speter conflict->base_abspath); 427251881Speter 428251881Speter /* "<prev-wc-file> xx </prev-wc-file>" */ 429251881Speter svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-wc-file", 430251881Speter conflict->my_abspath); 431251881Speter 432251881Speter /* "<cur-base-file> xx </cur-base-file>" */ 433251881Speter svn_cl__xml_tagged_cdata(&str, scratch_pool, "cur-base-file", 434251881Speter conflict->their_abspath); 435251881Speter 436251881Speter break; 437251881Speter 438251881Speter case svn_wc_conflict_kind_property: 439251881Speter /* "<prop-file> xx </prop-file>" */ 440251881Speter svn_cl__xml_tagged_cdata(&str, scratch_pool, "prop-file", 441251881Speter conflict->their_abspath); 442251881Speter break; 443251881Speter 444251881Speter default: 445251881Speter case svn_wc_conflict_kind_tree: 446251881Speter SVN_ERR_MALFUNCTION(); /* Handled separately */ 447251881Speter break; 448251881Speter } 449251881Speter 450251881Speter /* "</conflict>" */ 451251881Speter svn_xml_make_close_tag(&str, scratch_pool, "conflict"); 452251881Speter 453251881Speter return SVN_NO_ERROR; 454251881Speter} 455