xref: /reactos/dll/win32/xmllite/writer.c (revision 8540ab04)
1 /*
2  * IXmlWriter implementation
3  *
4  * Copyright 2011 Alistair Leslie-Hughes
5  * Copyright 2014, 2016 Nikolay Sivov for CodeWeavers
6  *
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.
11  *
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.
16  *
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
20  */
21 #define COBJMACROS
22 
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "objbase.h"
27 #include "xmllite.h"
28 #include "xmllite_private.h"
29 #include "initguid.h"
30 
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
34 
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
36 
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
39 
40 static const WCHAR closeelementW[] = {'<','/'};
41 static const WCHAR closetagW[] = {' ','/','>'};
42 static const WCHAR closepiW[] = {'?','>'};
43 static const WCHAR ltW[] = {'<'};
44 static const WCHAR gtW[] = {'>'};
45 static const WCHAR spaceW[] = {' '};
46 static const WCHAR quoteW[] = {'"'};
47 
48 struct output_buffer
49 {
50     char *data;
51     unsigned int allocated;
52     unsigned int written;
53     UINT codepage;
54 };
55 
56 typedef enum
57 {
58     XmlWriterState_Initial,         /* output is not set yet */
59     XmlWriterState_Ready,           /* SetOutput() was called, ready to start */
60     XmlWriterState_InvalidEncoding, /* SetOutput() was called, but output had invalid encoding */
61     XmlWriterState_PIDocStarted,    /* document was started with manually added 'xml' PI */
62     XmlWriterState_DocStarted,      /* document was started with WriteStartDocument() */
63     XmlWriterState_ElemStarted,     /* writing element */
64     XmlWriterState_Content,         /* content is accepted at this point */
65     XmlWriterState_DocClosed        /* WriteEndDocument was called */
66 } XmlWriterState;
67 
68 typedef struct
69 {
70     IXmlWriterOutput IXmlWriterOutput_iface;
71     LONG ref;
72     IUnknown *output;
73     ISequentialStream *stream;
74     IMalloc *imalloc;
75     xml_encoding encoding;
76     WCHAR *encoding_name; /* exactly as specified on output creation */
77     struct output_buffer buffer;
78 } xmlwriteroutput;
79 
80 static const struct IUnknownVtbl xmlwriteroutputvtbl;
81 
82 struct element
83 {
84     struct list entry;
85     WCHAR *qname;
86     unsigned int len; /* qname length in chars */
87 };
88 
89 typedef struct _xmlwriter
90 {
91     IXmlWriter IXmlWriter_iface;
92     LONG ref;
93     IMalloc *imalloc;
94     xmlwriteroutput *output;
95     unsigned int indent_level;
96     BOOL indent;
97     BOOL bom;
98     BOOL omitxmldecl;
99     XmlConformanceLevel conformance;
100     XmlWriterState state;
101     BOOL bomwritten;
102     BOOL starttagopen;
103     struct list elements;
104 } xmlwriter;
105 
106 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
107 {
108     return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
109 }
110 
111 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
112 {
113     return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
114 }
115 
116 static const char *debugstr_writer_prop(XmlWriterProperty prop)
117 {
118     static const char * const prop_names[] =
119     {
120         "MultiLanguage",
121         "Indent",
122         "ByteOrderMark",
123         "OmitXmlDeclaration",
124         "ConformanceLevel"
125     };
126 
127     if (prop > _XmlWriterProperty_Last)
128         return wine_dbg_sprintf("unknown property=%d", prop);
129 
130     return prop_names[prop];
131 }
132 
133 /* writer output memory allocation functions */
134 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
135 {
136     return m_alloc(output->imalloc, len);
137 }
138 
139 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
140 {
141     m_free(output->imalloc, mem);
142 }
143 
144 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
145 {
146     return m_realloc(output->imalloc, mem, len);
147 }
148 
149 /* writer memory allocation functions */
150 static inline void *writer_alloc(xmlwriter *writer, size_t len)
151 {
152     return m_alloc(writer->imalloc, len);
153 }
154 
155 static inline void writer_free(xmlwriter *writer, void *mem)
156 {
157     m_free(writer->imalloc, mem);
158 }
159 
160 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
161 {
162     struct element *ret;
163     int len;
164 
165     ret = writer_alloc(writer, sizeof(*ret));
166     if (!ret) return ret;
167 
168     len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
169     len += strlenW(local);
170 
171     ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
172     ret->len = len;
173     if (prefix) {
174         static const WCHAR colonW[] = {':',0};
175         strcpyW(ret->qname, prefix);
176         strcatW(ret->qname, colonW);
177     }
178     else
179         ret->qname[0] = 0;
180     strcatW(ret->qname, local);
181 
182     return ret;
183 }
184 
185 static void free_element(xmlwriter *writer, struct element *element)
186 {
187     writer_free(writer, element->qname);
188     writer_free(writer, element);
189 }
190 
191 static void push_element(xmlwriter *writer, struct element *element)
192 {
193     list_add_head(&writer->elements, &element->entry);
194 }
195 
196 static struct element *pop_element(xmlwriter *writer)
197 {
198     struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
199 
200     if (element)
201         list_remove(&element->entry);
202 
203     return element;
204 }
205 
206 static HRESULT init_output_buffer(xmlwriteroutput *output)
207 {
208     struct output_buffer *buffer = &output->buffer;
209     const int initial_len = 0x2000;
210     UINT cp = ~0u;
211     HRESULT hr;
212 
213     if (FAILED(hr = get_code_page(output->encoding, &cp)))
214         WARN("Failed to get code page for specified encoding.\n");
215 
216     buffer->data = writeroutput_alloc(output, initial_len);
217     if (!buffer->data) return E_OUTOFMEMORY;
218 
219     memset(buffer->data, 0, 4);
220     buffer->allocated = initial_len;
221     buffer->written = 0;
222     buffer->codepage = cp;
223 
224     return S_OK;
225 }
226 
227 static void free_output_buffer(xmlwriteroutput *output)
228 {
229     struct output_buffer *buffer = &output->buffer;
230     writeroutput_free(output, buffer->data);
231     buffer->data = NULL;
232     buffer->allocated = 0;
233     buffer->written = 0;
234 }
235 
236 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
237 {
238     struct output_buffer *buffer = &output->buffer;
239     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
240     if (buffer->allocated < buffer->written + length + 4) {
241         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
242         char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
243         if (!ptr) return E_OUTOFMEMORY;
244         buffer->data = ptr;
245         buffer->allocated = grown_size;
246     }
247 
248     return S_OK;
249 }
250 
251 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
252 {
253     struct output_buffer *buffer = &output->buffer;
254     int length;
255     HRESULT hr;
256     char *ptr;
257 
258     if (buffer->codepage == 1200) {
259         /* For UTF-16 encoding just copy. */
260         length = len == -1 ? strlenW(data) : len;
261         if (length) {
262             length *= sizeof(WCHAR);
263 
264             hr = grow_output_buffer(output, length);
265             if (FAILED(hr)) return hr;
266             ptr = buffer->data + buffer->written;
267 
268             memcpy(ptr, data, length);
269             buffer->written += length;
270             ptr += length;
271             /* null termination */
272             *(WCHAR*)ptr = 0;
273         }
274     }
275     else {
276         length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
277         hr = grow_output_buffer(output, length);
278         if (FAILED(hr)) return hr;
279         ptr = buffer->data + buffer->written;
280         length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
281         buffer->written += len == -1 ? length-1 : length;
282     }
283 
284     return S_OK;
285 }
286 
287 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
288 {
289     write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
290     write_output_buffer(output, data, len);
291     write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
292     return S_OK;
293 }
294 
295 /* TODO: test if we need to validate char range */
296 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
297 {
298     if (prefix) {
299         static const WCHAR colW[] = {':'};
300         write_output_buffer(output, prefix, -1);
301         write_output_buffer(output, colW, ARRAY_SIZE(colW));
302     }
303 
304     write_output_buffer(output, local_name, -1);
305 
306     return S_OK;
307 }
308 
309 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
310 {
311     if (writeroutput->stream) {
312         ISequentialStream_Release(writeroutput->stream);
313         writeroutput->stream = NULL;
314     }
315 }
316 
317 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
318 {
319     HRESULT hr;
320 
321     writeroutput_release_stream(writeroutput);
322     hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
323     if (hr != S_OK)
324         hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
325 
326     return hr;
327 }
328 
329 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
330 {
331     struct output_buffer *buffer;
332     ULONG written, offset = 0;
333     HRESULT hr;
334 
335     if (!output || !output->stream)
336         return S_OK;
337 
338     buffer = &output->buffer;
339 
340     /* It will loop forever until everything is written or an error occurred. */
341     do {
342         written = 0;
343         hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
344         if (FAILED(hr)) {
345             WARN("write to stream failed (0x%08x)\n", hr);
346             buffer->written = 0;
347             return hr;
348         }
349 
350         offset += written;
351         buffer->written -= written;
352     } while (buffer->written > 0);
353 
354     return S_OK;
355 }
356 
357 static HRESULT write_encoding_bom(xmlwriter *writer)
358 {
359     if (!writer->bom || writer->bomwritten) return S_OK;
360 
361     if (writer->output->encoding == XmlEncoding_UTF16) {
362         static const char utf16bom[] = {0xff, 0xfe};
363         struct output_buffer *buffer = &writer->output->buffer;
364         int len = sizeof(utf16bom);
365         HRESULT hr;
366 
367         hr = grow_output_buffer(writer->output, len);
368         if (FAILED(hr)) return hr;
369         memcpy(buffer->data + buffer->written, utf16bom, len);
370         buffer->written += len;
371     }
372 
373     writer->bomwritten = TRUE;
374     return S_OK;
375 }
376 
377 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
378 {
379     if (output->encoding_name)
380         return output->encoding_name;
381 
382     return get_encoding_name(output->encoding);
383 }
384 
385 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
386 {
387     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
388     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
389 
390     write_encoding_bom(writer);
391     writer->state = XmlWriterState_DocStarted;
392     if (writer->omitxmldecl) return S_OK;
393 
394     /* version */
395     write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
396 
397     /* encoding */
398     write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
399     write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
400 
401     /* standalone */
402     if (standalone == XmlStandalone_Omit)
403         write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
404     else {
405         static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
406         static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
407         static const WCHAR noW[] = {'n','o','\"','?','>'};
408 
409         write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
410         if (standalone == XmlStandalone_Yes)
411             write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
412         else
413             write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
414     }
415 
416     return S_OK;
417 }
418 
419 static HRESULT writer_close_starttag(xmlwriter *writer)
420 {
421     HRESULT hr;
422 
423     if (!writer->starttagopen) return S_OK;
424     hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
425     writer->starttagopen = FALSE;
426     return hr;
427 }
428 
429 static void writer_inc_indent(xmlwriter *writer)
430 {
431     writer->indent_level++;
432 }
433 
434 static void writer_dec_indent(xmlwriter *writer)
435 {
436     if (writer->indent_level)
437         writer->indent_level--;
438 }
439 
440 static void write_node_indent(xmlwriter *writer)
441 {
442     static const WCHAR dblspaceW[] = {' ',' '};
443     static const WCHAR crlfW[] = {'\r','\n'};
444     unsigned int indent_level = writer->indent_level;
445 
446     if (!writer->indent)
447         return;
448 
449     /* Do state check to prevent newline inserted after BOM. It is assumed that
450        state does not change between writing BOM and inserting indentation. */
451     if (writer->output->buffer.written && writer->state != XmlWriterState_Ready)
452         write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW));
453     while (indent_level--)
454         write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW));
455 }
456 
457 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
458 {
459     xmlwriter *This = impl_from_IXmlWriter(iface);
460 
461     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
462 
463     if (IsEqualGUID(riid, &IID_IXmlWriter) ||
464         IsEqualGUID(riid, &IID_IUnknown))
465     {
466         *ppvObject = iface;
467     }
468     else
469     {
470         FIXME("interface %s is not supported\n", debugstr_guid(riid));
471         *ppvObject = NULL;
472         return E_NOINTERFACE;
473     }
474 
475     IXmlWriter_AddRef(iface);
476 
477     return S_OK;
478 }
479 
480 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
481 {
482     xmlwriter *This = impl_from_IXmlWriter(iface);
483     ULONG ref = InterlockedIncrement(&This->ref);
484     TRACE("(%p)->(%u)\n", This, ref);
485     return ref;
486 }
487 
488 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
489 {
490     xmlwriter *This = impl_from_IXmlWriter(iface);
491     ULONG ref = InterlockedDecrement(&This->ref);
492 
493     TRACE("(%p)->(%u)\n", This, ref);
494 
495     if (ref == 0) {
496         struct element *element, *element2;
497         IMalloc *imalloc = This->imalloc;
498 
499         writeroutput_flush_stream(This->output);
500         if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
501 
502         /* element stack */
503         LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
504             list_remove(&element->entry);
505             free_element(This, element);
506         }
507 
508         writer_free(This, This);
509         if (imalloc) IMalloc_Release(imalloc);
510     }
511 
512     return ref;
513 }
514 
515 /*** IXmlWriter methods ***/
516 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
517 {
518     xmlwriter *This = impl_from_IXmlWriter(iface);
519     IXmlWriterOutput *writeroutput;
520     HRESULT hr;
521 
522     TRACE("(%p)->(%p)\n", This, output);
523 
524     if (This->output) {
525         writeroutput_release_stream(This->output);
526         IUnknown_Release(&This->output->IXmlWriterOutput_iface);
527         This->output = NULL;
528         This->bomwritten = FALSE;
529         This->indent_level = 0;
530     }
531 
532     /* just reset current output */
533     if (!output) {
534         This->state = XmlWriterState_Initial;
535         return S_OK;
536     }
537 
538     /* now try IXmlWriterOutput, ISequentialStream, IStream */
539     hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
540     if (hr == S_OK) {
541         if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
542             This->output = impl_from_IXmlWriterOutput(writeroutput);
543         else {
544             ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
545                 writeroutput, writeroutput->lpVtbl);
546             IUnknown_Release(writeroutput);
547             return E_FAIL;
548         }
549     }
550 
551     if (hr != S_OK || !writeroutput) {
552         /* create IXmlWriterOutput basing on supplied interface */
553         hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
554         if (hr != S_OK) return hr;
555         This->output = impl_from_IXmlWriterOutput(writeroutput);
556     }
557 
558     if (This->output->encoding == XmlEncoding_Unknown)
559         This->state = XmlWriterState_InvalidEncoding;
560     else
561         This->state = XmlWriterState_Ready;
562     return writeroutput_query_for_stream(This->output);
563 }
564 
565 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
566 {
567     xmlwriter *This = impl_from_IXmlWriter(iface);
568 
569     TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
570 
571     if (!value) return E_INVALIDARG;
572 
573     switch (property)
574     {
575         case XmlWriterProperty_Indent:
576             *value = This->indent;
577             break;
578         case XmlWriterProperty_ByteOrderMark:
579             *value = This->bom;
580             break;
581         case XmlWriterProperty_OmitXmlDeclaration:
582             *value = This->omitxmldecl;
583             break;
584         case XmlWriterProperty_ConformanceLevel:
585             *value = This->conformance;
586             break;
587         default:
588             FIXME("Unimplemented property (%u)\n", property);
589             return E_NOTIMPL;
590     }
591 
592     return S_OK;
593 }
594 
595 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
596 {
597     xmlwriter *This = impl_from_IXmlWriter(iface);
598 
599     TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
600 
601     switch (property)
602     {
603         case XmlWriterProperty_Indent:
604             This->indent = !!value;
605             break;
606         case XmlWriterProperty_ByteOrderMark:
607             This->bom = !!value;
608             break;
609         case XmlWriterProperty_OmitXmlDeclaration:
610             This->omitxmldecl = !!value;
611             break;
612         default:
613             FIXME("Unimplemented property (%u)\n", property);
614             return E_NOTIMPL;
615     }
616 
617     return S_OK;
618 }
619 
620 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
621                                   BOOL fWriteDefaultAttributes)
622 {
623     xmlwriter *This = impl_from_IXmlWriter(iface);
624 
625     FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
626 
627     return E_NOTIMPL;
628 }
629 
630 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
631     LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
632 {
633     static const WCHAR eqW[] = {'=','"'};
634     xmlwriter *This = impl_from_IXmlWriter(iface);
635 
636     TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
637                         debugstr_w(ns_uri), debugstr_w(value));
638 
639     switch (This->state)
640     {
641     case XmlWriterState_Initial:
642         return E_UNEXPECTED;
643     case XmlWriterState_Ready:
644     case XmlWriterState_DocClosed:
645         This->state = XmlWriterState_DocClosed;
646         return WR_E_INVALIDACTION;
647     case XmlWriterState_InvalidEncoding:
648         return MX_E_ENCODING;
649     default:
650         ;
651     }
652 
653     if (ns_prefix || ns_uri)
654     {
655         FIXME("namespaces are not supported.\n");
656         return E_NOTIMPL;
657     }
658 
659     write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
660     write_output_buffer(This->output, local_name, -1);
661     write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
662     write_output_buffer(This->output, value, -1);
663     write_output_buffer(This->output, quoteW, ARRAY_SIZE(quoteW));
664 
665     return S_OK;
666 }
667 
668 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
669 {
670     static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
671     static const WCHAR cdatacloseW[] = {']',']','>'};
672     write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
673     if (data)
674         write_output_buffer(output, data, len);
675     write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
676 }
677 
678 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
679 {
680     xmlwriter *This = impl_from_IXmlWriter(iface);
681     int len;
682 
683     TRACE("%p %s\n", This, debugstr_w(data));
684 
685     switch (This->state)
686     {
687     case XmlWriterState_Initial:
688         return E_UNEXPECTED;
689     case XmlWriterState_ElemStarted:
690         writer_close_starttag(This);
691         break;
692     case XmlWriterState_Ready:
693     case XmlWriterState_DocClosed:
694         This->state = XmlWriterState_DocClosed;
695         return WR_E_INVALIDACTION;
696     case XmlWriterState_InvalidEncoding:
697         return MX_E_ENCODING;
698     default:
699         ;
700     }
701 
702     len = data ? strlenW(data) : 0;
703 
704     write_node_indent(This);
705     if (!len)
706         write_cdata_section(This->output, NULL, 0);
707     else {
708         static const WCHAR cdatacloseW[] = {']',']','>',0};
709         while (len) {
710             const WCHAR *str = strstrW(data, cdatacloseW);
711             if (str) {
712                 str += 2;
713                 write_cdata_section(This->output, data, str - data);
714                 len -= str - data;
715                 data = str;
716             }
717             else {
718                 write_cdata_section(This->output, data, len);
719                 break;
720             }
721         }
722     }
723 
724     return S_OK;
725 }
726 
727 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
728 {
729     static const WCHAR fmtW[] = {'&','#','x','%','x',';',0};
730     xmlwriter *This = impl_from_IXmlWriter(iface);
731     WCHAR bufW[16];
732 
733     TRACE("%p %#x\n", This, ch);
734 
735     switch (This->state)
736     {
737     case XmlWriterState_Initial:
738         return E_UNEXPECTED;
739     case XmlWriterState_InvalidEncoding:
740         return MX_E_ENCODING;
741     case XmlWriterState_ElemStarted:
742         writer_close_starttag(This);
743         break;
744     case XmlWriterState_DocClosed:
745         return WR_E_INVALIDACTION;
746     default:
747         ;
748     }
749 
750     sprintfW(bufW, fmtW, ch);
751     write_output_buffer(This->output, bufW, -1);
752 
753     return S_OK;
754 }
755 
756 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
757 {
758     xmlwriter *This = impl_from_IXmlWriter(iface);
759 
760     FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
761 
762     switch (This->state)
763     {
764     case XmlWriterState_Initial:
765         return E_UNEXPECTED;
766     case XmlWriterState_InvalidEncoding:
767         return MX_E_ENCODING;
768     case XmlWriterState_DocClosed:
769         return WR_E_INVALIDACTION;
770     default:
771         ;
772     }
773 
774     return E_NOTIMPL;
775 }
776 
777 
778 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
779 {
780     static const WCHAR copenW[] = {'<','!','-','-'};
781     static const WCHAR ccloseW[] = {'-','-','>'};
782     xmlwriter *This = impl_from_IXmlWriter(iface);
783 
784     TRACE("%p %s\n", This, debugstr_w(comment));
785 
786     switch (This->state)
787     {
788     case XmlWriterState_Initial:
789         return E_UNEXPECTED;
790     case XmlWriterState_InvalidEncoding:
791         return MX_E_ENCODING;
792     case XmlWriterState_ElemStarted:
793         writer_close_starttag(This);
794         break;
795     case XmlWriterState_DocClosed:
796         return WR_E_INVALIDACTION;
797     default:
798         ;
799     }
800 
801     write_node_indent(This);
802     write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
803     if (comment) {
804         int len = strlenW(comment), i;
805 
806         /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
807            comment string */
808         if (len > 1) {
809             for (i = 0; i < len; i++) {
810                 write_output_buffer(This->output, comment + i, 1);
811                 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
812                     write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
813             }
814         }
815         else
816             write_output_buffer(This->output, comment, len);
817 
818         if (len && comment[len-1] == '-')
819             write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
820     }
821     write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
822 
823     return S_OK;
824 }
825 
826 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
827                                LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
828 {
829     xmlwriter *This = impl_from_IXmlWriter(iface);
830 
831     FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
832                         wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
833 
834     return E_NOTIMPL;
835 }
836 
837 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
838                                      LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
839 {
840     xmlwriter *This = impl_from_IXmlWriter(iface);
841 
842     TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
843                         wine_dbgstr_w(uri), wine_dbgstr_w(value));
844 
845     switch (This->state)
846     {
847     case XmlWriterState_Initial:
848         return E_UNEXPECTED;
849     case XmlWriterState_InvalidEncoding:
850         return MX_E_ENCODING;
851     case XmlWriterState_ElemStarted:
852         writer_close_starttag(This);
853         break;
854     case XmlWriterState_DocClosed:
855         return WR_E_INVALIDACTION;
856     default:
857         ;
858     }
859 
860     write_encoding_bom(This);
861     write_node_indent(This);
862     write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
863     write_output_qname(This->output, prefix, local_name);
864 
865     if (value)
866     {
867         write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
868         write_output_buffer(This->output, value, -1);
869         write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
870         write_output_qname(This->output, prefix, local_name);
871         write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
872     }
873     else
874         write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
875 
876     This->state = XmlWriterState_Content;
877 
878     return S_OK;
879 }
880 
881 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
882 {
883     xmlwriter *This = impl_from_IXmlWriter(iface);
884 
885     TRACE("%p\n", This);
886 
887     switch (This->state)
888     {
889     case XmlWriterState_Initial:
890         return E_UNEXPECTED;
891     case XmlWriterState_Ready:
892     case XmlWriterState_DocClosed:
893         This->state = XmlWriterState_DocClosed;
894         return WR_E_INVALIDACTION;
895     case XmlWriterState_InvalidEncoding:
896         return MX_E_ENCODING;
897     default:
898         ;
899     }
900 
901     /* empty element stack */
902     while (IXmlWriter_WriteEndElement(iface) == S_OK)
903         ;
904 
905     This->state = XmlWriterState_DocClosed;
906     return S_OK;
907 }
908 
909 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
910 {
911     xmlwriter *This = impl_from_IXmlWriter(iface);
912     struct element *element;
913 
914     TRACE("%p\n", This);
915 
916     switch (This->state)
917     {
918     case XmlWriterState_Initial:
919         return E_UNEXPECTED;
920     case XmlWriterState_Ready:
921     case XmlWriterState_DocClosed:
922         This->state = XmlWriterState_DocClosed;
923         return WR_E_INVALIDACTION;
924     case XmlWriterState_InvalidEncoding:
925         return MX_E_ENCODING;
926     default:
927         ;
928     }
929 
930     element = pop_element(This);
931     if (!element)
932         return WR_E_INVALIDACTION;
933 
934     writer_dec_indent(This);
935 
936     if (This->starttagopen)
937     {
938         write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
939         This->starttagopen = FALSE;
940     }
941     else {
942         /* write full end tag */
943         write_node_indent(This);
944         write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
945         write_output_buffer(This->output, element->qname, element->len);
946         write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
947     }
948 
949     return S_OK;
950 }
951 
952 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
953 {
954     xmlwriter *This = impl_from_IXmlWriter(iface);
955 
956     FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
957 
958     switch (This->state)
959     {
960     case XmlWriterState_Initial:
961         return E_UNEXPECTED;
962     case XmlWriterState_InvalidEncoding:
963         return MX_E_ENCODING;
964     case XmlWriterState_DocClosed:
965         return WR_E_INVALIDACTION;
966     default:
967         ;
968     }
969 
970     return E_NOTIMPL;
971 }
972 
973 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
974 {
975     xmlwriter *This = impl_from_IXmlWriter(iface);
976     struct element *element;
977 
978     TRACE("%p\n", This);
979 
980     switch (This->state)
981     {
982     case XmlWriterState_Initial:
983         return E_UNEXPECTED;
984     case XmlWriterState_Ready:
985     case XmlWriterState_DocClosed:
986         This->state = XmlWriterState_DocClosed;
987         return WR_E_INVALIDACTION;
988     case XmlWriterState_InvalidEncoding:
989         return MX_E_ENCODING;
990     default:
991         ;
992     }
993 
994     element = pop_element(This);
995     if (!element)
996         return WR_E_INVALIDACTION;
997 
998     writer_close_starttag(This);
999     writer_dec_indent(This);
1000 
1001     /* don't force full end tag to the next line */
1002     if (This->state == XmlWriterState_ElemStarted)
1003         This->state = XmlWriterState_Content;
1004     else
1005         write_node_indent(This);
1006 
1007     /* write full end tag */
1008     write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
1009     write_output_buffer(This->output, element->qname, element->len);
1010     write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1011 
1012     return S_OK;
1013 }
1014 
1015 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1016 {
1017     xmlwriter *This = impl_from_IXmlWriter(iface);
1018 
1019     FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1020 
1021     switch (This->state)
1022     {
1023     case XmlWriterState_Initial:
1024         return E_UNEXPECTED;
1025     case XmlWriterState_Ready:
1026     case XmlWriterState_DocClosed:
1027         This->state = XmlWriterState_DocClosed;
1028         return WR_E_INVALIDACTION;
1029     case XmlWriterState_InvalidEncoding:
1030         return MX_E_ENCODING;
1031     default:
1032         ;
1033     }
1034 
1035     return E_NOTIMPL;
1036 }
1037 
1038 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1039 {
1040     xmlwriter *This = impl_from_IXmlWriter(iface);
1041 
1042     FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1043 
1044     switch (This->state)
1045     {
1046     case XmlWriterState_Initial:
1047         return E_UNEXPECTED;
1048     case XmlWriterState_Ready:
1049     case XmlWriterState_DocClosed:
1050         This->state = XmlWriterState_DocClosed;
1051         return WR_E_INVALIDACTION;
1052     case XmlWriterState_InvalidEncoding:
1053         return MX_E_ENCODING;
1054     default:
1055         ;
1056     }
1057 
1058     return E_NOTIMPL;
1059 }
1060 
1061 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1062                             BOOL fWriteDefaultAttributes)
1063 {
1064     xmlwriter *This = impl_from_IXmlWriter(iface);
1065 
1066     FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1067 
1068     return E_NOTIMPL;
1069 }
1070 
1071 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1072                                    BOOL fWriteDefaultAttributes)
1073 {
1074     xmlwriter *This = impl_from_IXmlWriter(iface);
1075 
1076     FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1077 
1078     return E_NOTIMPL;
1079 }
1080 
1081 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1082                                              LPCWSTR text)
1083 {
1084     xmlwriter *This = impl_from_IXmlWriter(iface);
1085     static const WCHAR xmlW[] = {'x','m','l',0};
1086     static const WCHAR openpiW[] = {'<','?'};
1087 
1088     TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1089 
1090     switch (This->state)
1091     {
1092     case XmlWriterState_Initial:
1093         return E_UNEXPECTED;
1094     case XmlWriterState_InvalidEncoding:
1095         return MX_E_ENCODING;
1096     case XmlWriterState_DocStarted:
1097         if (!strcmpW(name, xmlW))
1098             return WR_E_INVALIDACTION;
1099         break;
1100     case XmlWriterState_ElemStarted:
1101     case XmlWriterState_DocClosed:
1102         return WR_E_INVALIDACTION;
1103     default:
1104         ;
1105     }
1106 
1107     write_encoding_bom(This);
1108     write_node_indent(This);
1109     write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
1110     write_output_buffer(This->output, name, -1);
1111     write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
1112     write_output_buffer(This->output, text, -1);
1113     write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1114 
1115     if (!strcmpW(name, xmlW))
1116         This->state = XmlWriterState_PIDocStarted;
1117 
1118     return S_OK;
1119 }
1120 
1121 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1122                                      LPCWSTR pwszNamespaceUri)
1123 {
1124     xmlwriter *This = impl_from_IXmlWriter(iface);
1125 
1126     FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1127 
1128     switch (This->state)
1129     {
1130     case XmlWriterState_Initial:
1131         return E_UNEXPECTED;
1132     case XmlWriterState_InvalidEncoding:
1133         return MX_E_ENCODING;
1134     case XmlWriterState_DocClosed:
1135         return WR_E_INVALIDACTION;
1136     default:
1137         ;
1138     }
1139 
1140     return E_NOTIMPL;
1141 }
1142 
1143 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1144 {
1145     xmlwriter *This = impl_from_IXmlWriter(iface);
1146 
1147     TRACE("%p %s\n", This, debugstr_w(data));
1148 
1149     if (!data)
1150         return S_OK;
1151 
1152     switch (This->state)
1153     {
1154     case XmlWriterState_Initial:
1155         return E_UNEXPECTED;
1156     case XmlWriterState_Ready:
1157         write_xmldecl(This, XmlStandalone_Omit);
1158         /* fallthrough */
1159     case XmlWriterState_DocStarted:
1160     case XmlWriterState_PIDocStarted:
1161         break;
1162     case XmlWriterState_InvalidEncoding:
1163         return MX_E_ENCODING;
1164     default:
1165         This->state = XmlWriterState_DocClosed;
1166         return WR_E_INVALIDACTION;
1167     }
1168 
1169     write_output_buffer(This->output, data, -1);
1170     return S_OK;
1171 }
1172 
1173 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface,  const WCHAR *pwch, UINT cwch)
1174 {
1175     xmlwriter *This = impl_from_IXmlWriter(iface);
1176 
1177     FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1178 
1179     switch (This->state)
1180     {
1181     case XmlWriterState_Initial:
1182         return E_UNEXPECTED;
1183     case XmlWriterState_InvalidEncoding:
1184         return MX_E_ENCODING;
1185     case XmlWriterState_DocClosed:
1186         return WR_E_INVALIDACTION;
1187     default:
1188         ;
1189     }
1190 
1191     return E_NOTIMPL;
1192 }
1193 
1194 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1195 {
1196     xmlwriter *This = impl_from_IXmlWriter(iface);
1197 
1198     TRACE("(%p)->(%d)\n", This, standalone);
1199 
1200     switch (This->state)
1201     {
1202     case XmlWriterState_Initial:
1203         return E_UNEXPECTED;
1204     case XmlWriterState_PIDocStarted:
1205         This->state = XmlWriterState_DocStarted;
1206         return S_OK;
1207     case XmlWriterState_Ready:
1208         break;
1209     case XmlWriterState_InvalidEncoding:
1210         return MX_E_ENCODING;
1211     default:
1212         This->state = XmlWriterState_DocClosed;
1213         return WR_E_INVALIDACTION;
1214     }
1215 
1216     return write_xmldecl(This, standalone);
1217 }
1218 
1219 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1220 {
1221     xmlwriter *This = impl_from_IXmlWriter(iface);
1222     struct element *element;
1223 
1224     TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1225 
1226     if (!local_name)
1227         return E_INVALIDARG;
1228 
1229     switch (This->state)
1230     {
1231     case XmlWriterState_Initial:
1232         return E_UNEXPECTED;
1233     case XmlWriterState_InvalidEncoding:
1234         return MX_E_ENCODING;
1235     case XmlWriterState_DocClosed:
1236         return WR_E_INVALIDACTION;
1237     default:
1238         ;
1239     }
1240 
1241     /* close pending element */
1242     if (This->starttagopen)
1243         write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1244 
1245     element = alloc_element(This, prefix, local_name);
1246     if (!element)
1247         return E_OUTOFMEMORY;
1248 
1249     write_encoding_bom(This);
1250     write_node_indent(This);
1251 
1252     This->state = XmlWriterState_ElemStarted;
1253     This->starttagopen = TRUE;
1254 
1255     push_element(This, element);
1256 
1257     write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
1258     write_output_qname(This->output, prefix, local_name);
1259     writer_inc_indent(This);
1260 
1261     return S_OK;
1262 }
1263 
1264 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1265 {
1266     static const WCHAR ampW[] = {'&','a','m','p',';'};
1267     static const WCHAR ltW[] = {'&','l','t',';'};
1268     static const WCHAR gtW[] = {'&','g','t',';'};
1269 
1270     while (*string)
1271     {
1272         switch (*string)
1273         {
1274         case '<':
1275             write_output_buffer(writer->output, ltW, ARRAY_SIZE(ltW));
1276             break;
1277         case '&':
1278             write_output_buffer(writer->output, ampW, ARRAY_SIZE(ampW));
1279             break;
1280         case '>':
1281             write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
1282             break;
1283         default:
1284             write_output_buffer(writer->output, string, 1);
1285         }
1286 
1287         string++;
1288     }
1289 }
1290 
1291 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1292 {
1293     xmlwriter *This = impl_from_IXmlWriter(iface);
1294 
1295     TRACE("%p %s\n", This, debugstr_w(string));
1296 
1297     if (!string)
1298         return S_OK;
1299 
1300     switch (This->state)
1301     {
1302     case XmlWriterState_Initial:
1303         return E_UNEXPECTED;
1304     case XmlWriterState_ElemStarted:
1305         writer_close_starttag(This);
1306         break;
1307     case XmlWriterState_Ready:
1308     case XmlWriterState_DocClosed:
1309         This->state = XmlWriterState_DocClosed;
1310         return WR_E_INVALIDACTION;
1311     case XmlWriterState_InvalidEncoding:
1312         return MX_E_ENCODING;
1313     default:
1314         ;
1315     }
1316 
1317     write_escaped_string(This, string);
1318     return S_OK;
1319 }
1320 
1321 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1322 {
1323     xmlwriter *This = impl_from_IXmlWriter(iface);
1324 
1325     FIXME("%p %d %d\n", This, wchLow, wchHigh);
1326 
1327     return E_NOTIMPL;
1328 }
1329 
1330 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1331 {
1332     xmlwriter *This = impl_from_IXmlWriter(iface);
1333 
1334     FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1335 
1336     return E_NOTIMPL;
1337 }
1338 
1339 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1340 {
1341     xmlwriter *This = impl_from_IXmlWriter(iface);
1342 
1343     TRACE("%p\n", This);
1344 
1345     return writeroutput_flush_stream(This->output);
1346 }
1347 
1348 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1349 {
1350     xmlwriter_QueryInterface,
1351     xmlwriter_AddRef,
1352     xmlwriter_Release,
1353     xmlwriter_SetOutput,
1354     xmlwriter_GetProperty,
1355     xmlwriter_SetProperty,
1356     xmlwriter_WriteAttributes,
1357     xmlwriter_WriteAttributeString,
1358     xmlwriter_WriteCData,
1359     xmlwriter_WriteCharEntity,
1360     xmlwriter_WriteChars,
1361     xmlwriter_WriteComment,
1362     xmlwriter_WriteDocType,
1363     xmlwriter_WriteElementString,
1364     xmlwriter_WriteEndDocument,
1365     xmlwriter_WriteEndElement,
1366     xmlwriter_WriteEntityRef,
1367     xmlwriter_WriteFullEndElement,
1368     xmlwriter_WriteName,
1369     xmlwriter_WriteNmToken,
1370     xmlwriter_WriteNode,
1371     xmlwriter_WriteNodeShallow,
1372     xmlwriter_WriteProcessingInstruction,
1373     xmlwriter_WriteQualifiedName,
1374     xmlwriter_WriteRaw,
1375     xmlwriter_WriteRawChars,
1376     xmlwriter_WriteStartDocument,
1377     xmlwriter_WriteStartElement,
1378     xmlwriter_WriteString,
1379     xmlwriter_WriteSurrogateCharEntity,
1380     xmlwriter_WriteWhitespace,
1381     xmlwriter_Flush
1382 };
1383 
1384 /** IXmlWriterOutput **/
1385 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1386 {
1387     xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1388 
1389     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1390 
1391     if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1392         IsEqualGUID(riid, &IID_IUnknown))
1393     {
1394         *ppvObject = iface;
1395     }
1396     else
1397     {
1398         WARN("interface %s not implemented\n", debugstr_guid(riid));
1399         *ppvObject = NULL;
1400         return E_NOINTERFACE;
1401     }
1402 
1403     IUnknown_AddRef(iface);
1404 
1405     return S_OK;
1406 }
1407 
1408 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1409 {
1410     xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1411     ULONG ref = InterlockedIncrement(&This->ref);
1412     TRACE("(%p)->(%d)\n", This, ref);
1413     return ref;
1414 }
1415 
1416 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1417 {
1418     xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1419     LONG ref = InterlockedDecrement(&This->ref);
1420 
1421     TRACE("(%p)->(%d)\n", This, ref);
1422 
1423     if (ref == 0)
1424     {
1425         IMalloc *imalloc = This->imalloc;
1426         if (This->output) IUnknown_Release(This->output);
1427         if (This->stream) ISequentialStream_Release(This->stream);
1428         free_output_buffer(This);
1429         writeroutput_free(This, This->encoding_name);
1430         writeroutput_free(This, This);
1431         if (imalloc) IMalloc_Release(imalloc);
1432     }
1433 
1434     return ref;
1435 }
1436 
1437 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1438 {
1439     xmlwriteroutput_QueryInterface,
1440     xmlwriteroutput_AddRef,
1441     xmlwriteroutput_Release
1442 };
1443 
1444 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1445 {
1446     xmlwriter *writer;
1447     HRESULT hr;
1448 
1449     TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1450 
1451     if (imalloc)
1452         writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1453     else
1454         writer = heap_alloc(sizeof(*writer));
1455     if(!writer) return E_OUTOFMEMORY;
1456 
1457     writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1458     writer->ref = 1;
1459     writer->imalloc = imalloc;
1460     if (imalloc) IMalloc_AddRef(imalloc);
1461     writer->output = NULL;
1462     writer->indent_level = 0;
1463     writer->indent = FALSE;
1464     writer->bom = TRUE;
1465     writer->omitxmldecl = FALSE;
1466     writer->conformance = XmlConformanceLevel_Document;
1467     writer->state = XmlWriterState_Initial;
1468     writer->bomwritten = FALSE;
1469     writer->starttagopen = FALSE;
1470     list_init(&writer->elements);
1471 
1472     hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
1473     IXmlWriter_Release(&writer->IXmlWriter_iface);
1474 
1475     TRACE("returning iface %p, hr %#x\n", *obj, hr);
1476 
1477     return hr;
1478 }
1479 
1480 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1481     const WCHAR *encoding_name, IXmlWriterOutput **output)
1482 {
1483     xmlwriteroutput *writeroutput;
1484     HRESULT hr;
1485 
1486     *output = NULL;
1487 
1488     if (imalloc)
1489         writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1490     else
1491         writeroutput = heap_alloc(sizeof(*writeroutput));
1492     if (!writeroutput)
1493         return E_OUTOFMEMORY;
1494 
1495     writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1496     writeroutput->ref = 1;
1497     writeroutput->imalloc = imalloc;
1498     if (imalloc)
1499         IMalloc_AddRef(imalloc);
1500     writeroutput->encoding = encoding;
1501     writeroutput->stream = NULL;
1502     hr = init_output_buffer(writeroutput);
1503     if (FAILED(hr)) {
1504         IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1505         return hr;
1506     }
1507 
1508     if (encoding_name) {
1509         unsigned int size = (strlenW(encoding_name) + 1) * sizeof(WCHAR);
1510         writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
1511         memcpy(writeroutput->encoding_name, encoding_name, size);
1512     }
1513     else
1514         writeroutput->encoding_name = NULL;
1515 
1516     IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1517 
1518     *output = &writeroutput->IXmlWriterOutput_iface;
1519 
1520     TRACE("returning iface %p\n", *output);
1521 
1522     return S_OK;
1523 }
1524 
1525 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1526                                                      IMalloc *imalloc,
1527                                                      LPCWSTR encoding,
1528                                                      IXmlWriterOutput **output)
1529 {
1530     static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1531     xml_encoding xml_enc;
1532 
1533     TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1534 
1535     if (!stream || !output) return E_INVALIDARG;
1536 
1537     xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1);
1538     return create_writer_output(stream, imalloc, xml_enc, encoding, output);
1539 }
1540 
1541 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream,
1542                                                          IMalloc *imalloc,
1543                                                          UINT codepage,
1544                                                          IXmlWriterOutput **output)
1545 {
1546     xml_encoding xml_enc;
1547 
1548     TRACE("%p %p %u %p\n", stream, imalloc, codepage, output);
1549 
1550     if (!stream || !output) return E_INVALIDARG;
1551 
1552     xml_enc = get_encoding_from_codepage(codepage);
1553     return create_writer_output(stream, imalloc, xml_enc, NULL, output);
1554 }
1555