[[[
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