comctl32/tests: Use CRT allocation functions.
[wine.git] / dlls / xmllite / writer.c
blob128d666e82c829a2e606a19323ac558aed9a4c99
1 /*
2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014-2018 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "xmllite.h"
30 #include "xmllite_private.h"
31 #include "initguid.h"
33 #include "wine/debug.h"
34 #include "wine/list.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
38 /* not defined in public headers */
39 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
41 static const WCHAR xmlnsuriW[] = L"http://www.w3.org/2000/xmlns/";
43 struct output_buffer
45 char *data;
46 unsigned int allocated;
47 unsigned int written;
48 UINT codepage;
51 typedef enum
53 XmlWriterState_Initial, /* output is not set yet */
54 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
55 XmlWriterState_InvalidEncoding, /* SetOutput() was called, but output had invalid encoding */
56 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
57 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
58 XmlWriterState_ElemStarted, /* writing element */
59 XmlWriterState_Content, /* content is accepted at this point */
60 XmlWriterState_DocClosed /* WriteEndDocument was called */
61 } XmlWriterState;
63 typedef struct
65 IXmlWriterOutput IXmlWriterOutput_iface;
66 LONG ref;
67 IUnknown *output;
68 ISequentialStream *stream;
69 IMalloc *imalloc;
70 xml_encoding encoding;
71 WCHAR *encoding_name; /* exactly as specified on output creation */
72 struct output_buffer buffer;
73 DWORD written : 1;
74 } xmlwriteroutput;
76 static const struct IUnknownVtbl xmlwriteroutputvtbl;
78 struct element
80 struct list entry;
81 WCHAR *qname;
82 unsigned int len; /* qname length in chars */
83 struct list ns;
86 struct ns
88 struct list entry;
89 WCHAR *prefix;
90 int prefix_len;
91 WCHAR *uri;
92 BOOL emitted;
93 struct element *element;
96 typedef struct _xmlwriter
98 IXmlWriter IXmlWriter_iface;
99 LONG ref;
100 IMalloc *imalloc;
101 xmlwriteroutput *output;
102 unsigned int indent_level;
103 BOOL indent;
104 BOOL bom;
105 BOOL omitxmldecl;
106 XmlConformanceLevel conformance;
107 XmlWriterState state;
108 struct list elements;
109 DWORD bomwritten : 1;
110 DWORD starttagopen : 1;
111 DWORD textnode : 1;
112 } xmlwriter;
114 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
116 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
119 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
121 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
124 static const char *debugstr_writer_prop(XmlWriterProperty prop)
126 static const char * const prop_names[] =
128 "MultiLanguage",
129 "Indent",
130 "ByteOrderMark",
131 "OmitXmlDeclaration",
132 "ConformanceLevel"
135 if (prop > _XmlWriterProperty_Last)
136 return wine_dbg_sprintf("unknown property=%d", prop);
138 return prop_names[prop];
141 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
142 const WCHAR *encoding_name, xmlwriteroutput **out);
144 /* writer output memory allocation functions */
145 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
147 return m_alloc(output->imalloc, len);
150 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
152 m_free(output->imalloc, mem);
155 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
157 return m_realloc(output->imalloc, mem, len);
160 /* writer memory allocation functions */
161 static inline void *writer_alloc(const xmlwriter *writer, size_t len)
163 return m_alloc(writer->imalloc, len);
166 static inline void writer_free(const xmlwriter *writer, void *mem)
168 m_free(writer->imalloc, mem);
171 static BOOL is_empty_string(const WCHAR *str)
173 return !str || !*str;
176 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
178 struct element *ret;
179 int len;
181 ret = writer_alloc(writer, sizeof(*ret));
182 if (!ret) return ret;
184 len = is_empty_string(prefix) ? 0 : lstrlenW(prefix) + 1 /* ':' */;
185 len += lstrlenW(local);
187 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
189 if (!ret->qname)
191 writer_free(writer, ret);
192 return NULL;
195 ret->len = len;
196 if (is_empty_string(prefix))
197 ret->qname[0] = 0;
198 else
200 lstrcpyW(ret->qname, prefix);
201 lstrcatW(ret->qname, L":");
203 lstrcatW(ret->qname, local);
204 list_init(&ret->ns);
206 return ret;
209 static void writer_free_element(xmlwriter *writer, struct element *element)
211 struct ns *ns, *ns2;
213 LIST_FOR_EACH_ENTRY_SAFE(ns, ns2, &element->ns, struct ns, entry)
215 list_remove(&ns->entry);
216 writer_free(writer, ns->prefix);
217 writer_free(writer, ns->uri);
218 writer_free(writer, ns);
221 writer_free(writer, element->qname);
222 writer_free(writer, element);
225 static void writer_free_element_stack(xmlwriter *writer)
227 struct element *element, *element2;
229 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &writer->elements, struct element, entry)
231 list_remove(&element->entry);
232 writer_free_element(writer, element);
236 static void writer_push_element(xmlwriter *writer, struct element *element)
238 list_add_head(&writer->elements, &element->entry);
241 static struct element *pop_element(xmlwriter *writer)
243 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
245 if (element)
246 list_remove(&element->entry);
248 return element;
251 static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len)
253 WCHAR *ret;
255 if (!str)
256 return NULL;
258 if (len == -1)
259 len = lstrlenW(str);
261 ret = writer_alloc(writer, (len + 1) * sizeof(WCHAR));
262 if (ret)
264 memcpy(ret, str, len * sizeof(WCHAR));
265 ret[len] = 0;
268 return ret;
271 static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
273 return writer_strndupW(writer, str, -1);
276 static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
278 struct element *element;
279 struct ns *ns;
281 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
282 if (!element)
283 return NULL;
285 if ((ns = writer_alloc(writer, sizeof(*ns))))
287 ns->prefix = writer_strndupW(writer, prefix, prefix_len);
288 ns->prefix_len = prefix_len;
289 ns->uri = writer_strdupW(writer, uri);
290 ns->emitted = FALSE;
291 ns->element = element;
292 list_add_tail(&element->ns, &ns->entry);
295 return ns;
298 static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
300 struct element *element;
301 struct ns *ns;
303 if (is_empty_string(prefix) || is_empty_string(uri))
304 return NULL;
306 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
308 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
310 if (!wcscmp(uri, ns->uri) && !wcscmp(prefix, ns->prefix))
311 return ns;
314 return NULL;
317 static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
319 struct element *element;
320 struct ns *ns;
322 if (is_empty_string(prefix) && is_empty_string(uri))
323 return NULL;
325 LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
327 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
329 if (!uri)
331 if (!ns->prefix) continue;
332 if (!wcscmp(ns->prefix, prefix))
333 return ns;
335 else if (!wcscmp(uri, ns->uri))
337 if (prefix && !*prefix)
338 return NULL;
339 if (!prefix || !wcscmp(prefix, ns->prefix))
340 return ns;
345 return NULL;
348 static HRESULT is_valid_ncname(const WCHAR *str, int *out)
350 int len = 0;
352 *out = 0;
354 if (!str || !*str)
355 return S_OK;
357 while (*str)
359 if (!is_ncnamechar(*str))
360 return WC_E_NAMECHARACTER;
361 len++;
362 str++;
365 *out = len;
366 return S_OK;
369 static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
371 unsigned int len = 1;
373 *out = 0;
375 if (!str || !*str)
376 return S_OK;
378 if (!is_namestartchar(*str++))
379 return WC_E_NAMECHARACTER;
381 while (*str)
383 if (!is_namechar(*str))
384 return WC_E_NAMECHARACTER;
385 len++;
386 str++;
389 *out = len;
390 return S_OK;
393 static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
395 unsigned int len = 0;
397 *out = 0;
399 if (!str || !*str)
400 return S_OK;
402 while (*str)
404 if (!is_pubchar(*str++))
405 return WC_E_PUBLICID;
406 len++;
409 *out = len;
411 return S_OK;
414 static HRESULT init_output_buffer(xmlwriteroutput *output)
416 struct output_buffer *buffer = &output->buffer;
417 const int initial_len = 0x2000;
418 UINT cp = ~0u;
419 HRESULT hr;
421 if (FAILED(hr = get_code_page(output->encoding, &cp)))
422 WARN("Failed to get code page for specified encoding.\n");
424 buffer->data = writeroutput_alloc(output, initial_len);
425 if (!buffer->data) return E_OUTOFMEMORY;
427 memset(buffer->data, 0, 4);
428 buffer->allocated = initial_len;
429 buffer->written = 0;
430 buffer->codepage = cp;
432 return S_OK;
435 static void free_output_buffer(xmlwriteroutput *output)
437 struct output_buffer *buffer = &output->buffer;
438 writeroutput_free(output, buffer->data);
439 buffer->data = NULL;
440 buffer->allocated = 0;
441 buffer->written = 0;
444 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
446 struct output_buffer *buffer = &output->buffer;
447 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
448 if (buffer->allocated < buffer->written + length + 4) {
449 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
450 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
451 if (!ptr) return E_OUTOFMEMORY;
452 buffer->data = ptr;
453 buffer->allocated = grown_size;
456 return S_OK;
459 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
461 struct output_buffer *buffer = &output->buffer;
462 int length;
463 HRESULT hr;
464 char *ptr;
466 if (buffer->codepage == 1200) {
467 /* For UTF-16 encoding just copy. */
468 length = len == -1 ? lstrlenW(data) : len;
469 if (length) {
470 length *= sizeof(WCHAR);
472 hr = grow_output_buffer(output, length);
473 if (FAILED(hr)) return hr;
474 ptr = buffer->data + buffer->written;
476 memcpy(ptr, data, length);
477 buffer->written += length;
478 ptr += length;
479 /* null termination */
480 *(WCHAR*)ptr = 0;
483 else {
484 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
485 hr = grow_output_buffer(output, length);
486 if (FAILED(hr)) return hr;
487 ptr = buffer->data + buffer->written;
488 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
489 buffer->written += len == -1 ? length-1 : length;
491 output->written = length != 0;
493 return S_OK;
496 static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch)
498 return write_output_buffer(output, &ch, 1);
501 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
503 write_output_buffer_char(output, '"');
504 if (!is_empty_string(data))
505 write_output_buffer(output, data, len);
506 write_output_buffer_char(output, '"');
507 return S_OK;
510 /* TODO: test if we need to validate char range */
511 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len,
512 const WCHAR *local_name, int local_len)
514 assert(prefix_len >= 0 && local_len >= 0);
516 if (prefix_len)
517 write_output_buffer(output, prefix, prefix_len);
519 if (prefix_len && local_len)
520 write_output_buffer_char(output, ':');
522 write_output_buffer(output, local_name, local_len);
524 return S_OK;
527 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
529 if (writeroutput->stream) {
530 ISequentialStream_Release(writeroutput->stream);
531 writeroutput->stream = NULL;
535 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
537 HRESULT hr;
539 writeroutput_release_stream(writeroutput);
540 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
541 if (hr != S_OK)
542 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
544 return hr;
547 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
549 struct output_buffer *buffer;
550 ULONG written, offset = 0;
551 HRESULT hr;
553 if (!output || !output->stream)
554 return S_OK;
556 buffer = &output->buffer;
558 /* It will loop forever until everything is written or an error occurred. */
559 do {
560 written = 0;
561 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
562 if (FAILED(hr)) {
563 WARN("write to stream failed %#lx.\n", hr);
564 buffer->written = 0;
565 return hr;
568 offset += written;
569 buffer->written -= written;
570 } while (buffer->written > 0);
572 return S_OK;
575 static HRESULT write_encoding_bom(xmlwriter *writer)
577 if (!writer->bom || writer->bomwritten) return S_OK;
579 if (writer->output->encoding == XmlEncoding_UTF16) {
580 static const char utf16bom[] = {0xff, 0xfe};
581 struct output_buffer *buffer = &writer->output->buffer;
582 int len = sizeof(utf16bom);
583 HRESULT hr;
585 hr = grow_output_buffer(writer->output, len);
586 if (FAILED(hr)) return hr;
587 memcpy(buffer->data + buffer->written, utf16bom, len);
588 buffer->written += len;
591 writer->bomwritten = TRUE;
592 return S_OK;
595 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
597 if (output->encoding_name)
598 return output->encoding_name;
600 return get_encoding_name(output->encoding);
603 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
605 write_encoding_bom(writer);
606 writer->state = XmlWriterState_DocStarted;
607 if (writer->omitxmldecl) return S_OK;
609 /* version */
610 write_output_buffer(writer->output, L"<?xml version=\"1.0\"", 19);
612 /* encoding */
613 write_output_buffer(writer->output, L" encoding=", 10);
614 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
616 /* standalone */
617 if (standalone == XmlStandalone_Omit)
618 write_output_buffer(writer->output, L"?>", 2);
619 else
621 write_output_buffer(writer->output, L" standalone=\"", 13);
622 if (standalone == XmlStandalone_Yes)
623 write_output_buffer(writer->output, L"yes\"?>", 6);
624 else
625 write_output_buffer(writer->output, L"no\"?>", 5);
628 return S_OK;
631 static void writer_output_ns(xmlwriter *writer, struct element *element)
633 struct ns *ns;
635 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
637 if (ns->emitted)
638 continue;
640 write_output_qname(writer->output, L" xmlns", 6, ns->prefix, ns->prefix_len);
641 write_output_buffer_char(writer->output, '=');
642 write_output_buffer_quoted(writer->output, ns->uri, -1);
646 static HRESULT writer_close_starttag(xmlwriter *writer)
648 HRESULT hr;
650 if (!writer->starttagopen) return S_OK;
652 writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry));
653 hr = write_output_buffer_char(writer->output, '>');
654 writer->starttagopen = 0;
655 return hr;
658 static void writer_inc_indent(xmlwriter *writer)
660 writer->indent_level++;
663 static void writer_dec_indent(xmlwriter *writer)
665 if (writer->indent_level)
666 writer->indent_level--;
669 static void write_node_indent(xmlwriter *writer)
671 unsigned int indent_level = writer->indent_level;
673 if (!writer->indent || writer->textnode)
675 writer->textnode = 0;
676 return;
679 /* Do state check to prevent newline inserted after BOM. It is assumed that
680 state does not change between writing BOM and inserting indentation. */
681 if (writer->output->written && writer->state != XmlWriterState_Ready)
682 write_output_buffer(writer->output, L"\r\n", 2);
683 while (indent_level--)
684 write_output_buffer(writer->output, L" ", 2);
686 writer->textnode = 0;
689 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
691 xmlwriter *This = impl_from_IXmlWriter(iface);
693 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
695 if (IsEqualGUID(riid, &IID_IXmlWriter) ||
696 IsEqualGUID(riid, &IID_IUnknown))
698 *ppvObject = iface;
700 else
702 FIXME("interface %s is not supported\n", debugstr_guid(riid));
703 *ppvObject = NULL;
704 return E_NOINTERFACE;
707 IXmlWriter_AddRef(iface);
709 return S_OK;
712 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
714 xmlwriter *writer = impl_from_IXmlWriter(iface);
715 ULONG ref = InterlockedIncrement(&writer->ref);
716 TRACE("%p, refcount %lu.\n", iface, ref);
717 return ref;
720 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
722 xmlwriter *writer = impl_from_IXmlWriter(iface);
723 ULONG ref = InterlockedDecrement(&writer->ref);
725 TRACE("%p, refcount %lu.\n", iface, ref);
727 if (!ref)
729 IMalloc *imalloc = writer->imalloc;
731 writeroutput_flush_stream(writer->output);
732 if (writer->output)
733 IUnknown_Release(&writer->output->IXmlWriterOutput_iface);
735 writer_free_element_stack(writer);
737 writer_free(writer, writer);
738 if (imalloc) IMalloc_Release(imalloc);
741 return ref;
744 /*** IXmlWriter methods ***/
745 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
747 xmlwriter *This = impl_from_IXmlWriter(iface);
748 IXmlWriterOutput *writeroutput;
749 HRESULT hr;
751 TRACE("(%p)->(%p)\n", This, output);
753 if (This->output) {
754 writeroutput_release_stream(This->output);
755 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
756 This->output = NULL;
757 This->bomwritten = 0;
758 This->textnode = 0;
759 This->indent_level = 0;
760 writer_free_element_stack(This);
763 /* just reset current output */
764 if (!output) {
765 This->state = XmlWriterState_Initial;
766 return S_OK;
769 /* now try IXmlWriterOutput, ISequentialStream, IStream */
770 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
771 if (hr == S_OK) {
772 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
773 This->output = impl_from_IXmlWriterOutput(writeroutput);
774 else {
775 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
776 writeroutput, writeroutput->lpVtbl);
777 IUnknown_Release(writeroutput);
778 return E_FAIL;
782 if (hr != S_OK || !writeroutput) {
783 /* Create output for given stream. */
784 hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
785 if (hr != S_OK)
786 return hr;
789 if (This->output->encoding == XmlEncoding_Unknown)
790 This->state = XmlWriterState_InvalidEncoding;
791 else
792 This->state = XmlWriterState_Ready;
793 return writeroutput_query_for_stream(This->output);
796 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
798 xmlwriter *This = impl_from_IXmlWriter(iface);
800 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
802 if (!value) return E_INVALIDARG;
804 switch (property)
806 case XmlWriterProperty_Indent:
807 *value = This->indent;
808 break;
809 case XmlWriterProperty_ByteOrderMark:
810 *value = This->bom;
811 break;
812 case XmlWriterProperty_OmitXmlDeclaration:
813 *value = This->omitxmldecl;
814 break;
815 case XmlWriterProperty_ConformanceLevel:
816 *value = This->conformance;
817 break;
818 default:
819 FIXME("Unimplemented property (%u)\n", property);
820 return E_NOTIMPL;
823 return S_OK;
826 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
828 xmlwriter *writer = impl_from_IXmlWriter(iface);
830 TRACE("%p, %s, %Id.\n", iface, debugstr_writer_prop(property), value);
832 switch (property)
834 case XmlWriterProperty_Indent:
835 writer->indent = !!value;
836 break;
837 case XmlWriterProperty_ByteOrderMark:
838 writer->bom = !!value;
839 break;
840 case XmlWriterProperty_OmitXmlDeclaration:
841 writer->omitxmldecl = !!value;
842 break;
843 default:
844 FIXME("Unimplemented property (%u)\n", property);
845 return E_NOTIMPL;
848 return S_OK;
851 static HRESULT writer_write_attribute(IXmlWriter *writer, IXmlReader *reader, BOOL write_default_attributes)
853 const WCHAR *prefix, *local, *uri, *value;
854 HRESULT hr;
856 if (IXmlReader_IsDefault(reader) && !write_default_attributes)
857 return S_OK;
859 if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
860 if (FAILED(hr = IXmlReader_GetLocalName(reader, &local, NULL))) return hr;
861 if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
862 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
863 return IXmlWriter_WriteAttributeString(writer, prefix, local, uri, value);
866 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
868 XmlNodeType node_type;
869 HRESULT hr = S_OK;
871 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
873 if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;
875 switch (node_type)
877 case XmlNodeType_Element:
878 case XmlNodeType_XmlDeclaration:
879 case XmlNodeType_Attribute:
880 if (node_type != XmlNodeType_Attribute)
882 if (FAILED(hr = IXmlReader_MoveToFirstAttribute(reader))) return hr;
883 if (hr == S_FALSE) return S_OK;
885 if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) return hr;
886 while (IXmlReader_MoveToNextAttribute(reader) == S_OK)
888 if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) break;
890 if (node_type != XmlNodeType_Attribute && SUCCEEDED(hr))
891 hr = IXmlReader_MoveToElement(reader);
892 break;
893 default:
894 WARN("Unexpected node type %d.\n", node_type);
895 return E_UNEXPECTED;
898 return hr;
901 static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
902 const WCHAR *local, int local_len, const WCHAR *value)
904 write_output_buffer_char(writer->output, ' ');
905 write_output_qname(writer->output, prefix, prefix_len, local, local_len);
906 write_output_buffer_char(writer->output, '=');
907 write_output_buffer_quoted(writer->output, value, -1);
910 static BOOL is_valid_xml_space_value(const WCHAR *value)
912 return value && (!wcscmp(value, L"preserve") || !wcscmp(value, L"default"));
915 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
916 LPCWSTR local, LPCWSTR uri, LPCWSTR value)
918 xmlwriter *writer = impl_from_IXmlWriter(iface);
919 BOOL is_xmlns_prefix, is_xmlns_local;
920 int prefix_len, local_len;
921 struct ns *ns;
922 HRESULT hr;
924 TRACE("%p, %s, %s, %s, %s.\n", iface, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
926 switch (writer->state)
928 case XmlWriterState_Initial:
929 return E_UNEXPECTED;
930 case XmlWriterState_Ready:
931 case XmlWriterState_DocClosed:
932 writer->state = XmlWriterState_DocClosed;
933 return WR_E_INVALIDACTION;
934 case XmlWriterState_InvalidEncoding:
935 return MX_E_ENCODING;
936 default:
940 /* Prefix "xmlns" */
941 is_xmlns_prefix = prefix && !wcscmp(prefix, L"xmlns");
942 if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
943 return WR_E_NSPREFIXDECLARED;
945 if (is_empty_string(local))
946 return E_INVALIDARG;
948 /* Validate prefix and local name */
949 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
950 return hr;
952 if (FAILED(hr = is_valid_ncname(local, &local_len)))
953 return hr;
955 is_xmlns_local = !wcscmp(local, L"xmlns");
957 /* Trivial case, no prefix. */
958 if (prefix_len == 0 && is_empty_string(uri))
960 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
961 return S_OK;
964 /* Predefined "xml" prefix. */
965 if (prefix_len && !wcscmp(prefix, L"xml"))
967 /* Valid "space" value is enforced. */
968 if (!wcscmp(local, L"space") && !is_valid_xml_space_value(value))
969 return WR_E_INVALIDXMLSPACE;
971 /* Redefinition is not allowed. */
972 if (!is_empty_string(uri))
973 return WR_E_XMLPREFIXDECLARATION;
975 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
977 return S_OK;
980 if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
982 if (prefix_len && !is_empty_string(uri))
983 return WR_E_XMLNSPREFIXDECLARATION;
985 /* Look for exact match defined in current element, and write it out. */
986 if (!(ns = writer_find_ns_current(writer, prefix, value)))
987 ns = writer_push_ns(writer, local, local_len, value);
988 ns->emitted = TRUE;
990 write_output_attribute(writer, L"xmlns", 5, local, local_len, value);
992 return S_OK;
995 /* Ignore prefix if URI wasn't specified. */
996 if (is_xmlns_local && is_empty_string(uri))
998 write_output_attribute(writer, NULL, 0, L"xmlns", 5, value);
999 return S_OK;
1002 if (!(ns = writer_find_ns(writer, prefix, uri)))
1004 if (is_empty_string(prefix) && !is_empty_string(uri))
1006 FIXME("Prefix autogeneration is not implemented.\n");
1007 return E_NOTIMPL;
1009 if (!is_empty_string(uri))
1010 ns = writer_push_ns(writer, prefix, prefix_len, uri);
1013 if (ns)
1014 write_output_attribute(writer, ns->prefix, ns->prefix_len, local, local_len, value);
1015 else
1016 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
1018 return S_OK;
1021 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
1023 write_output_buffer(output, L"<![CDATA[", 9);
1024 if (data)
1025 write_output_buffer(output, data, len);
1026 write_output_buffer(output, L"]]>", 3);
1029 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
1031 xmlwriter *This = impl_from_IXmlWriter(iface);
1032 int len;
1034 TRACE("%p %s\n", This, debugstr_w(data));
1036 switch (This->state)
1038 case XmlWriterState_Initial:
1039 return E_UNEXPECTED;
1040 case XmlWriterState_ElemStarted:
1041 writer_close_starttag(This);
1042 break;
1043 case XmlWriterState_Ready:
1044 case XmlWriterState_DocClosed:
1045 This->state = XmlWriterState_DocClosed;
1046 return WR_E_INVALIDACTION;
1047 case XmlWriterState_InvalidEncoding:
1048 return MX_E_ENCODING;
1049 default:
1053 len = data ? lstrlenW(data) : 0;
1055 write_node_indent(This);
1056 if (!len)
1057 write_cdata_section(This->output, NULL, 0);
1058 else
1060 while (len)
1062 const WCHAR *str = wcsstr(data, L"]]>");
1063 if (str) {
1064 str += 2;
1065 write_cdata_section(This->output, data, str - data);
1066 len -= str - data;
1067 data = str;
1069 else {
1070 write_cdata_section(This->output, data, len);
1071 break;
1076 return S_OK;
1079 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1081 xmlwriter *This = impl_from_IXmlWriter(iface);
1082 WCHAR bufW[16];
1084 TRACE("%p %#x\n", This, ch);
1086 switch (This->state)
1088 case XmlWriterState_Initial:
1089 return E_UNEXPECTED;
1090 case XmlWriterState_InvalidEncoding:
1091 return MX_E_ENCODING;
1092 case XmlWriterState_ElemStarted:
1093 writer_close_starttag(This);
1094 break;
1095 case XmlWriterState_DocClosed:
1096 return WR_E_INVALIDACTION;
1097 default:
1101 swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%x;", ch);
1102 write_output_buffer(This->output, bufW, -1);
1104 return S_OK;
1107 static HRESULT writer_get_next_write_count(const WCHAR *str, unsigned int length, unsigned int *count)
1109 if (!is_char(*str)) return WC_E_XMLCHARACTER;
1111 if (IS_HIGH_SURROGATE(*str))
1113 if (length < 2 || !IS_LOW_SURROGATE(*(str + 1)))
1114 return WR_E_INVALIDSURROGATEPAIR;
1116 *count = 2;
1118 else if (IS_LOW_SURROGATE(*str))
1119 return WR_E_INVALIDSURROGATEPAIR;
1120 else
1121 *count = 1;
1123 return S_OK;
1126 static HRESULT write_escaped_char(xmlwriter *writer, const WCHAR *string, unsigned int count)
1128 HRESULT hr;
1130 switch (*string)
1132 case '<':
1133 hr = write_output_buffer(writer->output, L"&lt;", 4);
1134 break;
1135 case '&':
1136 hr = write_output_buffer(writer->output, L"&amp;", 5);
1137 break;
1138 case '>':
1139 hr = write_output_buffer(writer->output, L"&gt;", 4);
1140 break;
1141 default:
1142 hr = write_output_buffer(writer->output, string, count);
1145 return hr;
1148 static HRESULT write_escaped_string(xmlwriter *writer, const WCHAR *string, unsigned int length)
1150 unsigned int count;
1151 HRESULT hr = S_OK;
1153 if (length == ~0u)
1155 while (*string)
1157 if (FAILED(hr = writer_get_next_write_count(string, ~0u, &count))) return hr;
1158 if (FAILED(hr = write_escaped_char(writer, string, count))) return hr;
1160 string += count;
1163 else
1165 while (length)
1167 if (FAILED(hr = writer_get_next_write_count(string, length, &count))) return hr;
1168 if (FAILED(hr = write_escaped_char(writer, string, count))) return hr;
1170 string += count;
1171 length -= count;
1175 return hr;
1178 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *characters, UINT length)
1180 xmlwriter *writer = impl_from_IXmlWriter(iface);
1182 TRACE("%p, %s, %d.\n", iface, debugstr_wn(characters, length), length);
1184 if ((characters == NULL && length != 0))
1185 return E_INVALIDARG;
1187 if (length == 0)
1188 return S_OK;
1190 switch (writer->state)
1192 case XmlWriterState_Initial:
1193 return E_UNEXPECTED;
1194 case XmlWriterState_InvalidEncoding:
1195 return MX_E_ENCODING;
1196 case XmlWriterState_ElemStarted:
1197 writer_close_starttag(writer);
1198 break;
1199 case XmlWriterState_Ready:
1200 case XmlWriterState_DocClosed:
1201 writer->state = XmlWriterState_DocClosed;
1202 return WR_E_INVALIDACTION;
1203 default:
1207 writer->textnode = 1;
1208 return write_escaped_string(writer, characters, length);
1211 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1213 xmlwriter *This = impl_from_IXmlWriter(iface);
1215 TRACE("%p %s\n", This, debugstr_w(comment));
1217 switch (This->state)
1219 case XmlWriterState_Initial:
1220 return E_UNEXPECTED;
1221 case XmlWriterState_InvalidEncoding:
1222 return MX_E_ENCODING;
1223 case XmlWriterState_ElemStarted:
1224 writer_close_starttag(This);
1225 break;
1226 case XmlWriterState_DocClosed:
1227 return WR_E_INVALIDACTION;
1228 default:
1232 write_node_indent(This);
1233 write_output_buffer(This->output, L"<!--", 4);
1234 if (comment) {
1235 int len = lstrlenW(comment), i;
1237 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1238 comment string */
1239 if (len > 1) {
1240 for (i = 0; i < len; i++) {
1241 write_output_buffer(This->output, comment + i, 1);
1242 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1243 write_output_buffer_char(This->output, ' ');
1246 else
1247 write_output_buffer(This->output, comment, len);
1249 if (len && comment[len-1] == '-')
1250 write_output_buffer_char(This->output, ' ');
1252 write_output_buffer(This->output, L"-->", 3);
1254 return S_OK;
1257 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
1258 LPCWSTR sysid, LPCWSTR subset)
1260 xmlwriter *This = impl_from_IXmlWriter(iface);
1261 unsigned int name_len, pubid_len;
1262 HRESULT hr;
1264 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
1265 wine_dbgstr_w(subset));
1267 switch (This->state)
1269 case XmlWriterState_Initial:
1270 return E_UNEXPECTED;
1271 case XmlWriterState_InvalidEncoding:
1272 return MX_E_ENCODING;
1273 case XmlWriterState_Content:
1274 case XmlWriterState_DocClosed:
1275 return WR_E_INVALIDACTION;
1276 default:
1280 if (is_empty_string(name))
1281 return E_INVALIDARG;
1283 if (FAILED(hr = is_valid_name(name, &name_len)))
1284 return hr;
1286 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1287 return hr;
1289 write_output_buffer(This->output, L"<!DOCTYPE ", 10);
1290 write_output_buffer(This->output, name, name_len);
1292 if (pubid)
1294 write_output_buffer(This->output, L" PUBLIC ", 8);
1295 write_output_buffer_quoted(This->output, pubid, pubid_len);
1296 write_output_buffer_char(This->output, ' ');
1297 write_output_buffer_quoted(This->output, sysid, -1);
1299 else if (sysid)
1301 write_output_buffer(This->output, L" SYSTEM ", 8);
1302 write_output_buffer_quoted(This->output, sysid, -1);
1305 if (subset)
1307 write_output_buffer_char(This->output, ' ');
1308 write_output_buffer_char(This->output, '[');
1309 write_output_buffer(This->output, subset, -1);
1310 write_output_buffer_char(This->output, ']');
1312 write_output_buffer_char(This->output, '>');
1314 This->state = XmlWriterState_Content;
1316 return S_OK;
1319 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
1320 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1322 xmlwriter *This = impl_from_IXmlWriter(iface);
1323 int prefix_len, local_len;
1324 struct ns *ns;
1325 HRESULT hr;
1327 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
1328 wine_dbgstr_w(uri), wine_dbgstr_w(value));
1330 switch (This->state)
1332 case XmlWriterState_Initial:
1333 return E_UNEXPECTED;
1334 case XmlWriterState_InvalidEncoding:
1335 return MX_E_ENCODING;
1336 case XmlWriterState_ElemStarted:
1337 writer_close_starttag(This);
1338 break;
1339 case XmlWriterState_DocClosed:
1340 return WR_E_INVALIDACTION;
1341 default:
1345 if (!local_name)
1346 return E_INVALIDARG;
1348 /* Validate prefix and local name */
1349 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1350 return hr;
1352 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1353 return hr;
1355 ns = writer_find_ns(This, prefix, uri);
1356 if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
1357 return WR_E_NSPREFIXWITHEMPTYNSURI;
1359 if (uri && !wcscmp(uri, xmlnsuriW))
1361 if (!prefix)
1362 return WR_E_XMLNSPREFIXDECLARATION;
1364 if (!is_empty_string(prefix))
1365 return WR_E_XMLNSURIDECLARATION;
1368 write_encoding_bom(This);
1369 write_node_indent(This);
1371 write_output_buffer_char(This->output, '<');
1372 if (ns)
1373 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1374 else
1375 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1377 if (!ns && (prefix_len || !is_empty_string(uri)))
1379 write_output_qname(This->output, L" xmlns", 6, prefix, prefix_len);
1380 write_output_buffer_char(This->output, '=');
1381 write_output_buffer_quoted(This->output, uri, -1);
1384 if (value)
1386 write_output_buffer_char(This->output, '>');
1387 write_output_buffer(This->output, value, -1);
1388 write_output_buffer(This->output, L"</", 2);
1389 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1390 write_output_buffer_char(This->output, '>');
1392 else
1393 write_output_buffer(This->output, L" />", 3);
1395 This->state = XmlWriterState_Content;
1397 return S_OK;
1400 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
1402 xmlwriter *This = impl_from_IXmlWriter(iface);
1404 TRACE("%p\n", This);
1406 switch (This->state)
1408 case XmlWriterState_Initial:
1409 return E_UNEXPECTED;
1410 case XmlWriterState_Ready:
1411 case XmlWriterState_DocClosed:
1412 This->state = XmlWriterState_DocClosed;
1413 return WR_E_INVALIDACTION;
1414 case XmlWriterState_InvalidEncoding:
1415 return MX_E_ENCODING;
1416 default:
1420 /* empty element stack */
1421 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1424 This->state = XmlWriterState_DocClosed;
1425 return S_OK;
1428 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
1430 xmlwriter *This = impl_from_IXmlWriter(iface);
1431 struct element *element;
1433 TRACE("%p\n", This);
1435 switch (This->state)
1437 case XmlWriterState_Initial:
1438 return E_UNEXPECTED;
1439 case XmlWriterState_Ready:
1440 case XmlWriterState_DocClosed:
1441 This->state = XmlWriterState_DocClosed;
1442 return WR_E_INVALIDACTION;
1443 case XmlWriterState_InvalidEncoding:
1444 return MX_E_ENCODING;
1445 default:
1449 element = pop_element(This);
1450 if (!element)
1451 return WR_E_INVALIDACTION;
1453 writer_dec_indent(This);
1455 if (This->starttagopen)
1457 writer_output_ns(This, element);
1458 write_output_buffer(This->output, L" />", 3);
1459 This->starttagopen = 0;
1461 else
1463 /* Write full end tag. */
1464 write_node_indent(This);
1465 write_output_buffer(This->output, L"</", 2);
1466 write_output_buffer(This->output, element->qname, element->len);
1467 write_output_buffer_char(This->output, '>');
1469 writer_free_element(This, element);
1471 return S_OK;
1474 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
1476 xmlwriter *This = impl_from_IXmlWriter(iface);
1478 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1480 switch (This->state)
1482 case XmlWriterState_Initial:
1483 return E_UNEXPECTED;
1484 case XmlWriterState_InvalidEncoding:
1485 return MX_E_ENCODING;
1486 case XmlWriterState_DocClosed:
1487 return WR_E_INVALIDACTION;
1488 default:
1492 return E_NOTIMPL;
1495 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
1497 xmlwriter *This = impl_from_IXmlWriter(iface);
1498 struct element *element;
1500 TRACE("%p\n", This);
1502 switch (This->state)
1504 case XmlWriterState_Initial:
1505 return E_UNEXPECTED;
1506 case XmlWriterState_Ready:
1507 case XmlWriterState_DocClosed:
1508 This->state = XmlWriterState_DocClosed;
1509 return WR_E_INVALIDACTION;
1510 case XmlWriterState_InvalidEncoding:
1511 return MX_E_ENCODING;
1512 case XmlWriterState_ElemStarted:
1513 writer_close_starttag(This);
1514 break;
1515 default:
1519 element = pop_element(This);
1520 if (!element)
1521 return WR_E_INVALIDACTION;
1523 writer_dec_indent(This);
1525 /* don't force full end tag to the next line */
1526 if (This->state == XmlWriterState_ElemStarted)
1528 This->state = XmlWriterState_Content;
1529 This->textnode = 0;
1531 else
1532 write_node_indent(This);
1534 /* write full end tag */
1535 write_output_buffer(This->output, L"</", 2);
1536 write_output_buffer(This->output, element->qname, element->len);
1537 write_output_buffer_char(This->output, '>');
1539 writer_free_element(This, element);
1541 return S_OK;
1544 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1546 xmlwriter *This = impl_from_IXmlWriter(iface);
1548 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1550 switch (This->state)
1552 case XmlWriterState_Initial:
1553 return E_UNEXPECTED;
1554 case XmlWriterState_Ready:
1555 case XmlWriterState_DocClosed:
1556 This->state = XmlWriterState_DocClosed;
1557 return WR_E_INVALIDACTION;
1558 case XmlWriterState_InvalidEncoding:
1559 return MX_E_ENCODING;
1560 default:
1564 return E_NOTIMPL;
1567 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1569 xmlwriter *This = impl_from_IXmlWriter(iface);
1571 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1573 switch (This->state)
1575 case XmlWriterState_Initial:
1576 return E_UNEXPECTED;
1577 case XmlWriterState_Ready:
1578 case XmlWriterState_DocClosed:
1579 This->state = XmlWriterState_DocClosed;
1580 return WR_E_INVALIDACTION;
1581 case XmlWriterState_InvalidEncoding:
1582 return MX_E_ENCODING;
1583 default:
1587 return E_NOTIMPL;
1590 static HRESULT writer_write_node(IXmlWriter *writer, IXmlReader *reader, BOOL shallow, BOOL write_default_attributes)
1592 XmlStandalone standalone = XmlStandalone_Omit;
1593 const WCHAR *name, *value, *prefix, *uri;
1594 unsigned int start_depth = 0, depth;
1595 XmlNodeType node_type;
1596 HRESULT hr;
1598 if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;
1600 switch (node_type)
1602 case XmlNodeType_None:
1603 if (shallow) return S_OK;
1604 while ((hr = IXmlReader_Read(reader, NULL)) == S_OK)
1606 if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1608 break;
1609 case XmlNodeType_Element:
1610 if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
1611 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1612 if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
1613 if (FAILED(hr = IXmlWriter_WriteStartElement(writer, prefix, name, uri))) return hr;
1614 if (FAILED(hr = IXmlWriter_WriteAttributes(writer, reader, write_default_attributes))) return hr;
1615 if (IXmlReader_IsEmptyElement(reader))
1617 hr = IXmlWriter_WriteEndElement(writer);
1619 else
1621 if (shallow) return S_OK;
1622 if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;
1623 if (FAILED(hr = IXmlReader_GetDepth(reader, &start_depth))) return hr;
1624 while ((hr = IXmlReader_Read(reader, &node_type)) == S_OK)
1626 if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1627 if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;
1629 depth = 0;
1630 if (FAILED(hr = IXmlReader_GetDepth(reader, &depth))) return hr;
1631 if (node_type == XmlNodeType_EndElement && (start_depth == depth - 1)) break;
1634 break;
1635 case XmlNodeType_Attribute:
1636 break;
1637 case XmlNodeType_Text:
1638 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1639 hr = IXmlWriter_WriteRaw(writer, value);
1640 break;
1641 case XmlNodeType_CDATA:
1642 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1643 hr = IXmlWriter_WriteCData(writer, value);
1644 break;
1645 case XmlNodeType_ProcessingInstruction:
1646 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1647 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1648 hr = IXmlWriter_WriteProcessingInstruction(writer, name, value);
1649 break;
1650 case XmlNodeType_Comment:
1651 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1652 hr = IXmlWriter_WriteComment(writer, value);
1653 break;
1654 case XmlNodeType_Whitespace:
1655 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1656 hr = IXmlWriter_WriteWhitespace(writer, value);
1657 break;
1658 case XmlNodeType_EndElement:
1659 hr = IXmlWriter_WriteFullEndElement(writer);
1660 break;
1661 case XmlNodeType_XmlDeclaration:
1662 while ((hr = IXmlReader_MoveToNextAttribute(reader)) == S_OK)
1664 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1665 if (!wcscmp(name, L"standalone"))
1667 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1668 standalone = !wcscmp(value, L"yes") ? XmlStandalone_Yes : XmlStandalone_No;
1671 if (SUCCEEDED(hr))
1672 hr = IXmlWriter_WriteStartDocument(writer, standalone);
1673 break;
1674 default:
1675 WARN("Unknown node type %d.\n", node_type);
1676 return E_UNEXPECTED;
1679 return hr;
1682 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
1684 HRESULT hr;
1686 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
1688 if (!reader)
1689 return E_INVALIDARG;
1691 if (SUCCEEDED(hr = writer_write_node(iface, reader, FALSE, write_default_attributes)))
1692 hr = IXmlReader_Read(reader, NULL);
1694 return hr;
1697 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
1699 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
1701 if (!reader)
1702 return E_INVALIDARG;
1704 return writer_write_node(iface, reader, TRUE, write_default_attributes);
1707 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1708 LPCWSTR text)
1710 xmlwriter *This = impl_from_IXmlWriter(iface);
1712 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1714 switch (This->state)
1716 case XmlWriterState_Initial:
1717 return E_UNEXPECTED;
1718 case XmlWriterState_InvalidEncoding:
1719 return MX_E_ENCODING;
1720 case XmlWriterState_DocStarted:
1721 if (!wcscmp(name, L"xml"))
1722 return WR_E_INVALIDACTION;
1723 break;
1724 case XmlWriterState_ElemStarted:
1725 writer_close_starttag(This);
1726 break;
1727 case XmlWriterState_DocClosed:
1728 return WR_E_INVALIDACTION;
1729 default:
1733 write_encoding_bom(This);
1734 write_node_indent(This);
1735 write_output_buffer(This->output, L"<?", 2);
1736 write_output_buffer(This->output, name, -1);
1737 write_output_buffer_char(This->output, ' ');
1738 write_output_buffer(This->output, text, -1);
1739 write_output_buffer(This->output, L"?>", 2);
1741 if (!wcscmp(name, L"xml"))
1742 This->state = XmlWriterState_PIDocStarted;
1744 return S_OK;
1747 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1748 LPCWSTR pwszNamespaceUri)
1750 xmlwriter *This = impl_from_IXmlWriter(iface);
1752 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1754 switch (This->state)
1756 case XmlWriterState_Initial:
1757 return E_UNEXPECTED;
1758 case XmlWriterState_InvalidEncoding:
1759 return MX_E_ENCODING;
1760 case XmlWriterState_DocClosed:
1761 return WR_E_INVALIDACTION;
1762 default:
1766 return E_NOTIMPL;
1769 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1771 xmlwriter *This = impl_from_IXmlWriter(iface);
1772 unsigned int count;
1773 HRESULT hr = S_OK;
1775 TRACE("%p %s\n", This, debugstr_w(data));
1777 if (!data)
1778 return S_OK;
1780 switch (This->state)
1782 case XmlWriterState_Initial:
1783 return E_UNEXPECTED;
1784 case XmlWriterState_Ready:
1785 write_xmldecl(This, XmlStandalone_Omit);
1786 /* fallthrough */
1787 case XmlWriterState_DocStarted:
1788 case XmlWriterState_PIDocStarted:
1789 break;
1790 case XmlWriterState_InvalidEncoding:
1791 return MX_E_ENCODING;
1792 case XmlWriterState_ElemStarted:
1793 writer_close_starttag(This);
1794 break;
1795 default:
1796 This->state = XmlWriterState_DocClosed;
1797 return WR_E_INVALIDACTION;
1800 while (*data)
1802 if (FAILED(hr = writer_get_next_write_count(data, ~0u, &count))) return hr;
1803 if (FAILED(hr = write_output_buffer(This->output, data, count))) return hr;
1805 data += count;
1808 return hr;
1811 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *characters, UINT length)
1813 xmlwriter *writer = impl_from_IXmlWriter(iface);
1814 HRESULT hr = S_OK;
1815 unsigned int count;
1817 TRACE("%p, %s, %d.\n", iface, debugstr_wn(characters, length), length);
1819 if ((characters == NULL && length != 0))
1820 return E_INVALIDARG;
1822 if (length == 0)
1823 return S_OK;
1825 switch (writer->state)
1827 case XmlWriterState_Initial:
1828 return E_UNEXPECTED;
1829 case XmlWriterState_InvalidEncoding:
1830 return MX_E_ENCODING;
1831 case XmlWriterState_DocClosed:
1832 return WR_E_INVALIDACTION;
1833 case XmlWriterState_Ready:
1834 write_xmldecl(writer, XmlStandalone_Omit);
1835 break;
1836 case XmlWriterState_ElemStarted:
1837 writer_close_starttag(writer);
1838 default:
1842 while (length)
1844 if (FAILED(hr = writer_get_next_write_count(characters, length, &count))) return hr;
1845 if (FAILED(hr = write_output_buffer(writer->output, characters, count))) return hr;
1847 characters += count;
1848 length -= count;
1851 return hr;
1854 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1856 xmlwriter *This = impl_from_IXmlWriter(iface);
1858 TRACE("(%p)->(%d)\n", This, standalone);
1860 switch (This->state)
1862 case XmlWriterState_Initial:
1863 return E_UNEXPECTED;
1864 case XmlWriterState_PIDocStarted:
1865 This->state = XmlWriterState_DocStarted;
1866 return S_OK;
1867 case XmlWriterState_Ready:
1868 break;
1869 case XmlWriterState_InvalidEncoding:
1870 return MX_E_ENCODING;
1871 default:
1872 This->state = XmlWriterState_DocClosed;
1873 return WR_E_INVALIDACTION;
1876 return write_xmldecl(This, standalone);
1879 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1881 xmlwriter *This = impl_from_IXmlWriter(iface);
1882 int prefix_len, local_len;
1883 struct element *element;
1884 struct ns *ns;
1885 HRESULT hr;
1887 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1889 if (!local_name)
1890 return E_INVALIDARG;
1892 switch (This->state)
1894 case XmlWriterState_Initial:
1895 return E_UNEXPECTED;
1896 case XmlWriterState_InvalidEncoding:
1897 return MX_E_ENCODING;
1898 case XmlWriterState_DocClosed:
1899 return WR_E_INVALIDACTION;
1900 case XmlWriterState_ElemStarted:
1901 writer_close_starttag(This);
1902 break;
1903 default:
1907 /* Validate prefix and local name */
1908 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1909 return hr;
1911 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1912 return hr;
1914 if (uri && !wcscmp(uri, xmlnsuriW))
1916 if (!prefix)
1917 return WR_E_XMLNSPREFIXDECLARATION;
1919 if (!is_empty_string(prefix))
1920 return WR_E_XMLNSURIDECLARATION;
1923 ns = writer_find_ns(This, prefix, uri);
1925 element = alloc_element(This, prefix, local_name);
1926 if (!element)
1927 return E_OUTOFMEMORY;
1929 write_encoding_bom(This);
1930 write_node_indent(This);
1932 This->state = XmlWriterState_ElemStarted;
1933 This->starttagopen = 1;
1935 writer_push_element(This, element);
1937 if (!ns && !is_empty_string(uri))
1938 writer_push_ns(This, prefix, prefix_len, uri);
1940 write_output_buffer_char(This->output, '<');
1941 if (ns)
1942 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1943 else
1944 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1945 writer_inc_indent(This);
1947 return S_OK;
1950 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1952 xmlwriter *This = impl_from_IXmlWriter(iface);
1954 TRACE("%p %s\n", This, debugstr_w(string));
1956 if (!string)
1957 return S_OK;
1959 switch (This->state)
1961 case XmlWriterState_Initial:
1962 return E_UNEXPECTED;
1963 case XmlWriterState_ElemStarted:
1964 writer_close_starttag(This);
1965 break;
1966 case XmlWriterState_Ready:
1967 case XmlWriterState_DocClosed:
1968 This->state = XmlWriterState_DocClosed;
1969 return WR_E_INVALIDACTION;
1970 case XmlWriterState_InvalidEncoding:
1971 return MX_E_ENCODING;
1972 default:
1976 This->textnode = 1;
1977 return write_escaped_string(This, string, ~0u);
1980 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1982 xmlwriter *writer = impl_from_IXmlWriter(iface);
1983 int codepoint;
1984 WCHAR bufW[16];
1986 TRACE("%p, %d, %d.\n", iface, wchLow, wchHigh);
1988 if (!IS_SURROGATE_PAIR(wchHigh, wchLow))
1989 return WC_E_XMLCHARACTER;
1991 switch (writer->state)
1993 case XmlWriterState_Initial:
1994 return E_UNEXPECTED;
1995 case XmlWriterState_InvalidEncoding:
1996 return MX_E_ENCODING;
1997 case XmlWriterState_ElemStarted:
1998 writer_close_starttag(writer);
1999 break;
2000 case XmlWriterState_DocClosed:
2001 return WR_E_INVALIDACTION;
2002 default:
2006 codepoint = ((wchHigh - 0xd800) * 0x400) + (wchLow - 0xdc00) + 0x10000;
2007 swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%X;", codepoint);
2008 write_output_buffer(writer->output, bufW, -1);
2010 return S_OK;
2013 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR text)
2015 xmlwriter *writer = impl_from_IXmlWriter(iface);
2016 size_t length = 0;
2018 TRACE("%p, %s.\n", iface, wine_dbgstr_w(text));
2020 switch (writer->state)
2022 case XmlWriterState_Initial:
2023 return E_UNEXPECTED;
2024 case XmlWriterState_ElemStarted:
2025 writer_close_starttag(writer);
2026 break;
2027 case XmlWriterState_InvalidEncoding:
2028 return MX_E_ENCODING;
2029 case XmlWriterState_Ready:
2030 break;
2031 default:
2032 return WR_E_INVALIDACTION;
2035 while (text[length])
2037 if (!is_wchar_space(text[length])) return WR_E_NONWHITESPACE;
2038 length++;
2041 write_output_buffer(writer->output, text, length);
2042 return S_OK;
2045 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
2047 xmlwriter *This = impl_from_IXmlWriter(iface);
2049 TRACE("%p\n", This);
2051 return writeroutput_flush_stream(This->output);
2054 static const struct IXmlWriterVtbl xmlwriter_vtbl =
2056 xmlwriter_QueryInterface,
2057 xmlwriter_AddRef,
2058 xmlwriter_Release,
2059 xmlwriter_SetOutput,
2060 xmlwriter_GetProperty,
2061 xmlwriter_SetProperty,
2062 xmlwriter_WriteAttributes,
2063 xmlwriter_WriteAttributeString,
2064 xmlwriter_WriteCData,
2065 xmlwriter_WriteCharEntity,
2066 xmlwriter_WriteChars,
2067 xmlwriter_WriteComment,
2068 xmlwriter_WriteDocType,
2069 xmlwriter_WriteElementString,
2070 xmlwriter_WriteEndDocument,
2071 xmlwriter_WriteEndElement,
2072 xmlwriter_WriteEntityRef,
2073 xmlwriter_WriteFullEndElement,
2074 xmlwriter_WriteName,
2075 xmlwriter_WriteNmToken,
2076 xmlwriter_WriteNode,
2077 xmlwriter_WriteNodeShallow,
2078 xmlwriter_WriteProcessingInstruction,
2079 xmlwriter_WriteQualifiedName,
2080 xmlwriter_WriteRaw,
2081 xmlwriter_WriteRawChars,
2082 xmlwriter_WriteStartDocument,
2083 xmlwriter_WriteStartElement,
2084 xmlwriter_WriteString,
2085 xmlwriter_WriteSurrogateCharEntity,
2086 xmlwriter_WriteWhitespace,
2087 xmlwriter_Flush
2090 /** IXmlWriterOutput **/
2091 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
2093 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
2095 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
2097 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
2098 IsEqualGUID(riid, &IID_IUnknown))
2100 *ppvObject = iface;
2102 else
2104 WARN("interface %s not implemented\n", debugstr_guid(riid));
2105 *ppvObject = NULL;
2106 return E_NOINTERFACE;
2109 IUnknown_AddRef(iface);
2111 return S_OK;
2114 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
2116 xmlwriteroutput *output = impl_from_IXmlWriterOutput(iface);
2117 ULONG ref = InterlockedIncrement(&output->ref);
2118 TRACE("%p, refcount %ld.\n", iface, ref);
2119 return ref;
2122 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
2124 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
2125 LONG ref = InterlockedDecrement(&This->ref);
2127 TRACE("%p, refcount %ld.\n", iface, ref);
2129 if (ref == 0)
2131 IMalloc *imalloc = This->imalloc;
2132 if (This->output) IUnknown_Release(This->output);
2133 if (This->stream) ISequentialStream_Release(This->stream);
2134 free_output_buffer(This);
2135 writeroutput_free(This, This->encoding_name);
2136 writeroutput_free(This, This);
2137 if (imalloc) IMalloc_Release(imalloc);
2140 return ref;
2143 static const struct IUnknownVtbl xmlwriteroutputvtbl =
2145 xmlwriteroutput_QueryInterface,
2146 xmlwriteroutput_AddRef,
2147 xmlwriteroutput_Release
2150 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
2152 xmlwriter *writer;
2153 HRESULT hr;
2155 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
2157 if (!(writer = m_alloc(imalloc, sizeof(*writer))))
2158 return E_OUTOFMEMORY;
2159 memset(writer, 0, sizeof(*writer));
2161 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
2162 writer->ref = 1;
2163 writer->imalloc = imalloc;
2164 if (imalloc) IMalloc_AddRef(imalloc);
2165 writer->bom = TRUE;
2166 writer->conformance = XmlConformanceLevel_Document;
2167 writer->state = XmlWriterState_Initial;
2168 list_init(&writer->elements);
2170 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
2171 IXmlWriter_Release(&writer->IXmlWriter_iface);
2173 TRACE("returning iface %p, hr %#lx.\n", *obj, hr);
2175 return hr;
2178 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
2179 const WCHAR *encoding_name, xmlwriteroutput **out)
2181 xmlwriteroutput *writeroutput;
2182 HRESULT hr;
2184 *out = NULL;
2186 if (!(writeroutput = m_alloc(imalloc, sizeof(*writeroutput))))
2187 return E_OUTOFMEMORY;
2188 memset(writeroutput, 0, sizeof(*writeroutput));
2190 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
2191 writeroutput->ref = 1;
2192 writeroutput->imalloc = imalloc;
2193 if (imalloc)
2194 IMalloc_AddRef(imalloc);
2195 writeroutput->encoding = encoding;
2196 hr = init_output_buffer(writeroutput);
2197 if (FAILED(hr)) {
2198 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
2199 return hr;
2202 if (encoding_name)
2204 unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
2205 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
2206 memcpy(writeroutput->encoding_name, encoding_name, size);
2209 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
2211 *out = writeroutput;
2213 TRACE("Created writer output %p\n", *out);
2215 return S_OK;
2218 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
2219 IXmlWriterOutput **out)
2221 xmlwriteroutput *output;
2222 xml_encoding xml_enc;
2223 HRESULT hr;
2225 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
2227 if (!stream || !out)
2228 return E_INVALIDARG;
2230 *out = NULL;
2232 xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
2233 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
2234 *out = &output->IXmlWriterOutput_iface;
2236 return hr;
2239 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
2240 IXmlWriterOutput **out)
2242 xmlwriteroutput *output;
2243 xml_encoding xml_enc;
2244 HRESULT hr;
2246 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
2248 if (!stream || !out)
2249 return E_INVALIDARG;
2251 *out = NULL;
2253 xml_enc = get_encoding_from_codepage(codepage);
2254 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
2255 *out = &output->IXmlWriterOutput_iface;
2257 return hr;