1251881Speter/*
2251881Speter * info-cmd.c -- Display information about a resource
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
30251881Speter#include "svn_string.h"
31251881Speter#include "svn_cmdline.h"
32251881Speter#include "svn_wc.h"
33251881Speter#include "svn_pools.h"
34251881Speter#include "svn_error_codes.h"
35251881Speter#include "svn_error.h"
36251881Speter#include "svn_dirent_uri.h"
37251881Speter#include "svn_path.h"
38251881Speter#include "svn_time.h"
39251881Speter#include "svn_xml.h"
40251881Speter#include "cl.h"
41251881Speter
42251881Speter#include "svn_private_config.h"
43251881Speter#include "cl-conflicts.h"
44251881Speter
45251881Speter
46251881Speter/*** Code. ***/
47251881Speter
48251881Speterstatic svn_error_t *
49251881Spetersvn_cl__info_print_time(apr_time_t atime,
50251881Speter                        const char *desc,
51251881Speter                        apr_pool_t *pool)
52251881Speter{
53251881Speter  const char *time_utf8;
54251881Speter
55251881Speter  time_utf8 = svn_time_to_human_cstring(atime, pool);
56251881Speter  return svn_cmdline_printf(pool, "%s: %s\n", desc, time_utf8);
57251881Speter}
58251881Speter
59251881Speter
60251881Speter/* Return string representation of SCHEDULE */
61251881Speterstatic const char *
62251881Speterschedule_str(svn_wc_schedule_t schedule)
63251881Speter{
64251881Speter  switch (schedule)
65251881Speter    {
66251881Speter    case svn_wc_schedule_normal:
67251881Speter      return "normal";
68251881Speter    case svn_wc_schedule_add:
69251881Speter      return "add";
70251881Speter    case svn_wc_schedule_delete:
71251881Speter      return "delete";
72251881Speter    case svn_wc_schedule_replace:
73251881Speter      return "replace";
74251881Speter    default:
75251881Speter      return "none";
76251881Speter    }
77251881Speter}
78251881Speter
79251881Speter
80251881Speter/* A callback of type svn_client_info_receiver2_t.
81251881Speter   Prints svn info in xml mode to standard out */
82251881Speterstatic svn_error_t *
83251881Speterprint_info_xml(void *baton,
84251881Speter               const char *target,
85251881Speter               const svn_client_info2_t *info,
86251881Speter               apr_pool_t *pool)
87251881Speter{
88251881Speter  svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
89251881Speter  const char *rev_str;
90251881Speter  const char *path_prefix = baton;
91251881Speter
92251881Speter  if (SVN_IS_VALID_REVNUM(info->rev))
93251881Speter    rev_str = apr_psprintf(pool, "%ld", info->rev);
94251881Speter  else
95251881Speter    rev_str = apr_pstrdup(pool, _("Resource is not under version control."));
96251881Speter
97251881Speter  /* "<entry ...>" */
98251881Speter  svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
99251881Speter                        "path", svn_cl__local_style_skip_ancestor(
100251881Speter                                  path_prefix, target, pool),
101251881Speter                        "kind", svn_cl__node_kind_str_xml(info->kind),
102251881Speter                        "revision", rev_str,
103251881Speter                        NULL);
104251881Speter
105251881Speter  /* "<url> xx </url>" */
106251881Speter  svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL);
107251881Speter
108251881Speter  if (info->repos_root_URL && info->URL)
109251881Speter    {
110251881Speter      /* "<relative-url> xx </relative-url>" */
111251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "relative-url",
112251881Speter                               apr_pstrcat(pool, "^/",
113251881Speter                                           svn_path_uri_encode(
114251881Speter                                               svn_uri_skip_ancestor(
115251881Speter                                                   info->repos_root_URL,
116251881Speter                                                   info->URL, pool),
117251881Speter                                               pool),
118251881Speter                                           NULL));
119251881Speter    }
120251881Speter
121251881Speter  if (info->repos_root_URL || info->repos_UUID)
122251881Speter    {
123251881Speter      /* "<repository>" */
124251881Speter      svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL);
125251881Speter
126251881Speter      /* "<root> xx </root>" */
127251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL);
128251881Speter
129251881Speter      /* "<uuid> xx </uuid>" */
130251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "uuid", info->repos_UUID);
131251881Speter
132251881Speter      /* "</repository>" */
133251881Speter      svn_xml_make_close_tag(&sb, pool, "repository");
134251881Speter    }
135251881Speter
136251881Speter  if (info->wc_info)
137251881Speter    {
138251881Speter      /* "<wc-info>" */
139251881Speter      svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL);
140251881Speter
141251881Speter      /* "<wcroot-abspath> xx </wcroot-abspath>" */
142251881Speter      if (info->wc_info->wcroot_abspath)
143251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "wcroot-abspath",
144251881Speter                                 info->wc_info->wcroot_abspath);
145251881Speter
146251881Speter      /* "<schedule> xx </schedule>" */
147251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "schedule",
148251881Speter                               schedule_str(info->wc_info->schedule));
149251881Speter
150251881Speter      /* "<depth> xx </depth>" */
151251881Speter      {
152251881Speter        svn_depth_t depth = info->wc_info->depth;
153251881Speter
154251881Speter        /* In the entries world info just passed depth infinity for files */
155251881Speter        if (depth == svn_depth_unknown && info->kind == svn_node_file)
156251881Speter          depth = svn_depth_infinity;
157251881Speter
158251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "depth", svn_depth_to_word(depth));
159251881Speter      }
160251881Speter
161251881Speter      /* "<copy-from-url> xx </copy-from-url>" */
162251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-url",
163251881Speter                               info->wc_info->copyfrom_url);
164251881Speter
165251881Speter      /* "<copy-from-rev> xx </copy-from-rev>" */
166251881Speter      if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
167251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-rev",
168251881Speter                                 apr_psprintf(pool, "%ld",
169251881Speter                                              info->wc_info->copyfrom_rev));
170251881Speter
171251881Speter      /* "<text-updated> xx </text-updated>" */
172251881Speter      if (info->wc_info->recorded_time)
173251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "text-updated",
174251881Speter                                 svn_time_to_cstring(
175251881Speter                                          info->wc_info->recorded_time,
176251881Speter                                          pool));
177251881Speter
178251881Speter      /* "<checksum> xx </checksum>" */
179251881Speter      /* ### Print the checksum kind. */
180251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "checksum",
181251881Speter                               svn_checksum_to_cstring(info->wc_info->checksum,
182251881Speter                                                       pool));
183251881Speter
184251881Speter      if (info->wc_info->changelist)
185251881Speter        /* "<changelist> xx </changelist>" */
186251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "changelist",
187251881Speter                                 info->wc_info->changelist);
188251881Speter
189251881Speter      if (info->wc_info->moved_from_abspath)
190251881Speter        {
191251881Speter          const char *relpath;
192251881Speter
193251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
194251881Speter                                             info->wc_info->moved_from_abspath);
195251881Speter
196251881Speter          /* <moved-from> xx </moved-from> */
197251881Speter          if (relpath && relpath[0] != '\0')
198251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-from", relpath);
199251881Speter          else
200251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-from",
201251881Speter                                     info->wc_info->moved_from_abspath);
202251881Speter        }
203251881Speter
204251881Speter      if (info->wc_info->moved_to_abspath)
205251881Speter        {
206251881Speter          const char *relpath;
207251881Speter
208251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
209251881Speter                                             info->wc_info->moved_to_abspath);
210251881Speter          /* <moved-to> xx </moved-to> */
211251881Speter          if (relpath && relpath[0] != '\0')
212251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-to", relpath);
213251881Speter          else
214251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-to",
215251881Speter                                     info->wc_info->moved_to_abspath);
216251881Speter        }
217251881Speter
218251881Speter      /* "</wc-info>" */
219251881Speter      svn_xml_make_close_tag(&sb, pool, "wc-info");
220251881Speter    }
221251881Speter
222251881Speter  if (info->last_changed_author
223251881Speter      || SVN_IS_VALID_REVNUM(info->last_changed_rev)
224251881Speter      || info->last_changed_date)
225251881Speter    {
226251881Speter      svn_cl__print_xml_commit(&sb, info->last_changed_rev,
227251881Speter                               info->last_changed_author,
228251881Speter                               svn_time_to_cstring(info->last_changed_date,
229251881Speter                                                   pool),
230251881Speter                               pool);
231251881Speter    }
232251881Speter
233251881Speter  if (info->wc_info && info->wc_info->conflicts)
234251881Speter    {
235251881Speter      int i;
236251881Speter
237251881Speter      for (i = 0; i < info->wc_info->conflicts->nelts; i++)
238251881Speter        {
239251881Speter          const svn_wc_conflict_description2_t *conflict =
240251881Speter                      APR_ARRAY_IDX(info->wc_info->conflicts, i,
241251881Speter                                    const svn_wc_conflict_description2_t *);
242251881Speter
243251881Speter          SVN_ERR(svn_cl__append_conflict_info_xml(sb, conflict, pool));
244251881Speter        }
245251881Speter    }
246251881Speter
247251881Speter  if (info->lock)
248251881Speter    svn_cl__print_xml_lock(&sb, info->lock, pool);
249251881Speter
250251881Speter  /* "</entry>" */
251251881Speter  svn_xml_make_close_tag(&sb, pool, "entry");
252251881Speter
253251881Speter  return svn_cl__error_checked_fputs(sb->data, stdout);
254251881Speter}
255251881Speter
256251881Speter
257251881Speter/* A callback of type svn_client_info_receiver2_t. */
258251881Speterstatic svn_error_t *
259251881Speterprint_info(void *baton,
260251881Speter           const char *target,
261251881Speter           const svn_client_info2_t *info,
262251881Speter           apr_pool_t *pool)
263251881Speter{
264251881Speter  const char *path_prefix = baton;
265251881Speter
266251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
267251881Speter                             svn_cl__local_style_skip_ancestor(
268251881Speter                               path_prefix, target, pool)));
269251881Speter
270251881Speter  /* ### remove this someday:  it's only here for cmdline output
271251881Speter     compatibility with svn 1.1 and older.  */
272251881Speter  if (info->kind != svn_node_dir)
273251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Name: %s\n"),
274251881Speter                               svn_dirent_basename(target, pool)));
275251881Speter
276251881Speter  if (info->wc_info && info->wc_info->wcroot_abspath)
277251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Working Copy Root Path: %s\n"),
278251881Speter                               svn_dirent_local_style(
279251881Speter                                            info->wc_info->wcroot_abspath,
280251881Speter                                            pool)));
281251881Speter
282251881Speter  if (info->URL)
283251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL));
284251881Speter
285251881Speter  if (info->URL && info->repos_root_URL)
286251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: ^/%s\n"),
287251881Speter                               svn_path_uri_encode(
288251881Speter                                   svn_uri_skip_ancestor(info->repos_root_URL,
289251881Speter                                                         info->URL, pool),
290251881Speter                                   pool)));
291251881Speter
292251881Speter  if (info->repos_root_URL)
293251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"),
294251881Speter                               info->repos_root_URL));
295251881Speter
296251881Speter  if (info->repos_UUID)
297251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Repository UUID: %s\n"),
298251881Speter                               info->repos_UUID));
299251881Speter
300251881Speter  if (SVN_IS_VALID_REVNUM(info->rev))
301251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Revision: %ld\n"), info->rev));
302251881Speter
303251881Speter  switch (info->kind)
304251881Speter    {
305251881Speter    case svn_node_file:
306251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: file\n")));
307251881Speter      break;
308251881Speter
309251881Speter    case svn_node_dir:
310251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: directory\n")));
311251881Speter      break;
312251881Speter
313251881Speter    case svn_node_none:
314251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: none\n")));
315251881Speter      break;
316251881Speter
317251881Speter    case svn_node_unknown:
318251881Speter    default:
319251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: unknown\n")));
320251881Speter      break;
321251881Speter    }
322251881Speter
323251881Speter  if (info->wc_info)
324251881Speter    {
325251881Speter      switch (info->wc_info->schedule)
326251881Speter        {
327251881Speter        case svn_wc_schedule_normal:
328251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: normal\n")));
329251881Speter          break;
330251881Speter
331251881Speter        case svn_wc_schedule_add:
332251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: add\n")));
333251881Speter          break;
334251881Speter
335251881Speter        case svn_wc_schedule_delete:
336251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: delete\n")));
337251881Speter          break;
338251881Speter
339251881Speter        case svn_wc_schedule_replace:
340251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: replace\n")));
341251881Speter          break;
342251881Speter
343251881Speter        default:
344251881Speter          break;
345251881Speter        }
346251881Speter
347251881Speter      switch (info->wc_info->depth)
348251881Speter        {
349251881Speter        case svn_depth_unknown:
350251881Speter          /* Unknown depth is the norm for remote directories anyway
351251881Speter             (although infinity would be equally appropriate).  Let's
352251881Speter             not bother to print it. */
353251881Speter          break;
354251881Speter
355251881Speter        case svn_depth_empty:
356251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: empty\n")));
357251881Speter          break;
358251881Speter
359251881Speter        case svn_depth_files:
360251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: files\n")));
361251881Speter          break;
362251881Speter
363251881Speter        case svn_depth_immediates:
364251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: immediates\n")));
365251881Speter          break;
366251881Speter
367251881Speter        case svn_depth_exclude:
368251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: exclude\n")));
369251881Speter          break;
370251881Speter
371251881Speter        case svn_depth_infinity:
372251881Speter          /* Infinity is the default depth for working copy
373251881Speter             directories.  Let's not print it, it's not special enough
374251881Speter             to be worth mentioning.  */
375251881Speter          break;
376251881Speter
377251881Speter        default:
378251881Speter          /* Other depths should never happen here. */
379251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: INVALID\n")));
380251881Speter        }
381251881Speter
382251881Speter      if (info->wc_info->copyfrom_url)
383251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Copied From URL: %s\n"),
384251881Speter                                   info->wc_info->copyfrom_url));
385251881Speter
386251881Speter      if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
387251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"),
388251881Speter                                   info->wc_info->copyfrom_rev));
389251881Speter      if (info->wc_info->moved_from_abspath)
390251881Speter        {
391251881Speter          const char *relpath;
392251881Speter
393251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
394251881Speter                                             info->wc_info->moved_from_abspath);
395251881Speter          if (relpath && relpath[0] != '\0')
396251881Speter            SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), relpath));
397251881Speter          else
398251881Speter            SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"),
399251881Speter                                       info->wc_info->moved_from_abspath));
400251881Speter        }
401251881Speter
402251881Speter      if (info->wc_info->moved_to_abspath)
403251881Speter        {
404251881Speter          const char *relpath;
405251881Speter
406251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
407251881Speter                                             info->wc_info->moved_to_abspath);
408251881Speter          if (relpath && relpath[0] != '\0')
409251881Speter            SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), relpath));
410251881Speter          else
411251881Speter            SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"),
412251881Speter                                       info->wc_info->moved_to_abspath));
413251881Speter        }
414251881Speter    }
415251881Speter
416251881Speter  if (info->last_changed_author)
417251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Author: %s\n"),
418251881Speter                               info->last_changed_author));
419251881Speter
420251881Speter  if (SVN_IS_VALID_REVNUM(info->last_changed_rev))
421251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Rev: %ld\n"),
422251881Speter                               info->last_changed_rev));
423251881Speter
424251881Speter  if (info->last_changed_date)
425251881Speter    SVN_ERR(svn_cl__info_print_time(info->last_changed_date,
426251881Speter                                    _("Last Changed Date"), pool));
427251881Speter
428251881Speter  if (info->wc_info)
429251881Speter    {
430251881Speter      if (info->wc_info->recorded_time)
431251881Speter        SVN_ERR(svn_cl__info_print_time(info->wc_info->recorded_time,
432251881Speter                                        _("Text Last Updated"), pool));
433251881Speter
434251881Speter      if (info->wc_info->checksum)
435251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Checksum: %s\n"),
436251881Speter                                   svn_checksum_to_cstring(
437251881Speter                                              info->wc_info->checksum, pool)));
438251881Speter
439251881Speter      if (info->wc_info->conflicts)
440251881Speter        {
441251881Speter          svn_boolean_t printed_prop_conflict_file = FALSE;
442251881Speter          int i;
443251881Speter
444251881Speter          for (i = 0; i < info->wc_info->conflicts->nelts; i++)
445251881Speter            {
446251881Speter              const svn_wc_conflict_description2_t *conflict =
447251881Speter                    APR_ARRAY_IDX(info->wc_info->conflicts, i,
448251881Speter                                  const svn_wc_conflict_description2_t *);
449251881Speter              const char *desc;
450251881Speter
451251881Speter              switch (conflict->kind)
452251881Speter                {
453251881Speter                  case svn_wc_conflict_kind_text:
454251881Speter                    if (conflict->base_abspath)
455251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
456251881Speter                                _("Conflict Previous Base File: %s\n"),
457251881Speter                                svn_cl__local_style_skip_ancestor(
458251881Speter                                        path_prefix, conflict->base_abspath,
459251881Speter                                        pool)));
460251881Speter
461251881Speter                    if (conflict->my_abspath)
462251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
463251881Speter                                _("Conflict Previous Working File: %s\n"),
464251881Speter                                svn_cl__local_style_skip_ancestor(
465251881Speter                                        path_prefix, conflict->my_abspath,
466251881Speter                                        pool)));
467251881Speter
468251881Speter                    if (conflict->their_abspath)
469251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
470251881Speter                                _("Conflict Current Base File: %s\n"),
471251881Speter                                svn_cl__local_style_skip_ancestor(
472251881Speter                                        path_prefix, conflict->their_abspath,
473251881Speter                                        pool)));
474251881Speter                  break;
475251881Speter
476251881Speter                  case svn_wc_conflict_kind_property:
477251881Speter                    if (! printed_prop_conflict_file)
478251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
479251881Speter                                _("Conflict Properties File: %s\n"),
480251881Speter                                svn_dirent_local_style(conflict->their_abspath,
481251881Speter                                                       pool)));
482251881Speter                    printed_prop_conflict_file = TRUE;
483251881Speter                  break;
484251881Speter
485251881Speter                  case svn_wc_conflict_kind_tree:
486251881Speter                    SVN_ERR(
487251881Speter                        svn_cl__get_human_readable_tree_conflict_description(
488251881Speter                                                    &desc, conflict, pool));
489251881Speter
490251881Speter                    SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
491251881Speter                                               _("Tree conflict"), desc));
492251881Speter                  break;
493251881Speter                }
494251881Speter            }
495251881Speter
496251881Speter          /* We only store one left and right version for all conflicts, which is
497251881Speter             referenced from all conflicts.
498251881Speter             Print it after the conflicts to match the 1.6/1.7 output where it is
499251881Speter             only available for tree conflicts */
500251881Speter          {
501251881Speter            const char *src_left_version;
502251881Speter            const char *src_right_version;
503251881Speter            const svn_wc_conflict_description2_t *conflict =
504251881Speter                  APR_ARRAY_IDX(info->wc_info->conflicts, 0,
505251881Speter                                const svn_wc_conflict_description2_t *);
506251881Speter
507251881Speter            src_left_version =
508251881Speter                        svn_cl__node_description(conflict->src_left_version,
509251881Speter                                                 info->repos_root_URL, pool);
510251881Speter
511251881Speter            src_right_version =
512251881Speter                        svn_cl__node_description(conflict->src_right_version,
513251881Speter                                                 info->repos_root_URL, pool);
514251881Speter
515251881Speter            if (src_left_version)
516251881Speter              SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
517251881Speter                                         _("Source  left"), /* (1) */
518251881Speter                                         src_left_version));
519251881Speter            /* (1): Sneaking in a space in "Source  left" so that
520251881Speter             * it is the same length as "Source right" while it still
521251881Speter             * starts in the same column. That's just a tiny tweak in
522251881Speter             * the English `svn'. */
523251881Speter
524251881Speter            if (src_right_version)
525251881Speter              SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
526251881Speter                                         _("Source right"),
527251881Speter                                         src_right_version));
528251881Speter          }
529251881Speter        }
530251881Speter    }
531251881Speter
532251881Speter  if (info->lock)
533251881Speter    {
534251881Speter      if (info->lock->token)
535251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Lock Token: %s\n"),
536251881Speter                                   info->lock->token));
537251881Speter
538251881Speter      if (info->lock->owner)
539251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Lock Owner: %s\n"),
540251881Speter                                   info->lock->owner));
541251881Speter
542251881Speter      if (info->lock->creation_date)
543251881Speter        SVN_ERR(svn_cl__info_print_time(info->lock->creation_date,
544251881Speter                                        _("Lock Created"), pool));
545251881Speter
546251881Speter      if (info->lock->expiration_date)
547251881Speter        SVN_ERR(svn_cl__info_print_time(info->lock->expiration_date,
548251881Speter                                        _("Lock Expires"), pool));
549251881Speter
550251881Speter      if (info->lock->comment)
551251881Speter        {
552251881Speter          int comment_lines;
553251881Speter          /* NOTE: The stdio will handle newline translation. */
554251881Speter          comment_lines = svn_cstring_count_newlines(info->lock->comment) + 1;
555251881Speter          SVN_ERR(svn_cmdline_printf(pool,
556251881Speter                                     Q_("Lock Comment (%i line):\n%s\n",
557251881Speter                                        "Lock Comment (%i lines):\n%s\n",
558251881Speter                                        comment_lines),
559251881Speter                                     comment_lines,
560251881Speter                                     info->lock->comment));
561251881Speter        }
562251881Speter    }
563251881Speter
564251881Speter  if (info->wc_info && info->wc_info->changelist)
565251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Changelist: %s\n"),
566251881Speter                               info->wc_info->changelist));
567251881Speter
568251881Speter  /* Print extra newline separator. */
569251881Speter  return svn_cmdline_printf(pool, "\n");
570251881Speter}
571251881Speter
572251881Speter
573251881Speter/* This implements the `svn_opt_subcommand_t' interface. */
574251881Spetersvn_error_t *
575251881Spetersvn_cl__info(apr_getopt_t *os,
576251881Speter             void *baton,
577251881Speter             apr_pool_t *pool)
578251881Speter{
579251881Speter  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
580251881Speter  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
581251881Speter  apr_array_header_t *targets = NULL;
582251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
583251881Speter  int i;
584251881Speter  svn_error_t *err;
585251881Speter  svn_boolean_t seen_nonexistent_target = FALSE;
586251881Speter  svn_opt_revision_t peg_revision;
587251881Speter  svn_client_info_receiver2_t receiver;
588251881Speter  const char *path_prefix;
589251881Speter
590251881Speter  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
591251881Speter                                                      opt_state->targets,
592251881Speter                                                      ctx, FALSE, pool));
593251881Speter
594251881Speter  /* Add "." if user passed 0 arguments. */
595251881Speter  svn_opt_push_implicit_dot_target(targets, pool);
596251881Speter
597251881Speter  if (opt_state->xml)
598251881Speter    {
599251881Speter      receiver = print_info_xml;
600251881Speter
601251881Speter      /* If output is not incremental, output the XML header and wrap
602251881Speter         everything in a top-level element. This makes the output in
603251881Speter         its entirety a well-formed XML document. */
604251881Speter      if (! opt_state->incremental)
605251881Speter        SVN_ERR(svn_cl__xml_print_header("info", pool));
606251881Speter    }
607251881Speter  else
608251881Speter    {
609251881Speter      receiver = print_info;
610251881Speter
611251881Speter      if (opt_state->incremental)
612251881Speter        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
613251881Speter                                _("'incremental' option only valid in XML "
614251881Speter                                  "mode"));
615251881Speter    }
616251881Speter
617251881Speter  if (opt_state->depth == svn_depth_unknown)
618251881Speter    opt_state->depth = svn_depth_empty;
619251881Speter
620251881Speter  SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
621251881Speter
622251881Speter  for (i = 0; i < targets->nelts; i++)
623251881Speter    {
624251881Speter      const char *truepath;
625251881Speter      const char *target = APR_ARRAY_IDX(targets, i, const char *);
626251881Speter
627251881Speter      svn_pool_clear(subpool);
628251881Speter      SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
629251881Speter
630251881Speter      /* Get peg revisions. */
631251881Speter      SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool));
632251881Speter
633251881Speter      /* If no peg-rev was attached to a URL target, then assume HEAD. */
634251881Speter      if (svn_path_is_url(truepath))
635251881Speter        {
636251881Speter          if (peg_revision.kind == svn_opt_revision_unspecified)
637251881Speter            peg_revision.kind = svn_opt_revision_head;
638251881Speter        }
639251881Speter      else
640251881Speter        {
641251881Speter          SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
642251881Speter        }
643251881Speter
644251881Speter      err = svn_client_info3(truepath,
645251881Speter                             &peg_revision, &(opt_state->start_revision),
646251881Speter                             opt_state->depth, TRUE, TRUE,
647251881Speter                             opt_state->changelists,
648251881Speter                             receiver, (void *) path_prefix,
649251881Speter                             ctx, subpool);
650251881Speter
651251881Speter      if (err)
652251881Speter        {
653251881Speter          /* If one of the targets is a non-existent URL or wc-entry,
654251881Speter             don't bail out.  Just warn and move on to the next target. */
655251881Speter          if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
656251881Speter              err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
657251881Speter            {
658251881Speter              svn_handle_warning2(stderr, err, "svn: ");
659251881Speter              svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n"));
660251881Speter            }
661251881Speter          else
662251881Speter            {
663251881Speter              return svn_error_trace(err);
664251881Speter            }
665251881Speter
666251881Speter          svn_error_clear(err);
667251881Speter          err = NULL;
668251881Speter          seen_nonexistent_target = TRUE;
669251881Speter        }
670251881Speter    }
671251881Speter  svn_pool_destroy(subpool);
672251881Speter
673251881Speter  if (opt_state->xml && (! opt_state->incremental))
674251881Speter    SVN_ERR(svn_cl__xml_print_footer("info", pool));
675251881Speter
676251881Speter  if (seen_nonexistent_target)
677251881Speter    return svn_error_create(
678251881Speter      SVN_ERR_ILLEGAL_TARGET, NULL,
679251881Speter      _("Could not display info for all targets because some "
680251881Speter        "targets don't exist"));
681251881Speter  else
682251881Speter    return SVN_NO_ERROR;
683251881Speter}
684