[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] "svnadmin dump": An optional XML output format.

From: David Summers <david_at_summersoft.fay.ar.us>
Date: 2002-12-21 12:48:11 CET

                             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

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.