msxml3: Fix some test failures on Windows 8.
[wine] / dlls / msxml3 / mxwriter.c
1 /*
2  *    MXWriter implementation
3  *
4  * Copyright 2011-2012 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 emptyW[] = {0};
43 static const WCHAR spaceW[] = {' '};
44 static const WCHAR quotW[]  = {'\"'};
45 static const WCHAR closetagW[] = {'>','\r','\n'};
46
47 /* should be ordered as encoding names are sorted */
48 typedef enum
49 {
50     XmlEncoding_ISO_8859_1 = 0,
51     XmlEncoding_ISO_8859_13,
52     XmlEncoding_ISO_8859_15,
53     XmlEncoding_ISO_8859_2,
54     XmlEncoding_ISO_8859_3,
55     XmlEncoding_ISO_8859_4,
56     XmlEncoding_ISO_8859_5,
57     XmlEncoding_ISO_8859_7,
58     XmlEncoding_ISO_8859_9,
59     XmlEncoding_UTF16,
60     XmlEncoding_UTF8,
61     XmlEncoding_Unknown
62 } xml_encoding;
63
64 struct xml_encoding_data
65 {
66     const WCHAR *encoding;
67     xml_encoding enc;
68     UINT cp;
69 };
70
71 static const WCHAR iso_8859_1W[] = {'i','s','o','-','8','8','5','9','-','1',0};
72 static const WCHAR iso_8859_2W[] = {'i','s','o','-','8','8','5','9','-','2',0};
73 static const WCHAR iso_8859_3W[] = {'i','s','o','-','8','8','5','9','-','3',0};
74 static const WCHAR iso_8859_4W[] = {'i','s','o','-','8','8','5','9','-','4',0};
75 static const WCHAR iso_8859_5W[] = {'i','s','o','-','8','8','5','9','-','5',0};
76 static const WCHAR iso_8859_7W[] = {'i','s','o','-','8','8','5','9','-','7',0};
77 static const WCHAR iso_8859_9W[] = {'i','s','o','-','8','8','5','9','-','9',0};
78 static const WCHAR iso_8859_13W[] = {'i','s','o','-','8','8','5','9','-','1','3',0};
79 static const WCHAR iso_8859_15W[] = {'i','s','o','-','8','8','5','9','-','1','5',0};
80 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
81 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
82
83 static const struct xml_encoding_data xml_encoding_map[] = {
84     { iso_8859_1W,  XmlEncoding_ISO_8859_1,  28591 },
85     { iso_8859_13W, XmlEncoding_ISO_8859_13, 28603 },
86     { iso_8859_15W, XmlEncoding_ISO_8859_15, 28605 },
87     { iso_8859_2W,  XmlEncoding_ISO_8859_2,  28592 },
88     { iso_8859_3W,  XmlEncoding_ISO_8859_3,  28593 },
89     { iso_8859_4W,  XmlEncoding_ISO_8859_4,  28594 },
90     { iso_8859_5W,  XmlEncoding_ISO_8859_5,  28595 },
91     { iso_8859_7W,  XmlEncoding_ISO_8859_7,  28597 },
92     { iso_8859_9W,  XmlEncoding_ISO_8859_9,  28599 },
93     { utf16W,       XmlEncoding_UTF16,          ~0 },
94     { utf8W,        XmlEncoding_UTF8,      CP_UTF8 }
95 };
96
97 typedef enum
98 {
99     OutputBuffer_Native  = 0x001,
100     OutputBuffer_Encoded = 0x010,
101     OutputBuffer_Both    = 0x100
102 } output_mode;
103
104 typedef enum
105 {
106     MXWriter_BOM = 0,
107     MXWriter_DisableEscaping,
108     MXWriter_Indent,
109     MXWriter_OmitXmlDecl,
110     MXWriter_Standalone,
111     MXWriter_LastProp
112 } mxwriter_prop;
113
114 typedef enum
115 {
116     EscapeValue,
117     EscapeText
118 } escape_mode;
119
120 typedef struct
121 {
122     char *data;
123     unsigned int allocated;
124     unsigned int written;
125 } encoded_buffer;
126
127 typedef struct
128 {
129     encoded_buffer utf16;
130     encoded_buffer encoded;
131     UINT code_page;
132 } output_buffer;
133
134 typedef struct
135 {
136     DispatchEx dispex;
137     IMXWriter IMXWriter_iface;
138     ISAXContentHandler ISAXContentHandler_iface;
139     ISAXLexicalHandler ISAXLexicalHandler_iface;
140     ISAXDeclHandler    ISAXDeclHandler_iface;
141
142     LONG ref;
143     MSXML_VERSION class_version;
144
145     VARIANT_BOOL props[MXWriter_LastProp];
146     BOOL prop_changed;
147     BOOL cdata;
148
149     BSTR version;
150
151     BSTR encoding; /* exact property value */
152     xml_encoding xml_enc;
153
154     /* contains a pending (or not closed yet) element name or NULL if
155        we don't have to close */
156     BSTR element;
157
158     IStream *dest;
159     ULONG dest_written;
160
161     output_buffer *buffer;
162 } mxwriter;
163
164 typedef struct
165 {
166     BSTR qname;
167     BSTR local;
168     BSTR uri;
169     BSTR type;
170     BSTR value;
171 } mxattribute;
172
173 typedef struct
174 {
175     DispatchEx dispex;
176     IMXAttributes IMXAttributes_iface;
177     ISAXAttributes ISAXAttributes_iface;
178     IVBSAXAttributes IVBSAXAttributes_iface;
179     LONG ref;
180
181     MSXML_VERSION class_version;
182
183     mxattribute *attr;
184     int length;
185     int allocated;
186 } mxattributes;
187
188 static inline mxattributes *impl_from_IMXAttributes( IMXAttributes *iface )
189 {
190     return CONTAINING_RECORD(iface, mxattributes, IMXAttributes_iface);
191 }
192
193 static inline mxattributes *impl_from_ISAXAttributes( ISAXAttributes *iface )
194 {
195     return CONTAINING_RECORD(iface, mxattributes, ISAXAttributes_iface);
196 }
197
198 static inline mxattributes *impl_from_IVBSAXAttributes( IVBSAXAttributes *iface )
199 {
200     return CONTAINING_RECORD(iface, mxattributes, IVBSAXAttributes_iface);
201 }
202
203 static HRESULT mxattributes_grow(mxattributes *This)
204 {
205     if (This->length < This->allocated) return S_OK;
206
207     This->allocated *= 2;
208     This->attr = heap_realloc(This->attr, This->allocated*sizeof(mxattribute));
209
210     return This->attr ? S_OK : E_OUTOFMEMORY;
211 }
212
213 static xml_encoding parse_encoding_name(const WCHAR *encoding)
214 {
215     int min, max, n, c;
216
217     min = 0;
218     max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
219
220     while (min <= max)
221     {
222         n = (min+max)/2;
223
224         c = strcmpiW(xml_encoding_map[n].encoding, encoding);
225         if (!c)
226             return xml_encoding_map[n].enc;
227
228         if (c > 0)
229             max = n-1;
230         else
231             min = n+1;
232     }
233
234     return XmlEncoding_Unknown;
235 }
236
237 static HRESULT init_encoded_buffer(encoded_buffer *buffer)
238 {
239     const int initial_len = 0x2000;
240     buffer->data = heap_alloc(initial_len);
241     if (!buffer->data) return E_OUTOFMEMORY;
242
243     memset(buffer->data, 0, 4);
244     buffer->allocated = initial_len;
245     buffer->written = 0;
246
247     return S_OK;
248 }
249
250 static void free_encoded_buffer(encoded_buffer *buffer)
251 {
252     heap_free(buffer->data);
253 }
254
255 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
256 {
257     const struct xml_encoding_data *data;
258
259     if (encoding == XmlEncoding_Unknown)
260     {
261         FIXME("unsupported encoding %d\n", encoding);
262         return E_NOTIMPL;
263     }
264
265     data = &xml_encoding_map[encoding];
266     *cp = data->cp;
267
268     return S_OK;
269 }
270
271 static HRESULT alloc_output_buffer(xml_encoding encoding, output_buffer **buffer)
272 {
273     output_buffer *ret;
274     HRESULT hr;
275
276     ret = heap_alloc(sizeof(*ret));
277     if (!ret) return E_OUTOFMEMORY;
278
279     hr = get_code_page(encoding, &ret->code_page);
280     if (hr != S_OK) {
281         heap_free(ret);
282         return hr;
283     }
284
285     hr = init_encoded_buffer(&ret->utf16);
286     if (hr != S_OK) {
287         heap_free(ret);
288         return hr;
289     }
290
291     /* currently we always create a default output buffer that is UTF-16 only,
292        but it's possible to allocate with specific encoding too */
293     if (encoding != XmlEncoding_UTF16) {
294         hr = init_encoded_buffer(&ret->encoded);
295         if (hr != S_OK) {
296             free_encoded_buffer(&ret->utf16);
297             heap_free(ret);
298             return hr;
299         }
300     }
301     else
302         memset(&ret->encoded, 0, sizeof(ret->encoded));
303
304     *buffer = ret;
305
306     return S_OK;
307 }
308
309 static void free_output_buffer(output_buffer *buffer)
310 {
311     free_encoded_buffer(&buffer->encoded);
312     free_encoded_buffer(&buffer->utf16);
313     heap_free(buffer);
314 }
315
316 static void grow_buffer(encoded_buffer *buffer, int length)
317 {
318     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
319     if (buffer->allocated < buffer->written + length + 4)
320     {
321         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
322         buffer->data = heap_realloc(buffer->data, grown_size);
323         buffer->allocated = grown_size;
324     }
325 }
326
327 static HRESULT write_output_buffer_mode(output_buffer *buffer, output_mode mode, const WCHAR *data, int len)
328 {
329     int length;
330     char *ptr;
331
332     if (mode & (OutputBuffer_Encoded | OutputBuffer_Both)) {
333         if (buffer->code_page != ~0)
334         {
335             length = WideCharToMultiByte(buffer->code_page, 0, data, len, NULL, 0, NULL, NULL);
336             grow_buffer(&buffer->encoded, length);
337             ptr = buffer->encoded.data + buffer->encoded.written;
338             length = WideCharToMultiByte(buffer->code_page, 0, data, len, ptr, length, NULL, NULL);
339             buffer->encoded.written += len == -1 ? length-1 : length;
340         }
341     }
342
343     if (mode & (OutputBuffer_Native | OutputBuffer_Both)) {
344         /* WCHAR data just copied */
345         length = len == -1 ? strlenW(data) : len;
346         if (length)
347         {
348             length *= sizeof(WCHAR);
349
350             grow_buffer(&buffer->utf16, length);
351             ptr = buffer->utf16.data + buffer->utf16.written;
352
353             memcpy(ptr, data, length);
354             buffer->utf16.written += length;
355             ptr += length;
356             /* null termination */
357             memset(ptr, 0, sizeof(WCHAR));
358         }
359     }
360
361     return S_OK;
362 }
363
364 static HRESULT write_output_buffer(output_buffer *buffer, const WCHAR *data, int len)
365 {
366     return write_output_buffer_mode(buffer, OutputBuffer_Both, data, len);
367 }
368
369 static HRESULT write_output_buffer_quoted(output_buffer *buffer, const WCHAR *data, int len)
370 {
371     write_output_buffer(buffer, quotW, 1);
372     write_output_buffer(buffer, data, len);
373     write_output_buffer(buffer, quotW, 1);
374
375     return S_OK;
376 }
377
378 /* frees buffer data, reallocates with a default lengths */
379 static void close_output_buffer(mxwriter *This)
380 {
381     heap_free(This->buffer->utf16.data);
382     heap_free(This->buffer->encoded.data);
383     init_encoded_buffer(&This->buffer->utf16);
384     init_encoded_buffer(&This->buffer->encoded);
385     get_code_page(This->xml_enc, &This->buffer->code_page);
386 }
387
388 /* escapes special characters like:
389    '<' -> "&lt;"
390    '&' -> "&amp;"
391    '"' -> "&quot;"
392    '>' -> "&gt;"
393 */
394 static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
395 {
396     static const WCHAR ltW[]    = {'&','l','t',';'};
397     static const WCHAR ampW[]   = {'&','a','m','p',';'};
398     static const WCHAR equotW[] = {'&','q','u','o','t',';'};
399     static const WCHAR gtW[]    = {'&','g','t',';'};
400
401     const int default_alloc = 100;
402     const int grow_thresh = 10;
403     int p = *len, conv_len;
404     WCHAR *ptr, *ret;
405
406     /* default buffer size to something if length is unknown */
407     conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
408     ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
409
410     while (*str && p)
411     {
412         if (ptr - ret > conv_len - grow_thresh)
413         {
414             int written = ptr - ret;
415             conv_len *= 2;
416             ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
417             ptr += written;
418         }
419
420         switch (*str)
421         {
422         case '<':
423             memcpy(ptr, ltW, sizeof(ltW));
424             ptr += sizeof(ltW)/sizeof(WCHAR);
425             break;
426         case '&':
427             memcpy(ptr, ampW, sizeof(ampW));
428             ptr += sizeof(ampW)/sizeof(WCHAR);
429             break;
430         case '>':
431             memcpy(ptr, gtW, sizeof(gtW));
432             ptr += sizeof(gtW)/sizeof(WCHAR);
433             break;
434         case '"':
435             if (mode == EscapeValue)
436             {
437                 memcpy(ptr, equotW, sizeof(equotW));
438                 ptr += sizeof(equotW)/sizeof(WCHAR);
439                 break;
440             }
441             /* fallthrough for text mode */
442         default:
443             *ptr++ = *str;
444             break;
445         }
446
447         str++;
448         if (*len != -1) p--;
449     }
450
451     if (*len != -1) *len = ptr-ret;
452     *++ptr = 0;
453
454     return ret;
455 }
456
457 static void write_prolog_buffer(const mxwriter *This)
458 {
459     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
460     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
461     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
462     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
463     static const WCHAR noW[] = {'n','o','\"','?','>'};
464     static const WCHAR crlfW[] = {'\r','\n'};
465
466     /* version */
467     write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
468     write_output_buffer_quoted(This->buffer, This->version, -1);
469
470     /* encoding */
471     write_output_buffer(This->buffer, encodingW, sizeof(encodingW)/sizeof(WCHAR));
472
473     /* always write UTF-16 to WCHAR buffer */
474     write_output_buffer_mode(This->buffer, OutputBuffer_Native, utf16W, sizeof(utf16W)/sizeof(WCHAR) - 1);
475     write_output_buffer_mode(This->buffer, OutputBuffer_Encoded, This->encoding, -1);
476     write_output_buffer(This->buffer, quotW, 1);
477
478     /* standalone */
479     write_output_buffer(This->buffer, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
480     if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
481         write_output_buffer(This->buffer, yesW, sizeof(yesW)/sizeof(WCHAR));
482     else
483         write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
484
485     write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
486 }
487
488 /* Attempts to the write data from the mxwriter's buffer to
489  * the destination stream (if there is one).
490  */
491 static HRESULT write_data_to_stream(mxwriter *This)
492 {
493     encoded_buffer *buffer;
494     ULONG written = 0;
495     HRESULT hr;
496
497     if (!This->dest)
498         return S_OK;
499
500     if (This->xml_enc != XmlEncoding_UTF16)
501         buffer = &This->buffer->encoded;
502     else
503         buffer = &This->buffer->utf16;
504
505     if (This->dest_written > buffer->written) {
506         ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->written);
507         return E_FAIL;
508     } else if (This->dest_written == buffer->written && This->xml_enc != XmlEncoding_UTF8)
509         /* Windows seems to make an empty write call when the encoding is UTF-8 and
510          * all the data has been written to the stream. It doesn't seem make this call
511          * for any other encodings.
512          */
513         return S_OK;
514
515     /* Write the current content from the output buffer into 'dest'.
516      * TODO: Check what Windows does if the IStream doesn't write all of
517      *       the data we give it at once.
518      */
519     hr = IStream_Write(This->dest, buffer->data+This->dest_written,
520                          buffer->written-This->dest_written, &written);
521     if (FAILED(hr)) {
522         WARN("Failed to write data to IStream (0x%08x)\n", hr);
523         return hr;
524     }
525
526     This->dest_written += written;
527     return hr;
528 }
529
530 /* Newly added element start tag left unclosed cause for empty elements
531    we have to close it differently. */
532 static void close_element_starttag(const mxwriter *This)
533 {
534     static const WCHAR gtW[] = {'>'};
535     if (!This->element) return;
536     write_output_buffer(This->buffer, gtW, 1);
537 }
538
539 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
540 {
541     SysFreeString(This->element);
542     if (name)
543         This->element = len != -1 ? SysAllocStringLen(name, len) : SysAllocString(name);
544     else
545         This->element = NULL;
546 }
547
548 static inline HRESULT flush_output_buffer(mxwriter *This)
549 {
550     close_element_starttag(This);
551     set_element_name(This, NULL, 0);
552     This->cdata = FALSE;
553     return write_data_to_stream(This);
554 }
555
556 /* Resets the mxwriter's output buffer by closing it, then creating a new
557  * output buffer using the given encoding.
558  */
559 static inline void reset_output_buffer(mxwriter *This)
560 {
561     close_output_buffer(This);
562     This->dest_written = 0;
563 }
564
565 static HRESULT writer_set_property(mxwriter *writer, mxwriter_prop property, VARIANT_BOOL value)
566 {
567     writer->props[property] = value;
568     writer->prop_changed = TRUE;
569     return S_OK;
570 }
571
572 static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop property, VARIANT_BOOL *value)
573 {
574     if (!value) return E_POINTER;
575     *value = writer->props[property];
576     return S_OK;
577 }
578
579 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
580 {
581     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
582 }
583
584 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
585 {
586     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
587 }
588
589 static inline mxwriter *impl_from_ISAXLexicalHandler(ISAXLexicalHandler *iface)
590 {
591     return CONTAINING_RECORD(iface, mxwriter, ISAXLexicalHandler_iface);
592 }
593
594 static inline mxwriter *impl_from_ISAXDeclHandler(ISAXDeclHandler *iface)
595 {
596     return CONTAINING_RECORD(iface, mxwriter, ISAXDeclHandler_iface);
597 }
598
599 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
600 {
601     mxwriter *This = impl_from_IMXWriter( iface );
602
603     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
604
605     *obj = NULL;
606
607     if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
608          IsEqualGUID( riid, &IID_IDispatch ) ||
609          IsEqualGUID( riid, &IID_IUnknown ) )
610     {
611         *obj = &This->IMXWriter_iface;
612     }
613     else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
614     {
615         *obj = &This->ISAXContentHandler_iface;
616     }
617     else if ( IsEqualGUID( riid, &IID_ISAXLexicalHandler ) )
618     {
619         *obj = &This->ISAXLexicalHandler_iface;
620     }
621     else if ( IsEqualGUID( riid, &IID_ISAXDeclHandler ) )
622     {
623         *obj = &This->ISAXDeclHandler_iface;
624     }
625     else if (dispex_query_interface(&This->dispex, riid, obj))
626     {
627         return *obj ? S_OK : E_NOINTERFACE;
628     }
629     else
630     {
631         ERR("interface %s not implemented\n", debugstr_guid(riid));
632         *obj = NULL;
633         return E_NOINTERFACE;
634     }
635
636     IMXWriter_AddRef(iface);
637     return S_OK;
638 }
639
640 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
641 {
642     mxwriter *This = impl_from_IMXWriter( iface );
643     LONG ref = InterlockedIncrement(&This->ref);
644
645     TRACE("(%p)->(%d)\n", This, ref);
646
647     return ref;
648 }
649
650 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
651 {
652     mxwriter *This = impl_from_IMXWriter( iface );
653     ULONG ref = InterlockedDecrement(&This->ref);
654
655     TRACE("(%p)->(%d)\n", This, ref);
656
657     if(!ref)
658     {
659         /* Windows flushes the buffer when the interface is destroyed. */
660         flush_output_buffer(This);
661         free_output_buffer(This->buffer);
662
663         if (This->dest) IStream_Release(This->dest);
664         SysFreeString(This->version);
665         SysFreeString(This->encoding);
666
667         SysFreeString(This->element);
668         release_dispex(&This->dispex);
669         heap_free(This);
670     }
671
672     return ref;
673 }
674
675 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
676 {
677     mxwriter *This = impl_from_IMXWriter( iface );
678     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
679 }
680
681 static HRESULT WINAPI mxwriter_GetTypeInfo(
682     IMXWriter *iface,
683     UINT iTInfo, LCID lcid,
684     ITypeInfo** ppTInfo )
685 {
686     mxwriter *This = impl_from_IMXWriter( iface );
687     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
688         iTInfo, lcid, ppTInfo);
689 }
690
691 static HRESULT WINAPI mxwriter_GetIDsOfNames(
692     IMXWriter *iface,
693     REFIID riid, LPOLESTR* rgszNames,
694     UINT cNames, LCID lcid, DISPID* rgDispId )
695 {
696     mxwriter *This = impl_from_IMXWriter( iface );
697     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
698         riid, rgszNames, cNames, lcid, rgDispId);
699 }
700
701 static HRESULT WINAPI mxwriter_Invoke(
702     IMXWriter *iface,
703     DISPID dispIdMember, REFIID riid, LCID lcid,
704     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
705     EXCEPINFO* pExcepInfo, UINT* puArgErr )
706 {
707     mxwriter *This = impl_from_IMXWriter( iface );
708     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
709         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
710 }
711
712 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
713 {
714     mxwriter *This = impl_from_IMXWriter( iface );
715     HRESULT hr;
716
717     TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
718
719     hr = flush_output_buffer(This);
720     if (FAILED(hr))
721         return hr;
722
723     switch (V_VT(&dest))
724     {
725     case VT_EMPTY:
726     {
727         if (This->dest) IStream_Release(This->dest);
728         This->dest = NULL;
729         reset_output_buffer(This);
730         break;
731     }
732     case VT_UNKNOWN:
733     {
734         IStream *stream;
735
736         hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
737         if (hr == S_OK)
738         {
739             /* Recreate the output buffer to make sure it's using the correct encoding. */
740             reset_output_buffer(This);
741
742             if (This->dest) IStream_Release(This->dest);
743             This->dest = stream;
744             break;
745         }
746
747         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
748         return E_NOTIMPL;
749     }
750     default:
751         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
752         return E_NOTIMPL;
753     }
754
755     return S_OK;
756 }
757
758 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
759 {
760     mxwriter *This = impl_from_IMXWriter( iface );
761
762     TRACE("(%p)->(%p)\n", This, dest);
763
764     if (!dest) return E_POINTER;
765
766     if (!This->dest)
767     {
768         HRESULT hr = flush_output_buffer(This);
769         if (FAILED(hr))
770             return hr;
771
772         V_VT(dest)   = VT_BSTR;
773         V_BSTR(dest) = SysAllocString((WCHAR*)This->buffer->utf16.data);
774
775         return S_OK;
776     }
777
778     /* we only support IStream output so far */
779     V_VT(dest) = VT_UNKNOWN;
780     V_UNKNOWN(dest) = (IUnknown*)This->dest;
781     IStream_AddRef(This->dest);
782
783     return S_OK;
784 }
785
786 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
787 {
788     mxwriter *This = impl_from_IMXWriter( iface );
789     xml_encoding enc;
790     HRESULT hr;
791
792     TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
793
794     enc = parse_encoding_name(encoding);
795     if (enc == XmlEncoding_Unknown)
796     {
797         FIXME("unsupported encoding %s\n", debugstr_w(encoding));
798         return E_INVALIDARG;
799     }
800
801     hr = flush_output_buffer(This);
802     if (FAILED(hr))
803         return hr;
804
805     SysReAllocString(&This->encoding, encoding);
806     This->xml_enc = enc;
807
808     TRACE("got encoding %d\n", This->xml_enc);
809     reset_output_buffer(This);
810     return S_OK;
811 }
812
813 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
814 {
815     mxwriter *This = impl_from_IMXWriter( iface );
816
817     TRACE("(%p)->(%p)\n", This, encoding);
818
819     if (!encoding) return E_POINTER;
820
821     *encoding = SysAllocString(This->encoding);
822     if (!*encoding) return E_OUTOFMEMORY;
823
824     return S_OK;
825 }
826
827 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
828 {
829     mxwriter *This = impl_from_IMXWriter( iface );
830
831     TRACE("(%p)->(%d)\n", This, value);
832     return writer_set_property(This, MXWriter_BOM, value);
833 }
834
835 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
836 {
837     mxwriter *This = impl_from_IMXWriter( iface );
838
839     TRACE("(%p)->(%p)\n", This, value);
840     return writer_get_property(This, MXWriter_BOM, value);
841 }
842
843 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
844 {
845     mxwriter *This = impl_from_IMXWriter( iface );
846
847     TRACE("(%p)->(%d)\n", This, value);
848     return writer_set_property(This, MXWriter_Indent, value);
849 }
850
851 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
852 {
853     mxwriter *This = impl_from_IMXWriter( iface );
854
855     TRACE("(%p)->(%p)\n", This, value);
856     return writer_get_property(This, MXWriter_Indent, value);
857 }
858
859 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
860 {
861     mxwriter *This = impl_from_IMXWriter( iface );
862
863     TRACE("(%p)->(%d)\n", This, value);
864     return writer_set_property(This, MXWriter_Standalone, value);
865 }
866
867 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
868 {
869     mxwriter *This = impl_from_IMXWriter( iface );
870
871     TRACE("(%p)->(%p)\n", This, value);
872     return writer_get_property(This, MXWriter_Standalone, value);
873 }
874
875 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
876 {
877     mxwriter *This = impl_from_IMXWriter( iface );
878
879     TRACE("(%p)->(%d)\n", This, value);
880     return writer_set_property(This, MXWriter_OmitXmlDecl, value);
881 }
882
883 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
884 {
885     mxwriter *This = impl_from_IMXWriter( iface );
886
887     TRACE("(%p)->(%p)\n", This, value);
888     return writer_get_property(This, MXWriter_OmitXmlDecl, value);
889 }
890
891 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
892 {
893     mxwriter *This = impl_from_IMXWriter( iface );
894
895     TRACE("(%p)->(%s)\n", This, debugstr_w(version));
896
897     if (!version) return E_INVALIDARG;
898
899     SysFreeString(This->version);
900     This->version = SysAllocString(version);
901
902     return S_OK;
903 }
904
905 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
906 {
907     mxwriter *This = impl_from_IMXWriter( iface );
908
909     TRACE("(%p)->(%p)\n", This, version);
910
911     if (!version) return E_POINTER;
912
913     return return_bstr(This->version, version);
914 }
915
916 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
917 {
918     mxwriter *This = impl_from_IMXWriter( iface );
919
920     TRACE("(%p)->(%d)\n", This, value);
921     return writer_set_property(This, MXWriter_DisableEscaping, value);
922 }
923
924 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
925 {
926     mxwriter *This = impl_from_IMXWriter( iface );
927
928     TRACE("(%p)->(%p)\n", This, value);
929     return writer_get_property(This, MXWriter_DisableEscaping, value);
930 }
931
932 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
933 {
934     mxwriter *This = impl_from_IMXWriter( iface );
935     TRACE("(%p)\n", This);
936     return flush_output_buffer(This);
937 }
938
939 static const struct IMXWriterVtbl MXWriterVtbl =
940 {
941     mxwriter_QueryInterface,
942     mxwriter_AddRef,
943     mxwriter_Release,
944     mxwriter_GetTypeInfoCount,
945     mxwriter_GetTypeInfo,
946     mxwriter_GetIDsOfNames,
947     mxwriter_Invoke,
948     mxwriter_put_output,
949     mxwriter_get_output,
950     mxwriter_put_encoding,
951     mxwriter_get_encoding,
952     mxwriter_put_byteOrderMark,
953     mxwriter_get_byteOrderMark,
954     mxwriter_put_indent,
955     mxwriter_get_indent,
956     mxwriter_put_standalone,
957     mxwriter_get_standalone,
958     mxwriter_put_omitXMLDeclaration,
959     mxwriter_get_omitXMLDeclaration,
960     mxwriter_put_version,
961     mxwriter_get_version,
962     mxwriter_put_disableOutputEscaping,
963     mxwriter_get_disableOutputEscaping,
964     mxwriter_flush
965 };
966
967 /*** ISAXContentHandler ***/
968 static HRESULT WINAPI SAXContentHandler_QueryInterface(
969     ISAXContentHandler *iface,
970     REFIID riid,
971     void **obj)
972 {
973     mxwriter *This = impl_from_ISAXContentHandler( iface );
974     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
975 }
976
977 static ULONG WINAPI SAXContentHandler_AddRef(ISAXContentHandler *iface)
978 {
979     mxwriter *This = impl_from_ISAXContentHandler( iface );
980     return IMXWriter_AddRef(&This->IMXWriter_iface);
981 }
982
983 static ULONG WINAPI SAXContentHandler_Release(ISAXContentHandler *iface)
984 {
985     mxwriter *This = impl_from_ISAXContentHandler( iface );
986     return IMXWriter_Release(&This->IMXWriter_iface);
987 }
988
989 static HRESULT WINAPI SAXContentHandler_putDocumentLocator(
990     ISAXContentHandler *iface,
991     ISAXLocator *locator)
992 {
993     mxwriter *This = impl_from_ISAXContentHandler( iface );
994     FIXME("(%p)->(%p)\n", This, locator);
995     return E_NOTIMPL;
996 }
997
998 static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
999 {
1000     mxwriter *This = impl_from_ISAXContentHandler( iface );
1001
1002     TRACE("(%p)\n", This);
1003
1004     /* If properties have been changed since the last "endDocument" call
1005      * we need to reset the output buffer. If we don't the output buffer
1006      * could end up with multiple XML documents in it, plus this seems to
1007      * be how Windows works.
1008      */
1009     if (This->prop_changed) {
1010         reset_output_buffer(This);
1011         This->prop_changed = FALSE;
1012     }
1013
1014     if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
1015
1016     write_prolog_buffer(This);
1017
1018     if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
1019         static const char utf16BOM[] = {0xff,0xfe};
1020
1021         if (This->props[MXWriter_BOM] == VARIANT_TRUE)
1022             /* Windows passes a NULL pointer as the pcbWritten parameter and
1023              * ignores any error codes returned from this Write call.
1024              */
1025             IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
1026     }
1027
1028     return S_OK;
1029 }
1030
1031 static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
1032 {
1033     mxwriter *This = impl_from_ISAXContentHandler( iface );
1034     TRACE("(%p)\n", This);
1035     This->prop_changed = FALSE;
1036     return flush_output_buffer(This);
1037 }
1038
1039 static HRESULT WINAPI SAXContentHandler_startPrefixMapping(
1040     ISAXContentHandler *iface,
1041     const WCHAR *prefix,
1042     int nprefix,
1043     const WCHAR *uri,
1044     int nuri)
1045 {
1046     mxwriter *This = impl_from_ISAXContentHandler( iface );
1047     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
1048     return E_NOTIMPL;
1049 }
1050
1051 static HRESULT WINAPI SAXContentHandler_endPrefixMapping(
1052     ISAXContentHandler *iface,
1053     const WCHAR *prefix,
1054     int nprefix)
1055 {
1056     mxwriter *This = impl_from_ISAXContentHandler( iface );
1057     FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
1058     return E_NOTIMPL;
1059 }
1060
1061 static HRESULT WINAPI SAXContentHandler_startElement(
1062     ISAXContentHandler *iface,
1063     const WCHAR *namespaceUri,
1064     int nnamespaceUri,
1065     const WCHAR *local_name,
1066     int nlocal_name,
1067     const WCHAR *QName,
1068     int nQName,
1069     ISAXAttributes *attr)
1070 {
1071     mxwriter *This = impl_from_ISAXContentHandler( iface );
1072     static const WCHAR ltW[] = {'<'};
1073
1074     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
1075         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
1076
1077     if (((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6) ||
1078         (nQName == -1 && This->class_version == MSXML6))
1079         return E_INVALIDARG;
1080
1081     close_element_starttag(This);
1082     set_element_name(This, QName ? QName  : emptyW,
1083                            QName ? nQName : 0);
1084
1085     write_output_buffer(This->buffer, ltW, 1);
1086     write_output_buffer(This->buffer, QName, nQName);
1087
1088     if (attr)
1089     {
1090         int length, i, escape;
1091         HRESULT hr;
1092
1093         hr = ISAXAttributes_getLength(attr, &length);
1094         if (FAILED(hr)) return hr;
1095
1096         escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
1097             (This->class_version == MSXML4 || This->class_version == MSXML6);
1098
1099         for (i = 0; i < length; i++)
1100         {
1101             static const WCHAR eqW[] = {'='};
1102             const WCHAR *str;
1103             int len = 0;
1104
1105             hr = ISAXAttributes_getQName(attr, i, &str, &len);
1106             if (FAILED(hr)) return hr;
1107
1108             /* space separator in front of every attribute */
1109             write_output_buffer(This->buffer, spaceW, 1);
1110             write_output_buffer(This->buffer, str, len);
1111
1112             write_output_buffer(This->buffer, eqW, 1);
1113
1114             len = 0;
1115             hr = ISAXAttributes_getValue(attr, i, &str, &len);
1116             if (FAILED(hr)) return hr;
1117
1118             if (escape)
1119             {
1120                 WCHAR *escaped = get_escaped_string(str, EscapeValue, &len);
1121                 write_output_buffer_quoted(This->buffer, escaped, len);
1122                 heap_free(escaped);
1123             }
1124             else
1125                 write_output_buffer_quoted(This->buffer, str, len);
1126         }
1127     }
1128
1129     return S_OK;
1130 }
1131
1132 static HRESULT WINAPI SAXContentHandler_endElement(
1133     ISAXContentHandler *iface,
1134     const WCHAR *namespaceUri,
1135     int nnamespaceUri,
1136     const WCHAR * local_name,
1137     int nlocal_name,
1138     const WCHAR *QName,
1139     int nQName)
1140 {
1141     mxwriter *This = impl_from_ISAXContentHandler( iface );
1142
1143     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1144         debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1145
1146     if (((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6) ||
1147          (nQName == -1 && This->class_version == MSXML6))
1148         return E_INVALIDARG;
1149
1150     if (This->element)
1151     {
1152         static const WCHAR closeW[] = {'/','>'};
1153         write_output_buffer(This->buffer, closeW, 2);
1154     }
1155     else
1156     {
1157         static const WCHAR closetagW[] = {'<','/'};
1158         static const WCHAR gtW[] = {'>'};
1159
1160         write_output_buffer(This->buffer, closetagW, 2);
1161         write_output_buffer(This->buffer, QName, nQName);
1162         write_output_buffer(This->buffer, gtW, 1);
1163     }
1164
1165     set_element_name(This, NULL, 0);
1166
1167     return S_OK;
1168 }
1169
1170 static HRESULT WINAPI SAXContentHandler_characters(
1171     ISAXContentHandler *iface,
1172     const WCHAR *chars,
1173     int nchars)
1174 {
1175     mxwriter *This = impl_from_ISAXContentHandler( iface );
1176
1177     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1178
1179     if (!chars) return E_INVALIDARG;
1180
1181     close_element_starttag(This);
1182     set_element_name(This, NULL, 0);
1183
1184     if (nchars)
1185     {
1186         if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
1187             write_output_buffer(This->buffer, chars, nchars);
1188         else
1189         {
1190             int len = nchars;
1191             WCHAR *escaped;
1192
1193             escaped = get_escaped_string(chars, EscapeText, &len);
1194             write_output_buffer(This->buffer, escaped, len);
1195             heap_free(escaped);
1196         }
1197     }
1198
1199     return S_OK;
1200 }
1201
1202 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1203     ISAXContentHandler *iface,
1204     const WCHAR *chars,
1205     int nchars)
1206 {
1207     mxwriter *This = impl_from_ISAXContentHandler( iface );
1208
1209     TRACE("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1210
1211     if (!chars) return E_INVALIDARG;
1212
1213     write_output_buffer(This->buffer, chars, nchars);
1214
1215     return S_OK;
1216 }
1217
1218 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1219     ISAXContentHandler *iface,
1220     const WCHAR *target,
1221     int ntarget,
1222     const WCHAR *data,
1223     int ndata)
1224 {
1225     mxwriter *This = impl_from_ISAXContentHandler( iface );
1226     static const WCHAR openpiW[] = {'<','?'};
1227     static const WCHAR closepiW[] = {'?','>','\r','\n'};
1228
1229     TRACE("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1230
1231     if (!target) return E_INVALIDARG;
1232
1233     write_output_buffer(This->buffer, openpiW, sizeof(openpiW)/sizeof(WCHAR));
1234
1235     if (*target)
1236         write_output_buffer(This->buffer, target, ntarget);
1237
1238     if (data && *data && ndata)
1239     {
1240         write_output_buffer(This->buffer, spaceW, 1);
1241         write_output_buffer(This->buffer, data, ndata);
1242     }
1243
1244     write_output_buffer(This->buffer, closepiW, sizeof(closepiW)/sizeof(WCHAR));
1245
1246     return S_OK;
1247 }
1248
1249 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1250     ISAXContentHandler *iface,
1251     const WCHAR *name,
1252     int nname)
1253 {
1254     mxwriter *This = impl_from_ISAXContentHandler( iface );
1255     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1256     return E_NOTIMPL;
1257 }
1258
1259 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1260 {
1261     SAXContentHandler_QueryInterface,
1262     SAXContentHandler_AddRef,
1263     SAXContentHandler_Release,
1264     SAXContentHandler_putDocumentLocator,
1265     SAXContentHandler_startDocument,
1266     SAXContentHandler_endDocument,
1267     SAXContentHandler_startPrefixMapping,
1268     SAXContentHandler_endPrefixMapping,
1269     SAXContentHandler_startElement,
1270     SAXContentHandler_endElement,
1271     SAXContentHandler_characters,
1272     SAXContentHandler_ignorableWhitespace,
1273     SAXContentHandler_processingInstruction,
1274     SAXContentHandler_skippedEntity
1275 };
1276
1277 /*** ISAXLexicalHandler ***/
1278 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1279     REFIID riid, void **obj)
1280 {
1281     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1282     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1283 }
1284
1285 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1286 {
1287     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1288     return IMXWriter_AddRef(&This->IMXWriter_iface);
1289 }
1290
1291 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1292 {
1293     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1294     return IMXWriter_Release(&This->IMXWriter_iface);
1295 }
1296
1297 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1298     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1299     const WCHAR *systemId, int systemId_len)
1300 {
1301     static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1302     static const WCHAR openintW[] = {'[','\r','\n'};
1303
1304     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1305
1306     TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1307         debugstr_wn(systemId, systemId_len));
1308
1309     if (!name) return E_INVALIDARG;
1310
1311     write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1312
1313     if (*name)
1314     {
1315         write_output_buffer(This->buffer, name, name_len);
1316         write_output_buffer(This->buffer, spaceW, 1);
1317     }
1318
1319     if (publicId)
1320     {
1321         static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1322
1323         write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1324         write_output_buffer_quoted(This->buffer, publicId, publicId_len);
1325
1326         if (!systemId) return E_INVALIDARG;
1327
1328         if (*publicId)
1329             write_output_buffer(This->buffer, spaceW, 1);
1330
1331         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1332
1333         if (*systemId)
1334             write_output_buffer(This->buffer, spaceW, 1);
1335     }
1336     else if (systemId)
1337     {
1338         static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1339
1340         write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1341         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1342         if (*systemId)
1343             write_output_buffer(This->buffer, spaceW, 1);
1344     }
1345
1346     write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1347
1348     return S_OK;
1349 }
1350
1351 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1352 {
1353     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1354     static const WCHAR closedtdW[] = {']','>','\r','\n'};
1355
1356     TRACE("(%p)\n", This);
1357
1358     write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1359
1360     return S_OK;
1361 }
1362
1363 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1364 {
1365     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1366     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1367     return E_NOTIMPL;
1368 }
1369
1370 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1371 {
1372     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1373     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1374     return E_NOTIMPL;
1375 }
1376
1377 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1378 {
1379     static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1380     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1381
1382     TRACE("(%p)\n", This);
1383
1384     write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1385     This->cdata = TRUE;
1386
1387     return S_OK;
1388 }
1389
1390 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1391 {
1392     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1393     static const WCHAR ecdataW[] = {']',']','>'};
1394
1395     TRACE("(%p)\n", This);
1396
1397     write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1398     This->cdata = FALSE;
1399
1400     return S_OK;
1401 }
1402
1403 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1404 {
1405     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1406     static const WCHAR copenW[] = {'<','!','-','-'};
1407     static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1408
1409     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1410
1411     if (!chars) return E_INVALIDARG;
1412
1413     close_element_starttag(This);
1414
1415     write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1416     if (nchars)
1417         write_output_buffer(This->buffer, chars, nchars);
1418     write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1419
1420     return S_OK;
1421 }
1422
1423 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1424 {
1425     SAXLexicalHandler_QueryInterface,
1426     SAXLexicalHandler_AddRef,
1427     SAXLexicalHandler_Release,
1428     SAXLexicalHandler_startDTD,
1429     SAXLexicalHandler_endDTD,
1430     SAXLexicalHandler_startEntity,
1431     SAXLexicalHandler_endEntity,
1432     SAXLexicalHandler_startCDATA,
1433     SAXLexicalHandler_endCDATA,
1434     SAXLexicalHandler_comment
1435 };
1436
1437 /*** ISAXDeclHandler ***/
1438 static HRESULT WINAPI SAXDeclHandler_QueryInterface(ISAXDeclHandler *iface,
1439     REFIID riid, void **obj)
1440 {
1441     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1442     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1443 }
1444
1445 static ULONG WINAPI SAXDeclHandler_AddRef(ISAXDeclHandler *iface)
1446 {
1447     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1448     return IMXWriter_AddRef(&This->IMXWriter_iface);
1449 }
1450
1451 static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
1452 {
1453     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1454     return IMXWriter_Release(&This->IMXWriter_iface);
1455 }
1456
1457 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
1458     const WCHAR *name, int n_name, const WCHAR *model, int n_model)
1459 {
1460     static const WCHAR elementW[] = {'<','!','E','L','E','M','E','N','T',' '};
1461     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1462
1463     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1464         debugstr_wn(model, n_model), n_model);
1465
1466     if (!name || !model) return E_INVALIDARG;
1467
1468     write_output_buffer(This->buffer, elementW, sizeof(elementW)/sizeof(WCHAR));
1469     if (n_name) {
1470         write_output_buffer(This->buffer, name, n_name);
1471         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1472     }
1473     if (n_model)
1474         write_output_buffer(This->buffer, model, n_model);
1475     write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
1476
1477     return S_OK;
1478 }
1479
1480 static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
1481     const WCHAR *element, int n_element, const WCHAR *attr, int n_attr,
1482     const WCHAR *type, int n_type, const WCHAR *Default, int n_default,
1483     const WCHAR *value, int n_value)
1484 {
1485     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1486     static const WCHAR attlistW[] = {'<','!','A','T','T','L','I','S','T',' '};
1487     static const WCHAR closetagW[] = {'>','\r','\n'};
1488
1489     TRACE("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d)\n", This, debugstr_wn(element, n_element), n_element,
1490         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
1491         debugstr_wn(value, n_value), n_value);
1492
1493     write_output_buffer(This->buffer, attlistW, sizeof(attlistW)/sizeof(WCHAR));
1494     if (n_element) {
1495         write_output_buffer(This->buffer, element, n_element);
1496         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1497     }
1498
1499     if (n_attr) {
1500         write_output_buffer(This->buffer, attr, n_attr);
1501         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1502     }
1503
1504     if (n_type) {
1505         write_output_buffer(This->buffer, type, n_type);
1506         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1507     }
1508
1509     if (n_default) {
1510         write_output_buffer(This->buffer, Default, n_default);
1511         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1512     }
1513
1514     if (n_value)
1515         write_output_buffer_quoted(This->buffer, value, n_value);
1516
1517     write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
1518
1519     return S_OK;
1520 }
1521
1522 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
1523     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
1524 {
1525     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1526     static const WCHAR entityW[] = {'<','!','E','N','T','I','T','Y',' '};
1527
1528     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1529         debugstr_wn(value, n_value), n_value);
1530
1531     if (!name || !value) return E_INVALIDARG;
1532
1533     write_output_buffer(This->buffer, entityW, sizeof(entityW)/sizeof(WCHAR));
1534     if (n_name) {
1535         write_output_buffer(This->buffer, name, n_name);
1536         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1537     }
1538
1539     if (n_value)
1540         write_output_buffer_quoted(This->buffer, value, n_value);
1541
1542     write_output_buffer(This->buffer, closetagW, sizeof(closetagW)/sizeof(WCHAR));
1543
1544     return S_OK;
1545 }
1546
1547 static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
1548     const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
1549     const WCHAR *systemId, int n_systemId)
1550 {
1551     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1552     FIXME("(%p)->(%s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1553         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
1554     return E_NOTIMPL;
1555 }
1556
1557 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
1558     SAXDeclHandler_QueryInterface,
1559     SAXDeclHandler_AddRef,
1560     SAXDeclHandler_Release,
1561     SAXDeclHandler_elementDecl,
1562     SAXDeclHandler_attributeDecl,
1563     SAXDeclHandler_internalEntityDecl,
1564     SAXDeclHandler_externalEntityDecl
1565 };
1566
1567 static const tid_t mxwriter_iface_tids[] = {
1568     IMXWriter_tid,
1569     0
1570 };
1571
1572 static dispex_static_data_t mxwriter_dispex = {
1573     NULL,
1574     IMXWriter_tid,
1575     NULL,
1576     mxwriter_iface_tids
1577 };
1578
1579 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1580 {
1581     static const WCHAR version10W[] = {'1','.','0',0};
1582     mxwriter *This;
1583     HRESULT hr;
1584
1585     TRACE("(%p, %p)\n", outer, ppObj);
1586
1587     if (outer) FIXME("support aggregation, outer\n");
1588
1589     This = heap_alloc( sizeof (*This) );
1590     if(!This)
1591         return E_OUTOFMEMORY;
1592
1593     This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1594     This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1595     This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1596     This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1597     This->ref = 1;
1598     This->class_version = version;
1599
1600     This->props[MXWriter_BOM] = VARIANT_TRUE;
1601     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1602     This->props[MXWriter_Indent] = VARIANT_FALSE;
1603     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1604     This->props[MXWriter_Standalone] = VARIANT_FALSE;
1605     This->prop_changed = FALSE;
1606     This->encoding = SysAllocString(utf16W);
1607     This->version  = SysAllocString(version10W);
1608     This->xml_enc  = XmlEncoding_UTF16;
1609
1610     This->element = NULL;
1611     This->cdata = FALSE;
1612
1613     This->dest = NULL;
1614     This->dest_written = 0;
1615
1616     hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1617     if (hr != S_OK) {
1618         SysFreeString(This->encoding);
1619         SysFreeString(This->version);
1620         heap_free(This);
1621         return hr;
1622     }
1623
1624     init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1625
1626     *ppObj = &This->IMXWriter_iface;
1627
1628     TRACE("returning iface %p\n", *ppObj);
1629
1630     return S_OK;
1631 }
1632
1633 static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
1634 {
1635     mxattributes *This = impl_from_IMXAttributes( iface );
1636
1637     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
1638
1639     *ppObj = NULL;
1640
1641     if ( IsEqualGUID( riid, &IID_IUnknown )  ||
1642          IsEqualGUID( riid, &IID_IDispatch ) ||
1643          IsEqualGUID( riid, &IID_IMXAttributes ))
1644     {
1645         *ppObj = iface;
1646     }
1647     else if ( IsEqualGUID( riid, &IID_ISAXAttributes ))
1648     {
1649         *ppObj = &This->ISAXAttributes_iface;
1650     }
1651     else if ( IsEqualGUID( riid, &IID_IVBSAXAttributes ))
1652     {
1653         *ppObj = &This->IVBSAXAttributes_iface;
1654     }
1655     else if (dispex_query_interface(&This->dispex, riid, ppObj))
1656     {
1657         return *ppObj ? S_OK : E_NOINTERFACE;
1658     }
1659     else
1660     {
1661         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1662         return E_NOINTERFACE;
1663     }
1664
1665     IMXAttributes_AddRef( iface );
1666
1667     return S_OK;
1668 }
1669
1670 static ULONG WINAPI MXAttributes_AddRef(IMXAttributes *iface)
1671 {
1672     mxattributes *This = impl_from_IMXAttributes( iface );
1673     ULONG ref = InterlockedIncrement( &This->ref );
1674     TRACE("(%p)->(%d)\n", This, ref );
1675     return ref;
1676 }
1677
1678 static ULONG WINAPI MXAttributes_Release(IMXAttributes *iface)
1679 {
1680     mxattributes *This = impl_from_IMXAttributes( iface );
1681     LONG ref = InterlockedDecrement( &This->ref );
1682
1683     TRACE("(%p)->(%d)\n", This, ref);
1684
1685     if (ref == 0)
1686     {
1687         int i;
1688
1689         for (i = 0; i < This->length; i++)
1690         {
1691             SysFreeString(This->attr[i].qname);
1692             SysFreeString(This->attr[i].local);
1693             SysFreeString(This->attr[i].uri);
1694             SysFreeString(This->attr[i].type);
1695             SysFreeString(This->attr[i].value);
1696         }
1697
1698         release_dispex(&This->dispex);
1699         heap_free(This->attr);
1700         heap_free(This);
1701     }
1702
1703     return ref;
1704 }
1705
1706 static HRESULT WINAPI MXAttributes_GetTypeInfoCount(IMXAttributes *iface, UINT* pctinfo)
1707 {
1708     mxattributes *This = impl_from_IMXAttributes( iface );
1709     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1710 }
1711
1712 static HRESULT WINAPI MXAttributes_GetTypeInfo(IMXAttributes *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1713 {
1714     mxattributes *This = impl_from_IMXAttributes( iface );
1715     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
1716 }
1717
1718 static HRESULT WINAPI MXAttributes_GetIDsOfNames(
1719     IMXAttributes *iface,
1720     REFIID riid,
1721     LPOLESTR* rgszNames,
1722     UINT cNames,
1723     LCID lcid,
1724     DISPID* rgDispId)
1725 {
1726     mxattributes *This = impl_from_IMXAttributes( iface );
1727     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1728         riid, rgszNames, cNames, lcid, rgDispId);
1729 }
1730
1731 static HRESULT WINAPI MXAttributes_Invoke(
1732     IMXAttributes *iface,
1733     DISPID dispIdMember,
1734     REFIID riid,
1735     LCID lcid,
1736     WORD wFlags,
1737     DISPPARAMS* pDispParams,
1738     VARIANT* pVarResult,
1739     EXCEPINFO* pExcepInfo,
1740     UINT* puArgErr)
1741 {
1742     mxattributes *This = impl_from_IMXAttributes( iface );
1743     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1744         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1745 }
1746
1747 static HRESULT WINAPI MXAttributes_addAttribute(IMXAttributes *iface,
1748     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1749 {
1750     mxattributes *This = impl_from_IMXAttributes( iface );
1751     mxattribute *attr;
1752     HRESULT hr;
1753
1754     TRACE("(%p)->(%s %s %s %s %s)\n", This, debugstr_w(uri), debugstr_w(localName),
1755         debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1756
1757     if ((!uri || !localName || !QName || !type || !value) && This->class_version != MSXML6)
1758         return E_INVALIDARG;
1759
1760     /* ensure array is large enough */
1761     hr = mxattributes_grow(This);
1762     if (hr != S_OK) return hr;
1763
1764     attr = &This->attr[This->length];
1765
1766     attr->qname = SysAllocString(QName);
1767     attr->local = SysAllocString(localName);
1768     attr->uri   = SysAllocString(uri);
1769     attr->type  = SysAllocString(type ? type : emptyW);
1770     attr->value = SysAllocString(value);
1771     This->length++;
1772
1773     return S_OK;
1774 }
1775
1776 static HRESULT WINAPI MXAttributes_addAttributeFromIndex(IMXAttributes *iface,
1777     VARIANT atts, int index)
1778 {
1779     mxattributes *This = impl_from_IMXAttributes( iface );
1780     FIXME("(%p)->(%s %d): stub\n", This, debugstr_variant(&atts), index);
1781     return E_NOTIMPL;
1782 }
1783
1784 static HRESULT WINAPI MXAttributes_clear(IMXAttributes *iface)
1785 {
1786     mxattributes *This = impl_from_IMXAttributes( iface );
1787     int i;
1788
1789     TRACE("(%p)\n", This);
1790
1791     for (i = 0; i < This->length; i++)
1792     {
1793         SysFreeString(This->attr[i].qname);
1794         SysFreeString(This->attr[i].local);
1795         SysFreeString(This->attr[i].uri);
1796         SysFreeString(This->attr[i].type);
1797         SysFreeString(This->attr[i].value);
1798         memset(&This->attr[i], 0, sizeof(mxattribute));
1799     }
1800
1801     This->length = 0;
1802
1803     return S_OK;
1804 }
1805
1806 static HRESULT WINAPI MXAttributes_removeAttribute(IMXAttributes *iface, int index)
1807 {
1808     mxattributes *This = impl_from_IMXAttributes( iface );
1809     FIXME("(%p)->(%d): stub\n", This, index);
1810     return E_NOTIMPL;
1811 }
1812
1813 static HRESULT WINAPI MXAttributes_setAttribute(IMXAttributes *iface, int index,
1814     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1815 {
1816     mxattributes *This = impl_from_IMXAttributes( iface );
1817     FIXME("(%p)->(%d %s %s %s %s %s): stub\n", This, index, debugstr_w(uri),
1818         debugstr_w(localName), debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1819     return E_NOTIMPL;
1820 }
1821
1822 static HRESULT WINAPI MXAttributes_setAttributes(IMXAttributes *iface, VARIANT atts)
1823 {
1824     mxattributes *This = impl_from_IMXAttributes( iface );
1825     FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&atts));
1826     return E_NOTIMPL;
1827 }
1828
1829 static HRESULT WINAPI MXAttributes_setLocalName(IMXAttributes *iface, int index,
1830     BSTR localName)
1831 {
1832     mxattributes *This = impl_from_IMXAttributes( iface );
1833     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(localName));
1834     return E_NOTIMPL;
1835 }
1836
1837 static HRESULT WINAPI MXAttributes_setQName(IMXAttributes *iface, int index, BSTR QName)
1838 {
1839     mxattributes *This = impl_from_IMXAttributes( iface );
1840     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(QName));
1841     return E_NOTIMPL;
1842 }
1843
1844 static HRESULT WINAPI MXAttributes_setURI(IMXAttributes *iface, int index, BSTR uri)
1845 {
1846     mxattributes *This = impl_from_IMXAttributes( iface );
1847     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(uri));
1848     return E_NOTIMPL;
1849 }
1850
1851 static HRESULT WINAPI MXAttributes_setValue(IMXAttributes *iface, int index, BSTR value)
1852 {
1853     mxattributes *This = impl_from_IMXAttributes( iface );
1854     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(value));
1855     return E_NOTIMPL;
1856 }
1857
1858 static const IMXAttributesVtbl MXAttributesVtbl = {
1859     MXAttributes_QueryInterface,
1860     MXAttributes_AddRef,
1861     MXAttributes_Release,
1862     MXAttributes_GetTypeInfoCount,
1863     MXAttributes_GetTypeInfo,
1864     MXAttributes_GetIDsOfNames,
1865     MXAttributes_Invoke,
1866     MXAttributes_addAttribute,
1867     MXAttributes_addAttributeFromIndex,
1868     MXAttributes_clear,
1869     MXAttributes_removeAttribute,
1870     MXAttributes_setAttribute,
1871     MXAttributes_setAttributes,
1872     MXAttributes_setLocalName,
1873     MXAttributes_setQName,
1874     MXAttributes_setURI,
1875     MXAttributes_setValue
1876 };
1877
1878 static HRESULT WINAPI SAXAttributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **ppObj)
1879 {
1880     mxattributes *This = impl_from_ISAXAttributes( iface );
1881     return IMXAttributes_QueryInterface(&This->IMXAttributes_iface, riid, ppObj);
1882 }
1883
1884 static ULONG WINAPI SAXAttributes_AddRef(ISAXAttributes *iface)
1885 {
1886     mxattributes *This = impl_from_ISAXAttributes( iface );
1887     return IMXAttributes_AddRef(&This->IMXAttributes_iface);
1888 }
1889
1890 static ULONG WINAPI SAXAttributes_Release(ISAXAttributes *iface)
1891 {
1892     mxattributes *This = impl_from_ISAXAttributes( iface );
1893     return IMXAttributes_Release(&This->IMXAttributes_iface);
1894 }
1895
1896 static HRESULT WINAPI SAXAttributes_getLength(ISAXAttributes *iface, int *length)
1897 {
1898     mxattributes *This = impl_from_ISAXAttributes( iface );
1899     TRACE("(%p)->(%p)\n", This, length);
1900
1901     if (!length && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1902        return E_POINTER;
1903
1904     *length = This->length;
1905
1906     return S_OK;
1907 }
1908
1909 static HRESULT WINAPI SAXAttributes_getURI(ISAXAttributes *iface, int index, const WCHAR **uri,
1910     int *len)
1911 {
1912     mxattributes *This = impl_from_ISAXAttributes( iface );
1913
1914     TRACE("(%p)->(%d %p %p)\n", This, index, uri, len);
1915
1916     if (index >= This->length || index < 0) return E_INVALIDARG;
1917     if (!uri || !len) return E_POINTER;
1918
1919     *len = SysStringLen(This->attr[index].uri);
1920     *uri = This->attr[index].uri;
1921
1922     return S_OK;
1923 }
1924
1925 static HRESULT WINAPI SAXAttributes_getLocalName(ISAXAttributes *iface, int index, const WCHAR **name,
1926     int *len)
1927 {
1928     mxattributes *This = impl_from_ISAXAttributes( iface );
1929
1930     TRACE("(%p)->(%d %p %p)\n", This, index, name, len);
1931
1932     if (index >= This->length || index < 0) return E_INVALIDARG;
1933     if (!name || !len) return E_POINTER;
1934
1935     *len = SysStringLen(This->attr[index].local);
1936     *name = This->attr[index].local;
1937
1938     return S_OK;
1939 }
1940
1941 static HRESULT WINAPI SAXAttributes_getQName(ISAXAttributes *iface, int index, const WCHAR **qname, int *length)
1942 {
1943     mxattributes *This = impl_from_ISAXAttributes( iface );
1944
1945     TRACE("(%p)->(%d %p %p)\n", This, index, qname, length);
1946
1947     if (index >= This->length) return E_INVALIDARG;
1948     if (!qname || !length) return E_POINTER;
1949
1950     *qname = This->attr[index].qname;
1951     *length = SysStringLen(This->attr[index].qname);
1952
1953     return S_OK;
1954 }
1955
1956 static HRESULT WINAPI SAXAttributes_getName(ISAXAttributes *iface, int index, const WCHAR **uri, int *uri_len,
1957     const WCHAR **local, int *local_len, const WCHAR **qname, int *qname_len)
1958 {
1959     mxattributes *This = impl_from_ISAXAttributes( iface );
1960
1961     TRACE("(%p)->(%d %p %p %p %p %p %p)\n", This, index, uri, uri_len, local, local_len, qname, qname_len);
1962
1963     if (index >= This->length || index < 0)
1964         return E_INVALIDARG;
1965
1966     if (!uri || !uri_len || !local || !local_len || !qname || !qname_len)
1967         return E_POINTER;
1968
1969     *uri_len = SysStringLen(This->attr[index].uri);
1970     *uri = This->attr[index].uri;
1971
1972     *local_len = SysStringLen(This->attr[index].local);
1973     *local = This->attr[index].local;
1974
1975     *qname_len = SysStringLen(This->attr[index].qname);
1976     *qname = This->attr[index].qname;
1977
1978     TRACE("(%s, %s, %s)\n", debugstr_w(*uri), debugstr_w(*local), debugstr_w(*qname));
1979
1980     return S_OK;
1981 }
1982
1983 static HRESULT WINAPI SAXAttributes_getIndexFromName(ISAXAttributes *iface, const WCHAR *uri, int uri_len,
1984     const WCHAR *name, int len, int *index)
1985 {
1986     mxattributes *This = impl_from_ISAXAttributes( iface );
1987     int i;
1988
1989     TRACE("(%p)->(%s:%d %s:%d %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
1990         debugstr_wn(name, len), len, index);
1991
1992     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1993         return E_POINTER;
1994
1995     if (!uri || !name || !index) return E_INVALIDARG;
1996
1997     for (i = 0; i < This->length; i++)
1998     {
1999         if (uri_len != SysStringLen(This->attr[i].uri)) continue;
2000         if (strncmpW(uri, This->attr[i].uri, uri_len)) continue;
2001
2002         if (len != SysStringLen(This->attr[i].local)) continue;
2003         if (strncmpW(name, This->attr[i].local, len)) continue;
2004
2005         *index = i;
2006         return S_OK;
2007     }
2008
2009     return E_INVALIDARG;
2010 }
2011
2012 static HRESULT WINAPI SAXAttributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR *qname,
2013     int len, int *index)
2014 {
2015     mxattributes *This = impl_from_ISAXAttributes( iface );
2016     int i;
2017
2018     TRACE("(%p)->(%s:%d %p)\n", This, debugstr_wn(qname, len), len, index);
2019
2020     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
2021         return E_POINTER;
2022
2023     if (!qname || !index || !len) return E_INVALIDARG;
2024
2025     for (i = 0; i < This->length; i++)
2026     {
2027         if (len != SysStringLen(This->attr[i].qname)) continue;
2028         if (strncmpW(qname, This->attr[i].qname, len)) continue;
2029
2030         *index = i;
2031         return S_OK;
2032     }
2033
2034     return E_INVALIDARG;
2035 }
2036
2037 static HRESULT WINAPI SAXAttributes_getType(ISAXAttributes *iface, int index, const WCHAR **type,
2038     int *len)
2039 {
2040     mxattributes *This = impl_from_ISAXAttributes( iface );
2041
2042     TRACE("(%p)->(%d %p %p)\n", This, index, type, len);
2043
2044     if (index >= This->length) return E_INVALIDARG;
2045
2046     if ((!type || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
2047        return E_POINTER;
2048
2049     *type = This->attr[index].type;
2050     *len = SysStringLen(This->attr[index].type);
2051
2052     return S_OK;
2053 }
2054
2055 static HRESULT WINAPI SAXAttributes_getTypeFromName(ISAXAttributes *iface, const WCHAR * pUri, int nUri,
2056     const WCHAR * pLocalName, int nLocalName, const WCHAR ** pType, int * nType)
2057 {
2058     mxattributes *This = impl_from_ISAXAttributes( iface );
2059     FIXME("(%p)->(%s:%d %s:%d %p %p): stub\n", This, debugstr_wn(pUri, nUri), nUri,
2060         debugstr_wn(pLocalName, nLocalName), nLocalName, pType, nType);
2061     return E_NOTIMPL;
2062 }
2063
2064 static HRESULT WINAPI SAXAttributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR * pQName,
2065     int nQName, const WCHAR ** pType, int * nType)
2066 {
2067     mxattributes *This = impl_from_ISAXAttributes( iface );
2068     FIXME("(%p)->(%s:%d %p %p): stub\n", This, debugstr_wn(pQName, nQName), nQName, pType, nType);
2069     return E_NOTIMPL;
2070 }
2071
2072 static HRESULT WINAPI SAXAttributes_getValue(ISAXAttributes *iface, int index, const WCHAR **value,
2073     int *len)
2074 {
2075     mxattributes *This = impl_from_ISAXAttributes( iface );
2076
2077     TRACE("(%p)->(%d %p %p)\n", This, index, value, len);
2078
2079     if (index >= This->length) return E_INVALIDARG;
2080
2081     if ((!value || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
2082        return E_POINTER;
2083
2084     *value = This->attr[index].value;
2085     *len = SysStringLen(This->attr[index].value);
2086
2087     return S_OK;
2088 }
2089
2090 static HRESULT WINAPI SAXAttributes_getValueFromName(ISAXAttributes *iface, const WCHAR *uri,
2091     int uri_len, const WCHAR *name, int name_len, const WCHAR **value, int *value_len)
2092 {
2093     mxattributes *This = impl_from_ISAXAttributes( iface );
2094     HRESULT hr;
2095     int index;
2096
2097     TRACE("(%p)->(%s:%d %s:%d %p %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
2098         debugstr_wn(name, name_len), name_len, value, value_len);
2099
2100     if (!uri || !name || !value || !value_len)
2101         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
2102
2103     hr = ISAXAttributes_getIndexFromName(iface, uri, uri_len, name, name_len, &index);
2104     if (hr == S_OK)
2105         hr = ISAXAttributes_getValue(iface, index, value, value_len);
2106
2107     return hr;
2108 }
2109
2110 static HRESULT WINAPI SAXAttributes_getValueFromQName(ISAXAttributes *iface, const WCHAR *qname,
2111     int qname_len, const WCHAR **value, int *value_len)
2112 {
2113     mxattributes *This = impl_from_ISAXAttributes( iface );
2114     HRESULT hr;
2115     int index;
2116
2117     TRACE("(%p)->(%s:%d %p %p)\n", This, debugstr_wn(qname, qname_len), qname_len, value, value_len);
2118
2119     if (!qname || !value || !value_len)
2120         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
2121
2122     hr = ISAXAttributes_getIndexFromQName(iface, qname, qname_len, &index);
2123     if (hr == S_OK)
2124         hr = ISAXAttributes_getValue(iface, index, value, value_len);
2125
2126     return hr;
2127 }
2128
2129 static const ISAXAttributesVtbl SAXAttributesVtbl = {
2130     SAXAttributes_QueryInterface,
2131     SAXAttributes_AddRef,
2132     SAXAttributes_Release,
2133     SAXAttributes_getLength,
2134     SAXAttributes_getURI,
2135     SAXAttributes_getLocalName,
2136     SAXAttributes_getQName,
2137     SAXAttributes_getName,
2138     SAXAttributes_getIndexFromName,
2139     SAXAttributes_getIndexFromQName,
2140     SAXAttributes_getType,
2141     SAXAttributes_getTypeFromName,
2142     SAXAttributes_getTypeFromQName,
2143     SAXAttributes_getValue,
2144     SAXAttributes_getValueFromName,
2145     SAXAttributes_getValueFromQName
2146 };
2147
2148 static HRESULT WINAPI VBSAXAttributes_QueryInterface(
2149         IVBSAXAttributes* iface,
2150         REFIID riid,
2151         void **ppvObject)
2152 {
2153     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2154     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
2155     return ISAXAttributes_QueryInterface(&This->ISAXAttributes_iface, riid, ppvObject);
2156 }
2157
2158 static ULONG WINAPI VBSAXAttributes_AddRef(IVBSAXAttributes* iface)
2159 {
2160     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2161     return ISAXAttributes_AddRef(&This->ISAXAttributes_iface);
2162 }
2163
2164 static ULONG WINAPI VBSAXAttributes_Release(IVBSAXAttributes* iface)
2165 {
2166     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2167     return ISAXAttributes_Release(&This->ISAXAttributes_iface);
2168 }
2169
2170 static HRESULT WINAPI VBSAXAttributes_GetTypeInfoCount( IVBSAXAttributes *iface, UINT* pctinfo )
2171 {
2172     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2173
2174     TRACE("(%p)->(%p)\n", This, pctinfo);
2175
2176     *pctinfo = 1;
2177
2178     return S_OK;
2179 }
2180
2181 static HRESULT WINAPI VBSAXAttributes_GetTypeInfo(
2182     IVBSAXAttributes *iface,
2183     UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
2184 {
2185     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2186     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
2187     return get_typeinfo(IVBSAXAttributes_tid, ppTInfo);
2188 }
2189
2190 static HRESULT WINAPI VBSAXAttributes_GetIDsOfNames(
2191     IVBSAXAttributes *iface,
2192     REFIID riid,
2193     LPOLESTR* rgszNames,
2194     UINT cNames,
2195     LCID lcid,
2196     DISPID* rgDispId)
2197 {
2198     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2199     ITypeInfo *typeinfo;
2200     HRESULT hr;
2201
2202     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
2203           lcid, rgDispId);
2204
2205     if(!rgszNames || cNames == 0 || !rgDispId)
2206         return E_INVALIDARG;
2207
2208     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
2209     if(SUCCEEDED(hr))
2210     {
2211         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2212         ITypeInfo_Release(typeinfo);
2213     }
2214
2215     return hr;
2216 }
2217
2218 static HRESULT WINAPI VBSAXAttributes_Invoke(
2219     IVBSAXAttributes *iface,
2220     DISPID dispIdMember,
2221     REFIID riid,
2222     LCID lcid,
2223     WORD wFlags,
2224     DISPPARAMS* pDispParams,
2225     VARIANT* pVarResult,
2226     EXCEPINFO* pExcepInfo,
2227     UINT* puArgErr)
2228 {
2229     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2230     ITypeInfo *typeinfo;
2231     HRESULT hr;
2232
2233     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
2234           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2235
2236     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
2237     if(SUCCEEDED(hr))
2238     {
2239         hr = ITypeInfo_Invoke(typeinfo, &This->IVBSAXAttributes_iface, dispIdMember, wFlags,
2240                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2241         ITypeInfo_Release(typeinfo);
2242     }
2243
2244     return hr;
2245 }
2246
2247 static HRESULT WINAPI VBSAXAttributes_get_length(IVBSAXAttributes* iface, int *len)
2248 {
2249     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2250     return ISAXAttributes_getLength(&This->ISAXAttributes_iface, len);
2251 }
2252
2253 static HRESULT WINAPI VBSAXAttributes_getURI(IVBSAXAttributes* iface, int index, BSTR *uri)
2254 {
2255     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2256     int len;
2257
2258     return ISAXAttributes_getURI(&This->ISAXAttributes_iface, index, (const WCHAR**)uri, &len);
2259 }
2260
2261 static HRESULT WINAPI VBSAXAttributes_getLocalName(IVBSAXAttributes* iface, int index, BSTR *name)
2262 {
2263     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2264     int len;
2265
2266     return ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, index, (const WCHAR**)name, &len);
2267 }
2268
2269 static HRESULT WINAPI VBSAXAttributes_getQName(IVBSAXAttributes* iface, int index, BSTR *qname)
2270 {
2271     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2272     int len;
2273
2274     return ISAXAttributes_getQName(&This->ISAXAttributes_iface, index, (const WCHAR**)qname, &len);
2275 }
2276
2277 static HRESULT WINAPI VBSAXAttributes_getIndexFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name, int *index)
2278 {
2279     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2280     return ISAXAttributes_getIndexFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2281             name, SysStringLen(name), index);
2282 }
2283
2284 static HRESULT WINAPI VBSAXAttributes_getIndexFromQName(IVBSAXAttributes* iface, BSTR qname, int *index)
2285 {
2286     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2287     return ISAXAttributes_getIndexFromQName(&This->ISAXAttributes_iface, qname,
2288             SysStringLen(qname), index);
2289 }
2290
2291 static HRESULT WINAPI VBSAXAttributes_getType(IVBSAXAttributes* iface, int index,BSTR *type)
2292 {
2293     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2294     int len;
2295
2296     return ISAXAttributes_getType(&This->ISAXAttributes_iface, index, (const WCHAR**)type, &len);
2297 }
2298
2299 static HRESULT WINAPI VBSAXAttributes_getTypeFromName(IVBSAXAttributes* iface, BSTR uri,
2300     BSTR name, BSTR *type)
2301 {
2302     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2303     int len;
2304
2305     return ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2306             name, SysStringLen(name), (const WCHAR**)type, &len);
2307 }
2308
2309 static HRESULT WINAPI VBSAXAttributes_getTypeFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *type)
2310 {
2311     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2312     int len;
2313
2314     return ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
2315             (const WCHAR**)type, &len);
2316 }
2317
2318 static HRESULT WINAPI VBSAXAttributes_getValue(IVBSAXAttributes* iface, int index, BSTR *value)
2319 {
2320     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2321     int len;
2322
2323     return ISAXAttributes_getValue(&This->ISAXAttributes_iface, index, (const WCHAR**)value, &len);
2324 }
2325
2326 static HRESULT WINAPI VBSAXAttributes_getValueFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name,
2327     BSTR *value)
2328 {
2329     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2330     int len;
2331
2332     return ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2333             name, SysStringLen(name), (const WCHAR**)value, &len);
2334 }
2335
2336 static HRESULT WINAPI VBSAXAttributes_getValueFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *value)
2337 {
2338     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2339     int len;
2340
2341     return ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
2342         (const WCHAR**)value, &len);
2343 }
2344
2345 static const struct IVBSAXAttributesVtbl VBSAXAttributesVtbl =
2346 {
2347     VBSAXAttributes_QueryInterface,
2348     VBSAXAttributes_AddRef,
2349     VBSAXAttributes_Release,
2350     VBSAXAttributes_GetTypeInfoCount,
2351     VBSAXAttributes_GetTypeInfo,
2352     VBSAXAttributes_GetIDsOfNames,
2353     VBSAXAttributes_Invoke,
2354     VBSAXAttributes_get_length,
2355     VBSAXAttributes_getURI,
2356     VBSAXAttributes_getLocalName,
2357     VBSAXAttributes_getQName,
2358     VBSAXAttributes_getIndexFromName,
2359     VBSAXAttributes_getIndexFromQName,
2360     VBSAXAttributes_getType,
2361     VBSAXAttributes_getTypeFromName,
2362     VBSAXAttributes_getTypeFromQName,
2363     VBSAXAttributes_getValue,
2364     VBSAXAttributes_getValueFromName,
2365     VBSAXAttributes_getValueFromQName
2366 };
2367
2368 static const tid_t mxattrs_iface_tids[] = {
2369     IMXAttributes_tid,
2370     0
2371 };
2372
2373 static dispex_static_data_t mxattrs_dispex = {
2374     NULL,
2375     IMXAttributes_tid,
2376     NULL,
2377     mxattrs_iface_tids
2378 };
2379
2380 HRESULT SAXAttributes_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
2381 {
2382     static const int default_count = 10;
2383     mxattributes *This;
2384
2385     TRACE("(%p, %p)\n", outer, ppObj);
2386
2387     This = heap_alloc( sizeof (*This) );
2388     if( !This )
2389         return E_OUTOFMEMORY;
2390
2391     This->IMXAttributes_iface.lpVtbl = &MXAttributesVtbl;
2392     This->ISAXAttributes_iface.lpVtbl = &SAXAttributesVtbl;
2393     This->IVBSAXAttributes_iface.lpVtbl = &VBSAXAttributesVtbl;
2394     This->ref = 1;
2395
2396     This->class_version = version;
2397
2398     This->attr = heap_alloc(default_count*sizeof(mxattribute));
2399     This->length = 0;
2400     This->allocated = default_count;
2401
2402     *ppObj = &This->IMXAttributes_iface;
2403
2404     init_dispex(&This->dispex, (IUnknown*)&This->IMXAttributes_iface, &mxattrs_dispex);
2405
2406     TRACE("returning iface %p\n", *ppObj);
2407
2408     return S_OK;
2409 }