[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH] add --xml to proplist command

From: Frierich Munch <colsebas_at_hotmail.com>
Date: 2005-07-24 18:09:30 CEST

On 7/24/05 2:29 PM, "Julian Foad" <julianfoad@btopenworld.com> sneezed:

> p.s. Though I'm not yet reviewing the code, I compiled it and there were some
> warnings:

>> subversion/clients/cmdline/proplist-cmd.c: In function `group_props_xml':
>> subversion/clients/cmdline/proplist-cmd.c:297: warning: control reaches end
>> of non-void function
>> subversion/clients/cmdline/proplist-cmd.c: In function `begin_xml_proplist':
>> subversion/clients/cmdline/proplist-cmd.c:326: warning: control reaches end
>> of non-void function
Sorry, I fixed this yesterday, but long story short: I don't have the time
to switch my entire setup to trunk, so yesterday morning I had to apply my
differences to the 1.2.1 tag, then apply those to a checkout of
trunk/subversion/clients/cmdline. Since then I thought I was applying my
new diffs to the checkout of trunk, but actually I was applying them to the
1.2.1 tag, and sending the diffs from trunk...sorry again.

>> subversion/clients/cmdline/proplist-cmd.c: In function `print_prop_xml':
>> subversion/clients/cmdline/proplist-cmd.c:191: warning: implicit declaration
>> of function `lround'
Fixed; (using pre-compiled headers here).

> Like Peter said, it's helpful to treat the DTD as a partial specification of
> the feature, so that's where I'll start.
>
> I don't see the point of making a special case for when the number of targets
> is exactly one: it just adds complexity. Unless you had a special reason,
> omit the "list" element and make "propertylist" have content "(target*)".
>
> Trying to parse property values into one of a number of different types seems
> to me inappropriate here. That cannot be done correctly without knowing what
> type of information each individual property contains.
I think it's really beneficial. It's not command-line output, and I think
it should be as stateful as possible.

Being said, I'm the first to acknowledged it's a bit akward. Compromise:

All values go into <values></values> tags. This really should be how it is
anyway, making room for inheritance to be expressed. Express type in a
"type" or uglier "possible_type" attribute.

99.9% of the time the deduction of value will be accurate.
For the times that its not, it is still a lossless operation, as the value
is always output as a string. The attribute is more of a hint (this value
was found to be capable o being expressed as) which would be far worse (in
my opinion) to deduce in xslt, and as the only user so far, is needed.

The only time the operation may be loseless is for dates, as we would always
ouput to an ISO date. This makes the xml conform to the standard, so it's
debatable whether the operation is really lossy.

If this still doesn't work for you, can this be opened on to users@svn to
see how most users would prefer.

> If your goal is to have the known "svn:" properties parsed and presented
> specially
That's not the goal. The goal is to represent information as stateful as
possible. I think special casing svn properties is more of a pain to
maintain.

Added "revision" attribute to target for revprop listings.

[[[
Add an XML output mode for "svn proplist".

* subversion/subversion/clients/cmdline/cl.h,
  subversion/subversion/clients/cmdline/props.c
  (svn_cl__print_prop_hash): Remove. Only being used in
  proplist-cmd.c, and re-factored to print different formats.

* subversion/subversion/clients/cmdline/proplist-cmd.c:
 (svn_cl__proplist) Modify. Test for opt_state->xml and acts accordingly.
 (iterate_prop_hash): New, moved out from svn_cl__proplist.
 (iterate_prop_array): New, based on svn_cl__print_prop_hash.
 (print_prop_xml): New. Print a single property in xml.
 (print_prop_cl): New. Print a single property to command-line.
 (group_props_xml): New. Print a group of properties to xml.
 (group_props_cl): New. Print a group of properties to command-line.
 (begin_xml_proplist): New. Start a property list xml stringbuf.
 (end_xml_proplist): New. End a property list xml stringbuf.

* subversion/subversion/clients/cmdline/main.c:
  (svn_cl__cmd_table): Modify. Add svn_cl__xml_opt to proplist command.

* subversion/subversion/clients/cmdline/dtd/proplist.dtd:
  Add.
]]]

Index: subversion/subversion/clients/cmdline/props.c
===================================================================
--- subversion/subversion/clients/cmdline/props.c (revision 15400)
+++ subversion/subversion/clients/cmdline/props.c (working copy)
@@ -42,41 +42,3 @@
      _("Must specify revision explicitly when operating on a "
        "revision property"));
 }
