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 static const WCHAR closeelementW
[] = {'<','/'};
41 static const WCHAR closetagW
[] = {' ','/','>'};
42 static const WCHAR closepiW
[] = {'?','>'};
43 static const WCHAR ltW
[] = {'<'};
44 static const WCHAR gtW
[] = {'>'};
45 static const WCHAR spaceW
[] = {' '};
46 static const WCHAR quoteW
[] = {'"'};
51 unsigned int allocated
;
58 XmlWriterState_Initial
, /* output is not set yet */
59 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
60 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
61 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
62 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
63 XmlWriterState_ElemStarted
, /* writing element */
64 XmlWriterState_Content
, /* content is accepted at this point */
65 XmlWriterState_DocClosed
/* WriteEndDocument was called */
70 IXmlWriterOutput IXmlWriterOutput_iface
;
73 ISequentialStream
*stream
;
75 xml_encoding encoding
;
76 WCHAR
*encoding_name
; /* exactly as specified on output creation */
77 struct output_buffer buffer
;
80 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
86 unsigned int len
; /* qname length in chars */
89 typedef struct _xmlwriter
91 IXmlWriter IXmlWriter_iface
;
94 xmlwriteroutput
*output
;
95 unsigned int indent_level
;
99 XmlConformanceLevel conformance
;
100 XmlWriterState state
;
103 struct list elements
;
106 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
108 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
111 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
113 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
116 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
118 static const char * const prop_names
[] =
123 "OmitXmlDeclaration",
127 if (prop
> _XmlWriterProperty_Last
)
128 return wine_dbg_sprintf("unknown property=%d", prop
);
130 return prop_names
[prop
];
133 /* writer output memory allocation functions */
134 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
136 return m_alloc(output
->imalloc
, len
);
139 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
141 m_free(output
->imalloc
, mem
);
144 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
146 return m_realloc(output
->imalloc
, mem
, len
);
149 /* writer memory allocation functions */
150 static inline void *writer_alloc(xmlwriter
*writer
, size_t len
)
152 return m_alloc(writer
->imalloc
, len
);
155 static inline void writer_free(xmlwriter
*writer
, void *mem
)
157 m_free(writer
->imalloc
, mem
);
160 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
165 ret
= writer_alloc(writer
, sizeof(*ret
));
166 if (!ret
) return ret
;
168 len
= prefix
? strlenW(prefix
) + 1 /* ':' */ : 0;
169 len
+= strlenW(local
);
171 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
174 static const WCHAR colonW
[] = {':',0};
175 strcpyW(ret
->qname
, prefix
);
176 strcatW(ret
->qname
, colonW
);
180 strcatW(ret
->qname
, local
);
185 static void free_element(xmlwriter
*writer
, struct element
*element
)
187 writer_free(writer
, element
->qname
);
188 writer_free(writer
, element
);
191 static void push_element(xmlwriter
*writer
, struct element
*element
)
193 list_add_head(&writer
->elements
, &element
->entry
);
196 static struct element
*pop_element(xmlwriter
*writer
)
198 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
201 list_remove(&element
->entry
);
206 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
208 struct output_buffer
*buffer
= &output
->buffer
;
209 const int initial_len
= 0x2000;
213 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
214 WARN("Failed to get code page for specified encoding.\n");
216 buffer
->data
= writeroutput_alloc(output
, initial_len
);
217 if (!buffer
->data
) return E_OUTOFMEMORY
;
219 memset(buffer
->data
, 0, 4);
220 buffer
->allocated
= initial_len
;
222 buffer
->codepage
= cp
;
227 static void free_output_buffer(xmlwriteroutput
*output
)
229 struct output_buffer
*buffer
= &output
->buffer
;
230 writeroutput_free(output
, buffer
->data
);
232 buffer
->allocated
= 0;
236 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
238 struct output_buffer
*buffer
= &output
->buffer
;
239 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
240 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
241 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
242 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
243 if (!ptr
) return E_OUTOFMEMORY
;
245 buffer
->allocated
= grown_size
;
251 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
253 struct output_buffer
*buffer
= &output
->buffer
;
258 if (buffer
->codepage
== 1200) {
259 /* For UTF-16 encoding just copy. */
260 length
= len
== -1 ? strlenW(data
) : len
;
262 length
*= sizeof(WCHAR
);
264 hr
= grow_output_buffer(output
, length
);
265 if (FAILED(hr
)) return hr
;
266 ptr
= buffer
->data
+ buffer
->written
;
268 memcpy(ptr
, data
, length
);
269 buffer
->written
+= length
;
271 /* null termination */
276 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
277 hr
= grow_output_buffer(output
, length
);
278 if (FAILED(hr
)) return hr
;
279 ptr
= buffer
->data
+ buffer
->written
;
280 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
281 buffer
->written
+= len
== -1 ? length
-1 : length
;
287 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
289 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
290 write_output_buffer(output
, data
, len
);
291 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
295 /* TODO: test if we need to validate char range */
296 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, const WCHAR
*local_name
)
299 static const WCHAR colW
[] = {':'};
300 write_output_buffer(output
, prefix
, -1);
301 write_output_buffer(output
, colW
, ARRAY_SIZE(colW
));
304 write_output_buffer(output
, local_name
, -1);
309 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
311 if (writeroutput
->stream
) {
312 ISequentialStream_Release(writeroutput
->stream
);
313 writeroutput
->stream
= NULL
;
317 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
321 writeroutput_release_stream(writeroutput
);
322 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
324 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
329 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
331 struct output_buffer
*buffer
;
332 ULONG written
, offset
= 0;
335 if (!output
|| !output
->stream
)
338 buffer
= &output
->buffer
;
340 /* It will loop forever until everything is written or an error occurred. */
343 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
345 WARN("write to stream failed (0x%08x)\n", hr
);
351 buffer
->written
-= written
;
352 } while (buffer
->written
> 0);
357 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
359 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
361 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
362 static const char utf16bom
[] = {0xff, 0xfe};
363 struct output_buffer
*buffer
= &writer
->output
->buffer
;
364 int len
= sizeof(utf16bom
);
367 hr
= grow_output_buffer(writer
->output
, len
);
368 if (FAILED(hr
)) return hr
;
369 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
370 buffer
->written
+= len
;
373 writer
->bomwritten
= TRUE
;
377 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
379 if (output
->encoding_name
)
380 return output
->encoding_name
;
382 return get_encoding_name(output
->encoding
);
385 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
387 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
388 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','='};
390 write_encoding_bom(writer
);
391 writer
->state
= XmlWriterState_DocStarted
;
392 if (writer
->omitxmldecl
) return S_OK
;
395 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
398 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
399 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
402 if (standalone
== XmlStandalone_Omit
)
403 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
405 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
406 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
407 static const WCHAR noW
[] = {'n','o','\"','?','>'};
409 write_output_buffer(writer
->output
, standaloneW
, ARRAY_SIZE(standaloneW
));
410 if (standalone
== XmlStandalone_Yes
)
411 write_output_buffer(writer
->output
, yesW
, ARRAY_SIZE(yesW
));
413 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
419 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
423 if (!writer
->starttagopen
) return S_OK
;
424 hr
= write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
425 writer
->starttagopen
= FALSE
;
429 static void writer_inc_indent(xmlwriter
*writer
)
431 writer
->indent_level
++;
434 static void writer_dec_indent(xmlwriter
*writer
)
436 if (writer
->indent_level
)
437 writer
->indent_level
--;
440 static void write_node_indent(xmlwriter
*writer
)
442 static const WCHAR dblspaceW
[] = {' ',' '};
443 static const WCHAR crlfW
[] = {'\r','\n'};
444 unsigned int indent_level
= writer
->indent_level
;
449 /* Do state check to prevent newline inserted after BOM. It is assumed that
450 state does not change between writing BOM and inserting indentation. */
451 if (writer
->output
->buffer
.written
&& writer
->state
!= XmlWriterState_Ready
)
452 write_output_buffer(writer
->output
, crlfW
, ARRAY_SIZE(crlfW
));
453 while (indent_level
--)
454 write_output_buffer(writer
->output
, dblspaceW
, ARRAY_SIZE(dblspaceW
));
457 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
459 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
461 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
463 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
464 IsEqualGUID(riid
, &IID_IUnknown
))
470 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
472 return E_NOINTERFACE
;
475 IXmlWriter_AddRef(iface
);
480 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
482 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
483 ULONG ref
= InterlockedIncrement(&This
->ref
);
484 TRACE("(%p)->(%u)\n", This
, ref
);
488 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
490 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
491 ULONG ref
= InterlockedDecrement(&This
->ref
);
493 TRACE("(%p)->(%u)\n", This
, ref
);
496 struct element
*element
, *element2
;
497 IMalloc
*imalloc
= This
->imalloc
;
499 writeroutput_flush_stream(This
->output
);
500 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
503 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &This
->elements
, struct element
, entry
) {
504 list_remove(&element
->entry
);
505 free_element(This
, element
);
508 writer_free(This
, This
);
509 if (imalloc
) IMalloc_Release(imalloc
);
515 /*** IXmlWriter methods ***/
516 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
518 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
519 IXmlWriterOutput
*writeroutput
;
522 TRACE("(%p)->(%p)\n", This
, output
);
525 writeroutput_release_stream(This
->output
);
526 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
528 This
->bomwritten
= FALSE
;
529 This
->indent_level
= 0;
532 /* just reset current output */
534 This
->state
= XmlWriterState_Initial
;
538 /* now try IXmlWriterOutput, ISequentialStream, IStream */
539 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
541 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
542 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
544 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
545 writeroutput
, writeroutput
->lpVtbl
);
546 IUnknown_Release(writeroutput
);
551 if (hr
!= S_OK
|| !writeroutput
) {
552 /* create IXmlWriterOutput basing on supplied interface */
553 hr
= CreateXmlWriterOutputWithEncodingName(output
, This
->imalloc
, NULL
, &writeroutput
);
554 if (hr
!= S_OK
) return hr
;
555 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
558 if (This
->output
->encoding
== XmlEncoding_Unknown
)
559 This
->state
= XmlWriterState_InvalidEncoding
;
561 This
->state
= XmlWriterState_Ready
;
562 return writeroutput_query_for_stream(This
->output
);
565 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
567 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
569 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
571 if (!value
) return E_INVALIDARG
;
575 case XmlWriterProperty_Indent
:
576 *value
= This
->indent
;
578 case XmlWriterProperty_ByteOrderMark
:
581 case XmlWriterProperty_OmitXmlDeclaration
:
582 *value
= This
->omitxmldecl
;
584 case XmlWriterProperty_ConformanceLevel
:
585 *value
= This
->conformance
;
588 FIXME("Unimplemented property (%u)\n", property
);
595 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
597 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
599 TRACE("(%p)->(%s %lu)\n", This
, debugstr_writer_prop(property
), value
);
603 case XmlWriterProperty_Indent
:
604 This
->indent
= !!value
;
606 case XmlWriterProperty_ByteOrderMark
:
609 case XmlWriterProperty_OmitXmlDeclaration
:
610 This
->omitxmldecl
= !!value
;
613 FIXME("Unimplemented property (%u)\n", property
);
620 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*pReader
,
621 BOOL fWriteDefaultAttributes
)
623 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
625 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
630 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR ns_prefix
,
631 LPCWSTR local_name
, LPCWSTR ns_uri
, LPCWSTR value
)
633 static const WCHAR eqW
[] = {'=','"'};
634 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
636 TRACE("%p %s %s %s %s\n", This
, debugstr_w(ns_prefix
), debugstr_w(local_name
),
637 debugstr_w(ns_uri
), debugstr_w(value
));
641 case XmlWriterState_Initial
:
643 case XmlWriterState_Ready
:
644 case XmlWriterState_DocClosed
:
645 This
->state
= XmlWriterState_DocClosed
;
646 return WR_E_INVALIDACTION
;
647 case XmlWriterState_InvalidEncoding
:
648 return MX_E_ENCODING
;
653 if (ns_prefix
|| ns_uri
)
655 FIXME("namespaces are not supported.\n");
659 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
660 write_output_buffer(This
->output
, local_name
, -1);
661 write_output_buffer(This
->output
, eqW
, ARRAY_SIZE(eqW
));
662 write_output_buffer(This
->output
, value
, -1);
663 write_output_buffer(This
->output
, quoteW
, ARRAY_SIZE(quoteW
));
668 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
670 static const WCHAR cdataopenW
[] = {'<','!','[','C','D','A','T','A','['};
671 static const WCHAR cdatacloseW
[] = {']',']','>'};
672 write_output_buffer(output
, cdataopenW
, ARRAY_SIZE(cdataopenW
));
674 write_output_buffer(output
, data
, len
);
675 write_output_buffer(output
, cdatacloseW
, ARRAY_SIZE(cdatacloseW
));
678 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
680 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
683 TRACE("%p %s\n", This
, debugstr_w(data
));
687 case XmlWriterState_Initial
:
689 case XmlWriterState_ElemStarted
:
690 writer_close_starttag(This
);
692 case XmlWriterState_Ready
:
693 case XmlWriterState_DocClosed
:
694 This
->state
= XmlWriterState_DocClosed
;
695 return WR_E_INVALIDACTION
;
696 case XmlWriterState_InvalidEncoding
:
697 return MX_E_ENCODING
;
702 len
= data
? strlenW(data
) : 0;
704 write_node_indent(This
);
706 write_cdata_section(This
->output
, NULL
, 0);
708 static const WCHAR cdatacloseW
[] = {']',']','>',0};
710 const WCHAR
*str
= strstrW(data
, cdatacloseW
);
713 write_cdata_section(This
->output
, data
, str
- data
);
718 write_cdata_section(This
->output
, data
, len
);
727 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
729 static const WCHAR fmtW
[] = {'&','#','x','%','x',';',0};
730 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
733 TRACE("%p %#x\n", This
, ch
);
737 case XmlWriterState_Initial
:
739 case XmlWriterState_InvalidEncoding
:
740 return MX_E_ENCODING
;
741 case XmlWriterState_ElemStarted
:
742 writer_close_starttag(This
);
744 case XmlWriterState_DocClosed
:
745 return WR_E_INVALIDACTION
;
750 sprintfW(bufW
, fmtW
, ch
);
751 write_output_buffer(This
->output
, bufW
, -1);
756 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
758 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
760 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
764 case XmlWriterState_Initial
:
766 case XmlWriterState_InvalidEncoding
:
767 return MX_E_ENCODING
;
768 case XmlWriterState_DocClosed
:
769 return WR_E_INVALIDACTION
;
778 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
780 static const WCHAR copenW
[] = {'<','!','-','-'};
781 static const WCHAR ccloseW
[] = {'-','-','>'};
782 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
784 TRACE("%p %s\n", This
, debugstr_w(comment
));
788 case XmlWriterState_Initial
:
790 case XmlWriterState_InvalidEncoding
:
791 return MX_E_ENCODING
;
792 case XmlWriterState_ElemStarted
:
793 writer_close_starttag(This
);
795 case XmlWriterState_DocClosed
:
796 return WR_E_INVALIDACTION
;
801 write_node_indent(This
);
802 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
804 int len
= strlenW(comment
), i
;
806 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
809 for (i
= 0; i
< len
; i
++) {
810 write_output_buffer(This
->output
, comment
+ i
, 1);
811 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
812 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
816 write_output_buffer(This
->output
, comment
, len
);
818 if (len
&& comment
[len
-1] == '-')
819 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
821 write_output_buffer(This
->output
, ccloseW
, ARRAY_SIZE(ccloseW
));
826 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR pwszName
, LPCWSTR pwszPublicId
,
827 LPCWSTR pwszSystemId
, LPCWSTR pwszSubset
)
829 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
831 FIXME("%p %s %s %s %s\n", This
, wine_dbgstr_w(pwszName
), wine_dbgstr_w(pwszPublicId
),
832 wine_dbgstr_w(pwszSystemId
), wine_dbgstr_w(pwszSubset
));
837 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
838 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
840 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
842 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
843 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
847 case XmlWriterState_Initial
:
849 case XmlWriterState_InvalidEncoding
:
850 return MX_E_ENCODING
;
851 case XmlWriterState_ElemStarted
:
852 writer_close_starttag(This
);
854 case XmlWriterState_DocClosed
:
855 return WR_E_INVALIDACTION
;
860 write_encoding_bom(This
);
861 write_node_indent(This
);
862 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
863 write_output_qname(This
->output
, prefix
, local_name
);
867 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
868 write_output_buffer(This
->output
, value
, -1);
869 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
870 write_output_qname(This
->output
, prefix
, local_name
);
871 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
874 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
876 This
->state
= XmlWriterState_Content
;
881 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
883 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
889 case XmlWriterState_Initial
:
891 case XmlWriterState_Ready
:
892 case XmlWriterState_DocClosed
:
893 This
->state
= XmlWriterState_DocClosed
;
894 return WR_E_INVALIDACTION
;
895 case XmlWriterState_InvalidEncoding
:
896 return MX_E_ENCODING
;
901 /* empty element stack */
902 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
905 This
->state
= XmlWriterState_DocClosed
;
909 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
911 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
912 struct element
*element
;
918 case XmlWriterState_Initial
:
920 case XmlWriterState_Ready
:
921 case XmlWriterState_DocClosed
:
922 This
->state
= XmlWriterState_DocClosed
;
923 return WR_E_INVALIDACTION
;
924 case XmlWriterState_InvalidEncoding
:
925 return MX_E_ENCODING
;
930 element
= pop_element(This
);
932 return WR_E_INVALIDACTION
;
934 writer_dec_indent(This
);
936 if (This
->starttagopen
)
938 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
939 This
->starttagopen
= FALSE
;
942 /* write full end tag */
943 write_node_indent(This
);
944 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
945 write_output_buffer(This
->output
, element
->qname
, element
->len
);
946 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
952 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
954 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
956 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
960 case XmlWriterState_Initial
:
962 case XmlWriterState_InvalidEncoding
:
963 return MX_E_ENCODING
;
964 case XmlWriterState_DocClosed
:
965 return WR_E_INVALIDACTION
;
973 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
975 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
976 struct element
*element
;
982 case XmlWriterState_Initial
:
984 case XmlWriterState_Ready
:
985 case XmlWriterState_DocClosed
:
986 This
->state
= XmlWriterState_DocClosed
;
987 return WR_E_INVALIDACTION
;
988 case XmlWriterState_InvalidEncoding
:
989 return MX_E_ENCODING
;
994 element
= pop_element(This
);
996 return WR_E_INVALIDACTION
;
998 writer_close_starttag(This
);
999 writer_dec_indent(This
);
1001 /* don't force full end tag to the next line */
1002 if (This
->state
== XmlWriterState_ElemStarted
)
1003 This
->state
= XmlWriterState_Content
;
1005 write_node_indent(This
);
1007 /* write full end tag */
1008 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1009 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1010 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1015 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1017 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1019 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1021 switch (This
->state
)
1023 case XmlWriterState_Initial
:
1024 return E_UNEXPECTED
;
1025 case XmlWriterState_Ready
:
1026 case XmlWriterState_DocClosed
:
1027 This
->state
= XmlWriterState_DocClosed
;
1028 return WR_E_INVALIDACTION
;
1029 case XmlWriterState_InvalidEncoding
:
1030 return MX_E_ENCODING
;
1038 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1040 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1042 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1044 switch (This
->state
)
1046 case XmlWriterState_Initial
:
1047 return E_UNEXPECTED
;
1048 case XmlWriterState_Ready
:
1049 case XmlWriterState_DocClosed
:
1050 This
->state
= XmlWriterState_DocClosed
;
1051 return WR_E_INVALIDACTION
;
1052 case XmlWriterState_InvalidEncoding
:
1053 return MX_E_ENCODING
;
1061 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*pReader
,
1062 BOOL fWriteDefaultAttributes
)
1064 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1066 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1071 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*pReader
,
1072 BOOL fWriteDefaultAttributes
)
1074 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1076 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1081 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1084 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1085 static const WCHAR xmlW
[] = {'x','m','l',0};
1086 static const WCHAR openpiW
[] = {'<','?'};
1088 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1090 switch (This
->state
)
1092 case XmlWriterState_Initial
:
1093 return E_UNEXPECTED
;
1094 case XmlWriterState_InvalidEncoding
:
1095 return MX_E_ENCODING
;
1096 case XmlWriterState_DocStarted
:
1097 if (!strcmpW(name
, xmlW
))
1098 return WR_E_INVALIDACTION
;
1100 case XmlWriterState_ElemStarted
:
1101 case XmlWriterState_DocClosed
:
1102 return WR_E_INVALIDACTION
;
1107 write_encoding_bom(This
);
1108 write_node_indent(This
);
1109 write_output_buffer(This
->output
, openpiW
, ARRAY_SIZE(openpiW
));
1110 write_output_buffer(This
->output
, name
, -1);
1111 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
1112 write_output_buffer(This
->output
, text
, -1);
1113 write_output_buffer(This
->output
, closepiW
, ARRAY_SIZE(closepiW
));
1115 if (!strcmpW(name
, xmlW
))
1116 This
->state
= XmlWriterState_PIDocStarted
;
1121 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1122 LPCWSTR pwszNamespaceUri
)
1124 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1126 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1128 switch (This
->state
)
1130 case XmlWriterState_Initial
:
1131 return E_UNEXPECTED
;
1132 case XmlWriterState_InvalidEncoding
:
1133 return MX_E_ENCODING
;
1134 case XmlWriterState_DocClosed
:
1135 return WR_E_INVALIDACTION
;
1143 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1145 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1147 TRACE("%p %s\n", This
, debugstr_w(data
));
1152 switch (This
->state
)
1154 case XmlWriterState_Initial
:
1155 return E_UNEXPECTED
;
1156 case XmlWriterState_Ready
:
1157 write_xmldecl(This
, XmlStandalone_Omit
);
1159 case XmlWriterState_DocStarted
:
1160 case XmlWriterState_PIDocStarted
:
1162 case XmlWriterState_InvalidEncoding
:
1163 return MX_E_ENCODING
;
1165 This
->state
= XmlWriterState_DocClosed
;
1166 return WR_E_INVALIDACTION
;
1169 write_output_buffer(This
->output
, data
, -1);
1173 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1175 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1177 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1179 switch (This
->state
)
1181 case XmlWriterState_Initial
:
1182 return E_UNEXPECTED
;
1183 case XmlWriterState_InvalidEncoding
:
1184 return MX_E_ENCODING
;
1185 case XmlWriterState_DocClosed
:
1186 return WR_E_INVALIDACTION
;
1194 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1196 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1198 TRACE("(%p)->(%d)\n", This
, standalone
);
1200 switch (This
->state
)
1202 case XmlWriterState_Initial
:
1203 return E_UNEXPECTED
;
1204 case XmlWriterState_PIDocStarted
:
1205 This
->state
= XmlWriterState_DocStarted
;
1207 case XmlWriterState_Ready
:
1209 case XmlWriterState_InvalidEncoding
:
1210 return MX_E_ENCODING
;
1212 This
->state
= XmlWriterState_DocClosed
;
1213 return WR_E_INVALIDACTION
;
1216 return write_xmldecl(This
, standalone
);
1219 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1221 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1222 struct element
*element
;
1224 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1227 return E_INVALIDARG
;
1229 switch (This
->state
)
1231 case XmlWriterState_Initial
:
1232 return E_UNEXPECTED
;
1233 case XmlWriterState_InvalidEncoding
:
1234 return MX_E_ENCODING
;
1235 case XmlWriterState_DocClosed
:
1236 return WR_E_INVALIDACTION
;
1241 /* close pending element */
1242 if (This
->starttagopen
)
1243 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1245 element
= alloc_element(This
, prefix
, local_name
);
1247 return E_OUTOFMEMORY
;
1249 write_encoding_bom(This
);
1250 write_node_indent(This
);
1252 This
->state
= XmlWriterState_ElemStarted
;
1253 This
->starttagopen
= TRUE
;
1255 push_element(This
, element
);
1257 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
1258 write_output_qname(This
->output
, prefix
, local_name
);
1259 writer_inc_indent(This
);
1264 static void write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
)
1266 static const WCHAR ampW
[] = {'&','a','m','p',';'};
1267 static const WCHAR ltW
[] = {'&','l','t',';'};
1268 static const WCHAR gtW
[] = {'&','g','t',';'};
1275 write_output_buffer(writer
->output
, ltW
, ARRAY_SIZE(ltW
));
1278 write_output_buffer(writer
->output
, ampW
, ARRAY_SIZE(ampW
));
1281 write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
1284 write_output_buffer(writer
->output
, string
, 1);
1291 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1293 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1295 TRACE("%p %s\n", This
, debugstr_w(string
));
1300 switch (This
->state
)
1302 case XmlWriterState_Initial
:
1303 return E_UNEXPECTED
;
1304 case XmlWriterState_ElemStarted
:
1305 writer_close_starttag(This
);
1307 case XmlWriterState_Ready
:
1308 case XmlWriterState_DocClosed
:
1309 This
->state
= XmlWriterState_DocClosed
;
1310 return WR_E_INVALIDACTION
;
1311 case XmlWriterState_InvalidEncoding
:
1312 return MX_E_ENCODING
;
1317 write_escaped_string(This
, string
);
1321 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1323 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1325 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1330 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR pwszWhitespace
)
1332 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1334 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszWhitespace
));
1339 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1341 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1343 TRACE("%p\n", This
);
1345 return writeroutput_flush_stream(This
->output
);
1348 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1350 xmlwriter_QueryInterface
,
1353 xmlwriter_SetOutput
,
1354 xmlwriter_GetProperty
,
1355 xmlwriter_SetProperty
,
1356 xmlwriter_WriteAttributes
,
1357 xmlwriter_WriteAttributeString
,
1358 xmlwriter_WriteCData
,
1359 xmlwriter_WriteCharEntity
,
1360 xmlwriter_WriteChars
,
1361 xmlwriter_WriteComment
,
1362 xmlwriter_WriteDocType
,
1363 xmlwriter_WriteElementString
,
1364 xmlwriter_WriteEndDocument
,
1365 xmlwriter_WriteEndElement
,
1366 xmlwriter_WriteEntityRef
,
1367 xmlwriter_WriteFullEndElement
,
1368 xmlwriter_WriteName
,
1369 xmlwriter_WriteNmToken
,
1370 xmlwriter_WriteNode
,
1371 xmlwriter_WriteNodeShallow
,
1372 xmlwriter_WriteProcessingInstruction
,
1373 xmlwriter_WriteQualifiedName
,
1375 xmlwriter_WriteRawChars
,
1376 xmlwriter_WriteStartDocument
,
1377 xmlwriter_WriteStartElement
,
1378 xmlwriter_WriteString
,
1379 xmlwriter_WriteSurrogateCharEntity
,
1380 xmlwriter_WriteWhitespace
,
1384 /** IXmlWriterOutput **/
1385 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1387 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1389 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1391 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1392 IsEqualGUID(riid
, &IID_IUnknown
))
1398 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1400 return E_NOINTERFACE
;
1403 IUnknown_AddRef(iface
);
1408 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1410 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1411 ULONG ref
= InterlockedIncrement(&This
->ref
);
1412 TRACE("(%p)->(%d)\n", This
, ref
);
1416 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
1418 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1419 LONG ref
= InterlockedDecrement(&This
->ref
);
1421 TRACE("(%p)->(%d)\n", This
, ref
);
1425 IMalloc
*imalloc
= This
->imalloc
;
1426 if (This
->output
) IUnknown_Release(This
->output
);
1427 if (This
->stream
) ISequentialStream_Release(This
->stream
);
1428 free_output_buffer(This
);
1429 writeroutput_free(This
, This
->encoding_name
);
1430 writeroutput_free(This
, This
);
1431 if (imalloc
) IMalloc_Release(imalloc
);
1437 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
1439 xmlwriteroutput_QueryInterface
,
1440 xmlwriteroutput_AddRef
,
1441 xmlwriteroutput_Release
1444 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
1449 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
1452 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1454 writer
= heap_alloc(sizeof(*writer
));
1455 if(!writer
) return E_OUTOFMEMORY
;
1457 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1459 writer
->imalloc
= imalloc
;
1460 if (imalloc
) IMalloc_AddRef(imalloc
);
1461 writer
->output
= NULL
;
1462 writer
->indent_level
= 0;
1463 writer
->indent
= FALSE
;
1465 writer
->omitxmldecl
= FALSE
;
1466 writer
->conformance
= XmlConformanceLevel_Document
;
1467 writer
->state
= XmlWriterState_Initial
;
1468 writer
->bomwritten
= FALSE
;
1469 writer
->starttagopen
= FALSE
;
1470 list_init(&writer
->elements
);
1472 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
1473 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
1475 TRACE("returning iface %p, hr %#x\n", *obj
, hr
);
1480 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1481 const WCHAR
*encoding_name
, IXmlWriterOutput
**output
)
1483 xmlwriteroutput
*writeroutput
;
1489 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
1491 writeroutput
= heap_alloc(sizeof(*writeroutput
));
1493 return E_OUTOFMEMORY
;
1495 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
1496 writeroutput
->ref
= 1;
1497 writeroutput
->imalloc
= imalloc
;
1499 IMalloc_AddRef(imalloc
);
1500 writeroutput
->encoding
= encoding
;
1501 writeroutput
->stream
= NULL
;
1502 hr
= init_output_buffer(writeroutput
);
1504 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1508 if (encoding_name
) {
1509 unsigned int size
= (strlenW(encoding_name
) + 1) * sizeof(WCHAR
);
1510 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
1511 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
1514 writeroutput
->encoding_name
= NULL
;
1516 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1518 *output
= &writeroutput
->IXmlWriterOutput_iface
;
1520 TRACE("returning iface %p\n", *output
);
1525 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
,
1528 IXmlWriterOutput
**output
)
1530 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
1531 xml_encoding xml_enc
;
1533 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), output
);
1535 if (!stream
|| !output
) return E_INVALIDARG
;
1537 xml_enc
= parse_encoding_name(encoding
? encoding
: utf8W
, -1);
1538 return create_writer_output(stream
, imalloc
, xml_enc
, encoding
, output
);
1541 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
,
1544 IXmlWriterOutput
**output
)
1546 xml_encoding xml_enc
;
1548 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, output
);
1550 if (!stream
|| !output
) return E_INVALIDARG
;
1552 xml_enc
= get_encoding_from_codepage(codepage
);
1553 return create_writer_output(stream
, imalloc
, xml_enc
, NULL
, output
);