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