-
-
-svn_error_t *
-svn_cl__print_prop_hash (apr_hash_t *prop_hash,
- svn_boolean_t names_only,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first (pool, prop_hash); hi; hi = apr_hash_next (hi))
- {
- const void *key;
- void *val;
- const char *pname;
- svn_string_t *propval;
- const char *pname_stdout;
-
- apr_hash_this (hi, &key, NULL, &val);
- pname = key;
- propval = val;
-
- if (svn_prop_needs_translation (pname))
- SVN_ERR (svn_subst_detranslate_string (&propval, propval,
- TRUE, pool));
-
- SVN_ERR (svn_cmdline_cstring_from_utf8 (&pname_stdout, pname, pool));
-
- /* ### We leave these printfs for now, since if propval wasn't translated
- * above, we don't know anything about its encoding. In fact, it
- * might be binary data... */
- if (names_only)
- printf (" %s\n", pname_stdout);
- else
- printf (" %s : %s\n", pname_stdout, propval->data);
- }
-
- return SVN_NO_ERROR;
-}
Index: subversion/subversion/clients/cmdline/cl.h
===================================================================
--- subversion/subversion/clients/cmdline/cl.h (revision 15400)
+++ subversion/subversion/clients/cmdline/cl.h (working copy)
@@ -249,7 +249,6 @@
                                    svn_boolean_t repos_locks,
                                    apr_pool_t *pool);
 
-
 /* Print STATUS for PATH in XML to stdout. Use POOL for temporary
    allocations. */
 svn_error_t *
@@ -257,19 +256,6 @@
                           svn_wc_status2_t *status,
                           apr_pool_t *pool);
 
-
-/* Print a hash that maps property names (char *) to property values
- (svn_string_t *). The names are assumed to be in UTF-8 format;
- the values are either in UTF-8 (the special Subversion props) or
- plain binary values.
-
- If NAMES_ONLY is true, print just names, else print names and
- values. */
-svn_error_t *
-svn_cl__print_prop_hash (apr_hash_t *prop_hash,
- svn_boolean_t names_only,
- apr_pool_t *pool);
-
 /* Return a SVN_ERR_CL_ARG_PARSING_ERROR error, with a message stating
    that one must give an explicit revision when operating on a
    revision property. */
Index: subversion/subversion/clients/cmdline/proplist-cmd.c
===================================================================
--- subversion/subversion/clients/cmdline/proplist-cmd.c (revision 15400)
+++ subversion/subversion/clients/cmdline/proplist-cmd.c (working copy)
@@ -22,11 +22,17 @@
 
 /*** Includes. ***/
 
+#include <math.h> /* lround */
+
 #include "svn_cmdline.h"
 #include "svn_pools.h"
 #include "svn_client.h"
 #include "svn_error.h"
+#include "svn_subst.h"
 #include "svn_path.h"
+#include "svn_xml.h"
+#include "svn_time.h"
+#include "svn_base64.h"
 #include "cl.h"
 
 #include "svn_private_config.h"
@@ -34,6 +40,304 @@
 
 /*** Code. ***/
 
