From fba7694887e3fc7695eabd118e2810a82d443dbc Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Mon, 23 Dec 2019 00:56:19 +0100 Subject: [PATCH] [XMLLITE] Sync with Wine Staging 4.18. CORE-16441 --- dll/win32/xmllite/reader.c | 34 +- dll/win32/xmllite/writer.c | 694 +++++++++++++++++++++++----- dll/win32/xmllite/xmllite_private.h | 5 + media/doc/README.WINE | 2 +- 4 files changed, 602 insertions(+), 133 deletions(-) diff --git a/dll/win32/xmllite/reader.c b/dll/win32/xmllite/reader.c index 9cb7f3bd776..ba09556adfa 100644 --- a/dll/win32/xmllite/reader.c +++ b/dll/win32/xmllite/reader.c @@ -29,10 +29,12 @@ #include "objbase.h" #include "xmllite.h" #include "xmllite_private.h" +#ifdef __REACTOS__ +#include +#endif #include "wine/debug.h" #include "wine/list.h" -#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(xmllite); @@ -95,7 +97,7 @@ static const WCHAR gtW[] = {'>',0}; static const WCHAR commentW[] = {'<','!','-','-',0}; static const WCHAR piW[] = {'<','?',0}; -static BOOL is_namestartchar(WCHAR ch); +BOOL is_namestartchar(WCHAR ch); static const char *debugstr_nodetype(XmlNodeType nodetype) { @@ -387,7 +389,7 @@ static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str if(str) { DWORD size; - size = (strlenW(str)+1)*sizeof(WCHAR); + size = (lstrlenW(str)+1)*sizeof(WCHAR); ret = readerinput_alloc(input, size); if (ret) memcpy(ret, str, size); } @@ -756,9 +758,9 @@ xml_encoding parse_encoding_name(const WCHAR *name, int len) n = (min+max)/2; if (len != -1) - c = strncmpiW(xml_encoding_map[n].name, name, len); + c = _wcsnicmp(xml_encoding_map[n].name, name, len); else - c = strcmpiW(xml_encoding_map[n].name, name); + c = wcsicmp(xml_encoding_map[n].name, name); if (!c) return xml_encoding_map[n].enc; @@ -1019,9 +1021,9 @@ static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding encoded_buffer *src = &readerinput->buffer->encoded; encoded_buffer *dest = &readerinput->buffer->utf16; int len, dest_len; + UINT cp = ~0u; HRESULT hr; WCHAR *ptr; - UINT cp; hr = get_code_page(enc, &cp); if (FAILED(hr)) return; @@ -1491,7 +1493,7 @@ static inline BOOL is_char(WCHAR ch) } /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */ -static inline BOOL is_pubchar(WCHAR ch) +BOOL is_pubchar(WCHAR ch) { return (ch == ' ') || (ch >= 'a' && ch <= 'z') || @@ -1504,7 +1506,7 @@ static inline BOOL is_pubchar(WCHAR ch) (ch == '_') || (ch == '\r') || (ch == '\n'); } -static inline BOOL is_namestartchar(WCHAR ch) +BOOL is_namestartchar(WCHAR ch) { return (ch == ':') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch >= 'a' && ch <= 'z') || @@ -1524,7 +1526,7 @@ static inline BOOL is_namestartchar(WCHAR ch) } /* [4 NS] NCName ::= Name - (Char* ':' Char*) */ -static inline BOOL is_ncnamechar(WCHAR ch) +BOOL is_ncnamechar(WCHAR ch) { return (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch >= 'a' && ch <= 'z') || @@ -1548,7 +1550,7 @@ static inline BOOL is_ncnamechar(WCHAR ch) (ch >= 0xfdf0 && ch <= 0xfffd); } -static inline BOOL is_namechar(WCHAR ch) +BOOL is_namechar(WCHAR ch) { return (ch == ':') || is_ncnamechar(ch); } @@ -1848,7 +1850,7 @@ static HRESULT reader_parse_externalid(xmlreader *reader) hr = reader_parse_pub_literal(reader, &pub); if (FAILED(hr)) return hr; - reader_init_cstrvalue(publicW, strlenW(publicW), &name); + reader_init_cstrvalue(publicW, lstrlenW(publicW), &name); hr = reader_add_attr(reader, NULL, &name, NULL, &pub, &position, 0); if (FAILED(hr)) return hr; @@ -1859,7 +1861,7 @@ static HRESULT reader_parse_externalid(xmlreader *reader) hr = reader_parse_sys_literal(reader, &sys); if (FAILED(hr)) return S_OK; - reader_init_cstrvalue(systemW, strlenW(systemW), &name); + reader_init_cstrvalue(systemW, lstrlenW(systemW), &name); hr = reader_add_attr(reader, NULL, &name, NULL, &sys, &position, 0); if (FAILED(hr)) return hr; @@ -1873,7 +1875,7 @@ static HRESULT reader_parse_externalid(xmlreader *reader) hr = reader_parse_sys_literal(reader, &sys); if (FAILED(hr)) return hr; - reader_init_cstrvalue(systemW, strlenW(systemW), &name); + reader_init_cstrvalue(systemW, lstrlenW(systemW), &name); return reader_add_attr(reader, NULL, &name, NULL, &sys, &position, 0); } @@ -3081,8 +3083,8 @@ static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface, if (!namespace_uri) namespace_uri = emptyW; - target_name_len = strlenW(local_name); - target_uri_len = strlenW(namespace_uri); + target_name_len = lstrlenW(local_name); + target_uri_len = lstrlenW(namespace_uri); LIST_FOR_EACH_ENTRY(attr, &This->attrs, struct attribute, entry) { @@ -3093,7 +3095,7 @@ static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface, reader_get_attribute_ns_uri(This, attr, &uri, &uri_len); if (name_len == target_name_len && uri_len == target_uri_len && - !strcmpW(name, local_name) && !strcmpW(uri, namespace_uri)) + !wcscmp(name, local_name) && !wcscmp(uri, namespace_uri)) { reader_set_current_attribute(This, attr); return S_OK; diff --git a/dll/win32/xmllite/writer.c b/dll/win32/xmllite/writer.c index 88f75b6e202..eb72436b5fc 100644 --- a/dll/win32/xmllite/writer.c +++ b/dll/win32/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 @@ -20,17 +20,22 @@ */ #define COBJMACROS +#include #include + #include "windef.h" #include "winbase.h" #include "objbase.h" #include "xmllite.h" #include "xmllite_private.h" +#ifdef __REACTOS__ +#include +#include +#endif #include "initguid.h" #include "wine/debug.h" #include "wine/list.h" -#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(xmllite); @@ -40,10 +45,8 @@ DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, static const WCHAR closeelementW[] = {'<','/'}; static const WCHAR closetagW[] = {' ','/','>'}; static const WCHAR closepiW[] = {'?','>'}; -static const WCHAR ltW[] = {'<'}; -static const WCHAR gtW[] = {'>'}; -static const WCHAR spaceW[] = {' '}; -static const WCHAR quoteW[] = {'"'}; +static const WCHAR xmlnsW[] = {' ','x','m','l','n','s'}; +static const WCHAR xmlnsuriW[] = {'h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','2','0','0','0','/','x','m','l','n','s','/',0}; struct output_buffer { @@ -75,6 +78,7 @@ typedef struct xml_encoding encoding; WCHAR *encoding_name; /* exactly as specified on output creation */ struct output_buffer buffer; + DWORD written : 1; } xmlwriteroutput; static const struct IUnknownVtbl xmlwriteroutputvtbl; @@ -84,6 +88,17 @@ struct element struct list entry; WCHAR *qname; unsigned int len; /* qname length in chars */ + struct list ns; +}; + +struct ns +{ + struct list entry; + WCHAR *prefix; + int prefix_len; + WCHAR *uri; + BOOL emitted; + struct element *element; }; typedef struct _xmlwriter @@ -98,9 +113,10 @@ typedef struct _xmlwriter BOOL omitxmldecl; XmlConformanceLevel conformance; XmlWriterState state; - BOOL bomwritten; - BOOL starttagopen; struct list elements; + DWORD bomwritten : 1; + DWORD starttagopen : 1; + DWORD textnode : 1; } xmlwriter; static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface) @@ -130,6 +146,9 @@ static const char *debugstr_writer_prop(XmlWriterProperty prop) return prop_names[prop]; } +static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding, + const WCHAR *encoding_name, xmlwriteroutput **out); + /* writer output memory allocation functions */ static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len) { @@ -147,12 +166,12 @@ static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, siz } /* writer memory allocation functions */ -static inline void *writer_alloc(xmlwriter *writer, size_t len) +static inline void *writer_alloc(const xmlwriter *writer, size_t len) { return m_alloc(writer->imalloc, len); } -static inline void writer_free(xmlwriter *writer, void *mem) +static inline void writer_free(const xmlwriter *writer, void *mem) { m_free(writer->imalloc, mem); } @@ -165,30 +184,52 @@ static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, con ret = writer_alloc(writer, sizeof(*ret)); if (!ret) return ret; - len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0; - len += strlenW(local); + len = prefix ? lstrlenW(prefix) + 1 /* ':' */ : 0; + len += lstrlenW(local); ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR)); ret->len = len; if (prefix) { static const WCHAR colonW[] = {':',0}; - strcpyW(ret->qname, prefix); - strcatW(ret->qname, colonW); + lstrcpyW(ret->qname, prefix); + lstrcatW(ret->qname, colonW); } else ret->qname[0] = 0; - strcatW(ret->qname, local); + lstrcatW(ret->qname, local); + list_init(&ret->ns); return ret; } -static void free_element(xmlwriter *writer, struct element *element) +static void writer_free_element(xmlwriter *writer, struct element *element) { + struct ns *ns, *ns2; + + LIST_FOR_EACH_ENTRY_SAFE(ns, ns2, &element->ns, struct ns, entry) + { + list_remove(&ns->entry); + writer_free(writer, ns->prefix); + writer_free(writer, ns->uri); + writer_free(writer, ns); + } + writer_free(writer, element->qname); writer_free(writer, element); } -static void push_element(xmlwriter *writer, struct element *element) +static void writer_free_element_stack(xmlwriter *writer) +{ + struct element *element, *element2; + + LIST_FOR_EACH_ENTRY_SAFE(element, element2, &writer->elements, struct element, entry) + { + list_remove(&element->entry); + writer_free_element(writer, element); + } +} + +static void writer_push_element(xmlwriter *writer, struct element *element) { list_add_head(&writer->elements, &element->entry); } @@ -203,6 +244,170 @@ static struct element *pop_element(xmlwriter *writer) return element; } +static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len) +{ + size_t size; + WCHAR *ret; + + if (!str) + return NULL; + + if (len == -1) + len = lstrlenW(str); + + size = (len + 1) * sizeof(WCHAR); + ret = writer_alloc(writer, size); + memcpy(ret, str, size); + return ret; +} + +static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str) +{ + return writer_strndupW(writer, str, -1); +} + +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 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 (!wcscmp(uri, ns->uri) && !wcscmp(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 (!wcscmp(ns->prefix, prefix)) + return ns; + } + else if (!wcscmp(uri, ns->uri)) + { + if (prefix && !*prefix) + return NULL; + if (!prefix || !wcscmp(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 is_valid_name(const WCHAR *str, unsigned int *out) +{ + unsigned int len = 1; + + *out = 0; + + if (!str || !*str) + return S_OK; + + if (!is_namestartchar(*str++)) + return WC_E_NAMECHARACTER; + + while (*str++) + { + if (!is_namechar(*str)) + return WC_E_NAMECHARACTER; + len++; + } + + *out = len; + return S_OK; +} + +static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out) +{ + unsigned int len = 0; + + *out = 0; + + if (!str || !*str) + return S_OK; + + while (*str) + { + if (!is_pubchar(*str++)) + return WC_E_PUBLICID; + len++; + } + + *out = len; + + return S_OK; +} + static HRESULT init_output_buffer(xmlwriteroutput *output) { struct output_buffer *buffer = &output->buffer; @@ -257,7 +462,7 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i if (buffer->codepage == 1200) { /* For UTF-16 encoding just copy. */ - length = len == -1 ? strlenW(data) : len; + length = len == -1 ? lstrlenW(data) : len; if (length) { length *= sizeof(WCHAR); @@ -280,28 +485,38 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL); buffer->written += len == -1 ? length-1 : length; } + output->written = length != 0; return S_OK; } +static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch) +{ + return write_output_buffer(output, &ch, 1); +} + 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); - write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW)); + write_output_buffer_char(output, '"'); + if (!is_empty_string(data)) + write_output_buffer(output, data, len); + write_output_buffer_char(output, '"'); return S_OK; } /* TODO: test if we need to validate char range */ -static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name) +static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len, + const WCHAR *local_name, int local_len) { - if (prefix) { - static const WCHAR colW[] = {':'}; - write_output_buffer(output, prefix, -1); - write_output_buffer(output, colW, ARRAY_SIZE(colW)); - } + assert(prefix_len >= 0 && local_len >= 0); + + if (prefix_len) + write_output_buffer(output, prefix, prefix_len); + + if (prefix_len && local_len) + write_output_buffer_char(output, ':'); - write_output_buffer(output, local_name, -1); + write_output_buffer(output, local_name, local_len); return S_OK; } @@ -416,13 +631,30 @@ static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone) return S_OK; } +static void writer_output_ns(xmlwriter *writer, struct element *element) +{ + struct ns *ns; + + 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_char(writer->output, '='); + write_output_buffer_quoted(writer->output, ns->uri, -1); + } +} + static HRESULT writer_close_starttag(xmlwriter *writer) { HRESULT hr; if (!writer->starttagopen) return S_OK; - hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW)); - writer->starttagopen = FALSE; + + writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry)); + hr = write_output_buffer_char(writer->output, '>'); + writer->starttagopen = 0; return hr; } @@ -443,15 +675,20 @@ static void write_node_indent(xmlwriter *writer) static const WCHAR crlfW[] = {'\r','\n'}; unsigned int indent_level = writer->indent_level; - if (!writer->indent) + if (!writer->indent || writer->textnode) + { + writer->textnode = 0; return; + } /* Do state check to prevent newline inserted after BOM. It is assumed that state does not change between writing BOM and inserting indentation. */ - if (writer->output->buffer.written && writer->state != XmlWriterState_Ready) + if (writer->output->written && writer->state != XmlWriterState_Ready) write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW)); while (indent_level--) write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW)); + + writer->textnode = 0; } static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject) @@ -493,17 +730,12 @@ static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface) TRACE("(%p)->(%u)\n", This, ref); if (ref == 0) { - struct element *element, *element2; IMalloc *imalloc = This->imalloc; writeroutput_flush_stream(This->output); if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface); - /* element stack */ - LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) { - list_remove(&element->entry); - free_element(This, element); - } + writer_free_element_stack(This); writer_free(This, This); if (imalloc) IMalloc_Release(imalloc); @@ -525,8 +757,10 @@ static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output) writeroutput_release_stream(This->output); IUnknown_Release(&This->output->IXmlWriterOutput_iface); This->output = NULL; - This->bomwritten = FALSE; + This->bomwritten = 0; + This->textnode = 0; This->indent_level = 0; + writer_free_element_stack(This); } /* just reset current output */ @@ -549,10 +783,10 @@ static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output) } if (hr != S_OK || !writeroutput) { - /* create IXmlWriterOutput basing on supplied interface */ - hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput); - if (hr != S_OK) return hr; - This->output = impl_from_IXmlWriterOutput(writeroutput); + /* Create output for given stream. */ + hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output); + if (hr != S_OK) + return hr; } if (This->output->encoding == XmlEncoding_Unknown) @@ -627,14 +861,39 @@ 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_char(writer->output, ' '); + write_output_qname(writer->output, prefix, prefix_len, local, local_len); + write_output_buffer_char(writer->output, '='); + write_output_buffer_quoted(writer->output, value, -1); +} + +static BOOL is_valid_xml_space_value(const WCHAR *value) { - static const WCHAR eqW[] = {'=','"'}; + static const WCHAR preserveW[] = {'p','r','e','s','e','r','v','e',0}; + static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0}; + + if (!value) + return FALSE; + + return !wcscmp(value, preserveW) || !wcscmp(value, defaultW); +} + +static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix, + LPCWSTR local, LPCWSTR uri, LPCWSTR value) +{ + static const WCHAR spaceattrW[] = {'s','p','a','c','e',0}; + static const WCHAR xmlnsW[] = {'x','m','l','n','s',0}; + static const WCHAR xmlW[] = {'x','m','l',0}; xmlwriter *This = impl_from_IXmlWriter(iface); + BOOL is_xmlns_prefix, is_xmlns_local; + int prefix_len, local_len; + 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) { @@ -650,17 +909,83 @@ static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ; } - if (ns_prefix || ns_uri) + /* Prefix "xmlns" */ + is_xmlns_prefix = prefix && !wcscmp(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; + + is_xmlns_local = !wcscmp(local, xmlnsW); + + /* Trivial case, no prefix. */ + if (prefix_len == 0 && is_empty_string(uri)) + { + write_output_attribute(This, prefix, prefix_len, local, local_len, value); + return S_OK; + } + + /* Predefined "xml" prefix. */ + if (prefix_len && !wcscmp(prefix, xmlW)) { - FIXME("namespaces are not supported.\n"); - return E_NOTIMPL; + /* Valid "space" value is enforced. */ + if (!wcscmp(local, spaceattrW) && !is_valid_xml_space_value(value)) + return WR_E_INVALIDXMLSPACE; + + /* Redefinition is not allowed. */ + if (!is_empty_string(uri)) + return WR_E_XMLPREFIXDECLARATION; + + write_output_attribute(This, prefix, prefix_len, local, local_len, value); + + return S_OK; + } + + if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(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; } - 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(This->output, value, -1); - write_output_buffer(This->output, quoteW, ARRAY_SIZE(quoteW)); + /* Ignore prefix is URI wasn't specified. */ + if (is_xmlns_local && is_empty_string(uri)) + { + write_output_attribute(This, NULL, 0, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, 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; } @@ -699,7 +1024,7 @@ static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data) ; } - len = data ? strlenW(data) : 0; + len = data ? lstrlenW(data) : 0; write_node_indent(This); if (!len) @@ -707,7 +1032,7 @@ static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data) else { static const WCHAR cdatacloseW[] = {']',']','>',0}; while (len) { - const WCHAR *str = strstrW(data, cdatacloseW); + const WCHAR *str = wcsstr(data, cdatacloseW); if (str) { str += 2; write_cdata_section(This->output, data, str - data); @@ -747,7 +1072,7 @@ static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch) ; } - sprintfW(bufW, fmtW, ch); + swprintf(bufW, fmtW, ch); write_output_buffer(This->output, bufW, -1); return S_OK; @@ -801,7 +1126,7 @@ static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment) write_node_indent(This); write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW)); if (comment) { - int len = strlenW(comment), i; + int len = lstrlenW(comment), i; /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant comment string */ @@ -809,35 +1134,92 @@ static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment) for (i = 0; i < len; i++) { write_output_buffer(This->output, comment + i, 1); if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-') - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); } } else write_output_buffer(This->output, comment, len); if (len && comment[len-1] == '-') - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); } write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW)); return S_OK; } -static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId, - LPCWSTR pwszSystemId, LPCWSTR pwszSubset) +static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid, + LPCWSTR sysid, LPCWSTR subset) { + static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '}; + static const WCHAR publicW[] = {' ','P','U','B','L','I','C',' '}; + static const WCHAR systemW[] = {' ','S','Y','S','T','E','M',' '}; xmlwriter *This = impl_from_IXmlWriter(iface); + unsigned int name_len, pubid_len; + HRESULT hr; - FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId), - wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset)); + TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid), + wine_dbgstr_w(subset)); - return E_NOTIMPL; + switch (This->state) + { + case XmlWriterState_Initial: + return E_UNEXPECTED; + case XmlWriterState_InvalidEncoding: + return MX_E_ENCODING; + case XmlWriterState_Content: + case XmlWriterState_DocClosed: + return WR_E_INVALIDACTION; + default: + ; + } + + if (is_empty_string(name)) + return E_INVALIDARG; + + if (FAILED(hr = is_valid_name(name, &name_len))) + return hr; + + if (FAILED(hr = is_valid_pubid(pubid, &pubid_len))) + return hr; + + write_output_buffer(This->output, doctypeW, ARRAY_SIZE(doctypeW)); + write_output_buffer(This->output, name, name_len); + + if (pubid) + { + write_output_buffer(This->output, publicW, ARRAY_SIZE(publicW)); + write_output_buffer_quoted(This->output, pubid, pubid_len); + write_output_buffer_char(This->output, ' '); + write_output_buffer_quoted(This->output, sysid, -1); + } + else if (sysid) + { + write_output_buffer(This->output, systemW, ARRAY_SIZE(systemW)); + write_output_buffer_quoted(This->output, sysid, -1); + } + + if (subset) + { + write_output_buffer_char(This->output, ' '); + write_output_buffer_char(This->output, '['); + write_output_buffer(This->output, subset, -1); + write_output_buffer_char(This->output, ']'); + } + write_output_buffer_char(This->output, '>'); + + This->state = XmlWriterState_Content; + + return S_OK; } static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri, LPCWSTR value) { xmlwriter *This = impl_from_IXmlWriter(iface); + int prefix_len, local_len; + struct ns *ns; + HRESULT hr; TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri), wine_dbgstr_w(value)); @@ -857,18 +1239,52 @@ static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR pr ; } + if (!local_name) + 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_name, &local_len))) + return hr; + + ns = writer_find_ns(This, prefix, uri); + if (!ns && !is_empty_string(prefix) && is_empty_string(uri)) + return WR_E_NSPREFIXWITHEMPTYNSURI; + + if (uri && !wcscmp(uri, xmlnsuriW)) + { + if (!prefix) + return WR_E_XMLNSPREFIXDECLARATION; + + if (!is_empty_string(prefix)) + return WR_E_XMLNSURIDECLARATION; + } + write_encoding_bom(This); write_node_indent(This); - write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW)); - write_output_qname(This->output, prefix, local_name); + + write_output_buffer_char(This->output, '<'); + if (ns) + write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len); + else + write_output_qname(This->output, prefix, prefix_len, local_name, local_len); + + if (!ns && (prefix_len || !is_empty_string(uri))) + { + write_output_qname(This->output, xmlnsW, ARRAY_SIZE(xmlnsW), prefix, prefix_len); + write_output_buffer_char(This->output, '='); + write_output_buffer_quoted(This->output, uri, -1); + } if (value) { - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); write_output_buffer(This->output, value, -1); write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); - write_output_qname(This->output, prefix, local_name); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_qname(This->output, prefix, prefix_len, local_name, local_len); + write_output_buffer_char(This->output, '>'); } else write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW)); @@ -935,16 +1351,19 @@ static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface) if (This->starttagopen) { + writer_output_ns(This, element); write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW)); - This->starttagopen = FALSE; + This->starttagopen = 0; } - else { - /* write full end tag */ + else + { + /* Write full end tag. */ write_node_indent(This); write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); write_output_buffer(This->output, element->qname, element->len); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); } + writer_free_element(This, element); return S_OK; } @@ -987,6 +1406,9 @@ static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface) return WR_E_INVALIDACTION; case XmlWriterState_InvalidEncoding: return MX_E_ENCODING; + case XmlWriterState_ElemStarted: + writer_close_starttag(This); + break; default: ; } @@ -995,19 +1417,23 @@ static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface) if (!element) return WR_E_INVALIDACTION; - writer_close_starttag(This); writer_dec_indent(This); /* don't force full end tag to the next line */ if (This->state == XmlWriterState_ElemStarted) + { This->state = XmlWriterState_Content; + This->textnode = 0; + } else write_node_indent(This); /* write full end tag */ write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); write_output_buffer(This->output, element->qname, element->len); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); + + writer_free_element(This, element); return S_OK; } @@ -1094,7 +1520,7 @@ static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LP case XmlWriterState_InvalidEncoding: return MX_E_ENCODING; case XmlWriterState_DocStarted: - if (!strcmpW(name, xmlW)) + if (!wcscmp(name, xmlW)) return WR_E_INVALIDACTION; break; case XmlWriterState_ElemStarted: @@ -1108,11 +1534,11 @@ static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LP write_node_indent(This); write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW)); write_output_buffer(This->output, name, -1); - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); write_output_buffer(This->output, text, -1); write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW)); - if (!strcmpW(name, xmlW)) + if (!wcscmp(name, xmlW)) This->state = XmlWriterState_PIDocStarted; return S_OK; @@ -1219,7 +1645,10 @@ static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandal static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri) { xmlwriter *This = impl_from_IXmlWriter(iface); + int prefix_len, local_len; struct element *element; + struct ns *ns; + HRESULT hr; TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri)); @@ -1234,13 +1663,30 @@ static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR pre return MX_E_ENCODING; case XmlWriterState_DocClosed: return WR_E_INVALIDACTION; + case XmlWriterState_ElemStarted: + writer_close_starttag(This); + break; default: ; } - /* close pending element */ - if (This->starttagopen) - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + /* Validate prefix and local name */ + if (FAILED(hr = is_valid_ncname(prefix, &prefix_len))) + return hr; + + if (FAILED(hr = is_valid_ncname(local_name, &local_len))) + return hr; + + if (uri && !wcscmp(uri, xmlnsuriW)) + { + if (!prefix) + return WR_E_XMLNSPREFIXDECLARATION; + + if (!is_empty_string(prefix)) + return WR_E_XMLNSURIDECLARATION; + } + + ns = writer_find_ns(This, prefix, uri); element = alloc_element(This, prefix, local_name); if (!element) @@ -1250,12 +1696,18 @@ static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR pre write_node_indent(This); This->state = XmlWriterState_ElemStarted; - This->starttagopen = TRUE; + This->starttagopen = 1; + + writer_push_element(This, element); - push_element(This, element); + if (!ns && uri) + writer_push_ns(This, prefix, prefix_len, uri); - write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW)); - write_output_qname(This->output, prefix, local_name); + write_output_buffer_char(This->output, '<'); + if (ns) + write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len); + else + write_output_qname(This->output, prefix, prefix_len, local_name, local_len); writer_inc_indent(This); return S_OK; @@ -1314,6 +1766,7 @@ static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *stri ; } + This->textnode = 1; write_escaped_string(This, string); return S_OK; } @@ -1452,21 +1905,18 @@ HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc) writer = IMalloc_Alloc(imalloc, sizeof(*writer)); else writer = heap_alloc(sizeof(*writer)); - if(!writer) return E_OUTOFMEMORY; + if (!writer) + return E_OUTOFMEMORY; + + memset(writer, 0, sizeof(*writer)); writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl; writer->ref = 1; writer->imalloc = imalloc; if (imalloc) IMalloc_AddRef(imalloc); - writer->output = NULL; - writer->indent_level = 0; - writer->indent = FALSE; writer->bom = TRUE; - writer->omitxmldecl = FALSE; writer->conformance = XmlConformanceLevel_Document; writer->state = XmlWriterState_Initial; - writer->bomwritten = FALSE; - writer->starttagopen = FALSE; list_init(&writer->elements); hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj); @@ -1478,12 +1928,12 @@ HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc) } static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding, - const WCHAR *encoding_name, IXmlWriterOutput **output) + const WCHAR *encoding_name, xmlwriteroutput **out) { xmlwriteroutput *writeroutput; HRESULT hr; - *output = NULL; + *out = NULL; if (imalloc) writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput)); @@ -1506,49 +1956,61 @@ static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_enco } if (encoding_name) { - unsigned int size = (strlenW(encoding_name) + 1) * sizeof(WCHAR); + unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR); writeroutput->encoding_name = writeroutput_alloc(writeroutput, size); memcpy(writeroutput->encoding_name, encoding_name, size); } else writeroutput->encoding_name = NULL; + writeroutput->written = 0; IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output); - *output = &writeroutput->IXmlWriterOutput_iface; + *out = writeroutput; - TRACE("returning iface %p\n", *output); + TRACE("Created writer output %p\n", *out); return S_OK; } -HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, - IMalloc *imalloc, - LPCWSTR encoding, - IXmlWriterOutput **output) +HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding, + IXmlWriterOutput **out) { - static const WCHAR utf8W[] = {'U','T','F','-','8',0}; + xmlwriteroutput *output; xml_encoding xml_enc; + HRESULT hr; + + TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out); + + if (!stream || !out) + return E_INVALIDARG; - TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output); + *out = NULL; - if (!stream || !output) return E_INVALIDARG; + xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8; + if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output))) + *out = &output->IXmlWriterOutput_iface; - xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1); - return create_writer_output(stream, imalloc, xml_enc, encoding, output); + return hr; } -HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, - IMalloc *imalloc, - UINT codepage, - IXmlWriterOutput **output) +HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage, + IXmlWriterOutput **out) { + xmlwriteroutput *output; xml_encoding xml_enc; + HRESULT hr; - TRACE("%p %p %u %p\n", stream, imalloc, codepage, output); + TRACE("%p %p %u %p\n", stream, imalloc, codepage, out); - if (!stream || !output) return E_INVALIDARG; + if (!stream || !out) + return E_INVALIDARG; + + *out = NULL; xml_enc = get_encoding_from_codepage(codepage); - return create_writer_output(stream, imalloc, xml_enc, NULL, output); + if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output))) + *out = &output->IXmlWriterOutput_iface; + + return hr; } diff --git a/dll/win32/xmllite/xmllite_private.h b/dll/win32/xmllite/xmllite_private.h index 7884e27453d..10f43760b41 100644 --- a/dll/win32/xmllite/xmllite_private.h +++ b/dll/win32/xmllite/xmllite_private.h @@ -60,4 +60,9 @@ HRESULT get_code_page(xml_encoding,UINT*) DECLSPEC_HIDDEN; const WCHAR *get_encoding_name(xml_encoding) DECLSPEC_HIDDEN; xml_encoding get_encoding_from_codepage(UINT) DECLSPEC_HIDDEN; +BOOL is_ncnamechar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_pubchar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_namestartchar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_namechar(WCHAR ch) DECLSPEC_HIDDEN; + #endif /* __XMLLITE_PRIVATE__ */ diff --git a/media/doc/README.WINE b/media/doc/README.WINE index bbdab7f171d..64ee6000989 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -219,7 +219,7 @@ dll/win32/xinput1_1 # Synced to WineStaging-2.9 dll/win32/xinput1_2 # Synced to WineStaging-2.9 dll/win32/xinput1_3 # Synced to WineStaging-2.9 dll/win32/xinput9_1_0 # Synced to WineStaging-2.9 -dll/win32/xmllite # Synced to WineStaging-3.3 +dll/win32/xmllite # Synced to WineStaging-4.18 dll/win32/xolehlp # Synced to WineStaging-3.21 dll/cpl/inetcpl # Synced to WineStaging-3.17 -- 2.17.1