winealsa: Fix AudioRenderClient Get/ReleaseBuffer protocol.
[wine] / dlls / msxml3 / mxwriter.c
1 /*
2  *    MXWriter implementation
3  *
4  * Copyright 2011 Nikolay Sivov for CodeWeavers
5  * Copyright 2011 Thomas Mullaly
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #define COBJMACROS
23 #include "config.h"
24
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
33
34 #include "msxml6.h"
35
36 #include "wine/debug.h"
37
38 #include "msxml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
41
42 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
43 static const WCHAR emptyW[] = {0};
44
45 typedef enum
46 {
47     XmlEncoding_UTF8,
48     XmlEncoding_UTF16,
49     XmlEncoding_Unknown
50 } xml_encoding;
51
52 typedef enum
53 {
54     OutputBuffer_Native  = 0x001,
55     OutputBuffer_Encoded = 0x010,
56     OutputBuffer_Both    = 0x100
57 } output_mode;
58
59 typedef enum
60 {
61     MXWriter_BOM = 0,
62     MXWriter_DisableEscaping,
63     MXWriter_Indent,
64     MXWriter_OmitXmlDecl,
65     MXWriter_Standalone,
66     MXWriter_LastProp
67 } mxwriter_prop;
68
69 typedef struct
70 {
71     char *data;
72     unsigned int allocated;
73     unsigned int written;
74 } encoded_buffer;
75
76 typedef struct
77 {
78     encoded_buffer utf16;
79     encoded_buffer encoded;
80     UINT code_page;
81 } output_buffer;
82
83 typedef struct
84 {
85     DispatchEx dispex;
86     IMXWriter IMXWriter_iface;
87     ISAXContentHandler ISAXContentHandler_iface;
88
89     LONG ref;
90     MSXML_VERSION class_version;
91
92     VARIANT_BOOL props[MXWriter_LastProp];
93     BOOL prop_changed;
94
95     BSTR version;
96
97     BSTR encoding; /* exact property value */
98     xml_encoding xml_enc;
99
100     /* contains a pending (or not closed yet) element name or NULL if
101        we don't have to close */
102     BSTR element;
103
104     IStream *dest;
105     ULONG dest_written;
106
107     output_buffer *buffer;
108 } mxwriter;
109
110 static xml_encoding parse_encoding_name(const WCHAR *encoding)
111 {
112     static const WCHAR utf8W[]  = {'U','T','F','-','8',0};
113     if (!strcmpiW(encoding, utf8W))  return XmlEncoding_UTF8;
114     if (!strcmpiW(encoding, utf16W)) return XmlEncoding_UTF16;
115     return XmlEncoding_Unknown;
116 }
117
118 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
119 {
120     const int initial_len = 0x2000;
121     buffer->data = heap_alloc(initial_len);
122     if (!buffer->data) return E_OUTOFMEMORY;
123
124     memset(buffer->data, 0, 4);
125     buffer->allocated = initial_len;
126     buffer->written = 0;
127
128     return S_OK;
129 }
130
131 static void free_encoded_buffer(encoded_buffer *buffer)
132 {
133     heap_free(buffer->data);
134 }
135
136 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
137 {
138     switch (encoding)
139     {
140     case XmlEncoding_UTF8:
141         *cp = CP_UTF8;
142         break;
143     case XmlEncoding_UTF16:
144         *cp = ~0;
145         break;
146     default:
147         FIXME("unsupported encoding %d\n", encoding);
148         return E_NOTIMPL;
149     }
150
151     return S_OK;
152 }
153
154 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
155 {
156     output_buffer *ret;
157     HRESULT hr;
158
159     ret = heap_alloc(sizeof(*ret));
160     if (!ret) return E_OUTOFMEMORY;
161
162     hr = get_code_page(encoding, &ret->code_page);
163     if (hr != S_OK) {
164         heap_free(ret);
165         return hr;
166     }
167
168     hr = init_encoded_buffer(&ret->utf16);
169     if (hr != S_OK) {
170         heap_free(ret);
171         return hr;
172     }
173
174     if (ret->code_page == CP_UTF8) {
175         hr = init_encoded_buffer(&ret->encoded);
176         if (hr != S_OK) {
177             free_encoded_buffer(&ret->utf16);
178             heap_free(ret);
179             return hr;
180         }
181     }
182     else
183         memset(&ret->encoded, 0, sizeof(ret->encoded));
184
185     *buffer = ret;
186
187     return S_OK;
188 }
189
190 static void free_output_buffer(output_buffer *buffer)
191 {
192     free_encoded_buffer(&buffer->encoded);
193     free_encoded_buffer(&buffer->utf16);
194     heap_free(buffer);
195 }
196
197 static void grow_buffer(encoded_buffer *buffer, int length)
198 {
199     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
200     if (buffer->allocated < buffer->written + length + 4)
201     {
202         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
203         buffer->data = heap_realloc(buffer->data, grown_size);
204         buffer->allocated = grown_size;
205     }
206 }
207
208 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
209 {
210     int length;
211     char *ptr;
212
213     if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
214         if (buffer->code_page == CP_UTF8)
215         {
216             length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
217             grow_buffer(&buffer->encoded, length);
218             ptr = buffer->encoded.data + buffer->encoded.written;
219             length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
220             buffer->encoded.written += len == -1 ? length-1 : length;
221         }
222     }
223
224     if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
225         /* WCHAR data just copied */
226         length = len == -1 ? strlenW(data) : len;
227         if (length)
228         {
229             length *= sizeof(WCHAR);
230
231             grow_buffer(&buffer->utf16, length);
232             ptr = buffer->utf16.data + buffer->utf16.written;
233
234             memcpy(ptr, data, length);
235             buffer->utf16.written += length;
236             ptr += length;
237             /* null termination */
238             memset(ptr, 0, sizeof(WCHAR));
239         }
240     }
241
242     return S_OK;
243 }
244
245 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
246 {
247     return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
248 }
249
250 /* frees buffer data, reallocates with a default lengths */
251 static void close_output_buffer(mxwriter *This)
252 {
253     heap_free(This->buffer->utf16.data);
254     heap_free(This->buffer->encoded.data);
255     init_encoded_buffer(&This->buffer->utf16);
256     init_encoded_buffer(&This->buffer->encoded);
257     get_code_page(This->xml_enc, &This->buffer->code_page);
258 }
259
260 /* escapes special characters like:
261    '<' -> "&lt;"
262    '&' -> "&amp;"
263    '"' -> "&quot;"
264    '>' -> "&gt;"
265 */
266 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
267 {
268     static const WCHAR ltW[]   = {'&','l','t',';'};
269     static const WCHAR ampW[]  = {'&','a','m','p',';'};
270     static const WCHAR quotW[] = {'&','q','u','o','t',';'};
271     static const WCHAR gtW[]   = {'&','g','t',';'};
272
273     const int default_alloc = 100;
274     const int grow_thresh = 10;
275     int p = *len, conv_len;
276     WCHAR *ptr, *ret;
277
278     /* default buffer size to something if length is unknown */
279     conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
280     ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
281
282     while (*str && p)
283     {
284         if (ptr - ret > conv_len - grow_thresh)
285         {
286             int written = ptr - ret;
287             conv_len *= 2;
288             ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
289             ptr += written;
290         }
291
292         switch (*str)
293         {
294         case '<':
295             memcpy(ptr, ltW, sizeof(ltW));
296             ptr += sizeof(ltW)/sizeof(WCHAR);
297             break;
298         case '&':
299             memcpy(ptr, ampW, sizeof(ampW));
300             ptr += sizeof(ampW)/sizeof(WCHAR);
301             break;
302         case '"':
303             memcpy(ptr, quotW, sizeof(quotW));
304             ptr += sizeof(quotW)/sizeof(WCHAR);
305             break;
306         case '>':
307             memcpy(ptr, gtW, sizeof(gtW));
308             ptr += sizeof(gtW)/sizeof(WCHAR);
309             break;
310         default:
311             *ptr++ = *str;
312             break;
313         }
314
315         str++;
316         if (*len != -1) p--;
317     }
318
319     if (*len != -1) *len = ptr-ret;
320     *++ptr = 0;
321
322     return ret;
323 }
324
325 static void write_prolog_buffer(const mxwriter *This)
326 {
327     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"'};
328     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
329     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
330     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
331     static const WCHAR noW[] = {'n','o','\"','?','>'};
332     static const WCHAR quotW[] = {'\"'};
333     static const WCHAR crlfW[] = {'\r','\n'};
334
335     /* version */
336     write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
337     write_output_buffer(This->buffer, This->version, -1);
338     write_output_buffer(This->buffer, quotW, 1);
339
340     /* encoding */
341     write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
342
343     /* always write UTF-16 to WCHAR buffer */
344     write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
345     write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
346     write_output_buffer(This->buffer, quotW, 1);
347
348     /* standalone */
349     write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
350     if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
351         write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
352     else
353         write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
354
355     write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
356 }
357
358 /* Attempts to the write data from the mxwriter's buffer to
359  * the destination stream (if there is one).
360  */
361 static HRESULT write_data_to_stream(mxwriter *This)
362 {
363     encoded_buffer *buffer;
364     ULONG written = 0;
365     HRESULT hr;
366
367     if (!This->dest)
368         return S_OK;
369
370     /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
371      * 'conv' buffer when UTF8 encoding is used.
372      */
373     if (This->xml_enc != XmlEncoding_UTF16)
374         buffer = &This->buffer->encoded;
375     else
376         buffer = &This->buffer->utf16;
377
378     if (This->dest_written > buffer->written) {
379         ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
380         return E_FAIL;
381     } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
382         /* Windows seems to make an empty write call when the encoding is UTF-8 and
383          * all the data has been written to the stream. It doesn't seem make this call
384          * for any other encodings.
385          */
386         return S_OK;
387
388     /* Write the current content from the output buffer into 'dest'.
389      * TODO: Check what Windows does if the IStream doesn't write all of
390      *       the data we give it at once.
391      */
392     hr = IStream_Write(This->dest, buffer->data+This->dest_written,
393                          buffer->written-This->dest_written, &written);
394     if (FAILED(hr)) {
395         WARN("Failed to write data to IStream (0x%08x)\n", hr);
396         return hr;
397     }
398
399     This->dest_written += written;
400     return hr;
401 }
402
403 /* Newly added element start tag left unclosed cause for empty elements
404    we have to close it differently. */
405 static void close_element_starttag(const mxwriter *This)
406 {
407     static const WCHAR gtW[] = {'>'};
408     if (!This->element) return;
409     write_output_buffer(This->buffer, gtW, 1);
410 }
411
412 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
413 {
414     SysFreeString(This->element);
415     This->element = name ? SysAllocStringLen(name, len) : NULL;
416 }
417
418 static inline HRESULT flush_output_buffer(mxwriter *This)
419 {
420     close_element_starttag(This);
421     set_element_name(This, NULL, 0);
422     return write_data_to_stream(This);
423 }
424
425 /* Resets the mxwriter's output buffer by closing it, then creating a new
426  * output buffer using the given encoding.
427  */
428 static inline void reset_output_buffer(mxwriter *This)
429 {
430     close_output_buffer(This);
431     This->dest_written = 0;
432 }
433
434 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
435 {
436     writer->props[property] = value;
437     writer->prop_changed = TRUE;
438     return S_OK;
439 }
440
441 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
442 {
443     if (!value) return E_POINTER;
444     *value = writer->props[property];
445     return S_OK;
446 }
447
448 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
449 {
450     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
451 }
452
453 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
454 {
455     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
456 }
457
458 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
459 {
460     mxwriter *This = impl_from_IMXWriter( iface );
461
462     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
463
464     *obj = NULL;
465
466     if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
467          IsEqualGUID( riid, &IID_IDispatch ) ||
468          IsEqualGUID( riid, &IID_IUnknown ) )
469     {
470         *obj = &This->IMXWriter_iface;
471     }
472     else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
473     {
474         *obj = &This->ISAXContentHandler_iface;
475     }
476     else if (dispex_query_interface(&This->dispex, riid, obj))
477     {
478         return *obj ? S_OK : E_NOINTERFACE;
479     }
480     else
481     {
482         ERR("interface %s not implemented\n", debugstr_guid(riid));
483         *obj = NULL;
484         return E_NOINTERFACE;
485     }
486
487     IMXWriter_AddRef(iface);
488     return S_OK;
489 }
490
491 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
492 {
493     mxwriter *This = impl_from_IMXWriter( iface );
494     LONG ref = InterlockedIncrement(&This->ref);
495
496     TRACE("(%p)->(%d)\n", This, ref);
497
498     return ref;
499 }
500
501 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
502 {
503     mxwriter *This = impl_from_IMXWriter( iface );
504     ULONG ref = InterlockedDecrement(&This->ref);
505
506     TRACE("(%p)->(%d)\n", This, ref);
507
508     if(!ref)
509     {
510         /* Windows flushes the buffer when the interface is destroyed. */
511         flush_output_buffer(This);
512         free_output_buffer(This->buffer);
513
514         if (This->dest) IStream_Release(This->dest);
515         SysFreeString(This->version);
516         SysFreeString(This->encoding);
517
518         SysFreeString(This->element);
519         release_dispex(&This->dispex);
520         heap_free(This);
521     }
522
523     return ref;
524 }
525
526 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
527 {
528     mxwriter *This = impl_from_IMXWriter( iface );
529     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
530 }
531
532 static HRESULT WINAPI mxwriter_GetTypeInfo(
533     IMXWriter *iface,
534     UINT iTInfo, LCID lcid,
535     ITypeInfo** ppTInfo )
536 {
537     mxwriter *This = impl_from_IMXWriter( iface );
538     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
539         iTInfo, lcid, ppTInfo);
540 }
541
542 static HRESULT WINAPI mxwriter_GetIDsOfNames(
543     IMXWriter *iface,
544     REFIID riid, LPOLESTR* rgszNames,
545     UINT cNames, LCID lcid, DISPID* rgDispId )
546 {
547     mxwriter *This = impl_from_IMXWriter( iface );
548     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
549         riid, rgszNames, cNames, lcid, rgDispId);
550 }
551
552 static HRESULT WINAPI mxwriter_Invoke(
553     IMXWriter *iface,
554     DISPID dispIdMember, REFIID riid, LCID lcid,
555     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
556     EXCEPINFO* pExcepInfo, UINT* puArgErr )
557 {
558     mxwriter *This = impl_from_IMXWriter( iface );
559     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
560         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
561 }
562
563 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
564 {
565     mxwriter *This = impl_from_IMXWriter( iface );
566     HRESULT hr;
567
568     TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
569
570     hr = flush_output_buffer(This);
571     if (FAILED(hr))
572         return hr;
573
574     switch (V_VT(&dest))
575     {
576     case VT_EMPTY:
577     {
578         if (This->dest) IStream_Release(This->dest);
579         This->dest = NULL;
580         reset_output_buffer(This);
581         break;
582     }
583     case VT_UNKNOWN:
584     {
585         IStream *stream;
586
587         hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
588         if (hr == S_OK)
589         {
590             /* Recreate the output buffer to make sure it's using the correct encoding. */
591             reset_output_buffer(This);
592
593             if (This->dest) IStream_Release(This->dest);
594             This->dest = stream;
595             break;
596         }
597
598         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
599         return E_NOTIMPL;
600     }
601     default:
602         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
603         return E_NOTIMPL;
604     }
605
606     return S_OK;
607 }
608
609 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
610 {
611     mxwriter *This = impl_from_IMXWriter( iface );
612
613     TRACE("(%p)->(%p)\n", This, dest);
614
615     if (!This->dest)
616     {
617         HRESULT hr = flush_output_buffer(This);
618         if (FAILED(hr))
619             return hr;
620
621         V_VT(dest)   = VT_BSTR;
622         V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
623
624         return S_OK;
625     }
626     else
627         FIXME("not implemented when stream is set up\n");
628
629     return E_NOTIMPL;
630 }
631
632 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
633 {
634     mxwriter *This = impl_from_IMXWriter( iface );
635     xml_encoding enc;
636     HRESULT hr;
637
638     TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
639
640     enc = parse_encoding_name(encoding);
641     if (enc == XmlEncoding_Unknown)
642     {
643         FIXME("unsupported encoding %s\n", debugstr_w(encoding));
644         return E_INVALIDARG;
645     }
646
647     hr = flush_output_buffer(This);
648     if (FAILED(hr))
649         return hr;
650
651     SysReAllocString(&This->encoding, encoding);
652     This->xml_enc = enc;
653
654     TRACE("got encoding %d\n", This->xml_enc);
655     reset_output_buffer(This);
656     return S_OK;
657 }
658
659 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
660 {
661     mxwriter *This = impl_from_IMXWriter( iface );
662
663     TRACE("(%p)->(%p)\n", This, encoding);
664
665     if (!encoding) return E_POINTER;
666
667     *encoding = SysAllocString(This->encoding);
668     if (!*encoding) return E_OUTOFMEMORY;
669
670     return S_OK;
671 }
672
673 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
674 {
675     mxwriter *This = impl_from_IMXWriter( iface );
676
677     TRACE("(%p)->(%d)\n", This, value);
678     return writer_set_property(This, MXWriter_BOM, value);
679 }
680
681 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
682 {
683     mxwriter *This = impl_from_IMXWriter( iface );
684
685     TRACE("(%p)->(%p)\n", This, value);
686     return writer_get_property(This, MXWriter_BOM, value);
687 }
688
689 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
690 {
691     mxwriter *This = impl_from_IMXWriter( iface );
692
693     TRACE("(%p)->(%d)\n", This, value);
694     return writer_set_property(This, MXWriter_Indent, value);
695 }
696
697 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
698 {
699     mxwriter *This = impl_from_IMXWriter( iface );
700
701     TRACE("(%p)->(%p)\n", This, value);
702     return writer_get_property(This, MXWriter_Indent, value);
703 }
704
705 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
706 {
707     mxwriter *This = impl_from_IMXWriter( iface );
708
709     TRACE("(%p)->(%d)\n", This, value);
710     return writer_set_property(This, MXWriter_Standalone, value);
711 }
712
713 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
714 {
715     mxwriter *This = impl_from_IMXWriter( iface );
716
717     TRACE("(%p)->(%p)\n", This, value);
718     return writer_get_property(This, MXWriter_Standalone, value);
719 }
720
721 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
722 {
723     mxwriter *This = impl_from_IMXWriter( iface );
724
725     TRACE("(%p)->(%d)\n", This, value);
726     return writer_set_property(This, MXWriter_OmitXmlDecl, value);
727 }
728
729 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
730 {
731     mxwriter *This = impl_from_IMXWriter( iface );
732
733     TRACE("(%p)->(%p)\n", This, value);
734     return writer_get_property(This, MXWriter_OmitXmlDecl, value);
735 }
736
737 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
738 {
739     mxwriter *This = impl_from_IMXWriter( iface );
740
741     TRACE("(%p)->(%s)\n", This, debugstr_w(version));
742
743     if (!version) return E_INVALIDARG;
744
745     SysFreeString(This->version);
746     This->version = SysAllocString(version);
747
748     return S_OK;
749 }
750
751 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
752 {
753     mxwriter *This = impl_from_IMXWriter( iface );
754
755     TRACE("(%p)->(%p)\n", This, version);
756
757     if (!version) return E_POINTER;
758
759     return return_bstr(This->version, version);
760 }
761
762 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
763 {
764     mxwriter *This = impl_from_IMXWriter( iface );
765
766     TRACE("(%p)->(%d)\n", This, value);
767     return writer_set_property(This, MXWriter_DisableEscaping, value);
768 }
769
770 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
771 {
772     mxwriter *This = impl_from_IMXWriter( iface );
773
774     TRACE("(%p)->(%p)\n", This, value);
775     return writer_get_property(This, MXWriter_DisableEscaping, value);
776 }
777
778 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
779 {
780     mxwriter *This = impl_from_IMXWriter( iface );
781     TRACE("(%p)\n", This);
782     return flush_output_buffer(This);
783 }
784
785 static const struct IMXWriterVtbl MXWriterVtbl =
786 {
787     mxwriter_QueryInterface,
788     mxwriter_AddRef,
789     mxwriter_Release,
790     mxwriter_GetTypeInfoCount,
791     mxwriter_GetTypeInfo,
792     mxwriter_GetIDsOfNames,
793     mxwriter_Invoke,
794     mxwriter_put_output,
795     mxwriter_get_output,
796     mxwriter_put_encoding,
797     mxwriter_get_encoding,
798     mxwriter_put_byteOrderMark,
799     mxwriter_get_byteOrderMark,
800     mxwriter_put_indent,
801     mxwriter_get_indent,
802     mxwriter_put_standalone,
803     mxwriter_get_standalone,
804     mxwriter_put_omitXMLDeclaration,
805     mxwriter_get_omitXMLDeclaration,
806     mxwriter_put_version,
807     mxwriter_get_version,
808     mxwriter_put_disableOutputEscaping,
809     mxwriter_get_disableOutputEscaping,
810     mxwriter_flush
811 };
812
813 /*** ISAXContentHandler ***/
814 static HRESULT WINAPI mxwriter_saxcontent_QueryInterface(
815     ISAXContentHandler *iface,
816     REFIID riid,
817     void **obj)
818 {
819     mxwriter *This = impl_from_ISAXContentHandler( iface );
820     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
821 }
822
823 static ULONG WINAPI mxwriter_saxcontent_AddRef(ISAXContentHandler *iface)
824 {
825     mxwriter *This = impl_from_ISAXContentHandler( iface );
826     return IMXWriter_AddRef(&This->IMXWriter_iface);
827 }
828
829 static ULONG WINAPI mxwriter_saxcontent_Release(ISAXContentHandler *iface)
830 {
831     mxwriter *This = impl_from_ISAXContentHandler( iface );
832     return IMXWriter_Release(&This->IMXWriter_iface);
833 }
834
835 static HRESULT WINAPI mxwriter_saxcontent_putDocumentLocator(
836     ISAXContentHandler *iface,
837     ISAXLocator *locator)
838 {
839     mxwriter *This = impl_from_ISAXContentHandler( iface );
840     FIXME("(%p)->(%p)\n", This, locator);
841     return E_NOTIMPL;
842 }
843
844 static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *iface)
845 {
846     mxwriter *This = impl_from_ISAXContentHandler( iface );
847
848     TRACE("(%p)\n", This);
849
850     /* If properties have been changed since the last "endDocument" call
851      * we need to reset the output buffer. If we don't the output buffer
852      * could end up with multiple XML documents in it, plus this seems to
853      * be how Windows works.
854      */
855     if (This->prop_changed) {
856         reset_output_buffer(This);
857         This->prop_changed = FALSE;
858     }
859
860     if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
861
862     write_prolog_buffer(This);
863
864     if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
865         static const char utf16BOM[] = {0xff,0xfe};
866
867         if (This->props[MXWriter_BOM] == VARIANT_TRUE)
868             /* Windows passes a NULL pointer as the pcbWritten parameter and
869              * ignores any error codes returned from this Write call.
870              */
871             IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
872     }
873
874     return S_OK;
875 }
876
877 static HRESULT WINAPI mxwriter_saxcontent_endDocument(ISAXContentHandler *iface)
878 {
879     mxwriter *This = impl_from_ISAXContentHandler( iface );
880     TRACE("(%p)\n", This);
881     This->prop_changed = FALSE;
882     return flush_output_buffer(This);
883 }
884
885 static HRESULT WINAPI mxwriter_saxcontent_startPrefixMapping(
886     ISAXContentHandler *iface,
887     const WCHAR *prefix,
888     int nprefix,
889     const WCHAR *uri,
890     int nuri)
891 {
892     mxwriter *This = impl_from_ISAXContentHandler( iface );
893     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
894     return E_NOTIMPL;
895 }
896
897 static HRESULT WINAPI mxwriter_saxcontent_endPrefixMapping(
898     ISAXContentHandler *iface,
899     const WCHAR *prefix,
900     int nprefix)
901 {
902     mxwriter *This = impl_from_ISAXContentHandler( iface );
903     FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
904     return E_NOTIMPL;
905 }
906
907 static HRESULT WINAPI mxwriter_saxcontent_startElement(
908     ISAXContentHandler *iface,
909     const WCHAR *namespaceUri,
910     int nnamespaceUri,
911     const WCHAR *local_name,
912     int nlocal_name,
913     const WCHAR *QName,
914     int nQName,
915     ISAXAttributes *attr)
916 {
917     mxwriter *This = impl_from_ISAXContentHandler( iface );
918     static const WCHAR ltW[] = {'<'};
919
920     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
921         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
922
923     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
924         return E_INVALIDARG;
925
926     close_element_starttag(This);
927     set_element_name(This, QName ? QName  : emptyW,
928                            QName ? nQName : 0);
929
930     write_output_buffer(This->buffer, ltW, 1);
931     write_output_buffer(This->buffer, QName, nQName);
932
933     if (attr)
934     {
935         HRESULT hr;
936         INT length;
937         INT i;
938
939         hr = ISAXAttributes_getLength(attr, &length);
940         if (FAILED(hr)) return hr;
941
942         for (i = 0; i < length; i++)
943         {
944             static const WCHAR spaceW[] = {' '};
945             static const WCHAR eqqW[] = {'=','\"'};
946             static const WCHAR quotW[] = {'\"'};
947             const WCHAR *str;
948             WCHAR *escaped;
949             INT len = 0;
950
951             hr = ISAXAttributes_getQName(attr, i, &str, &len);
952             if (FAILED(hr)) return hr;
953
954             /* space separator in front of every attribute */
955             write_output_buffer(This->buffer, spaceW, 1);
956             write_output_buffer(This->buffer, str, len);
957
958             write_output_buffer(This->buffer, eqqW, 2);
959
960             len = 0;
961             hr = ISAXAttributes_getValue(attr, i, &str, &len);
962             if (FAILED(hr)) return hr;
963
964             escaped = get_escaped_string(str, &len);
965             write_output_buffer(This->buffer, escaped, len);
966             heap_free(escaped);
967
968             write_output_buffer(This->buffer, quotW, 1);
969         }
970     }
971
972     return S_OK;
973 }
974
975 static HRESULT WINAPI mxwriter_saxcontent_endElement(
976     ISAXContentHandler *iface,
977     const WCHAR *namespaceUri,
978     int nnamespaceUri,
979     const WCHAR * local_name,
980     int nlocal_name,
981     const WCHAR *QName,
982     int nQName)
983 {
984     mxwriter *This = impl_from_ISAXContentHandler( iface );
985
986     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
987         debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
988
989     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
990         return E_INVALIDARG;
991
992     if (This->element && QName && !strncmpW(This->element, QName, nQName))
993     {
994         static const WCHAR closeW[] = {'/','>'};
995
996         write_output_buffer(This->buffer, closeW, 2);
997     }
998     else
999     {
1000         static const WCHAR closetagW[] = {'<','/'};
1001         static const WCHAR gtW[] = {'>'};
1002
1003         write_output_buffer(This->buffer, closetagW, 2);
1004         write_output_buffer(This->buffer, QName, nQName);
1005         write_output_buffer(This->buffer, gtW, 1);
1006     }
1007
1008     set_element_name(This, NULL, 0);
1009
1010     return S_OK;
1011 }
1012
1013 static HRESULT WINAPI mxwriter_saxcontent_characters(
1014     ISAXContentHandler *iface,
1015     const WCHAR *chars,
1016     int nchars)
1017 {
1018     mxwriter *This = impl_from_ISAXContentHandler( iface );
1019
1020     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1021
1022     if (!chars) return E_INVALIDARG;
1023
1024     close_element_starttag(This);
1025     set_element_name(This, NULL, 0);
1026
1027     if (nchars)
1028         write_output_buffer(This->buffer, chars, nchars);
1029
1030     return S_OK;
1031 }
1032
1033 static HRESULT WINAPI mxwriter_saxcontent_ignorableWhitespace(
1034     ISAXContentHandler *iface,
1035     const WCHAR *chars,
1036     int nchars)
1037 {
1038     mxwriter *This = impl_from_ISAXContentHandler( iface );
1039     FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1040     return E_NOTIMPL;
1041 }
1042
1043 static HRESULT WINAPI mxwriter_saxcontent_processingInstruction(
1044     ISAXContentHandler *iface,
1045     const WCHAR *target,
1046     int ntarget,
1047     const WCHAR *data,
1048     int ndata)
1049 {
1050     mxwriter *This = impl_from_ISAXContentHandler( iface );
1051     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1052     return E_NOTIMPL;
1053 }
1054
1055 static HRESULT WINAPI mxwriter_saxcontent_skippedEntity(
1056     ISAXContentHandler *iface,
1057     const WCHAR *name,
1058     int nname)
1059 {
1060     mxwriter *This = impl_from_ISAXContentHandler( iface );
1061     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1062     return E_NOTIMPL;
1063 }
1064
1065 static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl =
1066 {
1067     mxwriter_saxcontent_QueryInterface,
1068     mxwriter_saxcontent_AddRef,
1069     mxwriter_saxcontent_Release,
1070     mxwriter_saxcontent_putDocumentLocator,
1071     mxwriter_saxcontent_startDocument,
1072     mxwriter_saxcontent_endDocument,
1073     mxwriter_saxcontent_startPrefixMapping,
1074     mxwriter_saxcontent_endPrefixMapping,
1075     mxwriter_saxcontent_startElement,
1076     mxwriter_saxcontent_endElement,
1077     mxwriter_saxcontent_characters,
1078     mxwriter_saxcontent_ignorableWhitespace,
1079     mxwriter_saxcontent_processingInstruction,
1080     mxwriter_saxcontent_skippedEntity
1081 };
1082
1083 static const tid_t mxwriter_iface_tids[] = {
1084     IMXWriter_tid,
1085     0
1086 };
1087
1088 static dispex_static_data_t mxwriter_dispex = {
1089     NULL,
1090     IMXWriter_tid,
1091     NULL,
1092     mxwriter_iface_tids
1093 };
1094
1095 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1096 {
1097     static const WCHAR version10W[] = {'1','.','0',0};
1098     mxwriter *This;
1099     HRESULT hr;
1100
1101     TRACE("(%p, %p)\n", outer, ppObj);
1102
1103     if (outer) FIXME("support aggregation, outer\n");
1104
1105     This = heap_alloc( sizeof (*This) );
1106     if(!This)
1107         return E_OUTOFMEMORY;
1108
1109     This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1110     This->ISAXContentHandler_iface.lpVtbl = &mxwriter_saxcontent_vtbl;
1111     This->ref = 1;
1112     This->class_version = version;
1113
1114     This->props[MXWriter_BOM] = VARIANT_TRUE;
1115     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1116     This->props[MXWriter_Indent] = VARIANT_FALSE;
1117     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1118     This->props[MXWriter_Standalone] = VARIANT_FALSE;
1119     This->prop_changed = FALSE;
1120     This->encoding = SysAllocString(utf16W);
1121     This->version  = SysAllocString(version10W);
1122     This->xml_enc  = XmlEncoding_UTF16;
1123
1124     This->element = NULL;
1125
1126     This->dest = NULL;
1127     This->dest_written = 0;
1128
1129     hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1130     if (hr != S_OK) {
1131         SysFreeString(This->encoding);
1132         SysFreeString(This->version);
1133         heap_free(This);
1134         return hr;
1135     }
1136
1137     init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1138
1139     *ppObj = &This->IMXWriter_iface;
1140
1141     TRACE("returning iface %p\n", *ppObj);
1142
1143     return S_OK;
1144 }