2 * MXWriter implementation
4 * Copyright 2011 Nikolay Sivov for CodeWeavers
5 * Copyright 2011 Thomas Mullaly
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
27 # include <libxml/parser.h>
36 #include "wine/debug.h"
38 #include "msxml_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml
);
42 static const WCHAR utf16W
[] = {'U','T','F','-','1','6',0};
43 static const WCHAR emptyW
[] = {0};
54 OutputBuffer_Native
= 0x001,
55 OutputBuffer_Encoded
= 0x010,
56 OutputBuffer_Both
= 0x100
62 MXWriter_DisableEscaping
,
72 unsigned int allocated
;
79 encoded_buffer encoded
;
86 IMXWriter IMXWriter_iface
;
87 ISAXContentHandler ISAXContentHandler_iface
;
90 MSXML_VERSION class_version
;
92 VARIANT_BOOL props
[MXWriter_LastProp
];
97 BSTR encoding
; /* exact property value */
100 /* contains a pending (or not closed yet) element name or NULL if
101 we don't have to close */
107 output_buffer
*buffer
;
110 static xml_encoding
parse_encoding_name(const WCHAR
*encoding
)
112 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
113 if (!strcmpiW(encoding
, utf8W
)) return XmlEncoding_UTF8
;
114 if (!strcmpiW(encoding
, utf16W
)) return XmlEncoding_UTF16
;
115 return XmlEncoding_Unknown
;
118 static HRESULT
init_encoded_buffer(encoded_buffer
*buffer
)
120 const int initial_len
= 0x2000;
121 buffer
->data
= heap_alloc(initial_len
);
122 if (!buffer
->data
) return E_OUTOFMEMORY
;
124 memset(buffer
->data
, 0, 4);
125 buffer
->allocated
= initial_len
;
131 static void free_encoded_buffer(encoded_buffer
*buffer
)
133 heap_free(buffer
->data
);
136 static HRESULT
get_code_page(xml_encoding encoding
, UINT
*cp
)
140 case XmlEncoding_UTF8
:
143 case XmlEncoding_UTF16
:
147 FIXME("unsupported encoding %d\n", encoding
);
154 static HRESULT
alloc_output_buffer(xml_encoding encoding
, output_buffer
**buffer
)
159 ret
= heap_alloc(sizeof(*ret
));
160 if (!ret
) return E_OUTOFMEMORY
;
162 hr
= get_code_page(encoding
, &ret
->code_page
);
168 hr
= init_encoded_buffer(&ret
->utf16
);
174 if (ret
->code_page
== CP_UTF8
) {
175 hr
= init_encoded_buffer(&ret
->encoded
);
177 free_encoded_buffer(&ret
->utf16
);
183 memset(&ret
->encoded
, 0, sizeof(ret
->encoded
));
190 static void free_output_buffer(output_buffer
*buffer
)
192 free_encoded_buffer(&buffer
->encoded
);
193 free_encoded_buffer(&buffer
->utf16
);
197 static void grow_buffer(encoded_buffer
*buffer
, int length
)
199 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
200 if (buffer
->allocated
< buffer
->written
+ length
+ 4)
202 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
203 buffer
->data
= heap_realloc(buffer
->data
, grown_size
);
204 buffer
->allocated
= grown_size
;
208 static HRESULT
write_output_buffer_mode(output_buffer
*buffer
, output_mode mode
, const WCHAR
*data
, int len
)
213 if (mode
& (OutputBuffer_Encoded
| OutputBuffer_Both
)) {
214 if (buffer
->code_page
== CP_UTF8
)
216 length
= WideCharToMultiByte(buffer
->code_page
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
217 grow_buffer(&buffer
->encoded
, length
);
218 ptr
= buffer
->encoded
.data
+ buffer
->encoded
.written
;
219 length
= WideCharToMultiByte(buffer
->code_page
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
220 buffer
->encoded
.written
+= len
== -1 ? length
-1 : length
;
224 if (mode
& (OutputBuffer_Native
| OutputBuffer_Both
)) {
225 /* WCHAR data just copied */
226 length
= len
== -1 ? strlenW(data
) : len
;
229 length
*= sizeof(WCHAR
);
231 grow_buffer(&buffer
->utf16
, length
);
232 ptr
= buffer
->utf16
.data
+ buffer
->utf16
.written
;
234 memcpy(ptr
, data
, length
);
235 buffer
->utf16
.written
+= length
;
237 /* null termination */
238 memset(ptr
, 0, sizeof(WCHAR
));
245 static HRESULT
write_output_buffer(output_buffer
*buffer
, const WCHAR
*data
, int len
)
247 return write_output_buffer_mode(buffer
, OutputBuffer_Both
, data
, len
);
250 /* frees buffer data, reallocates with a default lengths */
251 static void close_output_buffer(mxwriter
*This
)
253 heap_free(This
->buffer
->utf16
.data
);
254 heap_free(This
->buffer
->encoded
.data
);
255 init_encoded_buffer(&This
->buffer
->utf16
);
256 init_encoded_buffer(&This
->buffer
->encoded
);
257 get_code_page(This
->xml_enc
, &This
->buffer
->code_page
);
260 /* escapes special characters like:
266 static WCHAR
*get_escaped_string(const WCHAR
*str
, int *len
)
268 static const WCHAR ltW
[] = {'&','l','t',';'};
269 static const WCHAR ampW
[] = {'&','a','m','p',';'};
270 static const WCHAR quotW
[] = {'&','q','u','o','t',';'};
271 static const WCHAR gtW
[] = {'&','g','t',';'};
273 const int default_alloc
= 100;
274 const int grow_thresh
= 10;
275 int p
= *len
, conv_len
;
278 /* default buffer size to something if length is unknown */
279 conv_len
= *len
== -1 ? default_alloc
: max(2**len
, default_alloc
);
280 ptr
= ret
= heap_alloc(conv_len
*sizeof(WCHAR
));
284 if (ptr
- ret
> conv_len
- grow_thresh
)
286 int written
= ptr
- ret
;
288 ptr
= ret
= heap_realloc(ret
, conv_len
*sizeof(WCHAR
));
295 memcpy(ptr
, ltW
, sizeof(ltW
));
296 ptr
+= sizeof(ltW
)/sizeof(WCHAR
);
299 memcpy(ptr
, ampW
, sizeof(ampW
));
300 ptr
+= sizeof(ampW
)/sizeof(WCHAR
);
303 memcpy(ptr
, quotW
, sizeof(quotW
));
304 ptr
+= sizeof(quotW
)/sizeof(WCHAR
);
307 memcpy(ptr
, gtW
, sizeof(gtW
));
308 ptr
+= sizeof(gtW
)/sizeof(WCHAR
);
319 if (*len
!= -1) *len
= ptr
-ret
;
325 static void write_prolog_buffer(const mxwriter
*This
)
327 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"'};
328 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
329 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
330 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
331 static const WCHAR noW
[] = {'n','o','\"','?','>'};
332 static const WCHAR quotW
[] = {'\"'};
333 static const WCHAR crlfW
[] = {'\r','\n'};
336 write_output_buffer(This
->buffer
, versionW
, sizeof(versionW
)/sizeof(WCHAR
));
337 write_output_buffer(This
->buffer
, This
->version
, -1);
338 write_output_buffer(This
->buffer
, quotW
, 1);
341 write_output_buffer(This
->buffer
, encodingW
, sizeof(encodingW
)/sizeof(WCHAR
));
343 /* always write UTF-16 to WCHAR buffer */
344 write_output_buffer_mode(This
->buffer
, OutputBuffer_Native
, utf16W
, sizeof(utf16W
)/sizeof(WCHAR
) - 1);
345 write_output_buffer_mode(This
->buffer
, OutputBuffer_Encoded
, This
->encoding
, -1);
346 write_output_buffer(This
->buffer
, quotW
, 1);
349 write_output_buffer(This
->buffer
, standaloneW
, sizeof(standaloneW
)/sizeof(WCHAR
));
350 if (This
->props
[MXWriter_Standalone
] == VARIANT_TRUE
)
351 write_output_buffer(This
->buffer
, yesW
, sizeof(yesW
)/sizeof(WCHAR
));
353 write_output_buffer(This
->buffer
, noW
, sizeof(noW
)/sizeof(WCHAR
));
355 write_output_buffer(This
->buffer
, crlfW
, sizeof(crlfW
)/sizeof(WCHAR
));
358 /* Attempts to the write data from the mxwriter's buffer to
359 * the destination stream (if there is one).
361 static HRESULT
write_data_to_stream(mxwriter
*This
)
363 encoded_buffer
*buffer
;
370 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
371 * 'conv' buffer when UTF8 encoding is used.
373 if (This
->xml_enc
!= XmlEncoding_UTF16
)
374 buffer
= &This
->buffer
->encoded
;
376 buffer
= &This
->buffer
->utf16
;
378 if (This
->dest_written
> buffer
->written
) {
379 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This
->dest_written
, buffer
->written
);
381 } else if (This
->dest_written
== buffer
->written
&& This
->xml_enc
!= XmlEncoding_UTF8
)
382 /* Windows seems to make an empty write call when the encoding is UTF-8 and
383 * all the data has been written to the stream. It doesn't seem make this call
384 * for any other encodings.
388 /* Write the current content from the output buffer into 'dest'.
389 * TODO: Check what Windows does if the IStream doesn't write all of
390 * the data we give it at once.
392 hr
= IStream_Write(This
->dest
, buffer
->data
+This
->dest_written
,
393 buffer
->written
-This
->dest_written
, &written
);
395 WARN("Failed to write data to IStream (0x%08x)\n", hr
);
399 This
->dest_written
+= written
;
403 /* Newly added element start tag left unclosed cause for empty elements
404 we have to close it differently. */
405 static void close_element_starttag(const mxwriter
*This
)
407 static const WCHAR gtW
[] = {'>'};
408 if (!This
->element
) return;
409 write_output_buffer(This
->buffer
, gtW
, 1);
412 static void set_element_name(mxwriter
*This
, const WCHAR
*name
, int len
)
414 SysFreeString(This
->element
);
415 This
->element
= name
? SysAllocStringLen(name
, len
) : NULL
;
418 static inline HRESULT
flush_output_buffer(mxwriter
*This
)
420 close_element_starttag(This
);
421 set_element_name(This
, NULL
, 0);
422 return write_data_to_stream(This
);
425 /* Resets the mxwriter's output buffer by closing it, then creating a new
426 * output buffer using the given encoding.
428 static inline void reset_output_buffer(mxwriter
*This
)
430 close_output_buffer(This
);
431 This
->dest_written
= 0;
434 static HRESULT
writer_set_property(mxwriter
*writer
, mxwriter_prop property
, VARIANT_BOOL value
)
436 writer
->props
[property
] = value
;
437 writer
->prop_changed
= TRUE
;
441 static HRESULT
writer_get_property(const mxwriter
*writer
, mxwriter_prop property
, VARIANT_BOOL
*value
)
443 if (!value
) return E_POINTER
;
444 *value
= writer
->props
[property
];
448 static inline mxwriter
*impl_from_IMXWriter(IMXWriter
*iface
)
450 return CONTAINING_RECORD(iface
, mxwriter
, IMXWriter_iface
);
453 static inline mxwriter
*impl_from_ISAXContentHandler(ISAXContentHandler
*iface
)
455 return CONTAINING_RECORD(iface
, mxwriter
, ISAXContentHandler_iface
);
458 static HRESULT WINAPI
mxwriter_QueryInterface(IMXWriter
*iface
, REFIID riid
, void **obj
)
460 mxwriter
*This
= impl_from_IMXWriter( iface
);
462 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
466 if ( IsEqualGUID( riid
, &IID_IMXWriter
) ||
467 IsEqualGUID( riid
, &IID_IDispatch
) ||
468 IsEqualGUID( riid
, &IID_IUnknown
) )
470 *obj
= &This
->IMXWriter_iface
;
472 else if ( IsEqualGUID( riid
, &IID_ISAXContentHandler
) )
474 *obj
= &This
->ISAXContentHandler_iface
;
476 else if (dispex_query_interface(&This
->dispex
, riid
, obj
))
478 return *obj
? S_OK
: E_NOINTERFACE
;
482 ERR("interface %s not implemented\n", debugstr_guid(riid
));
484 return E_NOINTERFACE
;
487 IMXWriter_AddRef(iface
);
491 static ULONG WINAPI
mxwriter_AddRef(IMXWriter
*iface
)
493 mxwriter
*This
= impl_from_IMXWriter( iface
);
494 LONG ref
= InterlockedIncrement(&This
->ref
);
496 TRACE("(%p)->(%d)\n", This
, ref
);
501 static ULONG WINAPI
mxwriter_Release(IMXWriter
*iface
)
503 mxwriter
*This
= impl_from_IMXWriter( iface
);
504 ULONG ref
= InterlockedDecrement(&This
->ref
);
506 TRACE("(%p)->(%d)\n", This
, ref
);
510 /* Windows flushes the buffer when the interface is destroyed. */
511 flush_output_buffer(This
);
512 free_output_buffer(This
->buffer
);
514 if (This
->dest
) IStream_Release(This
->dest
);
515 SysFreeString(This
->version
);
516 SysFreeString(This
->encoding
);
518 SysFreeString(This
->element
);
519 release_dispex(&This
->dispex
);
526 static HRESULT WINAPI
mxwriter_GetTypeInfoCount(IMXWriter
*iface
, UINT
* pctinfo
)
528 mxwriter
*This
= impl_from_IMXWriter( iface
);
529 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
532 static HRESULT WINAPI
mxwriter_GetTypeInfo(
534 UINT iTInfo
, LCID lcid
,
535 ITypeInfo
** ppTInfo
)
537 mxwriter
*This
= impl_from_IMXWriter( iface
);
538 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
539 iTInfo
, lcid
, ppTInfo
);
542 static HRESULT WINAPI
mxwriter_GetIDsOfNames(
544 REFIID riid
, LPOLESTR
* rgszNames
,
545 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
547 mxwriter
*This
= impl_from_IMXWriter( iface
);
548 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
549 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
552 static HRESULT WINAPI
mxwriter_Invoke(
554 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
555 WORD wFlags
, DISPPARAMS
* pDispParams
, VARIANT
* pVarResult
,
556 EXCEPINFO
* pExcepInfo
, UINT
* puArgErr
)
558 mxwriter
*This
= impl_from_IMXWriter( iface
);
559 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
560 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
563 static HRESULT WINAPI
mxwriter_put_output(IMXWriter
*iface
, VARIANT dest
)
565 mxwriter
*This
= impl_from_IMXWriter( iface
);
568 TRACE("(%p)->(%s)\n", This
, debugstr_variant(&dest
));
570 hr
= flush_output_buffer(This
);
578 if (This
->dest
) IStream_Release(This
->dest
);
580 reset_output_buffer(This
);
587 hr
= IUnknown_QueryInterface(V_UNKNOWN(&dest
), &IID_IStream
, (void**)&stream
);
590 /* Recreate the output buffer to make sure it's using the correct encoding. */
591 reset_output_buffer(This
);
593 if (This
->dest
) IStream_Release(This
->dest
);
598 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
602 FIXME("unhandled destination type %s\n", debugstr_variant(&dest
));
609 static HRESULT WINAPI
mxwriter_get_output(IMXWriter
*iface
, VARIANT
*dest
)
611 mxwriter
*This
= impl_from_IMXWriter( iface
);
613 TRACE("(%p)->(%p)\n", This
, dest
);
617 HRESULT hr
= flush_output_buffer(This
);
621 V_VT(dest
) = VT_BSTR
;
622 V_BSTR(dest
) = SysAllocString((WCHAR
*)This
->buffer
->utf16
.data
);
627 FIXME("not implemented when stream is set up\n");
632 static HRESULT WINAPI
mxwriter_put_encoding(IMXWriter
*iface
, BSTR encoding
)
634 mxwriter
*This
= impl_from_IMXWriter( iface
);
638 TRACE("(%p)->(%s)\n", This
, debugstr_w(encoding
));
640 enc
= parse_encoding_name(encoding
);
641 if (enc
== XmlEncoding_Unknown
)
643 FIXME("unsupported encoding %s\n", debugstr_w(encoding
));
647 hr
= flush_output_buffer(This
);
651 SysReAllocString(&This
->encoding
, encoding
);
654 TRACE("got encoding %d\n", This
->xml_enc
);
655 reset_output_buffer(This
);
659 static HRESULT WINAPI
mxwriter_get_encoding(IMXWriter
*iface
, BSTR
*encoding
)
661 mxwriter
*This
= impl_from_IMXWriter( iface
);
663 TRACE("(%p)->(%p)\n", This
, encoding
);
665 if (!encoding
) return E_POINTER
;
667 *encoding
= SysAllocString(This
->encoding
);
668 if (!*encoding
) return E_OUTOFMEMORY
;
673 static HRESULT WINAPI
mxwriter_put_byteOrderMark(IMXWriter
*iface
, VARIANT_BOOL value
)
675 mxwriter
*This
= impl_from_IMXWriter( iface
);
677 TRACE("(%p)->(%d)\n", This
, value
);
678 return writer_set_property(This
, MXWriter_BOM
, value
);
681 static HRESULT WINAPI
mxwriter_get_byteOrderMark(IMXWriter
*iface
, VARIANT_BOOL
*value
)
683 mxwriter
*This
= impl_from_IMXWriter( iface
);
685 TRACE("(%p)->(%p)\n", This
, value
);
686 return writer_get_property(This
, MXWriter_BOM
, value
);
689 static HRESULT WINAPI
mxwriter_put_indent(IMXWriter
*iface
, VARIANT_BOOL value
)
691 mxwriter
*This
= impl_from_IMXWriter( iface
);
693 TRACE("(%p)->(%d)\n", This
, value
);
694 return writer_set_property(This
, MXWriter_Indent
, value
);
697 static HRESULT WINAPI
mxwriter_get_indent(IMXWriter
*iface
, VARIANT_BOOL
*value
)
699 mxwriter
*This
= impl_from_IMXWriter( iface
);
701 TRACE("(%p)->(%p)\n", This
, value
);
702 return writer_get_property(This
, MXWriter_Indent
, value
);
705 static HRESULT WINAPI
mxwriter_put_standalone(IMXWriter
*iface
, VARIANT_BOOL value
)
707 mxwriter
*This
= impl_from_IMXWriter( iface
);
709 TRACE("(%p)->(%d)\n", This
, value
);
710 return writer_set_property(This
, MXWriter_Standalone
, value
);
713 static HRESULT WINAPI
mxwriter_get_standalone(IMXWriter
*iface
, VARIANT_BOOL
*value
)
715 mxwriter
*This
= impl_from_IMXWriter( iface
);
717 TRACE("(%p)->(%p)\n", This
, value
);
718 return writer_get_property(This
, MXWriter_Standalone
, value
);
721 static HRESULT WINAPI
mxwriter_put_omitXMLDeclaration(IMXWriter
*iface
, VARIANT_BOOL value
)
723 mxwriter
*This
= impl_from_IMXWriter( iface
);
725 TRACE("(%p)->(%d)\n", This
, value
);
726 return writer_set_property(This
, MXWriter_OmitXmlDecl
, value
);
729 static HRESULT WINAPI
mxwriter_get_omitXMLDeclaration(IMXWriter
*iface
, VARIANT_BOOL
*value
)
731 mxwriter
*This
= impl_from_IMXWriter( iface
);
733 TRACE("(%p)->(%p)\n", This
, value
);
734 return writer_get_property(This
, MXWriter_OmitXmlDecl
, value
);
737 static HRESULT WINAPI
mxwriter_put_version(IMXWriter
*iface
, BSTR version
)
739 mxwriter
*This
= impl_from_IMXWriter( iface
);
741 TRACE("(%p)->(%s)\n", This
, debugstr_w(version
));
743 if (!version
) return E_INVALIDARG
;
745 SysFreeString(This
->version
);
746 This
->version
= SysAllocString(version
);
751 static HRESULT WINAPI
mxwriter_get_version(IMXWriter
*iface
, BSTR
*version
)
753 mxwriter
*This
= impl_from_IMXWriter( iface
);
755 TRACE("(%p)->(%p)\n", This
, version
);
757 if (!version
) return E_POINTER
;
759 return return_bstr(This
->version
, version
);
762 static HRESULT WINAPI
mxwriter_put_disableOutputEscaping(IMXWriter
*iface
, VARIANT_BOOL value
)
764 mxwriter
*This
= impl_from_IMXWriter( iface
);
766 TRACE("(%p)->(%d)\n", This
, value
);
767 return writer_set_property(This
, MXWriter_DisableEscaping
, value
);
770 static HRESULT WINAPI
mxwriter_get_disableOutputEscaping(IMXWriter
*iface
, VARIANT_BOOL
*value
)
772 mxwriter
*This
= impl_from_IMXWriter( iface
);
774 TRACE("(%p)->(%p)\n", This
, value
);
775 return writer_get_property(This
, MXWriter_DisableEscaping
, value
);
778 static HRESULT WINAPI
mxwriter_flush(IMXWriter
*iface
)
780 mxwriter
*This
= impl_from_IMXWriter( iface
);
781 TRACE("(%p)\n", This
);
782 return flush_output_buffer(This
);
785 static const struct IMXWriterVtbl MXWriterVtbl
=
787 mxwriter_QueryInterface
,
790 mxwriter_GetTypeInfoCount
,
791 mxwriter_GetTypeInfo
,
792 mxwriter_GetIDsOfNames
,
796 mxwriter_put_encoding
,
797 mxwriter_get_encoding
,
798 mxwriter_put_byteOrderMark
,
799 mxwriter_get_byteOrderMark
,
802 mxwriter_put_standalone
,
803 mxwriter_get_standalone
,
804 mxwriter_put_omitXMLDeclaration
,
805 mxwriter_get_omitXMLDeclaration
,
806 mxwriter_put_version
,
807 mxwriter_get_version
,
808 mxwriter_put_disableOutputEscaping
,
809 mxwriter_get_disableOutputEscaping
,
813 /*** ISAXContentHandler ***/
814 static HRESULT WINAPI
mxwriter_saxcontent_QueryInterface(
815 ISAXContentHandler
*iface
,
819 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
820 return IMXWriter_QueryInterface(&This
->IMXWriter_iface
, riid
, obj
);
823 static ULONG WINAPI
mxwriter_saxcontent_AddRef(ISAXContentHandler
*iface
)
825 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
826 return IMXWriter_AddRef(&This
->IMXWriter_iface
);
829 static ULONG WINAPI
mxwriter_saxcontent_Release(ISAXContentHandler
*iface
)
831 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
832 return IMXWriter_Release(&This
->IMXWriter_iface
);
835 static HRESULT WINAPI
mxwriter_saxcontent_putDocumentLocator(
836 ISAXContentHandler
*iface
,
837 ISAXLocator
*locator
)
839 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
840 FIXME("(%p)->(%p)\n", This
, locator
);
844 static HRESULT WINAPI
mxwriter_saxcontent_startDocument(ISAXContentHandler
*iface
)
846 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
848 TRACE("(%p)\n", This
);
850 /* If properties have been changed since the last "endDocument" call
851 * we need to reset the output buffer. If we don't the output buffer
852 * could end up with multiple XML documents in it, plus this seems to
853 * be how Windows works.
855 if (This
->prop_changed
) {
856 reset_output_buffer(This
);
857 This
->prop_changed
= FALSE
;
860 if (This
->props
[MXWriter_OmitXmlDecl
] == VARIANT_TRUE
) return S_OK
;
862 write_prolog_buffer(This
);
864 if (This
->dest
&& This
->xml_enc
== XmlEncoding_UTF16
) {
865 static const char utf16BOM
[] = {0xff,0xfe};
867 if (This
->props
[MXWriter_BOM
] == VARIANT_TRUE
)
868 /* Windows passes a NULL pointer as the pcbWritten parameter and
869 * ignores any error codes returned from this Write call.
871 IStream_Write(This
->dest
, utf16BOM
, sizeof(utf16BOM
), NULL
);
877 static HRESULT WINAPI
mxwriter_saxcontent_endDocument(ISAXContentHandler
*iface
)
879 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
880 TRACE("(%p)\n", This
);
881 This
->prop_changed
= FALSE
;
882 return flush_output_buffer(This
);
885 static HRESULT WINAPI
mxwriter_saxcontent_startPrefixMapping(
886 ISAXContentHandler
*iface
,
892 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
893 FIXME("(%p)->(%s %s)\n", This
, debugstr_wn(prefix
, nprefix
), debugstr_wn(uri
, nuri
));
897 static HRESULT WINAPI
mxwriter_saxcontent_endPrefixMapping(
898 ISAXContentHandler
*iface
,
902 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
903 FIXME("(%p)->(%s)\n", This
, debugstr_wn(prefix
, nprefix
));
907 static HRESULT WINAPI
mxwriter_saxcontent_startElement(
908 ISAXContentHandler
*iface
,
909 const WCHAR
*namespaceUri
,
911 const WCHAR
*local_name
,
915 ISAXAttributes
*attr
)
917 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
918 static const WCHAR ltW
[] = {'<'};
920 TRACE("(%p)->(%s %s %s %p)\n", This
, debugstr_wn(namespaceUri
, nnamespaceUri
),
921 debugstr_wn(local_name
, nlocal_name
), debugstr_wn(QName
, nQName
), attr
);
923 if ((!namespaceUri
|| !local_name
|| !QName
) && This
->class_version
!= MSXML6
)
926 close_element_starttag(This
);
927 set_element_name(This
, QName
? QName
: emptyW
,
930 write_output_buffer(This
->buffer
, ltW
, 1);
931 write_output_buffer(This
->buffer
, QName
, nQName
);
939 hr
= ISAXAttributes_getLength(attr
, &length
);
940 if (FAILED(hr
)) return hr
;
942 for (i
= 0; i
< length
; i
++)
944 static const WCHAR spaceW
[] = {' '};
945 static const WCHAR eqqW
[] = {'=','\"'};
946 static const WCHAR quotW
[] = {'\"'};
951 hr
= ISAXAttributes_getQName(attr
, i
, &str
, &len
);
952 if (FAILED(hr
)) return hr
;
954 /* space separator in front of every attribute */
955 write_output_buffer(This
->buffer
, spaceW
, 1);
956 write_output_buffer(This
->buffer
, str
, len
);
958 write_output_buffer(This
->buffer
, eqqW
, 2);
961 hr
= ISAXAttributes_getValue(attr
, i
, &str
, &len
);
962 if (FAILED(hr
)) return hr
;
964 escaped
= get_escaped_string(str
, &len
);
965 write_output_buffer(This
->buffer
, escaped
, len
);
968 write_output_buffer(This
->buffer
, quotW
, 1);
975 static HRESULT WINAPI
mxwriter_saxcontent_endElement(
976 ISAXContentHandler
*iface
,
977 const WCHAR
*namespaceUri
,
979 const WCHAR
* local_name
,
984 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
986 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This
, debugstr_wn(namespaceUri
, nnamespaceUri
), nnamespaceUri
,
987 debugstr_wn(local_name
, nlocal_name
), nlocal_name
, debugstr_wn(QName
, nQName
), nQName
);
989 if ((!namespaceUri
|| !local_name
|| !QName
) && This
->class_version
!= MSXML6
)
992 if (This
->element
&& QName
&& !strncmpW(This
->element
, QName
, nQName
))
994 static const WCHAR closeW
[] = {'/','>'};
996 write_output_buffer(This
->buffer
, closeW
, 2);
1000 static const WCHAR closetagW
[] = {'<','/'};
1001 static const WCHAR gtW
[] = {'>'};
1003 write_output_buffer(This
->buffer
, closetagW
, 2);
1004 write_output_buffer(This
->buffer
, QName
, nQName
);
1005 write_output_buffer(This
->buffer
, gtW
, 1);
1008 set_element_name(This
, NULL
, 0);
1013 static HRESULT WINAPI
mxwriter_saxcontent_characters(
1014 ISAXContentHandler
*iface
,
1018 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
1020 TRACE("(%p)->(%s:%d)\n", This
, debugstr_wn(chars
, nchars
), nchars
);
1022 if (!chars
) return E_INVALIDARG
;
1024 close_element_starttag(This
);
1025 set_element_name(This
, NULL
, 0);
1028 write_output_buffer(This
->buffer
, chars
, nchars
);
1033 static HRESULT WINAPI
mxwriter_saxcontent_ignorableWhitespace(
1034 ISAXContentHandler
*iface
,
1038 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
1039 FIXME("(%p)->(%s)\n", This
, debugstr_wn(chars
, nchars
));
1043 static HRESULT WINAPI
mxwriter_saxcontent_processingInstruction(
1044 ISAXContentHandler
*iface
,
1045 const WCHAR
*target
,
1050 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
1051 FIXME("(%p)->(%s %s)\n", This
, debugstr_wn(target
, ntarget
), debugstr_wn(data
, ndata
));
1055 static HRESULT WINAPI
mxwriter_saxcontent_skippedEntity(
1056 ISAXContentHandler
*iface
,
1060 mxwriter
*This
= impl_from_ISAXContentHandler( iface
);
1061 FIXME("(%p)->(%s)\n", This
, debugstr_wn(name
, nname
));
1065 static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl
=
1067 mxwriter_saxcontent_QueryInterface
,
1068 mxwriter_saxcontent_AddRef
,
1069 mxwriter_saxcontent_Release
,
1070 mxwriter_saxcontent_putDocumentLocator
,
1071 mxwriter_saxcontent_startDocument
,
1072 mxwriter_saxcontent_endDocument
,
1073 mxwriter_saxcontent_startPrefixMapping
,
1074 mxwriter_saxcontent_endPrefixMapping
,
1075 mxwriter_saxcontent_startElement
,
1076 mxwriter_saxcontent_endElement
,
1077 mxwriter_saxcontent_characters
,
1078 mxwriter_saxcontent_ignorableWhitespace
,
1079 mxwriter_saxcontent_processingInstruction
,
1080 mxwriter_saxcontent_skippedEntity
1083 static const tid_t mxwriter_iface_tids
[] = {
1088 static dispex_static_data_t mxwriter_dispex
= {
1095 HRESULT
MXWriter_create(MSXML_VERSION version
, IUnknown
*outer
, void **ppObj
)
1097 static const WCHAR version10W
[] = {'1','.','0',0};
1101 TRACE("(%p, %p)\n", outer
, ppObj
);
1103 if (outer
) FIXME("support aggregation, outer\n");
1105 This
= heap_alloc( sizeof (*This
) );
1107 return E_OUTOFMEMORY
;
1109 This
->IMXWriter_iface
.lpVtbl
= &MXWriterVtbl
;
1110 This
->ISAXContentHandler_iface
.lpVtbl
= &mxwriter_saxcontent_vtbl
;
1112 This
->class_version
= version
;
1114 This
->props
[MXWriter_BOM
] = VARIANT_TRUE
;
1115 This
->props
[MXWriter_DisableEscaping
] = VARIANT_FALSE
;
1116 This
->props
[MXWriter_Indent
] = VARIANT_FALSE
;
1117 This
->props
[MXWriter_OmitXmlDecl
] = VARIANT_FALSE
;
1118 This
->props
[MXWriter_Standalone
] = VARIANT_FALSE
;
1119 This
->prop_changed
= FALSE
;
1120 This
->encoding
= SysAllocString(utf16W
);
1121 This
->version
= SysAllocString(version10W
);
1122 This
->xml_enc
= XmlEncoding_UTF16
;
1124 This
->element
= NULL
;
1127 This
->dest_written
= 0;
1129 hr
= alloc_output_buffer(This
->xml_enc
, &This
->buffer
);
1131 SysFreeString(This
->encoding
);
1132 SysFreeString(This
->version
);
1137 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IMXWriter_iface
, &mxwriter_dispex
);
1139 *ppObj
= &This
->IMXWriter_iface
;
1141 TRACE("returning iface %p\n", *ppObj
);