]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/svn/propget-cmd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / svn / propget-cmd.c
1 /*
2  * propget-cmd.c -- Print properties and values of files/dirs
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_hash.h"
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"
37 #include "svn_utf.h"
38 #include "svn_sorts.h"
39 #include "svn_subst.h"
40 #include "svn_dirent_uri.h"
41 #include "svn_path.h"
42 #include "svn_props.h"
43 #include "svn_xml.h"
44 #include "cl.h"
45
46 #include "private/svn_cmdline_private.h"
47
48 #include "svn_private_config.h"
49
50 \f
51 /*** Code. ***/
52
53 static svn_error_t *
54 stream_write(svn_stream_t *out,
55              const char *data,
56              apr_size_t len)
57 {
58   apr_size_t write_len = len;
59
60   /* We're gonna bail on an incomplete write here only because we know
61      that this stream is really stdout, which should never be blocking
62      on us. */
63   SVN_ERR(svn_stream_write(out, data, &write_len));
64   if (write_len != len)
65     return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
66                             _("Error writing to stream"));
67   return SVN_NO_ERROR;
68 }
69
70
71 static svn_error_t *
72 print_properties_xml(const char *pname,
73                      apr_hash_t *props,
74                      apr_array_header_t *inherited_props,
75                      apr_pool_t *pool)
76 {
77   apr_array_header_t *sorted_props;
78   int i;
79   apr_pool_t *iterpool = NULL;
80   svn_stringbuf_t *sb;
81
82   if (inherited_props && inherited_props->nelts)
83     {
84       iterpool = svn_pool_create(pool);
85
86       for (i = 0; i < inherited_props->nelts; i++)
87         {
88           const char *name_local;
89           svn_prop_inherited_item_t *iprop =
90            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
91           svn_string_t *propval = svn__apr_hash_index_val(
92             apr_hash_first(pool, iprop->prop_hash));
93
94           sb = NULL;
95           svn_pool_clear(iterpool);
96
97           if (svn_path_is_url(iprop->path_or_url))
98             name_local = iprop->path_or_url;
99           else
100             name_local = svn_dirent_local_style(iprop->path_or_url, iterpool);
101
102           svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
103                             "path", name_local, NULL);
104
105           svn_cmdline__print_xml_prop(&sb, pname, propval, TRUE, iterpool);
106           svn_xml_make_close_tag(&sb, iterpool, "target");
107
108           SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
109         }
110     }
111
112   if (iterpool == NULL)
113     iterpool = svn_pool_create(iterpool);
114
115   sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
116   for (i = 0; i < sorted_props->nelts; i++)
117     {
118       svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
119       const char *filename = item.key;
120       svn_string_t *propval = item.value;
121
122       sb = NULL;
123       svn_pool_clear(iterpool);
124
125       svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
126                         "path", filename, NULL);
127       svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE, iterpool);
128       svn_xml_make_close_tag(&sb, iterpool, "target");
129
130       SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
131     }
132
133   if (iterpool)
134     svn_pool_destroy(iterpool);
135
136   return SVN_NO_ERROR;
137 }
138
139 /* Print the property PNAME_UTF with the value PROPVAL set on ABSPATH_OR_URL
140    to the stream OUT.
141
142    If INHERITED_PROPERTY is true then the property described is inherited,
143    otherwise it is explicit.
144
145    WC_PATH_PREFIX is the absolute path of the current working directory (and
146    is ignored if ABSPATH_OR_URL is a URL).
147
148    All other arguments are as per print_properties. */
149 static svn_error_t *
150 print_single_prop(svn_string_t *propval,
151                   const char *target_abspath_or_url,
152                   const char *abspath_or_URL,
153                   const char *wc_path_prefix,
154                   svn_stream_t *out,
155                   const char *pname_utf8,
156                   svn_boolean_t print_filenames,
157                   svn_boolean_t omit_newline,
158                   svn_boolean_t like_proplist,
159                   svn_boolean_t inherited_property,
160                   apr_pool_t *scratch_pool)
161 {
162   if (print_filenames)
163     {
164       const char *header;
165
166       /* Print the file name. */
167
168       if (! svn_path_is_url(abspath_or_URL))
169         abspath_or_URL = svn_cl__local_style_skip_ancestor(wc_path_prefix,
170                                                            abspath_or_URL,
171                                                            scratch_pool);
172
173       /* In verbose mode, print exactly same as "proplist" does;
174        * otherwise, print a brief header. */
175       if (inherited_property)
176         {
177           if (like_proplist)
178             {
179               if (! svn_path_is_url(target_abspath_or_url))
180                 target_abspath_or_url =
181                   svn_cl__local_style_skip_ancestor(wc_path_prefix,
182                                                     target_abspath_or_url,
183                                                     scratch_pool);
184               header = apr_psprintf(
185                 scratch_pool,
186                 _("Inherited properties on '%s',\nfrom '%s':\n"),
187                 target_abspath_or_url, abspath_or_URL);
188             }
189           else
190             {
191               header = apr_psprintf(scratch_pool, "%s - ", abspath_or_URL);
192             }
193         }
194       else
195         header = apr_psprintf(scratch_pool, like_proplist
196                               ? _("Properties on '%s':\n")
197                               : "%s - ", abspath_or_URL);
198       SVN_ERR(svn_cmdline_cstring_from_utf8(&header, header, scratch_pool));
199       SVN_ERR(svn_subst_translate_cstring2(header, &header,
200                                            APR_EOL_STR,  /* 'native' eol */
201                                            FALSE, /* no repair */
202                                            NULL,  /* no keywords */
203                                            FALSE, /* no expansion */
204                                            scratch_pool));
205       SVN_ERR(stream_write(out, header, strlen(header)));
206     }
207
208   if (like_proplist)
209     {
210       /* Print the property name and value just as "proplist -v" does */
211       apr_hash_t *hash = apr_hash_make(scratch_pool);
212
213       svn_hash_sets(hash, pname_utf8, propval);
214       SVN_ERR(svn_cmdline__print_prop_hash(out, hash, FALSE, scratch_pool));
215     }
216   else
217     {
218       /* If this is a special Subversion property, it is stored as
219          UTF8, so convert to the native format. */
220       if (svn_prop_needs_translation(pname_utf8))
221         SVN_ERR(svn_subst_detranslate_string(&propval, propval,
222                                              TRUE, scratch_pool));
223
224       SVN_ERR(stream_write(out, propval->data, propval->len));
225
226       if (! omit_newline)
227         SVN_ERR(stream_write(out, APR_EOL_STR,
228                              strlen(APR_EOL_STR)));
229     }
230   return SVN_NO_ERROR;
231 }
232
233 /* Print the properties in PROPS and/or *INHERITED_PROPS to the stream OUT.
234    PROPS is a hash mapping (const char *) path to (svn_string_t) property
235    value.  INHERITED_PROPS is a depth-first ordered array of
236    svn_prop_inherited_item_t * structures.
237
238    TARGET_ABSPATH_OR_URL is the path which inherits INHERITED_PROPS.
239
240    PROPS may be an empty hash, but is never null.  INHERITED_PROPS may be
241    null.
242
243    If IS_URL is true, all paths in PROPS are URLs, else all paths are local
244    paths.
245
246    PNAME_UTF8 is the property name of all the properties.
247
248    If PRINT_FILENAMES is true, print the item's path before each property.
249
250    If OMIT_NEWLINE is true, don't add a newline at the end of each property.
251
252    If LIKE_PROPLIST is true, print everything in a more verbose format
253    like "svn proplist -v" does. */
254 static svn_error_t *
255 print_properties(svn_stream_t *out,
256                  const char *target_abspath_or_url,
257                  const char *pname_utf8,
258                  apr_hash_t *props,
259                  apr_array_header_t *inherited_props,
260                  svn_boolean_t print_filenames,
261                  svn_boolean_t omit_newline,
262                  svn_boolean_t like_proplist,
263                  apr_pool_t *pool)
264 {
265   apr_array_header_t *sorted_props;
266   int i;
267   apr_pool_t *iterpool = svn_pool_create(pool);
268   const char *path_prefix;
269
270   SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
271
272   if (inherited_props)
273     {
274       svn_pool_clear(iterpool);
275
276       for (i = 0; i < inherited_props->nelts; i++)
277         {
278           svn_prop_inherited_item_t *iprop =
279             APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
280           svn_string_t *propval = svn__apr_hash_index_val(apr_hash_first(pool,
281                                                           iprop->prop_hash));
282           SVN_ERR(print_single_prop(propval, target_abspath_or_url,
283                                     iprop->path_or_url,
284                                     path_prefix, out, pname_utf8,
285                                     print_filenames, omit_newline,
286                                     like_proplist, TRUE, iterpool));
287         }
288     }
289
290   sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
291   for (i = 0; i < sorted_props->nelts; i++)
292     {
293       svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
294       const char *filename = item.key;
295       svn_string_t *propval = item.value;
296
297       svn_pool_clear(iterpool);
298
299       SVN_ERR(print_single_prop(propval, target_abspath_or_url, filename,
300                                 path_prefix, out, pname_utf8, print_filenames,
301                                 omit_newline, like_proplist, FALSE,
302                                 iterpool));
303     }
304
305   svn_pool_destroy(iterpool);
306
307   return SVN_NO_ERROR;
308 }
309
310
311 /* This implements the `svn_opt_subcommand_t' interface. */
312 svn_error_t *
313 svn_cl__propget(apr_getopt_t *os,
314                 void *baton,
315                 apr_pool_t *pool)
316 {
317   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
318   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
319   const char *pname, *pname_utf8;
320   apr_array_header_t *args, *targets;
321   svn_stream_t *out;
322
323   if (opt_state->verbose && (opt_state->revprop || opt_state->strict
324                              || opt_state->xml))
325     return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
326                             _("--verbose cannot be used with --revprop or "
327                               "--strict or --xml"));
328
329   /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version
330      thereof) */
331   SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
332   pname = APR_ARRAY_IDX(args, 0, const char *);
333   SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
334   if (! svn_prop_name_is_valid(pname_utf8))
335     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
336                              _("'%s' is not a valid Subversion property name"),
337                              pname_utf8);
338
339   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
340                                                       opt_state->targets,
341                                                       ctx, FALSE, pool));
342
343   /* Add "." if user passed 0 file arguments */
344   svn_opt_push_implicit_dot_target(targets, pool);
345
346   /* Open a stream to stdout. */
347   SVN_ERR(svn_stream_for_stdout(&out, pool));
348
349   if (opt_state->revprop)  /* operate on a revprop */
350     {
351       svn_revnum_t rev;
352       const char *URL;
353       svn_string_t *propval;
354
355       if (opt_state->show_inherited_props)
356         return svn_error_create(
357           SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
358           _("--show-inherited-props can't be used with --revprop"));
359
360       SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
361                                       &URL, ctx, pool));
362
363       /* Let libsvn_client do the real work. */
364       SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
365                                      URL, &(opt_state->start_revision),
366                                      &rev, ctx, pool));
367
368       if (propval != NULL)
369         {
370           if (opt_state->xml)
371             {
372               svn_stringbuf_t *sb = NULL;
373               char *revstr = apr_psprintf(pool, "%ld", rev);
374
375               SVN_ERR(svn_cl__xml_print_header("properties", pool));
376
377               svn_xml_make_open_tag(&sb, pool, svn_xml_normal,
378                                     "revprops",
379                                     "rev", revstr, NULL);
380
381               svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE,
382                                           pool);
383
384               svn_xml_make_close_tag(&sb, pool, "revprops");
385
386               SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
387               SVN_ERR(svn_cl__xml_print_footer("properties", pool));
388             }
389           else
390             {
391               svn_string_t *printable_val = propval;
392
393               /* If this is a special Subversion property, it is stored as
394                  UTF8 and LF, so convert to the native locale and eol-style. */
395
396               if (svn_prop_needs_translation(pname_utf8))
397                 SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
398                                                      TRUE, pool));
399
400               SVN_ERR(stream_write(out, printable_val->data,
401                                    printable_val->len));
402               if (! opt_state->strict)
403                 SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR)));
404             }
405         }
406     }
407   else  /* operate on a normal, versioned property (not a revprop) */
408     {
409       apr_pool_t *subpool = svn_pool_create(pool);
410       int i;
411
412       if (opt_state->xml)
413         SVN_ERR(svn_cl__xml_print_header("properties", subpool));
414
415       if (opt_state->depth == svn_depth_unknown)
416         opt_state->depth = svn_depth_empty;
417
418       /* Strict mode only makes sense for a single target.  So make
419          sure we have only a single target, and that we're not being
420          asked to recurse on that target. */
421       if (opt_state->strict
422           && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty)))
423         return svn_error_create
424           (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
425            _("Strict output of property values only available for single-"
426              "target, non-recursive propget operations"));
427
428       for (i = 0; i < targets->nelts; i++)
429         {
430           const char *target = APR_ARRAY_IDX(targets, i, const char *);
431           apr_hash_t *props;
432           svn_boolean_t print_filenames;
433           svn_boolean_t omit_newline;
434           svn_boolean_t like_proplist;
435           const char *truepath;
436           svn_opt_revision_t peg_revision;
437           apr_array_header_t *inherited_props;
438
439           svn_pool_clear(subpool);
440           SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
441
442           /* Check for a peg revision. */
443           SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
444                                      subpool));
445
446           if (!svn_path_is_url(truepath))
447             SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
448
449           SVN_ERR(svn_client_propget5(
450             &props,
451             opt_state->show_inherited_props ? &inherited_props : NULL,
452             pname_utf8, truepath,
453             &peg_revision,
454             &(opt_state->start_revision),
455             NULL, opt_state->depth,
456             opt_state->changelists, ctx, subpool,
457             subpool));
458
459           /* Any time there is more than one thing to print, or where
460              the path associated with a printed thing is not obvious,
461              we'll print filenames.  That is, unless we've been told
462              not to do so with the --strict option. */
463           print_filenames = ((opt_state->depth > svn_depth_empty
464                               || targets->nelts > 1
465                               || apr_hash_count(props) > 1
466                               || opt_state->verbose
467                               || opt_state->show_inherited_props)
468                              && (! opt_state->strict));
469           omit_newline = opt_state->strict;
470           like_proplist = opt_state->verbose && !opt_state->strict;
471
472           if (opt_state->xml)
473             SVN_ERR(print_properties_xml(
474               pname_utf8, props,
475               opt_state->show_inherited_props ? inherited_props : NULL,
476               subpool));
477           else
478             SVN_ERR(print_properties(
479               out, truepath, pname_utf8,
480               props,
481               opt_state->show_inherited_props ? inherited_props : NULL,
482               print_filenames,
483               omit_newline, like_proplist, subpool));
484         }
485
486       if (opt_state->xml)
487         SVN_ERR(svn_cl__xml_print_footer("properties", subpool));
488
489       svn_pool_destroy(subpool);
490     }
491
492   return SVN_NO_ERROR;
493 }