msxml3: Support startDTD()/endDTD() in MXWriter.
[wine/multimedia.git] / dlls / msxml3 / mxwriter.c
blobc94faaaef3bc7f528c4a3df9a8a9bc7b12f5a39e
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};
44 static const WCHAR spaceW[] = {' '};
45 static const WCHAR quotW[] = {'\"'};
47 typedef enum
49 XmlEncoding_UTF8,
50 XmlEncoding_UTF16,
51 XmlEncoding_Unknown
52 } xml_encoding;
54 typedef enum
56 OutputBuffer_Native = 0x001,
57 OutputBuffer_Encoded = 0x010,
58 OutputBuffer_Both = 0x100
59 } output_mode;
61 typedef enum
63 MXWriter_BOM = 0,
64 MXWriter_DisableEscaping,
65 MXWriter_Indent,
66 MXWriter_OmitXmlDecl,
67 MXWriter_Standalone,
68 MXWriter_LastProp
69 } mxwriter_prop;
71 typedef struct
73 char *data;
74 unsigned int allocated;
75 unsigned int written;
76 } encoded_buffer;
78 typedef struct
80 encoded_buffer utf16;
81 encoded_buffer encoded;
82 UINT code_page;
83 } output_buffer;
85 typedef struct
87 DispatchEx dispex;
88 IMXWriter IMXWriter_iface;
89 ISAXContentHandler ISAXContentHandler_iface;
90 ISAXLexicalHandler ISAXLexicalHandler_iface;
92 LONG ref;
93 MSXML_VERSION class_version;
95 VARIANT_BOOL props[MXWriter_LastProp];
96 BOOL prop_changed;
98 BSTR version;
100 BSTR encoding; /* exact property value */
101 xml_encoding xml_enc;
103 /* contains a pending (or not closed yet) element name or NULL if
104 we don't have to close */
105 BSTR element;
107 IStream *dest;
108 ULONG dest_written;
110 output_buffer *buffer;
111 } mxwriter;
113 static xml_encoding parse_encoding_name(const WCHAR *encoding)
115 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
116 if (!strcmpiW(encoding, utf8W)) return XmlEncoding_UTF8;
117 if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
118 return XmlEncoding_Unknown;
121 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
123 const int initial_len = 0x2000;
124 buffer->data = heap_alloc(initial_len);
125 if (!buffer->data) return E_OUTOFMEMORY;
127 memset(buffer->data, 0, 4);
128 buffer->allocated = initial_len;
129 buffer->written = 0;
131 return S_OK;
134 static void free_encoded_buffer(encoded_buffer *buffer)
136 heap_free(buffer->data);
139 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
141 switch (encoding)
143 case XmlEncoding_UTF8:
144 *cp = CP_UTF8;
145 break;
146 case XmlEncoding_UTF16:
147 *cp = ~0;
148 break;
149 default:
150 FIXME("unsupported encoding %d\n", encoding);
151 return E_NOTIMPL;
154 return S_OK;
157 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
159 output_buffer *ret;
160 HRESULT hr;
162 ret = heap_alloc(sizeof(*ret));
163 if (!ret) return E_OUTOFMEMORY;
165 hr = get_code_page(encoding, &ret->code_page);
166 if (hr != S_OK) {
167 heap_free(ret);
168 return hr;
171 hr = init_encoded_buffer(&ret->utf16);
172 if (hr != S_OK) {
173 heap_free(ret);
174 return hr;
177 if (ret->code_page == CP_UTF8) {
178 hr = init_encoded_buffer(&ret->encoded);
179 if (hr != S_OK) {
180 free_encoded_buffer(&ret->utf16);
181 heap_free(ret);
182 return hr;
185 else
186 memset(&ret->encoded, 0, sizeof(ret->encoded));
188 *buffer = ret;
190 return S_OK;
193 static void free_output_buffer(output_buffer *buffer)
195 free_encoded_buffer(&buffer->encoded);
196 free_encoded_buffer(&buffer->utf16);
197 heap_free(buffer);
200 static void grow_buffer(encoded_buffer *buffer, int length)
202 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
203 if (buffer->allocated < buffer->written + length + 4)
205 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
206 buffer->data = heap_realloc(buffer->data, grown_size);
207 buffer->allocated = grown_size;
211 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
213 int length;
214 char *ptr;
216 if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
217 if (buffer->code_page == CP_UTF8)
219 length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
220 grow_buffer(&buffer->encoded, length);
221 ptr = buffer->encoded.data + buffer->encoded.written;
222 length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
223 buffer->encoded.written += len == -1 ? length-1 : length;
227 if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
228 /* WCHAR data just copied */
229 length = len == -1 ? strlenW(data) : len;
230 if (length)
232 length *= sizeof(WCHAR);
234 grow_buffer(&buffer->utf16, length);
235 ptr = buffer->utf16.data + buffer->utf16.written;
237 memcpy(ptr, data, length);
238 buffer->utf16.written += length;
239 ptr += length;
240 /* null termination */
241 memset(ptr, 0, sizeof(WCHAR));
245 return S_OK;
248 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
250 return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
253 /* frees buffer data, reallocates with a default lengths */
254 static void close_output_buffer(mxwriter *This)
256 heap_free(This->buffer->utf16.data);
257 heap_free(This->buffer->encoded.data);
258 init_encoded_buffer(&This->buffer->utf16);
259 init_encoded_buffer(&This->buffer->encoded);
260 get_code_page(This->xml_enc, &This->buffer->code_page);
263 /* escapes special characters like:
264 '<' -> "&lt;"
265 '&' -> "&amp;"
266 '"' -> "&quot;"
267 '>' -> "&gt;"
269 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
271 static const WCHAR ltW[] = {'&','l','t',';'};
272 static const WCHAR ampW[] = {'&','a','m','p',';'};
273 static const WCHAR equotW[] = {'&','q','u','o','t',';'};
274 static const WCHAR gtW[] = {'&','g','t',';'};
276 const int default_alloc = 100;
277 const int grow_thresh = 10;
278 int p = *len, conv_len;
279 WCHAR *ptr, *ret;
281 /* default buffer size to something if length is unknown */
282 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
283 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
285 while (*str && p)
287 if (ptr - ret > conv_len - grow_thresh)
289 int written = ptr - ret;
290 conv_len *= 2;
291 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
292 ptr += written;
295 switch (*str)
297 case '<':
298 memcpy(ptr, ltW, sizeof(ltW));
299 ptr += sizeof(ltW)/sizeof(WCHAR);
300 break;
301 case '&':
302 memcpy(ptr, ampW, sizeof(ampW));
303 ptr += sizeof(ampW)/sizeof(WCHAR);
304 break;
305 case '"':
306 memcpy(ptr, equotW, sizeof(equotW));
307 ptr += sizeof(equotW)/sizeof(WCHAR);
308 break;
309 case '>':
310 memcpy(ptr, gtW, sizeof(gtW));
311 ptr += sizeof(gtW)/sizeof(WCHAR);
312 break;
313 default:
314 *ptr++ = *str;
315 break;
318 str++;
319 if (*len != -1) p--;
322 if (*len != -1) *len = ptr-ret;
323 *++ptr = 0;
325 return ret;
328 static void write_prolog_buffer(const mxwriter *This)
330 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"'};
331 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
332 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
333 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
334 static const WCHAR noW[] = {'n','o','\"','?','>'};
335 static const WCHAR crlfW[] = {'\r','\n'};
337 /* version */
338 write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
339 write_output_buffer(This->buffer, This->version, -1);
340 write_output_buffer(This->buffer, quotW, 1);
342 /* encoding */
343 write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
345 /* always write UTF-16 to WCHAR buffer */
346 write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
347 write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
348 write_output_buffer(This->buffer, quotW, 1);
350 /* standalone */
351 write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
352 if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
353 write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
354 else
355 write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
357 write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
360 /* Attempts to the write data from the mxwriter's buffer to
361 * the destination stream (if there is one).
363 static HRESULT write_data_to_stream(mxwriter *This)
365 encoded_buffer *buffer;
366 ULONG written = 0;
367 HRESULT hr;
369 if (!This->dest)
370 return S_OK;
372 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
373 * 'conv' buffer when UTF8 encoding is used.
375 if (This->xml_enc != XmlEncoding_UTF16)
376 buffer = &This->buffer->encoded;
377 else
378 buffer = &This->buffer->utf16;
380 if (This->dest_written > buffer->written) {
381 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
382 return E_FAIL;
383 } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
384 /* Windows seems to make an empty write call when the encoding is UTF-8 and
385 * all the data has been written to the stream. It doesn't seem make this call
386 * for any other encodings.
388 return S_OK;
390 /* Write the current content from the output buffer into 'dest'.
391 * TODO: Check what Windows does if the IStream doesn't write all of
392 * the data we give it at once.
394 hr = IStream_Write(This->dest, buffer->data+This->dest_written,
395 buffer->written-This->dest_written, &written);
396 if (FAILED(hr)) {
397 WARN("Failed to write data to IStream (0x%08x)\n", hr);
398 return hr;
401 This->dest_written += written;
402 return hr;
405 /* Newly added element start tag left unclosed cause for empty elements
406 we have to close it differently. */
407 static void close_element_starttag(const mxwriter *This)
409 static const WCHAR gtW[] = {'>'};
410 if (!This->element) return;
411 write_output_buffer(This->buffer, gtW, 1);
414 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
416 SysFreeString(This->element);
417 This->element = name ? SysAllocStringLen(name, len) : NULL;
420 static inline HRESULT flush_output_buffer(mxwriter *This)
422 close_element_starttag(This);
423 set_element_name(This, NULL, 0);
424 return write_data_to_stream(This);
427 /* Resets the mxwriter's output buffer by closing it, then creating a new
428 * output buffer using the given encoding.
430 static inline void reset_output_buffer(mxwriter *This)
432 close_output_buffer(This);
433 This->dest_written = 0;
436 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
438 writer->props[property] = value;
439 writer->prop_changed = TRUE;
440 return S_OK;
443 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
445 if (!value) return E_POINTER;
446 *value = writer->props[property];
447 return S_OK;
450 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
452 return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
455 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
457 return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
460 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
462 return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
465 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
467 mxwriter *This = impl_from_IMXWriter( iface );
469 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
471 *obj = NULL;
473 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
474 IsEqualGUID( riid, &IID_IDispatch ) ||
475 IsEqualGUID( riid, &IID_IUnknown ) )
477 *obj = &This->IMXWriter_iface;
479 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
481 *obj = &This->ISAXContentHandler_iface;
483 else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
485 *obj = &This->ISAXLexicalHandler_iface;
487 else if (dispex_query_interface(&This->dispex, riid, obj))
489 return *obj ? S_OK : E_NOINTERFACE;
491 else
493 ERR("interface %s not implemented\n", debugstr_guid(riid));
494 *obj = NULL;
495 return E_NOINTERFACE;
498 IMXWriter_AddRef(iface);
499 return S_OK;
502 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
504 mxwriter *This = impl_from_IMXWriter( iface );
505 LONG ref = InterlockedIncrement(&This->ref);
507 TRACE("(%p)->(%d)\n", This, ref);
509 return ref;
512 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
514 mxwriter *This = impl_from_IMXWriter( iface );
515 ULONG ref = InterlockedDecrement(&This->ref);
517 TRACE("(%p)->(%d)\n", This, ref);
519 if(!ref)
521 /* Windows flushes the buffer when the interface is destroyed. */
522 flush_output_buffer(This);
523 free_output_buffer(This->buffer);
525 if (This->dest) IStream_Release(This->dest);
526 SysFreeString(This->version);
527 SysFreeString(This->encoding);
529 SysFreeString(This->element);
530 release_dispex(&This->dispex);
531 heap_free(This);
534 return ref;
537 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
539 mxwriter *This = impl_from_IMXWriter( iface );
540 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
543 static HRESULT WINAPI mxwriter_GetTypeInfo(
544 IMXWriter *iface,
545 UINT iTInfo, LCID lcid,
546 ITypeInfo** ppTInfo )
548 mxwriter *This = impl_from_IMXWriter( iface );
549 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
550 iTInfo, lcid, ppTInfo);
553 static HRESULT WINAPI mxwriter_GetIDsOfNames(
554 IMXWriter *iface,
555 REFIID riid, LPOLESTR* rgszNames,
556 UINT cNames, LCID lcid, DISPID* rgDispId )
558 mxwriter *This = impl_from_IMXWriter( iface );
559 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
560 riid, rgszNames, cNames, lcid, rgDispId);
563 static HRESULT WINAPI mxwriter_Invoke(
564 IMXWriter *iface,
565 DISPID dispIdMember, REFIID riid, LCID lcid,
566 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
567 EXCEPINFO* pExcepInfo, UINT* puArgErr )
569 mxwriter *This = impl_from_IMXWriter( iface );
570 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
571 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
574 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
576 mxwriter *This = impl_from_IMXWriter( iface );
577 HRESULT hr;
579 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
581 hr = flush_output_buffer(This);
582 if (FAILED(hr))
583 return hr;
585 switch (V_VT(&dest))
587 case VT_EMPTY:
589 if (This->dest) IStream_Release(This->dest);
590 This->dest = NULL;
591 reset_output_buffer(This);
592 break;
594 case VT_UNKNOWN:
596 IStream *stream;
598 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
599 if (hr == S_OK)
601 /* Recreate the output buffer to make sure it's using the correct encoding. */
602 reset_output_buffer(This);
604 if (This->dest) IStream_Release(This->dest);
605 This->dest = stream;
606 break;
609 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
610 return E_NOTIMPL;
612 default:
613 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
614 return E_NOTIMPL;
617 return S_OK;
620 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
622 mxwriter *This = impl_from_IMXWriter( iface );
624 TRACE("(%p)->(%p)\n", This, dest);
626 if (!This->dest)
628 HRESULT hr = flush_output_buffer(This);
629 if (FAILED(hr))
630 return hr;
632 V_VT(dest) = VT_BSTR;
633 V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
635 return S_OK;
637 else
638 FIXME("not implemented when stream is set up\n");
640 return E_NOTIMPL;
643 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
645 mxwriter *This = impl_from_IMXWriter( iface );
646 xml_encoding enc;
647 HRESULT hr;
649 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
651 enc = parse_encoding_name(encoding);
652 if (enc == XmlEncoding_Unknown)
654 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
655 return E_INVALIDARG;
658 hr = flush_output_buffer(This);
659 if (FAILED(hr))
660 return hr;
662 SysReAllocString(&This->encoding, encoding);
663 This->xml_enc = enc;
665 TRACE("got encoding %d\n", This->xml_enc);
666 reset_output_buffer(This);
667 return S_OK;
670 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
672 mxwriter *This = impl_from_IMXWriter( iface );
674 TRACE("(%p)->(%p)\n", This, encoding);
676 if (!encoding) return E_POINTER;
678 *encoding = SysAllocString(This->encoding);
679 if (!*encoding) return E_OUTOFMEMORY;
681 return S_OK;
684 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
686 mxwriter *This = impl_from_IMXWriter( iface );
688 TRACE("(%p)->(%d)\n", This, value);
689 return writer_set_property(This, MXWriter_BOM, value);
692 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
694 mxwriter *This = impl_from_IMXWriter( iface );
696 TRACE("(%p)->(%p)\n", This, value);
697 return writer_get_property(This, MXWriter_BOM, value);
700 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
702 mxwriter *This = impl_from_IMXWriter( iface );
704 TRACE("(%p)->(%d)\n", This, value);
705 return writer_set_property(This, MXWriter_Indent, value);
708 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
710 mxwriter *This = impl_from_IMXWriter( iface );
712 TRACE("(%p)->(%p)\n", This, value);
713 return writer_get_property(This, MXWriter_Indent, value);
716 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
718 mxwriter *This = impl_from_IMXWriter( iface );
720 TRACE("(%p)->(%d)\n", This, value);
721 return writer_set_property(This, MXWriter_Standalone, value);
724 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
726 mxwriter *This = impl_from_IMXWriter( iface );
728 TRACE("(%p)->(%p)\n", This, value);
729 return writer_get_property(This, MXWriter_Standalone, value);
732 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
734 mxwriter *This = impl_from_IMXWriter( iface );
736 TRACE("(%p)->(%d)\n", This, value);
737 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
740 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
742 mxwriter *This = impl_from_IMXWriter( iface );
744 TRACE("(%p)->(%p)\n", This, value);
745 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
748 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
750 mxwriter *This = impl_from_IMXWriter( iface );
752 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
754 if (!version) return E_INVALIDARG;
756 SysFreeString(This->version);
757 This->version = SysAllocString(version);
759 return S_OK;
762 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
764 mxwriter *This = impl_from_IMXWriter( iface );
766 TRACE("(%p)->(%p)\n", This, version);
768 if (!version) return E_POINTER;
770 return return_bstr(This->version, version);
773 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
775 mxwriter *This = impl_from_IMXWriter( iface );
777 TRACE("(%p)->(%d)\n", This, value);
778 return writer_set_property(This, MXWriter_DisableEscaping, value);
781 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
783 mxwriter *This = impl_from_IMXWriter( iface );
785 TRACE("(%p)->(%p)\n", This, value);
786 return writer_get_property(This, MXWriter_DisableEscaping, value);
789 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
791 mxwriter *This = impl_from_IMXWriter( iface );
792 TRACE("(%p)\n", This);
793 return flush_output_buffer(This);
796 static const struct IMXWriterVtbl MXWriterVtbl =
798 mxwriter_QueryInterface,
799 mxwriter_AddRef,
800 mxwriter_Release,
801 mxwriter_GetTypeInfoCount,
802 mxwriter_GetTypeInfo,
803 mxwriter_GetIDsOfNames,
804 mxwriter_Invoke,
805 mxwriter_put_output,
806 mxwriter_get_output,
807 mxwriter_put_encoding,
808 mxwriter_get_encoding,
809 mxwriter_put_byteOrderMark,
810 mxwriter_get_byteOrderMark,
811 mxwriter_put_indent,
812 mxwriter_get_indent,
813 mxwriter_put_standalone,
814 mxwriter_get_standalone,
815 mxwriter_put_omitXMLDeclaration,
816 mxwriter_get_omitXMLDeclaration,
817 mxwriter_put_version,
818 mxwriter_get_version,
819 mxwriter_put_disableOutputEscaping,
820 mxwriter_get_disableOutputEscaping,
821 mxwriter_flush
824 /*** ISAXContentHandler ***/
825 static HRESULT WINAPI SAXContentHandler_QueryInterface(
826 ISAXContentHandler *iface,
827 REFIID riid,
828 void **obj)
830 mxwriter *This = impl_from_ISAXContentHandler( iface );
831 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
834 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
836 mxwriter *This = impl_from_ISAXContentHandler( iface );
837 return IMXWriter_AddRef(&This->IMXWriter_iface);
840 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
842 mxwriter *This = impl_from_ISAXContentHandler( iface );
843 return IMXWriter_Release(&This->IMXWriter_iface);
846 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
847 ISAXContentHandler *iface,
848 ISAXLocator *locator)
850 mxwriter *This = impl_from_ISAXContentHandler( iface );
851 FIXME("(%p)->(%p)\n", This, locator);
852 return E_NOTIMPL;
855 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
857 mxwriter *This = impl_from_ISAXContentHandler( iface );
859 TRACE("(%p)\n", This);
861 /* If properties have been changed since the last "endDocument" call
862 * we need to reset the output buffer. If we don't the output buffer
863 * could end up with multiple XML documents in it, plus this seems to
864 * be how Windows works.
866 if (This->prop_changed) {
867 reset_output_buffer(This);
868 This->prop_changed = FALSE;
871 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
873 write_prolog_buffer(This);
875 if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
876 static const char utf16BOM[] = {0xff,0xfe};
878 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
879 /* Windows passes a NULL pointer as the pcbWritten parameter and
880 * ignores any error codes returned from this Write call.
882 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
885 return S_OK;
888 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
890 mxwriter *This = impl_from_ISAXContentHandler( iface );
891 TRACE("(%p)\n", This);
892 This->prop_changed = FALSE;
893 return flush_output_buffer(This);
896 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
897 ISAXContentHandler *iface,
898 const WCHAR *prefix,
899 int nprefix,
900 const WCHAR *uri,
901 int nuri)
903 mxwriter *This = impl_from_ISAXContentHandler( iface );
904 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
905 return E_NOTIMPL;
908 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
909 ISAXContentHandler *iface,
910 const WCHAR *prefix,
911 int nprefix)
913 mxwriter *This = impl_from_ISAXContentHandler( iface );
914 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
915 return E_NOTIMPL;
918 static HRESULT WINAPI SAXContentHandler_startElement(
919 ISAXContentHandler *iface,
920 const WCHAR *namespaceUri,
921 int nnamespaceUri,
922 const WCHAR *local_name,
923 int nlocal_name,
924 const WCHAR *QName,
925 int nQName,
926 ISAXAttributes *attr)
928 mxwriter *This = impl_from_ISAXContentHandler( iface );
929 static const WCHAR ltW[] = {'<'};
931 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
932 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
934 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
935 return E_INVALIDARG;
937 close_element_starttag(This);
938 set_element_name(This, QName ? QName : emptyW,
939 QName ? nQName : 0);
941 write_output_buffer(This->buffer, ltW, 1);
942 write_output_buffer(This->buffer, QName, nQName);
944 if (attr)
946 HRESULT hr;
947 INT length;
948 INT i;
950 hr = ISAXAttributes_getLength(attr, &length);
951 if (FAILED(hr)) return hr;
953 for (i = 0; i < length; i++)
955 static const WCHAR eqqW[] = {'=','\"'};
956 const WCHAR *str;
957 WCHAR *escaped;
958 INT len = 0;
960 hr = ISAXAttributes_getQName(attr, i, &str, &len);
961 if (FAILED(hr)) return hr;
963 /* space separator in front of every attribute */
964 write_output_buffer(This->buffer, spaceW, 1);
965 write_output_buffer(This->buffer, str, len);
967 write_output_buffer(This->buffer, eqqW, 2);
969 len = 0;
970 hr = ISAXAttributes_getValue(attr, i, &str, &len);
971 if (FAILED(hr)) return hr;
973 escaped = get_escaped_string(str, &len);
974 write_output_buffer(This->buffer, escaped, len);
975 heap_free(escaped);
977 write_output_buffer(This->buffer, quotW, 1);
981 return S_OK;
984 static HRESULT WINAPI SAXContentHandler_endElement(
985 ISAXContentHandler *iface,
986 const WCHAR *namespaceUri,
987 int nnamespaceUri,
988 const WCHAR * local_name,
989 int nlocal_name,
990 const WCHAR *QName,
991 int nQName)
993 mxwriter *This = impl_from_ISAXContentHandler( iface );
995 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
996 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
998 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
999 return E_INVALIDARG;
1001 if (This->element && QName && !strncmpW(This->element, QName, nQName))
1003 static const WCHAR closeW[] = {'/','>'};
1005 write_output_buffer(This->buffer, closeW, 2);
1007 else
1009 static const WCHAR closetagW[] = {'<','/'};
1010 static const WCHAR gtW[] = {'>'};
1012 write_output_buffer(This->buffer, closetagW, 2);
1013 write_output_buffer(This->buffer, QName, nQName);
1014 write_output_buffer(This->buffer, gtW, 1);
1017 set_element_name(This, NULL, 0);
1019 return S_OK;
1022 static HRESULT WINAPI SAXContentHandler_characters(
1023 ISAXContentHandler *iface,
1024 const WCHAR *chars,
1025 int nchars)
1027 mxwriter *This = impl_from_ISAXContentHandler( iface );
1029 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1031 if (!chars) return E_INVALIDARG;
1033 close_element_starttag(This);
1034 set_element_name(This, NULL, 0);
1036 if (nchars)
1037 write_output_buffer(This->buffer, chars, nchars);
1039 return S_OK;
1042 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1043 ISAXContentHandler *iface,
1044 const WCHAR *chars,
1045 int nchars)
1047 mxwriter *This = impl_from_ISAXContentHandler( iface );
1048 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1049 return E_NOTIMPL;
1052 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1053 ISAXContentHandler *iface,
1054 const WCHAR *target,
1055 int ntarget,
1056 const WCHAR *data,
1057 int ndata)
1059 mxwriter *This = impl_from_ISAXContentHandler( iface );
1060 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1061 return E_NOTIMPL;
1064 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1065 ISAXContentHandler *iface,
1066 const WCHAR *name,
1067 int nname)
1069 mxwriter *This = impl_from_ISAXContentHandler( iface );
1070 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1071 return E_NOTIMPL;
1074 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1076 SAXContentHandler_QueryInterface,
1077 SAXContentHandler_AddRef,
1078 SAXContentHandler_Release,
1079 SAXContentHandler_putDocumentLocator,
1080 SAXContentHandler_startDocument,
1081 SAXContentHandler_endDocument,
1082 SAXContentHandler_startPrefixMapping,
1083 SAXContentHandler_endPrefixMapping,
1084 SAXContentHandler_startElement,
1085 SAXContentHandler_endElement,
1086 SAXContentHandler_characters,
1087 SAXContentHandler_ignorableWhitespace,
1088 SAXContentHandler_processingInstruction,
1089 SAXContentHandler_skippedEntity
1092 /*** ISAXLexicalHandler ***/
1093 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1094 REFIID riid, void **obj)
1096 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1097 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1100 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1102 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1103 return IMXWriter_AddRef(&This->IMXWriter_iface);
1106 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1108 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1109 return IMXWriter_Release(&This->IMXWriter_iface);
1112 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1113 const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1114 const WCHAR *systemId, int systemId_len)
1116 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1117 static const WCHAR openintW[] = {'[','\r','\n'};
1119 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1121 TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1122 debugstr_wn(systemId, systemId_len));
1124 if (!name) return E_INVALIDARG;
1126 write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1128 if (*name)
1130 write_output_buffer(This->buffer, name, name_len);
1131 write_output_buffer(This->buffer, spaceW, 1);
1134 if (publicId)
1136 static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1138 write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1139 write_output_buffer(This->buffer, quotW, 1);
1140 write_output_buffer(This->buffer, publicId, publicId_len);
1141 write_output_buffer(This->buffer, quotW, 1);
1143 if (!systemId) return E_INVALIDARG;
1145 if (*publicId)
1146 write_output_buffer(This->buffer, spaceW, 1);
1148 write_output_buffer(This->buffer, quotW, 1);
1149 write_output_buffer(This->buffer, systemId, systemId_len);
1150 write_output_buffer(This->buffer, quotW, 1);
1152 if (*systemId)
1153 write_output_buffer(This->buffer, spaceW, 1);
1155 else if (systemId)
1157 static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1159 write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1160 write_output_buffer(This->buffer, quotW, 1);
1161 write_output_buffer(This->buffer, systemId, systemId_len);
1162 write_output_buffer(This->buffer, quotW, 1);
1163 if (*systemId)
1164 write_output_buffer(This->buffer, spaceW, 1);
1167 write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1169 return S_OK;
1172 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1174 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1175 static const WCHAR closedtdW[] = {']','>','\r','\n'};
1177 TRACE("(%p)\n", This);
1179 write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1181 return S_OK;
1184 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1186 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1187 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1188 return E_NOTIMPL;
1191 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1193 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1194 FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1195 return E_NOTIMPL;
1198 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1200 static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1201 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1203 TRACE("(%p)\n", This);
1205 write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1207 return S_OK;
1210 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1212 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1213 static const WCHAR ecdataW[] = {']',']','>'};
1215 TRACE("(%p)\n", This);
1217 write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1219 return S_OK;
1222 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1224 mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1225 static const WCHAR copenW[] = {'<','!','-','-'};
1226 static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1228 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1230 if (!chars) return E_INVALIDARG;
1232 close_element_starttag(This);
1234 write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1235 if (nchars)
1236 write_output_buffer(This->buffer, chars, nchars);
1237 write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1239 return S_OK;
1242 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1244 SAXLexicalHandler_QueryInterface,
1245 SAXLexicalHandler_AddRef,
1246 SAXLexicalHandler_Release,
1247 SAXLexicalHandler_startDTD,
1248 SAXLexicalHandler_endDTD,
1249 SAXLexicalHandler_startEntity,
1250 SAXLexicalHandler_endEntity,
1251 SAXLexicalHandler_startCDATA,
1252 SAXLexicalHandler_endCDATA,
1253 SAXLexicalHandler_comment
1256 static const tid_t mxwriter_iface_tids[] = {
1257 IMXWriter_tid,
1261 static dispex_static_data_t mxwriter_dispex = {
1262 NULL,
1263 IMXWriter_tid,
1264 NULL,
1265 mxwriter_iface_tids
1268 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1270 static const WCHAR version10W[] = {'1','.','0',0};
1271 mxwriter *This;
1272 HRESULT hr;
1274 TRACE("(%p, %p)\n", outer, ppObj);
1276 if (outer) FIXME("support aggregation, outer\n");
1278 This = heap_alloc( sizeof (*This) );
1279 if(!This)
1280 return E_OUTOFMEMORY;
1282 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1283 This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1284 This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1285 This->ref = 1;
1286 This->class_version = version;
1288 This->props[MXWriter_BOM] = VARIANT_TRUE;
1289 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1290 This->props[MXWriter_Indent] = VARIANT_FALSE;
1291 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1292 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1293 This->prop_changed = FALSE;
1294 This->encoding = SysAllocString(utf16W);
1295 This->version = SysAllocString(version10W);
1296 This->xml_enc = XmlEncoding_UTF16;
1298 This->element = NULL;
1300 This->dest = NULL;
1301 This->dest_written = 0;
1303 hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1304 if (hr != S_OK) {
1305 SysFreeString(This->encoding);
1306 SysFreeString(This->version);
1307 heap_free(This);
1308 return hr;
1311 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1313 *ppObj = &This->IMXWriter_iface;
1315 TRACE("returning iface %p\n", *ppObj);
1317 return S_OK;