2 * propget-cmd.c -- Print properties and values of files/dirs
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
31 #include "svn_cmdline.h"
32 #include "svn_pools.h"
33 #include "svn_client.h"
34 #include "svn_string.h"
35 #include "svn_error_codes.h"
36 #include "svn_error.h"
38 #include "svn_sorts.h"
39 #include "svn_subst.h"
40 #include "svn_dirent_uri.h"
42 #include "svn_props.h"
46 #include "private/svn_cmdline_private.h"
47 #include "private/svn_opt_private.h"
48 #include "private/svn_sorts_private.h"
49 #include "svn_private_config.h"
55 stream_write(svn_stream_t *out,
59 apr_size_t write_len = len;
61 /* We're gonna bail on an incomplete write here only because we know
62 that this stream is really stdout, which should never be blocking
64 SVN_ERR(svn_stream_write(out, data, &write_len));
66 return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
67 _("Error writing to stream"));
73 print_properties_xml(const char *pname,
75 apr_array_header_t *inherited_props,
78 apr_array_header_t *sorted_props;
80 apr_pool_t *iterpool = NULL;
83 if (inherited_props && inherited_props->nelts)
85 iterpool = svn_pool_create(pool);
87 for (i = 0; i < inherited_props->nelts; i++)
89 const char *name_local;
90 svn_prop_inherited_item_t *iprop =
91 APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
92 svn_string_t *propval = apr_hash_this_val(
93 apr_hash_first(pool, iprop->prop_hash));
96 svn_pool_clear(iterpool);
98 if (svn_path_is_url(iprop->path_or_url))
99 name_local = iprop->path_or_url;
101 name_local = svn_dirent_local_style(iprop->path_or_url, iterpool);
103 svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
104 "path", name_local, SVN_VA_NULL);
106 svn_cmdline__print_xml_prop(&sb, pname, propval, TRUE, iterpool);
107 svn_xml_make_close_tag(&sb, iterpool, "target");
109 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
113 if (iterpool == NULL)
114 iterpool = svn_pool_create(iterpool);
116 sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
117 for (i = 0; i < sorted_props->nelts; i++)
119 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
120 const char *filename = item.key;
121 svn_string_t *propval = item.value;
124 svn_pool_clear(iterpool);
126 svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
127 "path", filename, SVN_VA_NULL);
128 svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE, iterpool);
129 svn_xml_make_close_tag(&sb, iterpool, "target");
131 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
135 svn_pool_destroy(iterpool);
140 /* Print the property PNAME_UTF with the value PROPVAL set on ABSPATH_OR_URL
143 If INHERITED_PROPERTY is true then the property described is inherited,
144 otherwise it is explicit.
146 WC_PATH_PREFIX is the absolute path of the current working directory (and
147 is ignored if ABSPATH_OR_URL is a URL).
149 All other arguments are as per print_properties. */
151 print_single_prop(svn_string_t *propval,
152 const char *target_abspath_or_url,
153 const char *abspath_or_URL,
154 const char *wc_path_prefix,
156 const char *pname_utf8,
157 svn_boolean_t print_filenames,
158 svn_boolean_t omit_newline,
159 svn_boolean_t like_proplist,
160 svn_boolean_t inherited_property,
161 apr_pool_t *scratch_pool)
167 /* Print the file name. */
169 if (! svn_path_is_url(abspath_or_URL))
170 abspath_or_URL = svn_cl__local_style_skip_ancestor(wc_path_prefix,
174 /* In verbose mode, print exactly same as "proplist" does;
175 * otherwise, print a brief header. */
176 if (inherited_property)
180 if (! svn_path_is_url(target_abspath_or_url))
181 target_abspath_or_url =
182 svn_cl__local_style_skip_ancestor(wc_path_prefix,
183 target_abspath_or_url,
185 header = apr_psprintf(
187 _("Inherited properties on '%s',\nfrom '%s':\n"),
188 target_abspath_or_url, abspath_or_URL);
192 header = apr_psprintf(scratch_pool, "%s - ", abspath_or_URL);
196 header = apr_psprintf(scratch_pool, like_proplist
197 ? _("Properties on '%s':\n")
198 : "%s - ", abspath_or_URL);
199 SVN_ERR(svn_cmdline_cstring_from_utf8(&header, header, scratch_pool));
200 SVN_ERR(svn_subst_translate_cstring2(header, &header,
201 APR_EOL_STR, /* 'native' eol */
202 FALSE, /* no repair */
203 NULL, /* no keywords */
204 FALSE, /* no expansion */
206 SVN_ERR(stream_write(out, header, strlen(header)));
211 /* Print the property name and value just as "proplist -v" does */
212 apr_hash_t *hash = apr_hash_make(scratch_pool);
214 svn_hash_sets(hash, pname_utf8, propval);
215 SVN_ERR(svn_cmdline__print_prop_hash(out, hash, FALSE, scratch_pool));
219 /* If this is a special Subversion property, it is stored as
220 UTF8, so convert to the native format. */
221 if (svn_prop_needs_translation(pname_utf8))
222 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
223 TRUE, scratch_pool));
225 SVN_ERR(stream_write(out, propval->data, propval->len));
228 SVN_ERR(stream_write(out, APR_EOL_STR,
229 strlen(APR_EOL_STR)));
234 /* Print the properties in PROPS and/or *INHERITED_PROPS to the stream OUT.
235 PROPS is a hash mapping (const char *) path to (svn_string_t) property
236 value. INHERITED_PROPS is a depth-first ordered array of
237 svn_prop_inherited_item_t * structures.
239 TARGET_ABSPATH_OR_URL is the path which inherits INHERITED_PROPS.
241 PROPS may be an empty hash, but is never null. INHERITED_PROPS may be
244 If IS_URL is true, all paths in PROPS are URLs, else all paths are local
247 PNAME_UTF8 is the property name of all the properties.
249 If PRINT_FILENAMES is true, print the item's path before each property.
251 If OMIT_NEWLINE is true, don't add a newline at the end of each property.
253 If LIKE_PROPLIST is true, print everything in a more verbose format
254 like "svn proplist -v" does. */
256 print_properties(svn_stream_t *out,
257 const char *target_abspath_or_url,
258 const char *pname_utf8,
260 apr_array_header_t *inherited_props,
261 svn_boolean_t print_filenames,
262 svn_boolean_t omit_newline,
263 svn_boolean_t like_proplist,
266 apr_array_header_t *sorted_props;
268 apr_pool_t *iterpool = svn_pool_create(pool);
269 const char *path_prefix;
271 SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
275 svn_pool_clear(iterpool);
277 for (i = 0; i < inherited_props->nelts; i++)
279 svn_prop_inherited_item_t *iprop =
280 APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
281 svn_string_t *propval = apr_hash_this_val(apr_hash_first(pool,
283 SVN_ERR(print_single_prop(propval, target_abspath_or_url,
285 path_prefix, out, pname_utf8,
286 print_filenames, omit_newline,
287 like_proplist, TRUE, iterpool));
291 sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
292 for (i = 0; i < sorted_props->nelts; i++)
294 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
295 const char *filename = item.key;
296 svn_string_t *propval = item.value;
298 svn_pool_clear(iterpool);
300 SVN_ERR(print_single_prop(propval, target_abspath_or_url, filename,
301 path_prefix, out, pname_utf8, print_filenames,
302 omit_newline, like_proplist, FALSE,
306 svn_pool_destroy(iterpool);
312 /* This implements the `svn_opt_subcommand_t' interface. */
314 svn_cl__propget(apr_getopt_t *os,
318 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
319 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
320 const char *pname, *pname_utf8;
321 apr_array_header_t *args, *targets;
323 svn_boolean_t warned = FALSE;
325 if (opt_state->verbose && (opt_state->revprop || opt_state->no_newline
327 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
328 _("--verbose cannot be used with --revprop or "
329 "--no-newline or --xml"));
331 /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version
333 SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
334 pname = APR_ARRAY_IDX(args, 0, const char *);
335 SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
336 if (! svn_prop_name_is_valid(pname_utf8))
337 return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
338 _("'%s' is not a valid Subversion property name"),
341 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
345 /* Add "." if user passed 0 file arguments */
346 svn_opt_push_implicit_dot_target(targets, pool);
348 /* Open a stream to stdout. */
349 SVN_ERR(svn_stream_for_stdout(&out, pool));
351 if (opt_state->revprop) /* operate on a revprop */
355 svn_string_t *propval;
357 if (opt_state->show_inherited_props)
358 return svn_error_create(
359 SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
360 _("--show-inherited-props can't be used with --revprop"));
362 SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
365 /* Let libsvn_client do the real work. */
366 SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
367 URL, &(opt_state->start_revision),
372 return svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
373 _("Property '%s' not found on "
376 svn_opt__revision_to_string(
377 &opt_state->start_revision,
384 svn_stringbuf_t *sb = NULL;
385 char *revstr = apr_psprintf(pool, "%ld", rev);
387 SVN_ERR(svn_cl__xml_print_header("properties", pool));
389 svn_xml_make_open_tag(&sb, pool, svn_xml_normal,
391 "rev", revstr, SVN_VA_NULL);
393 svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE,
396 svn_xml_make_close_tag(&sb, pool, "revprops");
398 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
399 SVN_ERR(svn_cl__xml_print_footer("properties", pool));
403 svn_string_t *printable_val = propval;
405 /* If this is a special Subversion property, it is stored as
406 UTF8 and LF, so convert to the native locale and eol-style. */
408 if (svn_prop_needs_translation(pname_utf8))
409 SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
412 SVN_ERR(stream_write(out, printable_val->data,
413 printable_val->len));
414 if (! opt_state->no_newline)
415 SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR)));
419 else /* operate on a normal, versioned property (not a revprop) */
421 apr_pool_t *subpool = svn_pool_create(pool);
425 SVN_ERR(svn_cl__xml_print_header("properties", subpool));
427 if (opt_state->depth == svn_depth_unknown)
428 opt_state->depth = svn_depth_empty;
430 /* No-newline mode only makes sense for a single target. So make
431 sure we have only a single target, and that we're not being
432 asked to recurse on that target. */
433 if (opt_state->no_newline
434 && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty)
435 || (opt_state->show_inherited_props)))
436 return svn_error_create
437 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
438 _("--no-newline is only available for single-target,"
439 " non-recursive propget operations"));
441 for (i = 0; i < targets->nelts; i++)
443 const char *target = APR_ARRAY_IDX(targets, i, const char *);
445 svn_boolean_t print_filenames;
446 svn_boolean_t omit_newline;
447 svn_boolean_t like_proplist;
448 const char *truepath;
449 svn_opt_revision_t peg_revision;
450 apr_array_header_t *inherited_props;
452 svn_pool_clear(subpool);
453 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
455 /* Check for a peg revision. */
456 SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
459 if (!svn_path_is_url(truepath))
460 SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
462 SVN_ERR(svn_client_propget5(
464 opt_state->show_inherited_props ? &inherited_props : NULL,
465 pname_utf8, truepath,
467 &(opt_state->start_revision),
468 NULL, opt_state->depth,
469 opt_state->changelists, ctx, subpool,
472 /* Any time there is more than one thing to print, or where
473 the path associated with a printed thing is not obvious,
474 we'll print filenames. That is, unless we've been told
475 not to do so with the --no-newline option. */
476 print_filenames = ((opt_state->depth > svn_depth_empty
477 || targets->nelts > 1
478 || apr_hash_count(props) > 1
479 || opt_state->verbose
480 || opt_state->show_inherited_props)
481 && (! opt_state->no_newline));
482 omit_newline = opt_state->no_newline;
483 like_proplist = opt_state->verbose && !opt_state->no_newline;
485 /* If there are no properties, and exactly one node was queried,
487 if (opt_state->depth == svn_depth_empty
488 && !opt_state->show_inherited_props
489 && apr_hash_count(props) == 0)
492 err = svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
493 _("Property '%s' not found on '%s'"),
495 svn_handle_warning2(stderr, err, "svn: ");
496 svn_error_clear(err);
501 SVN_ERR(print_properties_xml(
503 opt_state->show_inherited_props ? inherited_props : NULL,
506 SVN_ERR(print_properties(
507 out, truepath, pname_utf8,
509 opt_state->show_inherited_props ? inherited_props : NULL,
511 omit_newline, like_proplist, subpool));
515 SVN_ERR(svn_cl__xml_print_footer("properties", subpool));
517 svn_pool_destroy(subpool);
521 return svn_error_create(SVN_ERR_BASE, NULL, NULL);