1251881Speter/*
2251881Speter * status.c:  the command-line's portion of the "svn status" command
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/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter#include "svn_hash.h"
30251881Speter#include "svn_cmdline.h"
31251881Speter#include "svn_wc.h"
32251881Speter#include "svn_dirent_uri.h"
33251881Speter#include "svn_xml.h"
34251881Speter#include "svn_time.h"
35251881Speter#include "cl.h"
36251881Speter#include "svn_private_config.h"
37251881Speter#include "cl-conflicts.h"
38251881Speter#include "private/svn_wc_private.h"
39251881Speter
40251881Speter/* Return the single character representation of STATUS */
41251881Speterstatic char
42251881Spetergenerate_status_code(enum svn_wc_status_kind status)
43251881Speter{
44251881Speter  switch (status)
45251881Speter    {
46251881Speter    case svn_wc_status_none:        return ' ';
47251881Speter    case svn_wc_status_normal:      return ' ';
48251881Speter    case svn_wc_status_added:       return 'A';
49251881Speter    case svn_wc_status_missing:     return '!';
50251881Speter    case svn_wc_status_incomplete:  return '!';
51251881Speter    case svn_wc_status_deleted:     return 'D';
52251881Speter    case svn_wc_status_replaced:    return 'R';
53251881Speter    case svn_wc_status_modified:    return 'M';
54251881Speter    case svn_wc_status_conflicted:  return 'C';
55251881Speter    case svn_wc_status_obstructed:  return '~';
56251881Speter    case svn_wc_status_ignored:     return 'I';
57251881Speter    case svn_wc_status_external:    return 'X';
58251881Speter    case svn_wc_status_unversioned: return '?';
59251881Speter    default:                        return '?';
60251881Speter    }
61251881Speter}
62251881Speter
63251881Speter/* Return the combined STATUS as shown in 'svn status' based
64251881Speter   on the node status and text status */
65251881Speterstatic enum svn_wc_status_kind
66251881Spetercombined_status(const svn_client_status_t *status)
67251881Speter{
68251881Speter  enum svn_wc_status_kind new_status = status->node_status;
69251881Speter
70251881Speter  switch (status->node_status)
71251881Speter    {
72251881Speter      case svn_wc_status_conflicted:
73251881Speter        if (!status->versioned && status->conflicted)
74251881Speter          {
75251881Speter            /* Report unversioned tree conflict victims as missing: '!' */
76251881Speter            new_status = svn_wc_status_missing;
77251881Speter            break;
78251881Speter          }
79251881Speter        /* fall through */
80251881Speter      case svn_wc_status_modified:
81251881Speter        /* This value might be the property status */
82251881Speter        new_status = status->text_status;
83251881Speter        break;
84251881Speter      default:
85251881Speter        break;
86251881Speter    }
87251881Speter
88251881Speter  return new_status;
89251881Speter}
90251881Speter
91251881Speter/* Return the combined repository STATUS as shown in 'svn status' based
92251881Speter   on the repository node status and repository text status */
93251881Speterstatic enum svn_wc_status_kind
94251881Spetercombined_repos_status(const svn_client_status_t *status)
95251881Speter{
96251881Speter  if (status->repos_node_status == svn_wc_status_modified)
97251881Speter    return status->repos_text_status;
98251881Speter
99251881Speter  return status->repos_node_status;
100251881Speter}
101251881Speter
102251881Speter/* Return the single character representation of the switched column
103251881Speter   status. */
104251881Speterstatic char
105251881Spetergenerate_switch_column_code(const svn_client_status_t *status)
106251881Speter{
107251881Speter  if (status->switched)
108251881Speter    return 'S';
109251881Speter  else if (status->file_external)
110251881Speter    return 'X';
111251881Speter  else
112251881Speter    return ' ';
113251881Speter}
114251881Speter
115251881Speter/* Return the detailed string representation of STATUS */
116251881Speterstatic const char *
117251881Spetergenerate_status_desc(enum svn_wc_status_kind status)
118251881Speter{
119251881Speter  switch (status)
120251881Speter    {
121251881Speter    case svn_wc_status_none:        return "none";
122251881Speter    case svn_wc_status_normal:      return "normal";
123251881Speter    case svn_wc_status_added:       return "added";
124251881Speter    case svn_wc_status_missing:     return "missing";
125251881Speter    case svn_wc_status_incomplete:  return "incomplete";
126251881Speter    case svn_wc_status_deleted:     return "deleted";
127251881Speter    case svn_wc_status_replaced:    return "replaced";
128251881Speter    case svn_wc_status_modified:    return "modified";
129251881Speter    case svn_wc_status_conflicted:  return "conflicted";
130251881Speter    case svn_wc_status_obstructed:  return "obstructed";
131251881Speter    case svn_wc_status_ignored:     return "ignored";
132251881Speter    case svn_wc_status_external:    return "external";
133251881Speter    case svn_wc_status_unversioned: return "unversioned";
134251881Speter    default:
135251881Speter      SVN_ERR_MALFUNCTION_NO_RETURN();
136251881Speter    }
137251881Speter}
138251881Speter
139251881Speter/* Make a relative path containing '..' elements as needed.
140262253Speter   TARGET_ABSPATH shall be the absolute version of TARGET_PATH.
141299742Sdim   TARGET_ABSPATH, TARGET_PATH and LOCAL_ABSPATH shall be canonical
142251881Speter
143262253Speter   If above conditions are met, a relative path that leads to PATH
144262253Speter   from TARGET_PATH is returned, but there is no error checking involved.
145251881Speter
146262253Speter   The returned path is allocated from RESULT_POOL, all other
147299742Sdim   allocations are made in SCRATCH_POOL.
148299742Sdim
149299742Sdim   Note that it is not possible to just join the resulting path to
150299742Sdim   reconstruct the real path as the "../" paths are relative from
151299742Sdim   a different base than the normal relative paths!
152299742Sdim */
153251881Speterstatic const char *
154262253Spetermake_relpath(const char *target_abspath,
155251881Speter             const char *target_path,
156299742Sdim             const char *local_abspath,
157251881Speter             apr_pool_t *result_pool,
158251881Speter             apr_pool_t *scratch_pool)
159251881Speter{
160251881Speter  const char *la;
161251881Speter  const char *parent_dir_els = "";
162299742Sdim  const char *t_relpath;
163299742Sdim  const char *p_relpath;
164251881Speter
165299742Sdim#ifdef SVN_DEBUG
166299742Sdim  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath));
167299742Sdim#endif
168262253Speter
169299742Sdim  t_relpath = svn_dirent_skip_ancestor(target_abspath, local_abspath);
170262253Speter
171299742Sdim  if (t_relpath)
172299742Sdim    return svn_dirent_join(target_path, t_relpath, result_pool);
173299742Sdim
174251881Speter  /* An example:
175251881Speter   *  relative_to_path = /a/b/c
176262253Speter   *  path             = /a/x/y/z
177251881Speter   *  result           = ../../x/y/z
178251881Speter   *
179251881Speter   * Another example (Windows specific):
180251881Speter   *  relative_to_path = F:/wc
181262253Speter   *  path             = C:/wc
182251881Speter   *  result           = C:/wc
183251881Speter   */
184251881Speter  /* Skip the common ancestor of both paths, here '/a'. */
185299742Sdim  la = svn_dirent_get_longest_ancestor(target_abspath, local_abspath,
186251881Speter                                       scratch_pool);
187251881Speter  if (*la == '\0')
188251881Speter    {
189251881Speter      /* Nothing in common: E.g. C:/ vs F:/ on Windows */
190299742Sdim      return apr_pstrdup(result_pool, local_abspath);
191251881Speter    }
192299742Sdim  t_relpath = svn_dirent_skip_ancestor(la, target_abspath);
193299742Sdim  p_relpath = svn_dirent_skip_ancestor(la, local_abspath);
194251881Speter
195251881Speter  /* In above example, we'd now have:
196251881Speter   *  relative_to_path = b/c
197262253Speter   *  path             = x/y/z */
198251881Speter
199251881Speter  /* Count the elements of relative_to_path and prepend as many '..' elements
200262253Speter   * to path. */
201299742Sdim  while (*t_relpath)
202251881Speter    {
203299742Sdim      t_relpath = svn_dirent_dirname(t_relpath, scratch_pool);
204251881Speter      parent_dir_els = svn_dirent_join(parent_dir_els, "..", scratch_pool);
205251881Speter    }
206251881Speter
207299742Sdim  /* This returns a ../ style path relative from the status target */
208299742Sdim  return svn_dirent_join(parent_dir_els, p_relpath, result_pool);
209251881Speter}
210251881Speter
211251881Speter
212251881Speter/* Print STATUS and PATH in a format determined by DETAILED and
213251881Speter   SHOW_LAST_COMMITTED. */
214251881Speterstatic svn_error_t *
215262253Speterprint_status(const char *target_abspath,
216262253Speter             const char *target_path,
217262253Speter             const char *path,
218251881Speter             svn_boolean_t detailed,
219251881Speter             svn_boolean_t show_last_committed,
220251881Speter             svn_boolean_t repos_locks,
221251881Speter             const svn_client_status_t *status,
222251881Speter             unsigned int *text_conflicts,
223251881Speter             unsigned int *prop_conflicts,
224251881Speter             unsigned int *tree_conflicts,
225251881Speter             svn_client_ctx_t *ctx,
226251881Speter             apr_pool_t *pool)
227251881Speter{
228251881Speter  enum svn_wc_status_kind node_status = status->node_status;
229251881Speter  enum svn_wc_status_kind prop_status = status->prop_status;
230251881Speter  char tree_status_code = ' ';
231251881Speter  const char *tree_desc_line = "";
232251881Speter  const char *moved_from_line = "";
233251881Speter  const char *moved_to_line = "";
234251881Speter
235251881Speter  /* For historic reasons svn ignores the property status for added nodes, even
236251881Speter     if these nodes were copied and have local property changes.
237251881Speter
238251881Speter     Note that it doesn't do this on replacements, or children of copies.
239251881Speter
240251881Speter     ### Our test suite would catch more errors if we reported property
241251881Speter         changes on copies. */
242251881Speter  if (node_status == svn_wc_status_added)
243251881Speter      prop_status = svn_wc_status_none;
244251881Speter
245251881Speter  /* To indicate this node is the victim of a tree conflict, we show
246251881Speter     'C' in the tree-conflict column, overriding any other status.
247251881Speter     We also print a separate line describing the nature of the tree
248251881Speter     conflict. */
249251881Speter  if (status->conflicted)
250251881Speter    {
251251881Speter      const char *desc;
252251881Speter      const char *local_abspath = status->local_abspath;
253251881Speter      svn_boolean_t text_conflicted;
254251881Speter      svn_boolean_t prop_conflicted;
255251881Speter      svn_boolean_t tree_conflicted;
256251881Speter
257251881Speter      if (status->versioned)
258251881Speter        {
259251881Speter          svn_error_t *err;
260251881Speter
261251881Speter          err = svn_wc_conflicted_p3(&text_conflicted,
262251881Speter                                     &prop_conflicted,
263251881Speter                                     &tree_conflicted, ctx->wc_ctx,
264251881Speter                                     local_abspath, pool);
265251881Speter
266251881Speter          if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
267251881Speter            {
268251881Speter              svn_error_clear(err);
269251881Speter              text_conflicted = FALSE;
270251881Speter              prop_conflicted = FALSE;
271251881Speter              tree_conflicted = FALSE;
272251881Speter            }
273251881Speter          else
274251881Speter            SVN_ERR(err);
275251881Speter        }
276251881Speter      else
277251881Speter        {
278251881Speter          text_conflicted = FALSE;
279251881Speter          prop_conflicted = FALSE;
280251881Speter          tree_conflicted = TRUE;
281251881Speter        }
282251881Speter
283251881Speter      if (tree_conflicted)
284251881Speter        {
285251881Speter          const svn_wc_conflict_description2_t *tree_conflict;
286251881Speter          SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, ctx->wc_ctx,
287251881Speter                                            local_abspath, pool, pool));
288251881Speter          SVN_ERR_ASSERT(tree_conflict != NULL);
289251881Speter
290251881Speter          tree_status_code = 'C';
291251881Speter          SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
292251881Speter                            &desc, tree_conflict, pool));
293251881Speter          tree_desc_line = apr_psprintf(pool, "\n      >   %s", desc);
294251881Speter          (*tree_conflicts)++;
295251881Speter        }
296251881Speter      else if (text_conflicted)
297251881Speter        (*text_conflicts)++;
298251881Speter      else if (prop_conflicted)
299251881Speter        (*prop_conflicts)++;
300251881Speter    }
301251881Speter
302251881Speter  /* Note that moved-from and moved-to information is only available in STATUS
303251881Speter   * for (op-)roots of a move. Those are exactly the nodes we want to show
304251881Speter   * move info for in 'svn status'. See also comments in svn_wc_status3_t. */
305251881Speter  if (status->moved_from_abspath && status->moved_to_abspath &&
306251881Speter      strcmp(status->moved_from_abspath, status->moved_to_abspath) == 0)
307251881Speter    {
308251881Speter      const char *relpath;
309251881Speter
310262253Speter      relpath = make_relpath(target_abspath, target_path,
311262253Speter                             status->moved_from_abspath,
312251881Speter                             pool, pool);
313251881Speter      relpath = svn_dirent_local_style(relpath, pool);
314251881Speter      moved_from_line = apr_pstrcat(pool, "\n        > ",
315251881Speter                                    apr_psprintf(pool,
316251881Speter                                                 _("swapped places with %s"),
317251881Speter                                                 relpath),
318299742Sdim                                    SVN_VA_NULL);
319251881Speter    }
320251881Speter  else if (status->moved_from_abspath || status->moved_to_abspath)
321251881Speter    {
322251881Speter      const char *relpath;
323251881Speter
324251881Speter      if (status->moved_from_abspath)
325251881Speter        {
326262253Speter          relpath = make_relpath(target_abspath, target_path,
327262253Speter                                 status->moved_from_abspath,
328251881Speter                                 pool, pool);
329251881Speter          relpath = svn_dirent_local_style(relpath, pool);
330251881Speter          moved_from_line = apr_pstrcat(pool, "\n        > ",
331251881Speter                                        apr_psprintf(pool, _("moved from %s"),
332251881Speter                                                     relpath),
333299742Sdim                                        SVN_VA_NULL);
334251881Speter        }
335251881Speter
336251881Speter      if (status->moved_to_abspath)
337251881Speter        {
338262253Speter          relpath = make_relpath(target_abspath, target_path,
339262253Speter                                 status->moved_to_abspath,
340251881Speter                                 pool, pool);
341251881Speter          relpath = svn_dirent_local_style(relpath, pool);
342251881Speter          moved_to_line = apr_pstrcat(pool, "\n        > ",
343251881Speter                                      apr_psprintf(pool, _("moved to %s"),
344251881Speter                                                   relpath),
345299742Sdim                                      SVN_VA_NULL);
346251881Speter        }
347251881Speter    }
348251881Speter
349262253Speter  path = svn_dirent_local_style(path, pool);
350262253Speter
351251881Speter  if (detailed)
352251881Speter    {
353251881Speter      char ood_status, lock_status;
354251881Speter      const char *working_rev;
355251881Speter
356251881Speter      if (! status->versioned)
357251881Speter        working_rev = "";
358251881Speter      else if (status->copied
359251881Speter               || ! SVN_IS_VALID_REVNUM(status->revision))
360251881Speter        working_rev = "-";
361251881Speter      else
362251881Speter        working_rev = apr_psprintf(pool, "%ld", status->revision);
363251881Speter
364251881Speter      if (status->repos_node_status != svn_wc_status_none)
365251881Speter        ood_status = '*';
366251881Speter      else
367251881Speter        ood_status = ' ';
368251881Speter
369251881Speter      if (repos_locks)
370251881Speter        {
371251881Speter          if (status->repos_lock)
372251881Speter            {
373251881Speter              if (status->lock)
374251881Speter                {
375251881Speter                  if (strcmp(status->repos_lock->token, status->lock->token)
376251881Speter                      == 0)
377251881Speter                    lock_status = 'K';
378251881Speter                  else
379251881Speter                    lock_status = 'T';
380251881Speter                }
381251881Speter              else
382251881Speter                lock_status = 'O';
383251881Speter            }
384251881Speter          else if (status->lock)
385251881Speter            lock_status = 'B';
386251881Speter          else
387251881Speter            lock_status = ' ';
388251881Speter        }
389251881Speter      else
390251881Speter        lock_status = (status->lock) ? 'K' : ' ';
391251881Speter
392251881Speter      if (show_last_committed)
393251881Speter        {
394251881Speter          const char *commit_rev;
395251881Speter          const char *commit_author;
396251881Speter
397251881Speter          if (SVN_IS_VALID_REVNUM(status->changed_rev))
398251881Speter            commit_rev = apr_psprintf(pool, "%ld", status->changed_rev);
399251881Speter          else if (status->versioned)
400251881Speter            commit_rev = " ? ";
401251881Speter          else
402251881Speter            commit_rev = "";
403251881Speter
404251881Speter          if (status->changed_author)
405251881Speter            commit_author = status->changed_author;
406251881Speter          else if (status->versioned)
407251881Speter            commit_author = " ? ";
408251881Speter          else
409251881Speter            commit_author = "";
410251881Speter
411251881Speter          SVN_ERR
412251881Speter            (svn_cmdline_printf(pool,
413251881Speter                                "%c%c%c%c%c%c%c %c %8s %8s %-12s %s%s%s%s\n",
414251881Speter                                generate_status_code(combined_status(status)),
415251881Speter                                generate_status_code(prop_status),
416251881Speter                                status->wc_is_locked ? 'L' : ' ',
417251881Speter                                status->copied ? '+' : ' ',
418251881Speter                                generate_switch_column_code(status),
419251881Speter                                lock_status,
420251881Speter                                tree_status_code,
421251881Speter                                ood_status,
422251881Speter                                working_rev,
423251881Speter                                commit_rev,
424251881Speter                                commit_author,
425251881Speter                                path,
426251881Speter                                moved_to_line,
427251881Speter                                moved_from_line,
428251881Speter                                tree_desc_line));
429251881Speter        }
430251881Speter      else
431251881Speter        SVN_ERR(
432251881Speter           svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %8s   %s%s%s%s\n",
433251881Speter                              generate_status_code(combined_status(status)),
434251881Speter                              generate_status_code(prop_status),
435251881Speter                              status->wc_is_locked ? 'L' : ' ',
436251881Speter                              status->copied ? '+' : ' ',
437251881Speter                              generate_switch_column_code(status),
438251881Speter                              lock_status,
439251881Speter                              tree_status_code,
440251881Speter                              ood_status,
441251881Speter                              working_rev,
442251881Speter                              path,
443251881Speter                              moved_to_line,
444251881Speter                              moved_from_line,
445251881Speter                              tree_desc_line));
446251881Speter    }
447251881Speter  else
448251881Speter    SVN_ERR(
449251881Speter       svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %s%s%s%s\n",
450251881Speter                          generate_status_code(combined_status(status)),
451251881Speter                          generate_status_code(prop_status),
452251881Speter                          status->wc_is_locked ? 'L' : ' ',
453251881Speter                          status->copied ? '+' : ' ',
454251881Speter                          generate_switch_column_code(status),
455251881Speter                          ((status->lock)
456251881Speter                           ? 'K' : ' '),
457251881Speter                          tree_status_code,
458251881Speter                          path,
459251881Speter                          moved_to_line,
460251881Speter                          moved_from_line,
461251881Speter                          tree_desc_line));
462251881Speter
463251881Speter  return svn_cmdline_fflush(stdout);
464251881Speter}
465251881Speter
466251881Speter
467251881Spetersvn_error_t *
468262253Spetersvn_cl__print_status_xml(const char *target_abspath,
469262253Speter                         const char *target_path,
470251881Speter                         const char *path,
471251881Speter                         const svn_client_status_t *status,
472251881Speter                         svn_client_ctx_t *ctx,
473251881Speter                         apr_pool_t *pool)
474251881Speter{
475251881Speter  svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
476251881Speter  apr_hash_t *att_hash;
477251881Speter  const char *local_abspath = status->local_abspath;
478251881Speter  svn_boolean_t tree_conflicted = FALSE;
479251881Speter
480251881Speter  if (status->node_status == svn_wc_status_none
481251881Speter      && status->repos_node_status == svn_wc_status_none)
482251881Speter    return SVN_NO_ERROR;
483251881Speter
484251881Speter  if (status->conflicted)
485251881Speter    SVN_ERR(svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
486251881Speter                                 ctx->wc_ctx, local_abspath, pool));
487251881Speter
488251881Speter  svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
489299742Sdim                        "path", svn_dirent_local_style(path, pool),
490299742Sdim                        SVN_VA_NULL);
491251881Speter
492251881Speter  att_hash = apr_hash_make(pool);
493251881Speter  svn_hash_sets(att_hash, "item",
494251881Speter                generate_status_desc(combined_status(status)));
495251881Speter
496251881Speter  svn_hash_sets(att_hash, "props",
497251881Speter                generate_status_desc(
498251881Speter                   (status->node_status != svn_wc_status_deleted)
499251881Speter                   ? status->prop_status
500251881Speter                   : svn_wc_status_none));
501251881Speter  if (status->wc_is_locked)
502251881Speter    svn_hash_sets(att_hash, "wc-locked", "true");
503251881Speter  if (status->copied)
504251881Speter    svn_hash_sets(att_hash, "copied", "true");
505251881Speter  if (status->switched)
506251881Speter    svn_hash_sets(att_hash, "switched", "true");
507251881Speter  if (status->file_external)
508251881Speter    svn_hash_sets(att_hash, "file-external", "true");
509251881Speter  if (status->versioned && ! status->copied)
510251881Speter    svn_hash_sets(att_hash, "revision",
511251881Speter                  apr_psprintf(pool, "%ld", status->revision));
512251881Speter  if (tree_conflicted)
513251881Speter    svn_hash_sets(att_hash, "tree-conflicted", "true");
514251881Speter  if (status->moved_from_abspath || status->moved_to_abspath)
515251881Speter    {
516251881Speter      const char *relpath;
517251881Speter
518251881Speter      if (status->moved_from_abspath)
519251881Speter        {
520262253Speter          relpath = make_relpath(target_abspath, target_path,
521262253Speter                                 status->moved_from_abspath,
522251881Speter                                 pool, pool);
523251881Speter          relpath = svn_dirent_local_style(relpath, pool);
524251881Speter          svn_hash_sets(att_hash, "moved-from", relpath);
525251881Speter        }
526251881Speter      if (status->moved_to_abspath)
527251881Speter        {
528262253Speter          relpath = make_relpath(target_abspath, target_path,
529262253Speter                                 status->moved_to_abspath,
530251881Speter                                 pool, pool);
531251881Speter          relpath = svn_dirent_local_style(relpath, pool);
532251881Speter          svn_hash_sets(att_hash, "moved-to", relpath);
533251881Speter        }
534251881Speter    }
535251881Speter  svn_xml_make_open_tag_hash(&sb, pool, svn_xml_normal, "wc-status",
536251881Speter                             att_hash);
537251881Speter
538251881Speter  if (SVN_IS_VALID_REVNUM(status->changed_rev))
539251881Speter    {
540251881Speter      svn_cl__print_xml_commit(&sb, status->changed_rev,
541251881Speter                               status->changed_author,
542251881Speter                               svn_time_to_cstring(status->changed_date,
543251881Speter                                                   pool),
544251881Speter                               pool);
545251881Speter    }
546251881Speter
547251881Speter  if (status->lock)
548251881Speter    svn_cl__print_xml_lock(&sb, status->lock, pool);
549251881Speter
550251881Speter  svn_xml_make_close_tag(&sb, pool, "wc-status");
551251881Speter
552251881Speter  if (status->repos_node_status != svn_wc_status_none
553251881Speter      || status->repos_lock)
554251881Speter    {
555251881Speter      svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repos-status",
556251881Speter                            "item",
557251881Speter                            generate_status_desc(combined_repos_status(status)),
558251881Speter                            "props",
559251881Speter                            generate_status_desc(status->repos_prop_status),
560299742Sdim                            SVN_VA_NULL);
561251881Speter      if (status->repos_lock)
562251881Speter        svn_cl__print_xml_lock(&sb, status->repos_lock, pool);
563251881Speter
564251881Speter      svn_xml_make_close_tag(&sb, pool, "repos-status");
565251881Speter    }
566251881Speter
567251881Speter  svn_xml_make_close_tag(&sb, pool, "entry");
568251881Speter
569251881Speter  return svn_cl__error_checked_fputs(sb->data, stdout);
570251881Speter}
571251881Speter
572251881Speter/* Called by status-cmd.c */
573251881Spetersvn_error_t *
574262253Spetersvn_cl__print_status(const char *target_abspath,
575262253Speter                     const char *target_path,
576251881Speter                     const char *path,
577251881Speter                     const svn_client_status_t *status,
578251881Speter                     svn_boolean_t suppress_externals_placeholders,
579251881Speter                     svn_boolean_t detailed,
580251881Speter                     svn_boolean_t show_last_committed,
581251881Speter                     svn_boolean_t skip_unrecognized,
582251881Speter                     svn_boolean_t repos_locks,
583251881Speter                     unsigned int *text_conflicts,
584251881Speter                     unsigned int *prop_conflicts,
585251881Speter                     unsigned int *tree_conflicts,
586251881Speter                     svn_client_ctx_t *ctx,
587251881Speter                     apr_pool_t *pool)
588251881Speter{
589251881Speter  if (! status
590251881Speter      || (skip_unrecognized
591251881Speter          && !(status->versioned
592251881Speter               || status->conflicted
593251881Speter               || status->node_status == svn_wc_status_external))
594251881Speter      || (status->node_status == svn_wc_status_none
595251881Speter          && status->repos_node_status == svn_wc_status_none))
596251881Speter    return SVN_NO_ERROR;
597251881Speter
598251881Speter  /* If we're trying not to print boring "X  /path/to/external"
599251881Speter     lines..." */
600251881Speter  if (suppress_externals_placeholders)
601251881Speter    {
602251881Speter      /* ... skip regular externals unmodified in the repository. */
603251881Speter      if ((status->node_status == svn_wc_status_external)
604251881Speter          && (status->repos_node_status == svn_wc_status_none)
605251881Speter          && (! status->conflicted))
606251881Speter        return SVN_NO_ERROR;
607251881Speter
608251881Speter      /* ... skip file externals that aren't modified locally or
609251881Speter         remotely, changelisted, or locked (in either sense of the
610251881Speter         word). */
611251881Speter      if ((status->file_external)
612251881Speter          && (status->repos_node_status == svn_wc_status_none)
613251881Speter          && ((status->node_status == svn_wc_status_normal)
614251881Speter              || (status->node_status == svn_wc_status_none))
615251881Speter          && ((status->prop_status == svn_wc_status_normal)
616251881Speter              || (status->prop_status == svn_wc_status_none))
617251881Speter          && (! status->changelist)
618251881Speter          && (! status->lock)
619251881Speter          && (! status->wc_is_locked)
620251881Speter          && (! status->conflicted))
621251881Speter        return SVN_NO_ERROR;
622251881Speter    }
623251881Speter
624262253Speter  return print_status(target_abspath, target_path, path,
625251881Speter                      detailed, show_last_committed, repos_locks, status,
626251881Speter                      text_conflicts, prop_conflicts, tree_conflicts,
627251881Speter                      ctx, pool);
628251881Speter}
629