svnadmin dump XML output
A Tome by David Summers
This patch is a first attempt at adding an optional XML output to the
"svnadmin dump" command. The long option is, suprisingly enough, "--xml". It
is documented in the svnadmin man page. This patch passes the "make check"
tests so I don't believe I've caused any changes/regressions to the older
dump format.
Theory of operation: The "svnadmin dump" editors and routines have been
modified to optionally output XML in place of each of the previous format
elements. I've tried to pretty much make things a one-to-one correspondence
with the previous format's items (modulo XML elements that have to be ended
on a subsequent line, whereas the previous format many times just had a
one-line data value).
The property contents are XML escaped. The file-content is
XML escaped if it is text and is base64 encoded if it is binary
(anything with svn:mime-type property where the property value doesn't
start with "text").
Since this is (almost) my first code patch, I'm interested in if I'm doing
things "the Right Way (TM)" wrt the HACKING guide, programming logic,
comments, log info, etc. Did I forget anything?
The next step, of course, will be to augment the "svnadmin load" command with
XML input processing.
Questions:
1. Is this the correct way to go about getting XML output from the
"svnadmin dump" command?
2. How should we go about adding redundant information so that it
minimizes potential corruption problems in the database or in the dump
format? (i.e., the content-length on the properties and files, am I
doing it right, or at least on the right path?)
3. Are the XML element and attribute names I chose OK or should there be
other names/attributes chosen, if so, why?
===================================================================
* subversion/svnadmin/main.c : Added --xml option to svnadmin dump subcommand.
Documented the option in the help information. Call svn_repos_dump_fs
with new xml boolean.
* subversion/svnadmin/svnadmin.1 : Documented --xml option for svnadmin dump
subcommand.
* subversion/svnadmin/svn_repos.h : Added xml boolean to svn_repos_dump_fs
declaration.
* subversion/svnadmin/libsvn_repos/dump.c (write_hash_to_stringbuf,
make_dir_baton, dump_node, open_root, add_directory, open_directory,
close_directory, add_file, open_file, change_dir_prop, get_dump_editor,
write_revision_record, svn_repos_dump_fs): Added xml boolean, XML
processing, and output statements to write XML output.
Index: subversion/svnadmin/main.c
===================================================================
--- subversion/svnadmin/main.c (revision 4187)
+++ subversion/svnadmin/main.c (working copy)
@@ -76,6 +76,7 @@
enum
{
svnadmin__incremental = SVN_OPT_FIRST_LONGOPT_ID,
+ svnadmin__xml,
svnadmin__follow_copies,
svnadmin__long_output
};
@@ -101,6 +102,9 @@
{"incremental", svnadmin__incremental, 0,
"dump incrementally"},
+ {"xml", svnadmin__xml, 0,
+ "dump output in XML format"},
+
{"copies", svnadmin__follow_copies, 0,
"follow copy history"},
@@ -124,14 +128,14 @@
{'r'} },
{"dump", subcommand_dump, {0},
- "usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER]] [--incremental]\n\n"
+ "usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER]] [--incremental] [--xml]\n\n"
"Dump the contents of filesystem to stdout in a 'dumpfile'\n"
"portable format, sending feedback to stderr. Dump revisions\n"
"LOWER rev through UPPER rev. If no revisions are given, dump all\n"
"revision trees. If only LOWER is given, dump that one revision tree.\n"
"If --incremental is passed, then the first revision dumped will be\n"
"a diff against the previous revision, instead of the usual fulltext.\n",
- {'r', svnadmin__incremental} },
+ {'r', svnadmin__incremental, svnadmin__xml} },
{"help", subcommand_help, {"?", "h"},
"usage: svn help [SUBCOMMAND1 [SUBCOMMAND2] ...]\n\n"
@@ -192,6 +196,7 @@
svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
svn_boolean_t help; /* --help or -? */
svn_boolean_t incremental; /* --incremental */
+ svn_boolean_t xml; /* --xml */
svn_boolean_t follow_copies; /* --copies */
svn_boolean_t long_output; /* --long */
};
@@ -285,7 +290,8 @@
apr_file_open_stderr, pool));
SVN_ERR (svn_repos_dump_fs (repos, stdout_stream, stderr_stream,
- lower, upper, opt_state->incremental, pool));
+ lower, upper, opt_state->incremental,
+ opt_state->xml, pool));
SVN_ERR (svn_repos_close (repos));
@@ -629,6 +635,9 @@
case svnadmin__incremental:
opt_state.incremental = TRUE;
break;
+ case svnadmin__xml:
+ opt_state.xml = TRUE;
+ break;
case svnadmin__follow_copies:
opt_state.follow_copies = TRUE;
break;
Index: subversion/svnadmin/svnadmin.1
===================================================================
--- subversion/svnadmin/svnadmin.1 (revision 4187)
+++ subversion/svnadmin/svnadmin.1 (working copy)
@@ -50,7 +50,7 @@
Create a new transaction based on
.IR base_rev .
.TP
-.BI dump " repos-path" " \fR[\fI \-rlower-rev\fR[\fI:upper-rev\fR] ] \fR[\fI--incremental\fR]\fI"
+.BI dump " repos-path" " \fR[\fI \-rlower-rev\fR[\fI:upper-rev\fR] ] \fR[\fI--incremental\fR]\fI \fR[\fI--xml\fR]\fI"
Dump the contents of filesystem to stdout in a 'dumpfile'
portable format, sending feedback to stderr. Dump revisions
.IR lower-rev
@@ -64,6 +64,9 @@
.BI --incremental
is passed, then the first revision dumped will be a diff against the
previous revision, instead of the usual fulltext.
+If
+.BI --xml
+is passed, the output will be in XML format.
.TP
.BI load " repos-path"
Read a 'dumpfile'-formatted stream from stdin, committing
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h (revision 4187)
+++ subversion/include/svn_repos.h (working copy)
@@ -652,6 +652,8 @@
If INCREMENTAL is TRUE, the first revision dumped will be a diff
against the previous revision (usually it looks like a full dump of
the tree).
+
+ If XML is TRUE, the output will be dumped in XML format.
*/
svn_error_t *svn_repos_dump_fs (svn_repos_t *repos,
svn_stream_t *dumpstream,
@@ -659,6 +661,7 @@
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t incremental,
+ svn_boolean_t xml,
apr_pool_t *pool);
Index: subversion/libsvn_repos/dump.c
===================================================================
--- subversion/libsvn_repos/dump.c (revision 4187)
+++ subversion/libsvn_repos/dump.c (working copy)
@@ -24,7 +24,8 @@
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_time.h"
-
+#include "svn_xml.h"
+#include "svn_base64.h"
#define ARE_VALID_COPY_ARGS(p,r) ((p && SVN_IS_VALID_REVNUM (r)) ? 1 : 0)
@@ -37,11 +38,16 @@
static void
write_hash_to_stringbuf (apr_hash_t *hash,
svn_stringbuf_t **strbuf,
+ svn_boolean_t xml,
+ svn_boolean_t *binary,
apr_pool_t *pool)
{
apr_hash_index_t *this; /* current hash entry */
char buf[SVN_KEYLINE_MAXLEN];
+ /* Only binary if we find svn:mime-type = application/octet-stream */
+ *binary = FALSE;
+
*strbuf = svn_stringbuf_create ("", pool);
for (this = apr_hash_first (pool, hash); this; this = apr_hash_next (this))
@@ -57,29 +63,60 @@
/* Output name length, then name. */
- svn_stringbuf_appendbytes (*strbuf, "K ", 2);
-
- sprintf (buf, "%" APR_SSIZE_T_FMT "%n", keylen, &bytes_used);
- svn_stringbuf_appendbytes (*strbuf, buf, bytes_used);
- svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+ if ( xml )
+ svn_stringbuf_appendbytes (*strbuf, " <prop name=\"", 18);
+ else
+ {
+ svn_stringbuf_appendbytes (*strbuf, "K ", 2);
+ sprintf (buf, "%" APR_SSIZE_T_FMT "%n", keylen, &bytes_used);
+ svn_stringbuf_appendbytes (*strbuf, buf, bytes_used);
+ svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+ }
svn_stringbuf_appendbytes (*strbuf, (const char *) key, keylen);
- svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+
+ if ( xml )
+ svn_stringbuf_appendbytes (*strbuf, "\">", 2);
+ else
+ svn_stringbuf_appendbytes (*strbuf, "\n", 1);
/* Output value length, then value. */
value = val;
- svn_stringbuf_appendbytes (*strbuf, "V ", 2);
+ if ( ! xml )
+ {
+ svn_stringbuf_appendbytes (*strbuf, "V ", 2);
- sprintf (buf, "%ld%n", (long int) value->len, &bytes_used);
- svn_stringbuf_appendbytes (*strbuf, buf, bytes_used);
- svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+ sprintf (buf, "%ld%n", (long int) value->len, &bytes_used);
+ svn_stringbuf_appendbytes (*strbuf, buf, bytes_used);
+ svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+ }
- svn_stringbuf_appendbytes (*strbuf, value->data, value->len);
- svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+ if ( xml )
+ {
+ svn_stringbuf_t *outstr = 0;
+ svn_xml_escape_string( &outstr, value, pool );
+ svn_stringbuf_appendbytes (*strbuf, outstr->data, outstr->len );
+ }
+ else
+ svn_stringbuf_appendbytes (*strbuf, value->data, value->len);
+
+ if ( xml )
+ svn_stringbuf_appendbytes (*strbuf, "</prop>\n", 8);
+ else
+ svn_stringbuf_appendbytes (*strbuf, "\n", 1);
+
+ /* If the key is "svn:mime-type" and the value != text then
+ it is binary. */
+ if (strncmp((const char *) key, "svn:mime-type",
+ keylen < 13 ? keylen : 13) == 0 &&
+ strncmp(value->data, "text",
+ value->len < 4 ? value->len : 4) != 0)
+ *binary = TRUE;
}
- svn_stringbuf_appendbytes (*strbuf, "PROPS-END\n", 10);
+ if ( ! xml )
+ svn_stringbuf_appendbytes (*strbuf, "PROPS-END\n", 10);
}
@@ -111,6 +148,9 @@
/* reusable buffer for writing file contents */
char buffer[SVN_STREAM_CHUNK_SIZE];
apr_size_t bufsize;
+
+ /* XML is true if the output should be in XML format. */
+ svn_boolean_t xml;
};
struct dir_baton
@@ -142,6 +182,9 @@
/* pool to be used for deleting the hash items */
apr_pool_t *pool;
+
+ /* XML is true if the output should be in XML format. */
+ svn_boolean_t xml;
};
@@ -164,6 +207,7 @@
void *edit_baton,
void *parent_dir_baton,
svn_boolean_t added,
+ svn_boolean_t xml,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
@@ -190,7 +234,8 @@
new_db->written_out = FALSE;
new_db->deleted_entries = apr_hash_make (pool);
new_db->pool = pool;
-
+ new_db->xml = xml;
+
return new_db;
}
@@ -215,6 +260,7 @@
svn_boolean_t is_copy,
const char *cmp_path,
svn_revnum_t cmp_rev,
+ svn_boolean_t xml,
apr_pool_t *pool)
{
svn_stringbuf_t *propstring;
@@ -224,17 +270,35 @@
svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
const char *compare_path = path;
svn_revnum_t compare_rev = eb->current_rev - 1;
+ svn_boolean_t binary = FALSE;
+
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool, " <node>\n"));
/* Write out metadata headers for this file node. */
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
-
- if (kind == svn_node_file)
+ if ( xml )
SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
- else if (kind == svn_node_dir)
+ " <node-path>%s</node-path>\n", path));
+ else
SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+ SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
+
+ if (kind == svn_node_file)
+ {
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-kind>file</node-kind>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
+ }
+ else if (kind == svn_node_dir)
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-kind>dir</node-kind>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
/* Validate the comparison path/rev. */
if (ARE_VALID_COPY_ARGS (cmp_path, cmp_rev))
@@ -248,9 +312,13 @@
svn_fs_root_t *compare_root;
int text_changed = 0, props_changed = 0;
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": change\n"));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-action>change</node-action>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": change\n"));
/* either the text or props changed, or possibly both. */
SVN_ERR (svn_fs_revision_root (&compare_root,
@@ -274,9 +342,13 @@
if (! is_copy)
{
/* a simple delete+add, implied by a single 'replace' action. */
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": replace\n"));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-action>replace</node-action>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": replace\n"));
/* definitely need to dump all content for a replace. */
if (kind == svn_node_file)
@@ -289,13 +361,17 @@
/* the path & kind headers have already been printed; just
add a delete action, and end the current record.*/
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": delete\n\n"));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-action>delete</node-action>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": delete\n\n"));
/* recurse: print an additional add-with-history record. */
SVN_ERR (dump_node (eb, path, kind, svn_node_action_add,
- is_copy, compare_path, compare_rev, pool));
+ is_copy, compare_path, compare_rev, xml, pool));
/* we can leave this routine quietly now, don't need to dump
any content; that was already done in the second record. */
@@ -305,9 +381,13 @@
}
else if (action == svn_node_action_delete)
{
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": delete\n"));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-action>delete</node-action>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": delete\n"));
/* we can leave this routine quietly now, don't need to dump
any content. */
@@ -316,8 +396,12 @@
}
else if (action == svn_node_action_add)
{
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <node-action>add</node-action>\n"));
+ else
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
if (! is_copy)
{
@@ -376,8 +460,16 @@
then our dumpstream format demands that at a *minimum*, we
see a lone "PROPS-END" as a divider between text and props
content within the content-block. */
- len = 2;
- return svn_stream_write (eb->stream, "\n\n", &len); /* ### needed? */
+ if ( xml )
+ {
+ len = 14;
+ return svn_stream_write (eb->stream, " </node>\n", &len); /* ### needed? */
+ }
+ else
+ {
+ len = 2;
+ return svn_stream_write (eb->stream, "\n\n", &len); /* ### needed? */
+ }
}
/*** Start prepping content to dump... ***/
@@ -388,12 +480,16 @@
if (must_dump_props)
{
SVN_ERR (svn_fs_node_proplist (&prophash, eb->fs_root, path, pool));
- write_hash_to_stringbuf (prophash, &propstring, pool);
+
+ write_hash_to_stringbuf (prophash, &propstring, xml, &binary, pool);
+
proplen = propstring->len;
content_length += proplen;
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
- ": %" APR_SIZE_T_FMT "\n", proplen));
+
+ if ( ! xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+ ": %" APR_SIZE_T_FMT "\n", proplen));
}
/* If we are supposed to dump text, write out a text length header here. */
@@ -401,9 +497,10 @@
{
SVN_ERR (svn_fs_file_length (&textlen, eb->fs_root, path, pool));
content_length += textlen;
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
- ": %" APR_OFF_T_FMT "\n", textlen));
+ if ( ! xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
+ ": %" APR_OFF_T_FMT "\n", textlen));
/* ### someday write a node-content-checksum here. */
}
@@ -411,15 +508,32 @@
and is the summation of the text and prop contents lengths. We
write this only for the benefit of non-Subversion RFC-822
parsers. */
- SVN_ERR (svn_stream_printf (eb->stream, pool,
- SVN_REPOS_DUMPFILE_CONTENT_LENGTH
- ": %" APR_OFF_T_FMT "\n\n", content_length));
+ if ( ! xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+ ": %" APR_OFF_T_FMT "\n\n", content_length));
/* Dump property content if we're supposed to do so. */
if (must_dump_props)
{
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool, " <proplist>\n"));
+
len = propstring->len;
SVN_ERR (svn_stream_write (eb->stream, propstring->data, &len));
+
+ if ( xml )
+ SVN_ERR (svn_stream_printf (eb->stream, pool, " </proplist>\n"));
+ }
+
+ if ( xml )
+ {
+ SVN_ERR (svn_stream_printf (eb->stream, pool,
+ " <file-content "
+ "content-length=\"%" APR_OFF_T_FMT "\"%s>",
+ content_length,
+ binary ? " content-transfer-encoding=\"base64\""
+ : ""));
}
/* Dump text content */
@@ -429,28 +543,84 @@
{
apr_size_t rlen, wlen;
svn_stream_t *contents;
+ svn_string_t instr;
SVN_ERR (svn_fs_file_contents (&contents, eb->fs_root, path, pool));
while (1)
{
+ svn_stringbuf_t *outstr = 0;
+
/* read a maximum number of bytes from the file, please. */
rlen = eb->bufsize;
SVN_ERR (svn_stream_read (contents, eb->buffer, &rlen));
-
- /* write however many bytes you read, please. */
- wlen = rlen;
- SVN_ERR (svn_stream_write (eb->stream, eb->buffer, &wlen));
- if (wlen != rlen)
+
+ if ( xml )
{
- /* Uh oh, didn't write as many bytes as we read, and no
- error was returned. According to the docstring, this
- should never happen. */
- return
- svn_error_createf (SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
- "Error dumping textual contents of %s.",
- path);
+ /* If it is binary then encode via base64 as we promised above. */
+ if ( binary )
+ {
+ svn_stringbuf_t instrb;
+ instrb.data = eb->buffer;
+ instrb.len = rlen;
+
+ outstr = svn_base64_encode_string ( &instrb, pool );
+
+ wlen = outstr->len;
+ SVN_ERR (svn_stream_write (eb->stream, outstr->data, &wlen));
+
+ if (wlen != outstr->len)
+ {
+ /* Uh oh, didn't write as many bytes as we read, and no
+ error was returned. According to the docstring, this
+ should never happen. */
+ return
+ svn_error_createf (SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ "Error dumping textual contents of %s.",
+ path);
+ }
+ }
+ else
+ {
+ /* XML Escape the output */
+ instr.data = eb->buffer;
+ instr.len = rlen;
+
+ svn_xml_escape_string( &outstr, &instr, pool );
+
+ wlen = outstr->len;
+ SVN_ERR (svn_stream_write (eb->stream, outstr->data, &wlen));
+
+ if (wlen != outstr->len)
+ {
+ /* Uh oh, didn't write as many bytes as we read, and no
+ error was returned. According to the docstring, this
+ should never happen. */
+ return
+ svn_error_createf (SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ "Error dumping textual contents of %s.",
+ path);
+ }
+ }
}
+ else /* Non XML (normal) write */
+ {
+ /* write however many bytes you read, please. */
+ wlen = rlen;
+ SVN_ERR (svn_stream_write (eb->stream, eb->buffer, &wlen));
+
+ if (wlen != rlen)
+ {
+ /* Uh oh, didn't write as many bytes as we read, and no
+ error was returned. According to the docstring, this
+ should never happen. */
+ return
+ svn_error_createf (SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ "Error dumping textual contents of %s.",
+ path);
+ }
+ }
+
if (rlen != eb->bufsize)
{
@@ -462,8 +632,18 @@
}
}
- len = 2;
- SVN_ERR (svn_stream_write (eb->stream, "\n\n", &len)); /* ### needed? */
+ if ( xml )
+ {
+ len = 16;
+ SVN_ERR (svn_stream_write (eb->stream, "</file-content>\n", &len)); /* ### needed? */
+ len = 12;
+ SVN_ERR (svn_stream_write (eb->stream, " </node>\n", &len)); /* ### needed? */
+ }
+ else
+ {
+ len = 2;
+ SVN_ERR (svn_stream_write (eb->stream, "\n\n", &len)); /* ### needed? */
+ }
return SVN_NO_ERROR;
}
@@ -475,8 +655,9 @@
apr_pool_t *pool,
void **root_baton)
{
+ struct edit_baton *eb = edit_baton;
*root_baton = make_dir_baton (NULL, NULL, SVN_INVALID_REVNUM,
- edit_baton, NULL, FALSE, pool);
+ edit_baton, NULL, FALSE, eb->xml, pool);
return SVN_NO_ERROR;
}
@@ -510,7 +691,8 @@
void *val;
svn_boolean_t is_copy = FALSE;
struct dir_baton *new_db
- = make_dir_baton (path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pool);
+ = make_dir_baton (path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pb->xml,
+ pool);
/* This might be a replacement -- is the path already deleted? */
val = apr_hash_get (pb->deleted_entries, path, APR_HASH_KEY_STRING);
@@ -525,6 +707,7 @@
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+ pb->xml,
pool));
if (val)
@@ -560,7 +743,7 @@
cmp_rev = pb->cmp_rev;
}
- new_db = make_dir_baton (path, cmp_path, cmp_rev, eb, pb, FALSE, pool);
+ new_db = make_dir_baton (path, cmp_path, cmp_rev, eb, pb, FALSE, eb->xml, pool);
*child_baton = new_db;
return SVN_NO_ERROR;
}
@@ -589,7 +772,7 @@
shouldn't care. */
SVN_ERR (dump_node (eb, path,
svn_node_unknown, svn_node_action_delete,
- FALSE, NULL, SVN_INVALID_REVNUM, subpool));
+ FALSE, NULL, SVN_INVALID_REVNUM, db->xml, subpool));
svn_pool_clear (subpool);
}
@@ -625,6 +808,7 @@
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+ pb->xml,
pool));
if (val)
@@ -659,7 +843,7 @@
SVN_ERR (dump_node (eb, path,
svn_node_file, svn_node_action_change,
- FALSE, cmp_path, cmp_rev, pool));
+ FALSE, cmp_path, cmp_rev, pb->xml, pool));
*file_baton = NULL; /* muhahahaha again */
return SVN_NO_ERROR;
@@ -682,7 +866,7 @@
{
SVN_ERR (dump_node (eb, db->path,
svn_node_dir, svn_node_action_change,
- FALSE, db->cmp_path, db->cmp_rev, pool));
+ FALSE, db->cmp_path, db->cmp_rev, db->xml, pool));
db->written_out = TRUE;
}
return SVN_NO_ERROR;
@@ -699,6 +883,7 @@
svn_stream_t *stream,
svn_stream_t *feedback_stream,
svn_revnum_t oldest_dumped_rev,
+ svn_boolean_t xml,
apr_pool_t *pool)
{
/* Allocate an edit baton to be stored in every directory baton.
@@ -715,6 +900,7 @@
eb->path = apr_pstrdup (pool, root_path);
SVN_ERR (svn_fs_revision_root (&(eb->fs_root), fs, to_rev, pool));
eb->current_rev = to_rev;
+ eb->xml = xml;
/* Set up the editor. */
dump_editor->open_root = open_root;
@@ -745,11 +931,13 @@
write_revision_record (svn_stream_t *stream,
svn_fs_t *fs,
svn_revnum_t rev,
+ svn_boolean_t xml,
apr_pool_t *pool)
{
apr_size_t len;
apr_hash_t *props;
svn_stringbuf_t *encoded_prophash;
+ svn_boolean_t binary = FALSE;
SVN_ERR (svn_fs_revision_proplist (&props, fs, rev, pool));
@@ -773,30 +961,47 @@
}
}
- write_hash_to_stringbuf (props, &encoded_prophash, pool);
+ write_hash_to_stringbuf (props, &encoded_prophash, xml, &binary, pool);
/* ### someday write a revision-content-checksum */
- SVN_ERR (svn_stream_printf (stream, pool,
- SVN_REPOS_DUMPFILE_REVISION_NUMBER
- ": %" SVN_REVNUM_T_FMT "\n", rev));
- SVN_ERR (svn_stream_printf (stream, pool,
- SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
- ": %" APR_SIZE_T_FMT "\n",
- encoded_prophash->len));
-
- /* Write out a regular Content-length header for the benefit of
- non-Subversion RFC-822 parsers. */
- SVN_ERR (svn_stream_printf (stream, pool,
- SVN_REPOS_DUMPFILE_CONTENT_LENGTH
- ": %" APR_SIZE_T_FMT "\n\n",
- encoded_prophash->len));
+ if ( xml )
+ {
+ SVN_ERR (svn_stream_printf (stream, pool,
+ " <proplist content-length=\"%" APR_SIZE_T_FMT "\">\n",
+ encoded_prophash->len));
+ }
+ else
+ {
+ SVN_ERR (svn_stream_printf (stream, pool,
+ SVN_REPOS_DUMPFILE_REVISION_NUMBER
+ ": %" SVN_REVNUM_T_FMT "\n", rev));
+ SVN_ERR (svn_stream_printf (stream, pool,
+ SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+ ": %" APR_SIZE_T_FMT "\n",
+ encoded_prophash->len));
+
+ /* Write out a regular Content-length header for the benefit of
+ non-Subversion RFC-822 parsers. */
+ SVN_ERR (svn_stream_printf (stream, pool,
+ SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+ ": %" APR_SIZE_T_FMT "\n\n",
+ encoded_prophash->len));
+ }
len = encoded_prophash->len;
SVN_ERR (svn_stream_write (stream, encoded_prophash->data, &len));
- len = 1;
- SVN_ERR (svn_stream_write (stream, "\n", &len));
+ if ( xml )
+ {
+ len = 16;
+ SVN_ERR (svn_stream_write (stream, " </proplist>\n", &len));
+ }
+ else
+ {
+ len = 1;
+ SVN_ERR (svn_stream_write (stream, "\n", &len));
+ }
return SVN_NO_ERROR;
}
@@ -811,6 +1016,7 @@
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t incremental,
+ svn_boolean_t xml,
apr_pool_t *pool)
{
const svn_delta_editor_t *dump_editor;
@@ -843,10 +1049,16 @@
whether or not this is an incremental
dump, so just simplify things. */
- /* Write out "general" metadata for the dumpfile. In this case, a
- magic header followed by a dumpfile format version. */
- SVN_ERR (svn_stream_printf (stream, pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER
- ": %d\n", SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+ /* Write out "general" metadata for the dumpfile. */
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<subversion-dump-file format-version=\"%d\">\n",
+ SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+ else
+ /* In this case, a magic header followed by a dumpfile format version. */
+ SVN_ERR (svn_stream_printf (stream, pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER
+ ": %d\n", SVN_REPOS_DUMPFILE_FORMAT_VERSION));
/* Main loop: we're going to dump revision i. */
for (i = start_rev; i <= end_rev; i++)
@@ -864,7 +1076,15 @@
{
/* Just write out the one revision 0 record and move on.
The parser might want to use its properties. */
- SVN_ERR (write_revision_record (stream, fs, 0, subpool));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool,
+ " <revision number=\"0\">\n"));
+
+ SVN_ERR (write_revision_record (stream, fs, 0, xml, subpool));
+
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool, " </revision>\n"));
+
to_rev = 0;
goto loop_end;
}
@@ -881,13 +1101,17 @@
to_rev = i;
}
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool,
+ " <revision number=\"%" SVN_REVNUM_T_FMT "\">\n", to_rev));
+
/* Write the revision record. */
- SVN_ERR (write_revision_record (stream, fs, to_rev, subpool));
+ SVN_ERR (write_revision_record (stream, fs, to_rev, xml, subpool));
/* The editor which dumps nodes to a file. */
SVN_ERR (get_dump_editor (&dump_editor, &dump_edit_baton,
fs, to_rev, "/", stream, feedback_stream,
- start_rev, subpool));
+ start_rev, xml, subpool));
/* Drive the editor. */
SVN_ERR (svn_fs_revision_root (&from_root, fs, from_rev, subpool));
@@ -901,6 +1125,9 @@
TRUE, /* send copyfrom args */
subpool));
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool, " </revision>\n"));
+
loop_end:
/* Reuse all memory consumed by the dump of this one revision. */
svn_pool_clear (subpool);
@@ -910,6 +1137,9 @@
to_rev);
}
+ if ( xml )
+ SVN_ERR (svn_stream_printf (stream, pool, "</subversion-dump-file>\n" ));
+
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
--
David Wayne Summers "Linux: Because reboots are for hardware upgrades!"
david_at_summersoft.fay.ar.us PGP Key: http://summersoft.fay.ar.us/~david/pgp.txt
PGP Key fingerprint = C0 E0 4F 50 DD A9 B6 2B 60 A1 31 7E D2 28 6D A8
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Dec 21 12:49:02 2002