]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svn/propget-cmd.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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 #include "private/svn_opt_private.h"
48 #include "private/svn_sorts_private.h"
49 #include "svn_private_config.h"
50
51 \f
52 /*** Code. ***/
53
54 static svn_error_t *
55 stream_write(svn_stream_t *out,
56              const char *data,
57              apr_size_t len)
58 {
59   apr_size_t write_len = len;
60
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
63      on us. */
64   SVN_ERR(svn_stream_write(out, data, &write_len));
65   if (write_len != len)
66     return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
67                             _("Error writing to stream"));
68   return SVN_NO_ERROR;
69 }
70
71
72 static svn_error_t *
73 print_properties_xml(const char *pname,
74                      apr_hash_t *props,
75                      apr_array_header_t *inherited_props,
76                      apr_pool_t *pool)
77 {
78   apr_array_header_t *sorted_props;
79   int i;
80   apr_pool_t *iterpool = NULL;
81   svn_stringbuf_t *sb;
82
83   if (inherited_props && inherited_props->nelts)
84     {
85       iterpool = svn_pool_create(pool);
86
87       for (i = 0; i < inherited_props->nelts; i++)
88         {
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));
94
95           sb = NULL;
96           svn_pool_clear(iterpool);
97
98           if (svn_path_is_url(iprop->path_or_url))
99             name_local = iprop->path_or_url;
100           else
101             name_local = svn_dirent_local_style(iprop->path_or_url, iterpool);
102
103           svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
104                             "path", name_local, SVN_VA_NULL);
105
106           svn_cmdline__print_xml_prop(&sb, pname, propval, TRUE, iterpool);
107           svn_xml_make_close_tag(&sb, iterpool, "target");
108
109           SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
110         }
111     }
112
113   if (iterpool == NULL)
114     iterpool = svn_pool_create(iterpool);
115
116   sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
117   for (i = 0; i < sorted_props->nelts; i++)
118     {
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;
122
123       sb = NULL;
124       svn_pool_clear(iterpool);
125
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");
130
131       SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
132     }
133
134   if (iterpool)
135     svn_pool_destroy(iterpool);
136
137   return SVN_NO_ERROR;
138 }
139
140 /* Print the property PNAME_UTF with the value PROPVAL set on ABSPATH_OR_URL
141    to the stream OUT.
142
143    If INHERITED_PROPERTY is true then the property described is inherited,
144    otherwise it is explicit.
145
146    WC_PATH_PREFIX is the absolute path of the current working directory (and
147    is ignored if ABSPATH_OR_URL is a URL).
148
149    All other arguments are as per print_properties. */
150 static svn_error_t *
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,
155                   svn_stream_t *out,
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)
162 {
163   if (print_filenames)
164     {
165       const char *header;
166
167       /* Print the file name. */
168
169       if (! svn_path_is_url(abspath_or_URL))
170         abspath_or_URL = svn_cl__local_style_skip_ancestor(wc_path_prefix,
171                                                            abspath_or_URL,
172                                                            scratch_pool);
173
174       /* In verbose mode, print exactly same as "proplist" does;
175        * otherwise, print a brief header. */
176       if (inherited_property)
177         {
178           if (like_proplist)
179             {
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,
184                                                     scratch_pool);
185               header = apr_psprintf(
186                 scratch_pool,
187                 _("Inherited properties on '%s',\nfrom '%s':\n"),
188                 target_abspath_or_url, abspath_or_URL);
189             }
190           else
191             {
192               header = apr_psprintf(scratch_pool, "%s - ", abspath_or_URL);
193             }
194         }
195       else
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 */
205                                            scratch_pool));
206       SVN_ERR(stream_write(out, header, strlen(header)));
207     }
208
209   if (like_proplist)
210     {
211       /* Print the property name and value just as "proplist -v" does */
212       apr_hash_t *hash = apr_hash_make(scratch_pool);
213
214       svn_hash_sets(hash, pname_utf8, propval);
215       SVN_ERR(svn_cmdline__print_prop_hash(out, hash, FALSE, scratch_pool));
216     }
217   else
218     {
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));
224
225       SVN_ERR(stream_write(out, propval->data, propval->len));
226
227       if (! omit_newline)
228         SVN_ERR(stream_write(out, APR_EOL_STR,
229                              strlen(APR_EOL_STR)));
230     }
231   return SVN_NO_ERROR;
232 }
233
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.
238
239    TARGET_ABSPATH_OR_URL is the path which inherits INHERITED_PROPS.
240
241    PROPS may be an empty hash, but is never null.  INHERITED_PROPS may be
242    null.
243
244    If IS_URL is true, all paths in PROPS are URLs, else all paths are local
245    paths.
246
247    PNAME_UTF8 is the property name of all the properties.
248
249    If PRINT_FILENAMES is true, print the item's path before each property.
250
251    If OMIT_NEWLINE is true, don't add a newline at the end of each property.
252
253    If LIKE_PROPLIST is true, print everything in a more verbose format
254    like "svn proplist -v" does. */
255 static svn_error_t *
256 print_properties(svn_stream_t *out,
257                  const char *target_abspath_or_url,
258                  const char *pname_utf8,
259                  apr_hash_t *props,
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,
264                  apr_pool_t *pool)
265 {
266   apr_array_header_t *sorted_props;
267   int i;
268   apr_pool_t *iterpool = svn_pool_create(pool);
269   const char *path_prefix;
270
271   SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
272
273   if (inherited_props)
274     {
275       svn_pool_clear(iterpool);
276
277       for (i = 0; i < inherited_props->nelts; i++)
278         {
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,
282                                                           iprop->prop_hash));
283           SVN_ERR(print_single_prop(propval, target_abspath_or_url,
284                                     iprop->path_or_url,
285                                     path_prefix, out, pname_utf8,
286                                     print_filenames, omit_newline,
287                                     like_proplist, TRUE, iterpool));
288         }
289     }
290
291   sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
292   for (i = 0; i < sorted_props->nelts; i++)
293     {
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;
297
298       svn_pool_clear(iterpool);
299
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,
303                                 iterpool));
304     }
305
306   svn_pool_destroy(iterpool);
307
308   return SVN_NO_ERROR;
309 }
310
311
312 /* This implements the `svn_opt_subcommand_t' interface. */
313 svn_error_t *
314 svn_cl__propget(apr_getopt_t *os,
315                 void *baton,
316                 apr_pool_t *pool)
317 {
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;
322   svn_stream_t *out;
323   svn_boolean_t warned = FALSE;
324
325   if (opt_state->verbose && (opt_state->revprop || opt_state->no_newline
326                              || opt_state->xml))
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"));
330
331   /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version
332      thereof) */
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"),
339                              pname_utf8);
340
341   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
342                                                       opt_state->targets,
343                                                       ctx, FALSE, pool));
344
345   /* Add "." if user passed 0 file arguments */
346   svn_opt_push_implicit_dot_target(targets, pool);
347
348   /* Open a stream to stdout. */
349   SVN_ERR(svn_stream_for_stdout(&out, pool));
350
351   if (opt_state->revprop)  /* operate on a revprop */
352     {
353       svn_revnum_t rev;
354       const char *URL;
355       svn_string_t *propval;
356
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"));
361
362       SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
363                                       &URL, ctx, pool));
364
365       /* Let libsvn_client do the real work. */
366       SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
367                                      URL, &(opt_state->start_revision),
368                                      &rev, ctx, pool));
369
370       if (propval == NULL)
371         {
372           return svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
373                                    _("Property '%s' not found on "
374                                      "revision %s"),
375                                    pname_utf8,
376                                    svn_opt__revision_to_string(
377                                      &opt_state->start_revision,
378                                      pool));
379         }
380       else
381         {
382           if (opt_state->xml)
383             {
384               svn_stringbuf_t *sb = NULL;
385               char *revstr = apr_psprintf(pool, "%ld", rev);
386
387               SVN_ERR(svn_cl__xml_print_header("properties", pool));
388
389               svn_xml_make_open_tag(&sb, pool, svn_xml_normal,
390                                     "revprops",
391                                     "rev", revstr, SVN_VA_NULL);
392
393               svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE,
394                                           pool);
395
396               svn_xml_make_close_tag(&sb, pool, "revprops");
397
398               SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
399               SVN_ERR(svn_cl__xml_print_footer("properties", pool));
400             }
401           else
402             {
403               svn_string_t *printable_val = propval;
404
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. */
407
408               if (svn_prop_needs_translation(pname_utf8))
409                 SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
410                                                      TRUE, pool));
411
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)));
416             }
417         }
418     }
419   else  /* operate on a normal, versioned property (not a revprop) */
420     {
421       apr_pool_t *subpool = svn_pool_create(pool);
422       int i;
423
424       if (opt_state->xml)
425         SVN_ERR(svn_cl__xml_print_header("properties", subpool));
426
427       if (opt_state->depth == svn_depth_unknown)
428         opt_state->depth = svn_depth_empty;
429
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"));
440
441       for (i = 0; i < targets->nelts; i++)
442         {
443           const char *target = APR_ARRAY_IDX(targets, i, const char *);
444           apr_hash_t *props;
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;
451
452           svn_pool_clear(subpool);
453           SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
454
455           /* Check for a peg revision. */
456           SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
457                                      subpool));
458
459           if (!svn_path_is_url(truepath))
460             SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
461
462           SVN_ERR(svn_client_propget5(
463             &props,
464             opt_state->show_inherited_props ? &inherited_props : NULL,
465             pname_utf8, truepath,
466             &peg_revision,
467             &(opt_state->start_revision),
468             NULL, opt_state->depth,
469             opt_state->changelists, ctx, subpool,
470             subpool));
471
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;
484
485           /* If there are no properties, and exactly one node was queried,
486              then warn. */
487           if (opt_state->depth == svn_depth_empty
488               && !opt_state->show_inherited_props
489               && apr_hash_count(props) == 0)
490             {
491               svn_error_t *err;
492               err = svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
493                                       _("Property '%s' not found on '%s'"),
494                                       pname_utf8, target);
495               svn_handle_warning2(stderr, err, "svn: ");
496               svn_error_clear(err);
497               warned = TRUE;
498             }
499
500           if (opt_state->xml)
501             SVN_ERR(print_properties_xml(
502               pname_utf8, props,
503               opt_state->show_inherited_props ? inherited_props : NULL,
504               subpool));
505           else
506             SVN_ERR(print_properties(
507               out, truepath, pname_utf8,
508               props,
509               opt_state->show_inherited_props ? inherited_props : NULL,
510               print_filenames,
511               omit_newline, like_proplist, subpool));
512         }
513
514       if (opt_state->xml)
515         SVN_ERR(svn_cl__xml_print_footer("properties", subpool));
516
517       svn_pool_destroy(subpool);
518     }
519
520   if (warned)
521     return svn_error_create(SVN_ERR_BASE, NULL, NULL);
522
523   return SVN_NO_ERROR;
524 }