xmllite/writer: Fix formatted output with WriteFullEndElement().
[wine.git] / dlls / xmllite / writer.c
blobbcc0e85804c2cf655b1c28ac4101ac8317b922e2
1 /*
2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014, 2016 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 <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"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
40 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
42 static const WCHAR closeelementW[] = {'<','/'};
43 static const WCHAR closepiW[] = {'?','>'};
44 static const WCHAR ltW[] = {'<'};
45 static const WCHAR gtW[] = {'>'};
46 static const WCHAR spaceW[] = {' '};
47 static const WCHAR quoteW[] = {'"'};
49 struct output_buffer
51 char *data;
52 unsigned int allocated;
53 unsigned int written;
54 UINT codepage;
57 typedef enum
59 XmlWriterState_Initial, /* output is not set yet */
60 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
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;
68 typedef struct
70 IXmlWriterOutput IXmlWriterOutput_iface;
71 LONG ref;
72 IUnknown *output;
73 ISequentialStream *stream;
74 IMalloc *imalloc;
75 xml_encoding encoding;
76 struct output_buffer buffer;
77 } xmlwriteroutput;
79 static const struct IUnknownVtbl xmlwriteroutputvtbl;
81 struct element
83 struct list entry;
84 WCHAR *qname;
85 unsigned int len; /* qname length in chars */
88 typedef struct _xmlwriter
90 IXmlWriter IXmlWriter_iface;
91 LONG ref;
92 IMalloc *imalloc;
93 xmlwriteroutput *output;
94 unsigned int indent_level;
95 BOOL indent;
96 BOOL bom;
97 BOOL omitxmldecl;
98 XmlConformanceLevel conformance;
99 XmlWriterState state;
100 BOOL bomwritten;
101 BOOL starttagopen;
102 struct list elements;
103 } xmlwriter;
105 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
107 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
110 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
112 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
115 static const char *debugstr_writer_prop(XmlWriterProperty prop)
117 static const char * const prop_names[] =
119 "MultiLanguage",
120 "Indent",
121 "ByteOrderMark",
122 "OmitXmlDeclaration",
123 "ConformanceLevel"
126 if (prop > _XmlWriterProperty_Last)
127 return wine_dbg_sprintf("unknown property=%d", prop);
129 return prop_names[prop];
132 /* writer output memory allocation functions */
133 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
135 return m_alloc(output->imalloc, len);
138 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
140 m_free(output->imalloc, mem);
143 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
145 return m_realloc(output->imalloc, mem, len);
148 /* writer memory allocation functions */
149 static inline void *writer_alloc(xmlwriter *writer, size_t len)
151 return m_alloc(writer->imalloc, len);
154 static inline void writer_free(xmlwriter *writer, void *mem)
156 m_free(writer->imalloc, mem);
159 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
161 struct element *ret;
162 int len;
164 ret = writer_alloc(writer, sizeof(*ret));
165 if (!ret) return ret;
167 len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
168 len += strlenW(local);
170 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
171 ret->len = len;
172 if (prefix) {
173 static const WCHAR colonW[] = {':',0};
174 strcpyW(ret->qname, prefix);
175 strcatW(ret->qname, colonW);
177 else
178 ret->qname[0] = 0;
179 strcatW(ret->qname, local);
181 return ret;
184 static void free_element(xmlwriter *writer, struct element *element)
186 writer_free(writer, element->qname);
187 writer_free(writer, element);
190 static void push_element(xmlwriter *writer, struct element *element)
192 list_add_head(&writer->elements, &element->entry);
195 static struct element *pop_element(xmlwriter *writer)
197 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
199 if (element)
200 list_remove(&element->entry);
202 return element;
205 static HRESULT init_output_buffer(xmlwriteroutput *output)
207 struct output_buffer *buffer = &output->buffer;
208 const int initial_len = 0x2000;
209 HRESULT hr;
210 UINT cp;
212 hr = get_code_page(output->encoding, &cp);
213 if (FAILED(hr)) return hr;
215 buffer->data = writeroutput_alloc(output, initial_len);
216 if (!buffer->data) return E_OUTOFMEMORY;
218 memset(buffer->data, 0, 4);
219 buffer->allocated = initial_len;
220 buffer->written = 0;
221 buffer->codepage = cp;
223 return S_OK;
226 static void free_output_buffer(xmlwriteroutput *output)
228 struct output_buffer *buffer = &output->buffer;
229 writeroutput_free(output, buffer->data);
230 buffer->data = NULL;
231 buffer->allocated = 0;
232 buffer->written = 0;
235 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
237 struct output_buffer *buffer = &output->buffer;
238 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
239 if (buffer->allocated < buffer->written + length + 4) {
240 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
241 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
242 if (!ptr) return E_OUTOFMEMORY;
243 buffer->data = ptr;
244 buffer->allocated = grown_size;
247 return S_OK;
250 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
252 struct output_buffer *buffer = &output->buffer;
253 int length;
254 HRESULT hr;
255 char *ptr;
257 if (buffer->codepage != ~0) {
258 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
259 hr = grow_output_buffer(output, length);
260 if (FAILED(hr)) return hr;
261 ptr = buffer->data + buffer->written;
262 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
263 buffer->written += len == -1 ? length-1 : length;
265 else {
266 /* WCHAR data just copied */
267 length = len == -1 ? strlenW(data) : len;
268 if (length) {
269 length *= sizeof(WCHAR);
271 hr = grow_output_buffer(output, length);
272 if (FAILED(hr)) return hr;
273 ptr = buffer->data + buffer->written;
275 memcpy(ptr, data, length);
276 buffer->written += length;
277 ptr += length;
278 /* null termination */
279 *(WCHAR*)ptr = 0;
283 return S_OK;
286 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
288 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
289 write_output_buffer(output, data, len);
290 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
291 return S_OK;
294 /* TODO: test if we need to validate char range */
295 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
297 if (prefix) {
298 static const WCHAR colW[] = {':'};
299 write_output_buffer(output, prefix, -1);
300 write_output_buffer(output, colW, ARRAY_SIZE(colW));
303 write_output_buffer(output, local_name, -1);
305 return S_OK;
308 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
310 if (writeroutput->stream) {
311 ISequentialStream_Release(writeroutput->stream);
312 writeroutput->stream = NULL;
316 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
318 HRESULT hr;
320 writeroutput_release_stream(writeroutput);
321 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
322 if (hr != S_OK)
323 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
325 return hr;
328 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
330 struct output_buffer *buffer;
331 ULONG written, offset = 0;
332 HRESULT hr;
334 if (!output || !output->stream)
335 return S_OK;
337 buffer = &output->buffer;
339 /* It will loop forever until everything is written or an error occurred. */
340 do {
341 written = 0;
342 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
343 if (FAILED(hr)) {
344 WARN("write to stream failed (0x%08x)\n", hr);
345 buffer->written = 0;
346 return hr;
349 offset += written;
350 buffer->written -= written;
351 } while (buffer->written > 0);
353 return S_OK;
356 static HRESULT write_encoding_bom(xmlwriter *writer)
358 if (!writer->bom || writer->bomwritten) return S_OK;
360 if (writer->output->encoding == XmlEncoding_UTF16) {
361 static const char utf16bom[] = {0xff, 0xfe};
362 struct output_buffer *buffer = &writer->output->buffer;
363 int len = sizeof(utf16bom);
364 HRESULT hr;
366 hr = grow_output_buffer(writer->output, len);
367 if (FAILED(hr)) return hr;
368 memcpy(buffer->data + buffer->written, utf16bom, len);
369 buffer->written += len;
372 writer->bomwritten = TRUE;
373 return S_OK;
376 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
378 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
379 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
381 write_encoding_bom(writer);
382 writer->state = XmlWriterState_DocStarted;
383 if (writer->omitxmldecl) return S_OK;
385 /* version */
386 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
388 /* encoding */
389 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
390 write_output_buffer_quoted(writer->output, get_encoding_name(writer->output->encoding), -1);
392 /* standalone */
393 if (standalone == XmlStandalone_Omit)
394 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
395 else {
396 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
397 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
398 static const WCHAR noW[] = {'n','o','\"','?','>'};
400 write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
401 if (standalone == XmlStandalone_Yes)
402 write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
403 else
404 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
407 return S_OK;
410 static HRESULT writer_close_starttag(xmlwriter *writer)
412 HRESULT hr;
414 if (!writer->starttagopen) return S_OK;
415 hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
416 writer->starttagopen = FALSE;
417 return hr;
420 static void writer_inc_indent(xmlwriter *writer)
422 writer->indent_level++;
425 static void writer_dec_indent(xmlwriter *writer)
427 if (writer->indent_level)
428 writer->indent_level--;
431 static void write_node_indent(xmlwriter *writer)
433 static const WCHAR dblspaceW[] = {' ',' '};
434 static const WCHAR crlfW[] = {'\r','\n'};
435 unsigned int indent_level = writer->indent_level;
437 if (!writer->indent)
438 return;
440 if (writer->output->buffer.written)
441 write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW));
442 while (indent_level--)
443 write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW));
446 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
448 xmlwriter *This = impl_from_IXmlWriter(iface);
450 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
452 if (IsEqualGUID(riid, &IID_IUnknown) ||
453 IsEqualGUID(riid, &IID_IXmlWriter))
455 *ppvObject = iface;
458 IXmlWriter_AddRef(iface);
460 return S_OK;
463 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
465 xmlwriter *This = impl_from_IXmlWriter(iface);
466 TRACE("%p\n", This);
467 return InterlockedIncrement(&This->ref);
470 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
472 xmlwriter *This = impl_from_IXmlWriter(iface);
473 LONG ref;
475 TRACE("%p\n", This);
477 ref = InterlockedDecrement(&This->ref);
478 if (ref == 0) {
479 struct element *element, *element2;
480 IMalloc *imalloc = This->imalloc;
482 IXmlWriter_Flush(iface);
483 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
485 /* element stack */
486 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
487 list_remove(&element->entry);
488 free_element(This, element);
491 writer_free(This, This);
492 if (imalloc) IMalloc_Release(imalloc);
495 return ref;
498 /*** IXmlWriter methods ***/
499 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
501 xmlwriter *This = impl_from_IXmlWriter(iface);
502 IXmlWriterOutput *writeroutput;
503 HRESULT hr;
505 TRACE("(%p)->(%p)\n", This, output);
507 if (This->output) {
508 writeroutput_release_stream(This->output);
509 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
510 This->output = NULL;
511 This->bomwritten = FALSE;
512 This->indent_level = 0;
515 /* just reset current output */
516 if (!output) {
517 This->state = XmlWriterState_Initial;
518 return S_OK;
521 /* now try IXmlWriterOutput, ISequentialStream, IStream */
522 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
523 if (hr == S_OK) {
524 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
525 This->output = impl_from_IXmlWriterOutput(writeroutput);
526 else {
527 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
528 writeroutput, writeroutput->lpVtbl);
529 IUnknown_Release(writeroutput);
530 return E_FAIL;
535 if (hr != S_OK || !writeroutput) {
536 /* create IXmlWriterOutput basing on supplied interface */
537 hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
538 if (hr != S_OK) return hr;
539 This->output = impl_from_IXmlWriterOutput(writeroutput);
542 This->state = XmlWriterState_Ready;
543 return writeroutput_query_for_stream(This->output);
546 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
548 xmlwriter *This = impl_from_IXmlWriter(iface);
550 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
552 if (!value) return E_INVALIDARG;
554 switch (property)
556 case XmlWriterProperty_Indent:
557 *value = This->indent;
558 break;
559 case XmlWriterProperty_ByteOrderMark:
560 *value = This->bom;
561 break;
562 case XmlWriterProperty_OmitXmlDeclaration:
563 *value = This->omitxmldecl;
564 break;
565 case XmlWriterProperty_ConformanceLevel:
566 *value = This->conformance;
567 break;
568 default:
569 FIXME("Unimplemented property (%u)\n", property);
570 return E_NOTIMPL;
573 return S_OK;
576 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
578 xmlwriter *This = impl_from_IXmlWriter(iface);
580 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
582 switch (property)
584 case XmlWriterProperty_Indent:
585 This->indent = !!value;
586 break;
587 case XmlWriterProperty_ByteOrderMark:
588 This->bom = !!value;
589 break;
590 case XmlWriterProperty_OmitXmlDeclaration:
591 This->omitxmldecl = !!value;
592 break;
593 default:
594 FIXME("Unimplemented property (%u)\n", property);
595 return E_NOTIMPL;
598 return S_OK;
601 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
602 BOOL fWriteDefaultAttributes)
604 xmlwriter *This = impl_from_IXmlWriter(iface);
606 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
608 return E_NOTIMPL;
611 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
612 LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
614 static const WCHAR eqW[] = {'=','"'};
615 xmlwriter *This = impl_from_IXmlWriter(iface);
617 TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
618 debugstr_w(ns_uri), debugstr_w(value));
620 switch (This->state)
622 case XmlWriterState_Initial:
623 return E_UNEXPECTED;
624 case XmlWriterState_Ready:
625 case XmlWriterState_DocClosed:
626 This->state = XmlWriterState_DocClosed;
627 return WR_E_INVALIDACTION;
628 default:
632 if (ns_prefix || ns_uri)
634 FIXME("namespaces are not supported.\n");
635 return E_NOTIMPL;
638 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
639 write_output_buffer(This->output, local_name, -1);
640 write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
641 write_output_buffer(This->output, value, -1);
642 write_output_buffer(This->output, quoteW, ARRAY_SIZE(quoteW));
644 return S_OK;
647 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
649 static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
650 static const WCHAR cdatacloseW[] = {']',']','>'};
651 write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
652 if (data)
653 write_output_buffer(output, data, len);
654 write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
657 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
659 xmlwriter *This = impl_from_IXmlWriter(iface);
660 int len;
662 TRACE("%p %s\n", This, debugstr_w(data));
664 switch (This->state)
666 case XmlWriterState_Initial:
667 return E_UNEXPECTED;
668 case XmlWriterState_ElemStarted:
669 writer_close_starttag(This);
670 break;
671 case XmlWriterState_Ready:
672 case XmlWriterState_DocClosed:
673 This->state = XmlWriterState_DocClosed;
674 return WR_E_INVALIDACTION;
675 default:
679 len = data ? strlenW(data) : 0;
681 write_node_indent(This);
682 if (!len)
683 write_cdata_section(This->output, NULL, 0);
684 else {
685 static const WCHAR cdatacloseW[] = {']',']','>',0};
686 while (len) {
687 const WCHAR *str = strstrW(data, cdatacloseW);
688 if (str) {
689 str += 2;
690 write_cdata_section(This->output, data, str - data);
691 len -= str - data;
692 data = str;
694 else {
695 write_cdata_section(This->output, data, len);
696 break;
701 return S_OK;
704 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
706 static const WCHAR fmtW[] = {'&','#','x','%','x',';',0};
707 xmlwriter *This = impl_from_IXmlWriter(iface);
708 WCHAR bufW[16];
710 TRACE("%p %#x\n", This, ch);
712 switch (This->state)
714 case XmlWriterState_Initial:
715 return E_UNEXPECTED;
716 case XmlWriterState_ElemStarted:
717 writer_close_starttag(This);
718 break;
719 case XmlWriterState_DocClosed:
720 return WR_E_INVALIDACTION;
721 default:
725 sprintfW(bufW, fmtW, ch);
726 write_output_buffer(This->output, bufW, -1);
728 return S_OK;
731 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
733 xmlwriter *This = impl_from_IXmlWriter(iface);
735 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
737 switch (This->state)
739 case XmlWriterState_Initial:
740 return E_UNEXPECTED;
741 case XmlWriterState_DocClosed:
742 return WR_E_INVALIDACTION;
743 default:
747 return E_NOTIMPL;
751 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
753 static const WCHAR copenW[] = {'<','!','-','-'};
754 static const WCHAR ccloseW[] = {'-','-','>'};
755 xmlwriter *This = impl_from_IXmlWriter(iface);
757 TRACE("%p %s\n", This, debugstr_w(comment));
759 switch (This->state)
761 case XmlWriterState_Initial:
762 return E_UNEXPECTED;
763 case XmlWriterState_ElemStarted:
764 writer_close_starttag(This);
765 break;
766 case XmlWriterState_DocClosed:
767 return WR_E_INVALIDACTION;
768 default:
772 write_node_indent(This);
773 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
774 if (comment) {
775 int len = strlenW(comment), i;
777 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
778 comment string */
779 if (len > 1) {
780 for (i = 0; i < len; i++) {
781 write_output_buffer(This->output, comment + i, 1);
782 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
783 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
786 else
787 write_output_buffer(This->output, comment, len);
789 if (len && comment[len-1] == '-')
790 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
792 write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
794 return S_OK;
797 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
798 LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
800 xmlwriter *This = impl_from_IXmlWriter(iface);
802 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
803 wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
805 return E_NOTIMPL;
808 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
809 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
811 xmlwriter *This = impl_from_IXmlWriter(iface);
813 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
814 wine_dbgstr_w(uri), wine_dbgstr_w(value));
816 switch (This->state)
818 case XmlWriterState_Initial:
819 return E_UNEXPECTED;
820 case XmlWriterState_ElemStarted:
821 writer_close_starttag(This);
822 break;
823 case XmlWriterState_Ready:
824 case XmlWriterState_DocStarted:
825 case XmlWriterState_PIDocStarted:
826 break;
827 default:
828 This->state = XmlWriterState_DocClosed;
829 return WR_E_INVALIDACTION;
832 write_encoding_bom(This);
833 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
834 write_output_qname(This->output, prefix, local_name);
835 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
837 if (value)
838 write_output_buffer(This->output, value, -1);
840 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
841 write_output_qname(This->output, prefix, local_name);
842 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
843 This->state = XmlWriterState_Content;
845 return S_OK;
848 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
850 xmlwriter *This = impl_from_IXmlWriter(iface);
852 TRACE("%p\n", This);
854 switch (This->state)
856 case XmlWriterState_Initial:
857 return E_UNEXPECTED;
858 case XmlWriterState_Ready:
859 case XmlWriterState_DocClosed:
860 This->state = XmlWriterState_DocClosed;
861 return WR_E_INVALIDACTION;
862 default:
866 /* empty element stack */
867 while (IXmlWriter_WriteEndElement(iface) == S_OK)
870 This->state = XmlWriterState_DocClosed;
871 return S_OK;
874 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
876 xmlwriter *This = impl_from_IXmlWriter(iface);
877 struct element *element;
879 TRACE("%p\n", This);
881 switch (This->state)
883 case XmlWriterState_Initial:
884 return E_UNEXPECTED;
885 case XmlWriterState_Ready:
886 case XmlWriterState_DocClosed:
887 This->state = XmlWriterState_DocClosed;
888 return WR_E_INVALIDACTION;
889 default:
893 element = pop_element(This);
894 if (!element)
895 return WR_E_INVALIDACTION;
897 writer_dec_indent(This);
899 if (This->starttagopen) {
900 static WCHAR closetagW[] = {' ','/','>'};
901 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
902 This->starttagopen = FALSE;
904 else {
905 /* write full end tag */
906 write_node_indent(This);
907 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
908 write_output_buffer(This->output, element->qname, element->len);
909 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
912 return S_OK;
915 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
917 xmlwriter *This = impl_from_IXmlWriter(iface);
919 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
921 switch (This->state)
923 case XmlWriterState_Initial:
924 return E_UNEXPECTED;
925 case XmlWriterState_DocClosed:
926 return WR_E_INVALIDACTION;
927 default:
931 return E_NOTIMPL;
934 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
936 xmlwriter *This = impl_from_IXmlWriter(iface);
937 struct element *element;
939 TRACE("%p\n", This);
941 switch (This->state)
943 case XmlWriterState_Initial:
944 return E_UNEXPECTED;
945 case XmlWriterState_Ready:
946 case XmlWriterState_DocClosed:
947 This->state = XmlWriterState_DocClosed;
948 return WR_E_INVALIDACTION;
949 default:
953 element = pop_element(This);
954 if (!element)
955 return WR_E_INVALIDACTION;
957 writer_close_starttag(This);
958 writer_dec_indent(This);
960 /* don't force full end tag to the next line */
961 if (This->state == XmlWriterState_ElemStarted)
962 This->state = XmlWriterState_Content;
963 else
964 write_node_indent(This);
966 /* write full end tag */
967 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
968 write_output_buffer(This->output, element->qname, element->len);
969 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
971 return S_OK;
974 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
976 xmlwriter *This = impl_from_IXmlWriter(iface);
978 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
980 switch (This->state)
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 default:
992 return E_NOTIMPL;
995 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
997 xmlwriter *This = impl_from_IXmlWriter(iface);
999 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1001 switch (This->state)
1003 case XmlWriterState_Initial:
1004 return E_UNEXPECTED;
1005 case XmlWriterState_Ready:
1006 case XmlWriterState_DocClosed:
1007 This->state = XmlWriterState_DocClosed;
1008 return WR_E_INVALIDACTION;
1009 default:
1013 return E_NOTIMPL;
1016 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1017 BOOL fWriteDefaultAttributes)
1019 xmlwriter *This = impl_from_IXmlWriter(iface);
1021 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1023 return E_NOTIMPL;
1026 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1027 BOOL fWriteDefaultAttributes)
1029 xmlwriter *This = impl_from_IXmlWriter(iface);
1031 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1033 return E_NOTIMPL;
1036 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1037 LPCWSTR text)
1039 xmlwriter *This = impl_from_IXmlWriter(iface);
1040 static const WCHAR xmlW[] = {'x','m','l',0};
1041 static const WCHAR openpiW[] = {'<','?'};
1043 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1045 switch (This->state)
1047 case XmlWriterState_Initial:
1048 return E_UNEXPECTED;
1049 case XmlWriterState_DocStarted:
1050 if (!strcmpW(name, xmlW))
1051 return WR_E_INVALIDACTION;
1052 break;
1053 case XmlWriterState_ElemStarted:
1054 case XmlWriterState_DocClosed:
1055 return WR_E_INVALIDACTION;
1056 default:
1060 write_encoding_bom(This);
1061 write_node_indent(This);
1062 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
1063 write_output_buffer(This->output, name, -1);
1064 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
1065 write_output_buffer(This->output, text, -1);
1066 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1068 if (!strcmpW(name, xmlW))
1069 This->state = XmlWriterState_PIDocStarted;
1071 return S_OK;
1074 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1075 LPCWSTR pwszNamespaceUri)
1077 xmlwriter *This = impl_from_IXmlWriter(iface);
1079 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1081 switch (This->state)
1083 case XmlWriterState_Initial:
1084 return E_UNEXPECTED;
1085 case XmlWriterState_DocClosed:
1086 return WR_E_INVALIDACTION;
1087 default:
1091 return E_NOTIMPL;
1094 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1096 xmlwriter *This = impl_from_IXmlWriter(iface);
1098 TRACE("%p %s\n", This, debugstr_w(data));
1100 if (!data)
1101 return S_OK;
1103 switch (This->state)
1105 case XmlWriterState_Initial:
1106 return E_UNEXPECTED;
1107 case XmlWriterState_Ready:
1108 write_xmldecl(This, XmlStandalone_Omit);
1109 /* fallthrough */
1110 case XmlWriterState_DocStarted:
1111 case XmlWriterState_PIDocStarted:
1112 break;
1113 default:
1114 This->state = XmlWriterState_DocClosed;
1115 return WR_E_INVALIDACTION;
1118 write_output_buffer(This->output, data, -1);
1119 return S_OK;
1122 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1124 xmlwriter *This = impl_from_IXmlWriter(iface);
1126 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1128 switch (This->state)
1130 case XmlWriterState_Initial:
1131 return E_UNEXPECTED;
1132 case XmlWriterState_DocClosed:
1133 return WR_E_INVALIDACTION;
1134 default:
1138 return E_NOTIMPL;
1141 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1143 xmlwriter *This = impl_from_IXmlWriter(iface);
1145 TRACE("(%p)->(%d)\n", This, standalone);
1147 switch (This->state)
1149 case XmlWriterState_Initial:
1150 return E_UNEXPECTED;
1151 case XmlWriterState_PIDocStarted:
1152 This->state = XmlWriterState_DocStarted;
1153 return S_OK;
1154 case XmlWriterState_Ready:
1155 break;
1156 default:
1157 This->state = XmlWriterState_DocClosed;
1158 return WR_E_INVALIDACTION;
1161 return write_xmldecl(This, standalone);
1164 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1166 xmlwriter *This = impl_from_IXmlWriter(iface);
1167 struct element *element;
1169 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1171 if (!local_name)
1172 return E_INVALIDARG;
1174 switch (This->state)
1176 case XmlWriterState_Initial:
1177 return E_UNEXPECTED;
1178 case XmlWriterState_DocClosed:
1179 return WR_E_INVALIDACTION;
1180 default:
1184 /* close pending element */
1185 if (This->starttagopen)
1186 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1188 element = alloc_element(This, prefix, local_name);
1189 if (!element)
1190 return E_OUTOFMEMORY;
1192 write_encoding_bom(This);
1193 This->state = XmlWriterState_ElemStarted;
1194 This->starttagopen = TRUE;
1196 push_element(This, element);
1198 write_node_indent(This);
1199 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
1200 write_output_qname(This->output, prefix, local_name);
1201 writer_inc_indent(This);
1203 return S_OK;
1206 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, LPCWSTR pwszText)
1208 xmlwriter *This = impl_from_IXmlWriter(iface);
1210 FIXME("%p %s\n", This, wine_dbgstr_w(pwszText));
1212 switch (This->state)
1214 case XmlWriterState_Initial:
1215 return E_UNEXPECTED;
1216 case XmlWriterState_Ready:
1217 case XmlWriterState_DocClosed:
1218 This->state = XmlWriterState_DocClosed;
1219 return WR_E_INVALIDACTION;
1220 default:
1224 return E_NOTIMPL;
1227 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1229 xmlwriter *This = impl_from_IXmlWriter(iface);
1231 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1233 return E_NOTIMPL;
1236 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1238 xmlwriter *This = impl_from_IXmlWriter(iface);
1240 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1242 return E_NOTIMPL;
1245 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1247 xmlwriter *This = impl_from_IXmlWriter(iface);
1249 TRACE("%p\n", This);
1251 return writeroutput_flush_stream(This->output);
1254 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1256 xmlwriter_QueryInterface,
1257 xmlwriter_AddRef,
1258 xmlwriter_Release,
1259 xmlwriter_SetOutput,
1260 xmlwriter_GetProperty,
1261 xmlwriter_SetProperty,
1262 xmlwriter_WriteAttributes,
1263 xmlwriter_WriteAttributeString,
1264 xmlwriter_WriteCData,
1265 xmlwriter_WriteCharEntity,
1266 xmlwriter_WriteChars,
1267 xmlwriter_WriteComment,
1268 xmlwriter_WriteDocType,
1269 xmlwriter_WriteElementString,
1270 xmlwriter_WriteEndDocument,
1271 xmlwriter_WriteEndElement,
1272 xmlwriter_WriteEntityRef,
1273 xmlwriter_WriteFullEndElement,
1274 xmlwriter_WriteName,
1275 xmlwriter_WriteNmToken,
1276 xmlwriter_WriteNode,
1277 xmlwriter_WriteNodeShallow,
1278 xmlwriter_WriteProcessingInstruction,
1279 xmlwriter_WriteQualifiedName,
1280 xmlwriter_WriteRaw,
1281 xmlwriter_WriteRawChars,
1282 xmlwriter_WriteStartDocument,
1283 xmlwriter_WriteStartElement,
1284 xmlwriter_WriteString,
1285 xmlwriter_WriteSurrogateCharEntity,
1286 xmlwriter_WriteWhitespace,
1287 xmlwriter_Flush
1290 /** IXmlWriterOutput **/
1291 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1293 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1295 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1297 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1298 IsEqualGUID(riid, &IID_IUnknown))
1300 *ppvObject = iface;
1302 else
1304 WARN("interface %s not implemented\n", debugstr_guid(riid));
1305 *ppvObject = NULL;
1306 return E_NOINTERFACE;
1309 IUnknown_AddRef(iface);
1311 return S_OK;
1314 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1316 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1317 ULONG ref = InterlockedIncrement(&This->ref);
1318 TRACE("(%p)->(%d)\n", This, ref);
1319 return ref;
1322 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1324 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1325 LONG ref = InterlockedDecrement(&This->ref);
1327 TRACE("(%p)->(%d)\n", This, ref);
1329 if (ref == 0)
1331 IMalloc *imalloc = This->imalloc;
1332 if (This->output) IUnknown_Release(This->output);
1333 if (This->stream) ISequentialStream_Release(This->stream);
1334 free_output_buffer(This);
1335 writeroutput_free(This, This);
1336 if (imalloc) IMalloc_Release(imalloc);
1339 return ref;
1342 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1344 xmlwriteroutput_QueryInterface,
1345 xmlwriteroutput_AddRef,
1346 xmlwriteroutput_Release
1349 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1351 xmlwriter *writer;
1353 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1355 if (!IsEqualGUID(riid, &IID_IXmlWriter))
1357 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1358 return E_FAIL;
1361 if (imalloc)
1362 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1363 else
1364 writer = heap_alloc(sizeof(*writer));
1365 if(!writer) return E_OUTOFMEMORY;
1367 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1368 writer->ref = 1;
1369 writer->imalloc = imalloc;
1370 if (imalloc) IMalloc_AddRef(imalloc);
1371 writer->output = NULL;
1372 writer->indent_level = 0;
1373 writer->indent = FALSE;
1374 writer->bom = TRUE;
1375 writer->omitxmldecl = FALSE;
1376 writer->conformance = XmlConformanceLevel_Document;
1377 writer->state = XmlWriterState_Initial;
1378 writer->bomwritten = FALSE;
1379 writer->starttagopen = FALSE;
1380 list_init(&writer->elements);
1382 *obj = &writer->IXmlWriter_iface;
1384 TRACE("returning iface %p\n", *obj);
1386 return S_OK;
1389 static HRESULT create_writer(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1390 IXmlWriterOutput **output)
1392 xmlwriteroutput *writeroutput;
1393 HRESULT hr;
1395 *output = NULL;
1397 if (imalloc)
1398 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1399 else
1400 writeroutput = heap_alloc(sizeof(*writeroutput));
1401 if(!writeroutput) return E_OUTOFMEMORY;
1403 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1404 writeroutput->ref = 1;
1405 writeroutput->imalloc = imalloc;
1406 if (imalloc) IMalloc_AddRef(imalloc);
1407 writeroutput->encoding = encoding;
1408 writeroutput->stream = NULL;
1409 hr = init_output_buffer(writeroutput);
1410 if (FAILED(hr)) {
1411 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1412 return hr;
1415 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1417 *output = &writeroutput->IXmlWriterOutput_iface;
1419 TRACE("returning iface %p\n", *output);
1421 return S_OK;
1424 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1425 IMalloc *imalloc,
1426 LPCWSTR encoding,
1427 IXmlWriterOutput **output)
1429 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1430 xml_encoding xml_enc;
1432 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1434 if (!stream || !output) return E_INVALIDARG;
1436 xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1);
1437 return create_writer(stream, imalloc, xml_enc, output);
1440 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream,
1441 IMalloc *imalloc,
1442 UINT codepage,
1443 IXmlWriterOutput **output)
1445 xml_encoding xml_enc;
1447 TRACE("%p %p %u %p\n", stream, imalloc, codepage, output);
1449 if (!stream || !output) return E_INVALIDARG;
1451 xml_enc = get_encoding_from_codepage(codepage);
1452 return create_writer(stream, imalloc, xml_enc, output);