]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svn/propget-cmd.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.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 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,
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, 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))
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 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,
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,
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, 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;
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 */
332   SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
333   pname = APR_ARRAY_IDX(args, 0, const char *);
334   SVN_ERR(svn_utf_cstring_to_utf8(&pname, pname, pool));
335   if (! svn_prop_name_is_valid(pname))
336     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
337                              _("'%s' is not a valid Subversion property name"),
338                              pname);
339
340   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
341                                                       opt_state->targets,
342                                                       ctx, FALSE, pool));
343
344   /* Add "." if user passed 0 file arguments */
345   svn_opt_push_implicit_dot_target(targets, pool);
346
347   /* Open a stream to stdout. */
348   SVN_ERR(svn_stream_for_stdout(&out, pool));
349
350   if (opt_state->revprop)  /* operate on a revprop */
351     {
352       svn_revnum_t rev;
353       const char *URL;
354       svn_string_t *propval;
355
356       if (opt_state->show_inherited_props)
357         return svn_error_create(
358           SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
359           _("--show-inherited-props can't be used with --revprop"));
360
361       SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
362                                       &URL, ctx, pool));
363
364       /* Let libsvn_client do the real work. */
365       SVN_ERR(svn_client_revprop_get(pname, &propval,
366                                      URL, &(opt_state->start_revision),
367                                      &rev, ctx, pool));
368
369       if (propval == NULL)
370         {
371           return svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
372                                    _("Property '%s' not found on "
373                                      "revision %s"),
374                                    pname,
375                                    svn_opt__revision_to_string(
376                                      &opt_state->start_revision,
377                                      pool));
378         }
379       else
380         {
381           if (opt_state->xml)
382             {
383               svn_stringbuf_t *sb = NULL;
384               char *revstr = apr_psprintf(pool, "%ld", rev);
385
386               SVN_ERR(svn_cl__xml_print_header("properties", pool));
387
388               svn_xml_make_open_tag(&sb, pool, svn_xml_normal,
389                                     "revprops",
390                                     "rev", revstr, SVN_VA_NULL);
391
392               svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE,
393                                           pool);
394
395               svn_xml_make_close_tag(&sb, pool, "revprops");
396
397               SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
398               SVN_ERR(svn_cl__xml_print_footer("properties", pool));
399             }
400           else
401             {
402               svn_string_t *printable_val = propval;
403
404               /* If this is a special Subversion property, it is stored as
405                  UTF8 and LF, so convert to the native locale and eol-style. */
406
407               if (svn_prop_needs_translation(pname))
408                 SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
409                                                      TRUE, pool));
410
411               SVN_ERR(stream_write(out, printable_val->data,
412                                    printable_val->len));
413               if (! opt_state->no_newline)
414                 SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR)));
415             }
416         }
417     }
418   else  /* operate on a normal, versioned property (not a revprop) */
419     {
420       apr_pool_t *subpool = svn_pool_create(pool);
421       int i;
422
423       if (opt_state->xml)
424         SVN_ERR(svn_cl__xml_print_header("properties", subpool));
425
426       if (opt_state->depth == svn_depth_unknown)
427         opt_state->depth = svn_depth_empty;
428
429       /* No-newline mode only makes sense for a single target.  So make
430          sure we have only a single target, and that we're not being
431          asked to recurse on that target. */
432       if (opt_state->no_newline
433           && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty)
434               || (opt_state->show_inherited_props)))
435         return svn_error_create
436           (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
437            _("--no-newline is only available for single-target,"
438              " non-recursive propget operations"));
439
440       for (i = 0; i < targets->nelts; i++)
441         {
442           const char *target = APR_ARRAY_IDX(targets, i, const char *);
443           apr_hash_t *props;
444           svn_boolean_t print_filenames;
445           svn_boolean_t omit_newline;
446           svn_boolean_t like_proplist;
447           const char *truepath;
448           svn_opt_revision_t peg_revision;
449           apr_array_header_t *inherited_props;
450
451           svn_pool_clear(subpool);
452           SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
453
454           /* Check for a peg revision. */
455           SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
456                                      subpool));
457
458           if (!svn_path_is_url(truepath))
459             SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
460
461           SVN_ERR(svn_client_propget5(
462             &props,
463             opt_state->show_inherited_props ? &inherited_props : NULL,
464             pname, truepath,
465             &peg_revision,
466             &(opt_state->start_revision),
467             NULL, opt_state->depth,
468             opt_state->changelists, ctx, subpool,
469             subpool));
470
471           /* Any time there is more than one thing to print, or where
472              the path associated with a printed thing is not obvious,
473              we'll print filenames.  That is, unless we've been told
474              not to do so with the --no-newline option. */
475           print_filenames = ((opt_state->depth > svn_depth_empty
476                               || targets->nelts > 1
477                               || apr_hash_count(props) > 1
478                               || opt_state->verbose
479                               || opt_state->show_inherited_props)
480                              && (! opt_state->no_newline));
481           omit_newline = opt_state->no_newline;
482           like_proplist = opt_state->verbose && !opt_state->no_newline;
483
484           /* If there are no properties, and exactly one node was queried,
485              then warn. */
486           if (opt_state->depth == svn_depth_empty
487               && !opt_state->show_inherited_props
488               && apr_hash_count(props) == 0)
489             {
490               svn_error_t *err;
491               err = svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
492                                       _("Property '%s' not found on '%s'"),
493                                       pname, target);
494               svn_handle_warning2(stderr, err, "svn: ");
495               svn_error_clear(err);
496               warned = TRUE;
497             }
498
499           if (opt_state->xml)
500             SVN_ERR(print_properties_xml(
501               pname, props,
502               opt_state->show_inherited_props ? inherited_props : NULL,
503               subpool));
504           else
505             SVN_ERR(print_properties(
506               out, truepath, pname,
507               props,
508               opt_state->show_inherited_props ? inherited_props : NULL,
509               print_filenames,
510               omit_newline, like_proplist, subpool));
511         }
512
513       if (opt_state->xml)
514         SVN_ERR(svn_cl__xml_print_footer("properties", subpool));
515
516       svn_pool_destroy(subpool);
517     }
518
519   if (warned)
520     return svn_error_create(SVN_ERR_BASE, NULL, NULL);
521
522   return SVN_NO_ERROR;
523 }