comctl32: Fix a logical operator typo.
[wine] / dlls / msxml3 / mxwriter.c
1 /*
2  *    MXWriter implementation
3  *
4  * Copyright 2011 Nikolay Sivov for CodeWeavers
5  * Copyright 2011 Thomas Mullaly
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23 #include "config.h"
24
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
33
34 #include "msxml6.h"
35
36 #include "wine/debug.h"
37
38 #include "msxml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
41
42 #ifdef HAVE_LIBXML2
43
44 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
45 static const WCHAR utf8W[]  = {'U','T','F','-','8',0};
46
47 static const char crlfA[] = "\r\n";
48
49 typedef enum
50 {
51     MXWriter_BOM = 0,
52     MXWriter_DisableEscaping,
53     MXWriter_Indent,
54     MXWriter_OmitXmlDecl,
55     MXWriter_Standalone,
56     MXWriter_LastProp
57 } MXWRITER_PROPS;
58
59 typedef struct _mxwriter
60 {
61     IMXWriter IMXWriter_iface;
62     ISAXContentHandler ISAXContentHandler_iface;
63
64     LONG ref;
65     MSXML_VERSION class_version;
66
67     VARIANT_BOOL props[MXWriter_LastProp];
68     BOOL prop_changed;
69     xmlCharEncoding encoding;
70     BSTR version;
71
72     IStream *dest;
73     ULONG   dest_written;
74
75     xmlOutputBufferPtr buffer;
76 } mxwriter;
77
78 static HRESULT bstr_from_xmlCharEncoding(xmlCharEncoding enc, BSTR *encoding)
79 {
80     const char *encodingA;
81
82     if (enc != XML_CHAR_ENCODING_UTF16LE && enc != XML_CHAR_ENCODING_UTF8) {
83         FIXME("Unsupported xmlCharEncoding: %d\n", enc);
84         *encoding = NULL;
85         return E_NOTIMPL;
86     }
87
88     encodingA = xmlGetCharEncodingName(enc);
89     if (encodingA) {
90         DWORD len = MultiByteToWideChar(CP_ACP, 0, encodingA, -1, NULL, 0);
91         *encoding = SysAllocStringLen(NULL, len-1);
92         if(*encoding)
93             MultiByteToWideChar( CP_ACP, 0, encodingA, -1, *encoding, len);
94     } else
95         *encoding = SysAllocStringLen(NULL, 0);
96
97     return *encoding ? S_OK : E_OUTOFMEMORY;
98 }
99
100 /* Attempts to the write data from the mxwriter's buffer to
101  * the destination stream (if there is one).
102  */
103 static HRESULT write_data_to_stream(mxwriter *This)
104 {
105     HRESULT hres;
106     ULONG written = 0;
107     xmlBufferPtr buffer = NULL;
108
109     if (!This->dest)
110         return S_OK;
111
112     /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
113      * 'conv' buffer when UTF8 encoding is used.
114      */
115     if (This->encoding == XML_CHAR_ENCODING_UTF8)
116         buffer = This->buffer->buffer;
117     else
118         buffer = This->buffer->conv;
119
120     if (This->dest_written > buffer->use) {
121         ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->use);
122         return E_FAIL;
123     } else if (This->dest_written == buffer->use && This->encoding != XML_CHAR_ENCODING_UTF8)
124         /* Windows seems to make an empty write call when the encoding is UTF-8 and
125          * all the data has been written to the stream. It doesn't seem make this call
126          * for any other encodings.
127          */
128         return S_OK;
129
130     /* Write the current content from the output buffer into 'dest'.
131      * TODO: Check what Windows does if the IStream doesn't write all of
132      *       the data we give it at once.
133      */
134     hres = IStream_Write(This->dest, buffer->content+This->dest_written,
135                          buffer->use-This->dest_written, &written);
136     if (FAILED(hres)) {
137         WARN("Failed to write data to IStream (%08x)\n", hres);
138         return hres;
139     }
140
141     This->dest_written += written;
142     return hres;
143 }
144
145 static inline HRESULT flush_output_buffer(mxwriter *This)
146 {
147     xmlOutputBufferFlush(This->buffer);
148     return write_data_to_stream(This);
149 }
150
151 /* Resets the mxwriter's output buffer by closing it, then creating a new
152  * output buffer using the given encoding.
153  */
154 static inline void reset_output_buffer(mxwriter *This)
155 {
156     xmlOutputBufferClose(This->buffer);
157     This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
158     This->dest_written = 0;
159 }
160
161 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
162 {
163     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
164 }
165
166 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
167 {
168     return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
169 }
170
171 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
172 {
173     mxwriter *This = impl_from_IMXWriter( iface );
174
175     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
176
177     if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
178          IsEqualGUID( riid, &IID_IDispatch ) ||
179          IsEqualGUID( riid, &IID_IUnknown ) )
180     {
181         *obj = &This->IMXWriter_iface;
182     }
183     else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
184     {
185         *obj = &This->ISAXContentHandler_iface;
186     }
187     else
188     {
189         ERR("interface %s not implemented\n", debugstr_guid(riid));
190         *obj = NULL;
191         return E_NOINTERFACE;
192     }
193
194     IMXWriter_AddRef(iface);
195     return S_OK;
196 }
197
198 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
199 {
200     mxwriter *This = impl_from_IMXWriter( iface );
201     LONG ref = InterlockedIncrement(&This->ref);
202
203     TRACE("(%p)->(%d)\n", This, ref);
204
205     return ref;
206 }
207
208 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
209 {
210     mxwriter *This = impl_from_IMXWriter( iface );
211     ULONG ref = InterlockedDecrement(&This->ref);
212
213     TRACE("(%p)->(%d)\n", This, ref);
214
215     if(!ref)
216     {
217         /* Windows flushes the buffer when the interface is destroyed. */
218         flush_output_buffer(This);
219
220         if (This->dest) IStream_Release(This->dest);
221         SysFreeString(This->version);
222
223         xmlOutputBufferClose(This->buffer);
224         heap_free(This);
225     }
226
227     return ref;
228 }
229
230 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
231 {
232     mxwriter *This = impl_from_IMXWriter( iface );
233
234     TRACE("(%p)->(%p)\n", This, pctinfo);
235
236     *pctinfo = 1;
237
238     return S_OK;
239 }
240
241 static HRESULT WINAPI mxwriter_GetTypeInfo(
242     IMXWriter *iface,
243     UINT iTInfo, LCID lcid,
244     ITypeInfo** ppTInfo )
245 {
246     mxwriter *This = impl_from_IMXWriter( iface );
247
248     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
249
250     return get_typeinfo(IMXWriter_tid, ppTInfo);
251 }
252
253 static HRESULT WINAPI mxwriter_GetIDsOfNames(
254     IMXWriter *iface,
255     REFIID riid, LPOLESTR* rgszNames,
256     UINT cNames, LCID lcid, DISPID* rgDispId )
257 {
258     mxwriter *This = impl_from_IMXWriter( iface );
259     ITypeInfo *typeinfo;
260     HRESULT hr;
261
262     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
263           lcid, rgDispId);
264
265     if(!rgszNames || cNames == 0 || !rgDispId)
266         return E_INVALIDARG;
267
268     hr = get_typeinfo(IMXWriter_tid, &typeinfo);
269     if(SUCCEEDED(hr))
270     {
271         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
272         ITypeInfo_Release(typeinfo);
273     }
274
275     return hr;
276 }
277
278 static HRESULT WINAPI mxwriter_Invoke(
279     IMXWriter *iface,
280     DISPID dispIdMember, REFIID riid, LCID lcid,
281     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
282     EXCEPINFO* pExcepInfo, UINT* puArgErr )
283 {
284     mxwriter *This = impl_from_IMXWriter( iface );
285     ITypeInfo *typeinfo;
286     HRESULT hr;
287
288     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
289           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
290
291     hr = get_typeinfo(IMXWriter_tid, &typeinfo);
292     if(SUCCEEDED(hr))
293     {
294         hr = ITypeInfo_Invoke(typeinfo, &This->IMXWriter_iface, dispIdMember, wFlags,
295                 pDispParams, pVarResult, pExcepInfo, puArgErr);
296         ITypeInfo_Release(typeinfo);
297     }
298
299     return hr;
300 }
301
302 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
303 {
304     mxwriter *This = impl_from_IMXWriter( iface );
305     HRESULT hr;
306
307     TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
308
309     hr = flush_output_buffer(This);
310     if (FAILED(hr))
311         return hr;
312
313     switch (V_VT(&dest))
314     {
315     case VT_EMPTY:
316     {
317         if (This->dest) IStream_Release(This->dest);
318         This->dest = NULL;
319
320         /* We need to reset the output buffer to UTF-16, since the only way
321          * the content of the mxwriter can be accessed now is through a BSTR.
322          */
323         This->encoding = xmlParseCharEncoding("UTF-16");
324         reset_output_buffer(This);
325         break;
326     }
327     case VT_UNKNOWN:
328     {
329         IStream *stream;
330
331         hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
332         if (hr == S_OK)
333         {
334             /* Recreate the output buffer to make sure it's using the correct encoding. */
335             reset_output_buffer(This);
336
337             if (This->dest) IStream_Release(This->dest);
338             This->dest = stream;
339             break;
340         }
341
342         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
343         return E_NOTIMPL;
344     }
345     default:
346         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
347         return E_NOTIMPL;
348     }
349
350     return S_OK;
351 }
352
353 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
354 {
355     mxwriter *This = impl_from_IMXWriter( iface );
356
357     TRACE("(%p)->(%p)\n", This, dest);
358
359     if (!This->dest)
360     {
361         HRESULT hr = flush_output_buffer(This);
362         if (FAILED(hr))
363             return hr;
364
365         /* TODO: Windows always seems to re-encode the XML to UTF-16 (this includes
366          * updating the XML decl so it says "UTF-16" instead of "UTF-8"). We don't
367          * support this yet...
368          */
369         if (This->encoding == XML_CHAR_ENCODING_UTF8) {
370             FIXME("XML re-encoding not supported yet\n");
371             return E_NOTIMPL;
372         }
373
374         V_VT(dest)   = VT_BSTR;
375         V_BSTR(dest) = SysAllocStringLen((const WCHAR*)This->buffer->conv->content,
376                                          This->buffer->conv->use/sizeof(WCHAR));
377
378         return S_OK;
379     }
380     else
381         FIXME("not implemented when stream is set up\n");
382
383     return E_NOTIMPL;
384 }
385
386 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
387 {
388     mxwriter *This = impl_from_IMXWriter( iface );
389
390     TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
391
392     /* FIXME: filter all supported encodings */
393     if (!strcmpW(encoding, utf16W) || !strcmpW(encoding, utf8W))
394     {
395         HRESULT hr;
396         LPSTR enc;
397
398         hr = flush_output_buffer(This);
399         if (FAILED(hr))
400             return hr;
401
402         enc = heap_strdupWtoA(encoding);
403         if (!enc)
404             return E_OUTOFMEMORY;
405
406         This->encoding = xmlParseCharEncoding(enc);
407         heap_free(enc);
408
409         reset_output_buffer(This);
410         return S_OK;
411     }
412     else
413     {
414         FIXME("unsupported encoding %s\n", debugstr_w(encoding));
415         return E_INVALIDARG;
416     }
417 }
418
419 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
420 {
421     mxwriter *This = impl_from_IMXWriter( iface );
422
423     TRACE("(%p)->(%p)\n", This, encoding);
424
425     if (!encoding) return E_POINTER;
426
427     return bstr_from_xmlCharEncoding(This->encoding, encoding);
428 }
429
430 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
431 {
432     mxwriter *This = impl_from_IMXWriter( iface );
433
434     TRACE("(%p)->(%d)\n", This, value);
435     This->props[MXWriter_BOM] = value;
436     This->prop_changed = TRUE;
437
438     return S_OK;
439 }
440
441 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
442 {
443     mxwriter *This = impl_from_IMXWriter( iface );
444
445     TRACE("(%p)->(%p)\n", This, value);
446
447     if (!value) return E_POINTER;
448
449     *value = This->props[MXWriter_BOM];
450
451     return S_OK;
452 }
453
454 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
455 {
456     mxwriter *This = impl_from_IMXWriter( iface );
457
458     TRACE("(%p)->(%d)\n", This, value);
459     This->props[MXWriter_Indent] = value;
460     This->prop_changed = TRUE;
461
462     return S_OK;
463 }
464
465 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
466 {
467     mxwriter *This = impl_from_IMXWriter( iface );
468
469     TRACE("(%p)->(%p)\n", This, value);
470
471     if (!value) return E_POINTER;
472
473     *value = This->props[MXWriter_Indent];
474
475     return S_OK;
476 }
477
478 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
479 {
480     mxwriter *This = impl_from_IMXWriter( iface );
481
482     TRACE("(%p)->(%d)\n", This, value);
483     This->props[MXWriter_Standalone] = value;
484     This->prop_changed = TRUE;
485
486     return S_OK;
487 }
488
489 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
490 {
491     mxwriter *This = impl_from_IMXWriter( iface );
492
493     TRACE("(%p)->(%p)\n", This, value);
494
495     if (!value) return E_POINTER;
496
497     *value = This->props[MXWriter_Standalone];
498
499     return S_OK;
500 }
501
502 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
503 {
504     mxwriter *This = impl_from_IMXWriter( iface );
505
506     TRACE("(%p)->(%d)\n", This, value);
507     This->props[MXWriter_OmitXmlDecl] = value;
508     This->prop_changed = TRUE;
509
510     return S_OK;
511 }
512
513 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
514 {
515     mxwriter *This = impl_from_IMXWriter( iface );
516
517     TRACE("(%p)->(%p)\n", This, value);
518
519     if (!value) return E_POINTER;
520
521     *value = This->props[MXWriter_OmitXmlDecl];
522
523     return S_OK;
524 }
525
526 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
527 {
528     mxwriter *This = impl_from_IMXWriter( iface );
529
530     TRACE("(%p)->(%s)\n", This, debugstr_w(version));
531
532     if (!version) return E_INVALIDARG;
533
534     SysFreeString(This->version);
535     This->version = SysAllocString(version);
536
537     return S_OK;
538 }
539
540 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
541 {
542     mxwriter *This = impl_from_IMXWriter( iface );
543
544     TRACE("(%p)->(%p)\n", This, version);
545
546     if (!version) return E_POINTER;
547
548     return return_bstr(This->version, version);
549 }
550
551 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
552 {
553     mxwriter *This = impl_from_IMXWriter( iface );
554
555     TRACE("(%p)->(%d)\n", This, value);
556     This->props[MXWriter_DisableEscaping] = value;
557     This->prop_changed = TRUE;
558
559     return S_OK;
560 }
561
562 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
563 {
564     mxwriter *This = impl_from_IMXWriter( iface );
565
566     TRACE("(%p)->(%p)\n", This, value);
567
568     if (!value) return E_POINTER;
569
570     *value = This->props[MXWriter_DisableEscaping];
571
572     return S_OK;
573 }
574
575 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
576 {
577     mxwriter *This = impl_from_IMXWriter( iface );
578     TRACE("(%p)\n", This);
579     return flush_output_buffer(This);
580 }
581
582 static const struct IMXWriterVtbl mxwriter_vtbl =
583 {
584     mxwriter_QueryInterface,
585     mxwriter_AddRef,
586     mxwriter_Release,
587     mxwriter_GetTypeInfoCount,
588     mxwriter_GetTypeInfo,
589     mxwriter_GetIDsOfNames,
590     mxwriter_Invoke,
591     mxwriter_put_output,
592     mxwriter_get_output,
593     mxwriter_put_encoding,
594     mxwriter_get_encoding,
595     mxwriter_put_byteOrderMark,
596     mxwriter_get_byteOrderMark,
597     mxwriter_put_indent,
598     mxwriter_get_indent,
599     mxwriter_put_standalone,
600     mxwriter_get_standalone,
601     mxwriter_put_omitXMLDeclaration,
602     mxwriter_get_omitXMLDeclaration,
603     mxwriter_put_version,
604     mxwriter_get_version,
605     mxwriter_put_disableOutputEscaping,
606     mxwriter_get_disableOutputEscaping,
607     mxwriter_flush
608 };
609
610 /*** ISAXContentHandler ***/
611 static HRESULT WINAPI mxwriter_saxcontent_QueryInterface(
612     ISAXContentHandler *iface,
613     REFIID riid,
614     void **obj)
615 {
616     mxwriter *This = impl_from_ISAXContentHandler( iface );
617     return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
618 }
619
620 static ULONG WINAPI mxwriter_saxcontent_AddRef(ISAXContentHandler *iface)
621 {
622     mxwriter *This = impl_from_ISAXContentHandler( iface );
623     return IMXWriter_AddRef(&This->IMXWriter_iface);
624 }
625
626 static ULONG WINAPI mxwriter_saxcontent_Release(ISAXContentHandler *iface)
627 {
628     mxwriter *This = impl_from_ISAXContentHandler( iface );
629     return IMXWriter_Release(&This->IMXWriter_iface);
630 }
631
632 static HRESULT WINAPI mxwriter_saxcontent_putDocumentLocator(
633     ISAXContentHandler *iface,
634     ISAXLocator *locator)
635 {
636     mxwriter *This = impl_from_ISAXContentHandler( iface );
637     FIXME("(%p)->(%p)\n", This, locator);
638     return E_NOTIMPL;
639 }
640
641 static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *iface)
642 {
643     mxwriter *This = impl_from_ISAXContentHandler( iface );
644     xmlChar *s;
645
646     TRACE("(%p)\n", This);
647
648     /* If properties have been changed since the last "endDocument" call
649      * we need to reset the output buffer. If we don't the output buffer
650      * could end up with multiple XML documents in it, plus this seems to
651      * be how Windows works.
652      */
653     if (This->prop_changed) {
654         reset_output_buffer(This);
655         This->prop_changed = FALSE;
656     }
657
658     if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
659
660     /* version */
661     xmlOutputBufferWriteString(This->buffer, "<?xml version=\"");
662     s = xmlchar_from_wchar(This->version);
663     xmlOutputBufferWriteString(This->buffer, (char*)s);
664     heap_free(s);
665     xmlOutputBufferWriteString(This->buffer, "\"");
666
667     /* encoding */
668     xmlOutputBufferWriteString(This->buffer, " encoding=\"");
669     xmlOutputBufferWriteString(This->buffer, xmlGetCharEncodingName(This->encoding));
670     xmlOutputBufferWriteString(This->buffer, "\"");
671
672     /* standalone */
673     xmlOutputBufferWriteString(This->buffer, " standalone=\"");
674     if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
675         xmlOutputBufferWriteString(This->buffer, "yes\"?>");
676     else
677         xmlOutputBufferWriteString(This->buffer, "no\"?>");
678
679     xmlOutputBufferWriteString(This->buffer, crlfA);
680
681     if (This->dest && This->encoding == XML_CHAR_ENCODING_UTF16LE) {
682         static const CHAR utf16BOM[] = {0xff,0xfe};
683
684         if (This->props[MXWriter_BOM] == VARIANT_TRUE)
685             /* Windows passes a NULL pointer as the pcbWritten parameter and
686              * ignores any error codes returned from this Write call.
687              */
688             IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
689     }
690
691     return S_OK;
692 }
693
694 static HRESULT WINAPI mxwriter_saxcontent_endDocument(ISAXContentHandler *iface)
695 {
696     mxwriter *This = impl_from_ISAXContentHandler( iface );
697     TRACE("(%p)\n", This);
698     This->prop_changed = FALSE;
699     return flush_output_buffer(This);
700 }
701
702 static HRESULT WINAPI mxwriter_saxcontent_startPrefixMapping(
703     ISAXContentHandler *iface,
704     const WCHAR *prefix,
705     int nprefix,
706     const WCHAR *uri,
707     int nuri)
708 {
709     mxwriter *This = impl_from_ISAXContentHandler( iface );
710     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
711     return E_NOTIMPL;
712 }
713
714 static HRESULT WINAPI mxwriter_saxcontent_endPrefixMapping(
715     ISAXContentHandler *iface,
716     const WCHAR *prefix,
717     int nprefix)
718 {
719     mxwriter *This = impl_from_ISAXContentHandler( iface );
720     FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
721     return E_NOTIMPL;
722 }
723
724 static HRESULT WINAPI mxwriter_saxcontent_startElement(
725     ISAXContentHandler *iface,
726     const WCHAR *namespaceUri,
727     int nnamespaceUri,
728     const WCHAR *local_name,
729     int nlocal_name,
730     const WCHAR *QName,
731     int nQName,
732     ISAXAttributes *attr)
733 {
734     mxwriter *This = impl_from_ISAXContentHandler( iface );
735     xmlChar *s;
736
737     TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
738         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
739
740     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
741         return E_INVALIDARG;
742
743     xmlOutputBufferWriteString(This->buffer, "<");
744     s = xmlchar_from_wchar(QName);
745     xmlOutputBufferWriteString(This->buffer, (char*)s);
746     heap_free(s);
747
748     if (attr)
749     {
750         HRESULT hr;
751         INT length;
752         INT i;
753
754         hr = ISAXAttributes_getLength(attr, &length);
755         if (FAILED(hr)) return hr;
756
757         for (i = 0; i < length; i++)
758         {
759             const WCHAR *str;
760             INT len;
761
762             hr = ISAXAttributes_getQName(attr, i, &str, &len);
763             if (FAILED(hr)) return hr;
764
765             /* space separator in front of every attribute */
766             xmlOutputBufferWriteString(This->buffer, " ");
767
768             s = xmlchar_from_wchar(str);
769             xmlOutputBufferWriteString(This->buffer, (char*)s);
770             heap_free(s);
771
772             xmlOutputBufferWriteString(This->buffer, "=\"");
773
774             hr = ISAXAttributes_getValue(attr, i, &str, &len);
775             if (FAILED(hr)) return hr;
776
777             s = xmlchar_from_wchar(str);
778             xmlOutputBufferWriteString(This->buffer, (char*)s);
779             heap_free(s);
780
781             xmlOutputBufferWriteString(This->buffer, "\"");
782         }
783     }
784
785     xmlOutputBufferWriteString(This->buffer, ">");
786
787     return S_OK;
788 }
789
790 static HRESULT WINAPI mxwriter_saxcontent_endElement(
791     ISAXContentHandler *iface,
792     const WCHAR *namespaceUri,
793     int nnamespaceUri,
794     const WCHAR * local_name,
795     int nlocal_name,
796     const WCHAR *QName,
797     int nQName)
798 {
799     mxwriter *This = impl_from_ISAXContentHandler( iface );
800     xmlChar *s;
801
802     TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
803         debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName));
804
805     if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
806         return E_INVALIDARG;
807
808     xmlOutputBufferWriteString(This->buffer, "</");
809     s = xmlchar_from_wchar(QName);
810     xmlOutputBufferWriteString(This->buffer, (char*)s);
811     heap_free(s);
812     xmlOutputBufferWriteString(This->buffer, ">");
813
814     return S_OK;
815 }
816
817 static HRESULT WINAPI mxwriter_saxcontent_characters(
818     ISAXContentHandler *iface,
819     const WCHAR *chars,
820     int nchars)
821 {
822     mxwriter *This = impl_from_ISAXContentHandler( iface );
823
824     TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
825
826     if (!chars) return E_INVALIDARG;
827
828     if (nchars)
829     {
830         xmlChar *s = xmlchar_from_wcharn(chars, nchars);
831         xmlOutputBufferWriteString(This->buffer, (char*)s);
832         heap_free(s);
833     }
834
835     return S_OK;
836 }
837
838 static HRESULT WINAPI mxwriter_saxcontent_ignorableWhitespace(
839     ISAXContentHandler *iface,
840     const WCHAR *chars,
841     int nchars)
842 {
843     mxwriter *This = impl_from_ISAXContentHandler( iface );
844     FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
845     return E_NOTIMPL;
846 }
847
848 static HRESULT WINAPI mxwriter_saxcontent_processingInstruction(
849     ISAXContentHandler *iface,
850     const WCHAR *target,
851     int ntarget,
852     const WCHAR *data,
853     int ndata)
854 {
855     mxwriter *This = impl_from_ISAXContentHandler( iface );
856     FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
857     return E_NOTIMPL;
858 }
859
860 static HRESULT WINAPI mxwriter_saxcontent_skippedEntity(
861     ISAXContentHandler *iface,
862     const WCHAR *name,
863     int nname)
864 {
865     mxwriter *This = impl_from_ISAXContentHandler( iface );
866     FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
867     return E_NOTIMPL;
868 }
869
870 static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl =
871 {
872     mxwriter_saxcontent_QueryInterface,
873     mxwriter_saxcontent_AddRef,
874     mxwriter_saxcontent_Release,
875     mxwriter_saxcontent_putDocumentLocator,
876     mxwriter_saxcontent_startDocument,
877     mxwriter_saxcontent_endDocument,
878     mxwriter_saxcontent_startPrefixMapping,
879     mxwriter_saxcontent_endPrefixMapping,
880     mxwriter_saxcontent_startElement,
881     mxwriter_saxcontent_endElement,
882     mxwriter_saxcontent_characters,
883     mxwriter_saxcontent_ignorableWhitespace,
884     mxwriter_saxcontent_processingInstruction,
885     mxwriter_saxcontent_skippedEntity
886 };
887
888 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *pUnkOuter, void **ppObj)
889 {
890     static const WCHAR version10W[] = {'1','.','0',0};
891     mxwriter *This;
892
893     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
894
895     if (pUnkOuter) FIXME("support aggregation, outer\n");
896
897     This = heap_alloc( sizeof (*This) );
898     if(!This)
899         return E_OUTOFMEMORY;
900
901     This->IMXWriter_iface.lpVtbl = &mxwriter_vtbl;
902     This->ISAXContentHandler_iface.lpVtbl = &mxwriter_saxcontent_vtbl;
903     This->ref = 1;
904     This->class_version = version;
905
906     This->props[MXWriter_BOM] = VARIANT_TRUE;
907     This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
908     This->props[MXWriter_Indent] = VARIANT_FALSE;
909     This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
910     This->props[MXWriter_Standalone] = VARIANT_FALSE;
911     This->prop_changed = FALSE;
912     This->encoding   = xmlParseCharEncoding("UTF-16");
913     This->version    = SysAllocString(version10W);
914
915     This->dest = NULL;
916     This->dest_written = 0;
917
918     This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
919
920     *ppObj = &This->IMXWriter_iface;
921
922     TRACE("returning iface %p\n", *ppObj);
923
924     return S_OK;
925 }
926
927 #else
928
929 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *pUnkOuter, void **obj)
930 {
931     MESSAGE("This program tried to use a MXXMLWriter object, but\n"
932             "libxml2 support was not present at compile time.\n");
933     return E_NOTIMPL;
934 }
935
936 #endif /* HAVE_LIBXML2 */