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