[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: Julian Foad <julianfoad_at_btopenworld.com>
Date: 2005-07-28 18:42:41 CEST

I don't know if you were expecting/wanting/not wanting me to do this, but I
have reworked your patch to produce the attached version.

There are lots of stylistic differences, but also functional differences including:

   - the DTD is different

   - the output from all targets to the "svn proplist" command are contained in
a single XML document (rather than one per explicit target), as with the other
commands that output XML.

The major stylistic change I made is:

   - I've removed one level of iterator function, as this made things simpler
in my opinion.

There are more changes I think should be made:

   - When displaying rev-props, the target's "path" attribute should be omitted
because it is irrelevant.

   - Should probably remove the "value" element, leaving the property's value
as the sole content of the "property" element. You made a remark about putting
the value in a sub-element but I can't see a real benefit.

   - Should combine this "proplist" (which includes values) with "propget"
because that would be identical except for only printing one property per
target. It should probably use an identical DTD.

   - Should make the XML output streamy, in the sense of printing the
accumulated XML string to stdout every now and then, e.g. per property or per path.

   -In XML mode, should accept "--incremental" and forbid "--verbose", just as
"svn list" does.

I'd appreciate comments by anybody on the above changes, and perhaps a review
of the patch although it won't be the final version. It would be great if you
want to continue to develop this patch with some of those changes (hmm, I hope
I haven't changed it so much that you won't like to work on it any more), but
otherwise I would be happy to do so.

- Julian

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

Patch by: "señior ¿tyrtle?" <machtyrtle@gmail.com>
            (a.k.a. Frierich Munch <colsebas@hotmail.com>)
          me

* subversion/clients/cmdline/dtd/proplist.dtd
  New file.

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

* subversion/clients/cmdline/proplist-cmd.c
  (prop_iter_function_t): New.
  (iterate_prop_hash): New, moved out from svn_cl__proplist.
  (print_prop_xml): New. Print a single property as XML.
  (print_prop_cl): New. Print a single property to command-line.
  (group_props_xml_rev): New. Print the properties of one item as XML.
  (group_props): New. Print the properties of one item.
  (begin_xml_proplist): New. Start a property list XML stringbuf.
  (end_xml_proplist): New. End a property list XML stringbuf.
  (svn_cl__proplist): Test for opt_state->xml and act accordingly.

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

Index: subversion/clients/cmdline/props.c
===================================================================
--- subversion/clients/cmdline/props.c (revision 15441)
+++ subversion/clients/cmdline/props.c (working copy)
@@ -22,12 +22,7 @@
 
 /*** Includes. ***/
 
-#include <apr_hash.h>
-#include "svn_cmdline.h"
-#include "svn_string.h"
 #include "svn_error.h"
-#include "svn_subst.h"
-#include "svn_props.h"
 #include "cl.h"
 
 #include "svn_private_config.h"
@@ -42,41 +37,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/clients/cmdline/cl.h
===================================================================
--- subversion/clients/cmdline/cl.h (revision 15441)
+++ subversion/clients/cmdline/cl.h (working copy)
@@ -258,23 +258,12 @@
                           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. */
 svn_error_t *svn_cl__revprop_no_rev_error (apr_pool_t *pool);
 
+
 /* Search for a text editor command in standard environment variables,
    and invoke it to edit CONTENTS (using a temporary file created in
    directory BASE_DIR). Return the new contents in *EDITED_CONTENTS,
Index: subversion/clients/cmdline/proplist-cmd.c
===================================================================
--- subversion/clients/cmdline/proplist-cmd.c (revision 15441)
+++ subversion/clients/cmdline/proplist-cmd.c (working copy)
@@ -26,7 +26,10 @@
 #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_base64.h"
 #include "cl.h"
 
 #include "svn_private_config.h"
@@ -34,6 +37,212 @@
 
 /*** Code. ***/
 
+/* A function that operates on a single property. The property name is in
+ PNAME, its value in PROPVAL. */
+typedef svn_error_t * (*prop_iter_function_t) (const char *pname,
+ const svn_string_t *propval,
+ void *baton,
+ apr_pool_t *pool);
+
+/* Iterate over a hash PROP_HASH that maps property names (char *) to
+ property values (svn_string_t *). Pass the property name and value to
+ the function PROP_FUNC, along with PROP_BATON. */
+static svn_error_t *
+iterate_prop_hash (apr_hash_t *prop_hash,
+ prop_iter_function_t prop_func,
+ void *prop_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);
+
+ SVN_ERR (prop_func (pname, propval, prop_baton, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Append the property name PNAME and value PROPVAL as XML into the string
+ SB which is an svn_stringbuf_t pointed to by BATON. The property value
+ is encoded as base64 if necessary.
+
+ This implements the prop_iter_function_t interface. */
+static svn_error_t *
+print_prop_xml (const char *pname,
+ const svn_string_t *propval,
+ void *baton,
+ apr_pool_t *pool)
+{
+ 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, "property",
+ "name", esc_name->data, NULL);
+
+ svn_stringbuf_appendcstr (sb, "\n");
+ if (!svn_xml_is_xml_safe (propval->data, propval->len))
+ {
+ propval = svn_base64_encode_string (propval, pool);
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+ "value", "encoding", "base64", NULL);
+ }
+ else
+ svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+ "value", NULL);
+
+ svn_xml_escape_cdata_string (&sb, propval, pool);
+ svn_xml_make_close_tag (&sb, pool, "value");
+
+ svn_xml_make_close_tag (&sb, pool, "property");
+ return SVN_NO_ERROR;
+}
+
+/* Print the property name PNAME and, if WITH_VALUES is true, its value
+ PROPVAL. WITH_VALUES is an svn_boolean_t pointed to by BATON.
+
+ This implements the prop_iter_function_t interface. */
+static svn_error_t *
+print_prop_cl (const char *pname,
+ const svn_string_t *propval,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const char *pname_stdout;
+ svn_boolean_t with_values = *(svn_boolean_t *) baton;
+
+ if (svn_prop_needs_translation (pname))
+ {
+ svn_string_t *newval;
+ SVN_ERR (svn_subst_detranslate_string (&newval, propval, TRUE, pool));
+ propval = newval;
+ }
+
+ 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 (with_values)
+ printf (" %s : %s\n", pname_stdout, propval->data);
+ else
+ printf (" %s\n", pname_stdout);
+
+ return SVN_NO_ERROR;
+}
+
+/* Group the properties given in PROP_HASH within a "target" tag-pair,
+ appending to the stringbuf SB.
+ If REV is SVN_INVALID_REVNUM, it is not output as an attribute.
+
+ <target path="PATH" [revision="REV"]>
+ ...
+ </target>
+ */
+static svn_error_t *
+group_props_xml (apr_hash_t *prop_hash,
+ const char *path,
+ svn_stringbuf_t *sb,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *escpath = NULL;
+
+ /* <target ...> */
+ 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");
+ return SVN_NO_ERROR;
+}
+
+/* For each target in PROPS (an array of svn_client_proplist_item_t), output the
+ * target path/URL (as determined by PATHS_ARE_URLS) followed by the properties
+ * of that target.
+ *
+ * If XML is true, output in XML format into the string SB, always including
+ * the properties' values; otherwise output plain text to standard output,
+ * including values only if WITH_VALUES is true. */
+static svn_error_t *
+group_props (apr_array_header_t *props,
+ svn_boolean_t paths_are_urls,
+ svn_boolean_t xml,
+ svn_stringbuf_t *sb,
+ svn_boolean_t with_values,
+ apr_pool_t *pool)
+{
+ int j;
+ apr_pool_t *iter_pool = svn_pool_create (pool);
+
+ for (j = 0; j < props->nelts; ++j)
+ {
+ const char *path;
+ svn_client_proplist_item_t *item
+ = ((svn_client_proplist_item_t **) props->elts)[j];
+
+ svn_pool_clear (iter_pool);
+
+ if (paths_are_urls)
+ path = item->node_name->data;
+ else
+ path = svn_path_local_style (item->node_name->data, iter_pool);
+
+ if (xml)
+ SVN_ERR (group_props_xml (item->prop_hash, path, sb,
+ SVN_INVALID_REVNUM, iter_pool));
+ else
+ {
+ SVN_ERR (svn_cmdline_printf (pool, "Properties on '%s':\n", path));
+ SVN_ERR (iterate_prop_hash (item->prop_hash, print_prop_cl,
+ &with_values, pool));
+ }
+ }
+ svn_pool_destroy (iter_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Set *SB to a new string buffer allocated in POOL, and write into it an XML
+ header and the first tag (<propertylist>). */
+static void
+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);
+}
+
+/* Close the XML tags started by begin_xml_proplist (</propertylist>),
+ and write 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,15 +252,21 @@
   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. */
