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