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