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
22 #include "xmllite_private.h"
24 #include <wine/list.h>
25 #include <wine/unicode.h>
27 /* not defined in public headers */
28 DEFINE_GUID(IID_IXmlWriterOutput
, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
30 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
32 static const WCHAR closeelementW
[] = {'<','/'};
33 static const WCHAR closetagW
[] = {' ','/','>'};
34 static const WCHAR closepiW
[] = {'?','>'};
35 static const WCHAR ltW
[] = {'<'};
36 static const WCHAR gtW
[] = {'>'};
37 static const WCHAR spaceW
[] = {' '};
38 static const WCHAR quoteW
[] = {'"'};
43 unsigned int allocated
;
50 XmlWriterState_Initial
, /* output is not set yet */
51 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
52 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
53 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
54 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
55 XmlWriterState_ElemStarted
, /* writing element */
56 XmlWriterState_Content
, /* content is accepted at this point */
57 XmlWriterState_DocClosed
/* WriteEndDocument was called */
62 IXmlWriterOutput IXmlWriterOutput_iface
;
65 ISequentialStream
*stream
;
67 xml_encoding encoding
;
68 WCHAR
*encoding_name
; /* exactly as specified on output creation */
69 struct output_buffer buffer
;
72 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
78 unsigned int len
; /* qname length in chars */
81 typedef struct _xmlwriter
83 IXmlWriter IXmlWriter_iface
;
86 xmlwriteroutput
*output
;
87 unsigned int indent_level
;
91 XmlConformanceLevel conformance
;
98 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
100 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
103 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
105 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
108 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
110 static const char * const prop_names
[] =
115 "OmitXmlDeclaration",
119 if (prop
> _XmlWriterProperty_Last
)
120 return wine_dbg_sprintf("unknown property=%d", prop
);
122 return prop_names
[prop
];
125 /* writer output memory allocation functions */
126 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
128 return m_alloc(output
->imalloc
, len
);
131 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
133 m_free(output
->imalloc
, mem
);
136 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
138 return m_realloc(output
->imalloc
, mem
, len
);
141 /* writer memory allocation functions */
142 static inline void *writer_alloc(xmlwriter
*writer
, size_t len
)
144 return m_alloc(writer
->imalloc
, len
);
147 static inline void writer_free(xmlwriter
*writer
, void *mem
)
149 m_free(writer
->imalloc
, mem
);
152 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
157 ret
= writer_alloc(writer
, sizeof(*ret
));
158 if (!ret
) return ret
;
160 len
= prefix
? strlenW(prefix
) + 1 /* ':' */ : 0;
161 len
+= strlenW(local
);
163 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
166 static const WCHAR colonW
[] = {':',0};
167 strcpyW(ret
->qname
, prefix
);
168 strcatW(ret
->qname
, colonW
);
172 strcatW(ret
->qname
, local
);
177 static void free_element(xmlwriter
*writer
, struct element
*element
)
179 writer_free(writer
, element
->qname
);
180 writer_free(writer
, element
);
183 static void push_element(xmlwriter
*writer
, struct element
*element
)
185 list_add_head(&writer
->elements
, &element
->entry
);
188 static struct element
*pop_element(xmlwriter
*writer
)
190 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
193 list_remove(&element
->entry
);
198 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
200 struct output_buffer
*buffer
= &output
->buffer
;
201 const int initial_len
= 0x2000;
205 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
206 WARN("Failed to get code page for specified encoding.\n");
208 buffer
->data
= writeroutput_alloc(output
, initial_len
);
209 if (!buffer
->data
) return E_OUTOFMEMORY
;
211 memset(buffer
->data
, 0, 4);
212 buffer
->allocated
= initial_len
;
214 buffer
->codepage
= cp
;
219 static void free_output_buffer(xmlwriteroutput
*output
)
221 struct output_buffer
*buffer
= &output
->buffer
;
222 writeroutput_free(output
, buffer
->data
);
224 buffer
->allocated
= 0;
228 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
230 struct output_buffer
*buffer
= &output
->buffer
;
231 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
232 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
233 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
234 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
235 if (!ptr
) return E_OUTOFMEMORY
;
237 buffer
->allocated
= grown_size
;
243 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
245 struct output_buffer
*buffer
= &output
->buffer
;
250 if (buffer
->codepage
== 1200) {
251 /* For UTF-16 encoding just copy. */
252 length
= len
== -1 ? strlenW(data
) : len
;
254 length
*= sizeof(WCHAR
);
256 hr
= grow_output_buffer(output
, length
);
257 if (FAILED(hr
)) return hr
;
258 ptr
= buffer
->data
+ buffer
->written
;
260 memcpy(ptr
, data
, length
);
261 buffer
->written
+= length
;
263 /* null termination */
268 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
269 hr
= grow_output_buffer(output
, length
);
270 if (FAILED(hr
)) return hr
;
271 ptr
= buffer
->data
+ buffer
->written
;
272 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
273 buffer
->written
+= len
== -1 ? length
-1 : length
;
279 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
281 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
282 write_output_buffer(output
, data
, len
);
283 write_output_buffer(output
, quoteW
, ARRAY_SIZE(quoteW
));
287 /* TODO: test if we need to validate char range */
288 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, const WCHAR
*local_name
)
291 static const WCHAR colW
[] = {':'};
292 write_output_buffer(output
, prefix
, -1);
293 write_output_buffer(output
, colW
, ARRAY_SIZE(colW
));
296 write_output_buffer(output
, local_name
, -1);
301 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
303 if (writeroutput
->stream
) {
304 ISequentialStream_Release(writeroutput
->stream
);
305 writeroutput
->stream
= NULL
;
309 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
313 writeroutput_release_stream(writeroutput
);
314 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
316 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
321 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
323 struct output_buffer
*buffer
;
324 ULONG written
, offset
= 0;
327 if (!output
|| !output
->stream
)
330 buffer
= &output
->buffer
;
332 /* It will loop forever until everything is written or an error occurred. */
335 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
337 WARN("write to stream failed (0x%08x)\n", hr
);
343 buffer
->written
-= written
;
344 } while (buffer
->written
> 0);
349 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
351 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
353 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
354 static const char utf16bom
[] = {0xff, 0xfe};
355 struct output_buffer
*buffer
= &writer
->output
->buffer
;
356 int len
= sizeof(utf16bom
);
359 hr
= grow_output_buffer(writer
->output
, len
);
360 if (FAILED(hr
)) return hr
;
361 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
362 buffer
->written
+= len
;
365 writer
->bomwritten
= TRUE
;
369 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
371 if (output
->encoding_name
)
372 return output
->encoding_name
;
374 return get_encoding_name(output
->encoding
);
377 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
379 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
380 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','='};
382 write_encoding_bom(writer
);
383 writer
->state
= XmlWriterState_DocStarted
;
384 if (writer
->omitxmldecl
) return S_OK
;
387 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
390 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
391 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
394 if (standalone
== XmlStandalone_Omit
)
395 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
397 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
398 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
399 static const WCHAR noW
[] = {'n','o','\"','?','>'};
401 write_output_buffer(writer
->output
, standaloneW
, ARRAY_SIZE(standaloneW
));
402 if (standalone
== XmlStandalone_Yes
)
403 write_output_buffer(writer
->output
, yesW
, ARRAY_SIZE(yesW
));
405 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
411 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
415 if (!writer
->starttagopen
) return S_OK
;
416 hr
= write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
417 writer
->starttagopen
= FALSE
;
421 static void writer_inc_indent(xmlwriter
*writer
)
423 writer
->indent_level
++;
426 static void writer_dec_indent(xmlwriter
*writer
)
428 if (writer
->indent_level
)
429 writer
->indent_level
--;
432 static void write_node_indent(xmlwriter
*writer
)
434 static const WCHAR dblspaceW
[] = {' ',' '};
435 static const WCHAR crlfW
[] = {'\r','\n'};
436 unsigned int indent_level
= writer
->indent_level
;
441 /* Do state check to prevent newline inserted after BOM. It is assumed that
442 state does not change between writing BOM and inserting indentation. */
443 if (writer
->output
->buffer
.written
&& writer
->state
!= XmlWriterState_Ready
)
444 write_output_buffer(writer
->output
, crlfW
, ARRAY_SIZE(crlfW
));
445 while (indent_level
--)
446 write_output_buffer(writer
->output
, dblspaceW
, ARRAY_SIZE(dblspaceW
));
449 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
451 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
453 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
455 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
456 IsEqualGUID(riid
, &IID_IUnknown
))
462 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
464 return E_NOINTERFACE
;
467 IXmlWriter_AddRef(iface
);
472 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
474 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
475 ULONG ref
= InterlockedIncrement(&This
->ref
);
476 TRACE("(%p)->(%u)\n", This
, ref
);
480 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
482 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
483 ULONG ref
= InterlockedDecrement(&This
->ref
);
485 TRACE("(%p)->(%u)\n", This
, ref
);
488 struct element
*element
, *element2
;
489 IMalloc
*imalloc
= This
->imalloc
;
491 writeroutput_flush_stream(This
->output
);
492 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
495 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &This
->elements
, struct element
, entry
) {
496 list_remove(&element
->entry
);
497 free_element(This
, element
);
500 writer_free(This
, This
);
501 if (imalloc
) IMalloc_Release(imalloc
);
507 /*** IXmlWriter methods ***/
508 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
510 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
511 IXmlWriterOutput
*writeroutput
;
514 TRACE("(%p)->(%p)\n", This
, output
);
517 writeroutput_release_stream(This
->output
);
518 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
520 This
->bomwritten
= FALSE
;
521 This
->indent_level
= 0;
524 /* just reset current output */
526 This
->state
= XmlWriterState_Initial
;
530 /* now try IXmlWriterOutput, ISequentialStream, IStream */
531 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
533 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
534 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
536 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
537 writeroutput
, writeroutput
->lpVtbl
);
538 IUnknown_Release(writeroutput
);
543 if (hr
!= S_OK
|| !writeroutput
) {
544 /* create IXmlWriterOutput basing on supplied interface */
545 hr
= CreateXmlWriterOutputWithEncodingName(output
, This
->imalloc
, NULL
, &writeroutput
);
546 if (hr
!= S_OK
) return hr
;
547 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
550 if (This
->output
->encoding
== XmlEncoding_Unknown
)
551 This
->state
= XmlWriterState_InvalidEncoding
;
553 This
->state
= XmlWriterState_Ready
;
554 return writeroutput_query_for_stream(This
->output
);
557 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
559 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
561 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
563 if (!value
) return E_INVALIDARG
;
567 case XmlWriterProperty_Indent
:
568 *value
= This
->indent
;
570 case XmlWriterProperty_ByteOrderMark
:
573 case XmlWriterProperty_OmitXmlDeclaration
:
574 *value
= This
->omitxmldecl
;
576 case XmlWriterProperty_ConformanceLevel
:
577 *value
= This
->conformance
;
580 FIXME("Unimplemented property (%u)\n", property
);
587 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
589 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
591 TRACE("(%p)->(%s %lu)\n", This
, debugstr_writer_prop(property
), value
);
595 case XmlWriterProperty_Indent
:
596 This
->indent
= !!value
;
598 case XmlWriterProperty_ByteOrderMark
:
601 case XmlWriterProperty_OmitXmlDeclaration
:
602 This
->omitxmldecl
= !!value
;
605 FIXME("Unimplemented property (%u)\n", property
);
612 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*pReader
,
613 BOOL fWriteDefaultAttributes
)
615 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
617 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
622 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR ns_prefix
,
623 LPCWSTR local_name
, LPCWSTR ns_uri
, LPCWSTR value
)
625 static const WCHAR eqW
[] = {'=','"'};
626 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
628 TRACE("%p %s %s %s %s\n", This
, debugstr_w(ns_prefix
), debugstr_w(local_name
),
629 debugstr_w(ns_uri
), debugstr_w(value
));
633 case XmlWriterState_Initial
:
635 case XmlWriterState_Ready
:
636 case XmlWriterState_DocClosed
:
637 This
->state
= XmlWriterState_DocClosed
;
638 return WR_E_INVALIDACTION
;
639 case XmlWriterState_InvalidEncoding
:
640 return MX_E_ENCODING
;
645 if (ns_prefix
|| ns_uri
)
647 FIXME("namespaces are not supported.\n");
651 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
652 write_output_buffer(This
->output
, local_name
, -1);
653 write_output_buffer(This
->output
, eqW
, ARRAY_SIZE(eqW
));
654 write_output_buffer(This
->output
, value
, -1);
655 write_output_buffer(This
->output
, quoteW
, ARRAY_SIZE(quoteW
));
660 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
662 static const WCHAR cdataopenW
[] = {'<','!','[','C','D','A','T','A','['};
663 static const WCHAR cdatacloseW
[] = {']',']','>'};
664 write_output_buffer(output
, cdataopenW
, ARRAY_SIZE(cdataopenW
));
666 write_output_buffer(output
, data
, len
);
667 write_output_buffer(output
, cdatacloseW
, ARRAY_SIZE(cdatacloseW
));
670 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
672 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
675 TRACE("%p %s\n", This
, debugstr_w(data
));
679 case XmlWriterState_Initial
:
681 case XmlWriterState_ElemStarted
:
682 writer_close_starttag(This
);
684 case XmlWriterState_Ready
:
685 case XmlWriterState_DocClosed
:
686 This
->state
= XmlWriterState_DocClosed
;
687 return WR_E_INVALIDACTION
;
688 case XmlWriterState_InvalidEncoding
:
689 return MX_E_ENCODING
;
694 len
= data
? strlenW(data
) : 0;
696 write_node_indent(This
);
698 write_cdata_section(This
->output
, NULL
, 0);
700 static const WCHAR cdatacloseW
[] = {']',']','>',0};
702 const WCHAR
*str
= strstrW(data
, cdatacloseW
);
705 write_cdata_section(This
->output
, data
, str
- data
);
710 write_cdata_section(This
->output
, data
, len
);
719 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
721 static const WCHAR fmtW
[] = {'&','#','x','%','x',';',0};
722 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
725 TRACE("%p %#x\n", This
, ch
);
729 case XmlWriterState_Initial
:
731 case XmlWriterState_InvalidEncoding
:
732 return MX_E_ENCODING
;
733 case XmlWriterState_ElemStarted
:
734 writer_close_starttag(This
);
736 case XmlWriterState_DocClosed
:
737 return WR_E_INVALIDACTION
;
742 sprintfW(bufW
, fmtW
, ch
);
743 write_output_buffer(This
->output
, bufW
, -1);
748 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
750 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
752 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
756 case XmlWriterState_Initial
:
758 case XmlWriterState_InvalidEncoding
:
759 return MX_E_ENCODING
;
760 case XmlWriterState_DocClosed
:
761 return WR_E_INVALIDACTION
;
770 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
772 static const WCHAR copenW
[] = {'<','!','-','-'};
773 static const WCHAR ccloseW
[] = {'-','-','>'};
774 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
776 TRACE("%p %s\n", This
, debugstr_w(comment
));
780 case XmlWriterState_Initial
:
782 case XmlWriterState_InvalidEncoding
:
783 return MX_E_ENCODING
;
784 case XmlWriterState_ElemStarted
:
785 writer_close_starttag(This
);
787 case XmlWriterState_DocClosed
:
788 return WR_E_INVALIDACTION
;
793 write_node_indent(This
);
794 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
796 int len
= strlenW(comment
), i
;
798 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
801 for (i
= 0; i
< len
; i
++) {
802 write_output_buffer(This
->output
, comment
+ i
, 1);
803 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
804 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
808 write_output_buffer(This
->output
, comment
, len
);
810 if (len
&& comment
[len
-1] == '-')
811 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
813 write_output_buffer(This
->output
, ccloseW
, ARRAY_SIZE(ccloseW
));
818 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR pwszName
, LPCWSTR pwszPublicId
,
819 LPCWSTR pwszSystemId
, LPCWSTR pwszSubset
)
821 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
823 FIXME("%p %s %s %s %s\n", This
, wine_dbgstr_w(pwszName
), wine_dbgstr_w(pwszPublicId
),
824 wine_dbgstr_w(pwszSystemId
), wine_dbgstr_w(pwszSubset
));
829 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
830 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
832 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
834 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
835 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
839 case XmlWriterState_Initial
:
841 case XmlWriterState_InvalidEncoding
:
842 return MX_E_ENCODING
;
843 case XmlWriterState_ElemStarted
:
844 writer_close_starttag(This
);
846 case XmlWriterState_DocClosed
:
847 return WR_E_INVALIDACTION
;
852 write_encoding_bom(This
);
853 write_node_indent(This
);
854 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
855 write_output_qname(This
->output
, prefix
, local_name
);
859 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
860 write_output_buffer(This
->output
, value
, -1);
861 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
862 write_output_qname(This
->output
, prefix
, local_name
);
863 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
866 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
868 This
->state
= XmlWriterState_Content
;
873 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
875 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
881 case XmlWriterState_Initial
:
883 case XmlWriterState_Ready
:
884 case XmlWriterState_DocClosed
:
885 This
->state
= XmlWriterState_DocClosed
;
886 return WR_E_INVALIDACTION
;
887 case XmlWriterState_InvalidEncoding
:
888 return MX_E_ENCODING
;
893 /* empty element stack */
894 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
897 This
->state
= XmlWriterState_DocClosed
;
901 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
903 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
904 struct element
*element
;
910 case XmlWriterState_Initial
:
912 case XmlWriterState_Ready
:
913 case XmlWriterState_DocClosed
:
914 This
->state
= XmlWriterState_DocClosed
;
915 return WR_E_INVALIDACTION
;
916 case XmlWriterState_InvalidEncoding
:
917 return MX_E_ENCODING
;
922 element
= pop_element(This
);
924 return WR_E_INVALIDACTION
;
926 writer_dec_indent(This
);
928 if (This
->starttagopen
)
930 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
931 This
->starttagopen
= FALSE
;
934 /* write full end tag */
935 write_node_indent(This
);
936 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
937 write_output_buffer(This
->output
, element
->qname
, element
->len
);
938 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
944 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
946 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
948 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
952 case XmlWriterState_Initial
:
954 case XmlWriterState_InvalidEncoding
:
955 return MX_E_ENCODING
;
956 case XmlWriterState_DocClosed
:
957 return WR_E_INVALIDACTION
;
965 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
967 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
968 struct element
*element
;
974 case XmlWriterState_Initial
:
976 case XmlWriterState_Ready
:
977 case XmlWriterState_DocClosed
:
978 This
->state
= XmlWriterState_DocClosed
;
979 return WR_E_INVALIDACTION
;
980 case XmlWriterState_InvalidEncoding
:
981 return MX_E_ENCODING
;
986 element
= pop_element(This
);
988 return WR_E_INVALIDACTION
;
990 writer_close_starttag(This
);
991 writer_dec_indent(This
);
993 /* don't force full end tag to the next line */
994 if (This
->state
== XmlWriterState_ElemStarted
)
995 This
->state
= XmlWriterState_Content
;
997 write_node_indent(This
);
999 /* write full end tag */
1000 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1001 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1002 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1007 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1009 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1011 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1013 switch (This
->state
)
1015 case XmlWriterState_Initial
:
1016 return E_UNEXPECTED
;
1017 case XmlWriterState_Ready
:
1018 case XmlWriterState_DocClosed
:
1019 This
->state
= XmlWriterState_DocClosed
;
1020 return WR_E_INVALIDACTION
;
1021 case XmlWriterState_InvalidEncoding
:
1022 return MX_E_ENCODING
;
1030 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1032 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1034 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1036 switch (This
->state
)
1038 case XmlWriterState_Initial
:
1039 return E_UNEXPECTED
;
1040 case XmlWriterState_Ready
:
1041 case XmlWriterState_DocClosed
:
1042 This
->state
= XmlWriterState_DocClosed
;
1043 return WR_E_INVALIDACTION
;
1044 case XmlWriterState_InvalidEncoding
:
1045 return MX_E_ENCODING
;
1053 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*pReader
,
1054 BOOL fWriteDefaultAttributes
)
1056 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1058 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1063 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*pReader
,
1064 BOOL fWriteDefaultAttributes
)
1066 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1068 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1073 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1076 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1077 static const WCHAR xmlW
[] = {'x','m','l',0};
1078 static const WCHAR openpiW
[] = {'<','?'};
1080 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1082 switch (This
->state
)
1084 case XmlWriterState_Initial
:
1085 return E_UNEXPECTED
;
1086 case XmlWriterState_InvalidEncoding
:
1087 return MX_E_ENCODING
;
1088 case XmlWriterState_DocStarted
:
1089 if (!strcmpW(name
, xmlW
))
1090 return WR_E_INVALIDACTION
;
1092 case XmlWriterState_ElemStarted
:
1093 case XmlWriterState_DocClosed
:
1094 return WR_E_INVALIDACTION
;
1099 write_encoding_bom(This
);
1100 write_node_indent(This
);
1101 write_output_buffer(This
->output
, openpiW
, ARRAY_SIZE(openpiW
));
1102 write_output_buffer(This
->output
, name
, -1);
1103 write_output_buffer(This
->output
, spaceW
, ARRAY_SIZE(spaceW
));
1104 write_output_buffer(This
->output
, text
, -1);
1105 write_output_buffer(This
->output
, closepiW
, ARRAY_SIZE(closepiW
));
1107 if (!strcmpW(name
, xmlW
))
1108 This
->state
= XmlWriterState_PIDocStarted
;
1113 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1114 LPCWSTR pwszNamespaceUri
)
1116 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1118 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1120 switch (This
->state
)
1122 case XmlWriterState_Initial
:
1123 return E_UNEXPECTED
;
1124 case XmlWriterState_InvalidEncoding
:
1125 return MX_E_ENCODING
;
1126 case XmlWriterState_DocClosed
:
1127 return WR_E_INVALIDACTION
;
1135 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1137 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1139 TRACE("%p %s\n", This
, debugstr_w(data
));
1144 switch (This
->state
)
1146 case XmlWriterState_Initial
:
1147 return E_UNEXPECTED
;
1148 case XmlWriterState_Ready
:
1149 write_xmldecl(This
, XmlStandalone_Omit
);
1151 case XmlWriterState_DocStarted
:
1152 case XmlWriterState_PIDocStarted
:
1154 case XmlWriterState_InvalidEncoding
:
1155 return MX_E_ENCODING
;
1157 This
->state
= XmlWriterState_DocClosed
;
1158 return WR_E_INVALIDACTION
;
1161 write_output_buffer(This
->output
, data
, -1);
1165 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1167 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1169 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1171 switch (This
->state
)
1173 case XmlWriterState_Initial
:
1174 return E_UNEXPECTED
;
1175 case XmlWriterState_InvalidEncoding
:
1176 return MX_E_ENCODING
;
1177 case XmlWriterState_DocClosed
:
1178 return WR_E_INVALIDACTION
;
1186 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1188 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1190 TRACE("(%p)->(%d)\n", This
, standalone
);
1192 switch (This
->state
)
1194 case XmlWriterState_Initial
:
1195 return E_UNEXPECTED
;
1196 case XmlWriterState_PIDocStarted
:
1197 This
->state
= XmlWriterState_DocStarted
;
1199 case XmlWriterState_Ready
:
1201 case XmlWriterState_InvalidEncoding
:
1202 return MX_E_ENCODING
;
1204 This
->state
= XmlWriterState_DocClosed
;
1205 return WR_E_INVALIDACTION
;
1208 return write_xmldecl(This
, standalone
);
1211 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1213 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1214 struct element
*element
;
1216 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1219 return E_INVALIDARG
;
1221 switch (This
->state
)
1223 case XmlWriterState_Initial
:
1224 return E_UNEXPECTED
;
1225 case XmlWriterState_InvalidEncoding
:
1226 return MX_E_ENCODING
;
1227 case XmlWriterState_DocClosed
:
1228 return WR_E_INVALIDACTION
;
1233 /* close pending element */
1234 if (This
->starttagopen
)
1235 write_output_buffer(This
->output
, gtW
, ARRAY_SIZE(gtW
));
1237 element
= alloc_element(This
, prefix
, local_name
);
1239 return E_OUTOFMEMORY
;
1241 write_encoding_bom(This
);
1242 write_node_indent(This
);
1244 This
->state
= XmlWriterState_ElemStarted
;
1245 This
->starttagopen
= TRUE
;
1247 push_element(This
, element
);
1249 write_output_buffer(This
->output
, ltW
, ARRAY_SIZE(ltW
));
1250 write_output_qname(This
->output
, prefix
, local_name
);
1251 writer_inc_indent(This
);
1256 static void write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
)
1258 static const WCHAR ampW
[] = {'&','a','m','p',';'};
1259 static const WCHAR ltW
[] = {'&','l','t',';'};
1260 static const WCHAR gtW
[] = {'&','g','t',';'};
1267 write_output_buffer(writer
->output
, ltW
, ARRAY_SIZE(ltW
));
1270 write_output_buffer(writer
->output
, ampW
, ARRAY_SIZE(ampW
));
1273 write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
1276 write_output_buffer(writer
->output
, string
, 1);
1283 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1285 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1287 TRACE("%p %s\n", This
, debugstr_w(string
));
1292 switch (This
->state
)
1294 case XmlWriterState_Initial
:
1295 return E_UNEXPECTED
;
1296 case XmlWriterState_ElemStarted
:
1297 writer_close_starttag(This
);
1299 case XmlWriterState_Ready
:
1300 case XmlWriterState_DocClosed
:
1301 This
->state
= XmlWriterState_DocClosed
;
1302 return WR_E_INVALIDACTION
;
1303 case XmlWriterState_InvalidEncoding
:
1304 return MX_E_ENCODING
;
1309 write_escaped_string(This
, string
);
1313 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1315 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1317 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1322 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR pwszWhitespace
)
1324 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1326 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszWhitespace
));
1331 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1333 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1335 TRACE("%p\n", This
);
1337 return writeroutput_flush_stream(This
->output
);
1340 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1342 xmlwriter_QueryInterface
,
1345 xmlwriter_SetOutput
,
1346 xmlwriter_GetProperty
,
1347 xmlwriter_SetProperty
,
1348 xmlwriter_WriteAttributes
,
1349 xmlwriter_WriteAttributeString
,
1350 xmlwriter_WriteCData
,
1351 xmlwriter_WriteCharEntity
,
1352 xmlwriter_WriteChars
,
1353 xmlwriter_WriteComment
,
1354 xmlwriter_WriteDocType
,
1355 xmlwriter_WriteElementString
,
1356 xmlwriter_WriteEndDocument
,
1357 xmlwriter_WriteEndElement
,
1358 xmlwriter_WriteEntityRef
,
1359 xmlwriter_WriteFullEndElement
,
1360 xmlwriter_WriteName
,
1361 xmlwriter_WriteNmToken
,
1362 xmlwriter_WriteNode
,
1363 xmlwriter_WriteNodeShallow
,
1364 xmlwriter_WriteProcessingInstruction
,
1365 xmlwriter_WriteQualifiedName
,
1367 xmlwriter_WriteRawChars
,
1368 xmlwriter_WriteStartDocument
,
1369 xmlwriter_WriteStartElement
,
1370 xmlwriter_WriteString
,
1371 xmlwriter_WriteSurrogateCharEntity
,
1372 xmlwriter_WriteWhitespace
,
1376 /** IXmlWriterOutput **/
1377 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1379 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1381 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1383 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1384 IsEqualGUID(riid
, &IID_IUnknown
))
1390 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1392 return E_NOINTERFACE
;
1395 IUnknown_AddRef(iface
);
1400 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1402 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1403 ULONG ref
= InterlockedIncrement(&This
->ref
);
1404 TRACE("(%p)->(%d)\n", This
, ref
);
1408 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
1410 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1411 LONG ref
= InterlockedDecrement(&This
->ref
);
1413 TRACE("(%p)->(%d)\n", This
, ref
);
1417 IMalloc
*imalloc
= This
->imalloc
;
1418 if (This
->output
) IUnknown_Release(This
->output
);
1419 if (This
->stream
) ISequentialStream_Release(This
->stream
);
1420 free_output_buffer(This
);
1421 writeroutput_free(This
, This
->encoding_name
);
1422 writeroutput_free(This
, This
);
1423 if (imalloc
) IMalloc_Release(imalloc
);
1429 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
1431 xmlwriteroutput_QueryInterface
,
1432 xmlwriteroutput_AddRef
,
1433 xmlwriteroutput_Release
1436 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
1441 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
1444 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1446 writer
= heap_alloc(sizeof(*writer
));
1447 if(!writer
) return E_OUTOFMEMORY
;
1449 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1451 writer
->imalloc
= imalloc
;
1452 if (imalloc
) IMalloc_AddRef(imalloc
);
1453 writer
->output
= NULL
;
1454 writer
->indent_level
= 0;
1455 writer
->indent
= FALSE
;
1457 writer
->omitxmldecl
= FALSE
;
1458 writer
->conformance
= XmlConformanceLevel_Document
;
1459 writer
->state
= XmlWriterState_Initial
;
1460 writer
->bomwritten
= FALSE
;
1461 writer
->starttagopen
= FALSE
;
1462 list_init(&writer
->elements
);
1464 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
1465 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
1467 TRACE("returning iface %p, hr %#x\n", *obj
, hr
);
1472 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1473 const WCHAR
*encoding_name
, IXmlWriterOutput
**output
)
1475 xmlwriteroutput
*writeroutput
;
1481 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
1483 writeroutput
= heap_alloc(sizeof(*writeroutput
));
1485 return E_OUTOFMEMORY
;
1487 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
1488 writeroutput
->ref
= 1;
1489 writeroutput
->imalloc
= imalloc
;
1491 IMalloc_AddRef(imalloc
);
1492 writeroutput
->encoding
= encoding
;
1493 writeroutput
->stream
= NULL
;
1494 hr
= init_output_buffer(writeroutput
);
1496 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1500 if (encoding_name
) {
1501 unsigned int size
= (strlenW(encoding_name
) + 1) * sizeof(WCHAR
);
1502 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
1503 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
1506 writeroutput
->encoding_name
= NULL
;
1508 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1510 *output
= &writeroutput
->IXmlWriterOutput_iface
;
1512 TRACE("returning iface %p\n", *output
);
1517 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
,
1520 IXmlWriterOutput
**output
)
1522 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
1523 xml_encoding xml_enc
;
1525 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), output
);
1527 if (!stream
|| !output
) return E_INVALIDARG
;
1529 xml_enc
= parse_encoding_name(encoding
? encoding
: utf8W
, -1);
1530 return create_writer_output(stream
, imalloc
, xml_enc
, encoding
, output
);
1533 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
,
1536 IXmlWriterOutput
**output
)
1538 xml_encoding xml_enc
;
1540 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, output
);
1542 if (!stream
|| !output
) return E_INVALIDARG
;
1544 xml_enc
= get_encoding_from_codepage(codepage
);
1545 return create_writer_output(stream
, imalloc
, xml_enc
, NULL
, output
);