msxml3: Don't use libxml2 encoding helpers.
[wine/multimedia.git] / dlls / msxml3 / mxwriter.c
blobbe03877e134834dfda5a32b6e98e597b8e720ca0
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 #ifdef HAVE_LIBXML2
44 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
45 static const WCHAR emptyW[] = {0};
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;
91 LONG ref;
92 MSXML_VERSION class_version;
94 VARIANT_BOOL props[MXWriter_LastProp];
95 BOOL prop_changed;
97 BSTR version;
99 BSTR encoding; /* exact property value */
100 xml_encoding xml_enc;
102 /* contains a pending (or not closed yet) element name or NULL if
103 we don't have to close */
104 BSTR element;
106 IStream *dest;
107 ULONG dest_written;
109 output_buffer *buffer;
110 } mxwriter;
112 static xml_encoding parse_encoding_name(const WCHAR *encoding)
114 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
115 if (!strcmpiW(encoding, utf8W)) return XmlEncoding_UTF8;
116 if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
117 return XmlEncoding_Unknown;
120 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
122 const int initial_len = 0x2000;
123 buffer->data = heap_alloc(initial_len);
124 if (!buffer->data) return E_OUTOFMEMORY;
126 memset(buffer->data, 0, 4);
127 buffer->allocated = initial_len;
128 buffer->written = 0;
130 return S_OK;
133 static void free_encoded_buffer(encoded_buffer *buffer)
135 heap_free(buffer->data);
138 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
140 switch (encoding)
142 case XmlEncoding_UTF8:
143 *cp = CP_UTF8;
144 break;
145 case XmlEncoding_UTF16:
146 *cp = ~0;
147 break;
148 default:
149 FIXME("unsupported encoding %d\n", encoding);
150 return E_NOTIMPL;
153 return S_OK;
156 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
158 output_buffer *ret;
159 HRESULT hr;
161 ret = heap_alloc(sizeof(*ret));
162 if (!ret) return E_OUTOFMEMORY;
164 hr = get_code_page(encoding, &ret->code_page);
165 if (hr != S_OK) {
166 heap_free(ret);
167 return hr;
170 hr = init_encoded_buffer(&ret->utf16);
171 if (hr != S_OK) {
172 heap_free(ret);
173 return hr;
176 if (ret->code_page == CP_UTF8) {
177 hr = init_encoded_buffer(&ret->encoded);
178 if (hr != S_OK) {
179 free_encoded_buffer(&ret->utf16);
180 heap_free(ret);
181 return hr;
184 else
185 memset(&ret->encoded, 0, sizeof(ret->encoded));
187 *buffer = ret;
189 return S_OK;
192 static void free_output_buffer(output_buffer *buffer)
194 free_encoded_buffer(&buffer->encoded);
195 free_encoded_buffer(&buffer->utf16);
196 heap_free(buffer);
199 static void grow_buffer(encoded_buffer *buffer, int length)
201 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
202 if (buffer->allocated < buffer->written + length + 4)
204 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
205 buffer->data = heap_realloc(buffer->data, grown_size);
206 buffer->allocated = grown_size;
210 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
212 int length;
213 char *ptr;
215 if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
216 if (buffer->code_page == CP_UTF8)
218 length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
219 grow_buffer(&buffer->encoded, length);
220 ptr = buffer->encoded.data + buffer->encoded.written;
221 length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
222 buffer->encoded.written += len == -1 ? length-1 : length;
226 if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
227 /* WCHAR data just copied */
228 length = len == -1 ? strlenW(data) : len;
229 if (length)
231 length *= sizeof(WCHAR);
233 grow_buffer(&buffer->utf16, length);
234 ptr = buffer->utf16.data + buffer->utf16.written;
236 memcpy(ptr, data, length);
237 buffer->utf16.written += length;
238 ptr += length;
239 /* null termination */
240 memset(ptr, 0, sizeof(WCHAR));
244 return S_OK;
247 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
249 return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
252 /* frees buffer data, reallocates with a default lengths */
253 static void close_output_buffer(mxwriter *This)
255 heap_free(This->buffer->utf16.data);
256 heap_free(This->buffer->encoded.data);
257 init_encoded_buffer(&This->buffer->utf16);
258 init_encoded_buffer(&This->buffer->encoded);
259 get_code_page(This->xml_enc, &This->buffer->code_page);
262 /* escapes special characters like:
263 '<' -> "&lt;"
264 '&' -> "&amp;"
265 '"' -> "&quot;"
266 '>' -> "&gt;"
268 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
270 static const WCHAR ltW[] = {'&','l','t',';'};
271 static const WCHAR ampW[] = {'&','a','m','p',';'};
272 static const WCHAR quotW[] = {'&','q','u','o','t',';'};
273 static const WCHAR gtW[] = {'&','g','t',';'};
275 const int default_alloc = 100;
276 const int grow_thresh = 10;
277 int p = *len, conv_len;
278 WCHAR *ptr, *ret;
280 /* default buffer size to something if length is unknown */
281 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
282 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
284 while (*str && p)
286 if (ptr - ret > conv_len - grow_thresh)
288 int written = ptr - ret;
289 conv_len *= 2;
290 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
291 ptr += written;
294 switch (*str)
296 case '<':
297 memcpy(ptr, ltW, sizeof(ltW));
298 ptr += sizeof(ltW)/sizeof(WCHAR);
299 break;
300 case '&':
301 memcpy(ptr, ampW, sizeof(ampW));
302 ptr += sizeof(ampW)/sizeof(WCHAR);
303 break;
304 case '"':
305 memcpy(ptr, quotW, sizeof(quotW));
306 ptr += sizeof(quotW)/sizeof(WCHAR);
307 break;
308 case '>':
309 memcpy(ptr, gtW, sizeof(gtW));
310 ptr += sizeof(gtW)/sizeof(WCHAR);
311 break;
312 default:
313 *ptr++ = *str;
314 break;
317 str++;
318 if (*len != -1) p--;
321 if (*len != -1) *len = ptr-ret;
322 *++ptr = 0;
324 return ret;
327 static void write_prolog_buffer(const mxwriter *This)
329 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"'};
330 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
331 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
332 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
333 static const WCHAR noW[] = {'n','o','\"','?','>'};
334 static const WCHAR quotW[] = {'\"'};
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 HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
462 mxwriter *This = impl_from_IMXWriter( iface );
464 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
466 *obj = NULL;
468 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
469 IsEqualGUID( riid, &IID_IDispatch ) ||
470 IsEqualGUID( riid, &IID_IUnknown ) )
472 *obj = &This->IMXWriter_iface;
474 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
476 *obj = &This->ISAXContentHandler_iface;
478 else if (dispex_query_interface(&This->dispex, riid, obj))
480 return *obj ? S_OK : E_NOINTERFACE;
482 else
484 ERR("interface %s not implemented\n", debugstr_guid(riid));
485 *obj = NULL;
486 return E_NOINTERFACE;
489 IMXWriter_AddRef(iface);
490 return S_OK;
493 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
495 mxwriter *This = impl_from_IMXWriter( iface );
496 LONG ref = InterlockedIncrement(&This->ref);
498 TRACE("(%p)->(%d)\n", This, ref);
500 return ref;
503 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
505 mxwriter *This = impl_from_IMXWriter( iface );
506 ULONG ref = InterlockedDecrement(&This->ref);
508 TRACE("(%p)->(%d)\n", This, ref);
510 if(!ref)
512 /* Windows flushes the buffer when the interface is destroyed. */
513 flush_output_buffer(This);
514 free_output_buffer(This->buffer);
516 if (This->dest) IStream_Release(This->dest);
517 SysFreeString(This->version);
518 SysFreeString(This->encoding);
520 SysFreeString(This->element);
521 release_dispex(&This->dispex);
522 heap_free(This);
525 return ref;
528 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
530 mxwriter *This = impl_from_IMXWriter( iface );
531 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
534 static HRESULT WINAPI mxwriter_GetTypeInfo(
535 IMXWriter *iface,
536 UINT iTInfo, LCID lcid,
537 ITypeInfo** ppTInfo )
539 mxwriter *This = impl_from_IMXWriter( iface );
540 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
541 iTInfo, lcid, ppTInfo);
544 static HRESULT WINAPI mxwriter_GetIDsOfNames(
545 IMXWriter *iface,
546 REFIID riid, LPOLESTR* rgszNames,
547 UINT cNames, LCID lcid, DISPID* rgDispId )
549 mxwriter *This = impl_from_IMXWriter( iface );
550 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
551 riid, rgszNames, cNames, lcid, rgDispId);
554 static HRESULT WINAPI mxwriter_Invoke(
555 IMXWriter *iface,
556 DISPID dispIdMember, REFIID riid, LCID lcid,
557 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
558 EXCEPINFO* pExcepInfo, UINT* puArgErr )
560 mxwriter *This = impl_from_IMXWriter( iface );
561 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
562 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
565 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
567 mxwriter *This = impl_from_IMXWriter( iface );
568 HRESULT hr;
570 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
572 hr = flush_output_buffer(This);
573 if (FAILED(hr))
574 return hr;
576 switch (V_VT(&dest))
578 case VT_EMPTY:
580 if (This->dest) IStream_Release(This->dest);
581 This->dest = NULL;
582 reset_output_buffer(This);
583 break;
585 case VT_UNKNOWN:
587 IStream *stream;
589 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
590 if (hr == S_OK)
592 /* Recreate the output buffer to make sure it's using the correct encoding. */
593 reset_output_buffer(This);
595 if (This->dest) IStream_Release(This->dest);
596 This->dest = stream;
597 break;
600 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
601 return E_NOTIMPL;
603 default:
604 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
605 return E_NOTIMPL;
608 return S_OK;
611 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
613 mxwriter *This = impl_from_IMXWriter( iface );
615 TRACE("(%p)->(%p)\n", This, dest);
617 if (!This->dest)
619 HRESULT hr = flush_output_buffer(This);
620 if (FAILED(hr))
621 return hr;
623 V_VT(dest) = VT_BSTR;
624 V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
626 return S_OK;
628 else
629 FIXME("not implemented when stream is set up\n");
631 return E_NOTIMPL;
634 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
636 mxwriter *This = impl_from_IMXWriter( iface );
637 xml_encoding enc;
638 HRESULT hr;
640 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
642 enc = parse_encoding_name(encoding);
643 if (enc == XmlEncoding_Unknown)
645 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
646 return E_INVALIDARG;
649 hr = flush_output_buffer(This);
650 if (FAILED(hr))
651 return hr;
653 SysReAllocString(&This->encoding, encoding);
654 This->xml_enc = enc;
656 TRACE("got encoding %d\n", This->xml_enc);
657 reset_output_buffer(This);
658 return S_OK;
661 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
663 mxwriter *This = impl_from_IMXWriter( iface );
665 TRACE("(%p)->(%p)\n", This, encoding);
667 if (!encoding) return E_POINTER;
669 *encoding = SysAllocString(This->encoding);
670 if (!*encoding) return E_OUTOFMEMORY;
672 return S_OK;
675 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
677 mxwriter *This = impl_from_IMXWriter( iface );
679 TRACE("(%p)->(%d)\n", This, value);
680 return writer_set_property(This, MXWriter_BOM, value);
683 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
685 mxwriter *This = impl_from_IMXWriter( iface );
687 TRACE("(%p)->(%p)\n", This, value);
688 return writer_get_property(This, MXWriter_BOM, value);
691 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
693 mxwriter *This = impl_from_IMXWriter( iface );
695 TRACE("(%p)->(%d)\n", This, value);
696 return writer_set_property(This, MXWriter_Indent, value);
699 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
701 mxwriter *This = impl_from_IMXWriter( iface );
703 TRACE("(%p)->(%p)\n", This, value);
704 return writer_get_property(This, MXWriter_Indent, value);
707 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
709 mxwriter *This = impl_from_IMXWriter( iface );
711 TRACE("(%p)->(%d)\n", This, value);
712 return writer_set_property(This, MXWriter_Standalone, value);
715 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
717 mxwriter *This = impl_from_IMXWriter( iface );
719 TRACE("(%p)->(%p)\n", This, value);
720 return writer_get_property(This, MXWriter_Standalone, value);
723 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
725 mxwriter *This = impl_from_IMXWriter( iface );
727 TRACE("(%p)->(%d)\n", This, value);
728 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
731 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
733 mxwriter *This = impl_from_IMXWriter( iface );
735 TRACE("(%p)->(%p)\n", This, value);
736 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
739 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
741 mxwriter *This = impl_from_IMXWriter( iface );
743 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
745 if (!version) return E_INVALIDARG;
747 SysFreeString(This->version);
748 This->version = SysAllocString(version);
750 return S_OK;
753 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
755 mxwriter *This = impl_from_IMXWriter( iface );
757 TRACE("(%p)->(%p)\n", This, version);
759 if (!version) return E_POINTER;
761 return return_bstr(This->version, version);
764 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
766 mxwriter *This = impl_from_IMXWriter( iface );
768 TRACE("(%p)->(%d)\n", This, value);
769 return writer_set_property(This, MXWriter_DisableEscaping, value);
772 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
774 mxwriter *This = impl_from_IMXWriter( iface );
776 TRACE("(%p)->(%p)\n", This, value);
777 return writer_get_property(This, MXWriter_DisableEscaping, value);
780 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
782 mxwriter *This = impl_from_IMXWriter( iface );
783 TRACE("(%p)\n", This);
784 return flush_output_buffer(This);
787 static const struct IMXWriterVtbl MXWriterVtbl =
789 mxwriter_QueryInterface,
790 mxwriter_AddRef,
791 mxwriter_Release,
792 mxwriter_GetTypeInfoCount,
793 mxwriter_GetTypeInfo,
794 mxwriter_GetIDsOfNames,
795 mxwriter_Invoke,
796 mxwriter_put_output,
797 mxwriter_get_output,
798 mxwriter_put_encoding,
799 mxwriter_get_encoding,
800 mxwriter_put_byteOrderMark,
801 mxwriter_get_byteOrderMark,
802 mxwriter_put_indent,
803 mxwriter_get_indent,
804 mxwriter_put_standalone,
805 mxwriter_get_standalone,
806 mxwriter_put_omitXMLDeclaration,
807 mxwriter_get_omitXMLDeclaration,
808 mxwriter_put_version,
809 mxwriter_get_version,
810 mxwriter_put_disableOutputEscaping,
811 mxwriter_get_disableOutputEscaping,
812 mxwriter_flush
815 /*** ISAXContentHandler ***/
816 static HRESULT WINAPI mxwriter_saxcontent_QueryInterface(
817 ISAXContentHandler *iface,
818 REFIID riid,
819 void **obj)
821 mxwriter *This = impl_from_ISAXContentHandler( iface );
822 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
825 static ULONG WINAPI mxwriter_saxcontent_AddRef(ISAXContentHandler *iface)
827 mxwriter *This = impl_from_ISAXContentHandler( iface );
828 return IMXWriter_AddRef(&This->IMXWriter_iface);
831 static ULONG WINAPI mxwriter_saxcontent_Release(ISAXContentHandler *iface)
833 mxwriter *This = impl_from_ISAXContentHandler( iface );
834 return IMXWriter_Release(&This->IMXWriter_iface);
837 static HRESULT WINAPI mxwriter_saxcontent_putDocumentLocator(
838 ISAXContentHandler *iface,
839 ISAXLocator *locator)
841 mxwriter *This = impl_from_ISAXContentHandler( iface );
842 FIXME("(%p)->(%p)\n", This, locator);
843 return E_NOTIMPL;
846 static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *iface)
848 mxwriter *This = impl_from_ISAXContentHandler( iface );
850 TRACE("(%p)\n", This);
852 /* If properties have been changed since the last "endDocument" call
853 * we need to reset the output buffer. If we don't the output buffer
854 * could end up with multiple XML documents in it, plus this seems to
855 * be how Windows works.
857 if (This->prop_changed) {
858 reset_output_buffer(This);
859 This->prop_changed = FALSE;
862 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
864 write_prolog_buffer(This);
866 if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
867 static const char utf16BOM[] = {0xff,0xfe};
869 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
870 /* Windows passes a NULL pointer as the pcbWritten parameter and
871 * ignores any error codes returned from this Write call.
873 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
876 return S_OK;
879 static HRESULT WINAPI mxwriter_saxcontent_endDocument(ISAXContentHandler *iface)
881 mxwriter *This = impl_from_ISAXContentHandler( iface );
882 TRACE("(%p)\n", This);
883 This->prop_changed = FALSE;
884 return flush_output_buffer(This);
887 static HRESULT WINAPI mxwriter_saxcontent_startPrefixMapping(
888 ISAXContentHandler *iface,
889 const WCHAR *prefix,
890 int nprefix,
891 const WCHAR *uri,
892 int nuri)
894 mxwriter *This = impl_from_ISAXContentHandler( iface );
895 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
896 return E_NOTIMPL;
899 static HRESULT WINAPI mxwriter_saxcontent_endPrefixMapping(
900 ISAXContentHandler *iface,
901 const WCHAR *prefix,
902 int nprefix)
904 mxwriter *This = impl_from_ISAXContentHandler( iface );
905 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
906 return E_NOTIMPL;
909 static HRESULT WINAPI mxwriter_saxcontent_startElement(
910 ISAXContentHandler *iface,
911 const WCHAR *namespaceUri,
912 int nnamespaceUri,
913 const WCHAR *local_name,
914 int nlocal_name,
915 const WCHAR *QName,
916 int nQName,
917 ISAXAttributes *attr)
919 mxwriter *This = impl_from_ISAXContentHandler( iface );
920 static const WCHAR ltW[] = {'<'};
922 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
923 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
925 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
926 return E_INVALIDARG;
928 close_element_starttag(This);
929 set_element_name(This, QName ? QName : emptyW,
930 QName ? nQName : 0);
932 write_output_buffer(This->buffer, ltW, 1);
933 write_output_buffer(This->buffer, QName, nQName);
935 if (attr)
937 HRESULT hr;
938 INT length;
939 INT i;
941 hr = ISAXAttributes_getLength(attr, &length);
942 if (FAILED(hr)) return hr;
944 for (i = 0; i < length; i++)
946 static const WCHAR spaceW[] = {' '};
947 static const WCHAR eqqW[] = {'=','\"'};
948 static const WCHAR quotW[] = {'\"'};
949 const WCHAR *str;
950 WCHAR *escaped;
951 INT len = 0;
953 hr = ISAXAttributes_getQName(attr, i, &str, &len);
954 if (FAILED(hr)) return hr;
956 /* space separator in front of every attribute */
957 write_output_buffer(This->buffer, spaceW, 1);
958 write_output_buffer(This->buffer, str, len);
960 write_output_buffer(This->buffer, eqqW, 2);
962 len = 0;
963 hr = ISAXAttributes_getValue(attr, i, &str, &len);
964 if (FAILED(hr)) return hr;
966 escaped = get_escaped_string(str, &len);
967 write_output_buffer(This->buffer, escaped, len);
968 heap_free(escaped);
970 write_output_buffer(This->buffer, quotW, 1);
974 return S_OK;
977 static HRESULT WINAPI mxwriter_saxcontent_endElement(
978 ISAXContentHandler *iface,
979 const WCHAR *namespaceUri,
980 int nnamespaceUri,
981 const WCHAR * local_name,
982 int nlocal_name,
983 const WCHAR *QName,
984 int nQName)
986 mxwriter *This = impl_from_ISAXContentHandler( iface );
988 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
989 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
991 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
992 return E_INVALIDARG;
994 if (This->element && QName && !strncmpW(This->element, QName, nQName))
996 static const WCHAR closeW[] = {'/','>'};
998 write_output_buffer(This->buffer, closeW, 2);
1000 else
1002 static const WCHAR closetagW[] = {'<','/'};
1003 static const WCHAR gtW[] = {'>'};
1005 write_output_buffer(This->buffer, closetagW, 2);
1006 write_output_buffer(This->buffer, QName, nQName);
1007 write_output_buffer(This->buffer, gtW, 1);
1010 set_element_name(This, NULL, 0);
1012 return S_OK;
1015 static HRESULT WINAPI mxwriter_saxcontent_characters(
1016 ISAXContentHandler *iface,
1017 const WCHAR *chars,
1018 int nchars)
1020 mxwriter *This = impl_from_ISAXContentHandler( iface );
1022 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1024 if (!chars) return E_INVALIDARG;
1026 close_element_starttag(This);
1027 set_element_name(This, NULL, 0);
1029 if (nchars)
1030 write_output_buffer(This->buffer, chars, nchars);
1032 return S_OK;
1035 static HRESULT WINAPI mxwriter_saxcontent_ignorableWhitespace(
1036 ISAXContentHandler *iface,
1037 const WCHAR *chars,
1038 int nchars)
1040 mxwriter *This = impl_from_ISAXContentHandler( iface );
1041 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1042 return E_NOTIMPL;
1045 static HRESULT WINAPI mxwriter_saxcontent_processingInstruction(
1046 ISAXContentHandler *iface,
1047 const WCHAR *target,
1048 int ntarget,
1049 const WCHAR *data,
1050 int ndata)
1052 mxwriter *This = impl_from_ISAXContentHandler( iface );
1053 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1054 return E_NOTIMPL;
1057 static HRESULT WINAPI mxwriter_saxcontent_skippedEntity(
1058 ISAXContentHandler *iface,
1059 const WCHAR *name,
1060 int nname)
1062 mxwriter *This = impl_from_ISAXContentHandler( iface );
1063 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1064 return E_NOTIMPL;
1067 static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl =
1069 mxwriter_saxcontent_QueryInterface,
1070 mxwriter_saxcontent_AddRef,
1071 mxwriter_saxcontent_Release,
1072 mxwriter_saxcontent_putDocumentLocator,
1073 mxwriter_saxcontent_startDocument,
1074 mxwriter_saxcontent_endDocument,
1075 mxwriter_saxcontent_startPrefixMapping,
1076 mxwriter_saxcontent_endPrefixMapping,
1077 mxwriter_saxcontent_startElement,
1078 mxwriter_saxcontent_endElement,
1079 mxwriter_saxcontent_characters,
1080 mxwriter_saxcontent_ignorableWhitespace,
1081 mxwriter_saxcontent_processingInstruction,
1082 mxwriter_saxcontent_skippedEntity
1085 static const tid_t mxwriter_iface_tids[] = {
1086 IMXWriter_tid,
1090 static dispex_static_data_t mxwriter_dispex = {
1091 NULL,
1092 IMXWriter_tid,
1093 NULL,
1094 mxwriter_iface_tids
1097 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1099 static const WCHAR version10W[] = {'1','.','0',0};
1100 mxwriter *This;
1101 HRESULT hr;
1103 TRACE("(%p, %p)\n", outer, ppObj);
1105 if (outer) FIXME("support aggregation, outer\n");
1107 This = heap_alloc( sizeof (*This) );
1108 if(!This)
1109 return E_OUTOFMEMORY;
1111 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1112 This->ISAXContentHandler_iface.lpVtbl = &mxwriter_saxcontent_vtbl;
1113 This->ref = 1;
1114 This->class_version = version;
1116 This->props[MXWriter_BOM] = VARIANT_TRUE;
1117 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1118 This->props[MXWriter_Indent] = VARIANT_FALSE;
1119 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1120 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1121 This->prop_changed = FALSE;
1122 This->encoding = SysAllocString(utf16W);
1123 This->version = SysAllocString(version10W);
1124 This->xml_enc = XmlEncoding_UTF16;
1126 This->element = NULL;
1128 This->dest = NULL;
1129 This->dest_written = 0;
1131 hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1132 if (hr != S_OK) {
1133 SysFreeString(This->encoding);
1134 SysFreeString(This->version);
1135 heap_free(This);
1136 return hr;
1139 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1141 *ppObj = &This->IMXWriter_iface;
1143 TRACE("returning iface %p\n", *ppObj);
1145 return S_OK;
1148 #else
1150 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **obj)
1152 MESSAGE("This program tried to use a MXXMLWriter object, but\n"
1153 "libxml2 support was not present at compile time.\n");
1154 return E_NOTIMPL;
1157 #endif /* HAVE_LIBXML2 */