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
28 #include "xmllite_private.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 closetagW
[] = {' ','/','>'};
44 static const WCHAR closepiW
[] = {'?','>'};
45 static const WCHAR ltW
[] = {'<'};
46 static const WCHAR gtW
[] = {'>'};
47 static const WCHAR spaceW
[] = {' '};
48 static const WCHAR quoteW
[] = {'"'};
53 unsigned int allocated
;
60 XmlWriterState_Initial
, /* output is not set yet */
61 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
62 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
63 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
64 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
65 XmlWriterState_ElemStarted
, /* writing element */
66 XmlWriterState_Content
, /* content is accepted at this point */
67 XmlWriterState_DocClosed
/* WriteEndDocument was called */
72 IXmlWriterOutput IXmlWriterOutput_iface
;
75 ISequentialStream
*stream
;
77 xml_encoding encoding
;
78 WCHAR
*encoding_name
; /* exactly as specified on output creation */
79 struct output_buffer buffer
;
82 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
88 unsigned int len
; /* qname length in chars */
91 typedef struct _xmlwriter
93 IXmlWriter IXmlWriter_iface
;
96 xmlwriteroutput
*output
;
97 unsigned int indent_level
;
101 XmlConformanceLevel conformance
;
102 XmlWriterState state
;
105 struct list elements
;
108 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
110 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
113 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
115 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
118 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
120 static const char * const prop_names
[] =
125 "OmitXmlDeclaration",
129 if (prop
> _XmlWriterProperty_Last
)
130 return wine_dbg_sprintf("unknown property=%d", prop
);
132 return prop_names
[prop
];
135 /* writer output memory allocation functions */
136 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
138 return m_alloc(output
->imalloc
, len
);
141 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
143 m_free(output
->imalloc
, mem
);
146 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
148 return m_realloc(output
->imalloc
, mem
, len
);
151 /* writer memory allocation functions */
152 static inline void *writer_alloc(xmlwriter
*writer
, size_t len
)
154 return m_alloc(writer
->imalloc
, len
);
157 static inline void writer_free(xmlwriter
*writer
, void *mem
)
159 m_free(writer
->imalloc
, mem
);
162 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
167 ret
= writer_alloc(writer
, sizeof(*ret
));
168 if (!ret
) return ret
;
170 len
= prefix
? strlenW(prefix
) + 1 /* ':' */ : 0;
171 len
+= strlenW(local
);
173 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
176 static const WCHAR colonW
[] = {':',0};
177 strcpyW(ret
->qname
, prefix
);
178 strcatW(ret
->qname
, colonW
);
182 strcatW(ret
->qname
, local
);
187 static void free_element(xmlwriter
*writer
, struct element
*element
)
189 writer_free(writer
, element
->qname
);
190 writer_free(writer
, element
);
193 static void push_element(xmlwriter
*writer
, struct element
*element
)
195 list_add_head(&writer
->elements
, &element
->entry
);
198 static struct element
*pop_element(xmlwriter
*writer
)
200 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
203 list_remove(&element
->entry
);
208 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
210 struct output_buffer
*buffer
= &output
->buffer
;
211 const int initial_len
= 0x2000;
215 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
216 WARN("Failed to get code page for specified encoding.\n");
218 buffer
->data
= writeroutput_alloc(output
, initial_len
);
219 if (!buffer
->data
) return E_OUTOFMEMORY
;
221 memset(buffer
->data
, 0, 4);
222 buffer
->allocated
= initial_len
;
224 buffer
->codepage
= cp
;
229 static void free_output_buffer(xmlwriteroutput
*output
)
231 struct output_buffer
*buffer
= &output
->buffer
;
232 writeroutput_free(output
, buffer
->data
);
234 buffer
->allocated
= 0;
238 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
240 struct output_buffer
*buffer
= &output
->buffer
;
241 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
242 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
243 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
244 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
245 if (!ptr
) return E_OUTOFMEMORY
;
247 buffer
->allocated
= grown_size
;
253 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
255 struct output_buffer
*buffer
= &output
->buffer
;
260 if (buffer
->codepage
== 1200) {
261 /* For UTF-16 encoding just copy. */
262 length
= len
== -1 ? strlenW(data
) : len
;
264 length
*= sizeof(WCHAR
);
266 hr
= grow_output_buffer(output
, length
);
267 if (FAILED(hr
)) return hr
;
268 ptr
= buffer
->data
+ buffer
->written
;
270 memcpy(ptr
, data
, length
);
271 buffer
->written
+= length
;
273 /* null termination */
278 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
279 hr
= grow_output_buffer(output
, length
);
280 if (FAILED(hr
)) return hr
;
281 ptr
= buffer
->data
+ buffer
->written
;
282 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
283 buffer
->written
+= len
== -1 ? length
-1 : length
;
289 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
291 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
292 write_output_buffer(output
, data
, len
);
293 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
297 /* TODO: test if we need to validate char range */
298 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, const WCHAR
*local_name
)
301 static const WCHAR colW
[] = {':'};
302 write_output_buffer(output
, prefix
, -1);
303 write_output_buffer(output
, colW
, ARRAY_SIZE(colW
));
306 write_output_buffer(output
, local_name
, -1);
311 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
313 if (writeroutput
->stream
) {
314 ISequentialStream_Release(writeroutput
->stream
);
315 writeroutput
->stream
= NULL
;
319 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
323 writeroutput_release_stream(writeroutput
);
324 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
326 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
331 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
333 struct output_buffer
*buffer
;
334 ULONG written
, offset
= 0;
337 if (!output
|| !output
->stream
)
340 buffer
= &output
->buffer
;
342 /* It will loop forever until everything is written or an error occurred. */
345 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
347 WARN("write to stream failed (0x%08x)\n", hr
);
353 buffer
->written
-= written
;
354 } while (buffer
->written
> 0);
359 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
361 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
363 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
364 static const char utf16bom
[] = {0xff, 0xfe};
365 struct output_buffer
*buffer
= &writer
->output
->buffer
;
366 int len
= sizeof(utf16bom
);
369 hr
= grow_output_buffer(writer
->output
, len
);
370 if (FAILED(hr
)) return hr
;
371 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
372 buffer
->written
+= len
;
375 writer
->bomwritten
= TRUE
;
379 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
381 if (output
->encoding_name
)
382 return output
->encoding_name
;
384 return get_encoding_name(output
->encoding
);
387 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
389 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
390 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','='};
392 write_encoding_bom(writer
);
393 writer
->state
= XmlWriterState_DocStarted
;
394 if (writer
->omitxmldecl
) return S_OK
;
397 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
400 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
401 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
404 if (standalone
== XmlStandalone_Omit
)
405 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
407 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
408 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
409 static const WCHAR noW
[] = {'n','o','\"','?','>'};
411 write_output_buffer(writer
->output
, standaloneW
, ARRAY_SIZE(standaloneW
));
412 if (standalone
== XmlStandalone_Yes
)
413 write_output_buffer(writer
->output
, yesW
, ARRAY_SIZE(yesW
));
415 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
421 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
425 if (!writer
->starttagopen
) return S_OK
;
426 hr
= write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
427 writer
->starttagopen
= FALSE
;
431 static void writer_inc_indent(xmlwriter
*writer
)
433 writer
->indent_level
++;
436 static void writer_dec_indent(xmlwriter
*writer
)
438 if (writer
->indent_level
)
439 writer
->indent_level
--;
442 static void write_node_indent(xmlwriter
*writer
)
444 static const WCHAR dblspaceW
[] = {' ',' '};
445 static const WCHAR crlfW
[] = {'\r','\n'};
446 unsigned int indent_level
= writer
->indent_level
;
451 /* Do state check to prevent newline inserted after BOM. It is assumed that
452 state does not change between writing BOM and inserting indentation. */
453 if (writer
->output
->buffer
.written
&& writer
->state
!= XmlWriterState_Ready
)
454 write_output_buffer(writer
->output
, crlfW
, ARRAY_SIZE(crlfW
));
455 while (indent_level
--)
456 write_output_buffer(writer
->output
, dblspaceW
, ARRAY_SIZE(dblspaceW
));
459 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
461 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
463 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
465 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
466 IsEqualGUID(riid
, &IID_IUnknown
))
472 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
474 return E_NOINTERFACE
;
477 IXmlWriter_AddRef(iface
);
482 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
484 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
485 ULONG ref
= InterlockedIncrement(&This
->ref
);
486 TRACE("(%p)->(%u)\n", This
, ref
);
490 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
492 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
493 ULONG ref
= InterlockedDecrement(&This
->ref
);
495 TRACE("(%p)->(%u)\n", This
, ref
);
498 struct element
*element
, *element2
;
499 IMalloc
*imalloc
= This
->imalloc
;
501 writeroutput_flush_stream(This
->output
);
502 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
505 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &This
->elements
, struct element
, entry
) {
506 list_remove(&element
->entry
);
507 free_element(This
, element
);
510 writer_free(This
, This
);
511 if (imalloc
) IMalloc_Release(imalloc
);
517 /*** IXmlWriter methods ***/
518 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
520 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
521 IXmlWriterOutput
*writeroutput
;
524 TRACE("(%p)->(%p)\n", This
, output
);
527 writeroutput_release_stream(This
->output
);
528 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
530 This
->bomwritten
= FALSE
;
531 This
->indent_level
= 0;
534 /* just reset current output */
536 This
->state
= XmlWriterState_Initial
;
540 /* now try IXmlWriterOutput, ISequentialStream, IStream */
541 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
543 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
544 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
546 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
547 writeroutput
, writeroutput
->lpVtbl
);
548 IUnknown_Release(writeroutput
);
553 if (hr
!= S_OK
|| !writeroutput
) {
554 /* create IXmlWriterOutput basing on supplied interface */
555 hr
= CreateXmlWriterOutputWithEncodingName(output
, This
->imalloc
, NULL
, &writeroutput
);
556 if (hr
!= S_OK
) return hr
;
557 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
560 if (This
->output
->encoding
== XmlEncoding_Unknown
)
561 This
->state
= XmlWriterState_InvalidEncoding
;
563 This
->state
= XmlWriterState_Ready
;
564 return writeroutput_query_for_stream(This
->output
);
567 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
569 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
571 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
573 if (!value
) return E_INVALIDARG
;
577 case XmlWriterProperty_Indent
:
578 *value
= This
->indent
;
580 case XmlWriterProperty_ByteOrderMark
:
583 case XmlWriterProperty_OmitXmlDeclaration
:
584 *value
= This
->omitxmldecl
;
586 case XmlWriterProperty_ConformanceLevel
:
587 *value
= This
->conformance
;
590 FIXME("Unimplemented property (%u)\n", property
);
597 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
599 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
601 TRACE("(%p)->(%s %lu)\n", This
, debugstr_writer_prop(property
), value
);
605 case XmlWriterProperty_Indent
:
606 This
->indent
= !!value
;
608 case XmlWriterProperty_ByteOrderMark
:
611 case XmlWriterProperty_OmitXmlDeclaration
:
612 This
->omitxmldecl
= !!value
;
615 FIXME("Unimplemented property (%u)\n", property
);
622 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*pReader
,
623 BOOL fWriteDefaultAttributes
)
625 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
627 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
632 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR ns_prefix
,
633 LPCWSTR local_name
, LPCWSTR ns_uri
, LPCWSTR value
)
635 static const WCHAR eqW
[] = {'=','"'};
636 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
638 TRACE("%p %s %s %s %s\n", This
, debugstr_w(ns_prefix
), debugstr_w(local_name
),
639 debugstr_w(ns_uri
), debugstr_w(value
));
643 case XmlWriterState_Initial
:
645 case XmlWriterState_Ready
:
646 case XmlWriterState_DocClosed
:
647 This
->state
= XmlWriterState_DocClosed
;
648 return WR_E_INVALIDACTION
;
649 case XmlWriterState_InvalidEncoding
:
650 return MX_E_ENCODING
;
655 if (ns_prefix
|| ns_uri
)
657 FIXME("namespaces are not supported.\n");
661 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
662 write_output_buffer(This
->output
, local_name
, -1);
663 write_output_buffer(This
->output
, eqW
, ARRAY_SIZE(eqW
));
664 write_output_buffer(This
->output
, value
, -1);
665 write_output_buffer(This
->output
, quoteW
, ARRAY_SIZE(quoteW
));
670 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
672 static const WCHAR cdataopenW
[] = {'<','!','[','C','D','A','T','A','['};
673 static const WCHAR cdatacloseW
[] = {']',']','>'};
674 write_output_buffer(output
, cdataopenW
, ARRAY_SIZE(cdataopenW
));
676 write_output_buffer(output
, data
, len
);
677 write_output_buffer(output
, cdatacloseW
, ARRAY_SIZE(cdatacloseW
));
680 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
682 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
685 TRACE("%p %s\n", This
, debugstr_w(data
));
689 case XmlWriterState_Initial
:
691 case XmlWriterState_ElemStarted
:
692 writer_close_starttag(This
);
694 case XmlWriterState_Ready
:
695 case XmlWriterState_DocClosed
:
696 This
->state
= XmlWriterState_DocClosed
;
697 return WR_E_INVALIDACTION
;
698 case XmlWriterState_InvalidEncoding
:
699 return MX_E_ENCODING
;
704 len
= data
? strlenW(data
) : 0;
706 write_node_indent(This
);
708 write_cdata_section(This
->output
, NULL
, 0);
710 static const WCHAR cdatacloseW
[] = {']',']','>',0};
712 const WCHAR
*str
= strstrW(data
, cdatacloseW
);
715 write_cdata_section(This
->output
, data
, str
- data
);
720 write_cdata_section(This
->output
, data
, len
);
729 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
731 static const WCHAR fmtW
[] = {'&','#','x','%','x',';',0};
732 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
735 TRACE("%p %#x\n", This
, ch
);
739 case XmlWriterState_Initial
:
741 case XmlWriterState_InvalidEncoding
:
742 return MX_E_ENCODING
;
743 case XmlWriterState_ElemStarted
:
744 writer_close_starttag(This
);
746 case XmlWriterState_DocClosed
:
747 return WR_E_INVALIDACTION
;
752 sprintfW(bufW
, fmtW
, ch
);
753 write_output_buffer(This
->output
, bufW
, -1);
758 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
760 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
762 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
766 case XmlWriterState_Initial
:
768 case XmlWriterState_InvalidEncoding
:
769 return MX_E_ENCODING
;
770 case XmlWriterState_DocClosed
:
771 return WR_E_INVALIDACTION
;
780 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
782 static const WCHAR copenW
[] = {'<','!','-','-'};
783 static const WCHAR ccloseW
[] = {'-','-','>'};
784 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
786 TRACE("%p %s\n", This
, debugstr_w(comment
));
790 case XmlWriterState_Initial
:
792 case XmlWriterState_InvalidEncoding
:
793 return MX_E_ENCODING
;
794 case XmlWriterState_ElemStarted
:
795 writer_close_starttag(This
);
797 case XmlWriterState_DocClosed
:
798 return WR_E_INVALIDACTION
;
803 write_node_indent(This
);
804 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
806 int len
= strlenW(comment
), i
;
808 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
811 for (i
= 0; i
< len
; i
++) {
812 write_output_buffer(This
->output
, comment
+ i
, 1);
813 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
814 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
818 write_output_buffer(This
->output
, comment
, len
);
820 if (len
&& comment
[len
-1] == '-')
821 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
823 write_output_buffer(This
->output
, ccloseW
, ARRAY_SIZE(ccloseW
));
828 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR pwszName
, LPCWSTR pwszPublicId
,
829 LPCWSTR pwszSystemId
, LPCWSTR pwszSubset
)
831 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
833 FIXME("%p %s %s %s %s\n", This
, wine_dbgstr_w(pwszName
), wine_dbgstr_w(pwszPublicId
),
834 wine_dbgstr_w(pwszSystemId
), wine_dbgstr_w(pwszSubset
));
839 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
840 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
842 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
844 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
845 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
849 case XmlWriterState_Initial
:
851 case XmlWriterState_InvalidEncoding
:
852 return MX_E_ENCODING
;
853 case XmlWriterState_ElemStarted
:
854 writer_close_starttag(This
);
856 case XmlWriterState_DocClosed
:
857 return WR_E_INVALIDACTION
;
862 write_encoding_bom(This
);
863 write_node_indent(This
);
864 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
865 write_output_qname(This
->output
, prefix
, local_name
);
869 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
870 write_output_buffer(This
->output
, value
, -1);
871 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
872 write_output_qname(This
->output
, prefix
, local_name
);
873 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
876 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
878 This
->state
= XmlWriterState_Content
;
883 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
885 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
891 case XmlWriterState_Initial
:
893 case XmlWriterState_Ready
:
894 case XmlWriterState_DocClosed
:
895 This
->state
= XmlWriterState_DocClosed
;
896 return WR_E_INVALIDACTION
;
897 case XmlWriterState_InvalidEncoding
:
898 return MX_E_ENCODING
;
903 /* empty element stack */
904 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
907 This
->state
= XmlWriterState_DocClosed
;
911 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
913 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
914 struct element
*element
;
920 case XmlWriterState_Initial
:
922 case XmlWriterState_Ready
:
923 case XmlWriterState_DocClosed
:
924 This
->state
= XmlWriterState_DocClosed
;
925 return WR_E_INVALIDACTION
;
926 case XmlWriterState_InvalidEncoding
:
927 return MX_E_ENCODING
;
932 element
= pop_element(This
);
934 return WR_E_INVALIDACTION
;
936 writer_dec_indent(This
);
938 if (This
->starttagopen
)
940 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
941 This
->starttagopen
= FALSE
;
944 /* write full end tag */
945 write_node_indent(This
);
946 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
947 write_output_buffer(This
->output
, element
->qname
, element
->len
);
948 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
954 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
956 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
958 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
962 case XmlWriterState_Initial
:
964 case XmlWriterState_InvalidEncoding
:
965 return MX_E_ENCODING
;
966 case XmlWriterState_DocClosed
:
967 return WR_E_INVALIDACTION
;
975 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
977 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
978 struct element
*element
;
984 case XmlWriterState_Initial
:
986 case XmlWriterState_Ready
:
987 case XmlWriterState_DocClosed
:
988 This
->state
= XmlWriterState_DocClosed
;
989 return WR_E_INVALIDACTION
;
990 case XmlWriterState_InvalidEncoding
:
991 return MX_E_ENCODING
;
996 element
= pop_element(This
);
998 return WR_E_INVALIDACTION
;
1000 writer_close_starttag(This
);
1001 writer_dec_indent(This
);
1003 /* don't force full end tag to the next line */
1004 if (This
->state
== XmlWriterState_ElemStarted
)
1005 This
->state
= XmlWriterState_Content
;
1007 write_node_indent(This
);
1009 /* write full end tag */
1010 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1011 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1012 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1017 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1019 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1021 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1023 switch (This
->state
)
1025 case XmlWriterState_Initial
:
1026 return E_UNEXPECTED
;
1027 case XmlWriterState_Ready
:
1028 case XmlWriterState_DocClosed
:
1029 This
->state
= XmlWriterState_DocClosed
;
1030 return WR_E_INVALIDACTION
;
1031 case XmlWriterState_InvalidEncoding
:
1032 return MX_E_ENCODING
;
1040 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1042 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1044 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1046 switch (This
->state
)
1048 case XmlWriterState_Initial
:
1049 return E_UNEXPECTED
;
1050 case XmlWriterState_Ready
:
1051 case XmlWriterState_DocClosed
:
1052 This
->state
= XmlWriterState_DocClosed
;
1053 return WR_E_INVALIDACTION
;
1054 case XmlWriterState_InvalidEncoding
:
1055 return MX_E_ENCODING
;
1063 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*pReader
,
1064 BOOL fWriteDefaultAttributes
)
1066 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1068 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1073 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*pReader
,
1074 BOOL fWriteDefaultAttributes
)
1076 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1078 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1083 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1086 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1087 static const WCHAR xmlW
[] = {'x','m','l',0};
1088 static const WCHAR openpiW
[] = {'<','?'};
1090 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1092 switch (This
->state
)
1094 case XmlWriterState_Initial
:
1095 return E_UNEXPECTED
;
1096 case XmlWriterState_InvalidEncoding
:
1097 return MX_E_ENCODING
;
1098 case XmlWriterState_DocStarted
:
1099 if (!strcmpW(name
, xmlW
))
1100 return WR_E_INVALIDACTION
;
1102 case XmlWriterState_ElemStarted
:
1103 case XmlWriterState_DocClosed
:
1104 return WR_E_INVALIDACTION
;
1109 write_encoding_bom(This
);
1110 write_node_indent(This
);
1111 write_output_buffer(This
->output
, openpiW
, ARRAY_SIZE(openpiW
));
1112 write_output_buffer(This
->output
, name
, -1);
1113 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
1114 write_output_buffer(This
->output
, text
, -1);
1115 write_output_buffer(This
->output
, closepiW
, ARRAY_SIZE(closepiW
));
1117 if (!strcmpW(name
, xmlW
))
1118 This
->state
= XmlWriterState_PIDocStarted
;
1123 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1124 LPCWSTR pwszNamespaceUri
)
1126 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1128 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1130 switch (This
->state
)
1132 case XmlWriterState_Initial
:
1133 return E_UNEXPECTED
;
1134 case XmlWriterState_InvalidEncoding
:
1135 return MX_E_ENCODING
;
1136 case XmlWriterState_DocClosed
:
1137 return WR_E_INVALIDACTION
;
1145 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1147 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1149 TRACE("%p %s\n", This
, debugstr_w(data
));
1154 switch (This
->state
)
1156 case XmlWriterState_Initial
:
1157 return E_UNEXPECTED
;
1158 case XmlWriterState_Ready
:
1159 write_xmldecl(This
, XmlStandalone_Omit
);
1161 case XmlWriterState_DocStarted
:
1162 case XmlWriterState_PIDocStarted
:
1164 case XmlWriterState_InvalidEncoding
:
1165 return MX_E_ENCODING
;
1167 This
->state
= XmlWriterState_DocClosed
;
1168 return WR_E_INVALIDACTION
;
1171 write_output_buffer(This
->output
, data
, -1);
1175 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1177 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1179 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1181 switch (This
->state
)
1183 case XmlWriterState_Initial
:
1184 return E_UNEXPECTED
;
1185 case XmlWriterState_InvalidEncoding
:
1186 return MX_E_ENCODING
;
1187 case XmlWriterState_DocClosed
:
1188 return WR_E_INVALIDACTION
;
1196 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1198 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1200 TRACE("(%p)->(%d)\n", This
, standalone
);
1202 switch (This
->state
)
1204 case XmlWriterState_Initial
:
1205 return E_UNEXPECTED
;
1206 case XmlWriterState_PIDocStarted
:
1207 This
->state
= XmlWriterState_DocStarted
;
1209 case XmlWriterState_Ready
:
1211 case XmlWriterState_InvalidEncoding
:
1212 return MX_E_ENCODING
;
1214 This
->state
= XmlWriterState_DocClosed
;
1215 return WR_E_INVALIDACTION
;
1218 return write_xmldecl(This
, standalone
);
1221 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1223 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1224 struct element
*element
;
1226 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1229 return E_INVALIDARG
;
1231 switch (This
->state
)
1233 case XmlWriterState_Initial
:
1234 return E_UNEXPECTED
;
1235 case XmlWriterState_InvalidEncoding
:
1236 return MX_E_ENCODING
;
1237 case XmlWriterState_DocClosed
:
1238 return WR_E_INVALIDACTION
;
1243 /* close pending element */
1244 if (This
->starttagopen
)
1245 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1247 element
= alloc_element(This
, prefix
, local_name
);
1249 return E_OUTOFMEMORY
;
1251 write_encoding_bom(This
);
1252 write_node_indent(This
);
1254 This
->state
= XmlWriterState_ElemStarted
;
1255 This
->starttagopen
= TRUE
;
1257 push_element(This
, element
);
1259 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
1260 write_output_qname(This
->output
, prefix
, local_name
);
1261 writer_inc_indent(This
);
1266 static void write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
)
1268 static const WCHAR ampW
[] = {'&','a','m','p',';'};
1269 static const WCHAR ltW
[] = {'&','l','t',';'};
1270 static const WCHAR gtW
[] = {'&','g','t',';'};
1277 write_output_buffer(writer
->output
, ltW
, ARRAY_SIZE(ltW
));
1280 write_output_buffer(writer
->output
, ampW
, ARRAY_SIZE(ampW
));
1283 write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
1286 write_output_buffer(writer
->output
, string
, 1);
1293 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1295 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1297 TRACE("%p %s\n", This
, debugstr_w(string
));
1302 switch (This
->state
)
1304 case XmlWriterState_Initial
:
1305 return E_UNEXPECTED
;
1306 case XmlWriterState_ElemStarted
:
1307 writer_close_starttag(This
);
1309 case XmlWriterState_Ready
:
1310 case XmlWriterState_DocClosed
:
1311 This
->state
= XmlWriterState_DocClosed
;
1312 return WR_E_INVALIDACTION
;
1313 case XmlWriterState_InvalidEncoding
:
1314 return MX_E_ENCODING
;
1319 write_escaped_string(This
, string
);
1323 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1325 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1327 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1332 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR pwszWhitespace
)
1334 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1336 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszWhitespace
));
1341 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1343 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1345 TRACE("%p\n", This
);
1347 return writeroutput_flush_stream(This
->output
);
1350 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1352 xmlwriter_QueryInterface
,
1355 xmlwriter_SetOutput
,
1356 xmlwriter_GetProperty
,
1357 xmlwriter_SetProperty
,
1358 xmlwriter_WriteAttributes
,
1359 xmlwriter_WriteAttributeString
,
1360 xmlwriter_WriteCData
,
1361 xmlwriter_WriteCharEntity
,
1362 xmlwriter_WriteChars
,
1363 xmlwriter_WriteComment
,
1364 xmlwriter_WriteDocType
,
1365 xmlwriter_WriteElementString
,
1366 xmlwriter_WriteEndDocument
,
1367 xmlwriter_WriteEndElement
,
1368 xmlwriter_WriteEntityRef
,
1369 xmlwriter_WriteFullEndElement
,
1370 xmlwriter_WriteName
,
1371 xmlwriter_WriteNmToken
,
1372 xmlwriter_WriteNode
,
1373 xmlwriter_WriteNodeShallow
,
1374 xmlwriter_WriteProcessingInstruction
,
1375 xmlwriter_WriteQualifiedName
,
1377 xmlwriter_WriteRawChars
,
1378 xmlwriter_WriteStartDocument
,
1379 xmlwriter_WriteStartElement
,
1380 xmlwriter_WriteString
,
1381 xmlwriter_WriteSurrogateCharEntity
,
1382 xmlwriter_WriteWhitespace
,
1386 /** IXmlWriterOutput **/
1387 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1389 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1391 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1393 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1394 IsEqualGUID(riid
, &IID_IUnknown
))
1400 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1402 return E_NOINTERFACE
;
1405 IUnknown_AddRef(iface
);
1410 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1412 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1413 ULONG ref
= InterlockedIncrement(&This
->ref
);
1414 TRACE("(%p)->(%d)\n", This
, ref
);
1418 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
1420 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1421 LONG ref
= InterlockedDecrement(&This
->ref
);
1423 TRACE("(%p)->(%d)\n", This
, ref
);
1427 IMalloc
*imalloc
= This
->imalloc
;
1428 if (This
->output
) IUnknown_Release(This
->output
);
1429 if (This
->stream
) ISequentialStream_Release(This
->stream
);
1430 free_output_buffer(This
);
1431 writeroutput_free(This
, This
->encoding_name
);
1432 writeroutput_free(This
, This
);
1433 if (imalloc
) IMalloc_Release(imalloc
);
1439 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
1441 xmlwriteroutput_QueryInterface
,
1442 xmlwriteroutput_AddRef
,
1443 xmlwriteroutput_Release
1446 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
1451 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
1454 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1456 writer
= heap_alloc(sizeof(*writer
));
1457 if(!writer
) return E_OUTOFMEMORY
;
1459 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1461 writer
->imalloc
= imalloc
;
1462 if (imalloc
) IMalloc_AddRef(imalloc
);
1463 writer
->output
= NULL
;
1464 writer
->indent_level
= 0;
1465 writer
->indent
= FALSE
;
1467 writer
->omitxmldecl
= FALSE
;
1468 writer
->conformance
= XmlConformanceLevel_Document
;
1469 writer
->state
= XmlWriterState_Initial
;
1470 writer
->bomwritten
= FALSE
;
1471 writer
->starttagopen
= FALSE
;
1472 list_init(&writer
->elements
);
1474 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
1475 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
1477 TRACE("returning iface %p, hr %#x\n", *obj
, hr
);
1482 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1483 const WCHAR
*encoding_name
, IXmlWriterOutput
**output
)
1485 xmlwriteroutput
*writeroutput
;
1491 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
1493 writeroutput
= heap_alloc(sizeof(*writeroutput
));
1495 return E_OUTOFMEMORY
;
1497 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
1498 writeroutput
->ref
= 1;
1499 writeroutput
->imalloc
= imalloc
;
1501 IMalloc_AddRef(imalloc
);
1502 writeroutput
->encoding
= encoding
;
1503 writeroutput
->stream
= NULL
;
1504 hr
= init_output_buffer(writeroutput
);
1506 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1510 if (encoding_name
) {
1511 unsigned int size
= (strlenW(encoding_name
) + 1) * sizeof(WCHAR
);
1512 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
1513 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
1516 writeroutput
->encoding_name
= NULL
;
1518 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1520 *output
= &writeroutput
->IXmlWriterOutput_iface
;
1522 TRACE("returning iface %p\n", *output
);
1527 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
,
1530 IXmlWriterOutput
**output
)
1532 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
1533 xml_encoding xml_enc
;
1535 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), output
);
1537 if (!stream
|| !output
) return E_INVALIDARG
;
1539 xml_enc
= parse_encoding_name(encoding
? encoding
: utf8W
, -1);
1540 return create_writer_output(stream
, imalloc
, xml_enc
, encoding
, output
);
1543 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
,
1546 IXmlWriterOutput
**output
)
1548 xml_encoding xml_enc
;
1550 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, output
);
1552 if (!stream
|| !output
) return E_INVALIDARG
;
1554 xml_enc
= get_encoding_from_codepage(codepage
);
1555 return create_writer_output(stream
, imalloc
, xml_enc
, NULL
, output
);