[[[
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/clients/cmdline/props.c
===================================================================
--- subversion/clients/cmdline/props.c	(revision 15400)
+++ 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/clients/cmdline/cl.h
===================================================================
--- subversion/clients/cmdline/cl.h	(revision 15400)
+++ 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/clients/cmdline/proplist-cmd.c
===================================================================
--- subversion/clients/cmdline/proplist-cmd.c	(revision 15400)
+++ subversion/clients/cmdline/proplist-cmd.c	(working copy)
@@ -26,7 +26,11 @@
 #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 +38,308 @@
 
 /*** 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:
+   <data/>     base-64 encoded binary data.
+   <date/>     an ISO-8601 date
+   <false/>    a boolean value of 0
+   <integer/>  an integer number
+   <real/>     a real/floating-point number
+   <string/>   a string
+   <true/>     a boolean value of 1
+
+   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)
+{
+  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))
+    {
+        svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "data",
+                               NULL);    
+        const svn_string_t *bin_prop = svn_base64_encode_string (propval,
+                                                                 pool);
+        svn_xml_escape_cdata_string (&sb, bin_prop, pool); 
+        svn_xml_make_close_tag (&sb, pool, "data");
+    }
+  else
+    {
+        long double number;
+        char c;
+        int rslt;
+        
+        /* Scan for a number and an extra char, if the char matches, then
+          it's probably string (i.e "90 tomatos added") */
+        rslt = sscanf (propval->data, "%Lf%c", &number, &c);
+        if (rslt==1)
+          {
+             const char *number_type;
+             long double number2 = lround(number);
+             
+             /* If new and old floating point numbers are not the same,
+              it's no integer */
+             number_type  = (number!=number2 ? "real" : "integer");
+
+             svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+                                    number_type, NULL);    
+             svn_xml_escape_cdata_string (&sb, propval, pool);
+             svn_xml_make_close_tag (&sb, pool, number_type);
+          }
+        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)
+               {
+                  const char *iso_time = svn_time_to_cstring (result, pool);
+                  svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+        	                             "date", NULL);    
+                  svn_xml_escape_cdata_cstring (&sb, iso_time, pool);
+                  svn_xml_make_close_tag (&sb, pool, "date");
+	             
+                  return SVN_NO_ERROR;
+               }
+             else if ( strcasecmp (propval->data, "true")==0 )
+               {
+                  svn_stringbuf_appendcstr (sb, "<true/>\n");
+                  return SVN_NO_ERROR;
+               }
+             else if ( strcasecmp (propval->data, "false")==0 )
+               {
+                  svn_stringbuf_appendcstr (sb, "<false/>\n");
+                  return SVN_NO_ERROR;
+               }
+
+             svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+	                     "string", NULL);    
+             svn_xml_escape_cdata_string (&sb, propval, pool);
+             svn_xml_make_close_tag (&sb, pool, "string");
+          }
+    }
+  
+  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 BATON.
+   <target path="_path_">
+      call iterate_prop_hash
+   </target>
+  
+  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)
+{
+  svn_stringbuf_t *escpath = NULL;
+  svn_stringbuf_t *sb = (svn_stringbuf_t*) baton;
+
+  /* <target path=" _path_ "> */
+  svn_xml_escape_attr_cstring (&escpath, path, pool);
+  svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "target",
+                         "path", escpath->data, 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");
+}
+
+/* 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>).  If IS_LIST is true, also opens <list> tag. */
+static svn_error_t *
+begin_xml_proplist (svn_stringbuf_t **sb,
+                    svn_boolean_t is_list,
+                    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);
+  if (is_list)
+    svn_xml_make_open_tag (sb, pool, svn_xml_normal, "list", NULL);
+}
+
+/* Closes the xml tags started by begin_xml_proplist, and writes SB to stdout.
+   If IS_LIST is true, first close <list> tag. */
+static svn_error_t *
+end_xml_proplist (svn_stringbuf_t *sb,
+                  svn_boolean_t is_list,
+                  apr_pool_t *pool)
+{
+  if (is_list)
+    svn_xml_make_close_tag (&sb, pool, "list");
+  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 +349,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 +388,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, FALSE, pool);
+          SVN_ERR (group_props_xml (proplist, URL, sb, pool));
+          SVN_ERR (end_xml_proplist (sb, FALSE, 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 +413,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 +434,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, (props->nelts>1), pool);                    
+              SVN_ERR (iterate_prop_array (props, (print_path_style_t)is_url,
+                                           group_props_xml, sb, pool));
+              SVN_ERR (end_xml_proplist (sb, (props->nelts>1), 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/clients/cmdline/main.c
===================================================================
--- subversion/clients/cmdline/main.c	(revision 15400)
+++ 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,17 @@
+<!-- XML DTD for Subversion command-line client output. -->
+
+<!-- For "svn proplist" -->
+<!ELEMENT propertylist (list | target)>
+<!ELEMENT list (target*)>     <!-- list is allowed to be empty -->
+<!ELEMENT target (entry*)>    <!-- allowed to be empty (no properties) -->
+<!ATTLIST target path CDATA #REQUIRED>   <!-- must have a local path or URL -->
+<!ELEMENT entry (integer | real | string | true | false | data | date)>
+<!ATTLIST entry name CDATA #REQUIRED>    <!-- entry must have a name -->
+
+<!ELEMENT string (#PCDATA)>   <!-- string -->
+<!ELEMENT integer (#PCDATA)>  <!-- integer number -->
+<!ELEMENT real (#PCDATA)>     <!-- floating point number -->
+<!ELEMENT true (#PCDATA)>     <!-- boolean <true/> -->
+<!ELEMENT false (#PCDATA)>    <!-- boolean <false/> -->
+<!ELEMENT date (#PCDATA)>     <!-- a date in ISO format -->
+<!ELEMENT data (#PCDATA)>     <!-- property value encoded in base-64 -->
\ 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 01:25:58 2005