]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/svn/info-cmd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / svn / info-cmd.c
1 /*
2  * info-cmd.c -- Display information about a resource
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_string.h"
31 #include "svn_cmdline.h"
32 #include "svn_wc.h"
33 #include "svn_pools.h"
34 #include "svn_error_codes.h"
35 #include "svn_error.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_path.h"
38 #include "svn_time.h"
39 #include "svn_xml.h"
40 #include "cl.h"
41
42 #include "svn_private_config.h"
43 #include "cl-conflicts.h"
44
45 \f
46 /*** Code. ***/
47
48 static svn_error_t *
49 svn_cl__info_print_time(apr_time_t atime,
50                         const char *desc,
51                         apr_pool_t *pool)
52 {
53   const char *time_utf8;
54
55   time_utf8 = svn_time_to_human_cstring(atime, pool);
56   return svn_cmdline_printf(pool, "%s: %s\n", desc, time_utf8);
57 }
58
59
60 /* Return string representation of SCHEDULE */
61 static const char *
62 schedule_str(svn_wc_schedule_t schedule)
63 {
64   switch (schedule)
65     {
66     case svn_wc_schedule_normal:
67       return "normal";
68     case svn_wc_schedule_add:
69       return "add";
70     case svn_wc_schedule_delete:
71       return "delete";
72     case svn_wc_schedule_replace:
73       return "replace";
74     default:
75       return "none";
76     }
77 }
78
79
80 /* A callback of type svn_client_info_receiver2_t.
81    Prints svn info in xml mode to standard out */
82 static svn_error_t *
83 print_info_xml(void *baton,
84                const char *target,
85                const svn_client_info2_t *info,
86                apr_pool_t *pool)
87 {
88   svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
89   const char *rev_str;
90   const char *path_prefix = baton;
91
92   if (SVN_IS_VALID_REVNUM(info->rev))
93     rev_str = apr_psprintf(pool, "%ld", info->rev);
94   else
95     rev_str = apr_pstrdup(pool, _("Resource is not under version control."));
96
97   /* "<entry ...>" */
98   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
99                         "path", svn_cl__local_style_skip_ancestor(
100                                   path_prefix, target, pool),
101                         "kind", svn_cl__node_kind_str_xml(info->kind),
102                         "revision", rev_str,
103                         NULL);
104
105   /* "<url> xx </url>" */
106   svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL);
107
108   if (info->repos_root_URL && info->URL)
109     {
110       /* "<relative-url> xx </relative-url>" */
111       svn_cl__xml_tagged_cdata(&sb, pool, "relative-url",
112                                apr_pstrcat(pool, "^/",
113                                            svn_path_uri_encode(
114                                                svn_uri_skip_ancestor(
115                                                    info->repos_root_URL,
116                                                    info->URL, pool),
117                                                pool),
118                                            NULL));
119     }
120
121   if (info->repos_root_URL || info->repos_UUID)
122     {
123       /* "<repository>" */
124       svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL);
125
126       /* "<root> xx </root>" */
127       svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL);
128
129       /* "<uuid> xx </uuid>" */
130       svn_cl__xml_tagged_cdata(&sb, pool, "uuid", info->repos_UUID);
131
132       /* "</repository>" */
133       svn_xml_make_close_tag(&sb, pool, "repository");
134     }
135
136   if (info->wc_info)
137     {
138       /* "<wc-info>" */
139       svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL);
140
141       /* "<wcroot-abspath> xx </wcroot-abspath>" */
142       if (info->wc_info->wcroot_abspath)
143         svn_cl__xml_tagged_cdata(&sb, pool, "wcroot-abspath",
144                                  info->wc_info->wcroot_abspath);
145
146       /* "<schedule> xx </schedule>" */
147       svn_cl__xml_tagged_cdata(&sb, pool, "schedule",
148                                schedule_str(info->wc_info->schedule));
149
150       /* "<depth> xx </depth>" */
151       {
152         svn_depth_t depth = info->wc_info->depth;
153
154         /* In the entries world info just passed depth infinity for files */
155         if (depth == svn_depth_unknown && info->kind == svn_node_file)
156           depth = svn_depth_infinity;
157
158         svn_cl__xml_tagged_cdata(&sb, pool, "depth", svn_depth_to_word(depth));
159       }
160
161       /* "<copy-from-url> xx </copy-from-url>" */
162       svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-url",
163                                info->wc_info->copyfrom_url);
164
165       /* "<copy-from-rev> xx </copy-from-rev>" */
166       if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
167         svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-rev",
168                                  apr_psprintf(pool, "%ld",
169                                               info->wc_info->copyfrom_rev));
170
171       /* "<text-updated> xx </text-updated>" */
172       if (info->wc_info->recorded_time)
173         svn_cl__xml_tagged_cdata(&sb, pool, "text-updated",
174                                  svn_time_to_cstring(
175                                           info->wc_info->recorded_time,
176                                           pool));
177
178       /* "<checksum> xx </checksum>" */
179       /* ### Print the checksum kind. */
180       svn_cl__xml_tagged_cdata(&sb, pool, "checksum",
181                                svn_checksum_to_cstring(info->wc_info->checksum,
182                                                        pool));
183
184       if (info->wc_info->changelist)
185         /* "<changelist> xx </changelist>" */
186         svn_cl__xml_tagged_cdata(&sb, pool, "changelist",
187                                  info->wc_info->changelist);
188
189       if (info->wc_info->moved_from_abspath)
190         {
191           const char *relpath;
192
193           relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
194                                              info->wc_info->moved_from_abspath);
195
196           /* <moved-from> xx </moved-from> */
197           if (relpath && relpath[0] != '\0')
198             svn_cl__xml_tagged_cdata(&sb, pool, "moved-from", relpath);
199           else
200             svn_cl__xml_tagged_cdata(&sb, pool, "moved-from",
201                                      info->wc_info->moved_from_abspath);
202         }
203
204       if (info->wc_info->moved_to_abspath)
205         {
206           const char *relpath;
207
208           relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
209                                              info->wc_info->moved_to_abspath);
210           /* <moved-to> xx </moved-to> */
211           if (relpath && relpath[0] != '\0')
212             svn_cl__xml_tagged_cdata(&sb, pool, "moved-to", relpath);
213           else
214             svn_cl__xml_tagged_cdata(&sb, pool, "moved-to",
215                                      info->wc_info->moved_to_abspath);
216         }
217
218       /* "</wc-info>" */
219       svn_xml_make_close_tag(&sb, pool, "wc-info");
220     }
221
222   if (info->last_changed_author
223       || SVN_IS_VALID_REVNUM(info->last_changed_rev)
224       || info->last_changed_date)
225     {
226       svn_cl__print_xml_commit(&sb, info->last_changed_rev,
227                                info->last_changed_author,
228                                svn_time_to_cstring(info->last_changed_date,
229                                                    pool),
230                                pool);
231     }
232
233   if (info->wc_info && info->wc_info->conflicts)
234     {
235       int i;
236
237       for (i = 0; i < info->wc_info->conflicts->nelts; i++)
238         {
239           const svn_wc_conflict_description2_t *conflict =
240                       APR_ARRAY_IDX(info->wc_info->conflicts, i,
241                                     const svn_wc_conflict_description2_t *);
242
243           SVN_ERR(svn_cl__append_conflict_info_xml(sb, conflict, pool));
244         }
245     }
246
247   if (info->lock)
248     svn_cl__print_xml_lock(&sb, info->lock, pool);
249
250   /* "</entry>" */
251   svn_xml_make_close_tag(&sb, pool, "entry");
252
253   return svn_cl__error_checked_fputs(sb->data, stdout);
254 }
255
256
257 /* A callback of type svn_client_info_receiver2_t. */
258 static svn_error_t *
259 print_info(void *baton,
260            const char *target,
261            const svn_client_info2_t *info,
262            apr_pool_t *pool)
263 {
264   const char *path_prefix = baton;
265
266   SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
267                              svn_cl__local_style_skip_ancestor(
268                                path_prefix, target, pool)));
269
270   /* ### remove this someday:  it's only here for cmdline output
271      compatibility with svn 1.1 and older.  */
272   if (info->kind != svn_node_dir)
273     SVN_ERR(svn_cmdline_printf(pool, _("Name: %s\n"),
274                                svn_dirent_basename(target, pool)));
275
276   if (info->wc_info && info->wc_info->wcroot_abspath)
277     SVN_ERR(svn_cmdline_printf(pool, _("Working Copy Root Path: %s\n"),
278                                svn_dirent_local_style(
279                                             info->wc_info->wcroot_abspath,
280                                             pool)));
281
282   if (info->URL)
283     SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL));
284
285   if (info->URL && info->repos_root_URL)
286     SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: ^/%s\n"),
287                                svn_path_uri_encode(
288                                    svn_uri_skip_ancestor(info->repos_root_URL,
289                                                          info->URL, pool),
290                                    pool)));
291
292   if (info->repos_root_URL)
293     SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"),
294                                info->repos_root_URL));
295
296   if (info->repos_UUID)
297     SVN_ERR(svn_cmdline_printf(pool, _("Repository UUID: %s\n"),
298                                info->repos_UUID));
299
300   if (SVN_IS_VALID_REVNUM(info->rev))
301     SVN_ERR(svn_cmdline_printf(pool, _("Revision: %ld\n"), info->rev));
302
303   switch (info->kind)
304     {
305     case svn_node_file:
306       SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: file\n")));
307       break;
308
309     case svn_node_dir:
310       SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: directory\n")));
311       break;
312
313     case svn_node_none:
314       SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: none\n")));
315       break;
316
317     case svn_node_unknown:
318     default:
319       SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: unknown\n")));
320       break;
321     }
322
323   if (info->wc_info)
324     {
325       switch (info->wc_info->schedule)
326         {
327         case svn_wc_schedule_normal:
328           SVN_ERR(svn_cmdline_printf(pool, _("Schedule: normal\n")));
329           break;
330
331         case svn_wc_schedule_add:
332           SVN_ERR(svn_cmdline_printf(pool, _("Schedule: add\n")));
333           break;
334
335         case svn_wc_schedule_delete:
336           SVN_ERR(svn_cmdline_printf(pool, _("Schedule: delete\n")));
337           break;
338
339         case svn_wc_schedule_replace:
340           SVN_ERR(svn_cmdline_printf(pool, _("Schedule: replace\n")));
341           break;
342
343         default:
344           break;
345         }
346
347       switch (info->wc_info->depth)
348         {
349         case svn_depth_unknown:
350           /* Unknown depth is the norm for remote directories anyway
351              (although infinity would be equally appropriate).  Let's
352              not bother to print it. */
353           break;
354
355         case svn_depth_empty:
356           SVN_ERR(svn_cmdline_printf(pool, _("Depth: empty\n")));
357           break;
358
359         case svn_depth_files:
360           SVN_ERR(svn_cmdline_printf(pool, _("Depth: files\n")));
361           break;
362
363         case svn_depth_immediates:
364           SVN_ERR(svn_cmdline_printf(pool, _("Depth: immediates\n")));
365           break;
366
367         case svn_depth_exclude:
368           SVN_ERR(svn_cmdline_printf(pool, _("Depth: exclude\n")));
369           break;
370
371         case svn_depth_infinity:
372           /* Infinity is the default depth for working copy
373              directories.  Let's not print it, it's not special enough
374              to be worth mentioning.  */
375           break;
376
377         default:
378           /* Other depths should never happen here. */
379           SVN_ERR(svn_cmdline_printf(pool, _("Depth: INVALID\n")));
380         }
381
382       if (info->wc_info->copyfrom_url)
383         SVN_ERR(svn_cmdline_printf(pool, _("Copied From URL: %s\n"),
384                                    info->wc_info->copyfrom_url));
385
386       if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
387         SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"),
388                                    info->wc_info->copyfrom_rev));
389       if (info->wc_info->moved_from_abspath)
390         {
391           const char *relpath;
392
393           relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
394                                              info->wc_info->moved_from_abspath);
395           if (relpath && relpath[0] != '\0')
396             SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), relpath));
397           else
398             SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"),
399                                        info->wc_info->moved_from_abspath));
400         }
401
402       if (info->wc_info->moved_to_abspath)
403         {
404           const char *relpath;
405
406           relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath,
407                                              info->wc_info->moved_to_abspath);
408           if (relpath && relpath[0] != '\0')
409             SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), relpath));
410           else
411             SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"),
412                                        info->wc_info->moved_to_abspath));
413         }
414     }
415
416   if (info->last_changed_author)
417     SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Author: %s\n"),
418                                info->last_changed_author));
419
420   if (SVN_IS_VALID_REVNUM(info->last_changed_rev))
421     SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Rev: %ld\n"),
422                                info->last_changed_rev));
423
424   if (info->last_changed_date)
425     SVN_ERR(svn_cl__info_print_time(info->last_changed_date,
426                                     _("Last Changed Date"), pool));
427
428   if (info->wc_info)
429     {
430       if (info->wc_info->recorded_time)
431         SVN_ERR(svn_cl__info_print_time(info->wc_info->recorded_time,
432                                         _("Text Last Updated"), pool));
433
434       if (info->wc_info->checksum)
435         SVN_ERR(svn_cmdline_printf(pool, _("Checksum: %s\n"),
436                                    svn_checksum_to_cstring(
437                                               info->wc_info->checksum, pool)));
438
439       if (info->wc_info->conflicts)
440         {
441           svn_boolean_t printed_prop_conflict_file = FALSE;
442           int i;
443
444           for (i = 0; i < info->wc_info->conflicts->nelts; i++)
445             {
446               const svn_wc_conflict_description2_t *conflict =
447                     APR_ARRAY_IDX(info->wc_info->conflicts, i,
448                                   const svn_wc_conflict_description2_t *);
449               const char *desc;
450
451               switch (conflict->kind)
452                 {
453                   case svn_wc_conflict_kind_text:
454                     if (conflict->base_abspath)
455                       SVN_ERR(svn_cmdline_printf(pool,
456                                 _("Conflict Previous Base File: %s\n"),
457                                 svn_cl__local_style_skip_ancestor(
458                                         path_prefix, conflict->base_abspath,
459                                         pool)));
460
461                     if (conflict->my_abspath)
462                       SVN_ERR(svn_cmdline_printf(pool,
463                                 _("Conflict Previous Working File: %s\n"),
464                                 svn_cl__local_style_skip_ancestor(
465                                         path_prefix, conflict->my_abspath,
466                                         pool)));
467
468                     if (conflict->their_abspath)
469                       SVN_ERR(svn_cmdline_printf(pool,
470                                 _("Conflict Current Base File: %s\n"),
471                                 svn_cl__local_style_skip_ancestor(
472                                         path_prefix, conflict->their_abspath,
473                                         pool)));
474                   break;
475
476                   case svn_wc_conflict_kind_property:
477                     if (! printed_prop_conflict_file)
478                       SVN_ERR(svn_cmdline_printf(pool,
479                                 _("Conflict Properties File: %s\n"),
480                                 svn_dirent_local_style(conflict->their_abspath,
481                                                        pool)));
482                     printed_prop_conflict_file = TRUE;
483                   break;
484
485                   case svn_wc_conflict_kind_tree:
486                     SVN_ERR(
487                         svn_cl__get_human_readable_tree_conflict_description(
488                                                     &desc, conflict, pool));
489
490                     SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
491                                                _("Tree conflict"), desc));
492                   break;
493                 }
494             }
495
496           /* We only store one left and right version for all conflicts, which is
497              referenced from all conflicts.
498              Print it after the conflicts to match the 1.6/1.7 output where it is
499              only available for tree conflicts */
500           {
501             const char *src_left_version;
502             const char *src_right_version;
503             const svn_wc_conflict_description2_t *conflict =
504                   APR_ARRAY_IDX(info->wc_info->conflicts, 0,
505                                 const svn_wc_conflict_description2_t *);
506
507             src_left_version =
508                         svn_cl__node_description(conflict->src_left_version,
509                                                  info->repos_root_URL, pool);
510
511             src_right_version =
512                         svn_cl__node_description(conflict->src_right_version,
513                                                  info->repos_root_URL, pool);
514
515             if (src_left_version)
516               SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
517                                          _("Source  left"), /* (1) */
518                                          src_left_version));
519             /* (1): Sneaking in a space in "Source  left" so that
520              * it is the same length as "Source right" while it still
521              * starts in the same column. That's just a tiny tweak in
522              * the English `svn'. */
523
524             if (src_right_version)
525               SVN_ERR(svn_cmdline_printf(pool, "  %s: %s\n",
526                                          _("Source right"),
527                                          src_right_version));
528           }
529         }
530     }
531
532   if (info->lock)
533     {
534       if (info->lock->token)
535         SVN_ERR(svn_cmdline_printf(pool, _("Lock Token: %s\n"),
536                                    info->lock->token));
537
538       if (info->lock->owner)
539         SVN_ERR(svn_cmdline_printf(pool, _("Lock Owner: %s\n"),
540                                    info->lock->owner));
541
542       if (info->lock->creation_date)
543         SVN_ERR(svn_cl__info_print_time(info->lock->creation_date,
544                                         _("Lock Created"), pool));
545
546       if (info->lock->expiration_date)
547         SVN_ERR(svn_cl__info_print_time(info->lock->expiration_date,
548                                         _("Lock Expires"), pool));
549
550       if (info->lock->comment)
551         {
552           int comment_lines;
553           /* NOTE: The stdio will handle newline translation. */
554           comment_lines = svn_cstring_count_newlines(info->lock->comment) + 1;
555           SVN_ERR(svn_cmdline_printf(pool,
556                                      Q_("Lock Comment (%i line):\n%s\n",
557                                         "Lock Comment (%i lines):\n%s\n",
558                                         comment_lines),
559                                      comment_lines,
560                                      info->lock->comment));
561         }
562     }
563
564   if (info->wc_info && info->wc_info->changelist)
565     SVN_ERR(svn_cmdline_printf(pool, _("Changelist: %s\n"),
566                                info->wc_info->changelist));
567
568   /* Print extra newline separator. */
569   return svn_cmdline_printf(pool, "\n");
570 }
571
572
573 /* This implements the `svn_opt_subcommand_t' interface. */
574 svn_error_t *
575 svn_cl__info(apr_getopt_t *os,
576              void *baton,
577              apr_pool_t *pool)
578 {
579   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
580   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
581   apr_array_header_t *targets = NULL;
582   apr_pool_t *subpool = svn_pool_create(pool);
583   int i;
584   svn_error_t *err;
585   svn_boolean_t seen_nonexistent_target = FALSE;
586   svn_opt_revision_t peg_revision;
587   svn_client_info_receiver2_t receiver;
588   const char *path_prefix;
589
590   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
591                                                       opt_state->targets,
592                                                       ctx, FALSE, pool));
593
594   /* Add "." if user passed 0 arguments. */
595   svn_opt_push_implicit_dot_target(targets, pool);
596
597   if (opt_state->xml)
598     {
599       receiver = print_info_xml;
600
601       /* If output is not incremental, output the XML header and wrap
602          everything in a top-level element. This makes the output in
603          its entirety a well-formed XML document. */
604       if (! opt_state->incremental)
605         SVN_ERR(svn_cl__xml_print_header("info", pool));
606     }
607   else
608     {
609       receiver = print_info;
610
611       if (opt_state->incremental)
612         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
613                                 _("'incremental' option only valid in XML "
614                                   "mode"));
615     }
616
617   if (opt_state->depth == svn_depth_unknown)
618     opt_state->depth = svn_depth_empty;
619
620   SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
621
622   for (i = 0; i < targets->nelts; i++)
623     {
624       const char *truepath;
625       const char *target = APR_ARRAY_IDX(targets, i, const char *);
626
627       svn_pool_clear(subpool);
628       SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
629
630       /* Get peg revisions. */
631       SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool));
632
633       /* If no peg-rev was attached to a URL target, then assume HEAD. */
634       if (svn_path_is_url(truepath))
635         {
636           if (peg_revision.kind == svn_opt_revision_unspecified)
637             peg_revision.kind = svn_opt_revision_head;
638         }
639       else
640         {
641           SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
642         }
643
644       err = svn_client_info3(truepath,
645                              &peg_revision, &(opt_state->start_revision),
646                              opt_state->depth, TRUE, TRUE,
647                              opt_state->changelists,
648                              receiver, (void *) path_prefix,
649                              ctx, subpool);
650
651       if (err)
652         {
653           /* If one of the targets is a non-existent URL or wc-entry,
654              don't bail out.  Just warn and move on to the next target. */
655           if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
656               err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
657             {
658               svn_handle_warning2(stderr, err, "svn: ");
659               svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n"));
660             }
661           else
662             {
663               return svn_error_trace(err);
664             }
665
666           svn_error_clear(err);
667           err = NULL;
668           seen_nonexistent_target = TRUE;
669         }
670     }
671   svn_pool_destroy(subpool);
672
673   if (opt_state->xml && (! opt_state->incremental))
674     SVN_ERR(svn_cl__xml_print_footer("info", pool));
675
676   if (seen_nonexistent_target)
677     return svn_error_create(
678       SVN_ERR_ILLEGAL_TARGET, NULL,
679       _("Could not display info for all targets because some "
680         "targets don't exist"));
681   else
682     return SVN_NO_ERROR;
683 }