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
79299742Sdim/* Return a relative URL from information in INFO using POOL for
80299742Sdim   temporary allocation. */
81299742Sdimstatic const char*
82299742Sdimrelative_url(const svn_client_info2_t *info, apr_pool_t *pool)
83299742Sdim{
84299742Sdim  return apr_pstrcat(pool, "^/",
85299742Sdim                     svn_path_uri_encode(
86299742Sdim                         svn_uri_skip_ancestor(info->repos_root_URL,
87299742Sdim                                               info->URL, pool),
88299742Sdim                         pool), SVN_VA_NULL);
89299742Sdim}
90251881Speter
91299742Sdim
92299742Sdim/* The kinds of items for print_info_item(). */
93299742Sdimtypedef enum
94299742Sdim{
95299742Sdim  /* Entry kind */
96299742Sdim  info_item_kind,
97299742Sdim
98299742Sdim  /* Repository location. */
99299742Sdim  info_item_url,
100299742Sdim  info_item_relative_url,
101299742Sdim  info_item_repos_root_url,
102299742Sdim  info_item_repos_uuid,
103299742Sdim
104299742Sdim  /* Working copy revision or repository HEAD revision */
105299742Sdim  info_item_revision,
106299742Sdim
107299742Sdim  /* Commit details. */
108299742Sdim  info_item_last_changed_rev,
109299742Sdim  info_item_last_changed_date,
110299742Sdim  info_item_last_changed_author,
111299742Sdim
112299742Sdim  /* Working copy information */
113299742Sdim  info_item_wc_root
114299742Sdim} info_item_t;
115299742Sdim
116299742Sdim/* Mapping between option keywords and info_item_t. */
117299742Sdimtypedef struct info_item_map_t
118299742Sdim{
119299742Sdim  const svn_string_t keyword;
120299742Sdim  const info_item_t print_what;
121299742Sdim} info_item_map_t;
122299742Sdim
123299742Sdim#define MAKE_STRING(x) { x, sizeof(x) - 1 }
124299742Sdimstatic const info_item_map_t info_item_map[] =
125299742Sdim  {
126299742Sdim    { MAKE_STRING("kind"),                info_item_kind },
127299742Sdim    { MAKE_STRING("url"),                 info_item_url },
128299742Sdim    { MAKE_STRING("relative-url"),        info_item_relative_url },
129299742Sdim    { MAKE_STRING("repos-root-url"),      info_item_repos_root_url },
130299742Sdim    { MAKE_STRING("repos-uuid"),          info_item_repos_uuid },
131299742Sdim    { MAKE_STRING("revision"),            info_item_revision },
132299742Sdim    { MAKE_STRING("last-changed-revision"),
133299742Sdim                                          info_item_last_changed_rev },
134299742Sdim    { MAKE_STRING("last-changed-date"),   info_item_last_changed_date },
135299742Sdim    { MAKE_STRING("last-changed-author"), info_item_last_changed_author },
136299742Sdim    { MAKE_STRING("wc-root"),             info_item_wc_root }
137299742Sdim  };
138299742Sdim#undef MAKE_STRING
139299742Sdim
140299742Sdimstatic const apr_size_t info_item_map_len =
141299742Sdim  (sizeof(info_item_map) / sizeof(info_item_map[0]));
142299742Sdim
143299742Sdim
144299742Sdim/* The baton type used by the info receiver functions. */
145299742Sdimtypedef struct print_info_baton_t
146299742Sdim{
147299742Sdim  /* The path prefix that output paths should be normalized to. */
148299742Sdim  const char *path_prefix;
149299742Sdim
150299742Sdim  /*
151299742Sdim   * The following fields are used by print_info_item().
152299742Sdim   */
153299742Sdim
154299742Sdim  /* Which item to print. */
155299742Sdim  info_item_t print_what;
156299742Sdim
157299742Sdim  /* Do we expect to show info for multiple targets? */
158299742Sdim  svn_boolean_t multiple_targets;
159299742Sdim
160299742Sdim  /* TRUE iff the current is a local path. */
161299742Sdim  svn_boolean_t target_is_path;
162299742Sdim
163299742Sdim  /* Did we already print a line of output? */
164299742Sdim  svn_boolean_t start_new_line;
165299742Sdim} print_info_baton_t;
166299742Sdim
167299742Sdim
168299742Sdim/* Find the appropriate info_item_t for KEYWORD and initialize
169299742Sdim * RECEIVER_BATON for print_info_item(). Use SCRATCH_POOL for
170299742Sdim * temporary allocation.
171299742Sdim */
172299742Sdimstatic svn_error_t *
173299742Sdimfind_print_what(const char *keyword,
174299742Sdim                print_info_baton_t *receiver_baton,
175299742Sdim                apr_pool_t *scratch_pool)
176299742Sdim{
177299742Sdim  svn_cl__simcheck_t **keywords = apr_palloc(
178299742Sdim      scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t*));
179299742Sdim  svn_cl__simcheck_t *kwbuf = apr_palloc(
180299742Sdim      scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t));
181299742Sdim  apr_size_t i;
182299742Sdim
183299742Sdim  for (i = 0; i < info_item_map_len; ++i)
184299742Sdim    {
185299742Sdim      keywords[i] = &kwbuf[i];
186299742Sdim      kwbuf[i].token.data = info_item_map[i].keyword.data;
187299742Sdim      kwbuf[i].token.len = info_item_map[i].keyword.len;
188299742Sdim      kwbuf[i].data = &info_item_map[i];
189299742Sdim    }
190299742Sdim
191299742Sdim  switch (svn_cl__similarity_check(keyword, keywords,
192299742Sdim                                   info_item_map_len, scratch_pool))
193299742Sdim    {
194299742Sdim      const info_item_map_t *kw0;
195299742Sdim      const info_item_map_t *kw1;
196299742Sdim      const info_item_map_t *kw2;
197299742Sdim
198299742Sdim    case 0:                     /* Exact match. */
199299742Sdim      kw0 = keywords[0]->data;
200299742Sdim      receiver_baton->print_what = kw0->print_what;
201299742Sdim      return SVN_NO_ERROR;
202299742Sdim
203299742Sdim    case 1:
204299742Sdim      /* The best alternative isn't good enough */
205299742Sdim      return svn_error_createf(
206299742Sdim          SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
207299742Sdim          _("'%s' is not a valid value for --show-item"),
208299742Sdim          keyword);
209299742Sdim
210299742Sdim    case 2:
211299742Sdim      /* There is only one good candidate */
212299742Sdim      kw0 = keywords[0]->data;
213299742Sdim      return svn_error_createf(
214299742Sdim          SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
215299742Sdim          _("'%s' is not a valid value for --show-item;"
216299742Sdim            " did you mean '%s'?"),
217299742Sdim          keyword, kw0->keyword.data);
218299742Sdim
219299742Sdim    case 3:
220299742Sdim      /* Suggest a list of the most likely candidates */
221299742Sdim      kw0 = keywords[0]->data;
222299742Sdim      kw1 = keywords[1]->data;
223299742Sdim      return svn_error_createf(
224299742Sdim          SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
225299742Sdim          _("'%s' is not a valid value for --show-item;"
226299742Sdim            " did you mean '%s' or '%s'?"),
227299742Sdim          keyword, kw0->keyword.data, kw1->keyword.data);
228299742Sdim
229299742Sdim    default:
230299742Sdim      /* Never suggest more than three candidates */
231299742Sdim      kw0 = keywords[0]->data;
232299742Sdim      kw1 = keywords[1]->data;
233299742Sdim      kw2 = keywords[2]->data;
234299742Sdim      return svn_error_createf(
235299742Sdim          SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
236299742Sdim          _("'%s' is not a valid value for --show-item;"
237299742Sdim            " did you mean '%s', '%s' or '%s'?"),
238299742Sdim          keyword, kw0->keyword.data, kw1->keyword.data, kw2->keyword.data);
239299742Sdim    }
240299742Sdim}
241299742Sdim
242251881Speter/* A callback of type svn_client_info_receiver2_t.
243251881Speter   Prints svn info in xml mode to standard out */
244251881Speterstatic svn_error_t *
245251881Speterprint_info_xml(void *baton,
246251881Speter               const char *target,
247251881Speter               const svn_client_info2_t *info,
248251881Speter               apr_pool_t *pool)
249251881Speter{
250251881Speter  svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
251251881Speter  const char *rev_str;
252299742Sdim  print_info_baton_t *const receiver_baton = baton;
253251881Speter
254251881Speter  if (SVN_IS_VALID_REVNUM(info->rev))
255251881Speter    rev_str = apr_psprintf(pool, "%ld", info->rev);
256251881Speter  else
257251881Speter    rev_str = apr_pstrdup(pool, _("Resource is not under version control."));
258251881Speter
259251881Speter  /* "<entry ...>" */
260251881Speter  svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
261251881Speter                        "path", svn_cl__local_style_skip_ancestor(
262299742Sdim                                  receiver_baton->path_prefix, target, pool),
263251881Speter                        "kind", svn_cl__node_kind_str_xml(info->kind),
264251881Speter                        "revision", rev_str,
265299742Sdim                        SVN_VA_NULL);
266251881Speter
267251881Speter  /* "<url> xx </url>" */
268251881Speter  svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL);
269251881Speter
270251881Speter  if (info->repos_root_URL && info->URL)
271251881Speter    {
272251881Speter      /* "<relative-url> xx </relative-url>" */
273251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "relative-url",
274299742Sdim                               relative_url(info, pool));
275251881Speter    }
276251881Speter
277251881Speter  if (info->repos_root_URL || info->repos_UUID)
278251881Speter    {
279251881Speter      /* "<repository>" */
280299742Sdim      svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository",
281299742Sdim                            SVN_VA_NULL);
282251881Speter
283251881Speter      /* "<root> xx </root>" */
284251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL);
285251881Speter
286251881Speter      /* "<uuid> xx </uuid>" */
287251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "uuid", info->repos_UUID);
288251881Speter
289251881Speter      /* "</repository>" */
290251881Speter      svn_xml_make_close_tag(&sb, pool, "repository");
291251881Speter    }
292251881Speter
293251881Speter  if (info->wc_info)
294251881Speter    {
295251881Speter      /* "<wc-info>" */
296299742Sdim      svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info",
297299742Sdim                            SVN_VA_NULL);
298251881Speter
299251881Speter      /* "<wcroot-abspath> xx </wcroot-abspath>" */
300251881Speter      if (info->wc_info->wcroot_abspath)
301251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "wcroot-abspath",
302251881Speter                                 info->wc_info->wcroot_abspath);
303251881Speter
304251881Speter      /* "<schedule> xx </schedule>" */
305251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "schedule",
306251881Speter                               schedule_str(info->wc_info->schedule));
307251881Speter
308251881Speter      /* "<depth> xx </depth>" */
309251881Speter      {
310251881Speter        svn_depth_t depth = info->wc_info->depth;
311251881Speter
312251881Speter        /* In the entries world info just passed depth infinity for files */
313251881Speter        if (depth == svn_depth_unknown && info->kind == svn_node_file)
314251881Speter          depth = svn_depth_infinity;
315251881Speter
316251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "depth", svn_depth_to_word(depth));
317251881Speter      }
318251881Speter
319251881Speter      /* "<copy-from-url> xx </copy-from-url>" */
320251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-url",
321251881Speter                               info->wc_info->copyfrom_url);
322251881Speter
323251881Speter      /* "<copy-from-rev> xx </copy-from-rev>" */
324251881Speter      if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
325251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-rev",
326251881Speter                                 apr_psprintf(pool, "%ld",
327251881Speter                                              info->wc_info->copyfrom_rev));
328251881Speter
329251881Speter      /* "<text-updated> xx </text-updated>" */
330251881Speter      if (info->wc_info->recorded_time)
331251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "text-updated",
332251881Speter                                 svn_time_to_cstring(
333251881Speter                                          info->wc_info->recorded_time,
334251881Speter                                          pool));
335251881Speter
336251881Speter      /* "<checksum> xx </checksum>" */
337251881Speter      /* ### Print the checksum kind. */
338251881Speter      svn_cl__xml_tagged_cdata(&sb, pool, "checksum",
339251881Speter                               svn_checksum_to_cstring(info->wc_info->checksum,
340251881Speter                                                       pool));
341251881Speter
342251881Speter      if (info->wc_info->changelist)
343251881Speter        /* "<changelist> xx </changelist>" */
344251881Speter        svn_cl__xml_tagged_cdata(&sb, pool, "changelist",
345251881Speter                                 info->wc_info->changelist);
346251881Speter
347251881Speter      if (info->wc_info->moved_from_abspath)
348251881Speter        {
349251881Speter          const char *relpath;
350251881Speter
351251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
352251881Speter                                             info->wc_info->moved_from_abspath);
353251881Speter
354251881Speter          /* <moved-from> xx </moved-from> */
355251881Speter          if (relpath && relpath[0] != '\0')
356251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-from", relpath);
357251881Speter          else
358251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-from",
359251881Speter                                     info->wc_info->moved_from_abspath);
360251881Speter        }
361251881Speter
362251881Speter      if (info->wc_info->moved_to_abspath)
363251881Speter        {
364251881Speter          const char *relpath;
365251881Speter
366251881Speter          relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
367251881Speter                                             info->wc_info->moved_to_abspath);
368251881Speter          /* <moved-to> xx </moved-to> */
369251881Speter          if (relpath && relpath[0] != '\0')
370251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-to", relpath);
371251881Speter          else
372251881Speter            svn_cl__xml_tagged_cdata(&sb, pool, "moved-to",
373251881Speter                                     info->wc_info->moved_to_abspath);
374251881Speter        }
375251881Speter
376251881Speter      /* "</wc-info>" */
377251881Speter      svn_xml_make_close_tag(&sb, pool, "wc-info");
378251881Speter    }
379251881Speter
380251881Speter  if (info->last_changed_author
381251881Speter      || SVN_IS_VALID_REVNUM(info->last_changed_rev)
382251881Speter      || info->last_changed_date)
383251881Speter    {
384251881Speter      svn_cl__print_xml_commit(&sb, info->last_changed_rev,
385251881Speter                               info->last_changed_author,
386251881Speter                               svn_time_to_cstring(info->last_changed_date,
387251881Speter                                                   pool),
388251881Speter                               pool);
389251881Speter    }
390251881Speter
391251881Speter  if (info->wc_info && info->wc_info->conflicts)
392251881Speter    {
393251881Speter      int i;
394251881Speter
395251881Speter      for (i = 0; i < info->wc_info->conflicts->nelts; i++)
396251881Speter        {
397251881Speter          const svn_wc_conflict_description2_t *conflict =
398251881Speter                      APR_ARRAY_IDX(info->wc_info->conflicts, i,
399251881Speter                                    const svn_wc_conflict_description2_t *);
400251881Speter
401251881Speter          SVN_ERR(svn_cl__append_conflict_info_xml(sb, conflict, pool));
402251881Speter        }
403251881Speter    }
404251881Speter
405251881Speter  if (info->lock)
406251881Speter    svn_cl__print_xml_lock(&sb, info->lock, pool);
407251881Speter
408251881Speter  /* "</entry>" */
409251881Speter  svn_xml_make_close_tag(&sb, pool, "entry");
410251881Speter
411251881Speter  return svn_cl__error_checked_fputs(sb->data, stdout);
412251881Speter}
413251881Speter
414251881Speter
415251881Speter/* A callback of type svn_client_info_receiver2_t. */
416251881Speterstatic svn_error_t *
417251881Speterprint_info(void *baton,
418251881Speter           const char *target,
419251881Speter           const svn_client_info2_t *info,
420251881Speter           apr_pool_t *pool)
421251881Speter{
422299742Sdim  print_info_baton_t *const receiver_baton = baton;
423251881Speter
424251881Speter  SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
425251881Speter                             svn_cl__local_style_skip_ancestor(
426299742Sdim                               receiver_baton->path_prefix, target, pool)));
427251881Speter
428251881Speter  /* ### remove this someday:  it's only here for cmdline output
429251881Speter     compatibility with svn 1.1 and older.  */
430251881Speter  if (info->kind != svn_node_dir)
431251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Name: %s\n"),
432251881Speter                               svn_dirent_basename(target, pool)));
433251881Speter
434251881Speter  if (info->wc_info && info->wc_info->wcroot_abspath)
435251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Working Copy Root Path: %s\n"),
436251881Speter                               svn_dirent_local_style(
437251881Speter                                            info->wc_info->wcroot_abspath,
438251881Speter                                            pool)));
439251881Speter
440251881Speter  if (info->URL)
441251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL));
442251881Speter
443251881Speter  if (info->URL && info->repos_root_URL)
444299742Sdim    SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: %s\n"),
445299742Sdim                               relative_url(info, pool)));
446251881Speter
447251881Speter  if (info->repos_root_URL)
448251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"),
449251881Speter                               info->repos_root_URL));
450251881Speter
451251881Speter  if (info->repos_UUID)
452251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Repository UUID: %s\n"),
453251881Speter                               info->repos_UUID));
454251881Speter
455251881Speter  if (SVN_IS_VALID_REVNUM(info->rev))
456251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Revision: %ld\n"), info->rev));
457251881Speter
458251881Speter  switch (info->kind)
459251881Speter    {
460251881Speter    case svn_node_file:
461251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: file\n")));
462251881Speter      break;
463251881Speter
464251881Speter    case svn_node_dir:
465251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: directory\n")));
466251881Speter      break;
467251881Speter
468251881Speter    case svn_node_none:
469251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: none\n")));
470251881Speter      break;
471251881Speter
472251881Speter    case svn_node_unknown:
473251881Speter    default:
474251881Speter      SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: unknown\n")));
475251881Speter      break;
476251881Speter    }
477251881Speter
478251881Speter  if (info->wc_info)
479251881Speter    {
480251881Speter      switch (info->wc_info->schedule)
481251881Speter        {
482251881Speter        case svn_wc_schedule_normal:
483251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: normal\n")));
484251881Speter          break;
485251881Speter
486251881Speter        case svn_wc_schedule_add:
487251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: add\n")));
488251881Speter          break;
489251881Speter
490251881Speter        case svn_wc_schedule_delete:
491251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: delete\n")));
492251881Speter          break;
493251881Speter
494251881Speter        case svn_wc_schedule_replace:
495251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Schedule: replace\n")));
496251881Speter          break;
497251881Speter
498251881Speter        default:
499251881Speter          break;
500251881Speter        }
501251881Speter
502251881Speter      switch (info->wc_info->depth)
503251881Speter        {
504251881Speter        case svn_depth_unknown:
505251881Speter          /* Unknown depth is the norm for remote directories anyway
506251881Speter             (although infinity would be equally appropriate).  Let's
507251881Speter             not bother to print it. */
508251881Speter          break;
509251881Speter
510251881Speter        case svn_depth_empty:
511251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: empty\n")));
512251881Speter          break;
513251881Speter
514251881Speter        case svn_depth_files:
515251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: files\n")));
516251881Speter          break;
517251881Speter
518251881Speter        case svn_depth_immediates:
519251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: immediates\n")));
520251881Speter          break;
521251881Speter
522251881Speter        case svn_depth_exclude:
523251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: exclude\n")));
524251881Speter          break;
525251881Speter
526251881Speter        case svn_depth_infinity:
527251881Speter          /* Infinity is the default depth for working copy
528251881Speter             directories.  Let's not print it, it's not special enough
529251881Speter             to be worth mentioning.  */
530251881Speter          break;
531251881Speter
532251881Speter        default:
533251881Speter          /* Other depths should never happen here. */
534251881Speter          SVN_ERR(svn_cmdline_printf(pool, _("Depth: INVALID\n")));
535251881Speter        }
536251881Speter
537251881Speter      if (info->wc_info->copyfrom_url)
538251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Copied From URL: %s\n"),
539251881Speter                                   info->wc_info->copyfrom_url));
540251881Speter
541251881Speter      if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
542251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"),
543251881Speter                                   info->wc_info->copyfrom_rev));
544251881Speter      if (info->wc_info->moved_from_abspath)
545299742Sdim        SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"),
546299742Sdim                                   svn_cl__local_style_skip_ancestor(
547299742Sdim                                      receiver_baton->path_prefix,
548299742Sdim                                      info->wc_info->moved_from_abspath,
549299742Sdim                                      pool)));
550251881Speter
551251881Speter      if (info->wc_info->moved_to_abspath)
552299742Sdim        SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"),
553299742Sdim                                   svn_cl__local_style_skip_ancestor(
554299742Sdim                                      receiver_baton->path_prefix,
555299742Sdim                                      info->wc_info->moved_to_abspath,
556299742Sdim                                      pool)));
557251881Speter    }
558251881Speter
559251881Speter  if (info->last_changed_author)
560251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Author: %s\n"),
561251881Speter                               info->last_changed_author));
562251881Speter
563251881Speter  if (SVN_IS_VALID_REVNUM(info->last_changed_rev))
564251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Rev: %ld\n"),
565251881Speter                               info->last_changed_rev));
566251881Speter
567251881Speter  if (info->last_changed_date)
568251881Speter    SVN_ERR(svn_cl__info_print_time(info->last_changed_date,
569251881Speter                                    _("Last Changed Date"), pool));
570251881Speter
571251881Speter  if (info->wc_info)
572251881Speter    {
573251881Speter      if (info->wc_info->recorded_time)
574251881Speter        SVN_ERR(svn_cl__info_print_time(info->wc_info->recorded_time,
575251881Speter                                        _("Text Last Updated"), pool));
576251881Speter
577251881Speter      if (info->wc_info->checksum)
578251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Checksum: %s\n"),
579251881Speter                                   svn_checksum_to_cstring(
580251881Speter                                              info->wc_info->checksum, pool)));
581251881Speter
582251881Speter      if (info->wc_info->conflicts)
583251881Speter        {
584251881Speter          svn_boolean_t printed_prop_conflict_file = FALSE;
585299742Sdim          svn_boolean_t printed_tc = FALSE;
586251881Speter          int i;
587251881Speter
588251881Speter          for (i = 0; i < info->wc_info->conflicts->nelts; i++)
589251881Speter            {
590251881Speter              const svn_wc_conflict_description2_t *conflict =
591251881Speter                    APR_ARRAY_IDX(info->wc_info->conflicts, i,
592251881Speter                                  const svn_wc_conflict_description2_t *);
593251881Speter              const char *desc;
594251881Speter
595251881Speter              switch (conflict->kind)
596251881Speter                {
597251881Speter                  case svn_wc_conflict_kind_text:
598251881Speter                    if (conflict->base_abspath)
599251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
600251881Speter                                _("Conflict Previous Base File: %s\n"),
601251881Speter                                svn_cl__local_style_skip_ancestor(
602299742Sdim                                        receiver_baton->path_prefix,
603299742Sdim                                        conflict->base_abspath,
604251881Speter                                        pool)));
605251881Speter
606251881Speter                    if (conflict->my_abspath)
607251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
608251881Speter                                _("Conflict Previous Working File: %s\n"),
609251881Speter                                svn_cl__local_style_skip_ancestor(
610299742Sdim                                        receiver_baton->path_prefix,
611299742Sdim                                        conflict->my_abspath,
612251881Speter                                        pool)));
613251881Speter
614251881Speter                    if (conflict->their_abspath)
615251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
616251881Speter                                _("Conflict Current Base File: %s\n"),
617251881Speter                                svn_cl__local_style_skip_ancestor(
618299742Sdim                                        receiver_baton->path_prefix,
619299742Sdim                                        conflict->their_abspath,
620251881Speter                                        pool)));
621251881Speter                  break;
622251881Speter
623251881Speter                  case svn_wc_conflict_kind_property:
624251881Speter                    if (! printed_prop_conflict_file)
625251881Speter                      SVN_ERR(svn_cmdline_printf(pool,
626251881Speter                                _("Conflict Properties File: %s\n"),
627299742Sdim                                svn_cl__local_style_skip_ancestor(
628299742Sdim                                        receiver_baton->path_prefix,
629299742Sdim                                        conflict->prop_reject_abspath,
630299742Sdim                                        pool)));
631251881Speter                    printed_prop_conflict_file = TRUE;
632251881Speter                  break;
633251881Speter
634251881Speter                  case svn_wc_conflict_kind_tree:
635299742Sdim                    printed_tc = TRUE;
636251881Speter                    SVN_ERR(
637251881Speter                        svn_cl__get_human_readable_tree_conflict_description(
638251881Speter                                                    &desc, conflict, pool));
639251881Speter
640251881Speter                    SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
641251881Speter                                               _("Tree conflict"), desc));
642251881Speter                  break;
643251881Speter                }
644251881Speter            }
645251881Speter
646251881Speter          /* We only store one left and right version for all conflicts, which is
647251881Speter             referenced from all conflicts.
648251881Speter             Print it after the conflicts to match the 1.6/1.7 output where it is
649251881Speter             only available for tree conflicts */
650251881Speter          {
651251881Speter            const char *src_left_version;
652251881Speter            const char *src_right_version;
653251881Speter            const svn_wc_conflict_description2_t *conflict =
654251881Speter                  APR_ARRAY_IDX(info->wc_info->conflicts, 0,
655251881Speter                                const svn_wc_conflict_description2_t *);
656251881Speter
657299742Sdim            if (!printed_tc)
658299742Sdim              {
659299742Sdim                const char *desc;
660299742Sdim
661299742Sdim                SVN_ERR(svn_cl__get_human_readable_action_description(&desc,
662299742Sdim                                        svn_wc_conflict_action_edit,
663299742Sdim                                        conflict->operation,
664299742Sdim                                        conflict->node_kind, pool));
665299742Sdim
666299742Sdim                SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
667299742Sdim                                               _("Conflict Details"), desc));
668299742Sdim              }
669299742Sdim
670251881Speter            src_left_version =
671251881Speter                        svn_cl__node_description(conflict->src_left_version,
672251881Speter                                                 info->repos_root_URL, pool);
673251881Speter
674251881Speter            src_right_version =
675251881Speter                        svn_cl__node_description(conflict->src_right_version,
676251881Speter                                                 info->repos_root_URL, pool);
677251881Speter
678251881Speter            if (src_left_version)
679251881Speter              SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
680251881Speter                                         _("Source  left"), /* (1) */
681251881Speter                                         src_left_version));
682251881Speter            /* (1): Sneaking in a space in "Source  left" so that
683251881Speter             * it is the same length as "Source right" while it still
684251881Speter             * starts in the same column. That's just a tiny tweak in
685251881Speter             * the English `svn'. */
686251881Speter
687251881Speter            if (src_right_version)
688251881Speter              SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
689251881Speter                                         _("Source right"),
690251881Speter                                         src_right_version));
691251881Speter          }
692251881Speter        }
693251881Speter    }
694251881Speter
695251881Speter  if (info->lock)
696251881Speter    {
697251881Speter      if (info->lock->token)
698251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Lock Token: %s\n"),
699251881Speter                                   info->lock->token));
700251881Speter
701251881Speter      if (info->lock->owner)
702251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Lock Owner: %s\n"),
703251881Speter                                   info->lock->owner));
704251881Speter
705251881Speter      if (info->lock->creation_date)
706251881Speter        SVN_ERR(svn_cl__info_print_time(info->lock->creation_date,
707251881Speter                                        _("Lock Created"), pool));
708251881Speter
709251881Speter      if (info->lock->expiration_date)
710251881Speter        SVN_ERR(svn_cl__info_print_time(info->lock->expiration_date,
711251881Speter                                        _("Lock Expires"), pool));
712251881Speter
713251881Speter      if (info->lock->comment)
714251881Speter        {
715251881Speter          int comment_lines;
716251881Speter          /* NOTE: The stdio will handle newline translation. */
717251881Speter          comment_lines = svn_cstring_count_newlines(info->lock->comment) + 1;
718251881Speter          SVN_ERR(svn_cmdline_printf(pool,
719251881Speter                                     Q_("Lock Comment (%i line):\n%s\n",
720251881Speter                                        "Lock Comment (%i lines):\n%s\n",
721251881Speter                                        comment_lines),
722251881Speter                                     comment_lines,
723251881Speter                                     info->lock->comment));
724251881Speter        }
725251881Speter    }
726251881Speter
727251881Speter  if (info->wc_info && info->wc_info->changelist)
728251881Speter    SVN_ERR(svn_cmdline_printf(pool, _("Changelist: %s\n"),
729251881Speter                               info->wc_info->changelist));
730251881Speter
731251881Speter  /* Print extra newline separator. */
732251881Speter  return svn_cmdline_printf(pool, "\n");
733251881Speter}
734251881Speter
735251881Speter
736299742Sdim/* Helper for print_info_item(): Print the value TEXT for TARGET_PATH,
737299742Sdim   either of which may be NULL. Use POOL for temporary allocation. */
738299742Sdimstatic svn_error_t *
739299742Sdimprint_info_item_string(const char *text, const char *target_path,
740299742Sdim                       apr_pool_t *pool)
741299742Sdim{
742299742Sdim  if (text)
743299742Sdim    {
744299742Sdim      if (target_path)
745299742Sdim        SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", text, target_path));
746299742Sdim      else
747299742Sdim        SVN_ERR(svn_cmdline_fputs(text, stdout, pool));
748299742Sdim    }
749299742Sdim  else if (target_path)
750299742Sdim    SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path));
751299742Sdim
752299742Sdim  return SVN_NO_ERROR;
753299742Sdim}
754299742Sdim
755299742Sdim/* Helper for print_info_item(): Print the revision number REV, which
756299742Sdim   may be SVN_INVALID_REVNUM, for TARGET_PATH, which may be NULL. Use
757299742Sdim   POOL for temporary allocation. */
758299742Sdimstatic svn_error_t *
759299742Sdimprint_info_item_revision(svn_revnum_t rev, const char *target_path,
760299742Sdim                         apr_pool_t *pool)
761299742Sdim{
762299742Sdim  if (SVN_IS_VALID_REVNUM(rev))
763299742Sdim    {
764299742Sdim      if (target_path)
765299742Sdim        SVN_ERR(svn_cmdline_printf(pool, "%-10ld %s", rev, target_path));
766299742Sdim      else
767309512Speter        SVN_ERR(svn_cmdline_printf(pool, "%ld", rev));
768299742Sdim    }
769299742Sdim  else if (target_path)
770299742Sdim    SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path));
771299742Sdim
772299742Sdim  return SVN_NO_ERROR;
773299742Sdim}
774299742Sdim
775299742Sdim/* A callback of type svn_client_info_receiver2_t. */
776299742Sdimstatic svn_error_t *
777299742Sdimprint_info_item(void *baton,
778299742Sdim                  const char *target,
779299742Sdim                  const svn_client_info2_t *info,
780299742Sdim                  apr_pool_t *pool)
781299742Sdim{
782299742Sdim  print_info_baton_t *const receiver_baton = baton;
783299742Sdim  const char *const target_path =
784299742Sdim    (!receiver_baton->multiple_targets ? NULL
785299742Sdim     : (!receiver_baton->target_is_path ? info->URL
786299742Sdim        : svn_cl__local_style_skip_ancestor(
787299742Sdim            receiver_baton->path_prefix, target, pool)));
788299742Sdim
789299742Sdim  if (receiver_baton->start_new_line)
790299742Sdim    SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
791299742Sdim
792299742Sdim  switch (receiver_baton->print_what)
793299742Sdim    {
794299742Sdim    case info_item_kind:
795299742Sdim      SVN_ERR(print_info_item_string(svn_node_kind_to_word(info->kind),
796299742Sdim                                     target_path, pool));
797299742Sdim      break;
798299742Sdim
799299742Sdim    case info_item_url:
800299742Sdim      SVN_ERR(print_info_item_string(info->URL, target_path, pool));
801299742Sdim      break;
802299742Sdim
803299742Sdim    case info_item_relative_url:
804299742Sdim      SVN_ERR(print_info_item_string(relative_url(info, pool),
805299742Sdim                                     target_path, pool));
806299742Sdim      break;
807299742Sdim
808299742Sdim    case info_item_repos_root_url:
809299742Sdim      SVN_ERR(print_info_item_string(info->repos_root_URL, target_path, pool));
810299742Sdim      break;
811299742Sdim
812299742Sdim    case info_item_repos_uuid:
813299742Sdim      SVN_ERR(print_info_item_string(info->repos_UUID, target_path, pool));
814299742Sdim      break;
815299742Sdim
816299742Sdim    case info_item_revision:
817299742Sdim      SVN_ERR(print_info_item_revision(info->rev, target_path, pool));
818299742Sdim      break;
819299742Sdim
820299742Sdim    case info_item_last_changed_rev:
821299742Sdim      SVN_ERR(print_info_item_revision(info->last_changed_rev,
822299742Sdim                                       target_path, pool));
823299742Sdim      break;
824299742Sdim
825299742Sdim    case info_item_last_changed_date:
826299742Sdim      SVN_ERR(print_info_item_string(
827299742Sdim                  (!info->last_changed_date ? NULL
828299742Sdim                   : svn_time_to_cstring(info->last_changed_date, pool)),
829299742Sdim                  target_path, pool));
830299742Sdim      break;
831299742Sdim
832299742Sdim    case info_item_last_changed_author:
833299742Sdim      SVN_ERR(print_info_item_string(info->last_changed_author,
834299742Sdim                                     target_path, pool));
835299742Sdim      break;
836299742Sdim
837299742Sdim    case info_item_wc_root:
838299742Sdim      SVN_ERR(print_info_item_string(
839299742Sdim                  (info->wc_info && info->wc_info->wcroot_abspath
840299742Sdim                   ? info->wc_info->wcroot_abspath : NULL),
841299742Sdim                  target_path, pool));
842299742Sdim      break;
843299742Sdim
844299742Sdim    default:
845299742Sdim      SVN_ERR_MALFUNCTION();
846299742Sdim    }
847299742Sdim
848299742Sdim  receiver_baton->start_new_line = TRUE;
849299742Sdim  return SVN_NO_ERROR;
850299742Sdim}
851299742Sdim
852299742Sdim
853251881Speter/* This implements the `svn_opt_subcommand_t' interface. */
854251881Spetersvn_error_t *
855251881Spetersvn_cl__info(apr_getopt_t *os,
856251881Speter             void *baton,
857251881Speter             apr_pool_t *pool)
858251881Speter{
859251881Speter  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
860251881Speter  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
861251881Speter  apr_array_header_t *targets = NULL;
862251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
863251881Speter  int i;
864251881Speter  svn_error_t *err;
865251881Speter  svn_boolean_t seen_nonexistent_target = FALSE;
866251881Speter  svn_opt_revision_t peg_revision;
867251881Speter  svn_client_info_receiver2_t receiver;
868299742Sdim  print_info_baton_t receiver_baton = { 0 };
869251881Speter
870251881Speter  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
871251881Speter                                                      opt_state->targets,
872251881Speter                                                      ctx, FALSE, pool));
873251881Speter
874251881Speter  /* Add "." if user passed 0 arguments. */
875251881Speter  svn_opt_push_implicit_dot_target(targets, pool);
876251881Speter
877251881Speter  if (opt_state->xml)
878251881Speter    {
879251881Speter      receiver = print_info_xml;
880251881Speter
881299742Sdim      if (opt_state->show_item)
882299742Sdim        return svn_error_create(
883299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
884299742Sdim            _("--show-item is not valid in --xml mode"));
885299742Sdim      if (opt_state->no_newline)
886299742Sdim        return svn_error_create(
887299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
888299742Sdim            _("--no-newline is not valid in --xml mode"));
889299742Sdim
890251881Speter      /* If output is not incremental, output the XML header and wrap
891251881Speter         everything in a top-level element. This makes the output in
892251881Speter         its entirety a well-formed XML document. */
893251881Speter      if (! opt_state->incremental)
894251881Speter        SVN_ERR(svn_cl__xml_print_header("info", pool));
895251881Speter    }
896299742Sdim  else if (opt_state->show_item)
897299742Sdim    {
898299742Sdim      receiver = print_info_item;
899299742Sdim
900299742Sdim      if (opt_state->incremental)
901299742Sdim        return svn_error_create(
902299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
903299742Sdim            _("--incremental is only valid in --xml mode"));
904299742Sdim
905299742Sdim      receiver_baton.multiple_targets = (opt_state->depth > svn_depth_empty
906299742Sdim                                         || targets->nelts > 1);
907299742Sdim      if (receiver_baton.multiple_targets && opt_state->no_newline)
908299742Sdim        return svn_error_create(
909299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
910299742Sdim            _("--no-newline is only available for single-target,"
911299742Sdim              " non-recursive info operations"));
912299742Sdim
913299742Sdim      SVN_ERR(find_print_what(opt_state->show_item, &receiver_baton, pool));
914299742Sdim      receiver_baton.start_new_line = FALSE;
915299742Sdim    }
916251881Speter  else
917251881Speter    {
918251881Speter      receiver = print_info;
919251881Speter
920251881Speter      if (opt_state->incremental)
921299742Sdim        return svn_error_create(
922299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
923299742Sdim            _("--incremental is only valid in --xml mode"));
924299742Sdim      if (opt_state->no_newline)
925299742Sdim        return svn_error_create(
926299742Sdim            SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
927299742Sdim            _("--no-newline' is only valid with --show-item"));
928251881Speter    }
929251881Speter
930251881Speter  if (opt_state->depth == svn_depth_unknown)
931251881Speter    opt_state->depth = svn_depth_empty;
932251881Speter
933299742Sdim  SVN_ERR(svn_dirent_get_absolute(&receiver_baton.path_prefix, "", pool));
934251881Speter
935251881Speter  for (i = 0; i < targets->nelts; i++)
936251881Speter    {
937251881Speter      const char *truepath;
938251881Speter      const char *target = APR_ARRAY_IDX(targets, i, const char *);
939251881Speter
940251881Speter      svn_pool_clear(subpool);
941251881Speter      SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
942251881Speter
943251881Speter      /* Get peg revisions. */
944251881Speter      SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool));
945251881Speter
946251881Speter      /* If no peg-rev was attached to a URL target, then assume HEAD. */
947251881Speter      if (svn_path_is_url(truepath))
948251881Speter        {
949251881Speter          if (peg_revision.kind == svn_opt_revision_unspecified)
950251881Speter            peg_revision.kind = svn_opt_revision_head;
951299742Sdim          receiver_baton.target_is_path = FALSE;
952251881Speter        }
953251881Speter      else
954251881Speter        {
955251881Speter          SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
956299742Sdim          receiver_baton.target_is_path = TRUE;
957251881Speter        }
958251881Speter
959299742Sdim      err = svn_client_info4(truepath,
960251881Speter                             &peg_revision, &(opt_state->start_revision),
961299742Sdim                             opt_state->depth,
962299742Sdim                             TRUE /* fetch_excluded */,
963299742Sdim                             TRUE /* fetch_actual_only */,
964299742Sdim                             opt_state->include_externals,
965251881Speter                             opt_state->changelists,
966299742Sdim                             receiver, &receiver_baton,
967251881Speter                             ctx, subpool);
968251881Speter
969251881Speter      if (err)
970251881Speter        {
971251881Speter          /* If one of the targets is a non-existent URL or wc-entry,
972251881Speter             don't bail out.  Just warn and move on to the next target. */
973251881Speter          if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
974251881Speter              err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
975251881Speter            {
976251881Speter              svn_handle_warning2(stderr, err, "svn: ");
977251881Speter              svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n"));
978251881Speter            }
979251881Speter          else
980251881Speter            {
981251881Speter              return svn_error_trace(err);
982251881Speter            }
983251881Speter
984251881Speter          svn_error_clear(err);
985251881Speter          err = NULL;
986251881Speter          seen_nonexistent_target = TRUE;
987251881Speter        }
988251881Speter    }
989251881Speter  svn_pool_destroy(subpool);
990251881Speter
991251881Speter  if (opt_state->xml && (! opt_state->incremental))
992251881Speter    SVN_ERR(svn_cl__xml_print_footer("info", pool));
993299742Sdim  else if (opt_state->show_item && !opt_state->no_newline
994299742Sdim           && receiver_baton.start_new_line)
995299742Sdim    SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
996251881Speter
997251881Speter  if (seen_nonexistent_target)
998251881Speter    return svn_error_create(
999251881Speter      SVN_ERR_ILLEGAL_TARGET, NULL,
1000251881Speter      _("Could not display info for all targets because some "
1001251881Speter        "targets don't exist"));
1002251881Speter  else
1003251881Speter    return SVN_NO_ERROR;
1004251881Speter}
1005