d3d11: Report D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW in CheckFormatSupport().
[wine.git] / dlls / xmllite / writer.c
blob35d4c0e6217116899f94764494a6131df33dee72
1 /*
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
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "xmllite.h"
30 #include "xmllite_private.h"
31 #include "initguid.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};
47 struct output_buffer
49 char *data;
50 unsigned int allocated;
51 unsigned int written;
52 UINT codepage;
55 typedef enum
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 */
65 } XmlWriterState;
67 typedef struct
69 IXmlWriterOutput IXmlWriterOutput_iface;
70 LONG ref;
71 IUnknown *output;
72 ISequentialStream *stream;
73 IMalloc *imalloc;
74 xml_encoding encoding;
75 WCHAR *encoding_name; /* exactly as specified on output creation */
76 struct output_buffer buffer;
77 DWORD written : 1;
78 } xmlwriteroutput;
80 static const struct IUnknownVtbl xmlwriteroutputvtbl;
82 struct element
84 struct list entry;
85 WCHAR *qname;
86 unsigned int len; /* qname length in chars */
87 struct list ns;
90 struct ns
92 struct list entry;
93 WCHAR *prefix;
94 int prefix_len;
95 WCHAR *uri;
96 BOOL emitted;
97 struct element *element;
100 typedef struct _xmlwriter
102 IXmlWriter IXmlWriter_iface;
103 LONG ref;
104 IMalloc *imalloc;
105 xmlwriteroutput *output;
106 unsigned int indent_level;
107 BOOL indent;
108 BOOL bom;
109 BOOL omitxmldecl;
110 XmlConformanceLevel conformance;
111 XmlWriterState state;
112 struct list elements;
113 DWORD bomwritten : 1;
114 DWORD starttagopen : 1;
115 DWORD textnode : 1;
116 } xmlwriter;
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[] =
132 "MultiLanguage",
133 "Indent",
134 "ByteOrderMark",
135 "OmitXmlDeclaration",
136 "ConformanceLevel"
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)
177 struct element *ret;
178 int len;
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));
187 ret->len = len;
188 if (prefix) {
189 static const WCHAR colonW[] = {':',0};
190 lstrcpyW(ret->qname, prefix);
191 lstrcatW(ret->qname, colonW);
193 else
194 ret->qname[0] = 0;
195 lstrcatW(ret->qname, local);
196 list_init(&ret->ns);
198 return ret;
201 static void writer_free_element(xmlwriter *writer, struct element *element)
203 struct ns *ns, *ns2;
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);
237 if (element)
238 list_remove(&element->entry);
240 return element;
243 static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len)
245 size_t size;
246 WCHAR *ret;
248 if (!str)
249 return NULL;
251 if (len == -1)
252 len = lstrlenW(str);
254 size = (len + 1) * sizeof(WCHAR);
255 ret = writer_alloc(writer, size);
256 memcpy(ret, str, size);
257 return ret;
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;
268 struct ns *ns;
270 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
271 if (!element)
272 return NULL;
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);
279 ns->emitted = FALSE;
280 ns->element = element;
281 list_add_tail(&element->ns, &ns->entry);
284 return ns;
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;
295 struct ns *ns;
297 if (is_empty_string(prefix) || is_empty_string(uri))
298 return NULL;
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))
305 return ns;
308 return NULL;
311 static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
313 struct element *element;
314 struct ns *ns;
316 if (is_empty_string(prefix) && is_empty_string(uri))
317 return NULL;
319 LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
321 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
323 if (!uri)
325 if (!ns->prefix) continue;
326 if (!wcscmp(ns->prefix, prefix))
327 return ns;
329 else if (!wcscmp(uri, ns->uri))
331 if (prefix && !*prefix)
332 return NULL;
333 if (!prefix || !wcscmp(prefix, ns->prefix))
334 return ns;
339 return NULL;
342 static HRESULT is_valid_ncname(const WCHAR *str, int *out)
344 int len = 0;
346 *out = 0;
348 if (!str || !*str)
349 return S_OK;
351 while (*str)
353 if (!is_ncnamechar(*str))
354 return WC_E_NAMECHARACTER;
355 len++;
356 str++;
359 *out = len;
360 return S_OK;
363 static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
365 unsigned int len = 1;
367 *out = 0;
369 if (!str || !*str)
370 return S_OK;
372 if (!is_namestartchar(*str++))
373 return WC_E_NAMECHARACTER;
375 while (*str++)
377 if (!is_namechar(*str))
378 return WC_E_NAMECHARACTER;
379 len++;
382 *out = len;
383 return S_OK;
386 static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
388 unsigned int len = 0;
390 *out = 0;
392 if (!str || !*str)
393 return S_OK;
395 while (*str)
397 if (!is_pubchar(*str++))
398 return WC_E_PUBLICID;
399 len++;
402 *out = len;
404 return S_OK;
407 static HRESULT init_output_buffer(xmlwriteroutput *output)
409 struct output_buffer *buffer = &output->buffer;
410 const int initial_len = 0x2000;
411 UINT cp = ~0u;
412 HRESULT hr;
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;
422 buffer->written = 0;
423 buffer->codepage = cp;
425 return S_OK;
428 static void free_output_buffer(xmlwriteroutput *output)
430 struct output_buffer *buffer = &output->buffer;
431 writeroutput_free(output, buffer->data);
432 buffer->data = NULL;
433 buffer->allocated = 0;
434 buffer->written = 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;
445 buffer->data = ptr;
446 buffer->allocated = grown_size;
449 return S_OK;
452 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
454 struct output_buffer *buffer = &output->buffer;
455 int length;
456 HRESULT hr;
457 char *ptr;
459 if (buffer->codepage == 1200) {
460 /* For UTF-16 encoding just copy. */
461 length = len == -1 ? lstrlenW(data) : len;
462 if (length) {
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;
471 ptr += length;
472 /* null termination */
473 *(WCHAR*)ptr = 0;
476 else {
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;
486 return S_OK;
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, '"');
500 return S_OK;
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);
509 if (prefix_len)
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);
517 return S_OK;
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)
530 HRESULT hr;
532 writeroutput_release_stream(writeroutput);
533 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
534 if (hr != S_OK)
535 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
537 return hr;
540 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
542 struct output_buffer *buffer;
543 ULONG written, offset = 0;
544 HRESULT hr;
546 if (!output || !output->stream)
547 return S_OK;
549 buffer = &output->buffer;
551 /* It will loop forever until everything is written or an error occurred. */
552 do {
553 written = 0;
554 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
555 if (FAILED(hr)) {
556 WARN("write to stream failed (0x%08x)\n", hr);
557 buffer->written = 0;
558 return hr;
561 offset += written;
562 buffer->written -= written;
563 } while (buffer->written > 0);
565 return S_OK;
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);
576 HRESULT hr;
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;
585 return S_OK;
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;
605 /* version */
606 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
608 /* encoding */
609 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
610 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
612 /* standalone */
613 if (standalone == XmlStandalone_Omit)
614 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
615 else {
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));
623 else
624 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
627 return S_OK;
630 static void writer_output_ns(xmlwriter *writer, struct element *element)
632 struct ns *ns;
634 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
636 if (ns->emitted)
637 continue;
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)
647 HRESULT hr;
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;
654 return hr;
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;
677 return;
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))
699 *ppvObject = iface;
701 else
703 FIXME("interface %s is not supported\n", debugstr_guid(riid));
704 *ppvObject = NULL;
705 return E_NOINTERFACE;
708 IXmlWriter_AddRef(iface);
710 return S_OK;
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);
718 return 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);
728 if (ref == 0) {
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);
740 return ref;
743 /*** IXmlWriter methods ***/
744 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
746 xmlwriter *This = impl_from_IXmlWriter(iface);
747 IXmlWriterOutput *writeroutput;
748 HRESULT hr;
750 TRACE("(%p)->(%p)\n", This, output);
752 if (This->output) {
753 writeroutput_release_stream(This->output);
754 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
755 This->output = NULL;
756 This->bomwritten = 0;
757 This->textnode = 0;
758 This->indent_level = 0;
759 writer_free_element_stack(This);
762 /* just reset current output */
763 if (!output) {
764 This->state = XmlWriterState_Initial;
765 return S_OK;
768 /* now try IXmlWriterOutput, ISequentialStream, IStream */
769 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
770 if (hr == S_OK) {
771 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
772 This->output = impl_from_IXmlWriterOutput(writeroutput);
773 else {
774 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
775 writeroutput, writeroutput->lpVtbl);
776 IUnknown_Release(writeroutput);
777 return E_FAIL;
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);
784 if (hr != S_OK)
785 return hr;
788 if (This->output->encoding == XmlEncoding_Unknown)
789 This->state = XmlWriterState_InvalidEncoding;
790 else
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;
803 switch (property)
805 case XmlWriterProperty_Indent:
806 *value = This->indent;
807 break;
808 case XmlWriterProperty_ByteOrderMark:
809 *value = This->bom;
810 break;
811 case XmlWriterProperty_OmitXmlDeclaration:
812 *value = This->omitxmldecl;
813 break;
814 case XmlWriterProperty_ConformanceLevel:
815 *value = This->conformance;
816 break;
817 default:
818 FIXME("Unimplemented property (%u)\n", property);
819 return E_NOTIMPL;
822 return S_OK;
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);
831 switch (property)
833 case XmlWriterProperty_Indent:
834 This->indent = !!value;
835 break;
836 case XmlWriterProperty_ByteOrderMark:
837 This->bom = !!value;
838 break;
839 case XmlWriterProperty_OmitXmlDeclaration:
840 This->omitxmldecl = !!value;
841 break;
842 default:
843 FIXME("Unimplemented property (%u)\n", property);
844 return E_NOTIMPL;
847 return S_OK;
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);
857 return E_NOTIMPL;
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};
874 if (!value)
875 return FALSE;
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;
889 struct ns *ns;
890 HRESULT hr;
892 TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
894 switch (This->state)
896 case XmlWriterState_Initial:
897 return E_UNEXPECTED;
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;
904 default:
908 /* Prefix "xmlns" */
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;
913 if (!local)
914 return E_INVALIDARG;
916 /* Validate prefix and local name */
917 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
918 return hr;
920 if (FAILED(hr = is_valid_ncname(local, &local_len)))
921 return hr;
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);
929 return S_OK;
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);
945 return S_OK;
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);
956 ns->emitted = TRUE;
958 write_output_attribute(This, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, local, local_len, value);
960 return S_OK;
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);
967 return S_OK;
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");
975 return E_NOTIMPL;
977 if (!is_empty_string(uri))
978 ns = writer_push_ns(This, prefix, prefix_len, uri);
981 if (ns)
982 write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
983 else
984 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
986 return S_OK;
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));
994 if (data)
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);
1002 int len;
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);
1012 break;
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;
1019 default:
1023 len = data ? lstrlenW(data) : 0;
1025 write_node_indent(This);
1026 if (!len)
1027 write_cdata_section(This->output, NULL, 0);
1028 else {
1029 static const WCHAR cdatacloseW[] = {']',']','>',0};
1030 while (len) {
1031 const WCHAR *str = wcsstr(data, cdatacloseW);
1032 if (str) {
1033 str += 2;
1034 write_cdata_section(This->output, data, str - data);
1035 len -= str - data;
1036 data = str;
1038 else {
1039 write_cdata_section(This->output, data, len);
1040 break;
1045 return S_OK;
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);
1052 WCHAR bufW[16];
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);
1064 break;
1065 case XmlWriterState_DocClosed:
1066 return WR_E_INVALIDACTION;
1067 default:
1071 swprintf(bufW, ARRAY_SIZE(bufW), fmtW, ch);
1072 write_output_buffer(This->output, bufW, -1);
1074 return S_OK;
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;
1091 default:
1095 return E_NOTIMPL;
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);
1115 break;
1116 case XmlWriterState_DocClosed:
1117 return WR_E_INVALIDACTION;
1118 default:
1122 write_node_indent(This);
1123 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
1124 if (comment) {
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
1128 comment string */
1129 if (len > 1) {
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, ' ');
1136 else
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));
1144 return S_OK;
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;
1155 HRESULT hr;
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;
1169 default:
1173 if (is_empty_string(name))
1174 return E_INVALIDARG;
1176 if (FAILED(hr = is_valid_name(name, &name_len)))
1177 return hr;
1179 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1180 return hr;
1182 write_output_buffer(This->output, doctypeW, ARRAY_SIZE(doctypeW));
1183 write_output_buffer(This->output, name, name_len);
1185 if (pubid)
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);
1192 else if (sysid)
1194 write_output_buffer(This->output, systemW, ARRAY_SIZE(systemW));
1195 write_output_buffer_quoted(This->output, sysid, -1);
1198 if (subset)
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;
1209 return S_OK;
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;
1217 struct ns *ns;
1218 HRESULT hr;
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);
1231 break;
1232 case XmlWriterState_DocClosed:
1233 return WR_E_INVALIDACTION;
1234 default:
1238 if (!local_name)
1239 return E_INVALIDARG;
1241 /* Validate prefix and local name */
1242 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1243 return hr;
1245 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1246 return hr;
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))
1254 if (!prefix)
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, '<');
1265 if (ns)
1266 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1267 else
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);
1277 if (value)
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, '>');
1285 else
1286 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
1288 This->state = XmlWriterState_Content;
1290 return S_OK;
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;
1309 default:
1313 /* empty element stack */
1314 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1317 This->state = XmlWriterState_DocClosed;
1318 return S_OK;
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;
1338 default:
1342 element = pop_element(This);
1343 if (!element)
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;
1354 else
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);
1364 return S_OK;
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;
1381 default:
1385 return E_NOTIMPL;
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);
1407 break;
1408 default:
1412 element = pop_element(This);
1413 if (!element)
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;
1422 This->textnode = 0;
1424 else
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);
1434 return S_OK;
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;
1453 default:
1457 return E_NOTIMPL;
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;
1476 default:
1480 return E_NOTIMPL;
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);
1490 return E_NOTIMPL;
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);
1500 return E_NOTIMPL;
1503 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1504 LPCWSTR text)
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;
1521 break;
1522 case XmlWriterState_ElemStarted:
1523 case XmlWriterState_DocClosed:
1524 return WR_E_INVALIDACTION;
1525 default:
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;
1540 return S_OK;
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;
1558 default:
1562 return E_NOTIMPL;
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));
1571 if (!data)
1572 return S_OK;
1574 switch (This->state)
1576 case XmlWriterState_Initial:
1577 return E_UNEXPECTED;
1578 case XmlWriterState_Ready:
1579 write_xmldecl(This, XmlStandalone_Omit);
1580 /* fallthrough */
1581 case XmlWriterState_DocStarted:
1582 case XmlWriterState_PIDocStarted:
1583 break;
1584 case XmlWriterState_InvalidEncoding:
1585 return MX_E_ENCODING;
1586 default:
1587 This->state = XmlWriterState_DocClosed;
1588 return WR_E_INVALIDACTION;
1591 write_output_buffer(This->output, data, -1);
1592 return S_OK;
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;
1609 default:
1613 return E_NOTIMPL;
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;
1628 return S_OK;
1629 case XmlWriterState_Ready:
1630 break;
1631 case XmlWriterState_InvalidEncoding:
1632 return MX_E_ENCODING;
1633 default:
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;
1646 struct ns *ns;
1647 HRESULT hr;
1649 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1651 if (!local_name)
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);
1664 break;
1665 default:
1669 /* Validate prefix and local name */
1670 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1671 return hr;
1673 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1674 return hr;
1676 if (uri && !wcscmp(uri, xmlnsuriW))
1678 if (!prefix)
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);
1688 if (!element)
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);
1699 if (!ns && uri)
1700 writer_push_ns(This, prefix, prefix_len, uri);
1702 write_output_buffer_char(This->output, '<');
1703 if (ns)
1704 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1705 else
1706 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1707 writer_inc_indent(This);
1709 return S_OK;
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',';'};
1718 while (*string)
1720 switch (*string)
1722 case '<':
1723 write_output_buffer(writer->output, ltW, ARRAY_SIZE(ltW));
1724 break;
1725 case '&':
1726 write_output_buffer(writer->output, ampW, ARRAY_SIZE(ampW));
1727 break;
1728 case '>':
1729 write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
1730 break;
1731 default:
1732 write_output_buffer(writer->output, string, 1);
1735 string++;
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));
1745 if (!string)
1746 return S_OK;
1748 switch (This->state)
1750 case XmlWriterState_Initial:
1751 return E_UNEXPECTED;
1752 case XmlWriterState_ElemStarted:
1753 writer_close_starttag(This);
1754 break;
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;
1761 default:
1765 This->textnode = 1;
1766 write_escaped_string(This, string);
1767 return S_OK;
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);
1776 return E_NOTIMPL;
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));
1785 return E_NOTIMPL;
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,
1800 xmlwriter_AddRef,
1801 xmlwriter_Release,
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,
1823 xmlwriter_WriteRaw,
1824 xmlwriter_WriteRawChars,
1825 xmlwriter_WriteStartDocument,
1826 xmlwriter_WriteStartElement,
1827 xmlwriter_WriteString,
1828 xmlwriter_WriteSurrogateCharEntity,
1829 xmlwriter_WriteWhitespace,
1830 xmlwriter_Flush
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))
1843 *ppvObject = iface;
1845 else
1847 WARN("interface %s not implemented\n", debugstr_guid(riid));
1848 *ppvObject = NULL;
1849 return E_NOINTERFACE;
1852 IUnknown_AddRef(iface);
1854 return S_OK;
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);
1862 return 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);
1872 if (ref == 0)
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);
1883 return ref;
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)
1895 xmlwriter *writer;
1896 HRESULT hr;
1898 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1900 if (imalloc)
1901 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1902 else
1903 writer = heap_alloc(sizeof(*writer));
1904 if (!writer)
1905 return E_OUTOFMEMORY;
1907 memset(writer, 0, sizeof(*writer));
1909 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1910 writer->ref = 1;
1911 writer->imalloc = imalloc;
1912 if (imalloc) IMalloc_AddRef(imalloc);
1913 writer->bom = TRUE;
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);
1923 return hr;
1926 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1927 const WCHAR *encoding_name, xmlwriteroutput **out)
1929 xmlwriteroutput *writeroutput;
1930 HRESULT hr;
1932 *out = NULL;
1934 if (imalloc)
1935 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1936 else
1937 writeroutput = heap_alloc(sizeof(*writeroutput));
1938 if (!writeroutput)
1939 return E_OUTOFMEMORY;
1941 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1942 writeroutput->ref = 1;
1943 writeroutput->imalloc = imalloc;
1944 if (imalloc)
1945 IMalloc_AddRef(imalloc);
1946 writeroutput->encoding = encoding;
1947 writeroutput->stream = NULL;
1948 hr = init_output_buffer(writeroutput);
1949 if (FAILED(hr)) {
1950 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1951 return hr;
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);
1959 else
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);
1969 return S_OK;
1972 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
1973 IXmlWriterOutput **out)
1975 xmlwriteroutput *output;
1976 xml_encoding xml_enc;
1977 HRESULT hr;
1979 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
1981 if (!stream || !out)
1982 return E_INVALIDARG;
1984 *out = NULL;
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;
1990 return hr;
1993 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
1994 IXmlWriterOutput **out)
1996 xmlwriteroutput *output;
1997 xml_encoding xml_enc;
1998 HRESULT hr;
2000 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
2002 if (!stream || !out)
2003 return E_INVALIDARG;
2005 *out = NULL;
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;
2011 return hr;