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