Index: subversion/clients/cmdline/props.c =================================================================== --- subversion/clients/cmdline/props.c (revision 17) +++ 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 17) +++ subversion/clients/cmdline/cl.h (working copy) @@ -254,18 +254,6 @@ svn_boolean_t repos_locks, 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 17) +++ 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,225 @@ /*** Code. ***/ +/* A function that will print the given property. The property name is in pname, + value in propval. */ +typedef svn_error_t* (*prop_print_function_t) (const char* pname, + svn_string_t *propval, + void *refcon, + apr_pool_t *pool); + +/* A function that will print grouping information/separators if we're printing + properties for more than one file/directory. The properties are contained in + prop_hash and should probably be passed to print_prop_hash along with a + prop_print_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 print_prop_array how to compute the path it will send for printing. + The first two values also double as boolean for whether the path is url. */ +typedef enum { + print_path_local, + print_path_url, + print_path_absolute, +} print_path_style_t; + +/* 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. + + Pass the property name and a (possibly de-translated) value + to the print function. */ +static svn_error_t * +print_prop_hash (apr_hash_t *prop_hash, + prop_print_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 void *key; + void *val; + const char *pname; + svn_string_t *propval; + + 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 (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 given print-function. + The path is computed as either local, url, or absolute depending on path_style. */ +static svn_error_t* +print_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. Append the svn_stringbuf_t + passed in baton. + This function is an prop_print_function_t*/ +static svn_error_t * +print_prop_xml (const char *pname, + svn_string_t *propval, + void *baton, + apr_pool_t *pool) +{ + svn_stringbuf_t *sb = (svn_stringbuf_t*) baton; + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "key", + NULL); + svn_xml_escape_cdata_cstring (&sb, pname, pool); + svn_xml_make_close_tag (&sb, pool, "key"); + + 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_cstring (&sb, bin_prop->data, pool); + svn_xml_make_close_tag (&sb, pool, "data"); + } + else + { + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "string", + NULL); + svn_xml_escape_cdata_cstring (&sb, propval->data, pool); + svn_xml_make_close_tag (&sb, pool, "string"); + } + + return SVN_NO_ERROR; +} + +/* Print the property name and, if names_only is non-null, it's value. + This function is an prop_print_function_t*/ +static svn_error_t * +print_prop_cl (const char *pname, + 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 from prop_hash. Appends + the svn_stringbuf_t passed in baton. + + call print_prop_hash + + + 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 *sb = (svn_stringbuf_t*) baton; + + /* */ + svn_stringbuf_t *escpath = svn_stringbuf_create ("", pool); + svn_xml_escape_attr_cstring (&escpath, path, pool); + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "dict", + "path", escpath->data, NULL); + + svn_stringbuf_appendcstr (sb, "\n"); + SVN_ERR (print_prop_hash (prop_hash, print_prop_xml, sb, pool)); + + /* */ + svn_xml_make_close_tag (&sb, pool, "dict"); +} + +/* Prints the filepath or url for the property, then the property itself. + 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 (print_prop_hash (prop_hash, print_prop_cl, baton, pool)); + return SVN_NO_ERROR; +} + +/* Create the xml stringbuf, writes header, and + open the first tag (). */ +static svn_error_t * +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, "plist", NULL); +} + +/* Writes the end tag () to sb, then prints to stdout*/ +static svn_error_t * +end_xml_proplist (svn_stringbuf_t *sb, + apr_pool_t *pool) +{ + svn_xml_make_close_tag (&sb, pool, "plist"); + 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,11 +265,12 @@ 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, - opt_state->targets, pool)); + opt_state->targets, pool)); /* Add "." if user passed 0 arguments */ svn_opt_push_implicit_dot_target (targets, pool); @@ -68,7 +291,7 @@ /* Either we have a URL target, or an implicit wc-path ('.') which needs to be converted to a URL. */ if (targets->nelts <= 0) - return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, + return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("No URL target available")); target = ((const char **) (targets->elts))[0]; SVN_ERR (svn_client_url_from_path (&URL, target, pool)); @@ -81,14 +304,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, pool); + SVN_ERR (group_props_xml (proplist, URL, sb, pool)); + SVN_ERR (end_xml_proplist (sb, 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 (print_prop_hash + (proplist, print_prop_cl, (void*)(! opt_state->verbose), pool)); + } } else /* operate on normal, versioned properties (not revprops) */ { @@ -98,7 +329,6 @@ { const char *target = ((const char **) (targets->elts))[i]; apr_array_header_t *props; - int j; svn_error_t *err; svn_boolean_t is_url = svn_path_is_url (target); const char *truepath; @@ -129,23 +359,20 @@ return err; } - 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, pool); + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "array", NULL); + SVN_ERR (print_prop_array (props, (is_url ? print_path_url : + (opt_state->verbose ? print_path_absolute : print_path_local)), + group_props_xml, sb, pool)); + svn_xml_make_close_tag (&sb, pool, "array"); + SVN_ERR (end_xml_proplist (sb, pool)); + } + else + SVN_ERR (print_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 17) +++ subversion/clients/cmdline/main.c (working copy) @@ -533,7 +533,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"},