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 closepiW
[] = {'?','>'};
44 static const WCHAR ltW
[] = {'<'};
45 static const WCHAR gtW
[] = {'>'};
46 static const WCHAR spaceW
[] = {' '};
47 static const WCHAR quoteW
[] = {'"'};
52 unsigned int allocated
;
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 */
70 IXmlWriterOutput IXmlWriterOutput_iface
;
73 ISequentialStream
*stream
;
75 xml_encoding encoding
;
76 struct output_buffer buffer
;
79 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
85 unsigned int len
; /* qname length in chars */
88 typedef struct _xmlwriter
90 IXmlWriter IXmlWriter_iface
;
93 xmlwriteroutput
*output
;
94 unsigned int indent_level
;
98 XmlConformanceLevel conformance
;
102 struct list elements
;
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
[] =
122 "OmitXmlDeclaration",
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
)
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
));
173 static const WCHAR colonW
[] = {':',0};
174 strcpyW(ret
->qname
, prefix
);
175 strcatW(ret
->qname
, colonW
);
179 strcatW(ret
->qname
, local
);
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
);
200 list_remove(&element
->entry
);
205 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
207 struct output_buffer
*buffer
= &output
->buffer
;
208 const int initial_len
= 0x2000;
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
;
221 buffer
->codepage
= cp
;
226 static void free_output_buffer(xmlwriteroutput
*output
)
228 struct output_buffer
*buffer
= &output
->buffer
;
229 writeroutput_free(output
, buffer
->data
);
231 buffer
->allocated
= 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
;
244 buffer
->allocated
= grown_size
;
250 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
252 struct output_buffer
*buffer
= &output
->buffer
;
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
;
266 /* WCHAR data just copied */
267 length
= len
== -1 ? strlenW(data
) : len
;
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
;
278 /* null termination */
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
));
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
)
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);
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
)
320 writeroutput_release_stream(writeroutput
);
321 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
323 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
328 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
330 struct output_buffer
*buffer
;
331 ULONG written
, offset
= 0;
334 if (!output
|| !output
->stream
)
337 buffer
= &output
->buffer
;
339 /* It will loop forever until everything is written or an error occurred. */
342 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
344 WARN("write to stream failed (0x%08x)\n", hr
);
350 buffer
->written
-= written
;
351 } while (buffer
->written
> 0);
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
);
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
;
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
;
386 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
389 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
390 write_output_buffer_quoted(writer
->output
, get_encoding_name(writer
->output
->encoding
), -1);
393 if (standalone
== XmlStandalone_Omit
)
394 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
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
));
404 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
410 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
414 if (!writer
->starttagopen
) return S_OK
;
415 hr
= write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
416 writer
->starttagopen
= FALSE
;
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
;
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
))
458 IXmlWriter_AddRef(iface
);
463 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
465 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
467 return InterlockedIncrement(&This
->ref
);
470 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
472 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
477 ref
= InterlockedDecrement(&This
->ref
);
479 struct element
*element
, *element2
;
480 IMalloc
*imalloc
= This
->imalloc
;
482 IXmlWriter_Flush(iface
);
483 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
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
);
498 /*** IXmlWriter methods ***/
499 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
501 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
502 IXmlWriterOutput
*writeroutput
;
505 TRACE("(%p)->(%p)\n", This
, output
);
508 writeroutput_release_stream(This
->output
);
509 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
511 This
->bomwritten
= FALSE
;
512 This
->indent_level
= 0;
515 /* just reset current output */
517 This
->state
= XmlWriterState_Initial
;
521 /* now try IXmlWriterOutput, ISequentialStream, IStream */
522 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
524 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
525 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
527 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
528 writeroutput
, writeroutput
->lpVtbl
);
529 IUnknown_Release(writeroutput
);
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
;
556 case XmlWriterProperty_Indent
:
557 *value
= This
->indent
;
559 case XmlWriterProperty_ByteOrderMark
:
562 case XmlWriterProperty_OmitXmlDeclaration
:
563 *value
= This
->omitxmldecl
;
565 case XmlWriterProperty_ConformanceLevel
:
566 *value
= This
->conformance
;
569 FIXME("Unimplemented property (%u)\n", property
);
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
);
584 case XmlWriterProperty_Indent
:
585 This
->indent
= !!value
;
587 case XmlWriterProperty_ByteOrderMark
:
590 case XmlWriterProperty_OmitXmlDeclaration
:
591 This
->omitxmldecl
= !!value
;
594 FIXME("Unimplemented property (%u)\n", property
);
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
);
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
));
622 case XmlWriterState_Initial
:
624 case XmlWriterState_Ready
:
625 case XmlWriterState_DocClosed
:
626 This
->state
= XmlWriterState_DocClosed
;
627 return WR_E_INVALIDACTION
;
632 if (ns_prefix
|| ns_uri
)
634 FIXME("namespaces are not supported.\n");
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
));
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
));
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
);
662 TRACE("%p %s\n", This
, debugstr_w(data
));
666 case XmlWriterState_Initial
:
668 case XmlWriterState_ElemStarted
:
669 writer_close_starttag(This
);
671 case XmlWriterState_Ready
:
672 case XmlWriterState_DocClosed
:
673 This
->state
= XmlWriterState_DocClosed
;
674 return WR_E_INVALIDACTION
;
679 len
= data
? strlenW(data
) : 0;
681 write_node_indent(This
);
683 write_cdata_section(This
->output
, NULL
, 0);
685 static const WCHAR cdatacloseW
[] = {']',']','>',0};
687 const WCHAR
*str
= strstrW(data
, cdatacloseW
);
690 write_cdata_section(This
->output
, data
, str
- data
);
695 write_cdata_section(This
->output
, data
, len
);
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
);
710 TRACE("%p %#x\n", This
, ch
);
714 case XmlWriterState_Initial
:
716 case XmlWriterState_ElemStarted
:
717 writer_close_starttag(This
);
719 case XmlWriterState_DocClosed
:
720 return WR_E_INVALIDACTION
;
725 sprintfW(bufW
, fmtW
, ch
);
726 write_output_buffer(This
->output
, bufW
, -1);
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
);
739 case XmlWriterState_Initial
:
741 case XmlWriterState_DocClosed
:
742 return WR_E_INVALIDACTION
;
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
));
761 case XmlWriterState_Initial
:
763 case XmlWriterState_ElemStarted
:
764 writer_close_starttag(This
);
766 case XmlWriterState_DocClosed
:
767 return WR_E_INVALIDACTION
;
772 write_node_indent(This
);
773 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
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
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
));
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
));
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
));
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
));
818 case XmlWriterState_Initial
:
820 case XmlWriterState_ElemStarted
:
821 writer_close_starttag(This
);
823 case XmlWriterState_Ready
:
824 case XmlWriterState_DocStarted
:
825 case XmlWriterState_PIDocStarted
:
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
));
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
;
848 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
850 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
856 case XmlWriterState_Initial
:
858 case XmlWriterState_Ready
:
859 case XmlWriterState_DocClosed
:
860 This
->state
= XmlWriterState_DocClosed
;
861 return WR_E_INVALIDACTION
;
866 /* empty element stack */
867 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
870 This
->state
= XmlWriterState_DocClosed
;
874 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
876 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
877 struct element
*element
;
883 case XmlWriterState_Initial
:
885 case XmlWriterState_Ready
:
886 case XmlWriterState_DocClosed
:
887 This
->state
= XmlWriterState_DocClosed
;
888 return WR_E_INVALIDACTION
;
893 element
= pop_element(This
);
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
;
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
));
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
));
923 case XmlWriterState_Initial
:
925 case XmlWriterState_DocClosed
:
926 return WR_E_INVALIDACTION
;
934 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
936 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
937 struct element
*element
;
943 case XmlWriterState_Initial
:
945 case XmlWriterState_Ready
:
946 case XmlWriterState_DocClosed
:
947 This
->state
= XmlWriterState_DocClosed
;
948 return WR_E_INVALIDACTION
;
953 element
= pop_element(This
);
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
;
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
));
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
));
982 case XmlWriterState_Initial
:
984 case XmlWriterState_Ready
:
985 case XmlWriterState_DocClosed
:
986 This
->state
= XmlWriterState_DocClosed
;
987 return WR_E_INVALIDACTION
;
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
;
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
);
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
);
1036 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
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
;
1053 case XmlWriterState_ElemStarted
:
1054 case XmlWriterState_DocClosed
:
1055 return WR_E_INVALIDACTION
;
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
;
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
;
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
));
1103 switch (This
->state
)
1105 case XmlWriterState_Initial
:
1106 return E_UNEXPECTED
;
1107 case XmlWriterState_Ready
:
1108 write_xmldecl(This
, XmlStandalone_Omit
);
1110 case XmlWriterState_DocStarted
:
1111 case XmlWriterState_PIDocStarted
:
1114 This
->state
= XmlWriterState_DocClosed
;
1115 return WR_E_INVALIDACTION
;
1118 write_output_buffer(This
->output
, data
, -1);
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
;
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
;
1154 case XmlWriterState_Ready
:
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
));
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
;
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
);
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
);
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
;
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
);
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
));
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
,
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
,
1281 xmlwriter_WriteRawChars
,
1282 xmlwriter_WriteStartDocument
,
1283 xmlwriter_WriteStartElement
,
1284 xmlwriter_WriteString
,
1285 xmlwriter_WriteSurrogateCharEntity
,
1286 xmlwriter_WriteWhitespace
,
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
))
1304 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1306 return E_NOINTERFACE
;
1309 IUnknown_AddRef(iface
);
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
);
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
);
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
);
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
)
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
));
1362 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1364 writer
= heap_alloc(sizeof(*writer
));
1365 if(!writer
) return E_OUTOFMEMORY
;
1367 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1369 writer
->imalloc
= imalloc
;
1370 if (imalloc
) IMalloc_AddRef(imalloc
);
1371 writer
->output
= NULL
;
1372 writer
->indent_level
= 0;
1373 writer
->indent
= FALSE
;
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
);
1389 static HRESULT
create_writer(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1390 IXmlWriterOutput
**output
)
1392 xmlwriteroutput
*writeroutput
;
1398 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
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
);
1411 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1415 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1417 *output
= &writeroutput
->IXmlWriterOutput_iface
;
1419 TRACE("returning iface %p\n", *output
);
1424 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
,
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
,
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
);