msxml3: Stub lexical handler support for MXWriter.
[wine/wine-gecko.git] / dlls / msxml3 / mxwriter.c
blob82f7ebd7eb5cc9dfcae2f7f73cd22f8b8375cc11
1 /*
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
22 #define COBJMACROS
23 #include "config.h"
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
34 #include "msxml6.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};
45 typedef enum
47 XmlEncoding_UTF8,
48 XmlEncoding_UTF16,
49 XmlEncoding_Unknown
50 } xml_encoding;
52 typedef enum
54 OutputBuffer_Native = 0x001,
55 OutputBuffer_Encoded = 0x010,
56 OutputBuffer_Both = 0x100
57 } output_mode;
59 typedef enum
61 MXWriter_BOM = 0,
62 MXWriter_DisableEscaping,
63 MXWriter_Indent,
64 MXWriter_OmitXmlDecl,
65 MXWriter_Standalone,
66 MXWriter_LastProp
67 } mxwriter_prop;
69 typedef struct
71 char *data;
72 unsigned int allocated;
73 unsigned int written;
74 } encoded_buffer;
76 typedef struct
78 encoded_buffer utf16;
79 encoded_buffer encoded;
80 UINT code_page;
81 } output_buffer;
83 typedef struct
85 DispatchEx dispex;
86 IMXWriter IMXWriter_iface;
87 ISAXContentHandler ISAXContentHandler_iface;
88 ISAXLexicalHandler ISAXLexicalHandler_iface;
90 LONG ref;
91 MSXML_VERSION class_version;
93 VARIANT_BOOL props[MXWriter_LastProp];
94 BOOL prop_changed;
96 BSTR version;
98 BSTR encoding; /* exact property value */
99 xml_encoding xml_enc;
101 /* contains a pending (or not closed yet) element name or NULL if
102 we don't have to close */
103 BSTR element;
105 IStream *dest;
106 ULONG dest_written;
108 output_buffer *buffer;
109 } mxwriter;
111 static xml_encoding parse_encoding_name(const WCHAR *encoding)
113 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
114 if (!strcmpiW(encoding, utf8W)) return XmlEncoding_UTF8;
115 if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
116 return XmlEncoding_Unknown;
119 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
121 const int initial_len = 0x2000;
122 buffer->data = heap_alloc(initial_len);
123 if (!buffer->data) return E_OUTOFMEMORY;
125 memset(buffer->data, 0, 4);
126 buffer->allocated = initial_len;
127 buffer->written = 0;
129 return S_OK;
132 static void free_encoded_buffer(encoded_buffer *buffer)
134 heap_free(buffer->data);
137 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
139 switch (encoding)
141 case XmlEncoding_UTF8:
142 *cp = CP_UTF8;
143 break;
144 case XmlEncoding_UTF16:
145 *cp = ~0;
146 break;
147 default:
148 FIXME("unsupported encoding %d\n", encoding);
149 return E_NOTIMPL;
152 return S_OK;
155 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
157 output_buffer *ret;
158 HRESULT hr;
160 ret = heap_alloc(sizeof(*ret));
161 if (!ret) return E_OUTOFMEMORY;
163 hr = get_code_page(encoding, &ret->code_page);
164 if (hr != S_OK) {
165 heap_free(ret);
166 return hr;
169 hr = init_encoded_buffer(&ret->utf16);
170 if (hr != S_OK) {
171 heap_free(ret);
172 return hr;
175 if (ret->code_page == CP_UTF8) {
176 hr = init_encoded_buffer(&ret->encoded);
177 if (hr != S_OK) {
178 free_encoded_buffer(&ret->utf16);
179 heap_free(ret);
180 return hr;
183 else
184 memset(&ret->encoded, 0, sizeof(ret->encoded));
186 *buffer = ret;
188 return S_OK;
191 static void free_output_buffer(output_buffer *buffer)
193 free_encoded_buffer(&buffer->encoded);
194 free_encoded_buffer(&buffer->utf16);
195 heap_free(buffer);
198 static void grow_buffer(encoded_buffer *buffer, int length)
200 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
201 if (buffer->allocated < buffer->written + length + 4)
203 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
204 buffer->data = heap_realloc(buffer->data, grown_size);
205 buffer->allocated = grown_size;
209 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
211 int length;
212 char *ptr;
214 if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
215 if (buffer->code_page == CP_UTF8)
217 length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
218 grow_buffer(&buffer->encoded, length);
219 ptr = buffer->encoded.data + buffer->encoded.written;
220 length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
221 buffer->encoded.written += len == -1 ? length-1 : length;
225 if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
226 /* WCHAR data just copied */
227 length = len == -1 ? strlenW(data) : len;
228 if (length)
230 length *= sizeof(WCHAR);
232 grow_buffer(&buffer->utf16, length);
233 ptr = buffer->utf16.data + buffer->utf16.written;
235 memcpy(ptr, data, length);
236 buffer->utf16.written += length;
237 ptr += length;
238 /* null termination */
239 memset(ptr, 0, sizeof(WCHAR));
243 return S_OK;
246 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
248 return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
251 /* frees buffer data, reallocates with a default lengths */
252 static void close_output_buffer(mxwriter *This)
254 heap_free(This->buffer->utf16.data);
255 heap_free(This->buffer->encoded.data);
256 init_encoded_buffer(&This->buffer->utf16);
257 init_encoded_buffer(&This->buffer->encoded);
258 get_code_page(This->xml_enc, &This->buffer->code_page);
261 /* escapes special characters like:
262 '<' -> "&lt;"
263 '&' -> "&amp;"
264 '"' -> "&quot;"
265 '>' -> "&gt;"
267 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
269 static const WCHAR ltW[] = {'&','l','t',';'};
270 static const WCHAR ampW[] = {'&','a','m','p',';'};
271 static const WCHAR quotW[] = {'&','q','u','o','t',';'};
272 static const WCHAR gtW[] = {'&','g','t',';'};
274 const int default_alloc = 100;
275 const int grow_thresh = 10;
276 int p = *len, conv_len;
277 WCHAR *ptr, *ret;
279 /* default buffer size to something if length is unknown */
280 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
281 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
283 while (*str && p)
285 if (ptr - ret > conv_len - grow_thresh)
287 int written = ptr - ret;
288 conv_len *= 2;
289 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
290 ptr += written;
293 switch (*str)
295 case '<':
296 memcpy(ptr, ltW, sizeof(ltW));
297 ptr += sizeof(ltW)/sizeof(WCHAR);
298 break;
299 case '&':
300 memcpy(ptr, ampW, sizeof(ampW));
301 ptr += sizeof(ampW)/sizeof(WCHAR);
302 break;
303 case '"':
304 memcpy(ptr, quotW, sizeof(quotW));
305 ptr += sizeof(quotW)/sizeof(WCHAR);
306 break;
307 case '>':
308 memcpy(ptr, gtW, sizeof(gtW));
309 ptr += sizeof(gtW)/sizeof(WCHAR);
310 break;
311 default:
312 *ptr++ = *str;
313 break;
316 str++;
317 if (*len != -1) p--;
320 if (*len != -1) *len = ptr-ret;
321 *++ptr = 0;
323 return ret;
326 static void write_prolog_buffer(const mxwriter *This)
328 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"'};
329 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
330 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
331 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
332 static const WCHAR noW[] = {'n','o','\"','?','>'};
333 static const WCHAR quotW[] = {'\"'};
334 static const WCHAR crlfW[] = {'\r','\n'};
336 /* version */
337 write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
338 write_output_buffer(This->buffer, This->version, -1);
339 write_output_buffer(This->buffer, quotW, 1);
341 /* encoding */
342 write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
344 /* always write UTF-16 to WCHAR buffer */
345 write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
346 write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
347 write_output_buffer(This->buffer, quotW, 1);
349 /* standalone */
350 write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
351 if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
352 write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
353 else
354 write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
356 write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
359 /* Attempts to the write data from the mxwriter's buffer to
360 * the destination stream (if there is one).
362 static HRESULT write_data_to_stream(mxwriter *This)
364 encoded_buffer *buffer;
365 ULONG written = 0;
366 HRESULT hr;
368 if (!This->dest)
369 return S_OK;
371 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
372 * 'conv' buffer when UTF8 encoding is used.
374 if (This->xml_enc != XmlEncoding_UTF16)
375 buffer = &This->buffer->encoded;
376 else
377 buffer = &This->buffer->utf16;
379 if (This->dest_written > buffer->written) {
380 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
381 return E_FAIL;
382 } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
383 /* Windows seems to make an empty write call when the encoding is UTF-8 and
384 * all the data has been written to the stream. It doesn't seem make this call
385 * for any other encodings.
387 return S_OK;
389 /* Write the current content from the output buffer into 'dest'.
390 * TODO: Check what Windows does if the IStream doesn't write all of
391 * the data we give it at once.
393 hr = IStream_Write(This->dest, buffer->data+This->dest_written,
394 buffer->written-This->dest_written, &written);
395 if (FAILED(hr)) {
396 WARN("Failed to write data to IStream (0x%08x)\n", hr);
397 return hr;
400 This->dest_written += written;
401 return hr;
404 /* Newly added element start tag left unclosed cause for empty elements
405 we have to close it differently. */
406 static void close_element_starttag(const mxwriter *This)
408 static const WCHAR gtW[] = {'>'};
409 if (!This->element) return;
410 write_output_buffer(This->buffer, gtW, 1);
413 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
415 SysFreeString(This->element);
416 This->element = name ? SysAllocStringLen(name, len) : NULL;
419 static inline HRESULT flush_output_buffer(mxwriter *This)
421 close_element_starttag(This);
422 set_element_name(This, NULL, 0);
423 return write_data_to_stream(This);
426 /* Resets the mxwriter's output buffer by closing it, then creating a new
427 * output buffer using the given encoding.
429 static inline void reset_output_buffer(mxwriter *This)
431 close_output_buffer(This);
432 This->dest_written = 0;
435 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
437 writer->props[property] = value;
438 writer->prop_changed = TRUE;
439 return S_OK;
442 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
444 if (!value) return E_POINTER;
445 *value = writer->props[property];
446 return S_OK;
449 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
451 return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
454 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
456 return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
459 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
461 return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
464 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
466 mxwriter *This = impl_from_IMXWriter( iface );
468 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
470 *obj = NULL;
472 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
473 IsEqualGUID( riid, &IID_IDispatch ) ||
474 IsEqualGUID( riid, &IID_IUnknown ) )
476 *obj = &This->IMXWriter_iface;
478 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
480 *obj = &This->ISAXContentHandler_iface;
482 else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
484 *obj = &This->ISAXLexicalHandler_iface;
486 else if (dispex_query_interface(&This->dispex, riid, obj))
488 return *obj ? S_OK : E_NOINTERFACE;
490 else
492 ERR("interface %s not implemented\n", debugstr_guid(riid));
493 *obj = NULL;
494 return E_NOINTERFACE;
497 IMXWriter_AddRef(iface);
498 return S_OK;
501 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
503 mxwriter *This = impl_from_IMXWriter( iface );
504 LONG ref = InterlockedIncrement(&This->ref);
506 TRACE("(%p)->(%d)\n", This, ref);
508 return ref;
511 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
513 mxwriter *This = impl_from_IMXWriter( iface );
514 ULONG ref = InterlockedDecrement(&This->ref);
516 TRACE("(%p)->(%d)\n", This, ref);
518 if(!ref)
520 /* Windows flushes the buffer when the interface is destroyed. */
521 flush_output_buffer(This);
522 free_output_buffer(This->buffer);
524 if (This->dest) IStream_Release(This->dest);
525 SysFreeString(This->version);
526 SysFreeString(This->encoding);
528 SysFreeString(This->element);
529 release_dispex(&This->dispex);
530 heap_free(This);
533 return ref;
536 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
538 mxwriter *This = impl_from_IMXWriter( iface );
539 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
542 static HRESULT WINAPI mxwriter_GetTypeInfo(
543 IMXWriter *iface,
544 UINT iTInfo, LCID lcid,
545 ITypeInfo** ppTInfo )
547 mxwriter *This = impl_from_IMXWriter( iface );
548 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
549 iTInfo, lcid, ppTInfo);
552 static HRESULT WINAPI mxwriter_GetIDsOfNames(
553 IMXWriter *iface,
554 REFIID riid, LPOLESTR* rgszNames,
555 UINT cNames, LCID lcid, DISPID* rgDispId )
557 mxwriter *This = impl_from_IMXWriter( iface );
558 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
559 riid, rgszNames, cNames, lcid, rgDispId);
562 static HRESULT WINAPI mxwriter_Invoke(
563 IMXWriter *iface,
564 DISPID dispIdMember, REFIID riid, LCID lcid,
565 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
566 EXCEPINFO* pExcepInfo, UINT* puArgErr )
568 mxwriter *This = impl_from_IMXWriter( iface );
569 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
570 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
573 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
575 mxwriter *This = impl_from_IMXWriter( iface );
576 HRESULT hr;
578 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
580 hr = flush_output_buffer(This);
581 if (FAILED(hr))
582 return hr;
584 switch (V_VT(&dest))
586 case VT_EMPTY:
588 if (This->dest) IStream_Release(This->dest);
589 This->dest = NULL;
590 reset_output_buffer(This);
591 break;
593 case VT_UNKNOWN:
595 IStream *stream;
597 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
598 if (hr == S_OK)
600 /* Recreate the output buffer to make sure it's using the correct encoding. */
601 reset_output_buffer(This);
603 if (This->dest) IStream_Release(This->dest);
604 This->dest = stream;
605 break;
608 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
609 return E_NOTIMPL;
611 default:
612 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
613 return E_NOTIMPL;
616 return S_OK;
619 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
621 mxwriter *This = impl_from_IMXWriter( iface );
623 TRACE("(%p)->(%p)\n", This, dest);
625 if (!This->dest)
627 HRESULT hr = flush_output_buffer(This);
628 if (FAILED(hr))
629 return hr;
631 V_VT(dest) = VT_BSTR;
632 V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
634 return S_OK;
636 else
637 FIXME("not implemented when stream is set up\n");
639 return E_NOTIMPL;
642 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
644 mxwriter *This = impl_from_IMXWriter( iface );
645 xml_encoding enc;
646 HRESULT hr;
648 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
650 enc = parse_encoding_name(encoding);
651 if (enc == XmlEncoding_Unknown)
653 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
654 return E_INVALIDARG;
657 hr = flush_output_buffer(This);
658 if (FAILED(hr))
659 return hr;
661 SysReAllocString(&This->encoding, encoding);
662 This->xml_enc = enc;
664 TRACE("got encoding %d\n", This->xml_enc);
665 reset_output_buffer(This);
666 return S_OK;
669 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
671 mxwriter *This = impl_from_IMXWriter( iface );
673 TRACE("(%p)->(%p)\n", This, encoding);
675 if (!encoding) return E_POINTER;
677 *encoding = SysAllocString(This->encoding);
678 if (!*encoding) return E_OUTOFMEMORY;
680 return S_OK;
683 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
685 mxwriter *This = impl_from_IMXWriter( iface );
687 TRACE("(%p)->(%d)\n", This, value);
688 return writer_set_property(This, MXWriter_BOM, value);
691 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
693 mxwriter *This = impl_from_IMXWriter( iface );
695 TRACE("(%p)->(%p)\n", This, value);
696 return writer_get_property(This, MXWriter_BOM, value);
699 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
701 mxwriter *This = impl_from_IMXWriter( iface );
703 TRACE("(%p)->(%d)\n", This, value);
704 return writer_set_property(This, MXWriter_Indent, value);
707 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
709 mxwriter *This = impl_from_IMXWriter( iface );
711 TRACE("(%p)->(%p)\n", This, value);
712 return writer_get_property(This, MXWriter_Indent, value);
715 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
717 mxwriter *This = impl_from_IMXWriter( iface );
719 TRACE("(%p)->(%d)\n", This, value);
720 return writer_set_property(This, MXWriter_Standalone, value);
723 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
725 mxwriter *This = impl_from_IMXWriter( iface );
727 TRACE("(%p)->(%p)\n", This, value);
728 return writer_get_property(This, MXWriter_Standalone, value);
731 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
733 mxwriter *This = impl_from_IMXWriter( iface );
735 TRACE("(%p)->(%d)\n", This, value);
736 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
739 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
741 mxwriter *This = impl_from_IMXWriter( iface );
743 TRACE("(%p)->(%p)\n", This, value);
744 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
747 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
749 mxwriter *This = impl_from_IMXWriter( iface );
751 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
753 if (!version) return E_INVALIDARG;
755 SysFreeString(This->version);
756 This->version = SysAllocString(version);
758 return S_OK;
761 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
763 mxwriter *This = impl_from_IMXWriter( iface );
765 TRACE("(%p)->(%p)\n", This, version);
767 if (!version) return E_POINTER;
769 return return_bstr(This->version, version);
772 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
774 mxwriter *This = impl_from_IMXWriter( iface );
776 TRACE("(%p)->(%d)\n", This, value);
777 return writer_set_property(This, MXWriter_DisableEscaping, value);
780 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
782 mxwriter *This = impl_from_IMXWriter( iface );
784 TRACE("(%p)->(%p)\n", This, value);
785 return writer_get_property(This, MXWriter_DisableEscaping, value);
788 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
790 mxwriter *This = impl_from_IMXWriter( iface );
791 TRACE("(%p)\n", This);
792 return flush_output_buffer(This);
795 static const struct IMXWriterVtbl MXWriterVtbl =
797 mxwriter_QueryInterface,
798 mxwriter_AddRef,
799 mxwriter_Release,
800 mxwriter_GetTypeInfoCount,
801 mxwriter_GetTypeInfo,
802 mxwriter_GetIDsOfNames,
803 mxwriter_Invoke,
804 mxwriter_put_output,
805 mxwriter_get_output,
806 mxwriter_put_encoding,
807 mxwriter_get_encoding,
808 mxwriter_put_byteOrderMark,
809 mxwriter_get_byteOrderMark,
810 mxwriter_put_indent,
811 mxwriter_get_indent,
812 mxwriter_put_standalone,
813 mxwriter_get_standalone,
814 mxwriter_put_omitXMLDeclaration,
815 mxwriter_get_omitXMLDeclaration,
816 mxwriter_put_version,
817 mxwriter_get_version,
818 mxwriter_put_disableOutputEscaping,
819 mxwriter_get_disableOutputEscaping,
820 mxwriter_flush
823 /*** ISAXContentHandler ***/
824 static HRESULT WINAPI SAXContentHandler_QueryInterface(
825 ISAXContentHandler *iface,
826 REFIID riid,
827 void **obj)
829 mxwriter *This = impl_from_ISAXContentHandler( iface );
830 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
833 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
835 mxwriter *This = impl_from_ISAXContentHandler( iface );
836 return IMXWriter_AddRef(&This->IMXWriter_iface);
839 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
841 mxwriter *This = impl_from_ISAXContentHandler( iface );
842 return IMXWriter_Release(&This->IMXWriter_iface);
845 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
846 ISAXContentHandler *iface,
847 ISAXLocator *locator)
849 mxwriter *This = impl_from_ISAXContentHandler( iface );
850 FIXME("(%p)->(%p)\n", This, locator);
851 return E_NOTIMPL;
854 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
856 mxwriter *This = impl_from_ISAXContentHandler( iface );
858 TRACE("(%p)\n", This);
860 /* If properties have been changed since the last "endDocument" call
861 * we need to reset the output buffer. If we don't the output buffer
862 * could end up with multiple XML documents in it, plus this seems to
863 * be how Windows works.
865 if (This->prop_changed) {
866 reset_output_buffer(This);
867 This->prop_changed = FALSE;
870 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
872 write_prolog_buffer(This);
874 if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
875 static const char utf16BOM[] = {0xff,0xfe};
877 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
878 /* Windows passes a NULL pointer as the pcbWritten parameter and
879 * ignores any error codes returned from this Write call.
881 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
884 return S_OK;
887 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
889 mxwriter *This = impl_from_ISAXContentHandler( iface );
890 TRACE("(%p)\n", This);
891 This->prop_changed = FALSE;
892 return flush_output_buffer(This);
895 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
896 ISAXContentHandler *iface,
897 const WCHAR *prefix,
898 int nprefix,
899 const WCHAR *uri,
900 int nuri)
902 mxwriter *This = impl_from_ISAXContentHandler( iface );
903 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
904 return E_NOTIMPL;
907 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
908 ISAXContentHandler *iface,
909 const WCHAR *prefix,
910 int nprefix)
912 mxwriter *This = impl_from_ISAXContentHandler( iface );
913 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
914 return E_NOTIMPL;
917 static HRESULT WINAPI SAXContentHandler_startElement(
918 ISAXContentHandler *iface,
919 const WCHAR *namespaceUri,
920 int nnamespaceUri,
921 const WCHAR *local_name,
922 int nlocal_name,
923 const WCHAR *QName,
924 int nQName,
925 ISAXAttributes *attr)
927 mxwriter *This = impl_from_ISAXContentHandler( iface );
928 static const WCHAR ltW[] = {'<'};
930 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
931 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
933 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
934 return E_INVALIDARG;
936 close_element_starttag(This);
937 set_element_name(This, QName ? QName : emptyW,
938 QName ? nQName : 0);
940 write_output_buffer(This->buffer, ltW, 1);
941 write_output_buffer(This->buffer, QName, nQName);
943 if (attr)
945 HRESULT hr;
946 INT length;
947 INT i;
949 hr = ISAXAttributes_getLength(attr, &length);
950 if (FAILED(hr)) return hr;
952 for (i = 0; i < length; i++)
954 static const WCHAR spaceW[] = {' '};
955 static const WCHAR eqqW[] = {'=','\"'};
956 static const WCHAR quotW[] = {'\"'};
957 const WCHAR *str;
958 WCHAR *escaped;
959 INT len = 0;
961 hr = ISAXAttributes_getQName(attr, i, &str, &len);
962 if (FAILED(hr)) return hr;
964 /* space separator in front of every attribute */
965 write_output_buffer(This->buffer, spaceW, 1);
966 write_output_buffer(This->buffer, str, len);
968 write_output_buffer(This->buffer, eqqW, 2);
970 len = 0;
971 hr = ISAXAttributes_getValue(attr, i, &str, &len);
972 if (FAILED(hr)) return hr;
974 escaped = get_escaped_string(str, &len);
975 write_output_buffer(This->buffer, escaped, len);
976 heap_free(escaped);
978 write_output_buffer(This->buffer, quotW, 1);
982 return S_OK;
985 static HRESULT WINAPI SAXContentHandler_endElement(
986 ISAXContentHandler *iface,
987 const WCHAR *namespaceUri,
988 int nnamespaceUri,
989 const WCHAR * local_name,
990 int nlocal_name,
991 const WCHAR *QName,
992 int nQName)
994 mxwriter *This = impl_from_ISAXContentHandler( iface );
996 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
997 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
999 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1000 return E_INVALIDARG;
1002 if (This->element && QName && !strncmpW(This->element, QName, nQName))
1004 static const WCHAR closeW[] = {'/','>'};
1006 write_output_buffer(This->buffer, closeW, 2);
1008 else
1010 static const WCHAR closetagW[] = {'<','/'};
1011 static const WCHAR gtW[] = {'>'};
1013 write_output_buffer(This->buffer, closetagW, 2);
1014 write_output_buffer(This->buffer, QName, nQName);
1015 write_output_buffer(This->buffer, gtW, 1);
1018 set_element_name(This, NULL, 0);
1020 return S_OK;
1023 static HRESULT WINAPI SAXContentHandler_characters(
1024 ISAXContentHandler *iface,
1025 const WCHAR *chars,
1026 int nchars)
1028 mxwriter *This = impl_from_ISAXContentHandler( iface );
1030 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1032 if (!chars) return E_INVALIDARG;
1034 close_element_starttag(This);
1035 set_element_name(This, NULL, 0);
1037 if (nchars)
1038 write_output_buffer(This->buffer, chars, nchars);
1040 return S_OK;
1043 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1044 ISAXContentHandler *iface,
1045 const WCHAR *chars,
1046 int nchars)
1048 mxwriter *This = impl_from_ISAXContentHandler( iface );
1049 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1050 return E_NOTIMPL;
1053 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1054 ISAXContentHandler *iface,
1055 const WCHAR *target,
1056 int ntarget,
1057 const WCHAR *data,
1058 int ndata)
1060 mxwriter *This = impl_from_ISAXContentHandler( iface );
1061 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1062 return E_NOTIMPL;
1065 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1066 ISAXContentHandler *iface,
1067 const WCHAR *name,
1068 int nname)
1070 mxwriter *This = impl_from_ISAXContentHandler( iface );
1071 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1072 return E_NOTIMPL;
1075 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1077 SAXContentHandler_QueryInterface,
1078 SAXContentHandler_AddRef,
1079 SAXContentHandler_Release,
1080 SAXContentHandler_putDocumentLocator,
1081 SAXContentHandler_startDocument,
1082 SAXContentHandler_endDocument,
1083 SAXContentHandler_startPrefixMapping,
1084 SAXContentHandler_endPrefixMapping,
1085 SAXContentHandler_startElement,
1086 SAXContentHandler_endElement,
1087 SAXContentHandler_characters,
1088 SAXContentHandler_ignorableWhitespace,
1089 SAXContentHandler_processingInstruction,
1090 SAXContentHandler_skippedEntity
1093 /*** ISAXLexicalHandler ***/
1094 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1095 REFIID riid, void **obj)
1097 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1098 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1101 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1103 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1104 return IMXWriter_AddRef(&This->IMXWriter_iface);
1107 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1109 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1110 return IMXWriter_Release(&This->IMXWriter_iface);
1113 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1114 const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1115 const WCHAR *systemId, int systemId_len)
1117 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1118 FIXME("(%p)->(%s %s %s): stub\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1119 debugstr_wn(systemId, systemId_len));
1120 return E_NOTIMPL;
1123 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1125 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1126 FIXME("(%p): stub\n", This);
1127 return E_NOTIMPL;
1130 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1132 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1133 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1134 return E_NOTIMPL;
1137 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1139 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1140 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1141 return E_NOTIMPL;
1144 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1146 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1147 FIXME("(%p): stub\n", This);
1148 return E_NOTIMPL;
1151 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1153 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1154 FIXME("(%p): stub\n", This);
1155 return E_NOTIMPL;
1158 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int len)
1160 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1161 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(chars, len));
1162 return E_NOTIMPL;
1165 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1167 SAXLexicalHandler_QueryInterface,
1168 SAXLexicalHandler_AddRef,
1169 SAXLexicalHandler_Release,
1170 SAXLexicalHandler_startDTD,
1171 SAXLexicalHandler_endDTD,
1172 SAXLexicalHandler_startEntity,
1173 SAXLexicalHandler_endEntity,
1174 SAXLexicalHandler_startCDATA,
1175 SAXLexicalHandler_endCDATA,
1176 SAXLexicalHandler_comment
1179 static const tid_t mxwriter_iface_tids[] = {
1180 IMXWriter_tid,
1184 static dispex_static_data_t mxwriter_dispex = {
1185 NULL,
1186 IMXWriter_tid,
1187 NULL,
1188 mxwriter_iface_tids
1191 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1193 static const WCHAR version10W[] = {'1','.','0',0};
1194 mxwriter *This;
1195 HRESULT hr;
1197 TRACE("(%p, %p)\n", outer, ppObj);
1199 if (outer) FIXME("support aggregation, outer\n");
1201 This = heap_alloc( sizeof (*This) );
1202 if(!This)
1203 return E_OUTOFMEMORY;
1205 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1206 This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1207 This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1208 This->ref = 1;
1209 This->class_version = version;
1211 This->props[MXWriter_BOM] = VARIANT_TRUE;
1212 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1213 This->props[MXWriter_Indent] = VARIANT_FALSE;
1214 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1215 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1216 This->prop_changed = FALSE;
1217 This->encoding = SysAllocString(utf16W);
1218 This->version = SysAllocString(version10W);
1219 This->xml_enc = XmlEncoding_UTF16;
1221 This->element = NULL;
1223 This->dest = NULL;
1224 This->dest_written = 0;
1226 hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1227 if (hr != S_OK) {
1228 SysFreeString(This->encoding);
1229 SysFreeString(This->version);
1230 heap_free(This);
1231 return hr;
1234 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1236 *ppObj = &This->IMXWriter_iface;
1238 TRACE("returning iface %p\n", *ppObj);
1240 return S_OK;