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 xmlnsuriW
[] = L
"http://www.w3.org/2000/xmlns/";
46 unsigned int allocated
;
53 XmlWriterState_Initial
, /* output is not set yet */
54 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
55 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
56 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
57 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
58 XmlWriterState_ElemStarted
, /* writing element */
59 XmlWriterState_Content
, /* content is accepted at this point */
60 XmlWriterState_DocClosed
/* WriteEndDocument was called */
65 IXmlWriterOutput IXmlWriterOutput_iface
;
68 ISequentialStream
*stream
;
70 xml_encoding encoding
;
71 WCHAR
*encoding_name
; /* exactly as specified on output creation */
72 struct output_buffer buffer
;
76 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
82 unsigned int len
; /* qname length in chars */
93 struct element
*element
;
96 typedef struct _xmlwriter
98 IXmlWriter IXmlWriter_iface
;
101 xmlwriteroutput
*output
;
102 unsigned int indent_level
;
106 XmlConformanceLevel conformance
;
107 XmlWriterState state
;
108 struct list elements
;
109 DWORD bomwritten
: 1;
110 DWORD starttagopen
: 1;
114 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
116 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
119 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
121 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
124 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
126 static const char * const prop_names
[] =
131 "OmitXmlDeclaration",
135 if (prop
> _XmlWriterProperty_Last
)
136 return wine_dbg_sprintf("unknown property=%d", prop
);
138 return prop_names
[prop
];
141 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
142 const WCHAR
*encoding_name
, xmlwriteroutput
**out
);
144 /* writer output memory allocation functions */
145 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
147 return m_alloc(output
->imalloc
, len
);
150 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
152 m_free(output
->imalloc
, mem
);
155 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
157 return m_realloc(output
->imalloc
, mem
, len
);
160 /* writer memory allocation functions */
161 static inline void *writer_alloc(const xmlwriter
*writer
, size_t len
)
163 return m_alloc(writer
->imalloc
, len
);
166 static inline void writer_free(const xmlwriter
*writer
, void *mem
)
168 m_free(writer
->imalloc
, mem
);
171 static BOOL
is_empty_string(const WCHAR
*str
)
173 return !str
|| !*str
;
176 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
181 ret
= writer_alloc(writer
, sizeof(*ret
));
182 if (!ret
) return ret
;
184 len
= is_empty_string(prefix
) ? 0 : lstrlenW(prefix
) + 1 /* ':' */;
185 len
+= lstrlenW(local
);
187 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
191 writer_free(writer
, ret
);
196 if (is_empty_string(prefix
))
200 lstrcpyW(ret
->qname
, prefix
);
201 lstrcatW(ret
->qname
, L
":");
203 lstrcatW(ret
->qname
, local
);
209 static void writer_free_element(xmlwriter
*writer
, struct element
*element
)
213 LIST_FOR_EACH_ENTRY_SAFE(ns
, ns2
, &element
->ns
, struct ns
, entry
)
215 list_remove(&ns
->entry
);
216 writer_free(writer
, ns
->prefix
);
217 writer_free(writer
, ns
->uri
);
218 writer_free(writer
, ns
);
221 writer_free(writer
, element
->qname
);
222 writer_free(writer
, element
);
225 static void writer_free_element_stack(xmlwriter
*writer
)
227 struct element
*element
, *element2
;
229 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &writer
->elements
, struct element
, entry
)
231 list_remove(&element
->entry
);
232 writer_free_element(writer
, element
);
236 static void writer_push_element(xmlwriter
*writer
, struct element
*element
)
238 list_add_head(&writer
->elements
, &element
->entry
);
241 static struct element
*pop_element(xmlwriter
*writer
)
243 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
246 list_remove(&element
->entry
);
251 static WCHAR
*writer_strndupW(const xmlwriter
*writer
, const WCHAR
*str
, int len
)
262 size
= (len
+ 1) * sizeof(WCHAR
);
263 ret
= writer_alloc(writer
, size
);
264 if (ret
) memcpy(ret
, str
, size
);
269 static WCHAR
*writer_strdupW(const xmlwriter
*writer
, const WCHAR
*str
)
271 return writer_strndupW(writer
, str
, -1);
274 static struct ns
*writer_push_ns(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
, const WCHAR
*uri
)
276 struct element
*element
;
279 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
283 if ((ns
= writer_alloc(writer
, sizeof(*ns
))))
285 ns
->prefix
= writer_strndupW(writer
, prefix
, prefix_len
);
286 ns
->prefix_len
= prefix_len
;
287 ns
->uri
= writer_strdupW(writer
, uri
);
289 ns
->element
= element
;
290 list_add_tail(&element
->ns
, &ns
->entry
);
296 static struct ns
*writer_find_ns_current(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
298 struct element
*element
;
301 if (is_empty_string(prefix
) || is_empty_string(uri
))
304 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
306 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
308 if (!wcscmp(uri
, ns
->uri
) && !wcscmp(prefix
, ns
->prefix
))
315 static struct ns
*writer_find_ns(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
317 struct element
*element
;
320 if (is_empty_string(prefix
) && is_empty_string(uri
))
323 LIST_FOR_EACH_ENTRY(element
, &writer
->elements
, struct element
, entry
)
325 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
329 if (!ns
->prefix
) continue;
330 if (!wcscmp(ns
->prefix
, prefix
))
333 else if (!wcscmp(uri
, ns
->uri
))
335 if (prefix
&& !*prefix
)
337 if (!prefix
|| !wcscmp(prefix
, ns
->prefix
))
346 static HRESULT
is_valid_ncname(const WCHAR
*str
, int *out
)
357 if (!is_ncnamechar(*str
))
358 return WC_E_NAMECHARACTER
;
367 static HRESULT
is_valid_name(const WCHAR
*str
, unsigned int *out
)
369 unsigned int len
= 1;
376 if (!is_namestartchar(*str
++))
377 return WC_E_NAMECHARACTER
;
381 if (!is_namechar(*str
))
382 return WC_E_NAMECHARACTER
;
390 static HRESULT
is_valid_pubid(const WCHAR
*str
, unsigned int *out
)
392 unsigned int len
= 0;
401 if (!is_pubchar(*str
++))
402 return WC_E_PUBLICID
;
411 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
413 struct output_buffer
*buffer
= &output
->buffer
;
414 const int initial_len
= 0x2000;
418 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
419 WARN("Failed to get code page for specified encoding.\n");
421 buffer
->data
= writeroutput_alloc(output
, initial_len
);
422 if (!buffer
->data
) return E_OUTOFMEMORY
;
424 memset(buffer
->data
, 0, 4);
425 buffer
->allocated
= initial_len
;
427 buffer
->codepage
= cp
;
432 static void free_output_buffer(xmlwriteroutput
*output
)
434 struct output_buffer
*buffer
= &output
->buffer
;
435 writeroutput_free(output
, buffer
->data
);
437 buffer
->allocated
= 0;
441 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
443 struct output_buffer
*buffer
= &output
->buffer
;
444 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
445 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
446 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
447 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
448 if (!ptr
) return E_OUTOFMEMORY
;
450 buffer
->allocated
= grown_size
;
456 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
458 struct output_buffer
*buffer
= &output
->buffer
;
463 if (buffer
->codepage
== 1200) {
464 /* For UTF-16 encoding just copy. */
465 length
= len
== -1 ? lstrlenW(data
) : len
;
467 length
*= sizeof(WCHAR
);
469 hr
= grow_output_buffer(output
, length
);
470 if (FAILED(hr
)) return hr
;
471 ptr
= buffer
->data
+ buffer
->written
;
473 memcpy(ptr
, data
, length
);
474 buffer
->written
+= length
;
476 /* null termination */
481 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
482 hr
= grow_output_buffer(output
, length
);
483 if (FAILED(hr
)) return hr
;
484 ptr
= buffer
->data
+ buffer
->written
;
485 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
486 buffer
->written
+= len
== -1 ? length
-1 : length
;
488 output
->written
= length
!= 0;
493 static HRESULT
write_output_buffer_char(xmlwriteroutput
*output
, WCHAR ch
)
495 return write_output_buffer(output
, &ch
, 1);
498 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
500 write_output_buffer_char(output
, '"');
501 if (!is_empty_string(data
))
502 write_output_buffer(output
, data
, len
);
503 write_output_buffer_char(output
, '"');
507 /* TODO: test if we need to validate char range */
508 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, int prefix_len
,
509 const WCHAR
*local_name
, int local_len
)
511 assert(prefix_len
>= 0 && local_len
>= 0);
514 write_output_buffer(output
, prefix
, prefix_len
);
516 if (prefix_len
&& local_len
)
517 write_output_buffer_char(output
, ':');
519 write_output_buffer(output
, local_name
, local_len
);
524 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
526 if (writeroutput
->stream
) {
527 ISequentialStream_Release(writeroutput
->stream
);
528 writeroutput
->stream
= NULL
;
532 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
536 writeroutput_release_stream(writeroutput
);
537 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
539 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
544 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
546 struct output_buffer
*buffer
;
547 ULONG written
, offset
= 0;
550 if (!output
|| !output
->stream
)
553 buffer
= &output
->buffer
;
555 /* It will loop forever until everything is written or an error occurred. */
558 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
560 WARN("write to stream failed %#lx.\n", hr
);
566 buffer
->written
-= written
;
567 } while (buffer
->written
> 0);
572 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
574 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
576 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
577 static const char utf16bom
[] = {0xff, 0xfe};
578 struct output_buffer
*buffer
= &writer
->output
->buffer
;
579 int len
= sizeof(utf16bom
);
582 hr
= grow_output_buffer(writer
->output
, len
);
583 if (FAILED(hr
)) return hr
;
584 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
585 buffer
->written
+= len
;
588 writer
->bomwritten
= TRUE
;
592 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
594 if (output
->encoding_name
)
595 return output
->encoding_name
;
597 return get_encoding_name(output
->encoding
);
600 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
602 write_encoding_bom(writer
);
603 writer
->state
= XmlWriterState_DocStarted
;
604 if (writer
->omitxmldecl
) return S_OK
;
607 write_output_buffer(writer
->output
, L
"<?xml version=\"1.0\"", 19);
610 write_output_buffer(writer
->output
, L
" encoding=", 10);
611 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
614 if (standalone
== XmlStandalone_Omit
)
615 write_output_buffer(writer
->output
, L
"?>", 2);
618 write_output_buffer(writer
->output
, L
" standalone=\"", 13);
619 if (standalone
== XmlStandalone_Yes
)
620 write_output_buffer(writer
->output
, L
"yes\"?>", 6);
622 write_output_buffer(writer
->output
, L
"no\"?>", 5);
628 static void writer_output_ns(xmlwriter
*writer
, struct element
*element
)
632 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
637 write_output_qname(writer
->output
, L
" xmlns", 6, ns
->prefix
, ns
->prefix_len
);
638 write_output_buffer_char(writer
->output
, '=');
639 write_output_buffer_quoted(writer
->output
, ns
->uri
, -1);
643 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
647 if (!writer
->starttagopen
) return S_OK
;
649 writer_output_ns(writer
, LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
));
650 hr
= write_output_buffer_char(writer
->output
, '>');
651 writer
->starttagopen
= 0;
655 static void writer_inc_indent(xmlwriter
*writer
)
657 writer
->indent_level
++;
660 static void writer_dec_indent(xmlwriter
*writer
)
662 if (writer
->indent_level
)
663 writer
->indent_level
--;
666 static void write_node_indent(xmlwriter
*writer
)
668 unsigned int indent_level
= writer
->indent_level
;
670 if (!writer
->indent
|| writer
->textnode
)
672 writer
->textnode
= 0;
676 /* Do state check to prevent newline inserted after BOM. It is assumed that
677 state does not change between writing BOM and inserting indentation. */
678 if (writer
->output
->written
&& writer
->state
!= XmlWriterState_Ready
)
679 write_output_buffer(writer
->output
, L
"\r\n", 2);
680 while (indent_level
--)
681 write_output_buffer(writer
->output
, L
" ", 2);
683 writer
->textnode
= 0;
686 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
688 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
690 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
692 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
693 IsEqualGUID(riid
, &IID_IUnknown
))
699 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
701 return E_NOINTERFACE
;
704 IXmlWriter_AddRef(iface
);
709 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
711 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
712 ULONG ref
= InterlockedIncrement(&writer
->ref
);
713 TRACE("%p, refcount %lu.\n", iface
, ref
);
717 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
719 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
720 ULONG ref
= InterlockedDecrement(&writer
->ref
);
722 TRACE("%p, refcount %lu.\n", iface
, ref
);
726 IMalloc
*imalloc
= writer
->imalloc
;
728 writeroutput_flush_stream(writer
->output
);
730 IUnknown_Release(&writer
->output
->IXmlWriterOutput_iface
);
732 writer_free_element_stack(writer
);
734 writer_free(writer
, writer
);
735 if (imalloc
) IMalloc_Release(imalloc
);
741 /*** IXmlWriter methods ***/
742 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
744 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
745 IXmlWriterOutput
*writeroutput
;
748 TRACE("(%p)->(%p)\n", This
, output
);
751 writeroutput_release_stream(This
->output
);
752 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
754 This
->bomwritten
= 0;
756 This
->indent_level
= 0;
757 writer_free_element_stack(This
);
760 /* just reset current output */
762 This
->state
= XmlWriterState_Initial
;
766 /* now try IXmlWriterOutput, ISequentialStream, IStream */
767 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
769 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
770 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
772 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
773 writeroutput
, writeroutput
->lpVtbl
);
774 IUnknown_Release(writeroutput
);
779 if (hr
!= S_OK
|| !writeroutput
) {
780 /* Create output for given stream. */
781 hr
= create_writer_output(output
, This
->imalloc
, XmlEncoding_UTF8
, NULL
, &This
->output
);
786 if (This
->output
->encoding
== XmlEncoding_Unknown
)
787 This
->state
= XmlWriterState_InvalidEncoding
;
789 This
->state
= XmlWriterState_Ready
;
790 return writeroutput_query_for_stream(This
->output
);
793 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
795 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
797 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
799 if (!value
) return E_INVALIDARG
;
803 case XmlWriterProperty_Indent
:
804 *value
= This
->indent
;
806 case XmlWriterProperty_ByteOrderMark
:
809 case XmlWriterProperty_OmitXmlDeclaration
:
810 *value
= This
->omitxmldecl
;
812 case XmlWriterProperty_ConformanceLevel
:
813 *value
= This
->conformance
;
816 FIXME("Unimplemented property (%u)\n", property
);
823 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
825 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
827 TRACE("%p, %s, %Id.\n", iface
, debugstr_writer_prop(property
), value
);
831 case XmlWriterProperty_Indent
:
832 writer
->indent
= !!value
;
834 case XmlWriterProperty_ByteOrderMark
:
835 writer
->bom
= !!value
;
837 case XmlWriterProperty_OmitXmlDeclaration
:
838 writer
->omitxmldecl
= !!value
;
841 FIXME("Unimplemented property (%u)\n", property
);
848 static HRESULT
writer_write_attribute(IXmlWriter
*writer
, IXmlReader
*reader
, BOOL write_default_attributes
)
850 const WCHAR
*prefix
, *local
, *uri
, *value
;
853 if (IXmlReader_IsDefault(reader
) && !write_default_attributes
)
856 if (FAILED(hr
= IXmlReader_GetPrefix(reader
, &prefix
, NULL
))) return hr
;
857 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &local
, NULL
))) return hr
;
858 if (FAILED(hr
= IXmlReader_GetNamespaceUri(reader
, &uri
, NULL
))) return hr
;
859 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
860 return IXmlWriter_WriteAttributeString(writer
, prefix
, local
, uri
, value
);
863 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
865 XmlNodeType node_type
;
868 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
870 if (FAILED(hr
= IXmlReader_GetNodeType(reader
, &node_type
))) return hr
;
874 case XmlNodeType_Element
:
875 case XmlNodeType_XmlDeclaration
:
876 case XmlNodeType_Attribute
:
877 if (node_type
!= XmlNodeType_Attribute
)
879 if (FAILED(hr
= IXmlReader_MoveToFirstAttribute(reader
))) return hr
;
880 if (hr
== S_FALSE
) return S_OK
;
882 if (FAILED(hr
= writer_write_attribute(iface
, reader
, write_default_attributes
))) return hr
;
883 while (IXmlReader_MoveToNextAttribute(reader
) == S_OK
)
885 if (FAILED(hr
= writer_write_attribute(iface
, reader
, write_default_attributes
))) break;
887 if (node_type
!= XmlNodeType_Attribute
&& SUCCEEDED(hr
))
888 hr
= IXmlReader_MoveToElement(reader
);
891 WARN("Unexpected node type %d.\n", node_type
);
898 static void write_output_attribute(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
,
899 const WCHAR
*local
, int local_len
, const WCHAR
*value
)
901 write_output_buffer_char(writer
->output
, ' ');
902 write_output_qname(writer
->output
, prefix
, prefix_len
, local
, local_len
);
903 write_output_buffer_char(writer
->output
, '=');
904 write_output_buffer_quoted(writer
->output
, value
, -1);
907 static BOOL
is_valid_xml_space_value(const WCHAR
*value
)
909 return value
&& (!wcscmp(value
, L
"preserve") || !wcscmp(value
, L
"default"));
912 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR prefix
,
913 LPCWSTR local
, LPCWSTR uri
, LPCWSTR value
)
915 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
916 BOOL is_xmlns_prefix
, is_xmlns_local
;
917 int prefix_len
, local_len
;
921 TRACE("%p, %s, %s, %s, %s.\n", iface
, debugstr_w(prefix
), debugstr_w(local
), debugstr_w(uri
), debugstr_w(value
));
923 switch (writer
->state
)
925 case XmlWriterState_Initial
:
927 case XmlWriterState_Ready
:
928 case XmlWriterState_DocClosed
:
929 writer
->state
= XmlWriterState_DocClosed
;
930 return WR_E_INVALIDACTION
;
931 case XmlWriterState_InvalidEncoding
:
932 return MX_E_ENCODING
;
938 is_xmlns_prefix
= prefix
&& !wcscmp(prefix
, L
"xmlns");
939 if (is_xmlns_prefix
&& is_empty_string(uri
) && is_empty_string(local
))
940 return WR_E_NSPREFIXDECLARED
;
942 if (is_empty_string(local
))
945 /* Validate prefix and local name */
946 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
949 if (FAILED(hr
= is_valid_ncname(local
, &local_len
)))
952 is_xmlns_local
= !wcscmp(local
, L
"xmlns");
954 /* Trivial case, no prefix. */
955 if (prefix_len
== 0 && is_empty_string(uri
))
957 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
961 /* Predefined "xml" prefix. */
962 if (prefix_len
&& !wcscmp(prefix
, L
"xml"))
964 /* Valid "space" value is enforced. */
965 if (!wcscmp(local
, L
"space") && !is_valid_xml_space_value(value
))
966 return WR_E_INVALIDXMLSPACE
;
968 /* Redefinition is not allowed. */
969 if (!is_empty_string(uri
))
970 return WR_E_XMLPREFIXDECLARATION
;
972 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
977 if (is_xmlns_prefix
|| (prefix_len
== 0 && uri
&& !wcscmp(uri
, xmlnsuriW
)))
979 if (prefix_len
&& !is_empty_string(uri
))
980 return WR_E_XMLNSPREFIXDECLARATION
;
982 /* Look for exact match defined in current element, and write it out. */
983 if (!(ns
= writer_find_ns_current(writer
, prefix
, value
)))
984 ns
= writer_push_ns(writer
, local
, local_len
, value
);
987 write_output_attribute(writer
, L
"xmlns", 5, local
, local_len
, value
);
992 /* Ignore prefix if URI wasn't specified. */
993 if (is_xmlns_local
&& is_empty_string(uri
))
995 write_output_attribute(writer
, NULL
, 0, L
"xmlns", 5, value
);
999 if (!(ns
= writer_find_ns(writer
, prefix
, uri
)))
1001 if (is_empty_string(prefix
) && !is_empty_string(uri
))
1003 FIXME("Prefix autogeneration is not implemented.\n");
1006 if (!is_empty_string(uri
))
1007 ns
= writer_push_ns(writer
, prefix
, prefix_len
, uri
);
1011 write_output_attribute(writer
, ns
->prefix
, ns
->prefix_len
, local
, local_len
, value
);
1013 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
1018 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
1020 write_output_buffer(output
, L
"<![CDATA[", 9);
1022 write_output_buffer(output
, data
, len
);
1023 write_output_buffer(output
, L
"]]>", 3);
1026 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
1028 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1031 TRACE("%p %s\n", This
, debugstr_w(data
));
1033 switch (This
->state
)
1035 case XmlWriterState_Initial
:
1036 return E_UNEXPECTED
;
1037 case XmlWriterState_ElemStarted
:
1038 writer_close_starttag(This
);
1040 case XmlWriterState_Ready
:
1041 case XmlWriterState_DocClosed
:
1042 This
->state
= XmlWriterState_DocClosed
;
1043 return WR_E_INVALIDACTION
;
1044 case XmlWriterState_InvalidEncoding
:
1045 return MX_E_ENCODING
;
1050 len
= data
? lstrlenW(data
) : 0;
1052 write_node_indent(This
);
1054 write_cdata_section(This
->output
, NULL
, 0);
1059 const WCHAR
*str
= wcsstr(data
, L
"]]>");
1062 write_cdata_section(This
->output
, data
, str
- data
);
1067 write_cdata_section(This
->output
, data
, len
);
1076 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
1078 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1081 TRACE("%p %#x\n", This
, ch
);
1083 switch (This
->state
)
1085 case XmlWriterState_Initial
:
1086 return E_UNEXPECTED
;
1087 case XmlWriterState_InvalidEncoding
:
1088 return MX_E_ENCODING
;
1089 case XmlWriterState_ElemStarted
:
1090 writer_close_starttag(This
);
1092 case XmlWriterState_DocClosed
:
1093 return WR_E_INVALIDACTION
;
1098 swprintf(bufW
, ARRAY_SIZE(bufW
), L
"&#x%x;", ch
);
1099 write_output_buffer(This
->output
, bufW
, -1);
1104 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1106 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1108 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1110 switch (This
->state
)
1112 case XmlWriterState_Initial
:
1113 return E_UNEXPECTED
;
1114 case XmlWriterState_InvalidEncoding
:
1115 return MX_E_ENCODING
;
1116 case XmlWriterState_DocClosed
:
1117 return WR_E_INVALIDACTION
;
1126 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
1128 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1130 TRACE("%p %s\n", This
, debugstr_w(comment
));
1132 switch (This
->state
)
1134 case XmlWriterState_Initial
:
1135 return E_UNEXPECTED
;
1136 case XmlWriterState_InvalidEncoding
:
1137 return MX_E_ENCODING
;
1138 case XmlWriterState_ElemStarted
:
1139 writer_close_starttag(This
);
1141 case XmlWriterState_DocClosed
:
1142 return WR_E_INVALIDACTION
;
1147 write_node_indent(This
);
1148 write_output_buffer(This
->output
, L
"<!--", 4);
1150 int len
= lstrlenW(comment
), i
;
1152 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1155 for (i
= 0; i
< len
; i
++) {
1156 write_output_buffer(This
->output
, comment
+ i
, 1);
1157 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
1158 write_output_buffer_char(This
->output
, ' ');
1162 write_output_buffer(This
->output
, comment
, len
);
1164 if (len
&& comment
[len
-1] == '-')
1165 write_output_buffer_char(This
->output
, ' ');
1167 write_output_buffer(This
->output
, L
"-->", 3);
1172 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR name
, LPCWSTR pubid
,
1173 LPCWSTR sysid
, LPCWSTR subset
)
1175 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1176 unsigned int name_len
, pubid_len
;
1179 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(pubid
), wine_dbgstr_w(sysid
),
1180 wine_dbgstr_w(subset
));
1182 switch (This
->state
)
1184 case XmlWriterState_Initial
:
1185 return E_UNEXPECTED
;
1186 case XmlWriterState_InvalidEncoding
:
1187 return MX_E_ENCODING
;
1188 case XmlWriterState_Content
:
1189 case XmlWriterState_DocClosed
:
1190 return WR_E_INVALIDACTION
;
1195 if (is_empty_string(name
))
1196 return E_INVALIDARG
;
1198 if (FAILED(hr
= is_valid_name(name
, &name_len
)))
1201 if (FAILED(hr
= is_valid_pubid(pubid
, &pubid_len
)))
1204 write_output_buffer(This
->output
, L
"<!DOCTYPE ", 10);
1205 write_output_buffer(This
->output
, name
, name_len
);
1209 write_output_buffer(This
->output
, L
" PUBLIC ", 8);
1210 write_output_buffer_quoted(This
->output
, pubid
, pubid_len
);
1211 write_output_buffer_char(This
->output
, ' ');
1212 write_output_buffer_quoted(This
->output
, sysid
, -1);
1216 write_output_buffer(This
->output
, L
" SYSTEM ", 8);
1217 write_output_buffer_quoted(This
->output
, sysid
, -1);
1222 write_output_buffer_char(This
->output
, ' ');
1223 write_output_buffer_char(This
->output
, '[');
1224 write_output_buffer(This
->output
, subset
, -1);
1225 write_output_buffer_char(This
->output
, ']');
1227 write_output_buffer_char(This
->output
, '>');
1229 This
->state
= XmlWriterState_Content
;
1234 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
1235 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
1237 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1238 int prefix_len
, local_len
;
1242 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
1243 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
1245 switch (This
->state
)
1247 case XmlWriterState_Initial
:
1248 return E_UNEXPECTED
;
1249 case XmlWriterState_InvalidEncoding
:
1250 return MX_E_ENCODING
;
1251 case XmlWriterState_ElemStarted
:
1252 writer_close_starttag(This
);
1254 case XmlWriterState_DocClosed
:
1255 return WR_E_INVALIDACTION
;
1261 return E_INVALIDARG
;
1263 /* Validate prefix and local name */
1264 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1267 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1270 ns
= writer_find_ns(This
, prefix
, uri
);
1271 if (!ns
&& !is_empty_string(prefix
) && is_empty_string(uri
))
1272 return WR_E_NSPREFIXWITHEMPTYNSURI
;
1274 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1277 return WR_E_XMLNSPREFIXDECLARATION
;
1279 if (!is_empty_string(prefix
))
1280 return WR_E_XMLNSURIDECLARATION
;
1283 write_encoding_bom(This
);
1284 write_node_indent(This
);
1286 write_output_buffer_char(This
->output
, '<');
1288 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1290 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1292 if (!ns
&& (prefix_len
|| !is_empty_string(uri
)))
1294 write_output_qname(This
->output
, L
" xmlns", 6, prefix
, prefix_len
);
1295 write_output_buffer_char(This
->output
, '=');
1296 write_output_buffer_quoted(This
->output
, uri
, -1);
1301 write_output_buffer_char(This
->output
, '>');
1302 write_output_buffer(This
->output
, value
, -1);
1303 write_output_buffer(This
->output
, L
"</", 2);
1304 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1305 write_output_buffer_char(This
->output
, '>');
1308 write_output_buffer(This
->output
, L
" />", 3);
1310 This
->state
= XmlWriterState_Content
;
1315 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
1317 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1319 TRACE("%p\n", This
);
1321 switch (This
->state
)
1323 case XmlWriterState_Initial
:
1324 return E_UNEXPECTED
;
1325 case XmlWriterState_Ready
:
1326 case XmlWriterState_DocClosed
:
1327 This
->state
= XmlWriterState_DocClosed
;
1328 return WR_E_INVALIDACTION
;
1329 case XmlWriterState_InvalidEncoding
:
1330 return MX_E_ENCODING
;
1335 /* empty element stack */
1336 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
1339 This
->state
= XmlWriterState_DocClosed
;
1343 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
1345 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1346 struct element
*element
;
1348 TRACE("%p\n", This
);
1350 switch (This
->state
)
1352 case XmlWriterState_Initial
:
1353 return E_UNEXPECTED
;
1354 case XmlWriterState_Ready
:
1355 case XmlWriterState_DocClosed
:
1356 This
->state
= XmlWriterState_DocClosed
;
1357 return WR_E_INVALIDACTION
;
1358 case XmlWriterState_InvalidEncoding
:
1359 return MX_E_ENCODING
;
1364 element
= pop_element(This
);
1366 return WR_E_INVALIDACTION
;
1368 writer_dec_indent(This
);
1370 if (This
->starttagopen
)
1372 writer_output_ns(This
, element
);
1373 write_output_buffer(This
->output
, L
" />", 3);
1374 This
->starttagopen
= 0;
1378 /* Write full end tag. */
1379 write_node_indent(This
);
1380 write_output_buffer(This
->output
, L
"</", 2);
1381 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1382 write_output_buffer_char(This
->output
, '>');
1384 writer_free_element(This
, element
);
1389 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
1391 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1393 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1395 switch (This
->state
)
1397 case XmlWriterState_Initial
:
1398 return E_UNEXPECTED
;
1399 case XmlWriterState_InvalidEncoding
:
1400 return MX_E_ENCODING
;
1401 case XmlWriterState_DocClosed
:
1402 return WR_E_INVALIDACTION
;
1410 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
1412 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1413 struct element
*element
;
1415 TRACE("%p\n", This
);
1417 switch (This
->state
)
1419 case XmlWriterState_Initial
:
1420 return E_UNEXPECTED
;
1421 case XmlWriterState_Ready
:
1422 case XmlWriterState_DocClosed
:
1423 This
->state
= XmlWriterState_DocClosed
;
1424 return WR_E_INVALIDACTION
;
1425 case XmlWriterState_InvalidEncoding
:
1426 return MX_E_ENCODING
;
1427 case XmlWriterState_ElemStarted
:
1428 writer_close_starttag(This
);
1434 element
= pop_element(This
);
1436 return WR_E_INVALIDACTION
;
1438 writer_dec_indent(This
);
1440 /* don't force full end tag to the next line */
1441 if (This
->state
== XmlWriterState_ElemStarted
)
1443 This
->state
= XmlWriterState_Content
;
1447 write_node_indent(This
);
1449 /* write full end tag */
1450 write_output_buffer(This
->output
, L
"</", 2);
1451 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1452 write_output_buffer_char(This
->output
, '>');
1454 writer_free_element(This
, element
);
1459 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1461 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1463 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1465 switch (This
->state
)
1467 case XmlWriterState_Initial
:
1468 return E_UNEXPECTED
;
1469 case XmlWriterState_Ready
:
1470 case XmlWriterState_DocClosed
:
1471 This
->state
= XmlWriterState_DocClosed
;
1472 return WR_E_INVALIDACTION
;
1473 case XmlWriterState_InvalidEncoding
:
1474 return MX_E_ENCODING
;
1482 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1484 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1486 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1488 switch (This
->state
)
1490 case XmlWriterState_Initial
:
1491 return E_UNEXPECTED
;
1492 case XmlWriterState_Ready
:
1493 case XmlWriterState_DocClosed
:
1494 This
->state
= XmlWriterState_DocClosed
;
1495 return WR_E_INVALIDACTION
;
1496 case XmlWriterState_InvalidEncoding
:
1497 return MX_E_ENCODING
;
1505 static HRESULT
writer_write_node(IXmlWriter
*writer
, IXmlReader
*reader
, BOOL shallow
, BOOL write_default_attributes
)
1507 XmlStandalone standalone
= XmlStandalone_Omit
;
1508 const WCHAR
*name
, *value
, *prefix
, *uri
;
1509 unsigned int start_depth
= 0, depth
;
1510 XmlNodeType node_type
;
1513 if (FAILED(hr
= IXmlReader_GetNodeType(reader
, &node_type
))) return hr
;
1517 case XmlNodeType_None
:
1518 if (shallow
) return S_OK
;
1519 while ((hr
= IXmlReader_Read(reader
, NULL
)) == S_OK
)
1521 if (FAILED(hr
= writer_write_node(writer
, reader
, FALSE
, write_default_attributes
))) return hr
;
1524 case XmlNodeType_Element
:
1525 if (FAILED(hr
= IXmlReader_GetPrefix(reader
, &prefix
, NULL
))) return hr
;
1526 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1527 if (FAILED(hr
= IXmlReader_GetNamespaceUri(reader
, &uri
, NULL
))) return hr
;
1528 if (FAILED(hr
= IXmlWriter_WriteStartElement(writer
, prefix
, name
, uri
))) return hr
;
1529 if (FAILED(hr
= IXmlWriter_WriteAttributes(writer
, reader
, write_default_attributes
))) return hr
;
1530 if (IXmlReader_IsEmptyElement(reader
))
1532 hr
= IXmlWriter_WriteEndElement(writer
);
1536 if (shallow
) return S_OK
;
1537 if (FAILED(hr
= IXmlReader_MoveToElement(reader
))) return hr
;
1538 if (FAILED(hr
= IXmlReader_GetDepth(reader
, &start_depth
))) return hr
;
1539 while ((hr
= IXmlReader_Read(reader
, &node_type
)) == S_OK
)
1541 if (FAILED(hr
= writer_write_node(writer
, reader
, FALSE
, write_default_attributes
))) return hr
;
1542 if (FAILED(hr
= IXmlReader_MoveToElement(reader
))) return hr
;
1545 if (FAILED(hr
= IXmlReader_GetDepth(reader
, &depth
))) return hr
;
1546 if (node_type
== XmlNodeType_EndElement
&& (start_depth
== depth
- 1)) break;
1550 case XmlNodeType_Attribute
:
1552 case XmlNodeType_Text
:
1553 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1554 hr
= IXmlWriter_WriteRaw(writer
, value
);
1556 case XmlNodeType_CDATA
:
1557 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1558 hr
= IXmlWriter_WriteCData(writer
, value
);
1560 case XmlNodeType_ProcessingInstruction
:
1561 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1562 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1563 hr
= IXmlWriter_WriteProcessingInstruction(writer
, name
, value
);
1565 case XmlNodeType_Comment
:
1566 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1567 hr
= IXmlWriter_WriteComment(writer
, value
);
1569 case XmlNodeType_Whitespace
:
1570 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1571 hr
= IXmlWriter_WriteWhitespace(writer
, value
);
1573 case XmlNodeType_EndElement
:
1574 hr
= IXmlWriter_WriteFullEndElement(writer
);
1576 case XmlNodeType_XmlDeclaration
:
1577 while ((hr
= IXmlReader_MoveToNextAttribute(reader
)) == S_OK
)
1579 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1580 if (!wcscmp(name
, L
"standalone"))
1582 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1583 standalone
= !wcscmp(value
, L
"yes") ? XmlStandalone_Yes
: XmlStandalone_No
;
1587 hr
= IXmlWriter_WriteStartDocument(writer
, standalone
);
1590 WARN("Unknown node type %d.\n", node_type
);
1591 return E_UNEXPECTED
;
1597 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
1601 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
1603 if (SUCCEEDED(hr
= writer_write_node(iface
, reader
, FALSE
, write_default_attributes
)))
1604 hr
= IXmlReader_Read(reader
, NULL
);
1609 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
1611 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
1613 return writer_write_node(iface
, reader
, TRUE
, write_default_attributes
);
1616 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1619 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1621 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1623 switch (This
->state
)
1625 case XmlWriterState_Initial
:
1626 return E_UNEXPECTED
;
1627 case XmlWriterState_InvalidEncoding
:
1628 return MX_E_ENCODING
;
1629 case XmlWriterState_DocStarted
:
1630 if (!wcscmp(name
, L
"xml"))
1631 return WR_E_INVALIDACTION
;
1633 case XmlWriterState_ElemStarted
:
1634 writer_close_starttag(This
);
1636 case XmlWriterState_DocClosed
:
1637 return WR_E_INVALIDACTION
;
1642 write_encoding_bom(This
);
1643 write_node_indent(This
);
1644 write_output_buffer(This
->output
, L
"<?", 2);
1645 write_output_buffer(This
->output
, name
, -1);
1646 write_output_buffer_char(This
->output
, ' ');
1647 write_output_buffer(This
->output
, text
, -1);
1648 write_output_buffer(This
->output
, L
"?>", 2);
1650 if (!wcscmp(name
, L
"xml"))
1651 This
->state
= XmlWriterState_PIDocStarted
;
1656 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1657 LPCWSTR pwszNamespaceUri
)
1659 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1661 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1663 switch (This
->state
)
1665 case XmlWriterState_Initial
:
1666 return E_UNEXPECTED
;
1667 case XmlWriterState_InvalidEncoding
:
1668 return MX_E_ENCODING
;
1669 case XmlWriterState_DocClosed
:
1670 return WR_E_INVALIDACTION
;
1678 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1680 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1682 TRACE("%p %s\n", This
, debugstr_w(data
));
1687 switch (This
->state
)
1689 case XmlWriterState_Initial
:
1690 return E_UNEXPECTED
;
1691 case XmlWriterState_Ready
:
1692 write_xmldecl(This
, XmlStandalone_Omit
);
1694 case XmlWriterState_DocStarted
:
1695 case XmlWriterState_PIDocStarted
:
1697 case XmlWriterState_InvalidEncoding
:
1698 return MX_E_ENCODING
;
1699 case XmlWriterState_ElemStarted
:
1700 writer_close_starttag(This
);
1703 This
->state
= XmlWriterState_DocClosed
;
1704 return WR_E_INVALIDACTION
;
1707 write_output_buffer(This
->output
, data
, -1);
1711 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*pwch
, UINT cwch
)
1713 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1715 FIXME("%p %s %d\n", This
, wine_dbgstr_w(pwch
), cwch
);
1717 switch (This
->state
)
1719 case XmlWriterState_Initial
:
1720 return E_UNEXPECTED
;
1721 case XmlWriterState_InvalidEncoding
:
1722 return MX_E_ENCODING
;
1723 case XmlWriterState_DocClosed
:
1724 return WR_E_INVALIDACTION
;
1732 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1734 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1736 TRACE("(%p)->(%d)\n", This
, standalone
);
1738 switch (This
->state
)
1740 case XmlWriterState_Initial
:
1741 return E_UNEXPECTED
;
1742 case XmlWriterState_PIDocStarted
:
1743 This
->state
= XmlWriterState_DocStarted
;
1745 case XmlWriterState_Ready
:
1747 case XmlWriterState_InvalidEncoding
:
1748 return MX_E_ENCODING
;
1750 This
->state
= XmlWriterState_DocClosed
;
1751 return WR_E_INVALIDACTION
;
1754 return write_xmldecl(This
, standalone
);
1757 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1759 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1760 int prefix_len
, local_len
;
1761 struct element
*element
;
1765 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1768 return E_INVALIDARG
;
1770 switch (This
->state
)
1772 case XmlWriterState_Initial
:
1773 return E_UNEXPECTED
;
1774 case XmlWriterState_InvalidEncoding
:
1775 return MX_E_ENCODING
;
1776 case XmlWriterState_DocClosed
:
1777 return WR_E_INVALIDACTION
;
1778 case XmlWriterState_ElemStarted
:
1779 writer_close_starttag(This
);
1785 /* Validate prefix and local name */
1786 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1789 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1792 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1795 return WR_E_XMLNSPREFIXDECLARATION
;
1797 if (!is_empty_string(prefix
))
1798 return WR_E_XMLNSURIDECLARATION
;
1801 ns
= writer_find_ns(This
, prefix
, uri
);
1803 element
= alloc_element(This
, prefix
, local_name
);
1805 return E_OUTOFMEMORY
;
1807 write_encoding_bom(This
);
1808 write_node_indent(This
);
1810 This
->state
= XmlWriterState_ElemStarted
;
1811 This
->starttagopen
= 1;
1813 writer_push_element(This
, element
);
1815 if (!ns
&& !is_empty_string(uri
))
1816 writer_push_ns(This
, prefix
, prefix_len
, uri
);
1818 write_output_buffer_char(This
->output
, '<');
1820 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1822 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1823 writer_inc_indent(This
);
1828 static void write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
)
1835 write_output_buffer(writer
->output
, L
"<", 4);
1838 write_output_buffer(writer
->output
, L
"&", 5);
1841 write_output_buffer(writer
->output
, L
">", 4);
1844 write_output_buffer(writer
->output
, string
, 1);
1851 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1853 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1855 TRACE("%p %s\n", This
, debugstr_w(string
));
1860 switch (This
->state
)
1862 case XmlWriterState_Initial
:
1863 return E_UNEXPECTED
;
1864 case XmlWriterState_ElemStarted
:
1865 writer_close_starttag(This
);
1867 case XmlWriterState_Ready
:
1868 case XmlWriterState_DocClosed
:
1869 This
->state
= XmlWriterState_DocClosed
;
1870 return WR_E_INVALIDACTION
;
1871 case XmlWriterState_InvalidEncoding
:
1872 return MX_E_ENCODING
;
1878 write_escaped_string(This
, string
);
1882 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1884 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1886 FIXME("%p %d %d\n", This
, wchLow
, wchHigh
);
1891 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR text
)
1893 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
1896 TRACE("%p, %s.\n", iface
, wine_dbgstr_w(text
));
1898 switch (writer
->state
)
1900 case XmlWriterState_Initial
:
1901 return E_UNEXPECTED
;
1902 case XmlWriterState_ElemStarted
:
1903 writer_close_starttag(writer
);
1905 case XmlWriterState_InvalidEncoding
:
1906 return MX_E_ENCODING
;
1907 case XmlWriterState_Ready
:
1910 return WR_E_INVALIDACTION
;
1913 while (text
[length
])
1915 if (!is_wchar_space(text
[length
])) return WR_E_NONWHITESPACE
;
1919 write_output_buffer(writer
->output
, text
, length
);
1923 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
1925 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1927 TRACE("%p\n", This
);
1929 return writeroutput_flush_stream(This
->output
);
1932 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
1934 xmlwriter_QueryInterface
,
1937 xmlwriter_SetOutput
,
1938 xmlwriter_GetProperty
,
1939 xmlwriter_SetProperty
,
1940 xmlwriter_WriteAttributes
,
1941 xmlwriter_WriteAttributeString
,
1942 xmlwriter_WriteCData
,
1943 xmlwriter_WriteCharEntity
,
1944 xmlwriter_WriteChars
,
1945 xmlwriter_WriteComment
,
1946 xmlwriter_WriteDocType
,
1947 xmlwriter_WriteElementString
,
1948 xmlwriter_WriteEndDocument
,
1949 xmlwriter_WriteEndElement
,
1950 xmlwriter_WriteEntityRef
,
1951 xmlwriter_WriteFullEndElement
,
1952 xmlwriter_WriteName
,
1953 xmlwriter_WriteNmToken
,
1954 xmlwriter_WriteNode
,
1955 xmlwriter_WriteNodeShallow
,
1956 xmlwriter_WriteProcessingInstruction
,
1957 xmlwriter_WriteQualifiedName
,
1959 xmlwriter_WriteRawChars
,
1960 xmlwriter_WriteStartDocument
,
1961 xmlwriter_WriteStartElement
,
1962 xmlwriter_WriteString
,
1963 xmlwriter_WriteSurrogateCharEntity
,
1964 xmlwriter_WriteWhitespace
,
1968 /** IXmlWriterOutput **/
1969 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
1971 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
1973 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1975 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
1976 IsEqualGUID(riid
, &IID_IUnknown
))
1982 WARN("interface %s not implemented\n", debugstr_guid(riid
));
1984 return E_NOINTERFACE
;
1987 IUnknown_AddRef(iface
);
1992 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
1994 xmlwriteroutput
*output
= impl_from_IXmlWriterOutput(iface
);
1995 ULONG ref
= InterlockedIncrement(&output
->ref
);
1996 TRACE("%p, refcount %ld.\n", iface
, ref
);
2000 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
2002 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
2003 LONG ref
= InterlockedDecrement(&This
->ref
);
2005 TRACE("%p, refcount %ld.\n", iface
, ref
);
2009 IMalloc
*imalloc
= This
->imalloc
;
2010 if (This
->output
) IUnknown_Release(This
->output
);
2011 if (This
->stream
) ISequentialStream_Release(This
->stream
);
2012 free_output_buffer(This
);
2013 writeroutput_free(This
, This
->encoding_name
);
2014 writeroutput_free(This
, This
);
2015 if (imalloc
) IMalloc_Release(imalloc
);
2021 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
2023 xmlwriteroutput_QueryInterface
,
2024 xmlwriteroutput_AddRef
,
2025 xmlwriteroutput_Release
2028 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
2033 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
2035 if (!(writer
= m_alloc(imalloc
, sizeof(*writer
))))
2036 return E_OUTOFMEMORY
;
2037 memset(writer
, 0, sizeof(*writer
));
2039 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
2041 writer
->imalloc
= imalloc
;
2042 if (imalloc
) IMalloc_AddRef(imalloc
);
2044 writer
->conformance
= XmlConformanceLevel_Document
;
2045 writer
->state
= XmlWriterState_Initial
;
2046 list_init(&writer
->elements
);
2048 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
2049 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
2051 TRACE("returning iface %p, hr %#lx.\n", *obj
, hr
);
2056 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
2057 const WCHAR
*encoding_name
, xmlwriteroutput
**out
)
2059 xmlwriteroutput
*writeroutput
;
2064 if (!(writeroutput
= m_alloc(imalloc
, sizeof(*writeroutput
))))
2065 return E_OUTOFMEMORY
;
2066 memset(writeroutput
, 0, sizeof(*writeroutput
));
2068 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
2069 writeroutput
->ref
= 1;
2070 writeroutput
->imalloc
= imalloc
;
2072 IMalloc_AddRef(imalloc
);
2073 writeroutput
->encoding
= encoding
;
2074 hr
= init_output_buffer(writeroutput
);
2076 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
2082 unsigned int size
= (lstrlenW(encoding_name
) + 1) * sizeof(WCHAR
);
2083 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
2084 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
2087 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
2089 *out
= writeroutput
;
2091 TRACE("Created writer output %p\n", *out
);
2096 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
, IMalloc
*imalloc
, const WCHAR
*encoding
,
2097 IXmlWriterOutput
**out
)
2099 xmlwriteroutput
*output
;
2100 xml_encoding xml_enc
;
2103 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), out
);
2105 if (!stream
|| !out
)
2106 return E_INVALIDARG
;
2110 xml_enc
= encoding
? parse_encoding_name(encoding
, -1) : XmlEncoding_UTF8
;
2111 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, encoding
, &output
)))
2112 *out
= &output
->IXmlWriterOutput_iface
;
2117 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
, IMalloc
*imalloc
, UINT codepage
,
2118 IXmlWriterOutput
**out
)
2120 xmlwriteroutput
*output
;
2121 xml_encoding xml_enc
;
2124 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, out
);
2126 if (!stream
|| !out
)
2127 return E_INVALIDARG
;
2131 xml_enc
= get_encoding_from_codepage(codepage
);
2132 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, NULL
, &output
)))
2133 *out
= &output
->IXmlWriterOutput_iface
;