1251881Speter/* 2251881Speter * propget-cmd.c -- Print properties and values of files/dirs 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_hash.h" 31251881Speter#include "svn_cmdline.h" 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_client.h" 34251881Speter#include "svn_string.h" 35251881Speter#include "svn_error_codes.h" 36251881Speter#include "svn_error.h" 37251881Speter#include "svn_utf.h" 38251881Speter#include "svn_sorts.h" 39251881Speter#include "svn_subst.h" 40251881Speter#include "svn_dirent_uri.h" 41251881Speter#include "svn_path.h" 42251881Speter#include "svn_props.h" 43251881Speter#include "svn_xml.h" 44251881Speter#include "cl.h" 45251881Speter 46251881Speter#include "private/svn_cmdline_private.h" 47251881Speter 48251881Speter#include "svn_private_config.h" 49251881Speter 50251881Speter 51251881Speter/*** Code. ***/ 52251881Speter 53251881Speterstatic svn_error_t * 54251881Speterstream_write(svn_stream_t *out, 55251881Speter const char *data, 56251881Speter apr_size_t len) 57251881Speter{ 58251881Speter apr_size_t write_len = len; 59251881Speter 60251881Speter /* We're gonna bail on an incomplete write here only because we know 61251881Speter that this stream is really stdout, which should never be blocking 62251881Speter on us. */ 63251881Speter SVN_ERR(svn_stream_write(out, data, &write_len)); 64251881Speter if (write_len != len) 65251881Speter return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, 66251881Speter _("Error writing to stream")); 67251881Speter return SVN_NO_ERROR; 68251881Speter} 69251881Speter 70251881Speter 71251881Speterstatic svn_error_t * 72251881Speterprint_properties_xml(const char *pname, 73251881Speter apr_hash_t *props, 74251881Speter apr_array_header_t *inherited_props, 75251881Speter apr_pool_t *pool) 76251881Speter{ 77251881Speter apr_array_header_t *sorted_props; 78251881Speter int i; 79251881Speter apr_pool_t *iterpool = NULL; 80251881Speter svn_stringbuf_t *sb; 81251881Speter 82251881Speter if (inherited_props && inherited_props->nelts) 83251881Speter { 84251881Speter iterpool = svn_pool_create(pool); 85251881Speter 86251881Speter for (i = 0; i < inherited_props->nelts; i++) 87251881Speter { 88251881Speter const char *name_local; 89251881Speter svn_prop_inherited_item_t *iprop = 90251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 91251881Speter svn_string_t *propval = svn__apr_hash_index_val( 92251881Speter apr_hash_first(pool, iprop->prop_hash)); 93251881Speter 94251881Speter sb = NULL; 95251881Speter svn_pool_clear(iterpool); 96251881Speter 97251881Speter if (svn_path_is_url(iprop->path_or_url)) 98251881Speter name_local = iprop->path_or_url; 99251881Speter else 100251881Speter name_local = svn_dirent_local_style(iprop->path_or_url, iterpool); 101251881Speter 102251881Speter svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target", 103251881Speter "path", name_local, NULL); 104251881Speter 105251881Speter svn_cmdline__print_xml_prop(&sb, pname, propval, TRUE, iterpool); 106251881Speter svn_xml_make_close_tag(&sb, iterpool, "target"); 107251881Speter 108251881Speter SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); 109251881Speter } 110251881Speter } 111251881Speter 112251881Speter if (iterpool == NULL) 113251881Speter iterpool = svn_pool_create(iterpool); 114251881Speter 115251881Speter sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool); 116251881Speter for (i = 0; i < sorted_props->nelts; i++) 117251881Speter { 118251881Speter svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); 119251881Speter const char *filename = item.key; 120251881Speter svn_string_t *propval = item.value; 121251881Speter 122251881Speter sb = NULL; 123251881Speter svn_pool_clear(iterpool); 124251881Speter 125251881Speter svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target", 126251881Speter "path", filename, NULL); 127251881Speter svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE, iterpool); 128251881Speter svn_xml_make_close_tag(&sb, iterpool, "target"); 129251881Speter 130251881Speter SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); 131251881Speter } 132251881Speter 133251881Speter if (iterpool) 134251881Speter svn_pool_destroy(iterpool); 135251881Speter 136251881Speter return SVN_NO_ERROR; 137251881Speter} 138251881Speter 139251881Speter/* Print the property PNAME_UTF with the value PROPVAL set on ABSPATH_OR_URL 140251881Speter to the stream OUT. 141251881Speter 142251881Speter If INHERITED_PROPERTY is true then the property described is inherited, 143251881Speter otherwise it is explicit. 144251881Speter 145251881Speter WC_PATH_PREFIX is the absolute path of the current working directory (and 146251881Speter is ignored if ABSPATH_OR_URL is a URL). 147251881Speter 148251881Speter All other arguments are as per print_properties. */ 149251881Speterstatic svn_error_t * 150251881Speterprint_single_prop(svn_string_t *propval, 151251881Speter const char *target_abspath_or_url, 152251881Speter const char *abspath_or_URL, 153251881Speter const char *wc_path_prefix, 154251881Speter svn_stream_t *out, 155251881Speter const char *pname_utf8, 156251881Speter svn_boolean_t print_filenames, 157251881Speter svn_boolean_t omit_newline, 158251881Speter svn_boolean_t like_proplist, 159251881Speter svn_boolean_t inherited_property, 160251881Speter apr_pool_t *scratch_pool) 161251881Speter{ 162251881Speter if (print_filenames) 163251881Speter { 164251881Speter const char *header; 165251881Speter 166251881Speter /* Print the file name. */ 167251881Speter 168251881Speter if (! svn_path_is_url(abspath_or_URL)) 169251881Speter abspath_or_URL = svn_cl__local_style_skip_ancestor(wc_path_prefix, 170251881Speter abspath_or_URL, 171251881Speter scratch_pool); 172251881Speter 173251881Speter /* In verbose mode, print exactly same as "proplist" does; 174251881Speter * otherwise, print a brief header. */ 175251881Speter if (inherited_property) 176251881Speter { 177251881Speter if (like_proplist) 178251881Speter { 179251881Speter if (! svn_path_is_url(target_abspath_or_url)) 180251881Speter target_abspath_or_url = 181251881Speter svn_cl__local_style_skip_ancestor(wc_path_prefix, 182251881Speter target_abspath_or_url, 183251881Speter scratch_pool); 184251881Speter header = apr_psprintf( 185251881Speter scratch_pool, 186251881Speter _("Inherited properties on '%s',\nfrom '%s':\n"), 187251881Speter target_abspath_or_url, abspath_or_URL); 188251881Speter } 189251881Speter else 190251881Speter { 191251881Speter header = apr_psprintf(scratch_pool, "%s - ", abspath_or_URL); 192251881Speter } 193251881Speter } 194251881Speter else 195251881Speter header = apr_psprintf(scratch_pool, like_proplist 196251881Speter ? _("Properties on '%s':\n") 197251881Speter : "%s - ", abspath_or_URL); 198251881Speter SVN_ERR(svn_cmdline_cstring_from_utf8(&header, header, scratch_pool)); 199251881Speter SVN_ERR(svn_subst_translate_cstring2(header, &header, 200251881Speter APR_EOL_STR, /* 'native' eol */ 201251881Speter FALSE, /* no repair */ 202251881Speter NULL, /* no keywords */ 203251881Speter FALSE, /* no expansion */ 204251881Speter scratch_pool)); 205251881Speter SVN_ERR(stream_write(out, header, strlen(header))); 206251881Speter } 207251881Speter 208251881Speter if (like_proplist) 209251881Speter { 210251881Speter /* Print the property name and value just as "proplist -v" does */ 211251881Speter apr_hash_t *hash = apr_hash_make(scratch_pool); 212251881Speter 213251881Speter svn_hash_sets(hash, pname_utf8, propval); 214251881Speter SVN_ERR(svn_cmdline__print_prop_hash(out, hash, FALSE, scratch_pool)); 215251881Speter } 216251881Speter else 217251881Speter { 218251881Speter /* If this is a special Subversion property, it is stored as 219251881Speter UTF8, so convert to the native format. */ 220251881Speter if (svn_prop_needs_translation(pname_utf8)) 221251881Speter SVN_ERR(svn_subst_detranslate_string(&propval, propval, 222251881Speter TRUE, scratch_pool)); 223251881Speter 224251881Speter SVN_ERR(stream_write(out, propval->data, propval->len)); 225251881Speter 226251881Speter if (! omit_newline) 227251881Speter SVN_ERR(stream_write(out, APR_EOL_STR, 228251881Speter strlen(APR_EOL_STR))); 229251881Speter } 230251881Speter return SVN_NO_ERROR; 231251881Speter} 232251881Speter 233251881Speter/* Print the properties in PROPS and/or *INHERITED_PROPS to the stream OUT. 234251881Speter PROPS is a hash mapping (const char *) path to (svn_string_t) property 235251881Speter value. INHERITED_PROPS is a depth-first ordered array of 236251881Speter svn_prop_inherited_item_t * structures. 237251881Speter 238251881Speter TARGET_ABSPATH_OR_URL is the path which inherits INHERITED_PROPS. 239251881Speter 240251881Speter PROPS may be an empty hash, but is never null. INHERITED_PROPS may be 241251881Speter null. 242251881Speter 243251881Speter If IS_URL is true, all paths in PROPS are URLs, else all paths are local 244251881Speter paths. 245251881Speter 246251881Speter PNAME_UTF8 is the property name of all the properties. 247251881Speter 248251881Speter If PRINT_FILENAMES is true, print the item's path before each property. 249251881Speter 250251881Speter If OMIT_NEWLINE is true, don't add a newline at the end of each property. 251251881Speter 252251881Speter If LIKE_PROPLIST is true, print everything in a more verbose format 253251881Speter like "svn proplist -v" does. */ 254251881Speterstatic svn_error_t * 255251881Speterprint_properties(svn_stream_t *out, 256251881Speter const char *target_abspath_or_url, 257251881Speter const char *pname_utf8, 258251881Speter apr_hash_t *props, 259251881Speter apr_array_header_t *inherited_props, 260251881Speter svn_boolean_t print_filenames, 261251881Speter svn_boolean_t omit_newline, 262251881Speter svn_boolean_t like_proplist, 263251881Speter apr_pool_t *pool) 264251881Speter{ 265251881Speter apr_array_header_t *sorted_props; 266251881Speter int i; 267251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 268251881Speter const char *path_prefix; 269251881Speter 270251881Speter SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool)); 271251881Speter 272251881Speter if (inherited_props) 273251881Speter { 274251881Speter svn_pool_clear(iterpool); 275251881Speter 276251881Speter for (i = 0; i < inherited_props->nelts; i++) 277251881Speter { 278251881Speter svn_prop_inherited_item_t *iprop = 279251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 280251881Speter svn_string_t *propval = svn__apr_hash_index_val(apr_hash_first(pool, 281251881Speter iprop->prop_hash)); 282251881Speter SVN_ERR(print_single_prop(propval, target_abspath_or_url, 283251881Speter iprop->path_or_url, 284251881Speter path_prefix, out, pname_utf8, 285251881Speter print_filenames, omit_newline, 286251881Speter like_proplist, TRUE, iterpool)); 287251881Speter } 288251881Speter } 289251881Speter 290251881Speter sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool); 291251881Speter for (i = 0; i < sorted_props->nelts; i++) 292251881Speter { 293251881Speter svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); 294251881Speter const char *filename = item.key; 295251881Speter svn_string_t *propval = item.value; 296251881Speter 297251881Speter svn_pool_clear(iterpool); 298251881Speter 299251881Speter SVN_ERR(print_single_prop(propval, target_abspath_or_url, filename, 300251881Speter path_prefix, out, pname_utf8, print_filenames, 301251881Speter omit_newline, like_proplist, FALSE, 302251881Speter iterpool)); 303251881Speter } 304251881Speter 305251881Speter svn_pool_destroy(iterpool); 306251881Speter 307251881Speter return SVN_NO_ERROR; 308251881Speter} 309251881Speter 310251881Speter 311251881Speter/* This implements the `svn_opt_subcommand_t' interface. */ 312251881Spetersvn_error_t * 313251881Spetersvn_cl__propget(apr_getopt_t *os, 314251881Speter void *baton, 315251881Speter apr_pool_t *pool) 316251881Speter{ 317251881Speter svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 318251881Speter svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 319251881Speter const char *pname, *pname_utf8; 320251881Speter apr_array_header_t *args, *targets; 321251881Speter svn_stream_t *out; 322251881Speter 323251881Speter if (opt_state->verbose && (opt_state->revprop || opt_state->strict 324251881Speter || opt_state->xml)) 325251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 326251881Speter _("--verbose cannot be used with --revprop or " 327251881Speter "--strict or --xml")); 328251881Speter 329251881Speter /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version 330251881Speter thereof) */ 331251881Speter SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool)); 332251881Speter pname = APR_ARRAY_IDX(args, 0, const char *); 333251881Speter SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool)); 334251881Speter if (! svn_prop_name_is_valid(pname_utf8)) 335251881Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 336251881Speter _("'%s' is not a valid Subversion property name"), 337251881Speter pname_utf8); 338251881Speter 339251881Speter SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 340251881Speter opt_state->targets, 341251881Speter ctx, FALSE, pool)); 342251881Speter 343251881Speter /* Add "." if user passed 0 file arguments */ 344251881Speter svn_opt_push_implicit_dot_target(targets, pool); 345251881Speter 346251881Speter /* Open a stream to stdout. */ 347251881Speter SVN_ERR(svn_stream_for_stdout(&out, pool)); 348251881Speter 349251881Speter if (opt_state->revprop) /* operate on a revprop */ 350251881Speter { 351251881Speter svn_revnum_t rev; 352251881Speter const char *URL; 353251881Speter svn_string_t *propval; 354251881Speter 355251881Speter if (opt_state->show_inherited_props) 356251881Speter return svn_error_create( 357251881Speter SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 358251881Speter _("--show-inherited-props can't be used with --revprop")); 359251881Speter 360251881Speter SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets, 361251881Speter &URL, ctx, pool)); 362251881Speter 363251881Speter /* Let libsvn_client do the real work. */ 364251881Speter SVN_ERR(svn_client_revprop_get(pname_utf8, &propval, 365251881Speter URL, &(opt_state->start_revision), 366251881Speter &rev, ctx, pool)); 367251881Speter 368251881Speter if (propval != NULL) 369251881Speter { 370251881Speter if (opt_state->xml) 371251881Speter { 372251881Speter svn_stringbuf_t *sb = NULL; 373251881Speter char *revstr = apr_psprintf(pool, "%ld", rev); 374251881Speter 375251881Speter SVN_ERR(svn_cl__xml_print_header("properties", pool)); 376251881Speter 377251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, 378251881Speter "revprops", 379251881Speter "rev", revstr, NULL); 380251881Speter 381251881Speter svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE, 382251881Speter pool); 383251881Speter 384251881Speter svn_xml_make_close_tag(&sb, pool, "revprops"); 385251881Speter 386251881Speter SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); 387251881Speter SVN_ERR(svn_cl__xml_print_footer("properties", pool)); 388251881Speter } 389251881Speter else 390251881Speter { 391251881Speter svn_string_t *printable_val = propval; 392251881Speter 393251881Speter /* If this is a special Subversion property, it is stored as 394251881Speter UTF8 and LF, so convert to the native locale and eol-style. */ 395251881Speter 396251881Speter if (svn_prop_needs_translation(pname_utf8)) 397251881Speter SVN_ERR(svn_subst_detranslate_string(&printable_val, propval, 398251881Speter TRUE, pool)); 399251881Speter 400251881Speter SVN_ERR(stream_write(out, printable_val->data, 401251881Speter printable_val->len)); 402251881Speter if (! opt_state->strict) 403251881Speter SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR))); 404251881Speter } 405251881Speter } 406251881Speter } 407251881Speter else /* operate on a normal, versioned property (not a revprop) */ 408251881Speter { 409251881Speter apr_pool_t *subpool = svn_pool_create(pool); 410251881Speter int i; 411251881Speter 412251881Speter if (opt_state->xml) 413251881Speter SVN_ERR(svn_cl__xml_print_header("properties", subpool)); 414251881Speter 415251881Speter if (opt_state->depth == svn_depth_unknown) 416251881Speter opt_state->depth = svn_depth_empty; 417251881Speter 418251881Speter /* Strict mode only makes sense for a single target. So make 419251881Speter sure we have only a single target, and that we're not being 420251881Speter asked to recurse on that target. */ 421251881Speter if (opt_state->strict 422251881Speter && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty))) 423251881Speter return svn_error_create 424251881Speter (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 425251881Speter _("Strict output of property values only available for single-" 426251881Speter "target, non-recursive propget operations")); 427251881Speter 428251881Speter for (i = 0; i < targets->nelts; i++) 429251881Speter { 430251881Speter const char *target = APR_ARRAY_IDX(targets, i, const char *); 431251881Speter apr_hash_t *props; 432251881Speter svn_boolean_t print_filenames; 433251881Speter svn_boolean_t omit_newline; 434251881Speter svn_boolean_t like_proplist; 435251881Speter const char *truepath; 436251881Speter svn_opt_revision_t peg_revision; 437251881Speter apr_array_header_t *inherited_props; 438251881Speter 439251881Speter svn_pool_clear(subpool); 440251881Speter SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); 441251881Speter 442251881Speter /* Check for a peg revision. */ 443251881Speter SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, 444251881Speter subpool)); 445251881Speter 446251881Speter if (!svn_path_is_url(truepath)) 447251881Speter SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool)); 448251881Speter 449251881Speter SVN_ERR(svn_client_propget5( 450251881Speter &props, 451251881Speter opt_state->show_inherited_props ? &inherited_props : NULL, 452251881Speter pname_utf8, truepath, 453251881Speter &peg_revision, 454251881Speter &(opt_state->start_revision), 455251881Speter NULL, opt_state->depth, 456251881Speter opt_state->changelists, ctx, subpool, 457251881Speter subpool)); 458251881Speter 459251881Speter /* Any time there is more than one thing to print, or where 460251881Speter the path associated with a printed thing is not obvious, 461251881Speter we'll print filenames. That is, unless we've been told 462251881Speter not to do so with the --strict option. */ 463251881Speter print_filenames = ((opt_state->depth > svn_depth_empty 464251881Speter || targets->nelts > 1 465251881Speter || apr_hash_count(props) > 1 466251881Speter || opt_state->verbose 467251881Speter || opt_state->show_inherited_props) 468251881Speter && (! opt_state->strict)); 469251881Speter omit_newline = opt_state->strict; 470251881Speter like_proplist = opt_state->verbose && !opt_state->strict; 471251881Speter 472251881Speter if (opt_state->xml) 473251881Speter SVN_ERR(print_properties_xml( 474251881Speter pname_utf8, props, 475251881Speter opt_state->show_inherited_props ? inherited_props : NULL, 476251881Speter subpool)); 477251881Speter else 478251881Speter SVN_ERR(print_properties( 479251881Speter out, truepath, pname_utf8, 480251881Speter props, 481251881Speter opt_state->show_inherited_props ? inherited_props : NULL, 482251881Speter print_filenames, 483251881Speter omit_newline, like_proplist, subpool)); 484251881Speter } 485251881Speter 486251881Speter if (opt_state->xml) 487251881Speter SVN_ERR(svn_cl__xml_print_footer("properties", subpool)); 488251881Speter 489251881Speter svn_pool_destroy(subpool); 490251881Speter } 491251881Speter 492251881Speter return SVN_NO_ERROR; 493251881Speter} 494