Signed-off-by: Nikolay Sivov nsivov@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) {