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