+/* A function that operates on a single property. The property name is in
+ PNAME, it's value in PROPVAL. A function of this type should be passed to
+ iterate_prop_hash. */
+typedef svn_error_t* (*prop_iter_function_t) (const char* pname,
+ const svn_string_t *propval,
+ void *refcon,
+ apr_pool_t *pool);
+
+/* A function that will arbitrarily group a set of properties if we're
+ iterating properties for more than one file/directory. The properties
+ are contained in PROP_HASH, and are the properties for the item in PATH.
+ PROP_HASH should probably be passed to iterate_prop_hash along with a
+ prop_iter_function_t. */
+typedef svn_error_t* (*prop_group_function_t) (apr_hash_t *prop_hash,
+ const char *path,
+ void *baton,
+ apr_pool_t *pool);
+
+/* Tell iterate_prop_array how to compute the path it will send for printing.
+ The first two values also double as a boolean for whether the path is url.
+*/
+typedef enum {
+ print_path_local,
+ print_path_url,
+ print_path_absolute,
+} print_path_style_t;
+
+/* Iterate a hash that maps property names (char *) to property values
+ (svn_string_t *). The names are assumed to be in UTF-8 format;
+ the values are either in UTF-8 (the special Subversion props) or
+ plain binary values.
+
+ Pass the property name and a (possibly de-translated) value
+ to the print function in PRINT_FUNC. */
+static svn_error_t *
+iterate_prop_hash (apr_hash_t *prop_hash,
+ prop_iter_function_t print_func,
+ void *baton,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first (pool, prop_hash); hi; hi = apr_hash_next (hi))
+ {
+ const char *pname;
+ svn_string_t *propval;
+
+ apr_hash_this (hi, (const void **)&pname, NULL, (void**)&propval);
+
+ if (svn_prop_needs_translation (pname))
+ SVN_ERR (svn_subst_detranslate_string (&propval, propval,
+ TRUE, pool));
+
+ SVN_ERR (print_func (pname, propval, baton, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Iterate an array of props, where each element is a hash that maps
+ property names (char *) to property values (svn_string_t *).
+
+ Pass one hash and the current property's path to the print-function given
+ in GROUP_FUNC. The path is computed as either local, url, or absolute
+ depending on PATH_STYLE. */
+static svn_error_t*
+iterate_prop_array (apr_array_header_t *props,
+ print_path_style_t path_style,
+ prop_group_function_t group_func,
+ void *baton,
+ apr_pool_t *pool)
+{
+ int j;
+ for (j = 0; j < props->nelts; ++j)
+ {
+ svn_client_proplist_item_t *item
+ = ((svn_client_proplist_item_t **)props->elts)[j];
+ const char *name_local;
+
+ switch (path_style)
+ {
+ case print_path_absolute:
+ SVN_ERR (svn_path_get_absolute (&name_local,
+ item->node_name->data, pool));
+ break;
+
+ case print_path_local:
+ name_local = svn_path_local_style (item->node_name->data,
+ pool);
+ break;
+
+ case print_path_url:
+ default:
+ name_local = item->node_name->data;
+ break;
+ }
+
+ SVN_ERR (group_func (item->prop_hash, name_local, baton, pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Print a property in xml, appending the svn_stringbuf_t
+ passed in baton. The property will be catagorized as one of 7 types
+ by attribute "type":
+ base64 base-64 encoded binary data.
+ date an ISO-8601 date
+ boolean a boolean value
+ integer an integer number
+ real a real/floating-point number
+ string a string
+
+ PNAME is the property name, PROPVAL it's value.
+ BATON is an svn_stringbuf_t.
+
+ This function is an prop_iter_function_t*/
+static svn_error_t *
+print_prop_xml (const char *pname,
+ const svn_string_t *propval,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const char *attr_type = "string";
+ svn_stringbuf_t *esc_name = NULL;
+ svn_stringbuf_t *sb = (svn_stringbuf_t*) baton;
+
+ svn_xml_escape_attr_cstring (&esc_name, pname, pool);
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "entry",
+ "name", esc_name->data, NULL);
+
+ if (!svn_xml_is_xml_safe (propval->data, propval->len))
+ {
+ propval = svn_base64_encode_string (propval, pool);
+ attr_type = "base64";
+ }
+ else
+ {
+ long double number;
+ char c;
+
+ /* If the first character is a digit, scan for a number and an extra
+ character, if the number and character match, then it's probably a
+ string (i.e "90 tomatos added"). */
+ if ( isdigit (propval->data[0]) &&
+ ( sscanf (propval->data, "%Lf%c", &number, &c) == 1 ) )
+ {
+ long double number2 = lround(number);
+
+ /* If new and old floating point numbers are not the same,
+ it's no integer */
+ attr_type = (number!=number2 ? "real" : "integer");
+ }
+ else
+ {
+ svn_error_t *svn_err;
+ apr_time_t result;
+ svn_boolean_t matched;
+
+ /* Ignore any errors on parsing a date...it might not be a date
+ after-all */
+ svn_err = svn_parse_date (&matched, &result, propval->data,
+ apr_time_now(), pool);
+ if (svn_err)
+ svn_error_clear (svn_err);
+ else if (matched)
+ {
+ propval = svn_string_create (svn_time_to_cstring (result, pool),
+ pool);
+ attr_type = "date";
+ }
+ else if ( ( strcasecmp (propval->data, "true")==0 ) ||
+ ( strcasecmp (propval->data, "false")==0 ) )
+ attr_type = "boolean";
+ }
+ }
+ svn_stringbuf_appendcstr (sb, "\n");
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+ "value", "type", attr_type, NULL);
+ svn_xml_escape_cdata_string (&sb, propval, pool);
+ svn_xml_make_close_tag (&sb, pool, "value");
+
+ svn_xml_make_close_tag (&sb, pool, "entry");
+ return SVN_NO_ERROR;
+}
+
+/* Print the property name and, if NAMES_ONLY is non-null, it's value.
+
+ PNAME is the property name, PROPVAL it's value.
+
+ This function is an prop_iter_function_t*/
+static svn_error_t *
+print_prop_cl (const char *pname,
+ const svn_string_t *propval,
+ void *names_only,
+ apr_pool_t *pool)
+{
+ const char *pname_stdout;
+ SVN_ERR (svn_cmdline_cstring_from_utf8 (&pname_stdout, pname, pool));
+ /* ### We leave these printfs for now, since if propval wasn't translated
+ * above, we don't know anything about its encoding. In fact, it
+ * might be binary data... */
+ if (names_only)
+ printf (" %s\n", pname_stdout);
+ else
+ printf (" %s : %s\n", pname_stdout, propval->data);
+
+ return SVN_NO_ERROR;
+}
+
+/* Groups the properties given in PROP_HASH within tag-pair.
+ Appends the svn_stringbuf_t passed in SB.
+ If REV is SVN_INVALID_REVNUM, it is not output as an attribute.
+ <target path="PATH" revision="REV?">
+ call iterate_prop_hash
+ </target>
+*/
+static svn_error_t*
+group_props_xml_rev (apr_hash_t *prop_hash,
+ const char *path,
+ svn_stringbuf_t *sb,
+ apr_pool_t *pool,
+ svn_revnum_t rev)
+{
+ svn_stringbuf_t *escpath = NULL;
+
+ /* <target path=" _path_ "> */
+ svn_xml_escape_attr_cstring (&escpath, path, pool);
+ if (rev==SVN_INVALID_REVNUM)
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "target",
+ "path", escpath->data, NULL);
+ else
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "target",
+ "path", escpath->data,
+ "revision", apr_psprintf (pool, "%ld", rev),
+ NULL);
+
+ svn_stringbuf_appendcstr (sb, "\n");
+ SVN_ERR (iterate_prop_hash (prop_hash, print_prop_xml, sb, pool));
+
+ /* </target> */
+ svn_xml_make_close_tag (&sb, pool, "target");
+}
+
+/* Call through to the real grouping function (group_props_xml_rev) when the
+ the proper revsision number is not known.
+ Currently used for printing normal (not revision) properties.
+
+ This function is a prop_group_function_t. */
+static svn_error_t*
+group_props_xml (apr_hash_t *prop_hash,
+ const char *path,
+ void *baton,
+ apr_pool_t *pool)
+{
+ return ( group_props_xml_rev (prop_hash, path, (svn_stringbuf_t*)baton,
+ pool, SVN_INVALID_REVNUM) );
+}
+
+
+/* Prints the filepath/url for the properties in PROP_HASH.
+ Then calls iterate_prop_hash to print the properties themselves.
+
+ This function is a prop_group_function_t. */
+static svn_error_t*
+group_props_cl (apr_hash_t *prop_hash,
+ const char *path,
+ void *baton,
+ apr_pool_t *pool)
+ {
+ SVN_ERR (svn_cmdline_printf (pool, "Properties on '%s':\n", path));
+ SVN_ERR (iterate_prop_hash (prop_hash, print_prop_cl, baton, pool));
+ return SVN_NO_ERROR;
+}
+
+/* Create an svn_stringbuf_t, writes an xml header, and opens the first tag
+ (<propertylist>). */
+static svn_error_t *
+begin_xml_proplist (svn_stringbuf_t **sb,
+ apr_pool_t *pool)
+{
+ *sb = svn_stringbuf_create ("", pool);
+ svn_xml_make_header (sb, pool);
+ svn_xml_make_open_tag (sb, pool, svn_xml_normal, "propertylist", NULL);
+ return SVN_NO_ERROR;
+}
+
+/* Closes the xml tags started by begin_xml_proplist (</propertylist>),
+ and writes SB to stdout. */
+static svn_error_t *
+end_xml_proplist (svn_stringbuf_t *sb,
+ apr_pool_t *pool)
+{
+ svn_xml_make_close_tag (&sb, pool, "propertylist");
+ SVN_ERR (svn_cl__error_checked_fputs (sb->data, stdout));
+ return SVN_NO_ERROR;
+}
+
+
 /* This implements the `svn_opt_subcommand_t' interface. */
 svn_error_t *
 svn_cl__proplist (apr_getopt_t *os,
@@ -43,6 +347,7 @@
   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
   apr_array_header_t *targets;
+ svn_stringbuf_t *sb;
   int i;
 
   /* Suck up all remaining args in the target array. */
@@ -81,14 +386,22 @@
       SVN_ERR (svn_client_revprop_list (&proplist,
                                         URL, &(opt_state->start_revision),
                                         &rev, ctx, pool));
-
- SVN_ERR
- (svn_cmdline_printf (pool,
+
+ if (opt_state->xml)
+ {
+ begin_xml_proplist (&sb, pool);
+ SVN_ERR (group_props_xml_rev (proplist, URL, sb, pool, rev));
+ SVN_ERR (end_xml_proplist (sb, pool));
+ }
+ else
+ {
+ SVN_ERR (svn_cmdline_printf (pool,
                              _("Unversioned properties on revision %ld:\n"),
                              rev));
 
- SVN_ERR (svn_cl__print_prop_hash
- (proplist, (! opt_state->verbose), pool));
+ SVN_ERR (iterate_prop_hash (proplist, print_prop_cl,
+ (void*)(! opt_state->verbose), pool));
+ }
     }
   else /* operate on normal, versioned properties (not revprops) */
     {
@@ -98,7 +411,6 @@
         {
           const char *target = ((const char **) (targets->elts))[i];
           apr_array_header_t *props;
- int j;
           svn_boolean_t is_url = svn_path_is_url (target);
           const char *truepath;
           svn_opt_revision_t peg_revision;
@@ -120,23 +432,18 @@
                     SVN_ERR_ENTRY_NOT_FOUND,
                     SVN_NO_ERROR));
 
- for (j = 0; j < props->nelts; ++j)
+ if (opt_state->xml)
             {
- svn_client_proplist_item_t *item
- = ((svn_client_proplist_item_t **)props->elts)[j];
- const char *name_local;
+ begin_xml_proplist (&sb, pool);
+ SVN_ERR (iterate_prop_array (props, (print_path_style_t)is_url,
+ group_props_xml, sb, pool));
+ SVN_ERR (end_xml_proplist (sb, pool));
+ }
+ else
+ SVN_ERR (iterate_prop_array (props, (print_path_style_t)is_url,
+ group_props_cl,
+ (void*)opt_state->verbose, pool));
 
- if (! is_url)
- name_local = svn_path_local_style (item->node_name->data,
- subpool);
- else
- name_local = item->node_name->data;
-
- SVN_ERR (svn_cmdline_printf(subpool, "Properties on '%s':\n",
- name_local));
- SVN_ERR (svn_cl__print_prop_hash
- (item->prop_hash, (! opt_state->verbose), subpool));
- }
         }
       svn_pool_destroy (subpool);
     }
Index: subversion/subversion/clients/cmdline/main.c
===================================================================
--- subversion/subversion/clients/cmdline/main.c (revision 15400)
+++ subversion/subversion/clients/cmdline/main.c (working copy)
@@ -548,7 +548,7 @@
        " 1. Lists versioned props. If specified, REV determines in which\n"
        " revision the target is first looked up.\n"
        " 2. Lists unversioned remote props on repos revision.\n"),
- {'v', 'R', 'r', 'q', svn_cl__revprop_opt, SVN_CL__AUTH_OPTIONS,
+ {'v', 'R', 'r', 'q', svn_cl__revprop_opt, svn_cl__xml_opt, SVN_CL__AUTH_OPTIONS,
      svn_cl__config_dir_opt} },
 
   { "propset", svn_cl__propset, {"pset", "ps"},
Index: subversion/subversion/clients/cmdline/dtd/proplist.dtd
===================================================================
--- subversion/subversion/clients/cmdline/dtd/proplist.dtd (revision 0)
+++ subversion/subversion/clients/cmdline/dtd/proplist.dtd (revision 0)
@@ -0,0 +1,16 @@
+<!-- XML DTD for Subversion command-line client output. -->
+
+<!-- For "svn proplist" -->
+<!ELEMENT propertylist (target*)>
+<!ELEMENT target (entry*)>
+<!-- target is allowed to be empty (no properties) -->
+
+<!-- target must have a local path or URL in "path" attribute-->
+<!-- target can optionally specify the revision number (integer) -->
+<!ATTLIST target path CDATA #REQUIRED
+ revision CDATA #IMPLIED>
+<!ELEMENT entry (value)> <!-- entry must have one and only a value -->
+<!ATTLIST entry name CDATA #REQUIRED> <!-- entry must have a name -->
+
+<!ELEMENT value (#PCDATA)>
+<!ATTLIST value type CDATA #IMPLIED> <!-- value can have a type -->
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Received on Sun Jul 24 18:14:45 2005

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.