- SVN_ERR (svn_opt_args_to_target_array2 (&targets, os,
+ SVN_ERR (svn_opt_args_to_target_array2 (&targets, os,
                                           opt_state->targets, pool));
 
   /* Add "." if user passed 0 arguments */
   svn_opt_push_implicit_dot_target (targets, pool);
 
+ if (opt_state->xml)
+ {
+ begin_xml_proplist (&sb, pool);
+ }
+
   if (opt_state->revprop) /* operate on revprops */
     {
       svn_revnum_t rev;
@@ -78,17 +293,23 @@
            _("Either a URL or versioned item is required"));
 
       /* Let libsvn_client do the real work. */
- SVN_ERR (svn_client_revprop_list (&proplist,
+ SVN_ERR (svn_client_revprop_list (&proplist,
                                         URL, &(opt_state->start_revision),
                                         &rev, ctx, pool));
-
- 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));
+ if (opt_state->xml)
+ {
+ SVN_ERR (group_props_xml (proplist, URL, sb, rev, pool));
+ }
+ else
+ {
+ SVN_ERR (svn_cmdline_printf
+ (pool,
+ _("Unversioned properties on revision %ld:\n"), rev));
+
+ SVN_ERR (iterate_prop_hash (proplist, print_prop_cl,
+ &opt_state->verbose, pool));
+ }
     }
   else /* operate on normal, versioned properties (not revprops) */
     {
@@ -98,7 +319,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;
@@ -109,7 +329,7 @@
           /* Check for a peg revision. */
           SVN_ERR (svn_opt_parse_path (&peg_revision, &truepath, target,
                                        subpool));
-
+
           SVN_ERR (svn_cl__try
                    (svn_client_proplist2 (&props, truepath, &peg_revision,
                                           &(opt_state->start_revision),
@@ -120,26 +340,17 @@
                     SVN_ERR_ENTRY_NOT_FOUND,
                     SVN_NO_ERROR));
 
- 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;
-
- 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_ERR (group_props (props, is_url, opt_state->xml, sb,
+ opt_state->verbose, pool));
         }
+
       svn_pool_destroy (subpool);
     }
 
+ if (opt_state->xml)
+ {
+ SVN_ERR (end_xml_proplist (sb, pool));
+ }
+
   return SVN_NO_ERROR;
 }
Index: subversion/clients/cmdline/main.c
===================================================================
--- subversion/clients/cmdline/main.c (revision 15441)
+++ 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/clients/cmdline/dtd/proplist.dtd
===================================================================
--- subversion/clients/cmdline/dtd/proplist.dtd (revision 0)
+++ 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 (property*)> <!-- a file or directory node -->
+ <!-- path: local path or URL -->
+ <!-- revision: rev num (integer) -->
+<!ATTLIST target path CDATA #REQUIRED
+ revision CDATA #IMPLIED>
+
+<!ELEMENT property (value)> <!-- a single property -->
+<!ATTLIST property name CDATA #REQUIRED> <!-- name: the property's name -->
+
+<!ELEMENT value (#PCDATA)> <!-- prop value, maybe in base64 -->
+<!ATTLIST value encoding CDATA #IMPLIED> <!-- encoding: absent or "base64" -->

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu Jul 28 18:49:59 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.