wininet: Use stored status code in HTTP_HttpEndRequestW.
[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         int length, i, escape;
1023         HRESULT hr;
1024
1025         hr = ISAXAttributes_getLength(attr, &length);
1026         if (FAILED(hr)) return hr;
1027
1028         escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
1029             (This->class_version == MSXML4 || This->class_version == MSXML6);
1030
1031         for (i = 0; i < length; i++)
1032         {
1033             static const WCHAR eqW[] = {'='};
1034             const WCHAR *str;
1035             int len = 0;
1036
1037             hr = ISAXAttributes_getQName(attr, i, &str, &len);
1038             if (FAILED(hr)) return hr;
1039
1040             /* space separator in front of every attribute */
1041             write_output_buffer(This->buffer, spaceW, 1);
1042             write_output_buffer(This->buffer, str, len);
1043
1044             write_output_buffer(This->buffer, eqW, 1);
1045
1046             len = 0;
1047             hr = ISAXAttributes_getValue(attr, i, &str, &len);
1048             if (FAILED(hr)) return hr;
1049
1050             if (escape)
1051             {
1052                 WCHAR *escaped = get_escaped_string(str, EscapeValue, &len);
1053                 write_output_buffer_quoted(This->buffer, escaped, len);
1054                 heap_free(escaped);
1055             }
1056             else
1057                 write_output_buffer_quoted(This->buffer, str, len);
1058         }
1059     }
1060
1061     return S_OK;
1062 }
1063
1064 static HRESULT WINAPI SAXContentHandler_endElement(
1065     ISAXContentHandler *iface,
1066     const WCHAR *namespaceUri,
1067     int nnamespaceUri,
1068     const WCHAR * local_name,
1069     int nlocal_name,
1070     const WCHAR *QName,
1071     int nQName)
1072 {
1073     mxwriter *This = impl_from_ISAXContentHandler( iface );
1074
1075     TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
1076         debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
1077
1078     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
1079         return E_INVALIDARG;
1080
1081     if (This->element && QName && !strncmpW(This->element, QName, nQName))
1082     {
1083         static const WCHAR closeW[] = {'/','>'};
1084
1085         write_output_buffer(This->buffer, closeW, 2);
1086     }
1087     else
1088     {
1089         static const WCHAR closetagW[] = {'<','/'};
1090         static const WCHAR gtW[] = {'>'};
1091
1092         write_output_buffer(This->buffer, closetagW, 2);
1093         write_output_buffer(This->buffer, QName, nQName);
1094         write_output_buffer(This->buffer, gtW, 1);
1095     }
1096
1097     set_element_name(This, NULL, 0);
1098
1099     return S_OK;
1100 }
1101
1102 static HRESULT WINAPI SAXContentHandler_characters(
1103     ISAXContentHandler *iface,
1104     const WCHAR *chars,
1105     int nchars)
1106 {
1107     mxwriter *This = impl_from_ISAXContentHandler( iface );
1108
1109     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1110
1111     if (!chars) return E_INVALIDARG;
1112
1113     close_element_starttag(This);
1114     set_element_name(This, NULL, 0);
1115
1116     if (nchars)
1117     {
1118         if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
1119             write_output_buffer(This->buffer, chars, nchars);
1120         else
1121         {
1122             int len = nchars;
1123             WCHAR *escaped;
1124
1125             escaped = get_escaped_string(chars, EscapeText, &len);
1126             write_output_buffer(This->buffer, escaped, len);
1127             heap_free(escaped);
1128         }
1129     }
1130
1131     return S_OK;
1132 }
1133
1134 static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
1135     ISAXContentHandler *iface,
1136     const WCHAR *chars,
1137     int nchars)
1138 {
1139     mxwriter *This = impl_from_ISAXContentHandler( iface );
1140
1141     TRACE("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
1142
1143     if (!chars) return E_INVALIDARG;
1144
1145     write_output_buffer(This->buffer, chars, nchars);
1146
1147     return S_OK;
1148 }
1149
1150 static HRESULT WINAPI SAXContentHandler_processingInstruction(
1151     ISAXContentHandler *iface,
1152     const WCHAR *target,
1153     int ntarget,
1154     const WCHAR *data,
1155     int ndata)
1156 {
1157     mxwriter *This = impl_from_ISAXContentHandler( iface );
1158     static const WCHAR openpiW[] = {'<','?'};
1159     static const WCHAR closepiW[] = {'?','>','\r','\n'};
1160
1161     TRACE("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1162
1163     if (!target) return E_INVALIDARG;
1164
1165     write_output_buffer(This->buffer, openpiW, sizeof(openpiW)/sizeof(WCHAR));
1166
1167     if (*target)
1168         write_output_buffer(This->buffer, target, ntarget);
1169
1170     if (data && *data && ndata)
1171     {
1172         write_output_buffer(This->buffer, spaceW, 1);
1173         write_output_buffer(This->buffer, data, ndata);
1174     }
1175
1176     write_output_buffer(This->buffer, closepiW, sizeof(closepiW)/sizeof(WCHAR));
1177
1178     return S_OK;
1179 }
1180
1181 static HRESULT WINAPI SAXContentHandler_skippedEntity(
1182     ISAXContentHandler *iface,
1183     const WCHAR *name,
1184     int nname)
1185 {
1186     mxwriter *This = impl_from_ISAXContentHandler( iface );
1187     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1188     return E_NOTIMPL;
1189 }
1190
1191 static const struct ISAXContentHandlerVtbl SAXContentHandlerVtbl =
1192 {
1193     SAXContentHandler_QueryInterface,
1194     SAXContentHandler_AddRef,
1195     SAXContentHandler_Release,
1196     SAXContentHandler_putDocumentLocator,
1197     SAXContentHandler_startDocument,
1198     SAXContentHandler_endDocument,
1199     SAXContentHandler_startPrefixMapping,
1200     SAXContentHandler_endPrefixMapping,
1201     SAXContentHandler_startElement,
1202     SAXContentHandler_endElement,
1203     SAXContentHandler_characters,
1204     SAXContentHandler_ignorableWhitespace,
1205     SAXContentHandler_processingInstruction,
1206     SAXContentHandler_skippedEntity
1207 };
1208
1209 /*** ISAXLexicalHandler ***/
1210 static HRESULT WINAPI SAXLexicalHandler_QueryInterface(ISAXLexicalHandler *iface,
1211     REFIID riid, void **obj)
1212 {
1213     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1214     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1215 }
1216
1217 static ULONG WINAPI SAXLexicalHandler_AddRef(ISAXLexicalHandler *iface)
1218 {
1219     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1220     return IMXWriter_AddRef(&This->IMXWriter_iface);
1221 }
1222
1223 static ULONG WINAPI SAXLexicalHandler_Release(ISAXLexicalHandler *iface)
1224 {
1225     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1226     return IMXWriter_Release(&This->IMXWriter_iface);
1227 }
1228
1229 static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
1230     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
1231     const WCHAR *systemId, int systemId_len)
1232 {
1233     static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1234     static const WCHAR openintW[] = {'[','\r','\n'};
1235
1236     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1237
1238     TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
1239         debugstr_wn(systemId, systemId_len));
1240
1241     if (!name) return E_INVALIDARG;
1242
1243     write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
1244
1245     if (*name)
1246     {
1247         write_output_buffer(This->buffer, name, name_len);
1248         write_output_buffer(This->buffer, spaceW, 1);
1249     }
1250
1251     if (publicId)
1252     {
1253         static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
1254
1255         write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
1256         write_output_buffer_quoted(This->buffer, publicId, publicId_len);
1257
1258         if (!systemId) return E_INVALIDARG;
1259
1260         if (*publicId)
1261             write_output_buffer(This->buffer, spaceW, 1);
1262
1263         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1264
1265         if (*systemId)
1266             write_output_buffer(This->buffer, spaceW, 1);
1267     }
1268     else if (systemId)
1269     {
1270         static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
1271
1272         write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
1273         write_output_buffer_quoted(This->buffer, systemId, systemId_len);
1274         if (*systemId)
1275             write_output_buffer(This->buffer, spaceW, 1);
1276     }
1277
1278     write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
1279
1280     return S_OK;
1281 }
1282
1283 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
1284 {
1285     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1286     static const WCHAR closedtdW[] = {']','>','\r','\n'};
1287
1288     TRACE("(%p)\n", This);
1289
1290     write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
1291
1292     return S_OK;
1293 }
1294
1295 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1296 {
1297     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1298     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1299     return E_NOTIMPL;
1300 }
1301
1302 static HRESULT WINAPI SAXLexicalHandler_endEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
1303 {
1304     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1305     FIXME("(%p)->(%s): stub\n", This, debugstr_wn(name, len));
1306     return E_NOTIMPL;
1307 }
1308
1309 static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
1310 {
1311     static const WCHAR scdataW[] = {'<','!','[','C','D','A','T','A','['};
1312     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1313
1314     TRACE("(%p)\n", This);
1315
1316     write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
1317     This->cdata = TRUE;
1318
1319     return S_OK;
1320 }
1321
1322 static HRESULT WINAPI SAXLexicalHandler_endCDATA(ISAXLexicalHandler *iface)
1323 {
1324     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1325     static const WCHAR ecdataW[] = {']',']','>'};
1326
1327     TRACE("(%p)\n", This);
1328
1329     write_output_buffer(This->buffer, ecdataW, sizeof(ecdataW)/sizeof(WCHAR));
1330     This->cdata = FALSE;
1331
1332     return S_OK;
1333 }
1334
1335 static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const WCHAR *chars, int nchars)
1336 {
1337     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
1338     static const WCHAR copenW[] = {'<','!','-','-'};
1339     static const WCHAR ccloseW[] = {'-','-','>','\r','\n'};
1340
1341     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
1342
1343     if (!chars) return E_INVALIDARG;
1344
1345     close_element_starttag(This);
1346
1347     write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
1348     if (nchars)
1349         write_output_buffer(This->buffer, chars, nchars);
1350     write_output_buffer(This->buffer, ccloseW, sizeof(ccloseW)/sizeof(WCHAR));
1351
1352     return S_OK;
1353 }
1354
1355 static const struct ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1356 {
1357     SAXLexicalHandler_QueryInterface,
1358     SAXLexicalHandler_AddRef,
1359     SAXLexicalHandler_Release,
1360     SAXLexicalHandler_startDTD,
1361     SAXLexicalHandler_endDTD,
1362     SAXLexicalHandler_startEntity,
1363     SAXLexicalHandler_endEntity,
1364     SAXLexicalHandler_startCDATA,
1365     SAXLexicalHandler_endCDATA,
1366     SAXLexicalHandler_comment
1367 };
1368
1369 /*** ISAXDeclHandler ***/
1370 static HRESULT WINAPI SAXDeclHandler_QueryInterface(ISAXDeclHandler *iface,
1371     REFIID riid, void **obj)
1372 {
1373     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1374     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
1375 }
1376
1377 static ULONG WINAPI SAXDeclHandler_AddRef(ISAXDeclHandler *iface)
1378 {
1379     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1380     return IMXWriter_AddRef(&This->IMXWriter_iface);
1381 }
1382
1383 static ULONG WINAPI SAXDeclHandler_Release(ISAXDeclHandler *iface)
1384 {
1385     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1386     return IMXWriter_Release(&This->IMXWriter_iface);
1387 }
1388
1389 static HRESULT WINAPI SAXDeclHandler_elementDecl(ISAXDeclHandler *iface,
1390     const WCHAR *name, int n_name, const WCHAR *model, int n_model)
1391 {
1392     static const WCHAR elementW[] = {'<','!','E','L','E','M','E','N','T',' '};
1393     static const WCHAR closeelementW[] = {'>','\r','\n'};
1394     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1395
1396     TRACE("(%p)->(%s:%d %s:%d)\n", This, debugstr_wn(name, n_name), n_name,
1397         debugstr_wn(model, n_model), n_model);
1398
1399     if (!name || !model) return E_INVALIDARG;
1400
1401     write_output_buffer(This->buffer, elementW, sizeof(elementW)/sizeof(WCHAR));
1402     if (n_name) {
1403         write_output_buffer(This->buffer, name, n_name);
1404         write_output_buffer(This->buffer, spaceW, sizeof(spaceW)/sizeof(WCHAR));
1405     }
1406     if (n_model)
1407         write_output_buffer(This->buffer, model, n_model);
1408     write_output_buffer(This->buffer, closeelementW, sizeof(closeelementW)/sizeof(WCHAR));
1409
1410     return S_OK;
1411 }
1412
1413 static HRESULT WINAPI SAXDeclHandler_attributeDecl(ISAXDeclHandler *iface,
1414     const WCHAR *element, int n_element, const WCHAR *attr, int n_attr,
1415     const WCHAR *type, int n_type, const WCHAR *Default, int n_default,
1416     const WCHAR *value, int n_value)
1417 {
1418     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1419     FIXME("(%p)->(%s:%d %s:%d %s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(element, n_element), n_element,
1420         debugstr_wn(attr, n_attr), n_attr, debugstr_wn(type, n_type), n_type, debugstr_wn(Default, n_default), n_default,
1421         debugstr_wn(value, n_value), n_value);
1422     return E_NOTIMPL;
1423 }
1424
1425 static HRESULT WINAPI SAXDeclHandler_internalEntityDecl(ISAXDeclHandler *iface,
1426     const WCHAR *name, int n_name, const WCHAR *value, int n_value)
1427 {
1428     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1429     FIXME("(%p)->(%s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1430         debugstr_wn(value, n_value), n_value);
1431     return E_NOTIMPL;
1432 }
1433
1434 static HRESULT WINAPI SAXDeclHandler_externalEntityDecl(ISAXDeclHandler *iface,
1435     const WCHAR *name, int n_name, const WCHAR *publicId, int n_publicId,
1436     const WCHAR *systemId, int n_systemId)
1437 {
1438     mxwriter *This = impl_from_ISAXDeclHandler( iface );
1439     FIXME("(%p)->(%s:%d %s:%d %s:%d): stub\n", This, debugstr_wn(name, n_name), n_name,
1440         debugstr_wn(publicId, n_publicId), n_publicId, debugstr_wn(systemId, n_systemId), n_systemId);
1441     return E_NOTIMPL;
1442 }
1443
1444 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = {
1445     SAXDeclHandler_QueryInterface,
1446     SAXDeclHandler_AddRef,
1447     SAXDeclHandler_Release,
1448     SAXDeclHandler_elementDecl,
1449     SAXDeclHandler_attributeDecl,
1450     SAXDeclHandler_internalEntityDecl,
1451     SAXDeclHandler_externalEntityDecl
1452 };
1453
1454 static const tid_t mxwriter_iface_tids[] = {
1455     IMXWriter_tid,
1456     0
1457 };
1458
1459 static dispex_static_data_t mxwriter_dispex = {
1460     NULL,
1461     IMXWriter_tid,
1462     NULL,
1463     mxwriter_iface_tids
1464 };
1465
1466 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1467 {
1468     static const WCHAR version10W[] = {'1','.','0',0};
1469     mxwriter *This;
1470     HRESULT hr;
1471
1472     TRACE("(%p, %p)\n", outer, ppObj);
1473
1474     if (outer) FIXME("support aggregation, outer\n");
1475
1476     This = heap_alloc( sizeof (*This) );
1477     if(!This)
1478         return E_OUTOFMEMORY;
1479
1480     This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1481     This->ISAXContentHandler_iface.lpVtbl = &SAXContentHandlerVtbl;
1482     This->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1483     This->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1484     This->ref = 1;
1485     This->class_version = version;
1486
1487     This->props[MXWriter_BOM] = VARIANT_TRUE;
1488     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1489     This->props[MXWriter_Indent] = VARIANT_FALSE;
1490     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1491     This->props[MXWriter_Standalone] = VARIANT_FALSE;
1492     This->prop_changed = FALSE;
1493     This->encoding = SysAllocString(utf16W);
1494     This->version  = SysAllocString(version10W);
1495     This->xml_enc  = XmlEncoding_UTF16;
1496
1497     This->element = NULL;
1498     This->cdata = FALSE;
1499
1500     This->dest = NULL;
1501     This->dest_written = 0;
1502
1503     hr = alloc_output_buffer(This->xml_enc, &This->buffer);
1504     if (hr != S_OK) {
1505         SysFreeString(This->encoding);
1506         SysFreeString(This->version);
1507         heap_free(This);
1508         return hr;
1509     }
1510
1511     init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1512
1513     *ppObj = &This->IMXWriter_iface;
1514
1515     TRACE("returning iface %p\n", *ppObj);
1516
1517     return S_OK;
1518 }
1519
1520 static HRESULT WINAPI MXAttributes_QueryInterface(IMXAttributes *iface, REFIID riid, void **ppObj)
1521 {
1522     mxattributes *This = impl_from_IMXAttributes( iface );
1523
1524     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppObj);
1525
1526     *ppObj = NULL;
1527
1528     if ( IsEqualGUID( riid, &IID_IUnknown )  ||
1529          IsEqualGUID( riid, &IID_IDispatch ) ||
1530          IsEqualGUID( riid, &IID_IMXAttributes ))
1531     {
1532         *ppObj = iface;
1533     }
1534     else if ( IsEqualGUID( riid, &IID_ISAXAttributes ))
1535     {
1536         *ppObj = &This->ISAXAttributes_iface;
1537     }
1538     else if ( IsEqualGUID( riid, &IID_IVBSAXAttributes ))
1539     {
1540         *ppObj = &This->IVBSAXAttributes_iface;
1541     }
1542     else if (dispex_query_interface(&This->dispex, riid, ppObj))
1543     {
1544         return *ppObj ? S_OK : E_NOINTERFACE;
1545     }
1546     else
1547     {
1548         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1549         return E_NOINTERFACE;
1550     }
1551
1552     IMXAttributes_AddRef( iface );
1553
1554     return S_OK;
1555 }
1556
1557 static ULONG WINAPI MXAttributes_AddRef(IMXAttributes *iface)
1558 {
1559     mxattributes *This = impl_from_IMXAttributes( iface );
1560     ULONG ref = InterlockedIncrement( &This->ref );
1561     TRACE("(%p)->(%d)\n", This, ref );
1562     return ref;
1563 }
1564
1565 static ULONG WINAPI MXAttributes_Release(IMXAttributes *iface)
1566 {
1567     mxattributes *This = impl_from_IMXAttributes( iface );
1568     LONG ref = InterlockedDecrement( &This->ref );
1569
1570     TRACE("(%p)->(%d)\n", This, ref);
1571
1572     if (ref == 0)
1573     {
1574         int i;
1575
1576         for (i = 0; i < This->length; i++)
1577         {
1578             SysFreeString(This->attr[i].qname);
1579             SysFreeString(This->attr[i].local);
1580             SysFreeString(This->attr[i].uri);
1581             SysFreeString(This->attr[i].type);
1582             SysFreeString(This->attr[i].value);
1583         }
1584
1585         release_dispex(&This->dispex);
1586         heap_free(This->attr);
1587         heap_free(This);
1588     }
1589
1590     return ref;
1591 }
1592
1593 static HRESULT WINAPI MXAttributes_GetTypeInfoCount(IMXAttributes *iface, UINT* pctinfo)
1594 {
1595     mxattributes *This = impl_from_IMXAttributes( iface );
1596     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1597 }
1598
1599 static HRESULT WINAPI MXAttributes_GetTypeInfo(IMXAttributes *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1600 {
1601     mxattributes *This = impl_from_IMXAttributes( iface );
1602     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
1603 }
1604
1605 static HRESULT WINAPI MXAttributes_GetIDsOfNames(
1606     IMXAttributes *iface,
1607     REFIID riid,
1608     LPOLESTR* rgszNames,
1609     UINT cNames,
1610     LCID lcid,
1611     DISPID* rgDispId)
1612 {
1613     mxattributes *This = impl_from_IMXAttributes( iface );
1614     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1615         riid, rgszNames, cNames, lcid, rgDispId);
1616 }
1617
1618 static HRESULT WINAPI MXAttributes_Invoke(
1619     IMXAttributes *iface,
1620     DISPID dispIdMember,
1621     REFIID riid,
1622     LCID lcid,
1623     WORD wFlags,
1624     DISPPARAMS* pDispParams,
1625     VARIANT* pVarResult,
1626     EXCEPINFO* pExcepInfo,
1627     UINT* puArgErr)
1628 {
1629     mxattributes *This = impl_from_IMXAttributes( iface );
1630     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1631         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1632 }
1633
1634 static HRESULT WINAPI MXAttributes_addAttribute(IMXAttributes *iface,
1635     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1636 {
1637     mxattributes *This = impl_from_IMXAttributes( iface );
1638     mxattribute *attr;
1639     HRESULT hr;
1640
1641     TRACE("(%p)->(%s %s %s %s %s)\n", This, debugstr_w(uri), debugstr_w(localName),
1642         debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1643
1644     if ((!uri || !localName || !QName || !type || !value) && This->class_version != MSXML6)
1645         return E_INVALIDARG;
1646
1647     /* ensure array is large enough */
1648     hr = mxattributes_grow(This);
1649     if (hr != S_OK) return hr;
1650
1651     attr = &This->attr[This->length];
1652
1653     attr->qname = SysAllocString(QName);
1654     attr->local = SysAllocString(localName);
1655     attr->uri   = SysAllocString(uri);
1656     attr->type  = SysAllocString(type ? type : emptyW);
1657     attr->value = SysAllocString(value);
1658     This->length++;
1659
1660     return S_OK;
1661 }
1662
1663 static HRESULT WINAPI MXAttributes_addAttributeFromIndex(IMXAttributes *iface,
1664     VARIANT atts, int index)
1665 {
1666     mxattributes *This = impl_from_IMXAttributes( iface );
1667     FIXME("(%p)->(%s %d): stub\n", This, debugstr_variant(&atts), index);
1668     return E_NOTIMPL;
1669 }
1670
1671 static HRESULT WINAPI MXAttributes_clear(IMXAttributes *iface)
1672 {
1673     mxattributes *This = impl_from_IMXAttributes( iface );
1674     int i;
1675
1676     TRACE("(%p)\n", This);
1677
1678     for (i = 0; i < This->length; i++)
1679     {
1680         SysFreeString(This->attr[i].qname);
1681         SysFreeString(This->attr[i].local);
1682         SysFreeString(This->attr[i].uri);
1683         SysFreeString(This->attr[i].type);
1684         SysFreeString(This->attr[i].value);
1685         memset(&This->attr[i], 0, sizeof(mxattribute));
1686     }
1687
1688     This->length = 0;
1689
1690     return S_OK;
1691 }
1692
1693 static HRESULT WINAPI MXAttributes_removeAttribute(IMXAttributes *iface, int index)
1694 {
1695     mxattributes *This = impl_from_IMXAttributes( iface );
1696     FIXME("(%p)->(%d): stub\n", This, index);
1697     return E_NOTIMPL;
1698 }
1699
1700 static HRESULT WINAPI MXAttributes_setAttribute(IMXAttributes *iface, int index,
1701     BSTR uri, BSTR localName, BSTR QName, BSTR type, BSTR value)
1702 {
1703     mxattributes *This = impl_from_IMXAttributes( iface );
1704     FIXME("(%p)->(%d %s %s %s %s %s): stub\n", This, index, debugstr_w(uri),
1705         debugstr_w(localName), debugstr_w(QName), debugstr_w(type), debugstr_w(value));
1706     return E_NOTIMPL;
1707 }
1708
1709 static HRESULT WINAPI MXAttributes_setAttributes(IMXAttributes *iface, VARIANT atts)
1710 {
1711     mxattributes *This = impl_from_IMXAttributes( iface );
1712     FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&atts));
1713     return E_NOTIMPL;
1714 }
1715
1716 static HRESULT WINAPI MXAttributes_setLocalName(IMXAttributes *iface, int index,
1717     BSTR localName)
1718 {
1719     mxattributes *This = impl_from_IMXAttributes( iface );
1720     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(localName));
1721     return E_NOTIMPL;
1722 }
1723
1724 static HRESULT WINAPI MXAttributes_setQName(IMXAttributes *iface, int index, BSTR QName)
1725 {
1726     mxattributes *This = impl_from_IMXAttributes( iface );
1727     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(QName));
1728     return E_NOTIMPL;
1729 }
1730
1731 static HRESULT WINAPI MXAttributes_setURI(IMXAttributes *iface, int index, BSTR uri)
1732 {
1733     mxattributes *This = impl_from_IMXAttributes( iface );
1734     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(uri));
1735     return E_NOTIMPL;
1736 }
1737
1738 static HRESULT WINAPI MXAttributes_setValue(IMXAttributes *iface, int index, BSTR value)
1739 {
1740     mxattributes *This = impl_from_IMXAttributes( iface );
1741     FIXME("(%p)->(%d %s): stub\n", This, index, debugstr_w(value));
1742     return E_NOTIMPL;
1743 }
1744
1745 static const IMXAttributesVtbl MXAttributesVtbl = {
1746     MXAttributes_QueryInterface,
1747     MXAttributes_AddRef,
1748     MXAttributes_Release,
1749     MXAttributes_GetTypeInfoCount,
1750     MXAttributes_GetTypeInfo,
1751     MXAttributes_GetIDsOfNames,
1752     MXAttributes_Invoke,
1753     MXAttributes_addAttribute,
1754     MXAttributes_addAttributeFromIndex,
1755     MXAttributes_clear,
1756     MXAttributes_removeAttribute,
1757     MXAttributes_setAttribute,
1758     MXAttributes_setAttributes,
1759     MXAttributes_setLocalName,
1760     MXAttributes_setQName,
1761     MXAttributes_setURI,
1762     MXAttributes_setValue
1763 };
1764
1765 static HRESULT WINAPI SAXAttributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **ppObj)
1766 {
1767     mxattributes *This = impl_from_ISAXAttributes( iface );
1768     return IMXAttributes_QueryInterface(&This->IMXAttributes_iface, riid, ppObj);
1769 }
1770
1771 static ULONG WINAPI SAXAttributes_AddRef(ISAXAttributes *iface)
1772 {
1773     mxattributes *This = impl_from_ISAXAttributes( iface );
1774     return IMXAttributes_AddRef(&This->IMXAttributes_iface);
1775 }
1776
1777 static ULONG WINAPI SAXAttributes_Release(ISAXAttributes *iface)
1778 {
1779     mxattributes *This = impl_from_ISAXAttributes( iface );
1780     return IMXAttributes_Release(&This->IMXAttributes_iface);
1781 }
1782
1783 static HRESULT WINAPI SAXAttributes_getLength(ISAXAttributes *iface, int *length)
1784 {
1785     mxattributes *This = impl_from_ISAXAttributes( iface );
1786     TRACE("(%p)->(%p)\n", This, length);
1787
1788     if (!length && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1789        return E_POINTER;
1790
1791     *length = This->length;
1792
1793     return S_OK;
1794 }
1795
1796 static HRESULT WINAPI SAXAttributes_getURI(ISAXAttributes *iface, int index, const WCHAR **uri,
1797     int *len)
1798 {
1799     mxattributes *This = impl_from_ISAXAttributes( iface );
1800
1801     TRACE("(%p)->(%d %p %p)\n", This, index, uri, len);
1802
1803     if (index >= This->length || index < 0) return E_INVALIDARG;
1804     if (!uri || !len) return E_POINTER;
1805
1806     *len = SysStringLen(This->attr[index].uri);
1807     *uri = This->attr[index].uri;
1808
1809     return S_OK;
1810 }
1811
1812 static HRESULT WINAPI SAXAttributes_getLocalName(ISAXAttributes *iface, int index, const WCHAR **name,
1813     int *len)
1814 {
1815     mxattributes *This = impl_from_ISAXAttributes( iface );
1816
1817     TRACE("(%p)->(%d %p %p)\n", This, index, name, len);
1818
1819     if (index >= This->length || index < 0) return E_INVALIDARG;
1820     if (!name || !len) return E_POINTER;
1821
1822     *len = SysStringLen(This->attr[index].local);
1823     *name = This->attr[index].local;
1824
1825     return S_OK;
1826 }
1827
1828 static HRESULT WINAPI SAXAttributes_getQName(ISAXAttributes *iface, int index, const WCHAR **qname, int *length)
1829 {
1830     mxattributes *This = impl_from_ISAXAttributes( iface );
1831
1832     TRACE("(%p)->(%d %p %p)\n", This, index, qname, length);
1833
1834     if (index >= This->length) return E_INVALIDARG;
1835     if (!qname || !length) return E_POINTER;
1836
1837     *qname = This->attr[index].qname;
1838     *length = SysStringLen(This->attr[index].qname);
1839
1840     return S_OK;
1841 }
1842
1843 static HRESULT WINAPI SAXAttributes_getName(ISAXAttributes *iface, int index, const WCHAR **uri, int *uri_len,
1844     const WCHAR **local, int *local_len, const WCHAR **qname, int *qname_len)
1845 {
1846     mxattributes *This = impl_from_ISAXAttributes( iface );
1847
1848     TRACE("(%p)->(%d %p %p %p %p %p %p)\n", This, index, uri, uri_len, local, local_len, qname, qname_len);
1849
1850     if (index >= This->length || index < 0)
1851         return E_INVALIDARG;
1852
1853     if (!uri || !uri_len || !local || !local_len || !qname || !qname_len)
1854         return E_POINTER;
1855
1856     *uri_len = SysStringLen(This->attr[index].uri);
1857     *uri = This->attr[index].uri;
1858
1859     *local_len = SysStringLen(This->attr[index].local);
1860     *local = This->attr[index].local;
1861
1862     *qname_len = SysStringLen(This->attr[index].qname);
1863     *qname = This->attr[index].qname;
1864
1865     TRACE("(%s, %s, %s)\n", debugstr_w(*uri), debugstr_w(*local), debugstr_w(*qname));
1866
1867     return S_OK;
1868 }
1869
1870 static HRESULT WINAPI SAXAttributes_getIndexFromName(ISAXAttributes *iface, const WCHAR *uri, int uri_len,
1871     const WCHAR *name, int len, int *index)
1872 {
1873     mxattributes *This = impl_from_ISAXAttributes( iface );
1874     int i;
1875
1876     TRACE("(%p)->(%s:%d %s:%d %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
1877         debugstr_wn(name, len), len, index);
1878
1879     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1880         return E_POINTER;
1881
1882     if (!uri || !name || !index) return E_INVALIDARG;
1883
1884     for (i = 0; i < This->length; i++)
1885     {
1886         if (uri_len != SysStringLen(This->attr[i].uri)) continue;
1887         if (strncmpW(uri, This->attr[i].uri, uri_len)) continue;
1888
1889         if (len != SysStringLen(This->attr[i].local)) continue;
1890         if (strncmpW(name, This->attr[i].local, len)) continue;
1891
1892         *index = i;
1893         return S_OK;
1894     }
1895
1896     return E_INVALIDARG;
1897 }
1898
1899 static HRESULT WINAPI SAXAttributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR *qname,
1900     int len, int *index)
1901 {
1902     mxattributes *This = impl_from_ISAXAttributes( iface );
1903     int i;
1904
1905     TRACE("(%p)->(%s:%d %p)\n", This, debugstr_wn(qname, len), len, index);
1906
1907     if (!index && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1908         return E_POINTER;
1909
1910     if (!qname || !index || !len) return E_INVALIDARG;
1911
1912     for (i = 0; i < This->length; i++)
1913     {
1914         if (len != SysStringLen(This->attr[i].qname)) continue;
1915         if (strncmpW(qname, This->attr[i].qname, len)) continue;
1916
1917         *index = i;
1918         return S_OK;
1919     }
1920
1921     return E_INVALIDARG;
1922 }
1923
1924 static HRESULT WINAPI SAXAttributes_getType(ISAXAttributes *iface, int index, const WCHAR **type,
1925     int *len)
1926 {
1927     mxattributes *This = impl_from_ISAXAttributes( iface );
1928
1929     TRACE("(%p)->(%d %p %p)\n", This, index, type, len);
1930
1931     if (index >= This->length) return E_INVALIDARG;
1932
1933     if ((!type || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1934        return E_POINTER;
1935
1936     *type = This->attr[index].type;
1937     *len = SysStringLen(This->attr[index].type);
1938
1939     return S_OK;
1940 }
1941
1942 static HRESULT WINAPI SAXAttributes_getTypeFromName(ISAXAttributes *iface, const WCHAR * pUri, int nUri,
1943     const WCHAR * pLocalName, int nLocalName, const WCHAR ** pType, int * nType)
1944 {
1945     mxattributes *This = impl_from_ISAXAttributes( iface );
1946     FIXME("(%p)->(%s:%d %s:%d %p %p): stub\n", This, debugstr_wn(pUri, nUri), nUri,
1947         debugstr_wn(pLocalName, nLocalName), nLocalName, pType, nType);
1948     return E_NOTIMPL;
1949 }
1950
1951 static HRESULT WINAPI SAXAttributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR * pQName,
1952     int nQName, const WCHAR ** pType, int * nType)
1953 {
1954     mxattributes *This = impl_from_ISAXAttributes( iface );
1955     FIXME("(%p)->(%s:%d %p %p): stub\n", This, debugstr_wn(pQName, nQName), nQName, pType, nType);
1956     return E_NOTIMPL;
1957 }
1958
1959 static HRESULT WINAPI SAXAttributes_getValue(ISAXAttributes *iface, int index, const WCHAR **value,
1960     int *len)
1961 {
1962     mxattributes *This = impl_from_ISAXAttributes( iface );
1963
1964     TRACE("(%p)->(%d %p %p)\n", This, index, value, len);
1965
1966     if (index >= This->length) return E_INVALIDARG;
1967
1968     if ((!value || !len) && (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3))
1969        return E_POINTER;
1970
1971     *value = This->attr[index].value;
1972     *len = SysStringLen(This->attr[index].value);
1973
1974     return S_OK;
1975 }
1976
1977 static HRESULT WINAPI SAXAttributes_getValueFromName(ISAXAttributes *iface, const WCHAR *uri,
1978     int uri_len, const WCHAR *name, int name_len, const WCHAR **value, int *value_len)
1979 {
1980     mxattributes *This = impl_from_ISAXAttributes( iface );
1981     HRESULT hr;
1982     int index;
1983
1984     TRACE("(%p)->(%s:%d %s:%d %p %p)\n", This, debugstr_wn(uri, uri_len), uri_len,
1985         debugstr_wn(name, name_len), name_len, value, value_len);
1986
1987     if (!uri || !name || !value || !value_len)
1988         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
1989
1990     hr = ISAXAttributes_getIndexFromName(iface, uri, uri_len, name, name_len, &index);
1991     if (hr == S_OK)
1992         hr = ISAXAttributes_getValue(iface, index, value, value_len);
1993
1994     return hr;
1995 }
1996
1997 static HRESULT WINAPI SAXAttributes_getValueFromQName(ISAXAttributes *iface, const WCHAR *qname,
1998     int qname_len, const WCHAR **value, int *value_len)
1999 {
2000     mxattributes *This = impl_from_ISAXAttributes( iface );
2001     HRESULT hr;
2002     int index;
2003
2004     TRACE("(%p)->(%s:%d %p %p)\n", This, debugstr_wn(qname, qname_len), qname_len, value, value_len);
2005
2006     if (!qname || !value || !value_len)
2007         return (This->class_version == MSXML_DEFAULT || This->class_version == MSXML3) ? E_POINTER : E_INVALIDARG;
2008
2009     hr = ISAXAttributes_getIndexFromQName(iface, qname, qname_len, &index);
2010     if (hr == S_OK)
2011         hr = ISAXAttributes_getValue(iface, index, value, value_len);
2012
2013     return hr;
2014 }
2015
2016 static const ISAXAttributesVtbl SAXAttributesVtbl = {
2017     SAXAttributes_QueryInterface,
2018     SAXAttributes_AddRef,
2019     SAXAttributes_Release,
2020     SAXAttributes_getLength,
2021     SAXAttributes_getURI,
2022     SAXAttributes_getLocalName,
2023     SAXAttributes_getQName,
2024     SAXAttributes_getName,
2025     SAXAttributes_getIndexFromName,
2026     SAXAttributes_getIndexFromQName,
2027     SAXAttributes_getType,
2028     SAXAttributes_getTypeFromName,
2029     SAXAttributes_getTypeFromQName,
2030     SAXAttributes_getValue,
2031     SAXAttributes_getValueFromName,
2032     SAXAttributes_getValueFromQName
2033 };
2034
2035 static HRESULT WINAPI VBSAXAttributes_QueryInterface(
2036         IVBSAXAttributes* iface,
2037         REFIID riid,
2038         void **ppvObject)
2039 {
2040     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2041     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
2042     return ISAXAttributes_QueryInterface(&This->ISAXAttributes_iface, riid, ppvObject);
2043 }
2044
2045 static ULONG WINAPI VBSAXAttributes_AddRef(IVBSAXAttributes* iface)
2046 {
2047     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2048     return ISAXAttributes_AddRef(&This->ISAXAttributes_iface);
2049 }
2050
2051 static ULONG WINAPI VBSAXAttributes_Release(IVBSAXAttributes* iface)
2052 {
2053     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2054     return ISAXAttributes_Release(&This->ISAXAttributes_iface);
2055 }
2056
2057 static HRESULT WINAPI VBSAXAttributes_GetTypeInfoCount( IVBSAXAttributes *iface, UINT* pctinfo )
2058 {
2059     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2060
2061     TRACE("(%p)->(%p)\n", This, pctinfo);
2062
2063     *pctinfo = 1;
2064
2065     return S_OK;
2066 }
2067
2068 static HRESULT WINAPI VBSAXAttributes_GetTypeInfo(
2069     IVBSAXAttributes *iface,
2070     UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
2071 {
2072     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2073     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
2074     return get_typeinfo(IVBSAXAttributes_tid, ppTInfo);
2075 }
2076
2077 static HRESULT WINAPI VBSAXAttributes_GetIDsOfNames(
2078     IVBSAXAttributes *iface,
2079     REFIID riid,
2080     LPOLESTR* rgszNames,
2081     UINT cNames,
2082     LCID lcid,
2083     DISPID* rgDispId)
2084 {
2085     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2086     ITypeInfo *typeinfo;
2087     HRESULT hr;
2088
2089     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
2090           lcid, rgDispId);
2091
2092     if(!rgszNames || cNames == 0 || !rgDispId)
2093         return E_INVALIDARG;
2094
2095     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
2096     if(SUCCEEDED(hr))
2097     {
2098         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2099         ITypeInfo_Release(typeinfo);
2100     }
2101
2102     return hr;
2103 }
2104
2105 static HRESULT WINAPI VBSAXAttributes_Invoke(
2106     IVBSAXAttributes *iface,
2107     DISPID dispIdMember,
2108     REFIID riid,
2109     LCID lcid,
2110     WORD wFlags,
2111     DISPPARAMS* pDispParams,
2112     VARIANT* pVarResult,
2113     EXCEPINFO* pExcepInfo,
2114     UINT* puArgErr)
2115 {
2116     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2117     ITypeInfo *typeinfo;
2118     HRESULT hr;
2119
2120     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
2121           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2122
2123     hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo);
2124     if(SUCCEEDED(hr))
2125     {
2126         hr = ITypeInfo_Invoke(typeinfo, &This->IVBSAXAttributes_iface, dispIdMember, wFlags,
2127                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2128         ITypeInfo_Release(typeinfo);
2129     }
2130
2131     return hr;
2132 }
2133
2134 static HRESULT WINAPI VBSAXAttributes_get_length(IVBSAXAttributes* iface, int *len)
2135 {
2136     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2137     return ISAXAttributes_getLength(&This->ISAXAttributes_iface, len);
2138 }
2139
2140 static HRESULT WINAPI VBSAXAttributes_getURI(IVBSAXAttributes* iface, int index, BSTR *uri)
2141 {
2142     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2143     int len;
2144
2145     return ISAXAttributes_getURI(&This->ISAXAttributes_iface, index, (const WCHAR**)uri, &len);
2146 }
2147
2148 static HRESULT WINAPI VBSAXAttributes_getLocalName(IVBSAXAttributes* iface, int index, BSTR *name)
2149 {
2150     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2151     int len;
2152
2153     return ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, index, (const WCHAR**)name, &len);
2154 }
2155
2156 static HRESULT WINAPI VBSAXAttributes_getQName(IVBSAXAttributes* iface, int index, BSTR *qname)
2157 {
2158     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2159     int len;
2160
2161     return ISAXAttributes_getQName(&This->ISAXAttributes_iface, index, (const WCHAR**)qname, &len);
2162 }
2163
2164 static HRESULT WINAPI VBSAXAttributes_getIndexFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name, int *index)
2165 {
2166     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2167     return ISAXAttributes_getIndexFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2168             name, SysStringLen(name), index);
2169 }
2170
2171 static HRESULT WINAPI VBSAXAttributes_getIndexFromQName(IVBSAXAttributes* iface, BSTR qname, int *index)
2172 {
2173     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2174     return ISAXAttributes_getIndexFromQName(&This->ISAXAttributes_iface, qname,
2175             SysStringLen(qname), index);
2176 }
2177
2178 static HRESULT WINAPI VBSAXAttributes_getType(IVBSAXAttributes* iface, int index,BSTR *type)
2179 {
2180     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2181     int len;
2182
2183     return ISAXAttributes_getType(&This->ISAXAttributes_iface, index, (const WCHAR**)type, &len);
2184 }
2185
2186 static HRESULT WINAPI VBSAXAttributes_getTypeFromName(IVBSAXAttributes* iface, BSTR uri,
2187     BSTR name, BSTR *type)
2188 {
2189     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2190     int len;
2191
2192     return ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2193             name, SysStringLen(name), (const WCHAR**)type, &len);
2194 }
2195
2196 static HRESULT WINAPI VBSAXAttributes_getTypeFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *type)
2197 {
2198     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2199     int len;
2200
2201     return ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
2202             (const WCHAR**)type, &len);
2203 }
2204
2205 static HRESULT WINAPI VBSAXAttributes_getValue(IVBSAXAttributes* iface, int index, BSTR *value)
2206 {
2207     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2208     int len;
2209
2210     return ISAXAttributes_getValue(&This->ISAXAttributes_iface, index, (const WCHAR**)value, &len);
2211 }
2212
2213 static HRESULT WINAPI VBSAXAttributes_getValueFromName(IVBSAXAttributes* iface, BSTR uri, BSTR name,
2214     BSTR *value)
2215 {
2216     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2217     int len;
2218
2219     return ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri),
2220             name, SysStringLen(name), (const WCHAR**)value, &len);
2221 }
2222
2223 static HRESULT WINAPI VBSAXAttributes_getValueFromQName(IVBSAXAttributes* iface, BSTR qname, BSTR *value)
2224 {
2225     mxattributes *This = impl_from_IVBSAXAttributes( iface );
2226     int len;
2227
2228     return ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, qname, SysStringLen(qname),
2229         (const WCHAR**)value, &len);
2230 }
2231
2232 static const struct IVBSAXAttributesVtbl VBSAXAttributesVtbl =
2233 {
2234     VBSAXAttributes_QueryInterface,
2235     VBSAXAttributes_AddRef,
2236     VBSAXAttributes_Release,
2237     VBSAXAttributes_GetTypeInfoCount,
2238     VBSAXAttributes_GetTypeInfo,
2239     VBSAXAttributes_GetIDsOfNames,
2240     VBSAXAttributes_Invoke,
2241     VBSAXAttributes_get_length,
2242     VBSAXAttributes_getURI,
2243     VBSAXAttributes_getLocalName,
2244     VBSAXAttributes_getQName,
2245     VBSAXAttributes_getIndexFromName,
2246     VBSAXAttributes_getIndexFromQName,
2247     VBSAXAttributes_getType,
2248     VBSAXAttributes_getTypeFromName,
2249     VBSAXAttributes_getTypeFromQName,
2250     VBSAXAttributes_getValue,
2251     VBSAXAttributes_getValueFromName,
2252     VBSAXAttributes_getValueFromQName
2253 };
2254
2255 static const tid_t mxattrs_iface_tids[] = {
2256     IMXAttributes_tid,
2257     0
2258 };
2259
2260 static dispex_static_data_t mxattrs_dispex = {
2261     NULL,
2262     IMXAttributes_tid,
2263     NULL,
2264     mxattrs_iface_tids
2265 };
2266
2267 HRESULT SAXAttributes_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
2268 {
2269     static const int default_count = 10;
2270     mxattributes *This;
2271
2272     TRACE("(%p, %p)\n", outer, ppObj);
2273
2274     This = heap_alloc( sizeof (*This) );
2275     if( !This )
2276         return E_OUTOFMEMORY;
2277
2278     This->IMXAttributes_iface.lpVtbl = &MXAttributesVtbl;
2279     This->ISAXAttributes_iface.lpVtbl = &SAXAttributesVtbl;
2280     This->IVBSAXAttributes_iface.lpVtbl = &VBSAXAttributesVtbl;
2281     This->ref = 1;
2282
2283     This->class_version = version;
2284
2285     This->attr = heap_alloc(default_count*sizeof(mxattribute));
2286     This->length = 0;
2287     This->allocated = default_count;
2288
2289     *ppObj = &This->IMXAttributes_iface;
2290
2291     init_dispex(&This->dispex, (IUnknown*)&This->IMXAttributes_iface, &mxattrs_dispex);
2292
2293     TRACE("returning iface %p\n", *ppObj);
2294
2295     return S_OK;
2296 }