xmllite/writer: Implement WriteNodeShallow().
[wine.git] / dlls / xmllite / writer.c
blobfb2784ab43980abcf5ab034c7c8e5be78cccd6fa
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 size_t size;
254 WCHAR *ret;
256 if (!str)
257 return NULL;
259 if (len == -1)
260 len = lstrlenW(str);
262 size = (len + 1) * sizeof(WCHAR);
263 ret = writer_alloc(writer, size);
264 if (ret) memcpy(ret, str, size);
266 return ret;
269 static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
271 return writer_strndupW(writer, str, -1);
274 static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
276 struct element *element;
277 struct ns *ns;
279 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
280 if (!element)
281 return NULL;
283 if ((ns = writer_alloc(writer, sizeof(*ns))))
285 ns->prefix = writer_strndupW(writer, prefix, prefix_len);
286 ns->prefix_len = prefix_len;
287 ns->uri = writer_strdupW(writer, uri);
288 ns->emitted = FALSE;
289 ns->element = element;
290 list_add_tail(&element->ns, &ns->entry);
293 return ns;
296 static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
298 struct element *element;
299 struct ns *ns;
301 if (is_empty_string(prefix) || is_empty_string(uri))
302 return NULL;
304 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
306 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
308 if (!wcscmp(uri, ns->uri) && !wcscmp(prefix, ns->prefix))
309 return ns;
312 return NULL;
315 static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
317 struct element *element;
318 struct ns *ns;
320 if (is_empty_string(prefix) && is_empty_string(uri))
321 return NULL;
323 LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
325 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
327 if (!uri)
329 if (!ns->prefix) continue;
330 if (!wcscmp(ns->prefix, prefix))
331 return ns;
333 else if (!wcscmp(uri, ns->uri))
335 if (prefix && !*prefix)
336 return NULL;
337 if (!prefix || !wcscmp(prefix, ns->prefix))
338 return ns;
343 return NULL;
346 static HRESULT is_valid_ncname(const WCHAR *str, int *out)
348 int len = 0;
350 *out = 0;
352 if (!str || !*str)
353 return S_OK;
355 while (*str)
357 if (!is_ncnamechar(*str))
358 return WC_E_NAMECHARACTER;
359 len++;
360 str++;
363 *out = len;
364 return S_OK;
367 static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
369 unsigned int len = 1;
371 *out = 0;
373 if (!str || !*str)
374 return S_OK;
376 if (!is_namestartchar(*str++))
377 return WC_E_NAMECHARACTER;
379 while (*str++)
381 if (!is_namechar(*str))
382 return WC_E_NAMECHARACTER;
383 len++;
386 *out = len;
387 return S_OK;
390 static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
392 unsigned int len = 0;
394 *out = 0;
396 if (!str || !*str)
397 return S_OK;
399 while (*str)
401 if (!is_pubchar(*str++))
402 return WC_E_PUBLICID;
403 len++;
406 *out = len;
408 return S_OK;
411 static HRESULT init_output_buffer(xmlwriteroutput *output)
413 struct output_buffer *buffer = &output->buffer;
414 const int initial_len = 0x2000;
415 UINT cp = ~0u;
416 HRESULT hr;
418 if (FAILED(hr = get_code_page(output->encoding, &cp)))
419 WARN("Failed to get code page for specified encoding.\n");
421 buffer->data = writeroutput_alloc(output, initial_len);
422 if (!buffer->data) return E_OUTOFMEMORY;
424 memset(buffer->data, 0, 4);
425 buffer->allocated = initial_len;
426 buffer->written = 0;
427 buffer->codepage = cp;
429 return S_OK;
432 static void free_output_buffer(xmlwriteroutput *output)
434 struct output_buffer *buffer = &output->buffer;
435 writeroutput_free(output, buffer->data);
436 buffer->data = NULL;
437 buffer->allocated = 0;
438 buffer->written = 0;
441 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
443 struct output_buffer *buffer = &output->buffer;
444 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
445 if (buffer->allocated < buffer->written + length + 4) {
446 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
447 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
448 if (!ptr) return E_OUTOFMEMORY;
449 buffer->data = ptr;
450 buffer->allocated = grown_size;
453 return S_OK;
456 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
458 struct output_buffer *buffer = &output->buffer;
459 int length;
460 HRESULT hr;
461 char *ptr;
463 if (buffer->codepage == 1200) {
464 /* For UTF-16 encoding just copy. */
465 length = len == -1 ? lstrlenW(data) : len;
466 if (length) {
467 length *= sizeof(WCHAR);
469 hr = grow_output_buffer(output, length);
470 if (FAILED(hr)) return hr;
471 ptr = buffer->data + buffer->written;
473 memcpy(ptr, data, length);
474 buffer->written += length;
475 ptr += length;
476 /* null termination */
477 *(WCHAR*)ptr = 0;
480 else {
481 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
482 hr = grow_output_buffer(output, length);
483 if (FAILED(hr)) return hr;
484 ptr = buffer->data + buffer->written;
485 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
486 buffer->written += len == -1 ? length-1 : length;
488 output->written = length != 0;
490 return S_OK;
493 static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch)
495 return write_output_buffer(output, &ch, 1);
498 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
500 write_output_buffer_char(output, '"');
501 if (!is_empty_string(data))
502 write_output_buffer(output, data, len);
503 write_output_buffer_char(output, '"');
504 return S_OK;
507 /* TODO: test if we need to validate char range */
508 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len,
509 const WCHAR *local_name, int local_len)
511 assert(prefix_len >= 0 && local_len >= 0);
513 if (prefix_len)
514 write_output_buffer(output, prefix, prefix_len);
516 if (prefix_len && local_len)
517 write_output_buffer_char(output, ':');
519 write_output_buffer(output, local_name, local_len);
521 return S_OK;
524 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
526 if (writeroutput->stream) {
527 ISequentialStream_Release(writeroutput->stream);
528 writeroutput->stream = NULL;
532 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
534 HRESULT hr;
536 writeroutput_release_stream(writeroutput);
537 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
538 if (hr != S_OK)
539 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
541 return hr;
544 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
546 struct output_buffer *buffer;
547 ULONG written, offset = 0;
548 HRESULT hr;
550 if (!output || !output->stream)
551 return S_OK;
553 buffer = &output->buffer;
555 /* It will loop forever until everything is written or an error occurred. */
556 do {
557 written = 0;
558 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
559 if (FAILED(hr)) {
560 WARN("write to stream failed %#lx.\n", hr);
561 buffer->written = 0;
562 return hr;
565 offset += written;
566 buffer->written -= written;
567 } while (buffer->written > 0);
569 return S_OK;
572 static HRESULT write_encoding_bom(xmlwriter *writer)
574 if (!writer->bom || writer->bomwritten) return S_OK;
576 if (writer->output->encoding == XmlEncoding_UTF16) {
577 static const char utf16bom[] = {0xff, 0xfe};
578 struct output_buffer *buffer = &writer->output->buffer;
579 int len = sizeof(utf16bom);
580 HRESULT hr;
582 hr = grow_output_buffer(writer->output, len);
583 if (FAILED(hr)) return hr;
584 memcpy(buffer->data + buffer->written, utf16bom, len);
585 buffer->written += len;
588 writer->bomwritten = TRUE;
589 return S_OK;
592 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
594 if (output->encoding_name)
595 return output->encoding_name;
597 return get_encoding_name(output->encoding);
600 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
602 write_encoding_bom(writer);
603 writer->state = XmlWriterState_DocStarted;
604 if (writer->omitxmldecl) return S_OK;
606 /* version */
607 write_output_buffer(writer->output, L"<?xml version=\"1.0\"", 19);
609 /* encoding */
610 write_output_buffer(writer->output, L" encoding=", 10);
611 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
613 /* standalone */
614 if (standalone == XmlStandalone_Omit)
615 write_output_buffer(writer->output, L"?>", 2);
616 else
618 write_output_buffer(writer->output, L" standalone=\"", 13);
619 if (standalone == XmlStandalone_Yes)
620 write_output_buffer(writer->output, L"yes\"?>", 6);
621 else
622 write_output_buffer(writer->output, L"no\"?>", 5);
625 return S_OK;
628 static void writer_output_ns(xmlwriter *writer, struct element *element)
630 struct ns *ns;
632 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
634 if (ns->emitted)
635 continue;
637 write_output_qname(writer->output, L" xmlns", 6, ns->prefix, ns->prefix_len);
638 write_output_buffer_char(writer->output, '=');
639 write_output_buffer_quoted(writer->output, ns->uri, -1);
643 static HRESULT writer_close_starttag(xmlwriter *writer)
645 HRESULT hr;
647 if (!writer->starttagopen) return S_OK;
649 writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry));
650 hr = write_output_buffer_char(writer->output, '>');
651 writer->starttagopen = 0;
652 return hr;
655 static void writer_inc_indent(xmlwriter *writer)
657 writer->indent_level++;
660 static void writer_dec_indent(xmlwriter *writer)
662 if (writer->indent_level)
663 writer->indent_level--;
666 static void write_node_indent(xmlwriter *writer)
668 unsigned int indent_level = writer->indent_level;
670 if (!writer->indent || writer->textnode)
672 writer->textnode = 0;
673 return;
676 /* Do state check to prevent newline inserted after BOM. It is assumed that
677 state does not change between writing BOM and inserting indentation. */
678 if (writer->output->written && writer->state != XmlWriterState_Ready)
679 write_output_buffer(writer->output, L"\r\n", 2);
680 while (indent_level--)
681 write_output_buffer(writer->output, L" ", 2);
683 writer->textnode = 0;
686 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
688 xmlwriter *This = impl_from_IXmlWriter(iface);
690 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
692 if (IsEqualGUID(riid, &IID_IXmlWriter) ||
693 IsEqualGUID(riid, &IID_IUnknown))
695 *ppvObject = iface;
697 else
699 FIXME("interface %s is not supported\n", debugstr_guid(riid));
700 *ppvObject = NULL;
701 return E_NOINTERFACE;
704 IXmlWriter_AddRef(iface);
706 return S_OK;
709 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
711 xmlwriter *writer = impl_from_IXmlWriter(iface);
712 ULONG ref = InterlockedIncrement(&writer->ref);
713 TRACE("%p, refcount %lu.\n", iface, ref);
714 return ref;
717 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
719 xmlwriter *writer = impl_from_IXmlWriter(iface);
720 ULONG ref = InterlockedDecrement(&writer->ref);
722 TRACE("%p, refcount %lu.\n", iface, ref);
724 if (!ref)
726 IMalloc *imalloc = writer->imalloc;
728 writeroutput_flush_stream(writer->output);
729 if (writer->output)
730 IUnknown_Release(&writer->output->IXmlWriterOutput_iface);
732 writer_free_element_stack(writer);
734 writer_free(writer, writer);
735 if (imalloc) IMalloc_Release(imalloc);
738 return ref;
741 /*** IXmlWriter methods ***/
742 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
744 xmlwriter *This = impl_from_IXmlWriter(iface);
745 IXmlWriterOutput *writeroutput;
746 HRESULT hr;
748 TRACE("(%p)->(%p)\n", This, output);
750 if (This->output) {
751 writeroutput_release_stream(This->output);
752 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
753 This->output = NULL;
754 This->bomwritten = 0;
755 This->textnode = 0;
756 This->indent_level = 0;
757 writer_free_element_stack(This);
760 /* just reset current output */
761 if (!output) {
762 This->state = XmlWriterState_Initial;
763 return S_OK;
766 /* now try IXmlWriterOutput, ISequentialStream, IStream */
767 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
768 if (hr == S_OK) {
769 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
770 This->output = impl_from_IXmlWriterOutput(writeroutput);
771 else {
772 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
773 writeroutput, writeroutput->lpVtbl);
774 IUnknown_Release(writeroutput);
775 return E_FAIL;
779 if (hr != S_OK || !writeroutput) {
780 /* Create output for given stream. */
781 hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
782 if (hr != S_OK)
783 return hr;
786 if (This->output->encoding == XmlEncoding_Unknown)
787 This->state = XmlWriterState_InvalidEncoding;
788 else
789 This->state = XmlWriterState_Ready;
790 return writeroutput_query_for_stream(This->output);
793 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
795 xmlwriter *This = impl_from_IXmlWriter(iface);
797 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
799 if (!value) return E_INVALIDARG;
801 switch (property)
803 case XmlWriterProperty_Indent:
804 *value = This->indent;
805 break;
806 case XmlWriterProperty_ByteOrderMark:
807 *value = This->bom;
808 break;
809 case XmlWriterProperty_OmitXmlDeclaration:
810 *value = This->omitxmldecl;
811 break;
812 case XmlWriterProperty_ConformanceLevel:
813 *value = This->conformance;
814 break;
815 default:
816 FIXME("Unimplemented property (%u)\n", property);
817 return E_NOTIMPL;
820 return S_OK;
823 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
825 xmlwriter *writer = impl_from_IXmlWriter(iface);
827 TRACE("%p, %s, %Id.\n", iface, debugstr_writer_prop(property), value);
829 switch (property)
831 case XmlWriterProperty_Indent:
832 writer->indent = !!value;
833 break;
834 case XmlWriterProperty_ByteOrderMark:
835 writer->bom = !!value;
836 break;
837 case XmlWriterProperty_OmitXmlDeclaration:
838 writer->omitxmldecl = !!value;
839 break;
840 default:
841 FIXME("Unimplemented property (%u)\n", property);
842 return E_NOTIMPL;
845 return S_OK;
848 static HRESULT writer_write_attribute(IXmlWriter *writer, IXmlReader *reader, BOOL write_default_attributes)
850 const WCHAR *prefix, *local, *uri, *value;
851 HRESULT hr;
853 if (IXmlReader_IsDefault(reader) && !write_default_attributes)
854 return S_OK;
856 if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
857 if (FAILED(hr = IXmlReader_GetLocalName(reader, &local, NULL))) return hr;
858 if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
859 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
860 return IXmlWriter_WriteAttributeString(writer, prefix, local, uri, value);
863 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
865 XmlNodeType node_type;
866 HRESULT hr = S_OK;
868 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
870 if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;
872 switch (node_type)
874 case XmlNodeType_Element:
875 case XmlNodeType_XmlDeclaration:
876 case XmlNodeType_Attribute:
877 if (node_type != XmlNodeType_Attribute)
879 if (FAILED(hr = IXmlReader_MoveToFirstAttribute(reader))) return hr;
880 if (hr == S_FALSE) return S_OK;
882 if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) return hr;
883 while (IXmlReader_MoveToNextAttribute(reader) == S_OK)
885 if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) break;
887 if (node_type != XmlNodeType_Attribute && SUCCEEDED(hr))
888 hr = IXmlReader_MoveToElement(reader);
889 break;
890 default:
891 WARN("Unexpected node type %d.\n", node_type);
892 return E_UNEXPECTED;
895 return hr;
898 static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
899 const WCHAR *local, int local_len, const WCHAR *value)
901 write_output_buffer_char(writer->output, ' ');
902 write_output_qname(writer->output, prefix, prefix_len, local, local_len);
903 write_output_buffer_char(writer->output, '=');
904 write_output_buffer_quoted(writer->output, value, -1);
907 static BOOL is_valid_xml_space_value(const WCHAR *value)
909 return value && (!wcscmp(value, L"preserve") || !wcscmp(value, L"default"));
912 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
913 LPCWSTR local, LPCWSTR uri, LPCWSTR value)
915 xmlwriter *writer = impl_from_IXmlWriter(iface);
916 BOOL is_xmlns_prefix, is_xmlns_local;
917 int prefix_len, local_len;
918 struct ns *ns;
919 HRESULT hr;
921 TRACE("%p, %s, %s, %s, %s.\n", iface, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
923 switch (writer->state)
925 case XmlWriterState_Initial:
926 return E_UNEXPECTED;
927 case XmlWriterState_Ready:
928 case XmlWriterState_DocClosed:
929 writer->state = XmlWriterState_DocClosed;
930 return WR_E_INVALIDACTION;
931 case XmlWriterState_InvalidEncoding:
932 return MX_E_ENCODING;
933 default:
937 /* Prefix "xmlns" */
938 is_xmlns_prefix = prefix && !wcscmp(prefix, L"xmlns");
939 if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
940 return WR_E_NSPREFIXDECLARED;
942 if (is_empty_string(local))
943 return E_INVALIDARG;
945 /* Validate prefix and local name */
946 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
947 return hr;
949 if (FAILED(hr = is_valid_ncname(local, &local_len)))
950 return hr;
952 is_xmlns_local = !wcscmp(local, L"xmlns");
954 /* Trivial case, no prefix. */
955 if (prefix_len == 0 && is_empty_string(uri))
957 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
958 return S_OK;
961 /* Predefined "xml" prefix. */
962 if (prefix_len && !wcscmp(prefix, L"xml"))
964 /* Valid "space" value is enforced. */
965 if (!wcscmp(local, L"space") && !is_valid_xml_space_value(value))
966 return WR_E_INVALIDXMLSPACE;
968 /* Redefinition is not allowed. */
969 if (!is_empty_string(uri))
970 return WR_E_XMLPREFIXDECLARATION;
972 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
974 return S_OK;
977 if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
979 if (prefix_len && !is_empty_string(uri))
980 return WR_E_XMLNSPREFIXDECLARATION;
982 /* Look for exact match defined in current element, and write it out. */
983 if (!(ns = writer_find_ns_current(writer, prefix, value)))
984 ns = writer_push_ns(writer, local, local_len, value);
985 ns->emitted = TRUE;
987 write_output_attribute(writer, L"xmlns", 5, local, local_len, value);
989 return S_OK;
992 /* Ignore prefix if URI wasn't specified. */
993 if (is_xmlns_local && is_empty_string(uri))
995 write_output_attribute(writer, NULL, 0, L"xmlns", 5, value);
996 return S_OK;
999 if (!(ns = writer_find_ns(writer, prefix, uri)))
1001 if (is_empty_string(prefix) && !is_empty_string(uri))
1003 FIXME("Prefix autogeneration is not implemented.\n");
1004 return E_NOTIMPL;
1006 if (!is_empty_string(uri))
1007 ns = writer_push_ns(writer, prefix, prefix_len, uri);
1010 if (ns)
1011 write_output_attribute(writer, ns->prefix, ns->prefix_len, local, local_len, value);
1012 else
1013 write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
1015 return S_OK;
1018 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
1020 write_output_buffer(output, L"<![CDATA[", 9);
1021 if (data)
1022 write_output_buffer(output, data, len);
1023 write_output_buffer(output, L"]]>", 3);
1026 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
1028 xmlwriter *This = impl_from_IXmlWriter(iface);
1029 int len;
1031 TRACE("%p %s\n", This, debugstr_w(data));
1033 switch (This->state)
1035 case XmlWriterState_Initial:
1036 return E_UNEXPECTED;
1037 case XmlWriterState_ElemStarted:
1038 writer_close_starttag(This);
1039 break;
1040 case XmlWriterState_Ready:
1041 case XmlWriterState_DocClosed:
1042 This->state = XmlWriterState_DocClosed;
1043 return WR_E_INVALIDACTION;
1044 case XmlWriterState_InvalidEncoding:
1045 return MX_E_ENCODING;
1046 default:
1050 len = data ? lstrlenW(data) : 0;
1052 write_node_indent(This);
1053 if (!len)
1054 write_cdata_section(This->output, NULL, 0);
1055 else
1057 while (len)
1059 const WCHAR *str = wcsstr(data, L"]]>");
1060 if (str) {
1061 str += 2;
1062 write_cdata_section(This->output, data, str - data);
1063 len -= str - data;
1064 data = str;
1066 else {
1067 write_cdata_section(This->output, data, len);
1068 break;
1073 return S_OK;
1076 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1078 xmlwriter *This = impl_from_IXmlWriter(iface);
1079 WCHAR bufW[16];
1081 TRACE("%p %#x\n", This, ch);
1083 switch (This->state)
1085 case XmlWriterState_Initial:
1086 return E_UNEXPECTED;
1087 case XmlWriterState_InvalidEncoding:
1088 return MX_E_ENCODING;
1089 case XmlWriterState_ElemStarted:
1090 writer_close_starttag(This);
1091 break;
1092 case XmlWriterState_DocClosed:
1093 return WR_E_INVALIDACTION;
1094 default:
1098 swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%x;", ch);
1099 write_output_buffer(This->output, bufW, -1);
1101 return S_OK;
1104 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1106 xmlwriter *This = impl_from_IXmlWriter(iface);
1108 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1110 switch (This->state)
1112 case XmlWriterState_Initial:
1113 return E_UNEXPECTED;
1114 case XmlWriterState_InvalidEncoding:
1115 return MX_E_ENCODING;
1116 case XmlWriterState_DocClosed:
1117 return WR_E_INVALIDACTION;
1118 default:
1122 return E_NOTIMPL;
1126 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1128 xmlwriter *This = impl_from_IXmlWriter(iface);
1130 TRACE("%p %s\n", This, debugstr_w(comment));
1132 switch (This->state)
1134 case XmlWriterState_Initial:
1135 return E_UNEXPECTED;
1136 case XmlWriterState_InvalidEncoding:
1137 return MX_E_ENCODING;
1138 case XmlWriterState_ElemStarted:
1139 writer_close_starttag(This);
1140 break;
1141 case XmlWriterState_DocClosed:
1142 return WR_E_INVALIDACTION;
1143 default:
1147 write_node_indent(This);
1148 write_output_buffer(This->output, L"<!--", 4);
1149 if (comment) {
1150 int len = lstrlenW(comment), i;
1152 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1153 comment string */
1154 if (len > 1) {
1155 for (i = 0; i < len; i++) {
1156 write_output_buffer(This->output, comment + i, 1);
1157 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1158 write_output_buffer_char(This->output, ' ');
1161 else
1162 write_output_buffer(This->output, comment, len);
1164 if (len && comment[len-1] == '-')
1165 write_output_buffer_char(This->output, ' ');
1167 write_output_buffer(This->output, L"-->", 3);
1169 return S_OK;
1172 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
1173 LPCWSTR sysid, LPCWSTR subset)
1175 xmlwriter *This = impl_from_IXmlWriter(iface);
1176 unsigned int name_len, pubid_len;
1177 HRESULT hr;
1179 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
1180 wine_dbgstr_w(subset));
1182 switch (This->state)
1184 case XmlWriterState_Initial:
1185 return E_UNEXPECTED;
1186 case XmlWriterState_InvalidEncoding:
1187 return MX_E_ENCODING;
1188 case XmlWriterState_Content:
1189 case XmlWriterState_DocClosed:
1190 return WR_E_INVALIDACTION;
1191 default:
1195 if (is_empty_string(name))
1196 return E_INVALIDARG;
1198 if (FAILED(hr = is_valid_name(name, &name_len)))
1199 return hr;
1201 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1202 return hr;
1204 write_output_buffer(This->output, L"<!DOCTYPE ", 10);
1205 write_output_buffer(This->output, name, name_len);
1207 if (pubid)
1209 write_output_buffer(This->output, L" PUBLIC ", 8);
1210 write_output_buffer_quoted(This->output, pubid, pubid_len);
1211 write_output_buffer_char(This->output, ' ');
1212 write_output_buffer_quoted(This->output, sysid, -1);
1214 else if (sysid)
1216 write_output_buffer(This->output, L" SYSTEM ", 8);
1217 write_output_buffer_quoted(This->output, sysid, -1);
1220 if (subset)
1222 write_output_buffer_char(This->output, ' ');
1223 write_output_buffer_char(This->output, '[');
1224 write_output_buffer(This->output, subset, -1);
1225 write_output_buffer_char(This->output, ']');
1227 write_output_buffer_char(This->output, '>');
1229 This->state = XmlWriterState_Content;
1231 return S_OK;
1234 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
1235 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1237 xmlwriter *This = impl_from_IXmlWriter(iface);
1238 int prefix_len, local_len;
1239 struct ns *ns;
1240 HRESULT hr;
1242 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
1243 wine_dbgstr_w(uri), wine_dbgstr_w(value));
1245 switch (This->state)
1247 case XmlWriterState_Initial:
1248 return E_UNEXPECTED;
1249 case XmlWriterState_InvalidEncoding:
1250 return MX_E_ENCODING;
1251 case XmlWriterState_ElemStarted:
1252 writer_close_starttag(This);
1253 break;
1254 case XmlWriterState_DocClosed:
1255 return WR_E_INVALIDACTION;
1256 default:
1260 if (!local_name)
1261 return E_INVALIDARG;
1263 /* Validate prefix and local name */
1264 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1265 return hr;
1267 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1268 return hr;
1270 ns = writer_find_ns(This, prefix, uri);
1271 if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
1272 return WR_E_NSPREFIXWITHEMPTYNSURI;
1274 if (uri && !wcscmp(uri, xmlnsuriW))
1276 if (!prefix)
1277 return WR_E_XMLNSPREFIXDECLARATION;
1279 if (!is_empty_string(prefix))
1280 return WR_E_XMLNSURIDECLARATION;
1283 write_encoding_bom(This);
1284 write_node_indent(This);
1286 write_output_buffer_char(This->output, '<');
1287 if (ns)
1288 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1289 else
1290 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1292 if (!ns && (prefix_len || !is_empty_string(uri)))
1294 write_output_qname(This->output, L" xmlns", 6, prefix, prefix_len);
1295 write_output_buffer_char(This->output, '=');
1296 write_output_buffer_quoted(This->output, uri, -1);
1299 if (value)
1301 write_output_buffer_char(This->output, '>');
1302 write_output_buffer(This->output, value, -1);
1303 write_output_buffer(This->output, L"</", 2);
1304 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1305 write_output_buffer_char(This->output, '>');
1307 else
1308 write_output_buffer(This->output, L" />", 3);
1310 This->state = XmlWriterState_Content;
1312 return S_OK;
1315 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
1317 xmlwriter *This = impl_from_IXmlWriter(iface);
1319 TRACE("%p\n", This);
1321 switch (This->state)
1323 case XmlWriterState_Initial:
1324 return E_UNEXPECTED;
1325 case XmlWriterState_Ready:
1326 case XmlWriterState_DocClosed:
1327 This->state = XmlWriterState_DocClosed;
1328 return WR_E_INVALIDACTION;
1329 case XmlWriterState_InvalidEncoding:
1330 return MX_E_ENCODING;
1331 default:
1335 /* empty element stack */
1336 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1339 This->state = XmlWriterState_DocClosed;
1340 return S_OK;
1343 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
1345 xmlwriter *This = impl_from_IXmlWriter(iface);
1346 struct element *element;
1348 TRACE("%p\n", This);
1350 switch (This->state)
1352 case XmlWriterState_Initial:
1353 return E_UNEXPECTED;
1354 case XmlWriterState_Ready:
1355 case XmlWriterState_DocClosed:
1356 This->state = XmlWriterState_DocClosed;
1357 return WR_E_INVALIDACTION;
1358 case XmlWriterState_InvalidEncoding:
1359 return MX_E_ENCODING;
1360 default:
1364 element = pop_element(This);
1365 if (!element)
1366 return WR_E_INVALIDACTION;
1368 writer_dec_indent(This);
1370 if (This->starttagopen)
1372 writer_output_ns(This, element);
1373 write_output_buffer(This->output, L" />", 3);
1374 This->starttagopen = 0;
1376 else
1378 /* Write full end tag. */
1379 write_node_indent(This);
1380 write_output_buffer(This->output, L"</", 2);
1381 write_output_buffer(This->output, element->qname, element->len);
1382 write_output_buffer_char(This->output, '>');
1384 writer_free_element(This, element);
1386 return S_OK;
1389 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
1391 xmlwriter *This = impl_from_IXmlWriter(iface);
1393 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1395 switch (This->state)
1397 case XmlWriterState_Initial:
1398 return E_UNEXPECTED;
1399 case XmlWriterState_InvalidEncoding:
1400 return MX_E_ENCODING;
1401 case XmlWriterState_DocClosed:
1402 return WR_E_INVALIDACTION;
1403 default:
1407 return E_NOTIMPL;
1410 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
1412 xmlwriter *This = impl_from_IXmlWriter(iface);
1413 struct element *element;
1415 TRACE("%p\n", This);
1417 switch (This->state)
1419 case XmlWriterState_Initial:
1420 return E_UNEXPECTED;
1421 case XmlWriterState_Ready:
1422 case XmlWriterState_DocClosed:
1423 This->state = XmlWriterState_DocClosed;
1424 return WR_E_INVALIDACTION;
1425 case XmlWriterState_InvalidEncoding:
1426 return MX_E_ENCODING;
1427 case XmlWriterState_ElemStarted:
1428 writer_close_starttag(This);
1429 break;
1430 default:
1434 element = pop_element(This);
1435 if (!element)
1436 return WR_E_INVALIDACTION;
1438 writer_dec_indent(This);
1440 /* don't force full end tag to the next line */
1441 if (This->state == XmlWriterState_ElemStarted)
1443 This->state = XmlWriterState_Content;
1444 This->textnode = 0;
1446 else
1447 write_node_indent(This);
1449 /* write full end tag */
1450 write_output_buffer(This->output, L"</", 2);
1451 write_output_buffer(This->output, element->qname, element->len);
1452 write_output_buffer_char(This->output, '>');
1454 writer_free_element(This, element);
1456 return S_OK;
1459 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1461 xmlwriter *This = impl_from_IXmlWriter(iface);
1463 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1465 switch (This->state)
1467 case XmlWriterState_Initial:
1468 return E_UNEXPECTED;
1469 case XmlWriterState_Ready:
1470 case XmlWriterState_DocClosed:
1471 This->state = XmlWriterState_DocClosed;
1472 return WR_E_INVALIDACTION;
1473 case XmlWriterState_InvalidEncoding:
1474 return MX_E_ENCODING;
1475 default:
1479 return E_NOTIMPL;
1482 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1484 xmlwriter *This = impl_from_IXmlWriter(iface);
1486 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1488 switch (This->state)
1490 case XmlWriterState_Initial:
1491 return E_UNEXPECTED;
1492 case XmlWriterState_Ready:
1493 case XmlWriterState_DocClosed:
1494 This->state = XmlWriterState_DocClosed;
1495 return WR_E_INVALIDACTION;
1496 case XmlWriterState_InvalidEncoding:
1497 return MX_E_ENCODING;
1498 default:
1502 return E_NOTIMPL;
1505 static HRESULT writer_write_node(IXmlWriter *writer, IXmlReader *reader, BOOL shallow, BOOL write_default_attributes)
1507 XmlStandalone standalone = XmlStandalone_Omit;
1508 const WCHAR *name, *value, *prefix, *uri;
1509 unsigned int start_depth = 0, depth;
1510 XmlNodeType node_type;
1511 HRESULT hr;
1513 if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;
1515 switch (node_type)
1517 case XmlNodeType_None:
1518 if (shallow) return S_OK;
1519 while ((hr = IXmlReader_Read(reader, NULL)) == S_OK)
1521 if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1523 break;
1524 case XmlNodeType_Element:
1525 if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
1526 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1527 if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
1528 if (FAILED(hr = IXmlWriter_WriteStartElement(writer, prefix, name, uri))) return hr;
1529 if (FAILED(hr = IXmlWriter_WriteAttributes(writer, reader, write_default_attributes))) return hr;
1530 if (IXmlReader_IsEmptyElement(reader))
1532 hr = IXmlWriter_WriteEndElement(writer);
1534 else
1536 if (shallow) return S_OK;
1537 if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;
1538 if (FAILED(hr = IXmlReader_GetDepth(reader, &start_depth))) return hr;
1539 while ((hr = IXmlReader_Read(reader, &node_type)) == S_OK)
1541 if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1542 if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;
1544 depth = 0;
1545 if (FAILED(hr = IXmlReader_GetDepth(reader, &depth))) return hr;
1546 if (node_type == XmlNodeType_EndElement && (start_depth == depth - 1)) break;
1549 break;
1550 case XmlNodeType_Attribute:
1551 break;
1552 case XmlNodeType_Text:
1553 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1554 hr = IXmlWriter_WriteRaw(writer, value);
1555 break;
1556 case XmlNodeType_CDATA:
1557 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1558 hr = IXmlWriter_WriteCData(writer, value);
1559 break;
1560 case XmlNodeType_ProcessingInstruction:
1561 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1562 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1563 hr = IXmlWriter_WriteProcessingInstruction(writer, name, value);
1564 break;
1565 case XmlNodeType_Comment:
1566 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1567 hr = IXmlWriter_WriteComment(writer, value);
1568 break;
1569 case XmlNodeType_Whitespace:
1570 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1571 hr = IXmlWriter_WriteWhitespace(writer, value);
1572 break;
1573 case XmlNodeType_EndElement:
1574 hr = IXmlWriter_WriteFullEndElement(writer);
1575 break;
1576 case XmlNodeType_XmlDeclaration:
1577 while ((hr = IXmlReader_MoveToNextAttribute(reader)) == S_OK)
1579 if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
1580 if (!wcscmp(name, L"standalone"))
1582 if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
1583 standalone = !wcscmp(value, L"yes") ? XmlStandalone_Yes : XmlStandalone_No;
1586 if (SUCCEEDED(hr))
1587 hr = IXmlWriter_WriteStartDocument(writer, standalone);
1588 break;
1589 default:
1590 WARN("Unknown node type %d.\n", node_type);
1591 return E_UNEXPECTED;
1594 return hr;
1597 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
1599 HRESULT hr;
1601 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
1603 if (SUCCEEDED(hr = writer_write_node(iface, reader, FALSE, write_default_attributes)))
1604 hr = IXmlReader_Read(reader, NULL);
1606 return hr;
1609 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
1611 TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
1613 return writer_write_node(iface, reader, TRUE, write_default_attributes);
1616 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1617 LPCWSTR text)
1619 xmlwriter *This = impl_from_IXmlWriter(iface);
1621 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1623 switch (This->state)
1625 case XmlWriterState_Initial:
1626 return E_UNEXPECTED;
1627 case XmlWriterState_InvalidEncoding:
1628 return MX_E_ENCODING;
1629 case XmlWriterState_DocStarted:
1630 if (!wcscmp(name, L"xml"))
1631 return WR_E_INVALIDACTION;
1632 break;
1633 case XmlWriterState_ElemStarted:
1634 writer_close_starttag(This);
1635 break;
1636 case XmlWriterState_DocClosed:
1637 return WR_E_INVALIDACTION;
1638 default:
1642 write_encoding_bom(This);
1643 write_node_indent(This);
1644 write_output_buffer(This->output, L"<?", 2);
1645 write_output_buffer(This->output, name, -1);
1646 write_output_buffer_char(This->output, ' ');
1647 write_output_buffer(This->output, text, -1);
1648 write_output_buffer(This->output, L"?>", 2);
1650 if (!wcscmp(name, L"xml"))
1651 This->state = XmlWriterState_PIDocStarted;
1653 return S_OK;
1656 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1657 LPCWSTR pwszNamespaceUri)
1659 xmlwriter *This = impl_from_IXmlWriter(iface);
1661 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1663 switch (This->state)
1665 case XmlWriterState_Initial:
1666 return E_UNEXPECTED;
1667 case XmlWriterState_InvalidEncoding:
1668 return MX_E_ENCODING;
1669 case XmlWriterState_DocClosed:
1670 return WR_E_INVALIDACTION;
1671 default:
1675 return E_NOTIMPL;
1678 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1680 xmlwriter *This = impl_from_IXmlWriter(iface);
1682 TRACE("%p %s\n", This, debugstr_w(data));
1684 if (!data)
1685 return S_OK;
1687 switch (This->state)
1689 case XmlWriterState_Initial:
1690 return E_UNEXPECTED;
1691 case XmlWriterState_Ready:
1692 write_xmldecl(This, XmlStandalone_Omit);
1693 /* fallthrough */
1694 case XmlWriterState_DocStarted:
1695 case XmlWriterState_PIDocStarted:
1696 break;
1697 case XmlWriterState_InvalidEncoding:
1698 return MX_E_ENCODING;
1699 case XmlWriterState_ElemStarted:
1700 writer_close_starttag(This);
1701 break;
1702 default:
1703 This->state = XmlWriterState_DocClosed;
1704 return WR_E_INVALIDACTION;
1707 write_output_buffer(This->output, data, -1);
1708 return S_OK;
1711 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1713 xmlwriter *This = impl_from_IXmlWriter(iface);
1715 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1717 switch (This->state)
1719 case XmlWriterState_Initial:
1720 return E_UNEXPECTED;
1721 case XmlWriterState_InvalidEncoding:
1722 return MX_E_ENCODING;
1723 case XmlWriterState_DocClosed:
1724 return WR_E_INVALIDACTION;
1725 default:
1729 return E_NOTIMPL;
1732 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1734 xmlwriter *This = impl_from_IXmlWriter(iface);
1736 TRACE("(%p)->(%d)\n", This, standalone);
1738 switch (This->state)
1740 case XmlWriterState_Initial:
1741 return E_UNEXPECTED;
1742 case XmlWriterState_PIDocStarted:
1743 This->state = XmlWriterState_DocStarted;
1744 return S_OK;
1745 case XmlWriterState_Ready:
1746 break;
1747 case XmlWriterState_InvalidEncoding:
1748 return MX_E_ENCODING;
1749 default:
1750 This->state = XmlWriterState_DocClosed;
1751 return WR_E_INVALIDACTION;
1754 return write_xmldecl(This, standalone);
1757 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1759 xmlwriter *This = impl_from_IXmlWriter(iface);
1760 int prefix_len, local_len;
1761 struct element *element;
1762 struct ns *ns;
1763 HRESULT hr;
1765 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1767 if (!local_name)
1768 return E_INVALIDARG;
1770 switch (This->state)
1772 case XmlWriterState_Initial:
1773 return E_UNEXPECTED;
1774 case XmlWriterState_InvalidEncoding:
1775 return MX_E_ENCODING;
1776 case XmlWriterState_DocClosed:
1777 return WR_E_INVALIDACTION;
1778 case XmlWriterState_ElemStarted:
1779 writer_close_starttag(This);
1780 break;
1781 default:
1785 /* Validate prefix and local name */
1786 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1787 return hr;
1789 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1790 return hr;
1792 if (uri && !wcscmp(uri, xmlnsuriW))
1794 if (!prefix)
1795 return WR_E_XMLNSPREFIXDECLARATION;
1797 if (!is_empty_string(prefix))
1798 return WR_E_XMLNSURIDECLARATION;
1801 ns = writer_find_ns(This, prefix, uri);
1803 element = alloc_element(This, prefix, local_name);
1804 if (!element)
1805 return E_OUTOFMEMORY;
1807 write_encoding_bom(This);
1808 write_node_indent(This);
1810 This->state = XmlWriterState_ElemStarted;
1811 This->starttagopen = 1;
1813 writer_push_element(This, element);
1815 if (!ns && !is_empty_string(uri))
1816 writer_push_ns(This, prefix, prefix_len, uri);
1818 write_output_buffer_char(This->output, '<');
1819 if (ns)
1820 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1821 else
1822 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1823 writer_inc_indent(This);
1825 return S_OK;
1828 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1830 while (*string)
1832 switch (*string)
1834 case '<':
1835 write_output_buffer(writer->output, L"&lt;", 4);
1836 break;
1837 case '&':
1838 write_output_buffer(writer->output, L"&amp;", 5);
1839 break;
1840 case '>':
1841 write_output_buffer(writer->output, L"&gt;", 4);
1842 break;
1843 default:
1844 write_output_buffer(writer->output, string, 1);
1847 string++;
1851 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1853 xmlwriter *This = impl_from_IXmlWriter(iface);
1855 TRACE("%p %s\n", This, debugstr_w(string));
1857 if (!string)
1858 return S_OK;
1860 switch (This->state)
1862 case XmlWriterState_Initial:
1863 return E_UNEXPECTED;
1864 case XmlWriterState_ElemStarted:
1865 writer_close_starttag(This);
1866 break;
1867 case XmlWriterState_Ready:
1868 case XmlWriterState_DocClosed:
1869 This->state = XmlWriterState_DocClosed;
1870 return WR_E_INVALIDACTION;
1871 case XmlWriterState_InvalidEncoding:
1872 return MX_E_ENCODING;
1873 default:
1877 This->textnode = 1;
1878 write_escaped_string(This, string);
1879 return S_OK;
1882 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1884 xmlwriter *This = impl_from_IXmlWriter(iface);
1886 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1888 return E_NOTIMPL;
1891 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR text)
1893 xmlwriter *writer = impl_from_IXmlWriter(iface);
1894 size_t length = 0;
1896 TRACE("%p, %s.\n", iface, wine_dbgstr_w(text));
1898 switch (writer->state)
1900 case XmlWriterState_Initial:
1901 return E_UNEXPECTED;
1902 case XmlWriterState_ElemStarted:
1903 writer_close_starttag(writer);
1904 break;
1905 case XmlWriterState_InvalidEncoding:
1906 return MX_E_ENCODING;
1907 case XmlWriterState_Ready:
1908 break;
1909 default:
1910 return WR_E_INVALIDACTION;
1913 while (text[length])
1915 if (!is_wchar_space(text[length])) return WR_E_NONWHITESPACE;
1916 length++;
1919 write_output_buffer(writer->output, text, length);
1920 return S_OK;
1923 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1925 xmlwriter *This = impl_from_IXmlWriter(iface);
1927 TRACE("%p\n", This);
1929 return writeroutput_flush_stream(This->output);
1932 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1934 xmlwriter_QueryInterface,
1935 xmlwriter_AddRef,
1936 xmlwriter_Release,
1937 xmlwriter_SetOutput,
1938 xmlwriter_GetProperty,
1939 xmlwriter_SetProperty,
1940 xmlwriter_WriteAttributes,
1941 xmlwriter_WriteAttributeString,
1942 xmlwriter_WriteCData,
1943 xmlwriter_WriteCharEntity,
1944 xmlwriter_WriteChars,
1945 xmlwriter_WriteComment,
1946 xmlwriter_WriteDocType,
1947 xmlwriter_WriteElementString,
1948 xmlwriter_WriteEndDocument,
1949 xmlwriter_WriteEndElement,
1950 xmlwriter_WriteEntityRef,
1951 xmlwriter_WriteFullEndElement,
1952 xmlwriter_WriteName,
1953 xmlwriter_WriteNmToken,
1954 xmlwriter_WriteNode,
1955 xmlwriter_WriteNodeShallow,
1956 xmlwriter_WriteProcessingInstruction,
1957 xmlwriter_WriteQualifiedName,
1958 xmlwriter_WriteRaw,
1959 xmlwriter_WriteRawChars,
1960 xmlwriter_WriteStartDocument,
1961 xmlwriter_WriteStartElement,
1962 xmlwriter_WriteString,
1963 xmlwriter_WriteSurrogateCharEntity,
1964 xmlwriter_WriteWhitespace,
1965 xmlwriter_Flush
1968 /** IXmlWriterOutput **/
1969 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1971 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1973 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1975 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1976 IsEqualGUID(riid, &IID_IUnknown))
1978 *ppvObject = iface;
1980 else
1982 WARN("interface %s not implemented\n", debugstr_guid(riid));
1983 *ppvObject = NULL;
1984 return E_NOINTERFACE;
1987 IUnknown_AddRef(iface);
1989 return S_OK;
1992 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1994 xmlwriteroutput *output = impl_from_IXmlWriterOutput(iface);
1995 ULONG ref = InterlockedIncrement(&output->ref);
1996 TRACE("%p, refcount %ld.\n", iface, ref);
1997 return ref;
2000 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
2002 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
2003 LONG ref = InterlockedDecrement(&This->ref);
2005 TRACE("%p, refcount %ld.\n", iface, ref);
2007 if (ref == 0)
2009 IMalloc *imalloc = This->imalloc;
2010 if (This->output) IUnknown_Release(This->output);
2011 if (This->stream) ISequentialStream_Release(This->stream);
2012 free_output_buffer(This);
2013 writeroutput_free(This, This->encoding_name);
2014 writeroutput_free(This, This);
2015 if (imalloc) IMalloc_Release(imalloc);
2018 return ref;
2021 static const struct IUnknownVtbl xmlwriteroutputvtbl =
2023 xmlwriteroutput_QueryInterface,
2024 xmlwriteroutput_AddRef,
2025 xmlwriteroutput_Release
2028 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
2030 xmlwriter *writer;
2031 HRESULT hr;
2033 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
2035 if (!(writer = m_alloc(imalloc, sizeof(*writer))))
2036 return E_OUTOFMEMORY;
2037 memset(writer, 0, sizeof(*writer));
2039 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
2040 writer->ref = 1;
2041 writer->imalloc = imalloc;
2042 if (imalloc) IMalloc_AddRef(imalloc);
2043 writer->bom = TRUE;
2044 writer->conformance = XmlConformanceLevel_Document;
2045 writer->state = XmlWriterState_Initial;
2046 list_init(&writer->elements);
2048 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
2049 IXmlWriter_Release(&writer->IXmlWriter_iface);
2051 TRACE("returning iface %p, hr %#lx.\n", *obj, hr);
2053 return hr;
2056 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
2057 const WCHAR *encoding_name, xmlwriteroutput **out)
2059 xmlwriteroutput *writeroutput;
2060 HRESULT hr;
2062 *out = NULL;
2064 if (!(writeroutput = m_alloc(imalloc, sizeof(*writeroutput))))
2065 return E_OUTOFMEMORY;
2066 memset(writeroutput, 0, sizeof(*writeroutput));
2068 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
2069 writeroutput->ref = 1;
2070 writeroutput->imalloc = imalloc;
2071 if (imalloc)
2072 IMalloc_AddRef(imalloc);
2073 writeroutput->encoding = encoding;
2074 hr = init_output_buffer(writeroutput);
2075 if (FAILED(hr)) {
2076 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
2077 return hr;
2080 if (encoding_name)
2082 unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
2083 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
2084 memcpy(writeroutput->encoding_name, encoding_name, size);
2087 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
2089 *out = writeroutput;
2091 TRACE("Created writer output %p\n", *out);
2093 return S_OK;
2096 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
2097 IXmlWriterOutput **out)
2099 xmlwriteroutput *output;
2100 xml_encoding xml_enc;
2101 HRESULT hr;
2103 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
2105 if (!stream || !out)
2106 return E_INVALIDARG;
2108 *out = NULL;
2110 xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
2111 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
2112 *out = &output->IXmlWriterOutput_iface;
2114 return hr;
2117 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
2118 IXmlWriterOutput **out)
2120 xmlwriteroutput *output;
2121 xml_encoding xml_enc;
2122 HRESULT hr;
2124 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
2126 if (!stream || !out)
2127 return E_INVALIDARG;
2129 *out = NULL;
2131 xml_enc = get_encoding_from_codepage(codepage);
2132 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
2133 *out = &output->IXmlWriterOutput_iface;
2135 return hr;