msxml3: Escape '<','&','"' and '>' in attribute value.
[wine.git] / dlls / msxml3 / mxwriter.c
blob5c5aca8c3200d752c08faa648cf3c7aea85aabef
1 /*
2 * MXWriter implementation
4 * Copyright 2011 Nikolay Sivov for CodeWeavers
5 * Copyright 2011 Thomas Mullaly
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.
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.
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
22 #define COBJMACROS
23 #include "config.h"
25 #include <stdarg.h>
26 #ifdef HAVE_LIBXML2
27 # include <libxml/parser.h>
28 #endif
30 #include "windef.h"
31 #include "winbase.h"
32 #include "ole2.h"
34 #include "msxml6.h"
36 #include "wine/debug.h"
38 #include "msxml_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
42 #ifdef HAVE_LIBXML2
44 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
45 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
47 static const char crlfA[] = "\r\n";
48 static const WCHAR emptyW[] = {0};
50 typedef enum
52 MXWriter_BOM = 0,
53 MXWriter_DisableEscaping,
54 MXWriter_Indent,
55 MXWriter_OmitXmlDecl,
56 MXWriter_Standalone,
57 MXWriter_LastProp
58 } MXWRITER_PROPS;
60 typedef struct
62 DispatchEx dispex;
63 IMXWriter IMXWriter_iface;
64 ISAXContentHandler ISAXContentHandler_iface;
66 LONG ref;
67 MSXML_VERSION class_version;
69 VARIANT_BOOL props[MXWriter_LastProp];
70 BOOL prop_changed;
71 xmlCharEncoding encoding;
72 BSTR version;
74 /* contains a pending (or not closed yet) element name or NULL if
75 we don't have to close */
76 BSTR element;
78 IStream *dest;
79 ULONG dest_written;
81 int decl_count; /* practically how many times startDocument was called */
82 int decl_written; /* byte length of document prolog */
84 xmlOutputBufferPtr buffer;
85 } mxwriter;
87 static HRESULT bstr_from_xmlCharEncoding(xmlCharEncoding enc, BSTR *encoding)
89 const char *encodingA;
91 if (enc != XML_CHAR_ENCODING_UTF16LE && enc != XML_CHAR_ENCODING_UTF8) {
92 FIXME("Unsupported xmlCharEncoding: %d\n", enc);
93 *encoding = NULL;
94 return E_NOTIMPL;
97 encodingA = xmlGetCharEncodingName(enc);
98 if (encodingA) {
99 DWORD len = MultiByteToWideChar(CP_ACP, 0, encodingA, -1, NULL, 0);
100 *encoding = SysAllocStringLen(NULL, len-1);
101 if(*encoding)
102 MultiByteToWideChar( CP_ACP, 0, encodingA, -1, *encoding, len);
103 } else
104 *encoding = SysAllocStringLen(NULL, 0);
106 return *encoding ? S_OK : E_OUTOFMEMORY;
109 /* escapes special characters like:
110 '<' -> "&lt;"
111 '&' -> "&amp;"
112 '"' -> "&quot;"
113 '>' -> "&gt;"
115 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
117 static const WCHAR ltW[] = {'&','l','t',';'};
118 static const WCHAR ampW[] = {'&','a','m','p',';'};
119 static const WCHAR quotW[] = {'&','q','u','o','t',';'};
120 static const WCHAR gtW[] = {'&','g','t',';'};
122 const int default_alloc = 100;
123 const int grow_thresh = 10;
124 int p = *len, conv_len;
125 WCHAR *ptr, *ret;
127 /* default buffer size to something if length is unknown */
128 conv_len = *len == -1 ? default_alloc : max(2**len, default_alloc);
129 ptr = ret = heap_alloc(conv_len*sizeof(WCHAR));
131 while (*str && p)
133 if (ptr - ret > conv_len - grow_thresh)
135 int written = ptr - ret;
136 conv_len *= 2;
137 ptr = ret = heap_realloc(ret, conv_len*sizeof(WCHAR));
138 ptr += written;
141 switch (*str)
143 case '<':
144 memcpy(ptr, ltW, sizeof(ltW));
145 ptr += sizeof(ltW)/sizeof(WCHAR);
146 break;
147 case '&':
148 memcpy(ptr, ampW, sizeof(ampW));
149 ptr += sizeof(ampW)/sizeof(WCHAR);
150 break;
151 case '"':
152 memcpy(ptr, quotW, sizeof(quotW));
153 ptr += sizeof(quotW)/sizeof(WCHAR);
154 break;
155 case '>':
156 memcpy(ptr, gtW, sizeof(gtW));
157 ptr += sizeof(gtW)/sizeof(WCHAR);
158 break;
159 default:
160 *ptr++ = *str;
161 break;
164 str++;
165 if (*len != -1) p--;
168 if (*len != -1) *len = ptr-ret;
169 *++ptr = 0;
171 return ret;
174 /* creates UTF-8 encoded prolog string with specified or store encoding value */
175 static int write_prolog_buffer(const mxwriter *This, xmlCharEncoding enc, xmlOutputBufferPtr buffer)
177 static const char version[] = "<?xml version=\"";
178 static const char encoding[] = " encoding=\"";
179 static const char standalone[] = " standalone=\"";
180 static const char yes[] = "yes\"?>";
181 static const char no[] = "no\"?>";
182 xmlBufferPtr buf;
183 xmlChar *s;
184 int use;
186 if (enc == XML_CHAR_ENCODING_UTF8)
187 buf = buffer->buffer;
188 else
189 buf = buffer->conv;
191 use = buf->use;
193 /* version */
194 xmlOutputBufferWrite(buffer, sizeof(version)-1, version);
195 s = xmlchar_from_wchar(This->version);
196 xmlOutputBufferWriteString(buffer, (char*)s);
197 heap_free(s);
198 xmlOutputBufferWrite(buffer, 1, "\"");
200 /* encoding */
201 xmlOutputBufferWrite(buffer, sizeof(encoding)-1, encoding);
202 xmlOutputBufferWriteString(buffer, xmlGetCharEncodingName(enc));
203 xmlOutputBufferWrite(buffer, 1, "\"");
205 /* standalone */
206 xmlOutputBufferWrite(buffer, sizeof(standalone)-1, standalone);
207 if (This->props[MXWriter_Standalone] == VARIANT_TRUE)
208 xmlOutputBufferWrite(buffer, sizeof(yes)-1, yes);
209 else
210 xmlOutputBufferWrite(buffer, sizeof(no)-1, no);
212 xmlOutputBufferWrite(buffer, sizeof(crlfA)-1, crlfA);
213 xmlOutputBufferFlush(buffer);
215 return buf->use - use;
218 /* Attempts to the write data from the mxwriter's buffer to
219 * the destination stream (if there is one).
221 static HRESULT write_data_to_stream(mxwriter *This)
223 xmlBufferPtr buffer;
224 ULONG written = 0;
225 HRESULT hr;
227 if (!This->dest)
228 return S_OK;
230 /* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
231 * 'conv' buffer when UTF8 encoding is used.
233 if (This->encoding == XML_CHAR_ENCODING_UTF8)
234 buffer = This->buffer->buffer;
235 else
236 buffer = This->buffer->conv;
238 if (This->dest_written > buffer->use) {
239 ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->use);
240 return E_FAIL;
241 } else if (This->dest_written == buffer->use && This->encoding != XML_CHAR_ENCODING_UTF8)
242 /* Windows seems to make an empty write call when the encoding is UTF-8 and
243 * all the data has been written to the stream. It doesn't seem make this call
244 * for any other encodings.
246 return S_OK;
248 /* Write the current content from the output buffer into 'dest'.
249 * TODO: Check what Windows does if the IStream doesn't write all of
250 * the data we give it at once.
252 hr = IStream_Write(This->dest, buffer->content+This->dest_written,
253 buffer->use-This->dest_written, &written);
254 if (FAILED(hr)) {
255 WARN("Failed to write data to IStream (0x%08x)\n", hr);
256 return hr;
259 This->dest_written += written;
260 return hr;
263 static void write_output_buffer(const mxwriter *This, const char *data, int len)
265 xmlOutputBufferWrite(This->buffer, len, data);
268 static void write_output_buffer_str(const mxwriter *This, const char *data)
270 xmlOutputBufferWriteString(This->buffer, data);
273 /* Newly added element start tag left unclosed cause for empty elements
274 we have to close it differently. */
275 static void close_element_starttag(const mxwriter *This)
277 static const char gt = '>';
278 if (!This->element) return;
279 write_output_buffer(This, &gt, 1);
282 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
284 SysFreeString(This->element);
285 This->element = name ? SysAllocStringLen(name, len) : NULL;
288 static inline HRESULT flush_output_buffer(mxwriter *This)
290 close_element_starttag(This);
291 set_element_name(This, NULL, 0);
292 xmlOutputBufferFlush(This->buffer);
293 return write_data_to_stream(This);
296 /* Resets the mxwriter's output buffer by closing it, then creating a new
297 * output buffer using the given encoding.
299 static inline void reset_output_buffer(mxwriter *This)
301 xmlOutputBufferClose(This->buffer);
302 This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
303 This->dest_written = 0;
304 This->decl_count = 0;
305 This->decl_written = 0;
308 static HRESULT writer_set_property(mxwriter *writer, MXWRITER_PROPS property, VARIANT_BOOL value)
310 writer->props[property] = value;
311 writer->prop_changed = TRUE;
312 return S_OK;
315 static HRESULT writer_get_property(const mxwriter *writer, MXWRITER_PROPS property, VARIANT_BOOL *value)
317 if (!value) return E_POINTER;
318 *value = writer->props[property];
319 return S_OK;
322 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
324 return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
327 static inline mxwriter *impl_from_ISAXContentHandler(ISAXContentHandler *iface)
329 return CONTAINING_RECORD(iface, mxwriter, ISAXContentHandler_iface);
332 static HRESULT WINAPI mxwriter_QueryInterface(IMXWriter *iface, REFIID riid, void **obj)
334 mxwriter *This = impl_from_IMXWriter( iface );
336 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
338 *obj = NULL;
340 if ( IsEqualGUID( riid, &IID_IMXWriter ) ||
341 IsEqualGUID( riid, &IID_IDispatch ) ||
342 IsEqualGUID( riid, &IID_IUnknown ) )
344 *obj = &This->IMXWriter_iface;
346 else if ( IsEqualGUID( riid, &IID_ISAXContentHandler ) )
348 *obj = &This->ISAXContentHandler_iface;
350 else if (dispex_query_interface(&This->dispex, riid, obj))
352 return *obj ? S_OK : E_NOINTERFACE;
354 else
356 ERR("interface %s not implemented\n", debugstr_guid(riid));
357 *obj = NULL;
358 return E_NOINTERFACE;
361 IMXWriter_AddRef(iface);
362 return S_OK;
365 static ULONG WINAPI mxwriter_AddRef(IMXWriter *iface)
367 mxwriter *This = impl_from_IMXWriter( iface );
368 LONG ref = InterlockedIncrement(&This->ref);
370 TRACE("(%p)->(%d)\n", This, ref);
372 return ref;
375 static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
377 mxwriter *This = impl_from_IMXWriter( iface );
378 ULONG ref = InterlockedDecrement(&This->ref);
380 TRACE("(%p)->(%d)\n", This, ref);
382 if(!ref)
384 /* Windows flushes the buffer when the interface is destroyed. */
385 flush_output_buffer(This);
387 if (This->dest) IStream_Release(This->dest);
388 SysFreeString(This->version);
390 xmlOutputBufferClose(This->buffer);
391 SysFreeString(This->element);
392 release_dispex(&This->dispex);
393 heap_free(This);
396 return ref;
399 static HRESULT WINAPI mxwriter_GetTypeInfoCount(IMXWriter *iface, UINT* pctinfo)
401 mxwriter *This = impl_from_IMXWriter( iface );
402 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
405 static HRESULT WINAPI mxwriter_GetTypeInfo(
406 IMXWriter *iface,
407 UINT iTInfo, LCID lcid,
408 ITypeInfo** ppTInfo )
410 mxwriter *This = impl_from_IMXWriter( iface );
411 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
412 iTInfo, lcid, ppTInfo);
415 static HRESULT WINAPI mxwriter_GetIDsOfNames(
416 IMXWriter *iface,
417 REFIID riid, LPOLESTR* rgszNames,
418 UINT cNames, LCID lcid, DISPID* rgDispId )
420 mxwriter *This = impl_from_IMXWriter( iface );
421 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
422 riid, rgszNames, cNames, lcid, rgDispId);
425 static HRESULT WINAPI mxwriter_Invoke(
426 IMXWriter *iface,
427 DISPID dispIdMember, REFIID riid, LCID lcid,
428 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
429 EXCEPINFO* pExcepInfo, UINT* puArgErr )
431 mxwriter *This = impl_from_IMXWriter( iface );
432 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
433 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
436 static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
438 mxwriter *This = impl_from_IMXWriter( iface );
439 HRESULT hr;
441 TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
443 hr = flush_output_buffer(This);
444 if (FAILED(hr))
445 return hr;
447 switch (V_VT(&dest))
449 case VT_EMPTY:
451 if (This->dest) IStream_Release(This->dest);
452 This->dest = NULL;
453 reset_output_buffer(This);
454 break;
456 case VT_UNKNOWN:
458 IStream *stream;
460 hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
461 if (hr == S_OK)
463 /* Recreate the output buffer to make sure it's using the correct encoding. */
464 reset_output_buffer(This);
466 if (This->dest) IStream_Release(This->dest);
467 This->dest = stream;
468 break;
471 FIXME("unhandled interface type for VT_UNKNOWN destination\n");
472 return E_NOTIMPL;
474 default:
475 FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
476 return E_NOTIMPL;
479 return S_OK;
482 static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
484 mxwriter *This = impl_from_IMXWriter( iface );
486 TRACE("(%p)->(%p)\n", This, dest);
488 if (!This->dest)
490 xmlOutputBufferPtr prolog;
491 BSTR output, body = NULL;
492 xmlBufferPtr buffer;
493 WCHAR *ptr;
494 HRESULT hr;
495 int i;
497 hr = flush_output_buffer(This);
498 if (FAILED(hr))
499 return hr;
501 if (This->decl_count)
503 prolog = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(xmlParseCharEncoding("UTF-16")));
504 write_prolog_buffer(This, xmlParseCharEncoding("UTF-16"), prolog);
506 else
507 prolog = NULL;
509 /* optimize some paticular cases */
510 /* 1. no prolog and UTF-8 buffer */
511 if (This->encoding == XML_CHAR_ENCODING_UTF8 && !prolog)
513 V_VT(dest) = VT_BSTR;
514 V_BSTR(dest) = bstr_from_xmlChar(This->buffer->buffer->content);
515 return S_OK;
518 /* 2. no prolog and UTF-16 buffer */
519 if (!prolog)
521 V_VT(dest) = VT_BSTR;
522 V_BSTR(dest) = SysAllocStringLen((const WCHAR*)This->buffer->conv->content,
523 This->buffer->conv->use/sizeof(WCHAR));
524 return S_OK;
527 V_BSTR(dest) = NULL;
529 if (This->encoding == XML_CHAR_ENCODING_UTF8)
531 buffer = This->buffer->buffer;
532 body = bstr_from_xmlChar(buffer->content+This->decl_written);
533 ptr = output = SysAllocStringByteLen(NULL, prolog->conv->use*This->decl_count +
534 SysStringByteLen(body));
536 else
538 buffer = This->buffer->conv;
539 ptr = output = SysAllocStringByteLen(NULL, prolog->conv->use*This->decl_count +
540 buffer->use - This->decl_written);
543 /* write prolog part */
544 i = This->decl_count;
545 while (i--)
547 memcpy(ptr, prolog->conv->content, prolog->conv->use);
548 ptr += prolog->conv->use/sizeof(WCHAR);
550 xmlOutputBufferClose(prolog);
552 /* write main part */
553 if (body)
555 memcpy(ptr, body, SysStringByteLen(body));
556 SysFreeString(body);
558 else
559 memcpy(ptr, buffer->content + This->decl_written, buffer->use-This->decl_written);
561 V_VT(dest) = VT_BSTR;
562 V_BSTR(dest) = output;
564 return S_OK;
566 else
567 FIXME("not implemented when stream is set up\n");
569 return E_NOTIMPL;
572 static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
574 mxwriter *This = impl_from_IMXWriter( iface );
576 TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
578 /* FIXME: filter all supported encodings */
579 if (!strcmpW(encoding, utf16W) || !strcmpW(encoding, utf8W))
581 HRESULT hr;
582 LPSTR enc;
584 hr = flush_output_buffer(This);
585 if (FAILED(hr))
586 return hr;
588 enc = heap_strdupWtoA(encoding);
589 if (!enc)
590 return E_OUTOFMEMORY;
592 This->encoding = xmlParseCharEncoding(enc);
593 heap_free(enc);
595 TRACE("got encoding %d\n", This->encoding);
596 reset_output_buffer(This);
597 return S_OK;
599 else
601 FIXME("unsupported encoding %s\n", debugstr_w(encoding));
602 return E_INVALIDARG;
606 static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
608 mxwriter *This = impl_from_IMXWriter( iface );
610 TRACE("(%p)->(%p)\n", This, encoding);
612 if (!encoding) return E_POINTER;
614 return bstr_from_xmlCharEncoding(This->encoding, encoding);
617 static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
619 mxwriter *This = impl_from_IMXWriter( iface );
621 TRACE("(%p)->(%d)\n", This, value);
622 return writer_set_property(This, MXWriter_BOM, value);
625 static HRESULT WINAPI mxwriter_get_byteOrderMark(IMXWriter *iface, VARIANT_BOOL *value)
627 mxwriter *This = impl_from_IMXWriter( iface );
629 TRACE("(%p)->(%p)\n", This, value);
630 return writer_get_property(This, MXWriter_BOM, value);
633 static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
635 mxwriter *This = impl_from_IMXWriter( iface );
637 TRACE("(%p)->(%d)\n", This, value);
638 return writer_set_property(This, MXWriter_Indent, value);
641 static HRESULT WINAPI mxwriter_get_indent(IMXWriter *iface, VARIANT_BOOL *value)
643 mxwriter *This = impl_from_IMXWriter( iface );
645 TRACE("(%p)->(%p)\n", This, value);
646 return writer_get_property(This, MXWriter_Indent, value);
649 static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL value)
651 mxwriter *This = impl_from_IMXWriter( iface );
653 TRACE("(%p)->(%d)\n", This, value);
654 return writer_set_property(This, MXWriter_Standalone, value);
657 static HRESULT WINAPI mxwriter_get_standalone(IMXWriter *iface, VARIANT_BOOL *value)
659 mxwriter *This = impl_from_IMXWriter( iface );
661 TRACE("(%p)->(%p)\n", This, value);
662 return writer_get_property(This, MXWriter_Standalone, value);
665 static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL value)
667 mxwriter *This = impl_from_IMXWriter( iface );
669 TRACE("(%p)->(%d)\n", This, value);
670 return writer_set_property(This, MXWriter_OmitXmlDecl, value);
673 static HRESULT WINAPI mxwriter_get_omitXMLDeclaration(IMXWriter *iface, VARIANT_BOOL *value)
675 mxwriter *This = impl_from_IMXWriter( iface );
677 TRACE("(%p)->(%p)\n", This, value);
678 return writer_get_property(This, MXWriter_OmitXmlDecl, value);
681 static HRESULT WINAPI mxwriter_put_version(IMXWriter *iface, BSTR version)
683 mxwriter *This = impl_from_IMXWriter( iface );
685 TRACE("(%p)->(%s)\n", This, debugstr_w(version));
687 if (!version) return E_INVALIDARG;
689 SysFreeString(This->version);
690 This->version = SysAllocString(version);
692 return S_OK;
695 static HRESULT WINAPI mxwriter_get_version(IMXWriter *iface, BSTR *version)
697 mxwriter *This = impl_from_IMXWriter( iface );
699 TRACE("(%p)->(%p)\n", This, version);
701 if (!version) return E_POINTER;
703 return return_bstr(This->version, version);
706 static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL value)
708 mxwriter *This = impl_from_IMXWriter( iface );
710 TRACE("(%p)->(%d)\n", This, value);
711 return writer_set_property(This, MXWriter_DisableEscaping, value);
714 static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIANT_BOOL *value)
716 mxwriter *This = impl_from_IMXWriter( iface );
718 TRACE("(%p)->(%p)\n", This, value);
719 return writer_get_property(This, MXWriter_DisableEscaping, value);
722 static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
724 mxwriter *This = impl_from_IMXWriter( iface );
725 TRACE("(%p)\n", This);
726 return flush_output_buffer(This);
729 static const struct IMXWriterVtbl MXWriterVtbl =
731 mxwriter_QueryInterface,
732 mxwriter_AddRef,
733 mxwriter_Release,
734 mxwriter_GetTypeInfoCount,
735 mxwriter_GetTypeInfo,
736 mxwriter_GetIDsOfNames,
737 mxwriter_Invoke,
738 mxwriter_put_output,
739 mxwriter_get_output,
740 mxwriter_put_encoding,
741 mxwriter_get_encoding,
742 mxwriter_put_byteOrderMark,
743 mxwriter_get_byteOrderMark,
744 mxwriter_put_indent,
745 mxwriter_get_indent,
746 mxwriter_put_standalone,
747 mxwriter_get_standalone,
748 mxwriter_put_omitXMLDeclaration,
749 mxwriter_get_omitXMLDeclaration,
750 mxwriter_put_version,
751 mxwriter_get_version,
752 mxwriter_put_disableOutputEscaping,
753 mxwriter_get_disableOutputEscaping,
754 mxwriter_flush
757 /*** ISAXContentHandler ***/
758 static HRESULT WINAPI mxwriter_saxcontent_QueryInterface(
759 ISAXContentHandler *iface,
760 REFIID riid,
761 void **obj)
763 mxwriter *This = impl_from_ISAXContentHandler( iface );
764 return IMXWriter_QueryInterface(&This->IMXWriter_iface, riid, obj);
767 static ULONG WINAPI mxwriter_saxcontent_AddRef(ISAXContentHandler *iface)
769 mxwriter *This = impl_from_ISAXContentHandler( iface );
770 return IMXWriter_AddRef(&This->IMXWriter_iface);
773 static ULONG WINAPI mxwriter_saxcontent_Release(ISAXContentHandler *iface)
775 mxwriter *This = impl_from_ISAXContentHandler( iface );
776 return IMXWriter_Release(&This->IMXWriter_iface);
779 static HRESULT WINAPI mxwriter_saxcontent_putDocumentLocator(
780 ISAXContentHandler *iface,
781 ISAXLocator *locator)
783 mxwriter *This = impl_from_ISAXContentHandler( iface );
784 FIXME("(%p)->(%p)\n", This, locator);
785 return E_NOTIMPL;
788 static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *iface)
790 mxwriter *This = impl_from_ISAXContentHandler( iface );
792 TRACE("(%p)\n", This);
794 /* If properties have been changed since the last "endDocument" call
795 * we need to reset the output buffer. If we don't the output buffer
796 * could end up with multiple XML documents in it, plus this seems to
797 * be how Windows works.
799 if (This->prop_changed) {
800 reset_output_buffer(This);
801 This->prop_changed = FALSE;
804 if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
806 This->decl_count++;
807 This->decl_written += write_prolog_buffer(This, This->encoding, This->buffer);
809 if (This->dest && This->encoding == XML_CHAR_ENCODING_UTF16LE) {
810 static const CHAR utf16BOM[] = {0xff,0xfe};
812 if (This->props[MXWriter_BOM] == VARIANT_TRUE)
813 /* Windows passes a NULL pointer as the pcbWritten parameter and
814 * ignores any error codes returned from this Write call.
816 IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
819 return S_OK;
822 static HRESULT WINAPI mxwriter_saxcontent_endDocument(ISAXContentHandler *iface)
824 mxwriter *This = impl_from_ISAXContentHandler( iface );
825 TRACE("(%p)\n", This);
826 This->prop_changed = FALSE;
827 return flush_output_buffer(This);
830 static HRESULT WINAPI mxwriter_saxcontent_startPrefixMapping(
831 ISAXContentHandler *iface,
832 const WCHAR *prefix,
833 int nprefix,
834 const WCHAR *uri,
835 int nuri)
837 mxwriter *This = impl_from_ISAXContentHandler( iface );
838 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(prefix, nprefix), debugstr_wn(uri, nuri));
839 return E_NOTIMPL;
842 static HRESULT WINAPI mxwriter_saxcontent_endPrefixMapping(
843 ISAXContentHandler *iface,
844 const WCHAR *prefix,
845 int nprefix)
847 mxwriter *This = impl_from_ISAXContentHandler( iface );
848 FIXME("(%p)->(%s)\n", This, debugstr_wn(prefix, nprefix));
849 return E_NOTIMPL;
852 static HRESULT WINAPI mxwriter_saxcontent_startElement(
853 ISAXContentHandler *iface,
854 const WCHAR *namespaceUri,
855 int nnamespaceUri,
856 const WCHAR *local_name,
857 int nlocal_name,
858 const WCHAR *QName,
859 int nQName,
860 ISAXAttributes *attr)
862 mxwriter *This = impl_from_ISAXContentHandler( iface );
863 xmlChar *s;
865 TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_wn(namespaceUri, nnamespaceUri),
866 debugstr_wn(local_name, nlocal_name), debugstr_wn(QName, nQName), attr);
868 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
869 return E_INVALIDARG;
871 close_element_starttag(This);
872 set_element_name(This, QName ? QName : emptyW,
873 QName ? nQName : 0);
875 write_output_buffer(This, "<", 1);
876 s = xmlchar_from_wcharn(QName, nQName);
877 write_output_buffer_str(This, (char*)s);
878 heap_free(s);
880 if (attr)
882 HRESULT hr;
883 INT length;
884 INT i;
886 hr = ISAXAttributes_getLength(attr, &length);
887 if (FAILED(hr)) return hr;
889 for (i = 0; i < length; i++)
891 const WCHAR *str;
892 WCHAR *escaped;
893 INT len = 0;
895 hr = ISAXAttributes_getQName(attr, i, &str, &len);
896 if (FAILED(hr)) return hr;
898 /* space separator in front of every attribute */
899 write_output_buffer(This, " ", 1);
901 s = xmlchar_from_wcharn(str, len);
902 write_output_buffer_str(This, (char*)s);
903 heap_free(s);
905 write_output_buffer(This, "=\"", 2);
907 len = 0;
908 hr = ISAXAttributes_getValue(attr, i, &str, &len);
909 if (FAILED(hr)) return hr;
911 escaped = get_escaped_string(str, &len);
912 s = xmlchar_from_wcharn(escaped, len);
913 write_output_buffer_str(This, (char*)s);
914 heap_free(escaped);
915 heap_free(s);
917 write_output_buffer(This, "\"", 1);
921 return S_OK;
924 static HRESULT WINAPI mxwriter_saxcontent_endElement(
925 ISAXContentHandler *iface,
926 const WCHAR *namespaceUri,
927 int nnamespaceUri,
928 const WCHAR * local_name,
929 int nlocal_name,
930 const WCHAR *QName,
931 int nQName)
933 mxwriter *This = impl_from_ISAXContentHandler( iface );
935 TRACE("(%p)->(%s:%d %s:%d %s:%d)\n", This, debugstr_wn(namespaceUri, nnamespaceUri), nnamespaceUri,
936 debugstr_wn(local_name, nlocal_name), nlocal_name, debugstr_wn(QName, nQName), nQName);
938 if ((!namespaceUri || !local_name || !QName) && This->class_version != MSXML6)
939 return E_INVALIDARG;
941 if (This->element && QName && !strncmpW(This->element, QName, nQName))
943 write_output_buffer(This, "/>", 2);
945 else
947 xmlChar *s = xmlchar_from_wcharn(QName, nQName);
949 write_output_buffer(This, "</", 2);
950 write_output_buffer_str(This, (char*)s);
951 write_output_buffer(This, ">", 1);
953 heap_free(s);
956 set_element_name(This, NULL, 0);
958 return S_OK;
961 static HRESULT WINAPI mxwriter_saxcontent_characters(
962 ISAXContentHandler *iface,
963 const WCHAR *chars,
964 int nchars)
966 mxwriter *This = impl_from_ISAXContentHandler( iface );
968 TRACE("(%p)->(%s:%d)\n", This, debugstr_wn(chars, nchars), nchars);
970 if (!chars) return E_INVALIDARG;
972 close_element_starttag(This);
973 set_element_name(This, NULL, 0);
975 if (nchars)
977 xmlChar *s = xmlchar_from_wcharn(chars, nchars);
978 write_output_buffer_str(This, (char*)s);
979 heap_free(s);
982 return S_OK;
985 static HRESULT WINAPI mxwriter_saxcontent_ignorableWhitespace(
986 ISAXContentHandler *iface,
987 const WCHAR *chars,
988 int nchars)
990 mxwriter *This = impl_from_ISAXContentHandler( iface );
991 FIXME("(%p)->(%s)\n", This, debugstr_wn(chars, nchars));
992 return E_NOTIMPL;
995 static HRESULT WINAPI mxwriter_saxcontent_processingInstruction(
996 ISAXContentHandler *iface,
997 const WCHAR *target,
998 int ntarget,
999 const WCHAR *data,
1000 int ndata)
1002 mxwriter *This = impl_from_ISAXContentHandler( iface );
1003 FIXME("(%p)->(%s %s)\n", This, debugstr_wn(target, ntarget), debugstr_wn(data, ndata));
1004 return E_NOTIMPL;
1007 static HRESULT WINAPI mxwriter_saxcontent_skippedEntity(
1008 ISAXContentHandler *iface,
1009 const WCHAR *name,
1010 int nname)
1012 mxwriter *This = impl_from_ISAXContentHandler( iface );
1013 FIXME("(%p)->(%s)\n", This, debugstr_wn(name, nname));
1014 return E_NOTIMPL;
1017 static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl =
1019 mxwriter_saxcontent_QueryInterface,
1020 mxwriter_saxcontent_AddRef,
1021 mxwriter_saxcontent_Release,
1022 mxwriter_saxcontent_putDocumentLocator,
1023 mxwriter_saxcontent_startDocument,
1024 mxwriter_saxcontent_endDocument,
1025 mxwriter_saxcontent_startPrefixMapping,
1026 mxwriter_saxcontent_endPrefixMapping,
1027 mxwriter_saxcontent_startElement,
1028 mxwriter_saxcontent_endElement,
1029 mxwriter_saxcontent_characters,
1030 mxwriter_saxcontent_ignorableWhitespace,
1031 mxwriter_saxcontent_processingInstruction,
1032 mxwriter_saxcontent_skippedEntity
1035 static const tid_t mxwriter_iface_tids[] = {
1036 IMXWriter_tid,
1040 static dispex_static_data_t mxwriter_dispex = {
1041 NULL,
1042 IMXWriter_tid,
1043 NULL,
1044 mxwriter_iface_tids
1047 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
1049 static const WCHAR version10W[] = {'1','.','0',0};
1050 mxwriter *This;
1052 TRACE("(%p, %p)\n", outer, ppObj);
1054 if (outer) FIXME("support aggregation, outer\n");
1056 This = heap_alloc( sizeof (*This) );
1057 if(!This)
1058 return E_OUTOFMEMORY;
1060 This->IMXWriter_iface.lpVtbl = &MXWriterVtbl;
1061 This->ISAXContentHandler_iface.lpVtbl = &mxwriter_saxcontent_vtbl;
1062 This->ref = 1;
1063 This->class_version = version;
1065 This->props[MXWriter_BOM] = VARIANT_TRUE;
1066 This->props[MXWriter_DisableEscaping] = VARIANT_FALSE;
1067 This->props[MXWriter_Indent] = VARIANT_FALSE;
1068 This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
1069 This->props[MXWriter_Standalone] = VARIANT_FALSE;
1070 This->prop_changed = FALSE;
1071 This->encoding = xmlParseCharEncoding("UTF-16");
1072 This->version = SysAllocString(version10W);
1074 This->element = NULL;
1076 This->decl_count = 0;
1077 This->decl_written = 0;
1079 This->dest = NULL;
1080 This->dest_written = 0;
1082 This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
1084 init_dispex(&This->dispex, (IUnknown*)&This->IMXWriter_iface, &mxwriter_dispex);
1086 *ppObj = &This->IMXWriter_iface;
1088 TRACE("returning iface %p\n", *ppObj);
1090 return S_OK;
1093 #else
1095 HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **obj)
1097 MESSAGE("This program tried to use a MXXMLWriter object, but\n"
1098 "libxml2 support was not present at compile time.\n");
1099 return E_NOTIMPL;
1102 #endif /* HAVE_LIBXML2 */