[PATCH 2/2] xmllite/writer: Improve namespace support in WriteAttributeString().

Nikolay Sivov nsivov at codeweavers.com
Wed Sep 12 08:25:52 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/xmllite/tests/writer.c |  67 ++++++++--
 dlls/xmllite/writer.c       | 251 ++++++++++++++++++++++++------------
 2 files changed, 224 insertions(+), 94 deletions(-)

diff --git a/dlls/xmllite/tests/writer.c b/dlls/xmllite/tests/writer.c
index 972eb96d9b..1710462deb 100644
--- a/dlls/xmllite/tests/writer.c
+++ b/dlls/xmllite/tests/writer.c
@@ -1604,28 +1604,54 @@ static void test_WriteAttributeString(void)
         const char *output;
         const char *output_partial;
         HRESULT hr;
+        int todo;
+        int todo_partial;
+        int todo_hr;
     }
     attribute_tests[] =
     {
         { NULL, "a", NULL, "b", "<e a=\"b\" />", "<e a=\"b\"" },
+        { "", "a", NULL, "b", "<e a=\"b\" />", "<e a=\"b\"" },
+        { NULL, "a", "", "b", "<e a=\"b\" />", "<e a=\"b\"" },
+        { "", "a", "", "b", "<e a=\"b\" />", "<e a=\"b\"" },
         { "prefix", "local", "uri", "b", "<e prefix:local=\"b\" xmlns:prefix=\"uri\" />", "<e prefix:local=\"b\"" },
         { NULL, "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e xmlns:a=\"defuri\" />", "<e xmlns:a=\"defuri\"" },
         { "xmlns", "a", NULL, "uri", "<e xmlns:a=\"uri\" />", "<e xmlns:a=\"uri\"" },
+        { "xmlns", "a", "", "uri", "<e xmlns:a=\"uri\" />", "<e xmlns:a=\"uri\"" },
+        { "prefix", "xmlns", "uri", "value", "<e prefix:xmlns=\"value\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"value\"" },
+        { "prefix", "xmlns", "uri", NULL, "<e prefix:xmlns=\"\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"\"" },
+        { "prefix", "xmlns", "uri", "", "<e prefix:xmlns=\"\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"\"" },
+        { "prefix", "xmlns", NULL, "uri", "<e xmlns=\"uri\" />", "<e xmlns=\"uri\"" },
+        { "prefix", "xmlns", "", "uri", "<e xmlns=\"uri\" />", "<e xmlns=\"uri\"" },
 
         /* Autogenerated prefix names. */
-        { NULL, "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"" },
-        { NULL, "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"" },
+        { NULL, "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
+        { NULL, "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"", S_OK, 1, 1, 1 },
+        { "", "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
+        { NULL, "a", "defuri", "", "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
+        { "", "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"", S_OK, 1, 1, 1 },
 
         /* Failing cases. */
         { NULL, NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
+        { "", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 1, 1, 1 },
+        { "", NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
+        { "", "", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG, 1, 1, 1 },
+        { NULL, "", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG, 1, 1, 1 },
+        { "prefix", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSURIDECLARATION, 1, 1, 1 },
         { "prefix", NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
         { "prefix", NULL, NULL, "b", "<e />", "<e", E_INVALIDARG },
         { "prefix", NULL, "uri", NULL, "<e />", "<e", E_INVALIDARG },
-        { "xmlns", NULL, NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
         { "xmlns", "a", "defuri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
-        { NULL, "xmlns", "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
-        { "xmlns", NULL, "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
-        { "prefix", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSURIDECLARATION },
+        { "xmlns", "a", "b", "uri", "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
+        { NULL, "xmlns", "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 0, 0, 1 },
+        { "xmlns", NULL, "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 0, 0, 1 },
+        { "pre:fix", "local", "uri", "b", "<e />", "<e", WC_E_NAMECHARACTER },
+        { "pre:fix", NULL, "uri", "b", "<e />", "<e", E_INVALIDARG },
+        { "prefix", "lo:cal", "uri", "b", "<e />", "<e", WC_E_NAMECHARACTER },
+        { "xmlns", NULL, NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
+        { "xmlns", NULL, "", "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
+        { "xmlns", "", NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
+        { "xmlns", "", "", "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
     };
 
     IXmlWriter *writer;
@@ -1650,13 +1676,13 @@ static void test_WriteAttributeString(void)
 
         hr = write_attribute_string(writer, attribute_tests[i].prefix, attribute_tests[i].local,
                 attribute_tests[i].uri, attribute_tests[i].value);
-    todo_wine_if(i != 0)
-        ok(hr == attribute_tests[i].hr, "%u: unexpected hr %#x.\n", i, hr);
+    todo_wine_if(attribute_tests[i].todo_hr)
+        ok(hr == attribute_tests[i].hr, "%u: unexpected hr %#x, expected %#x.\n", i, hr, attribute_tests[i].hr);
 
         hr = IXmlWriter_Flush(writer);
         ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
 
-        check_output(stream, attribute_tests[i].output_partial, i == 1 || i == 2 || i == 3 || i == 4, __LINE__);
+        check_output(stream, attribute_tests[i].output_partial, attribute_tests[i].todo_partial, __LINE__);
 
         hr = IXmlWriter_WriteEndDocument(writer);
         ok(hr == S_OK, "Failed to end document, hr %#x.\n", hr);
@@ -1664,11 +1690,11 @@ static void test_WriteAttributeString(void)
         hr = IXmlWriter_Flush(writer);
         ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
 
-        check_output(stream, attribute_tests[i].output, i == 1 || i == 2 || i == 3 || i == 4, __LINE__);
+        check_output(stream, attribute_tests[i].output, attribute_tests[i].todo, __LINE__);
         IStream_Release(stream);
     }
 
-    /* with namespaces */
+    /* With namespaces */
     stream = writer_set_output(writer);
 
     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
@@ -1678,12 +1704,14 @@ static void test_WriteAttributeString(void)
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
     hr = write_attribute_string(writer, "prefix", "local", "uri", "b");
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
     hr = write_attribute_string(writer, NULL, "a", NULL, "b");
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
+    hr = write_attribute_string(writer, "xmlns", "prefix", NULL, "uri");
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
     hr = write_attribute_string(writer, "p", "attr", NULL, "value");
     ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
 
@@ -1691,6 +1719,15 @@ todo_wine
 todo_wine
     ok(hr == WR_E_DUPLICATEATTRIBUTE, "got 0x%08x\n", hr);
 
+    hr = write_start_element(writer, NULL, "b", NULL);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = write_attribute_string(writer, NULL, "attr2", "outeruri", "value");
+    ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
+
+    hr = write_attribute_string(writer, "pr", "attr3", "outeruri", "value");
+    ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
+
     hr = IXmlWriter_WriteEndDocument(writer);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
@@ -1698,10 +1735,12 @@ todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
     CHECK_OUTPUT_TODO(stream,
-        "<p:a prefix:local=\"b\" a=\"b\" p:attr=\"value\" xmlns:prefix=\"uri\" xmlns:p=\"outeruri\" />");
+        "<p:a prefix:local=\"b\" a=\"b\" xmlns:prefix=\"uri\" p:attr=\"value\" xmlns:p=\"outeruri\">"
+          "<b p:attr2=\"value\" pr:attr3=\"value\" xmlns:pr=\"outeruri\" />"
+        "</p:a>");
 
-    IXmlWriter_Release(writer);
     IStream_Release(stream);
+    IXmlWriter_Release(writer);
 }
 
 static void test_WriteFullEndElement(void)
diff --git a/dlls/xmllite/writer.c b/dlls/xmllite/writer.c
index 31c11d9340..aa6ff7f92a 100644
--- a/dlls/xmllite/writer.c
+++ b/dlls/xmllite/writer.c
@@ -2,7 +2,7 @@
  * IXmlWriter implementation
  *
  * Copyright 2011 Alistair Leslie-Hughes
- * Copyright 2014, 2016 Nikolay Sivov for CodeWeavers
+ * Copyright 2014-2018 Nikolay Sivov for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -84,14 +84,6 @@ typedef struct
 
 static const struct IUnknownVtbl xmlwriteroutputvtbl;
 
-struct ns
-{
-    struct list entry;
-    WCHAR *prefix;
-    int prefix_len;
-    WCHAR *uri;
-};
-
 struct element
 {
     struct list entry;
@@ -100,6 +92,16 @@ struct element
     struct list ns;
 };
 
+struct ns
+{
+    struct list entry;
+    WCHAR *prefix;
+    int prefix_len;
+    WCHAR *uri;
+    BOOL emitted;
+    struct element *element;
+};
+
 typedef struct _xmlwriter
 {
     IXmlWriter IXmlWriter_iface;
@@ -261,22 +263,102 @@ static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
     return writer_strndupW(writer, str, -1);
 }
 
-static void writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
+static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
 {
     struct element *element;
     struct ns *ns;
 
     element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
     if (!element)
-        return;
+        return NULL;
 
     if ((ns = writer_alloc(writer, sizeof(*ns))))
     {
         ns->prefix = writer_strndupW(writer, prefix, prefix_len);
         ns->prefix_len = prefix_len;
         ns->uri = writer_strdupW(writer, uri);
+        ns->emitted = FALSE;
+        ns->element = element;
         list_add_tail(&element->ns, &ns->entry);
     }
+
+    return ns;
+}
+
+static BOOL is_empty_string(const WCHAR *str)
+{
+    return !str || !*str;
+}
+
+static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
+{
+    struct element *element;
+    struct ns *ns;
+
+    if (is_empty_string(prefix) || is_empty_string(uri))
+        return NULL;
+
+    element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
+
+    LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
+    {
+        if (!strcmpW(uri, ns->uri) && !strcmpW(prefix, ns->prefix))
+            return ns;
+    }
+
+    return NULL;
+}
+
+static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
+{
+    struct element *element;
+    struct ns *ns;
+
+    if (is_empty_string(prefix) && is_empty_string(uri))
+        return NULL;
+
+    LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
+    {
+        LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
+        {
+            if (!uri)
+            {
+                if (!ns->prefix) continue;
+                if (!strcmpW(ns->prefix, prefix))
+                    return ns;
+            }
+            else if (!strcmpW(uri, ns->uri))
+            {
+                if (prefix && !*prefix)
+                    return NULL;
+                if (!prefix || !strcmpW(prefix, ns->prefix))
+                    return ns;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static HRESULT is_valid_ncname(const WCHAR *str, int *out)
+{
+    int len = 0;
+
+    *out = 0;
+
+    if (!str || !*str)
+        return S_OK;
+
+    while (*str)
+    {
+        if (!is_ncnamechar(*str))
+            return WC_E_NAMECHARACTER;
+        len++;
+        str++;
+    }
+
+    *out = len;
+    return S_OK;
 }
 
 static HRESULT init_output_buffer(xmlwriteroutput *output)
@@ -363,7 +445,8 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i
 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
 {
     write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
-    write_output_buffer(output, data, len);
+    if (!is_empty_string(data))
+        write_output_buffer(output, data, len);
     write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
     return S_OK;
 }
@@ -503,6 +586,9 @@ static void writer_output_ns(xmlwriter *writer, struct element *element)
 
     LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
     {
+        if (ns->emitted)
+            continue;
+
         write_output_qname(writer->output, xmlnsW, ARRAY_SIZE(xmlnsW), ns->prefix, ns->prefix_len);
         write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW));
         write_output_buffer_quoted(writer->output, ns->uri, -1);
@@ -718,13 +804,26 @@ static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *p
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
-    LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
+static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
+        const WCHAR *local, int local_len, const WCHAR *value)
 {
+    write_output_buffer(writer->output, spaceW, ARRAY_SIZE(spaceW));
+    write_output_qname(writer->output, prefix, prefix_len, local, local_len);
+    write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW));
+    write_output_buffer_quoted(writer->output, value, -1);
+}
+
+static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
+    LPCWSTR local, LPCWSTR uri, LPCWSTR value)
+{
+    static const WCHAR xmlnsW[] = {'x','m','l','n','s',0};
     xmlwriter *This = impl_from_IXmlWriter(iface);
+    int prefix_len, local_len;
+    BOOL is_xmlns_prefix;
+    struct ns *ns;
+    HRESULT hr;
 
-    TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
-                        debugstr_w(ns_uri), debugstr_w(value));
+    TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
 
     switch (This->state)
     {
@@ -740,16 +839,65 @@ static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR
         ;
     }
 
-    if (ns_prefix || ns_uri)
+    /* Prefix "xmlns" */
+    is_xmlns_prefix = prefix && !strcmpW(prefix, xmlnsW);
+    if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
+        return WR_E_NSPREFIXDECLARED;
+
+    if (!local)
+        return E_INVALIDARG;
+
+    /* Validate prefix and local name */
+    if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
+        return hr;
+
+    if (FAILED(hr = is_valid_ncname(local, &local_len)))
+        return hr;
+
+    /* Trivial case, no prefix. */
+    if (prefix_len == 0 && is_empty_string(uri))
     {
-        FIXME("namespaces are not supported.\n");
-        return E_NOTIMPL;
+        write_output_attribute(This, prefix, prefix_len, local, local_len, value);
+        return S_OK;
     }
 
-    write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
-    write_output_buffer(This->output, local_name, -1);
-    write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
-    write_output_buffer_quoted(This->output, value, -1);
+    if (is_xmlns_prefix || (prefix_len == 0 && uri && !strcmpW(uri, xmlnsuriW)))
+    {
+        if (prefix_len && !is_empty_string(uri))
+            return WR_E_XMLNSPREFIXDECLARATION;
+
+        /* Look for exact match defined in current element, and write it out. */
+        if (!(ns = writer_find_ns_current(This, prefix, value)))
+            ns = writer_push_ns(This, local, local_len, value);
+        ns->emitted = TRUE;
+
+        write_output_attribute(This, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, local, local_len, value);
+
+        return S_OK;
+    }
+
+    /* Ignore prefix is URI wasn't specified. */
+    if (is_empty_string(uri))
+    {
+        write_output_attribute(This, NULL, 0, local, local_len, value);
+        return S_OK;
+    }
+
+    if (!(ns = writer_find_ns(This, prefix, uri)))
+    {
+        if (is_empty_string(prefix) && !is_empty_string(uri))
+        {
+            FIXME("Prefix autogeneration is not implemented.\n");
+            return E_NOTIMPL;
+        }
+        if (!is_empty_string(uri))
+            ns = writer_push_ns(This, prefix, prefix_len, uri);
+    }
+
+    if (ns)
+        write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
+    else
+        write_output_attribute(This, prefix, prefix_len, local, local_len, value);
 
     return S_OK;
 }
@@ -923,63 +1071,6 @@ static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName
     return E_NOTIMPL;
 }
 
-static HRESULT is_valid_ncname(const WCHAR *str, int *out)
-{
-    int len = 0;
-
-    *out = 0;
-
-    if (!str || !*str)
-        return S_OK;
-
-    while (*str)
-    {
-        if (!is_ncnamechar(*str))
-            return WC_E_NAMECHARACTER;
-        len++;
-        str++;
-    }
-
-    *out = len;
-    return S_OK;
-}
-
-static BOOL is_empty_string(const WCHAR *str)
-{
-    return !str || !*str;
-}
-
-static struct ns *writer_find_ns(xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
-{
-    struct element *element;
-    struct ns *ns;
-
-    if (is_empty_string(prefix) && is_empty_string(uri))
-        return NULL;
-
-    LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
-    {
-        LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
-        {
-            if (!uri)
-            {
-                if (!ns->prefix) continue;
-                if (!strcmpW(ns->prefix, prefix))
-                    return ns;
-            }
-            else if (!strcmpW(uri, ns->uri))
-            {
-                if (prefix && !*prefix)
-                    return NULL;
-                if (!prefix || !strcmpW(prefix, ns->prefix))
-                    return ns;
-            }
-        }
-    }
-
-    return NULL;
-}
-
 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
                                      LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
 {
-- 
2.18.0




More information about the wine-devel mailing list