propget-cmd.c revision 251881
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