2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014-2018 Nikolay Sivov for CodeWeavers
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
30 #include "xmllite_private.h"
33 #include "wine/debug.h"
34 #include "wine/list.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(xmllite
);
38 /* not defined in public headers */
39 DEFINE_GUID(IID_IXmlWriterOutput
, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
41 static const WCHAR closeelementW
[] = {'<','/'};
42 static const WCHAR closetagW
[] = {' ','/','>'};
43 static const WCHAR closepiW
[] = {'?','>'};
44 static const WCHAR xmlnsW
[] = {' ','x','m','l','n','s'};
45 static const WCHAR xmlnsuriW
[] = {'h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','2','0','0','0','/','x','m','l','n','s','/',0};
50 unsigned int allocated
;
57 XmlWriterState_Initial
, /* output is not set yet */
58 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
59 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
60 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
61 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
62 XmlWriterState_ElemStarted
, /* writing element */
63 XmlWriterState_Content
, /* content is accepted at this point */
64 XmlWriterState_DocClosed
/* WriteEndDocument was called */
69 IXmlWriterOutput IXmlWriterOutput_iface
;
72 ISequentialStream
*stream
;
74 xml_encoding encoding
;
75 WCHAR
*encoding_name
; /* exactly as specified on output creation */
76 struct output_buffer buffer
;
80 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
86 unsigned int len
; /* qname length in chars */
97 struct element
*element
;
100 typedef struct _xmlwriter
102 IXmlWriter IXmlWriter_iface
;
105 xmlwriteroutput
*output
;
106 unsigned int indent_level
;
110 XmlConformanceLevel conformance
;
111 XmlWriterState state
;
112 struct list elements
;
113 DWORD bomwritten
: 1;
114 DWORD starttagopen
: 1;
118 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
120 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
123 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
125 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
128 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
130 static const char * const prop_names
[] =
135 "OmitXmlDeclaration",
139 if (prop
> _XmlWriterProperty_Last
)
140 return wine_dbg_sprintf("unknown property=%d", prop
);
142 return prop_names
[prop
];
145 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
146 const WCHAR
*encoding_name
, xmlwriteroutput
**out
);
148 /* writer output memory allocation functions */
149 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
151 return m_alloc(output
->imalloc
, len
);
154 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
156 m_free(output
->imalloc
, mem
);
159 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
161 return m_realloc(output
->imalloc
, mem
, len
);
164 /* writer memory allocation functions */
165 static inline void *writer_alloc(const xmlwriter
*writer
, size_t len
)
167 return m_alloc(writer
->imalloc
, len
);
170 static inline void writer_free(const xmlwriter
*writer
, void *mem
)
172 m_free(writer
->imalloc
, mem
);
175 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
180 ret
= writer_alloc(writer
, sizeof(*ret
));
181 if (!ret
) return ret
;
183 len
= prefix
? lstrlenW(prefix
) + 1 /* ':' */ : 0;
184 len
+= lstrlenW(local
);
186 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
189 static const WCHAR colonW
[] = {':',0};
190 lstrcpyW(ret
->qname
, prefix
);
191 lstrcatW(ret
->qname
, colonW
);
195 lstrcatW(ret
->qname
, local
);
201 static void writer_free_element(xmlwriter
*writer
, struct element
*element
)
205 LIST_FOR_EACH_ENTRY_SAFE(ns
, ns2
, &element
->ns
, struct ns
, entry
)
207 list_remove(&ns
->entry
);
208 writer_free(writer
, ns
->prefix
);
209 writer_free(writer
, ns
->uri
);
210 writer_free(writer
, ns
);
213 writer_free(writer
, element
->qname
);
214 writer_free(writer
, element
);
217 static void writer_free_element_stack(xmlwriter
*writer
)
219 struct element
*element
, *element2
;
221 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &writer
->elements
, struct element
, entry
)
223 list_remove(&element
->entry
);
224 writer_free_element(writer
, element
);
228 static void writer_push_element(xmlwriter
*writer
, struct element
*element
)
230 list_add_head(&writer
->elements
, &element
->entry
);
233 static struct element
*pop_element(xmlwriter
*writer
)
235 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
238 list_remove(&element
->entry
);
243 static WCHAR
*writer_strndupW(const xmlwriter
*writer
, const WCHAR
*str
, int len
)
254 size
= (len
+ 1) * sizeof(WCHAR
);
255 ret
= writer_alloc(writer
, size
);
256 memcpy(ret
, str
, size
);
260 static WCHAR
*writer_strdupW(const xmlwriter
*writer
, const WCHAR
*str
)
262 return writer_strndupW(writer
, str
, -1);
265 static struct ns
*writer_push_ns(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
, const WCHAR
*uri
)
267 struct element
*element
;
270 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
274 if ((ns
= writer_alloc(writer
, sizeof(*ns
))))
276 ns
->prefix
= writer_strndupW(writer
, prefix
, prefix_len
);
277 ns
->prefix_len
= prefix_len
;
278 ns
->uri
= writer_strdupW(writer
, uri
);
280 ns
->element
= element
;
281 list_add_tail(&element
->ns
, &ns
->entry
);
287 static BOOL
is_empty_string(const WCHAR
*str
)
289 return !str
|| !*str
;
292 static struct ns
*writer_find_ns_current(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
294 struct element
*element
;
297 if (is_empty_string(prefix
) || is_empty_string(uri
))
300 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
302 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
304 if (!wcscmp(uri
, ns
->uri
) && !wcscmp(prefix
, ns
->prefix
))
311 static struct ns
*writer_find_ns(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
313 struct element
*element
;
316 if (is_empty_string(prefix
) && is_empty_string(uri
))
319 LIST_FOR_EACH_ENTRY(element
, &writer
->elements
, struct element
, entry
)
321 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
325 if (!ns
->prefix
) continue;
326 if (!wcscmp(ns
->prefix
, prefix
))
329 else if (!wcscmp(uri
, ns
->uri
))
331 if (prefix
&& !*prefix
)
333 if (!prefix
|| !wcscmp(prefix
, ns
->prefix
))
342 static HRESULT
is_valid_ncname(const WCHAR
*str
, int *out
)
353 if (!is_ncnamechar(*str
))
354 return WC_E_NAMECHARACTER
;
363 static HRESULT
is_valid_name(const WCHAR
*str
, unsigned int *out
)
365 unsigned int len
= 1;
372 if (!is_namestartchar(*str
++))
373 return WC_E_NAMECHARACTER
;
377 if (!is_namechar(*str
))
378 return WC_E_NAMECHARACTER
;
386 static HRESULT
is_valid_pubid(const WCHAR
*str
, unsigned int *out
)
388 unsigned int len
= 0;
397 if (!is_pubchar(*str
++))
398 return WC_E_PUBLICID
;
407 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
409 struct output_buffer
*buffer
= &output
->buffer
;
410 const int initial_len
= 0x2000;
414 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
415 WARN("Failed to get code page for specified encoding.\n");
417 buffer
->data
= writeroutput_alloc(output
, initial_len
);
418 if (!buffer
->data
) return E_OUTOFMEMORY
;
420 memset(buffer
->data
, 0, 4);
421 buffer
->allocated
= initial_len
;
423 buffer
->codepage
= cp
;
428 static void free_output_buffer(xmlwriteroutput
*output
)
430 struct output_buffer
*buffer
= &output
->buffer
;
431 writeroutput_free(output
, buffer
->data
);
433 buffer
->allocated
= 0;
437 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
439 struct output_buffer
*buffer
= &output
->buffer
;
440 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
441 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
442 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
443 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
444 if (!ptr
) return E_OUTOFMEMORY
;
446 buffer
->allocated
= grown_size
;
452 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
454 struct output_buffer
*buffer
= &output
->buffer
;
459 if (buffer
->codepage
== 1200) {
460 /* For UTF-16 encoding just copy. */
461 length
= len
== -1 ? lstrlenW(data
) : len
;
463 length
*= sizeof(WCHAR
);
465 hr
= grow_output_buffer(output
, length
);
466 if (FAILED(hr
)) return hr
;
467 ptr
= buffer
->data
+ buffer
->written
;
469 memcpy(ptr
, data
, length
);
470 buffer
->written
+= length
;
472 /* null termination */
477 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
478 hr
= grow_output_buffer(output
, length
);
479 if (FAILED(hr
)) return hr
;
480 ptr
= buffer
->data
+ buffer
->written
;
481 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
482 buffer
->written
+= len
== -1 ? length
-1 : length
;
484 output
->written
= length
!= 0;
489 static HRESULT
write_output_buffer_char(xmlwriteroutput
*output
, WCHAR ch
)
491 return write_output_buffer(output
, &ch
, 1);
494 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
496 write_output_buffer_char(output
, '"');
497 if (!is_empty_string(data
))
498 write_output_buffer(output
, data
, len
);
499 write_output_buffer_char(output
, '"');
503 /* TODO: test if we need to validate char range */
504 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, int prefix_len
,
505 const WCHAR
*local_name
, int local_len
)
507 assert(prefix_len
>= 0 && local_len
>= 0);
510 write_output_buffer(output
, prefix
, prefix_len
);
512 if (prefix_len
&& local_len
)
513 write_output_buffer_char(output
, ':');
515 write_output_buffer(output
, local_name
, local_len
);
520 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
522 if (writeroutput
->stream
) {
523 ISequentialStream_Release(writeroutput
->stream
);
524 writeroutput
->stream
= NULL
;
528 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
532 writeroutput_release_stream(writeroutput
);
533 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
535 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
540 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
542 struct output_buffer
*buffer
;
543 ULONG written
, offset
= 0;
546 if (!output
|| !output
->stream
)
549 buffer
= &output
->buffer
;
551 /* It will loop forever until everything is written or an error occurred. */
554 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
556 WARN("write to stream failed (0x%08x)\n", hr
);
562 buffer
->written
-= written
;
563 } while (buffer
->written
> 0);
568 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
570 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
572 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
573 static const char utf16bom
[] = {0xff, 0xfe};
574 struct output_buffer
*buffer
= &writer
->output
->buffer
;
575 int len
= sizeof(utf16bom
);
578 hr
= grow_output_buffer(writer
->output
, len
);
579 if (FAILED(hr
)) return hr
;
580 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
581 buffer
->written
+= len
;
584 writer
->bomwritten
= TRUE
;
588 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
590 if (output
->encoding_name
)
591 return output
->encoding_name
;
593 return get_encoding_name(output
->encoding
);
596 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
598 static const WCHAR versionW
[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
599 static const WCHAR encodingW
[] = {' ','e','n','c','o','d','i','n','g','='};
601 write_encoding_bom(writer
);
602 writer
->state
= XmlWriterState_DocStarted
;
603 if (writer
->omitxmldecl
) return S_OK
;
606 write_output_buffer(writer
->output
, versionW
, ARRAY_SIZE(versionW
));
609 write_output_buffer(writer
->output
, encodingW
, ARRAY_SIZE(encodingW
));
610 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
613 if (standalone
== XmlStandalone_Omit
)
614 write_output_buffer(writer
->output
, closepiW
, ARRAY_SIZE(closepiW
));
616 static const WCHAR standaloneW
[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
617 static const WCHAR yesW
[] = {'y','e','s','\"','?','>'};
618 static const WCHAR noW
[] = {'n','o','\"','?','>'};
620 write_output_buffer(writer
->output
, standaloneW
, ARRAY_SIZE(standaloneW
));
621 if (standalone
== XmlStandalone_Yes
)
622 write_output_buffer(writer
->output
, yesW
, ARRAY_SIZE(yesW
));
624 write_output_buffer(writer
->output
, noW
, ARRAY_SIZE(noW
));
630 static void writer_output_ns(xmlwriter
*writer
, struct element
*element
)
634 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
639 write_output_qname(writer
->output
, xmlnsW
, ARRAY_SIZE(xmlnsW
), ns
->prefix
, ns
->prefix_len
);
640 write_output_buffer_char(writer
->output
, '=');
641 write_output_buffer_quoted(writer
->output
, ns
->uri
, -1);
645 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
649 if (!writer
->starttagopen
) return S_OK
;
651 writer_output_ns(writer
, LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
));
652 hr
= write_output_buffer_char(writer
->output
, '>');
653 writer
->starttagopen
= 0;
657 static void writer_inc_indent(xmlwriter
*writer
)
659 writer
->indent_level
++;
662 static void writer_dec_indent(xmlwriter
*writer
)
664 if (writer
->indent_level
)
665 writer
->indent_level
--;
668 static void write_node_indent(xmlwriter
*writer
)
670 static const WCHAR dblspaceW
[] = {' ',' '};
671 static const WCHAR crlfW
[] = {'\r','\n'};
672 unsigned int indent_level
= writer
->indent_level
;
674 if (!writer
->indent
|| writer
->textnode
)
676 writer
->textnode
= 0;
680 /* Do state check to prevent newline inserted after BOM. It is assumed that
681 state does not change between writing BOM and inserting indentation. */
682 if (writer
->output
->written
&& writer
->state
!= XmlWriterState_Ready
)
683 write_output_buffer(writer
->output
, crlfW
, ARRAY_SIZE(crlfW
));
684 while (indent_level
--)
685 write_output_buffer(writer
->output
, dblspaceW
, ARRAY_SIZE(dblspaceW
));
687 writer
->textnode
= 0;
690 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
692 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
694 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
696 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
697 IsEqualGUID(riid
, &IID_IUnknown
))
703 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
705 return E_NOINTERFACE
;
708 IXmlWriter_AddRef(iface
);
713 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
715 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
716 ULONG ref
= InterlockedIncrement(&This
->ref
);
717 TRACE("(%p)->(%u)\n", This
, ref
);
721 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
723 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
724 ULONG ref
= InterlockedDecrement(&This
->ref
);
726 TRACE("(%p)->(%u)\n", This
, ref
);
729 IMalloc
*imalloc
= This
->imalloc
;
731 writeroutput_flush_stream(This
->output
);
732 if (This
->output
) IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
734 writer_free_element_stack(This
);
736 writer_free(This
, This
);
737 if (imalloc
) IMalloc_Release(imalloc
);
743 /*** IXmlWriter methods ***/
744 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
746 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
747 IXmlWriterOutput
*writeroutput
;
750 TRACE("(%p)->(%p)\n", This
, output
);
753 writeroutput_release_stream(This
->output
);
754 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
756 This
->bomwritten
= 0;
758 This
->indent_level
= 0;
759 writer_free_element_stack(This
);
762 /* just reset current output */
764 This
->state
= XmlWriterState_Initial
;
768 /* now try IXmlWriterOutput, ISequentialStream, IStream */
769 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
771 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
772 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
774 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
775 writeroutput
, writeroutput
->lpVtbl
);
776 IUnknown_Release(writeroutput
);
781 if (hr
!= S_OK
|| !writeroutput
) {
782 /* Create output for given stream. */
783 hr
= create_writer_output(output
, This
->imalloc
, XmlEncoding_UTF8
, NULL
, &This
->output
);
788 if (This
->output
->encoding
== XmlEncoding_Unknown
)
789 This
->state
= XmlWriterState_InvalidEncoding
;
791 This
->state
= XmlWriterState_Ready
;
792 return writeroutput_query_for_stream(This
->output
);
795 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
797 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
799 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
801 if (!value
) return E_INVALIDARG
;
805 case XmlWriterProperty_Indent
:
806 *value
= This
->indent
;
808 case XmlWriterProperty_ByteOrderMark
:
811 case XmlWriterProperty_OmitXmlDeclaration
:
812 *value
= This
->omitxmldecl
;
814 case XmlWriterProperty_ConformanceLevel
:
815 *value
= This
->conformance
;
818 FIXME("Unimplemented property (%u)\n", property
);
825 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
827 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
829 TRACE("(%p)->(%s %lu)\n", This
, debugstr_writer_prop(property
), value
);
833 case XmlWriterProperty_Indent
:
834 This
->indent
= !!value
;
836 case XmlWriterProperty_ByteOrderMark
:
839 case XmlWriterProperty_OmitXmlDeclaration
:
840 This
->omitxmldecl
= !!value
;
843 FIXME("Unimplemented property (%u)\n", property
);
850 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*pReader
,
851 BOOL fWriteDefaultAttributes
)
853 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
855 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
860 static void write_output_attribute(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
,
861 const WCHAR
*local
, int local_len
, const WCHAR
*value
)
863 write_output_buffer_char(writer
->output
, ' ');
864 write_output_qname(writer
->output
, prefix
, prefix_len
, local
, local_len
);
865 write_output_buffer_char(writer
->output
, '=');
866 write_output_buffer_quoted(writer
->output
, value
, -1);
869 static BOOL
is_valid_xml_space_value(const WCHAR
*value
)
871 static const WCHAR preserveW
[] = {'p','r','e','s','e','r','v','e',0};
872 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
877 return !wcscmp(value
, preserveW
) || !wcscmp(value
, defaultW
);
880 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR prefix
,
881 LPCWSTR local
, LPCWSTR uri
, LPCWSTR value
)
883 static const WCHAR spaceattrW
[] = {'s','p','a','c','e',0};
884 static const WCHAR xmlnsW
[] = {'x','m','l','n','s',0};
885 static const WCHAR xmlW
[] = {'x','m','l',0};
886 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
887 BOOL is_xmlns_prefix
, is_xmlns_local
;
888 int prefix_len
, local_len
;
892 TRACE("%p %s %s %s %s\n", This
, debugstr_w(prefix
), debugstr_w(local
), debugstr_w(uri
), debugstr_w(value
));
896 case XmlWriterState_Initial
:
898 case XmlWriterState_Ready
:
899 case XmlWriterState_DocClosed
:
900 This
->state
= XmlWriterState_DocClosed
;
901 return WR_E_INVALIDACTION
;
902 case XmlWriterState_InvalidEncoding
:
903 return MX_E_ENCODING
;
909 is_xmlns_prefix
= prefix
&& !wcscmp(prefix
, xmlnsW
);
910 if (is_xmlns_prefix
&& is_empty_string(uri
) && is_empty_string(local
))
911 return WR_E_NSPREFIXDECLARED
;
916 /* Validate prefix and local name */
917 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
920 if (FAILED(hr
= is_valid_ncname(local
, &local_len
)))
923 is_xmlns_local
= !wcscmp(local
, xmlnsW
);
925 /* Trivial case, no prefix. */
926 if (prefix_len
== 0 && is_empty_string(uri
))
928 write_output_attribute(This
, prefix
, prefix_len
, local
, local_len
, value
);
932 /* Predefined "xml" prefix. */
933 if (prefix_len
&& !wcscmp(prefix
, xmlW
))
935 /* Valid "space" value is enforced. */
936 if (!wcscmp(local
, spaceattrW
) && !is_valid_xml_space_value(value
))
937 return WR_E_INVALIDXMLSPACE
;
939 /* Redefinition is not allowed. */
940 if (!is_empty_string(uri
))
941 return WR_E_XMLPREFIXDECLARATION
;
943 write_output_attribute(This
, prefix
, prefix_len
, local
, local_len
, value
);
948 if (is_xmlns_prefix
|| (prefix_len
== 0 && uri
&& !wcscmp(uri
, xmlnsuriW
)))
950 if (prefix_len
&& !is_empty_string(uri
))
951 return WR_E_XMLNSPREFIXDECLARATION
;
953 /* Look for exact match defined in current element, and write it out. */
954 if (!(ns
= writer_find_ns_current(This
, prefix
, value
)))
955 ns
= writer_push_ns(This
, local
, local_len
, value
);
958 write_output_attribute(This
, xmlnsW
, ARRAY_SIZE(xmlnsW
) - 1, local
, local_len
, value
);
963 /* Ignore prefix is URI wasn't specified. */
964 if (is_xmlns_local
&& is_empty_string(uri
))
966 write_output_attribute(This
, NULL
, 0, xmlnsW
, ARRAY_SIZE(xmlnsW
) - 1, value
);
970 if (!(ns
= writer_find_ns(This
, prefix
, uri
)))
972 if (is_empty_string(prefix
) && !is_empty_string(uri
))
974 FIXME("Prefix autogeneration is not implemented.\n");
977 if (!is_empty_string(uri
))
978 ns
= writer_push_ns(This
, prefix
, prefix_len
, uri
);
982 write_output_attribute(This
, ns
->prefix
, ns
->prefix_len
, local
, local_len
, value
);
984 write_output_attribute(This
, prefix
, prefix_len
, local
, local_len
, value
);
989 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
991 static const WCHAR cdataopenW
[] = {'<','!','[','C','D','A','T','A','['};
992 static const WCHAR cdatacloseW
[] = {']',']','>'};
993 write_output_buffer(output
, cdataopenW
, ARRAY_SIZE(cdataopenW
));
995 write_output_buffer(output
, data
, len
);
996 write_output_buffer(output
, cdatacloseW
, ARRAY_SIZE(cdatacloseW
));
999 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
1001 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1004 TRACE("%p %s\n", This
, debugstr_w(data
));
1006 switch (This
->state
)
1008 case XmlWriterState_Initial
:
1009 return E_UNEXPECTED
;
1010 case XmlWriterState_ElemStarted
:
1011 writer_close_starttag(This
);
1013 case XmlWriterState_Ready
:
1014 case XmlWriterState_DocClosed
:
1015 This
->state
= XmlWriterState_DocClosed
;
1016 return WR_E_INVALIDACTION
;
1017 case XmlWriterState_InvalidEncoding
:
1018 return MX_E_ENCODING
;
1023 len
= data
? lstrlenW(data
) : 0;
1025 write_node_indent(This
);
1027 write_cdata_section(This
->output
, NULL
, 0);
1029 static const WCHAR cdatacloseW
[] = {']',']','>',0};
1031 const WCHAR
*str
= wcsstr(data
, cdatacloseW
);
1034 write_cdata_section(This
->output
, data
, str
- data
);
1039 write_cdata_section(This
->output
, data
, len
);
1048 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
1050 static const WCHAR fmtW
[] = {'&','#','x','%','x',';',0};
1051 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1054 TRACE("%p %#x\n", This
, ch
);
1056 switch (This
->state
)
1058 case XmlWriterState_Initial
:
1059 return E_UNEXPECTED
;
1060 case XmlWriterState_InvalidEncoding
:
1061 return MX_E_ENCODING
;
1062 case XmlWriterState_ElemStarted
:
1063 writer_close_starttag(This
);
1065 case XmlWriterState_DocClosed
:
1066 return WR_E_INVALIDACTION
;
1071 swprintf(bufW
, ARRAY_SIZE(bufW
), fmtW
, ch
);
1072 write_output_buffer(This
->output
, bufW
, -1);
1077 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1079 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1081 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1083 switch (This
->state
)
1085 case XmlWriterState_Initial
:
1086 return E_UNEXPECTED
;
1087 case XmlWriterState_InvalidEncoding
:
1088 return MX_E_ENCODING
;
1089 case XmlWriterState_DocClosed
:
1090 return WR_E_INVALIDACTION
;
1099 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
1101 static const WCHAR copenW
[] = {'<','!','-','-'};
1102 static const WCHAR ccloseW
[] = {'-','-','>'};
1103 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1105 TRACE("%p %s\n", This
, debugstr_w(comment
));
1107 switch (This
->state
)
1109 case XmlWriterState_Initial
:
1110 return E_UNEXPECTED
;
1111 case XmlWriterState_InvalidEncoding
:
1112 return MX_E_ENCODING
;
1113 case XmlWriterState_ElemStarted
:
1114 writer_close_starttag(This
);
1116 case XmlWriterState_DocClosed
:
1117 return WR_E_INVALIDACTION
;
1122 write_node_indent(This
);
1123 write_output_buffer(This
->output
, copenW
, ARRAY_SIZE(copenW
));
1125 int len
= lstrlenW(comment
), i
;
1127 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1130 for (i
= 0; i
< len
; i
++) {
1131 write_output_buffer(This
->output
, comment
+ i
, 1);
1132 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
1133 write_output_buffer_char(This
->output
, ' ');
1137 write_output_buffer(This
->output
, comment
, len
);
1139 if (len
&& comment
[len
-1] == '-')
1140 write_output_buffer_char(This
->output
, ' ');
1142 write_output_buffer(This
->output
, ccloseW
, ARRAY_SIZE(ccloseW
));
1147 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR name
, LPCWSTR pubid
,
1148 LPCWSTR sysid
, LPCWSTR subset
)
1150 static const WCHAR doctypeW
[] = {'<','!','D','O','C','T','Y','P','E',' '};
1151 static const WCHAR publicW
[] = {' ','P','U','B','L','I','C',' '};
1152 static const WCHAR systemW
[] = {' ','S','Y','S','T','E','M',' '};
1153 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1154 unsigned int name_len
, pubid_len
;
1157 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(pubid
), wine_dbgstr_w(sysid
),
1158 wine_dbgstr_w(subset
));
1160 switch (This
->state
)
1162 case XmlWriterState_Initial
:
1163 return E_UNEXPECTED
;
1164 case XmlWriterState_InvalidEncoding
:
1165 return MX_E_ENCODING
;
1166 case XmlWriterState_Content
:
1167 case XmlWriterState_DocClosed
:
1168 return WR_E_INVALIDACTION
;
1173 if (is_empty_string(name
))
1174 return E_INVALIDARG
;
1176 if (FAILED(hr
= is_valid_name(name
, &name_len
)))
1179 if (FAILED(hr
= is_valid_pubid(pubid
, &pubid_len
)))
1182 write_output_buffer(This
->output
, doctypeW
, ARRAY_SIZE(doctypeW
));
1183 write_output_buffer(This
->output
, name
, name_len
);
1187 write_output_buffer(This
->output
, publicW
, ARRAY_SIZE(publicW
));
1188 write_output_buffer_quoted(This
->output
, pubid
, pubid_len
);
1189 write_output_buffer_char(This
->output
, ' ');
1190 write_output_buffer_quoted(This
->output
, sysid
, -1);
1194 write_output_buffer(This
->output
, systemW
, ARRAY_SIZE(systemW
));
1195 write_output_buffer_quoted(This
->output
, sysid
, -1);
1200 write_output_buffer_char(This
->output
, ' ');
1201 write_output_buffer_char(This
->output
, '[');
1202 write_output_buffer(This
->output
, subset
, -1);
1203 write_output_buffer_char(This
->output
, ']');
1205 write_output_buffer_char(This
->output
, '>');
1207 This
->state
= XmlWriterState_Content
;
1212 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
1213 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
1215 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1216 int prefix_len
, local_len
;
1220 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
1221 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
1223 switch (This
->state
)
1225 case XmlWriterState_Initial
:
1226 return E_UNEXPECTED
;
1227 case XmlWriterState_InvalidEncoding
:
1228 return MX_E_ENCODING
;
1229 case XmlWriterState_ElemStarted
:
1230 writer_close_starttag(This
);
1232 case XmlWriterState_DocClosed
:
1233 return WR_E_INVALIDACTION
;
1239 return E_INVALIDARG
;
1241 /* Validate prefix and local name */
1242 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1245 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1248 ns
= writer_find_ns(This
, prefix
, uri
);
1249 if (!ns
&& !is_empty_string(prefix
) && is_empty_string(uri
))
1250 return WR_E_NSPREFIXWITHEMPTYNSURI
;
1252 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1255 return WR_E_XMLNSPREFIXDECLARATION
;
1257 if (!is_empty_string(prefix
))
1258 return WR_E_XMLNSURIDECLARATION
;
1261 write_encoding_bom(This
);
1262 write_node_indent(This
);
1264 write_output_buffer_char(This
->output
, '<');
1266 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1268 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1270 if (!ns
&& (prefix_len
|| !is_empty_string(uri
)))
1272 write_output_qname(This
->output
, xmlnsW
, ARRAY_SIZE(xmlnsW
), prefix
, prefix_len
);
1273 write_output_buffer_char(This
->output
, '=');
1274 write_output_buffer_quoted(This
->output
, uri
, -1);
1279 write_output_buffer_char(This
->output
, '>');
1280 write_output_buffer(This
->output
, value
, -1);
1281 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1282 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1283 write_output_buffer_char(This
->output
, '>');
1286 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
1288 This
->state
= XmlWriterState_Content
;
1293 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
1295 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1297 TRACE("%p\n", This
);
1299 switch (This
->state
)
1301 case XmlWriterState_Initial
:
1302 return E_UNEXPECTED
;
1303 case XmlWriterState_Ready
:
1304 case XmlWriterState_DocClosed
:
1305 This
->state
= XmlWriterState_DocClosed
;
1306 return WR_E_INVALIDACTION
;
1307 case XmlWriterState_InvalidEncoding
:
1308 return MX_E_ENCODING
;
1313 /* empty element stack */
1314 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
1317 This
->state
= XmlWriterState_DocClosed
;
1321 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
1323 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1324 struct element
*element
;
1326 TRACE("%p\n", This
);
1328 switch (This
->state
)
1330 case XmlWriterState_Initial
:
1331 return E_UNEXPECTED
;
1332 case XmlWriterState_Ready
:
1333 case XmlWriterState_DocClosed
:
1334 This
->state
= XmlWriterState_DocClosed
;
1335 return WR_E_INVALIDACTION
;
1336 case XmlWriterState_InvalidEncoding
:
1337 return MX_E_ENCODING
;
1342 element
= pop_element(This
);
1344 return WR_E_INVALIDACTION
;
1346 writer_dec_indent(This
);
1348 if (This
->starttagopen
)
1350 writer_output_ns(This
, element
);
1351 write_output_buffer(This
->output
, closetagW
, ARRAY_SIZE(closetagW
));
1352 This
->starttagopen
= 0;
1356 /* Write full end tag. */
1357 write_node_indent(This
);
1358 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1359 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1360 write_output_buffer_char(This
->output
, '>');
1362 writer_free_element(This
, element
);
1367 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
1369 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1371 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1373 switch (This
->state
)
1375 case XmlWriterState_Initial
:
1376 return E_UNEXPECTED
;
1377 case XmlWriterState_InvalidEncoding
:
1378 return MX_E_ENCODING
;
1379 case XmlWriterState_DocClosed
:
1380 return WR_E_INVALIDACTION
;
1388 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
1390 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1391 struct element
*element
;
1393 TRACE("%p\n", This
);
1395 switch (This
->state
)
1397 case XmlWriterState_Initial
:
1398 return E_UNEXPECTED
;
1399 case XmlWriterState_Ready
:
1400 case XmlWriterState_DocClosed
:
1401 This
->state
= XmlWriterState_DocClosed
;
1402 return WR_E_INVALIDACTION
;
1403 case XmlWriterState_InvalidEncoding
:
1404 return MX_E_ENCODING
;
1405 case XmlWriterState_ElemStarted
:
1406 writer_close_starttag(This
);
1412 element
= pop_element(This
);
1414 return WR_E_INVALIDACTION
;
1416 writer_dec_indent(This
);
1418 /* don't force full end tag to the next line */
1419 if (This
->state
== XmlWriterState_ElemStarted
)
1421 This
->state
= XmlWriterState_Content
;
1425 write_node_indent(This
);
1427 /* write full end tag */
1428 write_output_buffer(This
->output
, closeelementW
, ARRAY_SIZE(closeelementW
));
1429 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1430 write_output_buffer_char(This
->output
, '>');
1432 writer_free_element(This
, element
);
1437 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1439 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1441 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1443 switch (This
->state
)
1445 case XmlWriterState_Initial
:
1446 return E_UNEXPECTED
;
1447 case XmlWriterState_Ready
:
1448 case XmlWriterState_DocClosed
:
1449 This
->state
= XmlWriterState_DocClosed
;
1450 return WR_E_INVALIDACTION
;
1451 case XmlWriterState_InvalidEncoding
:
1452 return MX_E_ENCODING
;
1460 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1462 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1464 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1466 switch (This
->state
)
1468 case XmlWriterState_Initial
:
1469 return E_UNEXPECTED
;
1470 case XmlWriterState_Ready
:
1471 case XmlWriterState_DocClosed
:
1472 This
->state
= XmlWriterState_DocClosed
;
1473 return WR_E_INVALIDACTION
;
1474 case XmlWriterState_InvalidEncoding
:
1475 return MX_E_ENCODING
;
1483 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*pReader
,
1484 BOOL fWriteDefaultAttributes
)
1486 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1488 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1493 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*pReader
,
1494 BOOL fWriteDefaultAttributes
)
1496 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1498 FIXME("%p %p %d\n", This
, pReader
, fWriteDefaultAttributes
);
1503 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1506 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1507 static const WCHAR xmlW
[] = {'x','m','l',0};
1508 static const WCHAR openpiW
[] = {'<','?'};
1510 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1512 switch (This
->state
)
1514 case XmlWriterState_Initial
:
1515 return E_UNEXPECTED
;
1516 case XmlWriterState_InvalidEncoding
:
1517 return MX_E_ENCODING
;
1518 case XmlWriterState_DocStarted
:
1519 if (!wcscmp(name
, xmlW
))
1520 return WR_E_INVALIDACTION
;
1522 case XmlWriterState_ElemStarted
:
1523 case XmlWriterState_DocClosed
:
1524 return WR_E_INVALIDACTION
;
1529 write_encoding_bom(This
);
1530 write_node_indent(This
);
1531 write_output_buffer(This
->output
, openpiW
, ARRAY_SIZE(openpiW
));
1532 write_output_buffer(This
->output
, name
, -1);
1533 write_output_buffer_char(This
->output
, ' ');
1534 write_output_buffer(This
->output
, text
, -1);
1535 write_output_buffer(This
->output
, closepiW
, ARRAY_SIZE(closepiW
));
1537 if (!wcscmp(name
, xmlW
))
1538 This
->state
= XmlWriterState_PIDocStarted
;
1543 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1544 LPCWSTR pwszNamespaceUri
)
1546 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1548 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1550 switch (This
->state
)
1552 case XmlWriterState_Initial
:
1553 return E_UNEXPECTED
;
1554 case XmlWriterState_InvalidEncoding
:
1555 return MX_E_ENCODING
;
1556 case XmlWriterState_DocClosed
:
1557 return WR_E_INVALIDACTION
;
1565 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1567 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1569 TRACE("%p %s\n", This
, debugstr_w(data
));
1574 switch (This
->state
)
1576 case XmlWriterState_Initial
:
1577 return E_UNEXPECTED
;
1578 case XmlWriterState_Ready
:
1579 write_xmldecl(This
, XmlStandalone_Omit
);
1581 case XmlWriterState_DocStarted
:
1582 case XmlWriterState_PIDocStarted
:
1584 case XmlWriterState_InvalidEncoding
:
1585 return MX_E_ENCODING
;
1587 This
->state
= XmlWriterState_DocClosed
;
1588 return WR_E_INVALIDACTION
;
1591 write_output_buffer(This
->output
, data
, -1);
1595 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1597 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1599 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1601 switch (This
->state
)
1603 case XmlWriterState_Initial
:
1604 return E_UNEXPECTED
;
1605 case XmlWriterState_InvalidEncoding
:
1606 return MX_E_ENCODING
;
1607 case XmlWriterState_DocClosed
:
1608 return WR_E_INVALIDACTION
;
1616 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1618 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1620 TRACE("(%p)->(%d)\n", This
, standalone
);
1622 switch (This
->state
)
1624 case XmlWriterState_Initial
:
1625 return E_UNEXPECTED
;
1626 case XmlWriterState_PIDocStarted
:
1627 This
->state
= XmlWriterState_DocStarted
;
1629 case XmlWriterState_Ready
:
1631 case XmlWriterState_InvalidEncoding
:
1632 return MX_E_ENCODING
;
1634 This
->state
= XmlWriterState_DocClosed
;
1635 return WR_E_INVALIDACTION
;
1638 return write_xmldecl(This
, standalone
);
1641 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1643 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1644 int prefix_len
, local_len
;
1645 struct element
*element
;
1649 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1652 return E_INVALIDARG
;
1654 switch (This
->state
)
1656 case XmlWriterState_Initial
:
1657 return E_UNEXPECTED
;
1658 case XmlWriterState_InvalidEncoding
:
1659 return MX_E_ENCODING
;
1660 case XmlWriterState_DocClosed
:
1661 return WR_E_INVALIDACTION
;
1662 case XmlWriterState_ElemStarted
:
1663 writer_close_starttag(This
);
1669 /* Validate prefix and local name */
1670 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1673 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1676 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1679 return WR_E_XMLNSPREFIXDECLARATION
;
1681 if (!is_empty_string(prefix
))
1682 return WR_E_XMLNSURIDECLARATION
;
1685 ns
= writer_find_ns(This
, prefix
, uri
);
1687 element
= alloc_element(This
, prefix
, local_name
);
1689 return E_OUTOFMEMORY
;
1691 write_encoding_bom(This
);
1692 write_node_indent(This
);
1694 This
->state
= XmlWriterState_ElemStarted
;
1695 This
->starttagopen
= 1;
1697 writer_push_element(This
, element
);
1700 writer_push_ns(This
, prefix
, prefix_len
, uri
);
1702 write_output_buffer_char(This
->output
, '<');
1704 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1706 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1707 writer_inc_indent(This
);
1712 static void write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
)
1714 static const WCHAR ampW
[] = {'&','a','m','p',';'};
1715 static const WCHAR ltW
[] = {'&','l','t',';'};
1716 static const WCHAR gtW
[] = {'&','g','t',';'};
1723 write_output_buffer(writer
->output
, ltW
, ARRAY_SIZE(ltW
));
1726 write_output_buffer(writer
->output
, ampW
, ARRAY_SIZE(ampW
));
1729 write_output_buffer(writer
->output
, gtW
, ARRAY_SIZE(gtW
));
1732 write_output_buffer(writer
->output
, string
, 1);
1739 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1741 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1743 TRACE("%p %s\n", This
, debugstr_w(string
));
1748 switch (This
->state
)
1750 case XmlWriterState_Initial
:
1751 return E_UNEXPECTED
;
1752 case XmlWriterState_ElemStarted
:
1753 writer_close_starttag(This
);
1755 case XmlWriterState_Ready
:
1756 case XmlWriterState_DocClosed
:
1757 This
->state
= XmlWriterState_DocClosed
;
1758 return WR_E_INVALIDACTION
;
1759 case XmlWriterState_InvalidEncoding
:
1760 return MX_E_ENCODING
;
1766 write_escaped_string(This
, string
);
1770 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1772 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1774 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1779 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR pwszWhitespace
)
1781 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1783 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszWhitespace
));
1788 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1790 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1792 TRACE("%p\n", This
);
1794 return writeroutput_flush_stream(This
->output
);
1797 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1799 xmlwriter_QueryInterface
,
1802 xmlwriter_SetOutput
,
1803 xmlwriter_GetProperty
,
1804 xmlwriter_SetProperty
,
1805 xmlwriter_WriteAttributes
,
1806 xmlwriter_WriteAttributeString
,
1807 xmlwriter_WriteCData
,
1808 xmlwriter_WriteCharEntity
,
1809 xmlwriter_WriteChars
,
1810 xmlwriter_WriteComment
,
1811 xmlwriter_WriteDocType
,
1812 xmlwriter_WriteElementString
,
1813 xmlwriter_WriteEndDocument
,
1814 xmlwriter_WriteEndElement
,
1815 xmlwriter_WriteEntityRef
,
1816 xmlwriter_WriteFullEndElement
,
1817 xmlwriter_WriteName
,
1818 xmlwriter_WriteNmToken
,
1819 xmlwriter_WriteNode
,
1820 xmlwriter_WriteNodeShallow
,
1821 xmlwriter_WriteProcessingInstruction
,
1822 xmlwriter_WriteQualifiedName
,
1824 xmlwriter_WriteRawChars
,
1825 xmlwriter_WriteStartDocument
,
1826 xmlwriter_WriteStartElement
,
1827 xmlwriter_WriteString
,
1828 xmlwriter_WriteSurrogateCharEntity
,
1829 xmlwriter_WriteWhitespace
,
1833 /** IXmlWriterOutput **/
1834 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1836 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1838 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1840 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1841 IsEqualGUID(riid
, &IID_IUnknown
))
1847 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1849 return E_NOINTERFACE
;
1852 IUnknown_AddRef(iface
);
1857 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1859 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1860 ULONG ref
= InterlockedIncrement(&This
->ref
);
1861 TRACE("(%p)->(%d)\n", This
, ref
);
1865 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
1867 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1868 LONG ref
= InterlockedDecrement(&This
->ref
);
1870 TRACE("(%p)->(%d)\n", This
, ref
);
1874 IMalloc
*imalloc
= This
->imalloc
;
1875 if (This
->output
) IUnknown_Release(This
->output
);
1876 if (This
->stream
) ISequentialStream_Release(This
->stream
);
1877 free_output_buffer(This
);
1878 writeroutput_free(This
, This
->encoding_name
);
1879 writeroutput_free(This
, This
);
1880 if (imalloc
) IMalloc_Release(imalloc
);
1886 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
1888 xmlwriteroutput_QueryInterface
,
1889 xmlwriteroutput_AddRef
,
1890 xmlwriteroutput_Release
1893 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
1898 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
1901 writer
= IMalloc_Alloc(imalloc
, sizeof(*writer
));
1903 writer
= heap_alloc(sizeof(*writer
));
1905 return E_OUTOFMEMORY
;
1907 memset(writer
, 0, sizeof(*writer
));
1909 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
1911 writer
->imalloc
= imalloc
;
1912 if (imalloc
) IMalloc_AddRef(imalloc
);
1914 writer
->conformance
= XmlConformanceLevel_Document
;
1915 writer
->state
= XmlWriterState_Initial
;
1916 list_init(&writer
->elements
);
1918 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
1919 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
1921 TRACE("returning iface %p, hr %#x\n", *obj
, hr
);
1926 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
1927 const WCHAR
*encoding_name
, xmlwriteroutput
**out
)
1929 xmlwriteroutput
*writeroutput
;
1935 writeroutput
= IMalloc_Alloc(imalloc
, sizeof(*writeroutput
));
1937 writeroutput
= heap_alloc(sizeof(*writeroutput
));
1939 return E_OUTOFMEMORY
;
1941 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
1942 writeroutput
->ref
= 1;
1943 writeroutput
->imalloc
= imalloc
;
1945 IMalloc_AddRef(imalloc
);
1946 writeroutput
->encoding
= encoding
;
1947 writeroutput
->stream
= NULL
;
1948 hr
= init_output_buffer(writeroutput
);
1950 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
1954 if (encoding_name
) {
1955 unsigned int size
= (lstrlenW(encoding_name
) + 1) * sizeof(WCHAR
);
1956 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
1957 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
1960 writeroutput
->encoding_name
= NULL
;
1961 writeroutput
->written
= 0;
1963 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
1965 *out
= writeroutput
;
1967 TRACE("Created writer output %p\n", *out
);
1972 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
, IMalloc
*imalloc
, const WCHAR
*encoding
,
1973 IXmlWriterOutput
**out
)
1975 xmlwriteroutput
*output
;
1976 xml_encoding xml_enc
;
1979 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), out
);
1981 if (!stream
|| !out
)
1982 return E_INVALIDARG
;
1986 xml_enc
= encoding
? parse_encoding_name(encoding
, -1) : XmlEncoding_UTF8
;
1987 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, encoding
, &output
)))
1988 *out
= &output
->IXmlWriterOutput_iface
;
1993 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
, IMalloc
*imalloc
, UINT codepage
,
1994 IXmlWriterOutput
**out
)
1996 xmlwriteroutput
*output
;
1997 xml_encoding xml_enc
;
2000 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, out
);
2002 if (!stream
|| !out
)
2003 return E_INVALIDARG
;
2007 xml_enc
= get_encoding_from_codepage(codepage
);
2008 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, NULL
, &output
)))
2009 *out
= &output
->IXmlWriterOutput_iface
;