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