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};
44 static const WCHAR spaceW[] = {' '};
45 static const WCHAR quotW[] = {'\"'};
56 OutputBuffer_Native = 0x001,
57 OutputBuffer_Encoded = 0x010,
58 OutputBuffer_Both = 0x100
64 MXWriter_DisableEscaping,
80 unsigned int allocated;
87 encoded_buffer encoded;
94 IMXWriter IMXWriter_iface;
95 ISAXContentHandler ISAXContentHandler_iface;
96 ISAXLexicalHandler ISAXLexicalHandler_iface;
99 MSXML_VERSION class_version;
101 VARIANT_BOOL props[MXWriter_LastProp];
107 BSTR encoding; /* exact property value */
108 xml_encoding xml_enc;
110 /* contains a pending (or not closed yet) element name or NULL if
111 we don't have to close */
117 output_buffer *buffer;
120 static xml_encoding parse_encoding_name(const WCHAR *encoding)
122 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
123 if (!strcmpiW(encoding, utf8W)) return XmlEncoding_UTF8;
124 if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
125 return XmlEncoding_Unknown;
128 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
130 const int initial_len = 0x2000;
131 buffer->data = heap_alloc(initial_len);
132 if (!buffer->data) return E_OUTOFMEMORY;
134 memset(buffer->data, 0, 4);
135 buffer->allocated = initial_len;
141 static void free_encoded_buffer(encoded_buffer *buffer)
143 heap_free(buffer->data);
146 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
150 case XmlEncoding_UTF8:
153 case XmlEncoding_UTF16:
157 FIXME("unsupported encoding %d\n", encoding);
164 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
169 ret = heap_alloc(sizeof(*ret));
170 if (!ret) return E_OUTOFMEMORY;
172 hr = get_code_page(encoding, &ret->code_page);
178 hr = init_encoded_buffer(&ret->utf16);
184 if (ret->code_page == CP_UTF8) {
185 hr = init_encoded_buffer(&ret->encoded);
187 free_encoded_buffer(&ret->utf16);
193 memset(&ret->encoded, 0, sizeof(ret->encoded));
200 static void free_output_buffer(output_buffer *buffer)
202 free_encoded_buffer(&buffer->encoded);
203 free_encoded_buffer(&buffer->utf16);
207 static void grow_buffer(encoded_buffer *buffer, int length)
209 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
210 if (buffer->allocated < buffer->written + length + 4)
212 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
213 buffer->data = heap_realloc(buffer->data, grown_size);
214 buffer->allocated = grown_size;
218 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
223 if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
224 if (buffer->code_page == CP_UTF8)
226 length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
227 grow_buffer(&buffer->encoded, length);
228 ptr = buffer->encoded.data + buffer->encoded.written;
229 length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
230 buffer->encoded.written += len == -1 ? length-1 : length;
234 if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
235 /* WCHAR data just copied */
236 length = len == -1 ? strlenW(data) : len;
239 length *= sizeof(WCHAR);
241 grow_buffer(&buffer->utf16, length);
242 ptr = buffer->utf16.data + buffer->utf16.written;
244 memcpy(ptr, data, length);
245 buffer->utf16.written += length;
247 /* null termination */
248 memset(ptr, 0, sizeof(WCHAR));
255 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
257 return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
260 static HRESULT write_output_buffer_quoted(output_buffer *buffer, const WCHAR *data, int len)
262 write_output_buffer(buffer, quotW, 1);
263 write_output_buffer(buffer, data, len);
264 write_output_buffer(buffer, quotW, 1);
269 /* frees buffer data, reallocates with a default lengths */
270 static void close_output_buffer(mxwriter *This)
272 heap_free(This->buffer->utf16.data);
273 heap_free(This->buffer->encoded.data);
274 init_encoded_buffer(&This->buffer->utf16);
275 init_encoded_buffer(&This->buffer->encoded);
276 get_code_page(This->xml_enc, &This->buffer->code_page);
279 /* escapes special characters like:
285 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
287 static const WCHAR ltW[] = {'&','l','t',';'};
288 static const WCHAR ampW[] = {'&','a','m','p',';'};
289 static const WCHAR equotW[] = {'&','q','u','o','t',';'};
290 static const WCHAR gtW[] = {'&','g','t',';'};
292 const int default_alloc = 100;
293 const int grow_thresh = 10;
294 int p = *len, conv_len;
297 /* default buffer size to something if length is unknown */
298 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
299 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
303 if (ptr - ret > conv_len - grow_thresh)
305 int written = ptr - ret;
307 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
314 memcpy(ptr, ltW, sizeof(ltW));
315 ptr += sizeof(ltW)/sizeof(WCHAR);
318 memcpy(ptr, ampW, sizeof(ampW));
319 ptr += sizeof(ampW)/sizeof(WCHAR);
322 memcpy(ptr, gtW, sizeof(gtW));
323 ptr += sizeof(gtW)/sizeof(WCHAR);
326 if (mode == EscapeValue)
328 memcpy(ptr, equotW, sizeof(equotW));
329 ptr += sizeof(equotW)/sizeof(WCHAR);
332 /* fallthrough for text mode */
342 if (*len != -1) *len = ptr-ret;
348 static void write_prolog_buffer(const mxwriter *This)
350 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
351 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
352 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
353 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
354 static const WCHAR noW[] = {'n','o','\"','?','>'};
355 static const WCHAR crlfW[] = {'\r','\n'};
358 write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
359 write_output_buffer_quoted(This->buffer, This->version, -1);
362 write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
364 /* always write UTF-16 to WCHAR buffer */
365 write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
366 write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
367 write_output_buffer(This->buffer, quotW, 1);
370 write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
371 if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
372 write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
374 write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
376 write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
379 /* Attempts to the write data from the mxwriter's buffer to
380 * the destination stream (if there is one).
382 static HRESULT write_data_to_stream(mxwriter *This)
384 encoded_buffer *buffer;
391 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
392 * 'conv' buffer when UTF8 encoding is used.
394 if (This->xml_enc != XmlEncoding_UTF16)
395 buffer = &This->buffer->encoded;
397 buffer = &This->buffer->utf16;
399 if (This->dest_written > buffer->written) {
400 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
402 } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
403 /* Windows seems to make an empty write call when the encoding is UTF-8 and
404 * all the data has been written to the stream. It doesn't seem make this call
405 * for any other encodings.
409 /* Write the current content from the output buffer into 'dest'.
410 * TODO: Check what Windows does if the IStream doesn't write all of
411 * the data we give it at once.
413 hr = IStream_Write(This->dest, buffer->data+This->dest_written,
414 buffer->written-This->dest_written, &written);
416 WARN("Failed to write data to IStream (0x%08x)\n", hr);
420 This->dest_written += written;
424 /* Newly added element start tag left unclosed cause for empty elements
425 we have to close it differently. */
426 static void close_element_starttag(const mxwriter *This)
428 static const WCHAR gtW[] = {'>'};
429 if (!This->element) return;
430 write_output_buffer(This->buffer, gtW, 1);
433 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
435 SysFreeString(This->element);
436 This->element = name ? SysAllocStringLen(name, len) : NULL;
439 static inline HRESULT flush_output_buffer(mxwriter *This)
441 close_element_starttag(This);
442 set_element_name(This, NULL, 0);
444 return write_data_to_stream(This);
447 /* Resets the mxwriter's output buffer by closing it, then creating a new
448 * output buffer using the given encoding.
450 static inline void reset_output_buffer(mxwriter *This)
452 close_output_buffer(This);
453 This->dest_written = 0;
456 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
458 writer->props[property] = value;
459 writer->prop_changed = TRUE;
463 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
465 if (!value) return E_POINTER;
466 *value = writer->props[property];
470 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
472 return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
475 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
477 return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
480 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
482 return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
485 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
487 mxwriter *This = impl_from_IMXWriter( iface );
489 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
493 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
494 IsEqualGUID( riid, &IID_IDispatch ) ||
495 IsEqualGUID( riid, &IID_IUnknown ) )
497 *obj = &This->IMXWriter_iface;
499 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
501 *obj = &This->ISAXContentHandler_iface;
503 else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
505 *obj = &This->ISAXLexicalHandler_iface;
507 else if (dispex_query_interface(&This->dispex, riid, obj))
509 return *obj ? S_OK : E_NOINTERFACE;
513 ERR("interface %s not implemented\n", debugstr_guid(riid));
515 return E_NOINTERFACE;
518 IMXWriter_AddRef(iface);
522 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
524 mxwriter *This = impl_from_IMXWriter( iface );
525 LONG ref = InterlockedIncrement(&This->ref);
527 TRACE("(%p)->(%d)\n", This, ref);
532 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
534 mxwriter *This = impl_from_IMXWriter( iface );
535 ULONG ref = InterlockedDecrement(&This->ref);
537 TRACE("(%p)->(%d)\n", This, ref);
541 /* Windows flushes the buffer when the interface is destroyed. */
542 flush_output_buffer(This);
543 free_output_buffer(This->buffer);
545 if (This->dest) IStream_Release(This->dest);
546 SysFreeString(This->version);
547 SysFreeString(This->encoding);
549 SysFreeString(This->element);
550 release_dispex(&This->dispex);
557 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
559 mxwriter *This = impl_from_IMXWriter( iface );
560 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
563 static HRESULT WINAPI mxwriter_GetTypeInfo(
565 UINT iTInfo, LCID lcid,
566 ITypeInfo** ppTInfo )
568 mxwriter *This = impl_from_IMXWriter( iface );
569 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
570 iTInfo, lcid, ppTInfo);
573 static HRESULT WINAPI mxwriter_GetIDsOfNames(
575 REFIID riid, LPOLESTR* rgszNames,
576 UINT cNames, LCID lcid, DISPID* rgDispId )
578 mxwriter *This = impl_from_IMXWriter( iface );
579 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
580 riid, rgszNames, cNames, lcid, rgDispId);
583 static HRESULT WINAPI mxwriter_Invoke(
585 DISPID dispIdMember, REFIID riid, LCID lcid,
586 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
587 EXCEPINFO* pExcepInfo, UINT* puArgErr )
589 mxwriter *This = impl_from_IMXWriter( iface );
590 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
591 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
594 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
596 mxwriter *This = impl_from_IMXWriter( iface );
599 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
601 hr = flush_output_buffer(This);
609 if (This->dest) IStream_Release(This->dest);
611 reset_output_buffer(This);
618 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
621 /* Recreate the output buffer to make sure it's using the correct encoding. */
622 reset_output_buffer(This);
624 if (This->dest) IStream_Release(This->dest);
629 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
633 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
640 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
642 mxwriter *This = impl_from_IMXWriter( iface );
644 TRACE("(%p)->(%p)\n", This, dest);
648 HRESULT hr = flush_output_buffer(This);
652 V_VT(dest) = VT_BSTR;
653 V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
658 FIXME("not implemented when stream is set up\n");
663 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
665 mxwriter *This = impl_from_IMXWriter( iface );
669 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
671 enc = parse_encoding_name(encoding);
672 if (enc == XmlEncoding_Unknown)
674 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
678 hr = flush_output_buffer(This);
682 SysReAllocString(&This->encoding, encoding);
685 TRACE("got encoding %d\n", This->xml_enc);
686 reset_output_buffer(This);
690 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
692 mxwriter *This = impl_from_IMXWriter( iface );
694 TRACE("(%p)->(%p)\n", This, encoding);
696 if (!encoding) return E_POINTER;
698 *encoding = SysAllocString(This->encoding);
699 if (!*encoding) return E_OUTOFMEMORY;
704 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
706 mxwriter *This = impl_from_IMXWriter( iface );
708 TRACE("(%p)->(%d)\n", This, value);
709 return writer_set_property(This, MXWriter_BOM, value);
712 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
714 mxwriter *This = impl_from_IMXWriter( iface );
716 TRACE("(%p)->(%p)\n", This, value);
717 return writer_get_property(This, MXWriter_BOM, value);
720 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
722 mxwriter *This = impl_from_IMXWriter( iface );
724 TRACE("(%p)->(%d)\n", This, value);
725 return writer_set_property(This, MXWriter_Indent, value);
728 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
730 mxwriter *This = impl_from_IMXWriter( iface );
732 TRACE("(%p)->(%p)\n", This, value);
733 return writer_get_property(This, MXWriter_Indent, value);
736 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
738 mxwriter *This = impl_from_IMXWriter( iface );
740 TRACE("(%p)->(%d)\n", This, value);
741 return writer_set_property(This, MXWriter_Standalone, value);
744 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
746 mxwriter *This = impl_from_IMXWriter( iface );
748 TRACE("(%p)->(%p)\n", This, value);
749 return writer_get_property(This, MXWriter_Standalone, value);
752 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
754 mxwriter *This = impl_from_IMXWriter( iface );
756 TRACE("(%p)->(%d)\n", This, value);
757 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
760 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
762 mxwriter *This = impl_from_IMXWriter( iface );
764 TRACE("(%p)->(%p)\n", This, value);
765 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
768 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
770 mxwriter *This = impl_from_IMXWriter( iface );
772 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
774 if (!version) return E_INVALIDARG;
776 SysFreeString(This->version);
777 This->version = SysAllocString(version);
782 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
784 mxwriter *This = impl_from_IMXWriter( iface );
786 TRACE("(%p)->(%p)\n", This, version);
788 if (!version) return E_POINTER;
790 return return_bstr(This->version, version);
793 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
795 mxwriter *This = impl_from_IMXWriter( iface );
797 TRACE("(%p)->(%d)\n", This, value);
798 return writer_set_property(This, MXWriter_DisableEscaping, value);
801 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
803 mxwriter *This = impl_from_IMXWriter( iface );
805 TRACE("(%p)->(%p)\n", This, value);
806 return writer_get_property(This, MXWriter_DisableEscaping, value);
809 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
811 mxwriter *This = impl_from_IMXWriter( iface );
812 TRACE("(%p)\n", This);
813 return flush_output_buffer(This);
816 static const struct IMXWriterVtbl MXWriterVtbl =
818 mxwriter_QueryInterface,
821 mxwriter_GetTypeInfoCount,
822 mxwriter_GetTypeInfo,
823 mxwriter_GetIDsOfNames,
827 mxwriter_put_encoding,
828 mxwriter_get_encoding,
829 mxwriter_put_byteOrderMark,
830 mxwriter_get_byteOrderMark,
833 mxwriter_put_standalone,
834 mxwriter_get_standalone,
835 mxwriter_put_omitXMLDeclaration,
836 mxwriter_get_omitXMLDeclaration,
837 mxwriter_put_version,
838 mxwriter_get_version,
839 mxwriter_put_disableOutputEscaping,
840 mxwriter_get_disableOutputEscaping,
844 /*** ISAXContentHandler ***/
845 static HRESULT WINAPI SAXContentHandler_QueryInterface(
846 ISAXContentHandler *iface,
850 mxwriter *This = impl_from_ISAXContentHandler( iface );
851 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
854 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
856 mxwriter *This = impl_from_ISAXContentHandler( iface );
857 return IMXWriter_AddRef(&This->IMXWriter_iface);
860 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
862 mxwriter *This = impl_from_ISAXContentHandler( iface );
863 return IMXWriter_Release(&This->IMXWriter_iface);
866 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
867 ISAXContentHandler *iface,
868 ISAXLocator *locator)
870 mxwriter *This = impl_from_ISAXContentHandler( iface );
871 FIXME("(%p)->(%p)\n", This, locator);
875 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
877 mxwriter *This = impl_from_ISAXContentHandler( iface );
879 TRACE("(%p)\n", This);
881 /* If properties have been changed since the last "endDocument" call
882 * we need to reset the output buffer. If we don't the output buffer
883 * could end up with multiple XML documents in it, plus this seems to
884 * be how Windows works.
886 if (This->prop_changed) {
887 reset_output_buffer(This);
888 This->prop_changed = FALSE;
891 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
893 write_prolog_buffer(This);
895 if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
896 static const char utf16BOM[] = {0xff,0xfe};
898 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
899 /* Windows passes a NULL pointer as the pcbWritten parameter and
900 * ignores any error codes returned from this Write call.
902 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
908 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
910 mxwriter *This = impl_from_ISAXContentHandler( iface );
911 TRACE("(%p)\n", This);
912 This->prop_changed = FALSE;
913 return flush_output_buffer(This);
916 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
917 ISAXContentHandler *iface,
923 mxwriter *This = impl_from_ISAXContentHandler( iface );
924 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
928 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
929 ISAXContentHandler *iface,
933 mxwriter *This = impl_from_ISAXContentHandler( iface );
934 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
938 static HRESULT WINAPI SAXContentHandler_startElement(
939 ISAXContentHandler *iface,
940 const WCHAR *namespaceUri,
942 const WCHAR *local_name,
946 ISAXAttributes *attr)
948 mxwriter *This = impl_from_ISAXContentHandler( iface );
949 static const WCHAR ltW[] = {'<'};
951 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
952 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
954 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
957 close_element_starttag(This);
958 set_element_name(This, QName ? QName : emptyW,
961 write_output_buffer(This->buffer, ltW, 1);
962 write_output_buffer(This->buffer, QName, nQName);
970 hr = ISAXAttributes_getLength(attr, &length);
971 if (FAILED(hr)) return hr;
973 for (i = 0; i < length; i++)
975 static const WCHAR eqW[] = {'='};
980 hr = ISAXAttributes_getQName(attr, i, &str, &len);
981 if (FAILED(hr)) return hr;
983 /* space separator in front of every attribute */
984 write_output_buffer(This->buffer, spaceW, 1);
985 write_output_buffer(This->buffer, str, len);
987 write_output_buffer(This->buffer, eqW, 1);
990 hr = ISAXAttributes_getValue(attr, i, &str, &len);
991 if (FAILED(hr)) return hr;
993 escaped = get_escaped_string(str, EscapeValue, &len);
994 write_output_buffer_quoted(This->buffer, escaped, len);
1002 static HRESULT WINAPI SAXContentHandler_endElement(
1003 ISAXContentHandler *iface,
1004 const WCHAR *namespaceUri,
1006 const WCHAR * local_name,
1011 mxwriter *This = impl_from_ISAXContentHandler( iface );
1013 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1014 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1016 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1017 return E_INVALIDARG;
1019 if (This->element && QName && !strncmpW(This->element, QName, nQName))
1021 static const WCHAR closeW[] = {'/','>'};
1023 write_output_buffer(This->buffer, closeW, 2);
1027 static const WCHAR closetagW[] = {'<','/'};
1028 static const WCHAR gtW[] = {'>'};
1030 write_output_buffer(This->buffer, closetagW, 2);
1031 write_output_buffer(This->buffer, QName, nQName);
1032 write_output_buffer(This->buffer, gtW, 1);
1035 set_element_name(This, NULL, 0);
1040 static HRESULT WINAPI SAXContentHandler_characters(
1041 ISAXContentHandler *iface,
1045 mxwriter *This = impl_from_ISAXContentHandler( iface );
1047 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1049 if (!chars) return E_INVALIDARG;
1051 close_element_starttag(This);
1052 set_element_name(This, NULL, 0);
1057 write_output_buffer(This->buffer, chars, nchars);
1063 escaped = get_escaped_string(chars, EscapeText, &len);
1064 write_output_buffer(This->buffer, escaped, len);
1072 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1073 ISAXContentHandler *iface,
1077 mxwriter *This = impl_from_ISAXContentHandler( iface );
1078 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1082 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1083 ISAXContentHandler *iface,
1084 const WCHAR *target,
1089 mxwriter *This = impl_from_ISAXContentHandler( iface );
1090 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1094 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1095 ISAXContentHandler *iface,
1099 mxwriter *This = impl_from_ISAXContentHandler( iface );
1100 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1104 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1106 SAXContentHandler_QueryInterface,
1107 SAXContentHandler_AddRef,
1108 SAXContentHandler_Release,
1109 SAXContentHandler_putDocumentLocator,
1110 SAXContentHandler_startDocument,
1111 SAXContentHandler_endDocument,
1112 SAXContentHandler_startPrefixMapping,
1113 SAXContentHandler_endPrefixMapping,
1114 SAXContentHandler_startElement,
1115 SAXContentHandler_endElement,
1116 SAXContentHandler_characters,
1117 SAXContentHandler_ignorableWhitespace,
1118 SAXContentHandler_processingInstruction,
1119 SAXContentHandler_skippedEntity
1122 /*** ISAXLexicalHandler ***/
1123 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1124 REFIID riid, void **obj)
1126 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1127 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1130 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1132 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1133 return IMXWriter_AddRef(&This->IMXWriter_iface);
1136 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1138 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1139 return IMXWriter_Release(&This->IMXWriter_iface);
1142 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1143 const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1144 const WCHAR *systemId, int systemId_len)
1146 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1147 static const WCHAR openintW[] = {'[','\r','\n'};
1149 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1151 TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1152 debugstr_wn(systemId, systemId_len));
1154 if (!name) return E_INVALIDARG;
1156 write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1160 write_output_buffer(This->buffer, name, name_len);
1161 write_output_buffer(This->buffer, spaceW, 1);
1166 static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1168 write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1169 write_output_buffer_quoted(This->buffer, publicId, publicId_len);
1171 if (!systemId) return E_INVALIDARG;
1174 write_output_buffer(This->buffer, spaceW, 1);
1176 write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1179 write_output_buffer(This->buffer, spaceW, 1);
1183 static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1185 write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1186 write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1188 write_output_buffer(This->buffer, spaceW, 1);
1191 write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1196 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1198 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1199 static const WCHAR closedtdW[] = {']','>','\r','\n'};
1201 TRACE("(%p)\n", This);
1203 write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1208 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1210 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1211 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1215 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1217 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1218 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1222 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1224 static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1225 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1227 TRACE("(%p)\n", This);
1229 write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1235 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1237 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1238 static const WCHAR ecdataW[] = {']',']','>'};
1240 TRACE("(%p)\n", This);
1242 write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1243 This->cdata = FALSE;
1248 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1250 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1251 static const WCHAR copenW[] = {'<','!','-','-'};
1252 static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1254 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1256 if (!chars) return E_INVALIDARG;
1258 close_element_starttag(This);
1260 write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1262 write_output_buffer(This->buffer, chars, nchars);
1263 write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1268 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1270 SAXLexicalHandler_QueryInterface,
1271 SAXLexicalHandler_AddRef,
1272 SAXLexicalHandler_Release,
1273 SAXLexicalHandler_startDTD,
1274 SAXLexicalHandler_endDTD,
1275 SAXLexicalHandler_startEntity,
1276 SAXLexicalHandler_endEntity,
1277 SAXLexicalHandler_startCDATA,
1278 SAXLexicalHandler_endCDATA,
1279 SAXLexicalHandler_comment
1282 static const tid_t mxwriter_iface_tids[] = {
1287 static dispex_static_data_t mxwriter_dispex = {
1294 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1296 static const WCHAR version10W[] = {'1','.','0',0};
1300 TRACE("(%p, %p)\n", outer, ppObj);
1302 if (outer) FIXME("support aggregation, outer\n");
1304 This = heap_alloc( sizeof (*This) );
1306 return E_OUTOFMEMORY;
1308 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1309 This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1310 This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1312 This->class_version = version;
1314 This->props[MXWriter_BOM] = VARIANT_TRUE;
1315 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1316 This->props[MXWriter_Indent] = VARIANT_FALSE;
1317 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1318 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1319 This->prop_changed = FALSE;
1320 This->encoding = SysAllocString(utf16W);
1321 This->version = SysAllocString(version10W);
1322 This->xml_enc = XmlEncoding_UTF16;
1324 This->element = NULL;
1325 This->cdata = FALSE;
1328 This->dest_written = 0;
1330 hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1332 SysFreeString(This->encoding);
1333 SysFreeString(This->version);
1338 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1340 *ppObj = &This->IMXWriter_iface;
1342 TRACE("returning iface %p\n", *ppObj);