Author: rinrab
Date: Sun May 18 19:20:48 2025
New Revision: 1925686

URL: http://svn.apache.org/viewvc?rev=1925686&view=rev
Log:
On the 'xml-writer' branch: Implement the xml writer api.

* subversion/include/svn_xml.h
  (svn_xml_writer_t,
  (svn_xml_writer_create,
   svn_xml_writer_close,
   svn_xml_writer_flush,
   svn_xml_write_open_tag,
   svn_xml_write_open_tag_v,
   svn_xml_write_open_tag_hash,
   svn_xml_write_cdata_cstring,
   svn_xml_write_cdata,
   svn_xml_write_close_tag,
   svn_xml_write_header): Declare symbols.

* subversion/libsvn_subr/xml_writer.c
  (xml_stream_write,
   xml_ensure_bytes,
   xml_write_byte,
   xml_write_bytes,
   xml_write_cstring): Implement functions for writing data to the buffer.

  (svn_xml_writer_create,
   svn_xml_writer_close,
   svn_xml_writer_flush,
   svn_xml_write_open_tag,
   svn_xml_write_open_tag_v,
   svn_xml_write_open_tag_hash,
   svn_xml_write_cdata_cstring,
   svn_xml_write_cdata,
   svn_xml_write_close_tag,
   svn_xml_write_header): Implement symbols.

* subversion/tests/libsvn_subr/xml-test.c
  (test_xml_writer,
   test_xml_writer_always_flush): New tests.
  (test_funcs): Run those tests.

Any kind of feedback will be much appreciated!!

Added:
    subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c   (with 
props)
Modified:
    subversion/branches/xml-writer/subversion/include/svn_xml.h
    subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c

Modified: subversion/branches/xml-writer/subversion/include/svn_xml.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/include/svn_xml.h?rev=1925686&r1=1925685&r2=1925686&view=diff
==============================================================================
--- subversion/branches/xml-writer/subversion/include/svn_xml.h (original)
+++ subversion/branches/xml-writer/subversion/include/svn_xml.h Sun May 18 
19:20:48 2025
@@ -399,6 +399,189 @@ svn_xml_make_close_tag(svn_stringbuf_t *
                        apr_pool_t *pool,
                        const char *tagname);
 
+/*---------------------------------------------------------------*/
+
+/** XML writer, writing XML tags to a generic stream.
+ *
+ * Summary
+ * ---
+ *
+ * The XML writer APIs provides functionality to write the XML tags to
+ * any writable stream, without having a need to manage a stringbuf,
+ * periodically write it to the output, and empty it then -- the XML
+ * writer implements this functionality.
+ *
+ * Usage
+ * ---
+ *
+ * 1. Create the writer using svn_xml_writer_create() before performing any
+ *    writes.
+ *
+ * 2. During the lifetime of the XML writer, all the svn_xml_write_* functions
+ *    can be safely invoked. Please note that they won't immediately write the
+ *    data to the stream (more below), but you may force it using the
+ *    svn_xml_writer_flush() function.
+ *
+ * 3. After the operation, the callers MUST close the writer using the
+ *    svn_xml_writer_close() function. It will flush the remaining data in
+ *    the buffer and close the ostream.
+ *
+ * Buffering
+ * ---
+ *
+ * The XML writer implements a temporary buffer, which will be filled with
+ * the content before it would have been flushed into the stream. This is
+ * used for optimization purposes, so we won't invoke the entire sequence
+ * of stream's write callbacks on each tag we want to write. The callers
+ * may use the svn_xml_writer_flush() function if they want to explicitly
+ * flush it to the stream. Otherwise, the writer will automatically flush
+ * the buffer as soon as it is about to exceed the limit.
+ *
+ * The buffering allows the callers to not care about how much tags do
+ * they want to write. If you are using it in a simple loop, no flushes are
+ * required. You may confidently rely to the automatic flush. However, if
+ * the code following after the write will wait for a long operation, for
+ * example, when the tag has been opened, but we need to do a request to a
+ * server, it's recommended to flush the buffer, so we won't have an opened
+ * tag partially written before the freeze.
+ *
+ * @since New in 1.15.
+ */
+
+typedef struct svn_xml_writer_t svn_xml_writer_t;
+
+/** Create an XML writer for writing to @a ostream. Sets @a writer with
+ * the result, allocated in @a result_pool.
+ *
+ * @a ostream will be closed with the writer, in the svn_xml_writer_close()
+ * function.
+ *
+ * The callers MUST close the writer via svn_xml_writer_close().
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_create(svn_xml_writer_t **writer,
+                      svn_stream_t *ostream,
+                      apr_pool_t *result_pool);
+
+/** Close @a xml_writer. This will also close the stream.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_close(svn_xml_writer_t *xml_writer);
+
+/** Flush the buffer of @a xml_writer to the stream.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_flush(svn_xml_writer_t *xml_writer);
+
+/** Write a new xml tag @a tagname to @a xml_writer.
+ *
+ * Take the tag's attributes from varargs, a SVN_VA_NULL-terminated list of
+ * alternating <tt>char *</tt> key and <tt>char *</tt> val.  Do xml-escaping
+ * on each val.
+ *
+ * @a style is one of the enumerated styles in @c svn_xml_open_tag_style.
+ *
+ * Use @a scratch_pool for temporary allocations.
+ *
+ * This function is similar to svn_xml_make_open_tag(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag(svn_xml_writer_t *xml_writer,
+                       apr_pool_t *scratch_pool,
+                       enum svn_xml_open_tag_style style,
+                       const char *tagname, ...) SVN_NEEDS_SENTINEL_NULL;
+
+/** Like svn_xml_write_open_tag(), but takes a @c va_list instead of being
+ * variadic.
+ *
+ * This function is similar to svn_xml_make_open_tag_v(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag_v(svn_xml_writer_t *xml_writer,
+                         apr_pool_t *scratch_pool,
+                         enum svn_xml_open_tag_style style,
+                         const char *tagname, va_list ap);
+
+/** Like svn_xml_write_open_tag(), but takes a hash table of attributes
+ * (<tt>char *</tt> keys mapping to <tt>char *</tt> values).
+ *
+ * You might ask, why not just provide svn_xml_make_tag_atts()?
+ *
+ * The reason is that a hash table is the most natural interface to an
+ * attribute list; the fact that Expat uses <tt>char **</tt> atts instead is
+ * certainly a defensible implementation decision, but since we'd have
+ * to have special code to support such lists throughout Subversion
+ * anyway, we might as well write that code for the natural interface
+ * (hashes) and then convert in the few cases where conversion is
+ * needed.  Someday it might even be nice to change expat-lite to work
+ * with apr hashes.
+ *
+ * See conversion functions svn_xml_make_att_hash() and
+ * svn_xml_make_att_hash_overlaying().  Callers should use those to
+ * convert Expat attr lists into hashes when necessary.
+ *
+ * This function is similar to svn_xml_make_open_tag_hash(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag_hash(svn_xml_writer_t *xml_writer,
+                            apr_pool_t *scratch_pool,
+                            enum svn_xml_open_tag_style style,
+                            const char *tagname, apr_hash_t *attributes);
+
+/** Writes and escapes cdata from a NULL-terminated string @a str
+ * to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_cdata_cstring(svn_xml_writer_t *xml_writer,
+                            const char *str);
+
+/** Writes and escapes @a len cdata chars from @a data to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_cdata(svn_xml_writer_t *xml_writer,
+                    const char *data, apr_size_t len);
+
+/** Write @a tagname close tag to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_close_tag(svn_xml_writer_t *xml_writer,
+                        apr_pool_t *scratch_pool,
+                        const char *tagname);
+
+/** Write an XML header to @a xml_writer.
+ *
+ * Fully-formed XML documents should start out with a header,
+ * something like <pre>
+ *         \<?xml version="1.0" encoding="UTF-8"?\>
+ * </pre>
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_header(svn_xml_writer_t *xml_writer,
+                     const char *encoding,
+                     apr_pool_t *scratch_pool);
 
 #ifdef __cplusplus
 }

Added: subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c?rev=1925686&view=auto
==============================================================================
--- subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c (added)
+++ subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c Sun May 
18 19:20:48 2025
@@ -0,0 +1,369 @@
+/*
+ * xml_writer.c:  svn_xml_writer_t implementation
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+
+
+/*** Includes. ***/
+
+#include <assert.h>
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_io.h"
+#include "svn_string.h"
+#include "svn_xml.h"
+
+#include "svn_private_config.h"
+
+#define BUFFER_LENGTH 512
+
+
+/*** svn_xml_writer_t constructor and destructor ***/
+
+struct svn_xml_writer_t
+{
+  /* buffer and its offset */
+  char buffer[BUFFER_LENGTH];
+  apr_size_t offset;
+
+  /* an output stream, to write the XML to. */
+  svn_stream_t *ostream;
+
+  /* where this object is allocated, so we can free it easily */
+  apr_pool_t *pool;
+};
+
+svn_error_t *
+svn_xml_writer_create(svn_xml_writer_t **writer,
+                      svn_stream_t *ostream,
+                      apr_pool_t *result_pool)
+{
+  svn_xml_writer_t *result = apr_palloc(result_pool, sizeof(*result));
+
+  result->offset = 0;
+  result->ostream = ostream;
+  result->pool = result_pool;
+
+  *writer = result;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_writer_close(svn_xml_writer_t *writer)
+{
+  if (writer)
+    {
+      SVN_ERR(svn_xml_writer_flush(writer));
+      SVN_ERR(svn_stream_close(writer->ostream));
+      writer->ostream = NULL;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/*** Buffering and writing routines ***/
+
+static svn_error_t *
+xml_stream_write(svn_xml_writer_t *writer, const char *data, apr_size_t len)
+{
+  apr_size_t write_len = len;
+
+  /* We're gonna bail on an incomplete write here only because we know
+     that this stream is really stdout, which should never be blocking
+     on us. */
+  SVN_ERR(svn_stream_write(writer->ostream, data, &write_len));
+  if (write_len != len)
+    return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+                            _("Error writing to stream"));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_writer_flush(svn_xml_writer_t *writer)
+{
+  if (writer->offset > 0)
+    {
+      SVN_ERR(xml_stream_write(writer, writer->buffer, writer->offset));
+      writer->offset = 0;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_ensure_bytes(svn_xml_writer_t *writer, apr_size_t bytes)
+{
+  if (writer->offset + bytes > BUFFER_LENGTH)
+    SVN_ERR(svn_xml_writer_flush(writer));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_byte(svn_xml_writer_t *writer, char b)
+{
+  SVN_ERR(xml_ensure_bytes(writer, 1));
+  writer->buffer[writer->offset++] = b;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_bytes(svn_xml_writer_t *writer, const char *data, apr_size_t len)
+{
+  if (len < BUFFER_LENGTH)
+    {
+      SVN_ERR(xml_ensure_bytes(writer, len));
+      memcpy(writer->buffer + writer->offset, data, len);
+      writer->offset += len;
+    }
+  else
+    {
+      SVN_ERR(svn_xml_writer_flush(writer));
+      SVN_ERR(xml_stream_write(writer, data, len));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_cstring(svn_xml_writer_t *writer, const char *str)
+{
+  return svn_error_trace(xml_write_bytes(writer, str, strlen(str)));
+}
+
+svn_error_t *
+svn_xml_write_raw(svn_xml_writer_t *writer,
+                  const char *data, apr_size_t len)
+{
+  return svn_error_trace(xml_write_bytes(writer, data, len));
+}
+
+/* XML Escaping */
+
+static svn_error_t *
+xml_write_escaped_attr(svn_xml_writer_t *xml_writer,
+                       const char *data, apr_size_t len)
+{
+  const char *end = data + len;
+  const char *p = data, *q;
+
+  while (1)
+    {
+      /* Find a character which needs to be quoted and append bytes up
+         to that point. */
+      q = p;
+      while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '"' &&
+             *q != '\'' && *q != '\r' && *q != '\n' && *q != '\t')
+        q++;
+      SVN_ERR(xml_write_bytes(xml_writer, p, q - p));
+
+      /* We may already be a winner.  */
+      if (q == end)
+        break;
+
+      /* Append the entity reference for the character.  */
+      if (*q == '&')
+        SVN_ERR(xml_write_cstring(xml_writer, "&amp;"));
+      else if (*q == '<')
+        SVN_ERR(xml_write_cstring(xml_writer, "&lt;"));
+      else if (*q == '>')
+        SVN_ERR(xml_write_cstring(xml_writer, "&gt;"));
+      else if (*q == '"')
+        SVN_ERR(xml_write_cstring(xml_writer, "&quot;"));
+      else if (*q == '\'')
+        SVN_ERR(xml_write_cstring(xml_writer, "&apos;"));
+      else if (*q == '\r')
+        SVN_ERR(xml_write_cstring(xml_writer, "&#13;"));
+      else if (*q == '\n')
+        SVN_ERR(xml_write_cstring(xml_writer, "&#10;"));
+      else if (*q == '\t')
+        SVN_ERR(xml_write_cstring(xml_writer, "&#9;"));
+
+      p = q + 1;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_xml_write_cdata(svn_xml_writer_t *xml_writer,
+                    const char *data, apr_size_t len)
+{
+  const char *end = data + len;
+  const char *p = data, *q;
+
+  while (1)
+    {
+      /* Find a character which needs to be quoted and append bytes up
+         to that point.  Strictly speaking, '>' only needs to be
+         quoted if it follows "]]", but it's easier to quote it all
+         the time.
+
+         So, why are we escaping '\r' here?  Well, according to the
+         XML spec, '\r\n' gets converted to '\n' during XML parsing.
+         Also, any '\r' not followed by '\n' is converted to '\n'.  By
+         golly, if we say we want to escape a '\r', we want to make
+         sure it remains a '\r'!  */
+      q = p;
+      while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
+        q++;
+      SVN_ERR(xml_write_bytes(xml_writer, p, q - p));
+
+      /* We may already be a winner.  */
+      if (q == end)
+        break;
+
+      /* Append the entity reference for the character.  */
+      if (*q == '&')
+        SVN_ERR(xml_write_cstring(xml_writer, "&amp;"));
+      else if (*q == '<')
+        SVN_ERR(xml_write_cstring(xml_writer, "&lt;"));
+      else if (*q == '>')
+        SVN_ERR(xml_write_cstring(xml_writer, "&gt;"));
+      else if (*q == '\r')
+        SVN_ERR(xml_write_cstring(xml_writer, "&#13;"));
+
+      p = q + 1;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+
+/*** Writing an open tag ***/
+
+static svn_error_t *
+xml_write_attribute(svn_xml_writer_t *xml_writer,
+                    const char *key, const char *val)
+{
+  SVN_ERR(xml_write_cstring(xml_writer, "\n   "));
+  SVN_ERR(xml_write_cstring(xml_writer, key));
+  SVN_ERR(xml_write_cstring(xml_writer, "=\""));
+  SVN_ERR(xml_write_escaped_attr(xml_writer, val, strlen(val)));
+  SVN_ERR(xml_write_byte(xml_writer, '"'));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_open_tag_hash(svn_xml_writer_t *xml_writer,
+                            apr_pool_t *scratch_pool,
+                            enum svn_xml_open_tag_style style,
+                            const char *tagname, apr_hash_t *attributes)
+{
+  apr_hash_index_t *hi;
+
+  SVN_ERR(xml_write_byte(xml_writer, '<'));
+  SVN_ERR(xml_write_cstring(xml_writer, tagname));
+
+  for (hi = apr_hash_first(scratch_pool, attributes);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const void *key;
+      void *val;
+
+      apr_hash_this(hi, &key, NULL, &val);
+      assert(val != NULL);
+
+      SVN_ERR(xml_write_attribute(xml_writer, key, val));
+    }
+
+  if (style == svn_xml_self_closing)
+    SVN_ERR(xml_write_byte(xml_writer, '/'));
+  SVN_ERR(xml_write_byte(xml_writer, '>'));
+  if (style != svn_xml_protect_pcdata)
+    SVN_ERR(xml_write_byte(xml_writer, '\n'));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_open_tag_v(svn_xml_writer_t *xml_writer,
+                         apr_pool_t *scratch_pool,
+                         enum svn_xml_open_tag_style style,
+                         const char *tagname,
+                         va_list ap)
+{
+  apr_hash_t *ht = svn_xml_ap_to_hash(ap, scratch_pool);
+  return svn_error_trace(svn_xml_write_open_tag_hash(xml_writer, scratch_pool,
+                                                     style, tagname, ht));
+}
+
+svn_error_t *
+svn_xml_write_open_tag(svn_xml_writer_t *xml_writer,
+                       apr_pool_t *scratch_pool,
+                       enum svn_xml_open_tag_style style,
+                       const char *tagname,
+                       ...)
+{
+  va_list ap;
+  svn_error_t *err;
+
+  va_start(ap, tagname);
+  err = svn_xml_write_open_tag_v(xml_writer, scratch_pool, style, tagname, ap);
+  va_end(ap);
+
+  return svn_error_trace(err);
+}
+
+
+
+svn_error_t *
+svn_xml_write_cdata_cstring(svn_xml_writer_t *xml_writer, const char *str)
+{
+  return svn_error_trace(svn_xml_write_cdata(xml_writer, str, strlen(str)));
+}
+
+/* close tag */
+
+svn_error_t *
+svn_xml_write_close_tag(svn_xml_writer_t *xml_writer,
+                        apr_pool_t *scratch_pool,
+                        const char *tagname)
+{
+  SVN_ERR(xml_write_cstring(xml_writer, "</"));
+  SVN_ERR(xml_write_cstring(xml_writer, tagname));
+  SVN_ERR(xml_write_cstring(xml_writer, ">\n"));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_header(svn_xml_writer_t *xml_writer,
+                     const char *encoding,
+                     apr_pool_t *scratch_pool)
+{
+  SVN_ERR(xml_write_cstring(xml_writer, "<?xml version=\"1.0\""));
+  if (encoding)
+    {
+      SVN_ERR(xml_write_cstring(xml_writer, " encoding=\""));
+      SVN_ERR(xml_write_cstring(xml_writer, encoding));
+      SVN_ERR(xml_write_cstring(xml_writer, "\""));
+    }
+  SVN_ERR(xml_write_cstring(xml_writer, "?>\n"));
+
+  return SVN_NO_ERROR;
+}

Propchange: subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c?rev=1925686&r1=1925685&r2=1925686&view=diff
==============================================================================
--- subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c 
(original)
+++ subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c Sun 
May 18 19:20:48 2025
@@ -396,6 +396,85 @@ test_xml_simple_attr_escape(apr_pool_t *
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_xml_writer(apr_pool_t *pool)
+{
+  svn_stringbuf_t *str = svn_stringbuf_create_empty(pool);
+  svn_stream_t *stream = svn_stream_from_stringbuf(str, pool);
+  svn_xml_writer_t *xml_writer;
+
+  SVN_ERR(svn_xml_writer_create(&xml_writer, stream, pool));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "root",
+                                 SVN_VA_NULL));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag1",
+                                 SVN_VA_NULL));
+  SVN_ERR(svn_xml_write_cdata_cstring(xml_writer, "value"));
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag1"));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag2",
+                                 "a", "v", SVN_VA_NULL));
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag2"));
+
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "root"));
+
+  SVN_ERR(svn_xml_writer_close(xml_writer));
+
+  SVN_TEST_STRING_ASSERT(str->data, "<root>\n"
+                                    "<tag1>\n"
+                                    "value</tag1>\n"
+                                    "<tag2\n"
+                                    "   a=\"v\">\n"
+                                    "</tag2>\n"
+                                    "</root>\n");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_xml_writer_always_flush(apr_pool_t *pool)
+{
+  svn_stringbuf_t *str = svn_stringbuf_create_empty(pool);
+  svn_stream_t *stream = svn_stream_from_stringbuf(str, pool);
+  svn_xml_writer_t *xml_writer;
+
+  SVN_ERR(svn_xml_writer_create(&xml_writer, stream, pool));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "root",
+                                 SVN_VA_NULL));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag1",
+                                 SVN_VA_NULL));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+  SVN_ERR(svn_xml_write_cdata_cstring(xml_writer, "value"));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag1"));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+  SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag2",
+                                 "a", "v", SVN_VA_NULL));
+  SVN_ERR(svn_xml_writer_flush(xml_writer));
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag2"));
+  SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "root"));
+
+  SVN_ERR(svn_xml_writer_close(xml_writer));
+  
+  SVN_TEST_STRING_ASSERT(str->data, "<root>\n"
+                                    "<tag1>\n"
+                                    "value</tag1>\n"
+                                    "<tag2\n"
+                                    "   a=\"v\">\n"
+                                    "</tag2>\n"
+                                    "</root>\n");
+
+  return SVN_NO_ERROR;
+}
+
 /* The test table.  */
 static int max_threads = 1;
 
@@ -426,6 +505,10 @@ static struct svn_test_descriptor_t test
                    "simple XML cdata escaping test"),
     SVN_TEST_PASS2(test_xml_simple_attr_escape,
                    "simple XML attribute escaping test"),
+    SVN_TEST_PASS2(test_xml_writer,
+                   "test svn_xml_writer_t"),
+    SVN_TEST_PASS2(test_xml_writer_always_flush,
+                   "test flush xml-writer after each action"),
     SVN_TEST_NULL
   };
 


Reply via email to