2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014 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
[] = {' '};
51 unsigned int allocated
;
58 XmlWriterState_Initial
, /* output is not set yet */
59 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
60 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
61 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
62 XmlWriterState_ElemStarted
, /* writing element */
63 XmlWriterState_Content
, /* content is accepted at this point */
64 XmlWriterState_DocClosed
/* WriteEndDocument was called */
69 IXmlWriterOutput IXmlWriterOutput_iface
;
72 ISequentialStream
*stream
;
74 xml_encoding encoding
;
75 struct output_buffer buffer
;
78 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
84 unsigned int len
; /* qname length in chars */
87 typedef struct _xmlwriter
89 IXmlWriter IXmlWriter_iface
;
92 xmlwriteroutput
*output
;
96 XmlConformanceLevel conformance
;
100 struct list elements
;
103 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
105 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
108 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
110 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
113 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
115 static const char * const prop_names
[] =
120 "OmitXmlDeclaration",
124 if (prop
> _XmlWriterProperty_Last
)
125 return wine_dbg_sprintf("unknown property=%d", prop
);
127 return prop_names
[prop
];
130 /* writer output memory allocation functions */
131 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
133 return m_alloc(output
->imalloc
, len
);
136 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
138 m_free(output
->imalloc
, mem
);
141 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
143 return m_realloc(output
->imalloc
, mem
, len
);
146 /* writer memory allocation functions */
147 static inline void *writer_alloc(xmlwriter
*writer
, size_t len
)
149 return m_alloc(writer
->imalloc
, len
);
152 static inline void writer_free(xmlwriter
*writer
, void *mem
)
154 m_free(writer
->imalloc
, mem
);
157 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
162 ret
= writer_alloc(writer
, sizeof(*ret
));
163 if (!ret
) return ret
;
165 len
= prefix
? strlenW(prefix
) + 1 /* ':' */ : 0;
166 len
+= strlenW(local
);
168 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
171 static const WCHAR colonW
[] = {':',0};
172 strcpyW(ret
->qname
, prefix
);
173 strcatW(ret
->qname
, colonW
);
177 strcatW(ret
->qname
, local
);
182 static void free_element(xmlwriter
*writer
, struct element
*element
)
184 writer_free(writer
, element
->qname
);
185 writer_free(writer
, element
);
188 static void push_element(xmlwriter
*writer
, struct element
*element
)
190 list_add_head(&writer
->elements
, &element
->entry
);
193 static struct element
*pop_element(xmlwriter
*writer
)
195 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
198 list_remove(&element
->entry
);
203 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
205 struct output_buffer
*buffer
= &output
->buffer
;
206 const int initial_len
= 0x2000;
210 hr
= get_code_page(output
->encoding
, &cp
);
211 if (FAILED(hr
)) return hr
;
213 buffer
->data
= writeroutput_alloc(output
, initial_len
);
214 if (!buffer
->data
) return E_OUTOFMEMORY
;
216 memset(buffer
->data
, 0, 4);
217 buffer
->allocated
= initial_len
;
219 buffer
->codepage
= cp
;
224 static void free_output_buffer(xmlwriteroutput
*output
)
226 struct output_buffer
*buffer
= &output
->buffer
;
227 writeroutput_free(output
, buffer
->data
);
229 buffer
->allocated
= 0;
233 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
235 struct output_buffer
*buffer
= &output
->buffer
;
236 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
237 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
238 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
239 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
240 if (!ptr
) return E_OUTOFMEMORY
;
242 buffer
->allocated
= grown_size
;
248 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
250 struct output_buffer
*buffer
= &output
->buffer
;
255 if (buffer
->codepage
!= ~0) {
256 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
257 hr
= grow_output_buffer(output
, length
);
258 if (FAILED(hr
)) return hr
;
259 ptr
= buffer
->data
+ buffer
->written
;
260 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
261 buffer
->written
+= len
== -1 ? length
-1 : length
;
264 /* WCHAR data just copied */
265 length
= len
== -1 ? strlenW(data
) : len
;
267 length
*= sizeof(WCHAR
);
269 hr
= grow_output_buffer(output
, length
);
270 if (FAILED(hr
)) return hr
;
271 ptr
= buffer
->data
+ buffer
->written
;
273 memcpy(ptr
, data
, length
);
274 buffer
->written
+= length
;
276 /* null termination */
284 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
286 static const WCHAR quoteW
[] = {'"'};
287 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
288 write_output_buffer(output
, data
, len
);
289 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
293 /* TODO: test if we need to validate char range */
294 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, const WCHAR
*local_name
)
297 static const WCHAR colW
[] = {':'};
298 write_output_buffer(output
, prefix
, -1);
299 write_output_buffer(output
, colW
, ARRAY_SIZE(colW
));
302 write_output_buffer(output
, local_name
, -1);
307 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
309 if (writeroutput
->stream
) {
310 ISequentialStream_Release(writeroutput
->stream
);
311 writeroutput
->stream
= NULL
;
315 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
319 writeroutput_release_stream(writeroutput
);
320 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
322 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
327 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
329 struct output_buffer
*buffer
;
330 ULONG written
, offset
= 0;
333 if (!output
|| !output
->stream
)
336 buffer
= &output
->buffer
;
338 /* It will loop forever until everything is written or an error occurred. */
341 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
343 WARN("write to stream failed (0x%08x)\n", hr
);
349 buffer
->written
-= written
;
350 } while (buffer
->written
> 0);
355 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
357 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
359 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
360 static const char utf16bom
[] = {0xff, 0xfe};
361 struct output_buffer
*buffer
= &writer
->output
->buffer
;
362 int len
= sizeof(utf16bom
);
365 hr
= grow_output_buffer(writer
->output
, len
);
366 if (FAILED(hr
)) return hr
;
367 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
368 buffer
->written
+= len
;
371 writer
->bomwritten
= TRUE
;
375 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
377 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
378 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','='};
380 write_encoding_bom(writer
);
381 writer
->state
= XmlWriterState_DocStarted
;
382 if (writer
->omitxmldecl
) return S_OK
;
385 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
388 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
389 write_output_buffer_quoted(writer
->output
, get_encoding_name(writer
->output
->encoding
), -1);
392 if (standalone
== XmlStandalone_Omit
)
393 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
395 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
396 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
397 static const WCHAR noW
[] = {'n','o','\"','?','>'};
399 write_output_buffer(writer
->output
, standaloneW
, ARRAY_SIZE(standaloneW
));
400 if (standalone
== XmlStandalone_Yes
)
401 write_output_buffer(writer
->output
, yesW
, ARRAY_SIZE(yesW
));
403 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
409 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
413 if (!writer
->starttagopen
) return S_OK
;
414 hr
= write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
415 writer
->starttagopen
= FALSE
;
416 writer
->state
= XmlWriterState_Content
;
420 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
422 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
424 TRACE("%p %s %p\n", This
, debugstr_guid(riid
), ppvObject
);
426 if (IsEqualGUID(riid
, &IID_IUnknown
) ||
427 IsEqualGUID(riid
, &IID_IXmlWriter
))
432 IXmlWriter_AddRef(iface
);
437 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
439 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
441 return InterlockedIncrement(&This
->ref
);
444 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
446 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
451 ref
= InterlockedDecrement(&This
->ref
);
453 struct element
*element
, *element2
;
454 IMalloc
*imalloc
= This
->imalloc
;
456 IXmlWriter_Flush(iface
);
457 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
460 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &This
->elements
, struct element
, entry
) {
461 list_remove(&element
->entry
);
462 free_element(This
, element
);
465 writer_free(This
, This
);
466 if (imalloc
) IMalloc_Release(imalloc
);
472 /*** IXmlWriter methods ***/
473 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
475 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
476 IXmlWriterOutput
*writeroutput
;
479 TRACE("(%p)->(%p)\n", This
, output
);
482 writeroutput_release_stream(This
->output
);
483 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
485 This
->bomwritten
= FALSE
;
488 /* just reset current output */
490 This
->state
= XmlWriterState_Initial
;
494 /* now try IXmlWriterOutput, ISequentialStream, IStream */
495 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
497 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
498 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
500 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
501 writeroutput
, writeroutput
->lpVtbl
);
502 IUnknown_Release(writeroutput
);
508 if (hr
!= S_OK
|| !writeroutput
) {
509 /* create IXmlWriterOutput basing on supplied interface */
510 hr
= CreateXmlWriterOutputWithEncodingName(output
, This
->imalloc
, NULL
, &writeroutput
);
511 if (hr
!= S_OK
) return hr
;
512 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
515 This
->state
= XmlWriterState_Ready
;
516 return writeroutput_query_for_stream(This
->output
);
519 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
521 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
523 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
525 if (!value
) return E_INVALIDARG
;
529 case XmlWriterProperty_Indent
:
530 *value
= This
->indent
;
532 case XmlWriterProperty_ByteOrderMark
:
535 case XmlWriterProperty_OmitXmlDeclaration
:
536 *value
= This
->omitxmldecl
;
538 case XmlWriterProperty_ConformanceLevel
:
539 *value
= This
->conformance
;
542 FIXME("Unimplemented property (%u)\n", property
);
549 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
551 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
553 TRACE("(%p)->(%s %lu)\n", This
, debugstr_writer_prop(property
), value
);
557 case XmlWriterProperty_ByteOrderMark
:
560 case XmlWriterProperty_OmitXmlDeclaration
:
561 This
->omitxmldecl
= !!value
;
564 FIXME("Unimplemented property (%u)\n", property
);
571 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*pReader
,
572 BOOL fWriteDefaultAttributes
)
574 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
576 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
581 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR pwszPrefix
,
582 LPCWSTR pwszLocalName
, LPCWSTR pwszNamespaceUri
,
585 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
587 FIXME("%p %s %s %s %s\n", This
, wine_dbgstr_w(pwszPrefix
), wine_dbgstr_w(pwszLocalName
),
588 wine_dbgstr_w(pwszNamespaceUri
), wine_dbgstr_w(pwszValue
));
592 case XmlWriterState_Initial
:
594 case XmlWriterState_Ready
:
595 case XmlWriterState_DocClosed
:
596 This
->state
= XmlWriterState_DocClosed
;
597 return WR_E_INVALIDACTION
;
605 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
607 static const WCHAR cdataopenW
[] = {'<','!','[','C','D','A','T','A','['};
608 static const WCHAR cdatacloseW
[] = {']',']','>'};
609 write_output_buffer(output
, cdataopenW
, ARRAY_SIZE(cdataopenW
));
611 write_output_buffer(output
, data
, len
);
612 write_output_buffer(output
, cdatacloseW
, ARRAY_SIZE(cdatacloseW
));
615 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
617 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
620 TRACE("%p %s\n", This
, debugstr_w(data
));
624 case XmlWriterState_Initial
:
626 case XmlWriterState_ElemStarted
:
627 writer_close_starttag(This
);
629 case XmlWriterState_Ready
:
630 case XmlWriterState_DocClosed
:
631 This
->state
= XmlWriterState_DocClosed
;
632 return WR_E_INVALIDACTION
;
637 len
= data
? strlenW(data
) : 0;
640 write_cdata_section(This
->output
, NULL
, 0);
642 static const WCHAR cdatacloseW
[] = {']',']','>',0};
644 const WCHAR
*str
= strstrW(data
, cdatacloseW
);
647 write_cdata_section(This
->output
, data
, str
- data
);
652 write_cdata_section(This
->output
, data
, len
);
661 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
663 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
665 FIXME("%p %x\n", This
, ch
);
669 case XmlWriterState_Initial
:
671 case XmlWriterState_DocClosed
:
672 return WR_E_INVALIDACTION
;
680 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
682 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
684 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
688 case XmlWriterState_Initial
:
690 case XmlWriterState_DocClosed
:
691 return WR_E_INVALIDACTION
;
699 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
701 static const WCHAR copenW
[] = {'<','!','-','-'};
702 static const WCHAR ccloseW
[] = {'-','-','>'};
703 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
705 TRACE("%p %s\n", This
, debugstr_w(comment
));
709 case XmlWriterState_Initial
:
711 case XmlWriterState_ElemStarted
:
712 writer_close_starttag(This
);
714 case XmlWriterState_DocClosed
:
715 return WR_E_INVALIDACTION
;
720 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
722 int len
= strlenW(comment
), i
;
724 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
727 for (i
= 0; i
< len
; i
++) {
728 write_output_buffer(This
->output
, comment
+ i
, 1);
729 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
730 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
734 write_output_buffer(This
->output
, comment
, len
);
736 if (len
&& comment
[len
-1] == '-')
737 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
739 write_output_buffer(This
->output
, ccloseW
, ARRAY_SIZE(ccloseW
));
744 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR pwszName
, LPCWSTR pwszPublicId
,
745 LPCWSTR pwszSystemId
, LPCWSTR pwszSubset
)
747 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
749 FIXME("%p %s %s %s %s\n", This
, wine_dbgstr_w(pwszName
), wine_dbgstr_w(pwszPublicId
),
750 wine_dbgstr_w(pwszSystemId
), wine_dbgstr_w(pwszSubset
));
755 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
756 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
758 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
760 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
761 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
765 case XmlWriterState_Initial
:
767 case XmlWriterState_ElemStarted
:
768 writer_close_starttag(This
);
770 case XmlWriterState_Ready
:
771 case XmlWriterState_DocStarted
:
772 case XmlWriterState_PIDocStarted
:
775 This
->state
= XmlWriterState_DocClosed
;
776 return WR_E_INVALIDACTION
;
779 write_encoding_bom(This
);
780 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
781 write_output_qname(This
->output
, prefix
, local_name
);
782 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
785 write_output_buffer(This
->output
, value
, -1);
787 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
788 write_output_qname(This
->output
, prefix
, local_name
);
789 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
790 This
->state
= XmlWriterState_Content
;
795 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
797 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
803 case XmlWriterState_Initial
:
805 case XmlWriterState_Ready
:
806 case XmlWriterState_DocClosed
:
807 This
->state
= XmlWriterState_DocClosed
;
808 return WR_E_INVALIDACTION
;
813 /* empty element stack */
814 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
817 This
->state
= XmlWriterState_DocClosed
;
821 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
823 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
824 struct element
*element
;
830 case XmlWriterState_Initial
:
832 case XmlWriterState_Ready
:
833 case XmlWriterState_DocClosed
:
834 This
->state
= XmlWriterState_DocClosed
;
835 return WR_E_INVALIDACTION
;
840 element
= pop_element(This
);
842 return WR_E_INVALIDACTION
;
844 if (This
->starttagopen
) {
845 static WCHAR closetagW
[] = {' ','/','>'};
846 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
847 This
->starttagopen
= FALSE
;
850 /* write full end tag */
851 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
852 write_output_buffer(This
->output
, element
->qname
, element
->len
);
853 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
859 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
861 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
863 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
867 case XmlWriterState_Initial
:
869 case XmlWriterState_DocClosed
:
870 return WR_E_INVALIDACTION
;
878 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
880 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
881 struct element
*element
;
887 case XmlWriterState_Initial
:
889 case XmlWriterState_Ready
:
890 case XmlWriterState_DocClosed
:
891 This
->state
= XmlWriterState_DocClosed
;
892 return WR_E_INVALIDACTION
;
897 element
= pop_element(This
);
899 return WR_E_INVALIDACTION
;
901 /* write full end tag */
902 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
903 write_output_buffer(This
->output
, element
->qname
, element
->len
);
904 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
905 This
->starttagopen
= FALSE
;
910 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
912 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
914 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
918 case XmlWriterState_Initial
:
920 case XmlWriterState_Ready
:
921 case XmlWriterState_DocClosed
:
922 This
->state
= XmlWriterState_DocClosed
;
923 return WR_E_INVALIDACTION
;
931 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
933 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
935 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
939 case XmlWriterState_Initial
:
941 case XmlWriterState_Ready
:
942 case XmlWriterState_DocClosed
:
943 This
->state
= XmlWriterState_DocClosed
;
944 return WR_E_INVALIDACTION
;
952 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*pReader
,
953 BOOL fWriteDefaultAttributes
)
955 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
957 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
962 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*pReader
,
963 BOOL fWriteDefaultAttributes
)
965 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
967 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
972 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
975 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
976 static const WCHAR xmlW
[] = {'x','m','l',0};
977 static const WCHAR openpiW
[] = {'<','?'};
979 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
983 case XmlWriterState_Initial
:
985 case XmlWriterState_DocStarted
:
986 if (!strcmpW(name
, xmlW
))
987 return WR_E_INVALIDACTION
;
989 case XmlWriterState_ElemStarted
:
990 case XmlWriterState_DocClosed
:
991 return WR_E_INVALIDACTION
;
996 write_encoding_bom(This
);
997 write_output_buffer(This
->output
, openpiW
, ARRAY_SIZE(openpiW
));
998 write_output_buffer(This
->output
, name
, -1);
999 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
1000 write_output_buffer(This
->output
, text
, -1);
1001 write_output_buffer(This
->output
, closepiW
, ARRAY_SIZE(closepiW
));
1003 if (!strcmpW(name
, xmlW
))
1004 This
->state
= XmlWriterState_PIDocStarted
;
1009 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1010 LPCWSTR pwszNamespaceUri
)
1012 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1014 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1016 switch (This
->state
)
1018 case XmlWriterState_Initial
:
1019 return E_UNEXPECTED
;
1020 case XmlWriterState_DocClosed
:
1021 return WR_E_INVALIDACTION
;
1029 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1031 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1033 TRACE("%p %s\n", This
, debugstr_w(data
));
1038 switch (This
->state
)
1040 case XmlWriterState_Initial
:
1041 return E_UNEXPECTED
;
1042 case XmlWriterState_Ready
:
1043 write_xmldecl(This
, XmlStandalone_Omit
);
1045 case XmlWriterState_DocStarted
:
1046 case XmlWriterState_PIDocStarted
:
1049 This
->state
= XmlWriterState_DocClosed
;
1050 return WR_E_INVALIDACTION
;
1053 write_output_buffer(This
->output
, data
, -1);
1057 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1059 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1061 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1063 switch (This
->state
)
1065 case XmlWriterState_Initial
:
1066 return E_UNEXPECTED
;
1067 case XmlWriterState_DocClosed
:
1068 return WR_E_INVALIDACTION
;
1076 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1078 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1080 TRACE("(%p)->(%d)\n", This
, standalone
);
1082 switch (This
->state
)
1084 case XmlWriterState_Initial
:
1085 return E_UNEXPECTED
;
1086 case XmlWriterState_PIDocStarted
:
1087 This
->state
= XmlWriterState_DocStarted
;
1089 case XmlWriterState_Ready
:
1092 This
->state
= XmlWriterState_DocClosed
;
1093 return WR_E_INVALIDACTION
;
1096 return write_xmldecl(This
, standalone
);
1099 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1101 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1102 struct element
*element
;
1104 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1107 return E_INVALIDARG
;
1109 switch (This
->state
)
1111 case XmlWriterState_Initial
:
1112 return E_UNEXPECTED
;
1113 case XmlWriterState_DocClosed
:
1114 return WR_E_INVALIDACTION
;
1119 /* close pending element */
1120 if (This
->starttagopen
)
1121 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1123 element
= alloc_element(This
, prefix
, local_name
);
1125 return E_OUTOFMEMORY
;
1127 write_encoding_bom(This
);
1128 This
->state
= XmlWriterState_ElemStarted
;
1129 This
->starttagopen
= TRUE
;
1131 push_element(This
, element
);
1133 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
1134 write_output_qname(This
->output
, prefix
, local_name
);
1139 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, LPCWSTR pwszText
)
1141 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1143 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszText
));
1145 switch (This
->state
)
1147 case XmlWriterState_Initial
:
1148 return E_UNEXPECTED
;
1149 case XmlWriterState_Ready
:
1150 case XmlWriterState_DocClosed
:
1151 This
->state
= XmlWriterState_DocClosed
;
1152 return WR_E_INVALIDACTION
;
1160 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1162 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1164 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1169 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR pwszWhitespace
)
1171 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1173 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszWhitespace
));
1178 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1180 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1182 TRACE("%p\n", This
);
1184 return writeroutput_flush_stream(This
->output
);
1187 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1189 xmlwriter_QueryInterface
,
1192 xmlwriter_SetOutput
,
1193 xmlwriter_GetProperty
,
1194 xmlwriter_SetProperty
,
1195 xmlwriter_WriteAttributes
,
1196 xmlwriter_WriteAttributeString
,
1197 xmlwriter_WriteCData
,
1198 xmlwriter_WriteCharEntity
,
1199 xmlwriter_WriteChars
,
1200 xmlwriter_WriteComment
,
1201 xmlwriter_WriteDocType
,
1202 xmlwriter_WriteElementString
,
1203 xmlwriter_WriteEndDocument
,
1204 xmlwriter_WriteEndElement
,
1205 xmlwriter_WriteEntityRef
,
1206 xmlwriter_WriteFullEndElement
,
1207 xmlwriter_WriteName
,
1208 xmlwriter_WriteNmToken
,
1209 xmlwriter_WriteNode
,
1210 xmlwriter_WriteNodeShallow
,
1211 xmlwriter_WriteProcessingInstruction
,
1212 xmlwriter_WriteQualifiedName
,
1214 xmlwriter_WriteRawChars
,
1215 xmlwriter_WriteStartDocument
,
1216 xmlwriter_WriteStartElement
,
1217 xmlwriter_WriteString
,
1218 xmlwriter_WriteSurrogateCharEntity
,
1219 xmlwriter_WriteWhitespace
,
1223 /** IXmlWriterOutput **/
1224 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1226 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1228 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1230 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1231 IsEqualGUID(riid
, &IID_IUnknown
))
1237 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1239 return E_NOINTERFACE
;
1242 IUnknown_AddRef(iface
);
1247 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1249 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1250 ULONG ref
= InterlockedIncrement(&This
->ref
);
1251 TRACE("(%p)->(%d)\n", This
, ref
);
1255 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
1257 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1258 LONG ref
= InterlockedDecrement(&This
->ref
);
1260 TRACE("(%p)->(%d)\n", This
, ref
);
1264 IMalloc
*imalloc
= This
->imalloc
;
1265 if (This
->output
) IUnknown_Release(This
->output
);
1266 if (This
->stream
) ISequentialStream_Release(This
->stream
);
1267 free_output_buffer(This
);
1268 writeroutput_free(This
, This
);
1269 if (imalloc
) IMalloc_Release(imalloc
);
1275 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
1277 xmlwriteroutput_QueryInterface
,
1278 xmlwriteroutput_AddRef
,
1279 xmlwriteroutput_Release
1282 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
1286 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
1288 if (!IsEqualGUID(riid
, &IID_IXmlWriter
))
1290 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid
));
1295 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1297 writer
= heap_alloc(sizeof(*writer
));
1298 if(!writer
) return E_OUTOFMEMORY
;
1300 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1302 writer
->imalloc
= imalloc
;
1303 if (imalloc
) IMalloc_AddRef(imalloc
);
1304 writer
->output
= NULL
;
1305 writer
->indent
= FALSE
;
1307 writer
->omitxmldecl
= FALSE
;
1308 writer
->conformance
= XmlConformanceLevel_Document
;
1309 writer
->state
= XmlWriterState_Initial
;
1310 writer
->bomwritten
= FALSE
;
1311 writer
->starttagopen
= FALSE
;
1312 list_init(&writer
->elements
);
1314 *obj
= &writer
->IXmlWriter_iface
;
1316 TRACE("returning iface %p\n", *obj
);
1321 static HRESULT
create_writer(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1322 IXmlWriterOutput
**output
)
1324 xmlwriteroutput
*writeroutput
;
1330 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
1332 writeroutput
= heap_alloc(sizeof(*writeroutput
));
1333 if(!writeroutput
) return E_OUTOFMEMORY
;
1335 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
1336 writeroutput
->ref
= 1;
1337 writeroutput
->imalloc
= imalloc
;
1338 if (imalloc
) IMalloc_AddRef(imalloc
);
1339 writeroutput
->encoding
= encoding
;
1340 writeroutput
->stream
= NULL
;
1341 hr
= init_output_buffer(writeroutput
);
1343 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1347 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1349 *output
= &writeroutput
->IXmlWriterOutput_iface
;
1351 TRACE("returning iface %p\n", *output
);
1356 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
,
1359 IXmlWriterOutput
**output
)
1361 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
1362 xml_encoding xml_enc
;
1364 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), output
);
1366 if (!stream
|| !output
) return E_INVALIDARG
;
1368 xml_enc
= parse_encoding_name(encoding
? encoding
: utf8W
, -1);
1369 return create_writer(stream
, imalloc
, xml_enc
, output
);
1372 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
,
1375 IXmlWriterOutput
**output
)
1377 xml_encoding xml_enc
;
1379 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, output
);
1381 if (!stream
|| !output
) return E_INVALIDARG
;
1383 xml_enc
= get_encoding_from_codepage(codepage
);
1384 return create_writer(stream
, imalloc
, xml_enc
, output
);