Index: subversion/clients/cmdline/main.c =================================================================== --- subversion/clients/cmdline/main.c (revision 14784) +++ subversion/clients/cmdline/main.c (working copy) @@ -359,8 +359,8 @@ "\n" " Print information about each TARGET (default: '.')\n" " TARGET may be either a working-copy path or URL.\n"), - {'r', 'R', svn_cl__targets_opt, SVN_CL__AUTH_OPTIONS, - svn_cl__config_dir_opt} }, + {'r', 'R', svn_cl__targets_opt, svn_cl__incremental_opt, svn_cl__xml_opt, + SVN_CL__AUTH_OPTIONS, svn_cl__config_dir_opt} }, { "list", svn_cl__ls, {"ls"}, N_("List directory entries in the repository.\n" Index: subversion/clients/cmdline/dtd/info.dtd =================================================================== --- subversion/clients/cmdline/dtd/info.dtd (revision 0) +++ subversion/clients/cmdline/dtd/info.dtd (revision 0) @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: subversion/clients/cmdline/info-cmd.c =================================================================== --- subversion/clients/cmdline/info-cmd.c (revision 14784) +++ subversion/clients/cmdline/info-cmd.c (working copy) @@ -28,6 +28,7 @@ #include "svn_error.h" #include "svn_path.h" #include "svn_time.h" +#include "svn_xml.h" #include "cl.h" #include "svn_private_config.h" @@ -47,8 +48,372 @@ return SVN_NO_ERROR; } +/* Prints XML header */ +static svn_error_t * +print_header_xml (apr_pool_t *pool) +{ + svn_stringbuf_t *sb = svn_stringbuf_create ("", pool); + /* */ + svn_xml_make_header (&sb, pool); + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "info", NULL); + + return svn_cl__error_checked_fputs (sb->data, stdout); +} + + +/* Prints XML target */ static svn_error_t * +print_target_xml (apr_pool_t *pool, const char *target, + svn_boolean_t to_open) +{ + svn_stringbuf_t *sb = svn_stringbuf_create ("", pool); + + if (to_open) + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "target", + "path", target, NULL); + else + /* "" */ + svn_xml_make_close_tag (&sb, pool, "target"); + + return svn_cl__error_checked_fputs (sb->data, stdout); +} + + +/* Prints XML footer */ +static svn_error_t * +print_footer_xml (apr_pool_t *pool) +{ + svn_stringbuf_t *sb = svn_stringbuf_create ("", pool); + /* "" */ + svn_xml_make_close_tag (&sb, pool, "info"); + return svn_cl__error_checked_fputs (sb->data, stdout); +} + + +/* prints svn info in xml mode to standard out */ +static svn_error_t * +print_info_xml (const char *target, + const svn_info_t *info, + apr_pool_t *pool) +{ + const char *str_kind; + svn_stringbuf_t *sb = svn_stringbuf_create ("", pool); + + switch (info->kind) + { + case svn_node_file: + str_kind = "file"; + break; + + case svn_node_dir: + str_kind = "directory"; + break; + + case svn_node_none: + str_kind = "none"; + break; + + case svn_node_unknown: + default: + str_kind = "unknown"; + break; + } + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "entry", + "path", svn_path_local_style (target, pool), + "type", str_kind, + NULL); + + if (info->URL) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "url", NULL); + svn_xml_escape_cdata_cstring (&sb, info->URL, pool); + svn_xml_make_close_tag (&sb, pool, "url"); + } + + if (SVN_IS_VALID_REVNUM (info->rev)) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "revision", NULL); + svn_xml_escape_cdata_cstring (&sb, apr_psprintf (pool, + "%" APR_OFF_T_FMT, info->rev), pool); + svn_xml_make_close_tag (&sb, pool, "revision"); + } + + if (info->repos_root_URL || info->repos_UUID) + { + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "repository", NULL); + + if (info->repos_root_URL) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "root", NULL); + svn_xml_escape_cdata_cstring (&sb, info->repos_root_URL, pool); + svn_xml_make_close_tag (&sb, pool, "root"); + } + + if (info->repos_UUID) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "uuid", NULL); + svn_xml_escape_cdata_cstring (&sb, info->repos_UUID, pool); + svn_xml_make_close_tag (&sb, pool, "uuid"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "repository"); + } + + if (info->has_wc_info) + { + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "wc-info", NULL); + + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "schedule", NULL); + switch (info->schedule) + { + case svn_wc_schedule_normal: + svn_xml_escape_cdata_cstring (&sb, "normal", pool); + break; + + case svn_wc_schedule_add: + svn_xml_escape_cdata_cstring (&sb, "add", pool); + break; + + case svn_wc_schedule_delete: + svn_xml_escape_cdata_cstring (&sb, "delete", pool); + break; + + case svn_wc_schedule_replace: + svn_xml_escape_cdata_cstring (&sb, "replace", pool); + break; + + default: + break; + } + /* "" */ + svn_xml_make_close_tag (&sb, pool, "schedule"); + + if (info->copyfrom_url) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "copy-from-url", NULL); + svn_xml_escape_cdata_cstring (&sb, info->copyfrom_url, pool); + svn_xml_make_close_tag (&sb, pool, "copy-from-url"); + } + + if (SVN_IS_VALID_REVNUM (info->copyfrom_rev)) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "copied-from-rev", NULL); + svn_xml_escape_cdata_cstring (&sb, apr_psprintf (pool, + "%" APR_OFF_T_FMT, + info->copyfrom_rev), pool); + svn_xml_make_close_tag (&sb, pool, "copied-from-rev"); + } + + if (info->checksum) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "checksum", NULL); + svn_xml_escape_cdata_cstring (&sb, info->checksum, pool); + svn_xml_make_close_tag (&sb, pool, "checksum"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "wc-info"); + } + + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "last-changed", NULL); + + if (info->last_changed_author) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "author", NULL); + svn_xml_escape_cdata_cstring (&sb, info->last_changed_author, pool); + svn_xml_make_close_tag (&sb, pool, "author"); + } + + if (SVN_IS_VALID_REVNUM (info->last_changed_rev)) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "last-rev", NULL); + svn_xml_escape_cdata_cstring (&sb, apr_psprintf (pool, + "%" APR_OFF_T_FMT, + info->last_changed_rev), pool); + svn_xml_make_close_tag (&sb, pool, "last-rev"); + } + + if (info->last_changed_date) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "date", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (info->last_changed_date, pool), pool); + svn_xml_make_close_tag (&sb, pool, "date"); + } + + if (info->has_wc_info) + { + if (info->text_time) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "text-updated", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (info->text_time, pool), pool); + svn_xml_make_close_tag (&sb, pool, "text-updated"); + } + + if (info->prop_time) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "prop-updated", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (info->prop_time, pool), pool); + svn_xml_make_close_tag (&sb, pool, "prop-updated"); + } + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "last-changed"); + + + if (info->conflict_old || info->conflict_wrk + || info->conflict_new || info->prejfile ) + { + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "conflict", NULL); + + if (info->conflict_old) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "prev-basefile", NULL); + svn_xml_escape_cdata_cstring (&sb, info->conflict_old, pool); + svn_xml_make_close_tag (&sb, pool, "prev-basefile"); + } + + if (info->conflict_wrk) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "prev-wrkfile", NULL); + svn_xml_escape_cdata_cstring (&sb, info->conflict_wrk, pool); + svn_xml_make_close_tag (&sb, pool, "prev-wrkfile"); + } + + if (info->conflict_new) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "cur-basefile", NULL); + svn_xml_escape_cdata_cstring (&sb, info->conflict_new, pool); + svn_xml_make_close_tag (&sb, pool, "cur-basefile"); + } + + if (info->prejfile) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "prop-file", NULL); + svn_xml_escape_cdata_cstring (&sb, info->prejfile, pool); + svn_xml_make_close_tag (&sb, pool, "prop-file"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "conflict"); + } + + if (info->lock) + { + /* "" */ + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "lock", NULL); + + if (info->lock->token) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "token", NULL); + svn_xml_escape_cdata_cstring (&sb, info->lock->token, pool); + svn_xml_make_close_tag (&sb, pool, "token"); + } + + if (info->lock->owner) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "owner", NULL); + svn_xml_escape_cdata_cstring (&sb, info->lock->owner, pool); + svn_xml_make_close_tag (&sb, pool, "owner"); + } + + if (info->lock->creation_date) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "created", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (info->lock->creation_date, pool), + pool); + svn_xml_make_close_tag (&sb, pool, "created"); + } + + if (info->lock->expiration_date) + { + /* " xx " */ + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "expires", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (info->lock->expiration_date, pool), + pool); + svn_xml_make_close_tag (&sb, pool, "expires"); + } + + if (info->lock->comment) + { + /* " xxxx " */ + int comment_lines; + /* NOTE: The stdio will handle newline translation. */ + comment_lines = svn_cstring_count_newlines (info->lock->comment) + 1; + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "comment", + "lines", apr_psprintf (pool, "%d", + comment_lines), NULL); + + svn_xml_escape_cdata_cstring (&sb, info->lock->comment, pool); + svn_xml_make_close_tag (&sb, pool, "comment"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "lock"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, pool, "entry"); + + return svn_cl__error_checked_fputs (sb->data, stdout); +} + + +static svn_error_t * print_info (const char *target, const svn_info_t *info, apr_pool_t *pool) @@ -225,7 +590,10 @@ const svn_info_t *info, apr_pool_t *pool) { - return print_info (path, info, pool); + if ((((svn_cl__cmd_baton_t *) baton)->opt_state)->xml) + return print_info_xml (path, info, pool); + else + return print_info (path, info, pool); } @@ -249,6 +617,23 @@ /* Add "." if user passed 0 arguments. */ svn_opt_push_implicit_dot_target (targets, pool); + + if (opt_state->xml) + { + /* If output is not incremental, output the XML header and wrap + everything in a top-level element. This makes the output in + its entirety a well-formed XML document. */ + if (! opt_state->incremental) + SVN_ERR (print_header_xml (pool)); + + } + else + { + if (opt_state->incremental) + return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'incremental' option only valid in XML " + "mode")); + } for (i = 0; i < targets->nelts; i++) { @@ -266,26 +651,34 @@ && (peg_revision.kind == svn_opt_revision_unspecified)) peg_revision.kind = svn_opt_revision_head; + if (opt_state->xml) + print_target_xml (pool, target, TRUE); + err = svn_client_info (truepath, &peg_revision, &(opt_state->start_revision), - info_receiver, NULL, + info_receiver, baton, opt_state->recursive, ctx, subpool); + if (opt_state->xml) + print_target_xml (pool, target, FALSE); + /* If one of the targets is a non-existent URL or wc-entry, don't bail out. Just warn and move on to the next target. */ if (err && err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE) { svn_error_clear (err); - SVN_ERR (svn_cmdline_printf - (subpool, _("%s: (Not a versioned resource)\n\n"), + SVN_ERR (svn_cmdline_fprintf + (stderr, subpool, + _("%s: (Not a versioned resource)\n\n"), svn_path_local_style (target, pool))); continue; } else if (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL) { svn_error_clear (err); - SVN_ERR (svn_cmdline_printf - (subpool, _("%s: (Not a valid URL)\n\n"), + SVN_ERR (svn_cmdline_fprintf + (stderr, subpool, + _("%s: (Not a valid URL)\n\n"), svn_path_local_style (target, pool))); continue; } @@ -295,5 +688,8 @@ } svn_pool_destroy (subpool); + if (opt_state->xml && (! opt_state->incremental)) + err = print_footer_xml (pool); + return SVN_NO_ERROR; } Index: tools/client-side/bash_completion =================================================================== --- tools/client-side/bash_completion (revision 14784) +++ tools/client-side/bash_completion (working copy) @@ -99,7 +99,8 @@ --editor-cmd $pOpts" ;; info) - cmdOpts="$rOpts --targets -R --recursive" + cmdOpts="$rOpts --targets -R --recursive \ + --incremental --xml" ;; list|ls) cmdOpts="$rOpts -v --verbose -R --recursive $pOpts \