[XMLLITE] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / dll / win32 / xmllite / writer.c
1 /*
2 * IXmlWriter implementation
3 *
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014, 2016 Nikolay Sivov for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "xmllite_private.h"
23
24 #include <wine/list.h>
25 #include <wine/unicode.h>
26
27 /* not defined in public headers */
28 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
29
30 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
31
32 static const WCHAR closeelementW[] = {'<','/'};
33 static const WCHAR closetagW[] = {' ','/','>'};
34 static const WCHAR closepiW[] = {'?','>'};
35 static const WCHAR ltW[] = {'<'};
36 static const WCHAR gtW[] = {'>'};
37 static const WCHAR spaceW[] = {' '};
38 static const WCHAR quoteW[] = {'"'};
39
40 struct output_buffer
41 {
42 char *data;
43 unsigned int allocated;
44 unsigned int written;
45 UINT codepage;
46 };
47
48 typedef enum
49 {
50 XmlWriterState_Initial, /* output is not set yet */
51 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
52 XmlWriterState_InvalidEncoding, /* SetOutput() was called, but output had invalid encoding */
53 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
54 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
55 XmlWriterState_ElemStarted, /* writing element */
56 XmlWriterState_Content, /* content is accepted at this point */
57 XmlWriterState_DocClosed /* WriteEndDocument was called */
58 } XmlWriterState;
59
60 typedef struct
61 {
62 IXmlWriterOutput IXmlWriterOutput_iface;
63 LONG ref;
64 IUnknown *output;
65 ISequentialStream *stream;
66 IMalloc *imalloc;
67 xml_encoding encoding;
68 WCHAR *encoding_name; /* exactly as specified on output creation */
69 struct output_buffer buffer;
70 } xmlwriteroutput;
71
72 static const struct IUnknownVtbl xmlwriteroutputvtbl;
73
74 struct element
75 {
76 struct list entry;
77 WCHAR *qname;
78 unsigned int len; /* qname length in chars */
79 };
80
81 typedef struct _xmlwriter
82 {
83 IXmlWriter IXmlWriter_iface;
84 LONG ref;
85 IMalloc *imalloc;
86 xmlwriteroutput *output;
87 unsigned int indent_level;
88 BOOL indent;
89 BOOL bom;
90 BOOL omitxmldecl;
91 XmlConformanceLevel conformance;
92 XmlWriterState state;
93 BOOL bomwritten;
94 BOOL starttagopen;
95 struct list elements;
96 } xmlwriter;
97
98 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
99 {
100 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
101 }
102
103 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
104 {
105 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
106 }
107
108 static const char *debugstr_writer_prop(XmlWriterProperty prop)
109 {
110 static const char * const prop_names[] =
111 {
112 "MultiLanguage",
113 "Indent",
114 "ByteOrderMark",
115 "OmitXmlDeclaration",
116 "ConformanceLevel"
117 };
118
119 if (prop > _XmlWriterProperty_Last)
120 return wine_dbg_sprintf("unknown property=%d", prop);
121
122 return prop_names[prop];
123 }
124
125 /* writer output memory allocation functions */
126 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
127 {
128 return m_alloc(output->imalloc, len);
129 }
130
131 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
132 {
133 m_free(output->imalloc, mem);
134 }
135
136 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
137 {
138 return m_realloc(output->imalloc, mem, len);
139 }
140
141 /* writer memory allocation functions */
142 static inline void *writer_alloc(xmlwriter *writer, size_t len)
143 {
144 return m_alloc(writer->imalloc, len);
145 }
146
147 static inline void writer_free(xmlwriter *writer, void *mem)
148 {
149 m_free(writer->imalloc, mem);
150 }
151
152 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
153 {
154 struct element *ret;
155 int len;
156
157 ret = writer_alloc(writer, sizeof(*ret));
158 if (!ret) return ret;
159
160 len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
161 len += strlenW(local);
162
163 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
164 ret->len = len;
165 if (prefix) {
166 static const WCHAR colonW[] = {':',0};
167 strcpyW(ret->qname, prefix);
168 strcatW(ret->qname, colonW);
169 }
170 else
171 ret->qname[0] = 0;
172 strcatW(ret->qname, local);
173
174 return ret;
175 }
176
177 static void free_element(xmlwriter *writer, struct element *element)
178 {
179 writer_free(writer, element->qname);
180 writer_free(writer, element);
181 }
182
183 static void push_element(xmlwriter *writer, struct element *element)
184 {
185 list_add_head(&writer->elements, &element->entry);
186 }
187
188 static struct element *pop_element(xmlwriter *writer)
189 {
190 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
191
192 if (element)
193 list_remove(&element->entry);
194
195 return element;
196 }
197
198 static HRESULT init_output_buffer(xmlwriteroutput *output)
199 {
200 struct output_buffer *buffer = &output->buffer;
201 const int initial_len = 0x2000;
202 UINT cp = ~0u;
203 HRESULT hr;
204
205 if (FAILED(hr = get_code_page(output->encoding, &cp)))
206 WARN("Failed to get code page for specified encoding.\n");
207
208 buffer->data = writeroutput_alloc(output, initial_len);
209 if (!buffer->data) return E_OUTOFMEMORY;
210
211 memset(buffer->data, 0, 4);
212 buffer->allocated = initial_len;
213 buffer->written = 0;
214 buffer->codepage = cp;
215
216 return S_OK;
217 }
218
219 static void free_output_buffer(xmlwriteroutput *output)
220 {
221 struct output_buffer *buffer = &output->buffer;
222 writeroutput_free(output, buffer->data);
223 buffer->data = NULL;
224 buffer->allocated = 0;
225 buffer->written = 0;
226 }
227
228 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
229 {
230 struct output_buffer *buffer = &output->buffer;
231 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
232 if (buffer->allocated < buffer->written + length + 4) {
233 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
234 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
235 if (!ptr) return E_OUTOFMEMORY;
236 buffer->data = ptr;
237 buffer->allocated = grown_size;
238 }
239
240 return S_OK;
241 }
242
243 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
244 {
245 struct output_buffer *buffer = &output->buffer;
246 int length;
247 HRESULT hr;
248 char *ptr;
249
250 if (buffer->codepage == 1200) {
251 /* For UTF-16 encoding just copy. */
252 length = len == -1 ? strlenW(data) : len;
253 if (length) {
254 length *= sizeof(WCHAR);
255
256 hr = grow_output_buffer(output, length);
257 if (FAILED(hr)) return hr;
258 ptr = buffer->data + buffer->written;
259
260 memcpy(ptr, data, length);
261 buffer->written += length;
262 ptr += length;
263 /* null termination */
264 *(WCHAR*)ptr = 0;
265 }
266 }
267 else {
268 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
269 hr = grow_output_buffer(output, length);
270 if (FAILED(hr)) return hr;
271 ptr = buffer->data + buffer->written;
272 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
273 buffer->written += len == -1 ? length-1 : length;
274 }
275
276 return S_OK;
277 }
278
279 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
280 {
281 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
282 write_output_buffer(output, data, len);
283 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
284 return S_OK;
285 }
286
287 /* TODO: test if we need to validate char range */
288 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
289 {
290 if (prefix) {
291 static const WCHAR colW[] = {':'};
292 write_output_buffer(output, prefix, -1);
293 write_output_buffer(output, colW, ARRAY_SIZE(colW));
294 }
295
296 write_output_buffer(output, local_name, -1);
297
298 return S_OK;
299 }
300
301 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
302 {
303 if (writeroutput->stream) {
304 ISequentialStream_Release(writeroutput->stream);
305 writeroutput->stream = NULL;
306 }
307 }
308
309 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
310 {
311 HRESULT hr;
312
313 writeroutput_release_stream(writeroutput);
314 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
315 if (hr != S_OK)
316 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
317
318 return hr;
319 }
320
321 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
322 {
323 struct output_buffer *buffer;
324 ULONG written, offset = 0;
325 HRESULT hr;
326
327 if (!output || !output->stream)
328 return S_OK;
329
330 buffer = &output->buffer;
331
332 /* It will loop forever until everything is written or an error occurred. */
333 do {
334 written = 0;
335 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
336 if (FAILED(hr)) {
337 WARN("write to stream failed (0x%08x)\n", hr);
338 buffer->written = 0;
339 return hr;
340 }
341
342 offset += written;
343 buffer->written -= written;
344 } while (buffer->written > 0);
345
346 return S_OK;
347 }
348
349 static HRESULT write_encoding_bom(xmlwriter *writer)
350 {
351 if (!writer->bom || writer->bomwritten) return S_OK;
352
353 if (writer->output->encoding == XmlEncoding_UTF16) {
354 static const char utf16bom[] = {0xff, 0xfe};
355 struct output_buffer *buffer = &writer->output->buffer;
356 int len = sizeof(utf16bom);
357 HRESULT hr;
358
359 hr = grow_output_buffer(writer->output, len);
360 if (FAILED(hr)) return hr;
361 memcpy(buffer->data + buffer->written, utf16bom, len);
362 buffer->written += len;
363 }
364
365 writer->bomwritten = TRUE;
366 return S_OK;
367 }
368
369 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
370 {
371 if (output->encoding_name)
372 return output->encoding_name;
373
374 return get_encoding_name(output->encoding);
375 }
376
377 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
378 {
379 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
380 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
381
382 write_encoding_bom(writer);
383 writer->state = XmlWriterState_DocStarted;
384 if (writer->omitxmldecl) return S_OK;
385
386 /* version */
387 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
388
389 /* encoding */
390 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
391 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
392
393 /* standalone */
394 if (standalone == XmlStandalone_Omit)
395 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
396 else {
397 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
398 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
399 static const WCHAR noW[] = {'n','o','\"','?','>'};
400
401 write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
402 if (standalone == XmlStandalone_Yes)
403 write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
404 else
405 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
406 }
407
408 return S_OK;
409 }
410
411 static HRESULT writer_close_starttag(xmlwriter *writer)
412 {
413 HRESULT hr;
414
415 if (!writer->starttagopen) return S_OK;
416 hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
417 writer->starttagopen = FALSE;
418 return hr;
419 }
420
421 static void writer_inc_indent(xmlwriter *writer)
422 {
423 writer->indent_level++;
424 }
425
426 static void writer_dec_indent(xmlwriter *writer)
427 {
428 if (writer->indent_level)
429 writer->indent_level--;
430 }
431
432 static void write_node_indent(xmlwriter *writer)
433 {
434 static const WCHAR dblspaceW[] = {' ',' '};
435 static const WCHAR crlfW[] = {'\r','\n'};
436 unsigned int indent_level = writer->indent_level;
437
438 if (!writer->indent)
439 return;
440
441 /* Do state check to prevent newline inserted after BOM. It is assumed that
442 state does not change between writing BOM and inserting indentation. */
443 if (writer->output->buffer.written && writer->state != XmlWriterState_Ready)
444 write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW));
445 while (indent_level--)
446 write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW));
447 }
448
449 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
450 {
451 xmlwriter *This = impl_from_IXmlWriter(iface);
452
453 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
454
455 if (IsEqualGUID(riid, &IID_IXmlWriter) ||
456 IsEqualGUID(riid, &IID_IUnknown))
457 {
458 *ppvObject = iface;
459 }
460 else
461 {
462 FIXME("interface %s is not supported\n", debugstr_guid(riid));
463 *ppvObject = NULL;
464 return E_NOINTERFACE;
465 }
466
467 IXmlWriter_AddRef(iface);
468
469 return S_OK;
470 }
471
472 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
473 {
474 xmlwriter *This = impl_from_IXmlWriter(iface);
475 ULONG ref = InterlockedIncrement(&This->ref);
476 TRACE("(%p)->(%u)\n", This, ref);
477 return ref;
478 }
479
480 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
481 {
482 xmlwriter *This = impl_from_IXmlWriter(iface);
483 ULONG ref = InterlockedDecrement(&This->ref);
484
485 TRACE("(%p)->(%u)\n", This, ref);
486
487 if (ref == 0) {
488 struct element *element, *element2;
489 IMalloc *imalloc = This->imalloc;
490
491 writeroutput_flush_stream(This->output);
492 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
493
494 /* element stack */
495 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
496 list_remove(&element->entry);
497 free_element(This, element);
498 }
499
500 writer_free(This, This);
501 if (imalloc) IMalloc_Release(imalloc);
502 }
503
504 return ref;
505 }
506
507 /*** IXmlWriter methods ***/
508 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
509 {
510 xmlwriter *This = impl_from_IXmlWriter(iface);
511 IXmlWriterOutput *writeroutput;
512 HRESULT hr;
513
514 TRACE("(%p)->(%p)\n", This, output);
515
516 if (This->output) {
517 writeroutput_release_stream(This->output);
518 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
519 This->output = NULL;
520 This->bomwritten = FALSE;
521 This->indent_level = 0;
522 }
523
524 /* just reset current output */
525 if (!output) {
526 This->state = XmlWriterState_Initial;
527 return S_OK;
528 }
529
530 /* now try IXmlWriterOutput, ISequentialStream, IStream */
531 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
532 if (hr == S_OK) {
533 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
534 This->output = impl_from_IXmlWriterOutput(writeroutput);
535 else {
536 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
537 writeroutput, writeroutput->lpVtbl);
538 IUnknown_Release(writeroutput);
539 return E_FAIL;
540 }
541 }
542
543 if (hr != S_OK || !writeroutput) {
544 /* create IXmlWriterOutput basing on supplied interface */
545 hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
546 if (hr != S_OK) return hr;
547 This->output = impl_from_IXmlWriterOutput(writeroutput);
548 }
549
550 if (This->output->encoding == XmlEncoding_Unknown)
551 This->state = XmlWriterState_InvalidEncoding;
552 else
553 This->state = XmlWriterState_Ready;
554 return writeroutput_query_for_stream(This->output);
555 }
556
557 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
558 {
559 xmlwriter *This = impl_from_IXmlWriter(iface);
560
561 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
562
563 if (!value) return E_INVALIDARG;
564
565 switch (property)
566 {
567 case XmlWriterProperty_Indent:
568 *value = This->indent;
569 break;
570 case XmlWriterProperty_ByteOrderMark:
571 *value = This->bom;
572 break;
573 case XmlWriterProperty_OmitXmlDeclaration:
574 *value = This->omitxmldecl;
575 break;
576 case XmlWriterProperty_ConformanceLevel:
577 *value = This->conformance;
578 break;
579 default:
580 FIXME("Unimplemented property (%u)\n", property);
581 return E_NOTIMPL;
582 }
583
584 return S_OK;
585 }
586
587 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
588 {
589 xmlwriter *This = impl_from_IXmlWriter(iface);
590
591 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
592
593 switch (property)
594 {
595 case XmlWriterProperty_Indent:
596 This->indent = !!value;
597 break;
598 case XmlWriterProperty_ByteOrderMark:
599 This->bom = !!value;
600 break;
601 case XmlWriterProperty_OmitXmlDeclaration:
602 This->omitxmldecl = !!value;
603 break;
604 default:
605 FIXME("Unimplemented property (%u)\n", property);
606 return E_NOTIMPL;
607 }
608
609 return S_OK;
610 }
611
612 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
613 BOOL fWriteDefaultAttributes)
614 {
615 xmlwriter *This = impl_from_IXmlWriter(iface);
616
617 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
618
619 return E_NOTIMPL;
620 }
621
622 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
623 LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
624 {
625 static const WCHAR eqW[] = {'=','"'};
626 xmlwriter *This = impl_from_IXmlWriter(iface);
627
628 TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
629 debugstr_w(ns_uri), debugstr_w(value));
630
631 switch (This->state)
632 {
633 case XmlWriterState_Initial:
634 return E_UNEXPECTED;
635 case XmlWriterState_Ready:
636 case XmlWriterState_DocClosed:
637 This->state = XmlWriterState_DocClosed;
638 return WR_E_INVALIDACTION;
639 case XmlWriterState_InvalidEncoding:
640 return MX_E_ENCODING;
641 default:
642 ;
643 }
644
645 if (ns_prefix || ns_uri)
646 {
647 FIXME("namespaces are not supported.\n");
648 return E_NOTIMPL;
649 }
650
651 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
652 write_output_buffer(This->output, local_name, -1);
653 write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
654 write_output_buffer(This->output, value, -1);
655 write_output_buffer(This->output, quoteW, ARRAY_SIZE(quoteW));
656
657 return S_OK;
658 }
659
660 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
661 {
662 static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
663 static const WCHAR cdatacloseW[] = {']',']','>'};
664 write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
665 if (data)
666 write_output_buffer(output, data, len);
667 write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
668 }
669
670 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
671 {
672 xmlwriter *This = impl_from_IXmlWriter(iface);
673 int len;
674
675 TRACE("%p %s\n", This, debugstr_w(data));
676
677 switch (This->state)
678 {
679 case XmlWriterState_Initial:
680 return E_UNEXPECTED;
681 case XmlWriterState_ElemStarted:
682 writer_close_starttag(This);
683 break;
684 case XmlWriterState_Ready:
685 case XmlWriterState_DocClosed:
686 This->state = XmlWriterState_DocClosed;
687 return WR_E_INVALIDACTION;
688 case XmlWriterState_InvalidEncoding:
689 return MX_E_ENCODING;
690 default:
691 ;
692 }
693
694 len = data ? strlenW(data) : 0;
695
696 write_node_indent(This);
697 if (!len)
698 write_cdata_section(This->output, NULL, 0);
699 else {
700 static const WCHAR cdatacloseW[] = {']',']','>',0};
701 while (len) {
702 const WCHAR *str = strstrW(data, cdatacloseW);
703 if (str) {
704 str += 2;
705 write_cdata_section(This->output, data, str - data);
706 len -= str - data;
707 data = str;
708 }
709 else {
710 write_cdata_section(This->output, data, len);
711 break;
712 }
713 }
714 }
715
716 return S_OK;
717 }
718
719 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
720 {
721 static const WCHAR fmtW[] = {'&','#','x','%','x',';',0};
722 xmlwriter *This = impl_from_IXmlWriter(iface);
723 WCHAR bufW[16];
724
725 TRACE("%p %#x\n", This, ch);
726
727 switch (This->state)
728 {
729 case XmlWriterState_Initial:
730 return E_UNEXPECTED;
731 case XmlWriterState_InvalidEncoding:
732 return MX_E_ENCODING;
733 case XmlWriterState_ElemStarted:
734 writer_close_starttag(This);
735 break;
736 case XmlWriterState_DocClosed:
737 return WR_E_INVALIDACTION;
738 default:
739 ;
740 }
741
742 sprintfW(bufW, fmtW, ch);
743 write_output_buffer(This->output, bufW, -1);
744
745 return S_OK;
746 }
747
748 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
749 {
750 xmlwriter *This = impl_from_IXmlWriter(iface);
751
752 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
753
754 switch (This->state)
755 {
756 case XmlWriterState_Initial:
757 return E_UNEXPECTED;
758 case XmlWriterState_InvalidEncoding:
759 return MX_E_ENCODING;
760 case XmlWriterState_DocClosed:
761 return WR_E_INVALIDACTION;
762 default:
763 ;
764 }
765
766 return E_NOTIMPL;
767 }
768
769
770 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
771 {
772 static const WCHAR copenW[] = {'<','!','-','-'};
773 static const WCHAR ccloseW[] = {'-','-','>'};
774 xmlwriter *This = impl_from_IXmlWriter(iface);
775
776 TRACE("%p %s\n", This, debugstr_w(comment));
777
778 switch (This->state)
779 {
780 case XmlWriterState_Initial:
781 return E_UNEXPECTED;
782 case XmlWriterState_InvalidEncoding:
783 return MX_E_ENCODING;
784 case XmlWriterState_ElemStarted:
785 writer_close_starttag(This);
786 break;
787 case XmlWriterState_DocClosed:
788 return WR_E_INVALIDACTION;
789 default:
790 ;
791 }
792
793 write_node_indent(This);
794 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
795 if (comment) {
796 int len = strlenW(comment), i;
797
798 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
799 comment string */
800 if (len > 1) {
801 for (i = 0; i < len; i++) {
802 write_output_buffer(This->output, comment + i, 1);
803 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
804 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
805 }
806 }
807 else
808 write_output_buffer(This->output, comment, len);
809
810 if (len && comment[len-1] == '-')
811 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
812 }
813 write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
814
815 return S_OK;
816 }
817
818 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
819 LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
820 {
821 xmlwriter *This = impl_from_IXmlWriter(iface);
822
823 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
824 wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
825
826 return E_NOTIMPL;
827 }
828
829 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
830 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
831 {
832 xmlwriter *This = impl_from_IXmlWriter(iface);
833
834 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
835 wine_dbgstr_w(uri), wine_dbgstr_w(value));
836
837 switch (This->state)
838 {
839 case XmlWriterState_Initial:
840 return E_UNEXPECTED;
841 case XmlWriterState_InvalidEncoding:
842 return MX_E_ENCODING;
843 case XmlWriterState_ElemStarted:
844 writer_close_starttag(This);
845 break;
846 case XmlWriterState_DocClosed:
847 return WR_E_INVALIDACTION;
848 default:
849 ;
850 }
851
852 write_encoding_bom(This);
853 write_node_indent(This);
854 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
855 write_output_qname(This->output, prefix, local_name);
856
857 if (value)
858 {
859 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
860 write_output_buffer(This->output, value, -1);
861 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
862 write_output_qname(This->output, prefix, local_name);
863 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
864 }
865 else
866 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
867
868 This->state = XmlWriterState_Content;
869
870 return S_OK;
871 }
872
873 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
874 {
875 xmlwriter *This = impl_from_IXmlWriter(iface);
876
877 TRACE("%p\n", This);
878
879 switch (This->state)
880 {
881 case XmlWriterState_Initial:
882 return E_UNEXPECTED;
883 case XmlWriterState_Ready:
884 case XmlWriterState_DocClosed:
885 This->state = XmlWriterState_DocClosed;
886 return WR_E_INVALIDACTION;
887 case XmlWriterState_InvalidEncoding:
888 return MX_E_ENCODING;
889 default:
890 ;
891 }
892
893 /* empty element stack */
894 while (IXmlWriter_WriteEndElement(iface) == S_OK)
895 ;
896
897 This->state = XmlWriterState_DocClosed;
898 return S_OK;
899 }
900
901 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
902 {
903 xmlwriter *This = impl_from_IXmlWriter(iface);
904 struct element *element;
905
906 TRACE("%p\n", This);
907
908 switch (This->state)
909 {
910 case XmlWriterState_Initial:
911 return E_UNEXPECTED;
912 case XmlWriterState_Ready:
913 case XmlWriterState_DocClosed:
914 This->state = XmlWriterState_DocClosed;
915 return WR_E_INVALIDACTION;
916 case XmlWriterState_InvalidEncoding:
917 return MX_E_ENCODING;
918 default:
919 ;
920 }
921
922 element = pop_element(This);
923 if (!element)
924 return WR_E_INVALIDACTION;
925
926 writer_dec_indent(This);
927
928 if (This->starttagopen)
929 {
930 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
931 This->starttagopen = FALSE;
932 }
933 else {
934 /* write full end tag */
935 write_node_indent(This);
936 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
937 write_output_buffer(This->output, element->qname, element->len);
938 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
939 }
940
941 return S_OK;
942 }
943
944 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
945 {
946 xmlwriter *This = impl_from_IXmlWriter(iface);
947
948 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
949
950 switch (This->state)
951 {
952 case XmlWriterState_Initial:
953 return E_UNEXPECTED;
954 case XmlWriterState_InvalidEncoding:
955 return MX_E_ENCODING;
956 case XmlWriterState_DocClosed:
957 return WR_E_INVALIDACTION;
958 default:
959 ;
960 }
961
962 return E_NOTIMPL;
963 }
964
965 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
966 {
967 xmlwriter *This = impl_from_IXmlWriter(iface);
968 struct element *element;
969
970 TRACE("%p\n", This);
971
972 switch (This->state)
973 {
974 case XmlWriterState_Initial:
975 return E_UNEXPECTED;
976 case XmlWriterState_Ready:
977 case XmlWriterState_DocClosed:
978 This->state = XmlWriterState_DocClosed;
979 return WR_E_INVALIDACTION;
980 case XmlWriterState_InvalidEncoding:
981 return MX_E_ENCODING;
982 default:
983 ;
984 }
985
986 element = pop_element(This);
987 if (!element)
988 return WR_E_INVALIDACTION;
989
990 writer_close_starttag(This);
991 writer_dec_indent(This);
992
993 /* don't force full end tag to the next line */
994 if (This->state == XmlWriterState_ElemStarted)
995 This->state = XmlWriterState_Content;
996 else
997 write_node_indent(This);
998
999 /* write full end tag */
1000 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
1001 write_output_buffer(This->output, element->qname, element->len);
1002 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1003
1004 return S_OK;
1005 }
1006
1007 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1008 {
1009 xmlwriter *This = impl_from_IXmlWriter(iface);
1010
1011 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1012
1013 switch (This->state)
1014 {
1015 case XmlWriterState_Initial:
1016 return E_UNEXPECTED;
1017 case XmlWriterState_Ready:
1018 case XmlWriterState_DocClosed:
1019 This->state = XmlWriterState_DocClosed;
1020 return WR_E_INVALIDACTION;
1021 case XmlWriterState_InvalidEncoding:
1022 return MX_E_ENCODING;
1023 default:
1024 ;
1025 }
1026
1027 return E_NOTIMPL;
1028 }
1029
1030 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1031 {
1032 xmlwriter *This = impl_from_IXmlWriter(iface);
1033
1034 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1035
1036 switch (This->state)
1037 {
1038 case XmlWriterState_Initial:
1039 return E_UNEXPECTED;
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;
1046 default:
1047 ;
1048 }
1049
1050 return E_NOTIMPL;
1051 }
1052
1053 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1054 BOOL fWriteDefaultAttributes)
1055 {
1056 xmlwriter *This = impl_from_IXmlWriter(iface);
1057
1058 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1059
1060 return E_NOTIMPL;
1061 }
1062
1063 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1064 BOOL fWriteDefaultAttributes)
1065 {
1066 xmlwriter *This = impl_from_IXmlWriter(iface);
1067
1068 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1069
1070 return E_NOTIMPL;
1071 }
1072
1073 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1074 LPCWSTR text)
1075 {
1076 xmlwriter *This = impl_from_IXmlWriter(iface);
1077 static const WCHAR xmlW[] = {'x','m','l',0};
1078 static const WCHAR openpiW[] = {'<','?'};
1079
1080 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1081
1082 switch (This->state)
1083 {
1084 case XmlWriterState_Initial:
1085 return E_UNEXPECTED;
1086 case XmlWriterState_InvalidEncoding:
1087 return MX_E_ENCODING;
1088 case XmlWriterState_DocStarted:
1089 if (!strcmpW(name, xmlW))
1090 return WR_E_INVALIDACTION;
1091 break;
1092 case XmlWriterState_ElemStarted:
1093 case XmlWriterState_DocClosed:
1094 return WR_E_INVALIDACTION;
1095 default:
1096 ;
1097 }
1098
1099 write_encoding_bom(This);
1100 write_node_indent(This);
1101 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
1102 write_output_buffer(This->output, name, -1);
1103 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
1104 write_output_buffer(This->output, text, -1);
1105 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1106
1107 if (!strcmpW(name, xmlW))
1108 This->state = XmlWriterState_PIDocStarted;
1109
1110 return S_OK;
1111 }
1112
1113 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1114 LPCWSTR pwszNamespaceUri)
1115 {
1116 xmlwriter *This = impl_from_IXmlWriter(iface);
1117
1118 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1119
1120 switch (This->state)
1121 {
1122 case XmlWriterState_Initial:
1123 return E_UNEXPECTED;
1124 case XmlWriterState_InvalidEncoding:
1125 return MX_E_ENCODING;
1126 case XmlWriterState_DocClosed:
1127 return WR_E_INVALIDACTION;
1128 default:
1129 ;
1130 }
1131
1132 return E_NOTIMPL;
1133 }
1134
1135 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1136 {
1137 xmlwriter *This = impl_from_IXmlWriter(iface);
1138
1139 TRACE("%p %s\n", This, debugstr_w(data));
1140
1141 if (!data)
1142 return S_OK;
1143
1144 switch (This->state)
1145 {
1146 case XmlWriterState_Initial:
1147 return E_UNEXPECTED;
1148 case XmlWriterState_Ready:
1149 write_xmldecl(This, XmlStandalone_Omit);
1150 /* fallthrough */
1151 case XmlWriterState_DocStarted:
1152 case XmlWriterState_PIDocStarted:
1153 break;
1154 case XmlWriterState_InvalidEncoding:
1155 return MX_E_ENCODING;
1156 default:
1157 This->state = XmlWriterState_DocClosed;
1158 return WR_E_INVALIDACTION;
1159 }
1160
1161 write_output_buffer(This->output, data, -1);
1162 return S_OK;
1163 }
1164
1165 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1166 {
1167 xmlwriter *This = impl_from_IXmlWriter(iface);
1168
1169 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1170
1171 switch (This->state)
1172 {
1173 case XmlWriterState_Initial:
1174 return E_UNEXPECTED;
1175 case XmlWriterState_InvalidEncoding:
1176 return MX_E_ENCODING;
1177 case XmlWriterState_DocClosed:
1178 return WR_E_INVALIDACTION;
1179 default:
1180 ;
1181 }
1182
1183 return E_NOTIMPL;
1184 }
1185
1186 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1187 {
1188 xmlwriter *This = impl_from_IXmlWriter(iface);
1189
1190 TRACE("(%p)->(%d)\n", This, standalone);
1191
1192 switch (This->state)
1193 {
1194 case XmlWriterState_Initial:
1195 return E_UNEXPECTED;
1196 case XmlWriterState_PIDocStarted:
1197 This->state = XmlWriterState_DocStarted;
1198 return S_OK;
1199 case XmlWriterState_Ready:
1200 break;
1201 case XmlWriterState_InvalidEncoding:
1202 return MX_E_ENCODING;
1203 default:
1204 This->state = XmlWriterState_DocClosed;
1205 return WR_E_INVALIDACTION;
1206 }
1207
1208 return write_xmldecl(This, standalone);
1209 }
1210
1211 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1212 {
1213 xmlwriter *This = impl_from_IXmlWriter(iface);
1214 struct element *element;
1215
1216 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1217
1218 if (!local_name)
1219 return E_INVALIDARG;
1220
1221 switch (This->state)
1222 {
1223 case XmlWriterState_Initial:
1224 return E_UNEXPECTED;
1225 case XmlWriterState_InvalidEncoding:
1226 return MX_E_ENCODING;
1227 case XmlWriterState_DocClosed:
1228 return WR_E_INVALIDACTION;
1229 default:
1230 ;
1231 }
1232
1233 /* close pending element */
1234 if (This->starttagopen)
1235 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1236
1237 element = alloc_element(This, prefix, local_name);
1238 if (!element)
1239 return E_OUTOFMEMORY;
1240
1241 write_encoding_bom(This);
1242 write_node_indent(This);
1243
1244 This->state = XmlWriterState_ElemStarted;
1245 This->starttagopen = TRUE;
1246
1247 push_element(This, element);
1248
1249 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
1250 write_output_qname(This->output, prefix, local_name);
1251 writer_inc_indent(This);
1252
1253 return S_OK;
1254 }
1255
1256 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1257 {
1258 static const WCHAR ampW[] = {'&','a','m','p',';'};
1259 static const WCHAR ltW[] = {'&','l','t',';'};
1260 static const WCHAR gtW[] = {'&','g','t',';'};
1261
1262 while (*string)
1263 {
1264 switch (*string)
1265 {
1266 case '<':
1267 write_output_buffer(writer->output, ltW, ARRAY_SIZE(ltW));
1268 break;
1269 case '&':
1270 write_output_buffer(writer->output, ampW, ARRAY_SIZE(ampW));
1271 break;
1272 case '>':
1273 write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
1274 break;
1275 default:
1276 write_output_buffer(writer->output, string, 1);
1277 }
1278
1279 string++;
1280 }
1281 }
1282
1283 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1284 {
1285 xmlwriter *This = impl_from_IXmlWriter(iface);
1286
1287 TRACE("%p %s\n", This, debugstr_w(string));
1288
1289 if (!string)
1290 return S_OK;
1291
1292 switch (This->state)
1293 {
1294 case XmlWriterState_Initial:
1295 return E_UNEXPECTED;
1296 case XmlWriterState_ElemStarted:
1297 writer_close_starttag(This);
1298 break;
1299 case XmlWriterState_Ready:
1300 case XmlWriterState_DocClosed:
1301 This->state = XmlWriterState_DocClosed;
1302 return WR_E_INVALIDACTION;
1303 case XmlWriterState_InvalidEncoding:
1304 return MX_E_ENCODING;
1305 default:
1306 ;
1307 }
1308
1309 write_escaped_string(This, string);
1310 return S_OK;
1311 }
1312
1313 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1314 {
1315 xmlwriter *This = impl_from_IXmlWriter(iface);
1316
1317 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1318
1319 return E_NOTIMPL;
1320 }
1321
1322 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1323 {
1324 xmlwriter *This = impl_from_IXmlWriter(iface);
1325
1326 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1327
1328 return E_NOTIMPL;
1329 }
1330
1331 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1332 {
1333 xmlwriter *This = impl_from_IXmlWriter(iface);
1334
1335 TRACE("%p\n", This);
1336
1337 return writeroutput_flush_stream(This->output);
1338 }
1339
1340 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1341 {
1342 xmlwriter_QueryInterface,
1343 xmlwriter_AddRef,
1344 xmlwriter_Release,
1345 xmlwriter_SetOutput,
1346 xmlwriter_GetProperty,
1347 xmlwriter_SetProperty,
1348 xmlwriter_WriteAttributes,
1349 xmlwriter_WriteAttributeString,
1350 xmlwriter_WriteCData,
1351 xmlwriter_WriteCharEntity,
1352 xmlwriter_WriteChars,
1353 xmlwriter_WriteComment,
1354 xmlwriter_WriteDocType,
1355 xmlwriter_WriteElementString,
1356 xmlwriter_WriteEndDocument,
1357 xmlwriter_WriteEndElement,
1358 xmlwriter_WriteEntityRef,
1359 xmlwriter_WriteFullEndElement,
1360 xmlwriter_WriteName,
1361 xmlwriter_WriteNmToken,
1362 xmlwriter_WriteNode,
1363 xmlwriter_WriteNodeShallow,
1364 xmlwriter_WriteProcessingInstruction,
1365 xmlwriter_WriteQualifiedName,
1366 xmlwriter_WriteRaw,
1367 xmlwriter_WriteRawChars,
1368 xmlwriter_WriteStartDocument,
1369 xmlwriter_WriteStartElement,
1370 xmlwriter_WriteString,
1371 xmlwriter_WriteSurrogateCharEntity,
1372 xmlwriter_WriteWhitespace,
1373 xmlwriter_Flush
1374 };
1375
1376 /** IXmlWriterOutput **/
1377 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1378 {
1379 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1380
1381 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1382
1383 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1384 IsEqualGUID(riid, &IID_IUnknown))
1385 {
1386 *ppvObject = iface;
1387 }
1388 else
1389 {
1390 WARN("interface %s not implemented\n", debugstr_guid(riid));
1391 *ppvObject = NULL;
1392 return E_NOINTERFACE;
1393 }
1394
1395 IUnknown_AddRef(iface);
1396
1397 return S_OK;
1398 }
1399
1400 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1401 {
1402 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1403 ULONG ref = InterlockedIncrement(&This->ref);
1404 TRACE("(%p)->(%d)\n", This, ref);
1405 return ref;
1406 }
1407
1408 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1409 {
1410 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1411 LONG ref = InterlockedDecrement(&This->ref);
1412
1413 TRACE("(%p)->(%d)\n", This, ref);
1414
1415 if (ref == 0)
1416 {
1417 IMalloc *imalloc = This->imalloc;
1418 if (This->output) IUnknown_Release(This->output);
1419 if (This->stream) ISequentialStream_Release(This->stream);
1420 free_output_buffer(This);
1421 writeroutput_free(This, This->encoding_name);
1422 writeroutput_free(This, This);
1423 if (imalloc) IMalloc_Release(imalloc);
1424 }
1425
1426 return ref;
1427 }
1428
1429 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1430 {
1431 xmlwriteroutput_QueryInterface,
1432 xmlwriteroutput_AddRef,
1433 xmlwriteroutput_Release
1434 };
1435
1436 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1437 {
1438 xmlwriter *writer;
1439 HRESULT hr;
1440
1441 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1442
1443 if (imalloc)
1444 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1445 else
1446 writer = heap_alloc(sizeof(*writer));
1447 if(!writer) return E_OUTOFMEMORY;
1448
1449 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1450 writer->ref = 1;
1451 writer->imalloc = imalloc;
1452 if (imalloc) IMalloc_AddRef(imalloc);
1453 writer->output = NULL;
1454 writer->indent_level = 0;
1455 writer->indent = FALSE;
1456 writer->bom = TRUE;
1457 writer->omitxmldecl = FALSE;
1458 writer->conformance = XmlConformanceLevel_Document;
1459 writer->state = XmlWriterState_Initial;
1460 writer->bomwritten = FALSE;
1461 writer->starttagopen = FALSE;
1462 list_init(&writer->elements);
1463
1464 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
1465 IXmlWriter_Release(&writer->IXmlWriter_iface);
1466
1467 TRACE("returning iface %p, hr %#x\n", *obj, hr);
1468
1469 return hr;
1470 }
1471
1472 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1473 const WCHAR *encoding_name, IXmlWriterOutput **output)
1474 {
1475 xmlwriteroutput *writeroutput;
1476 HRESULT hr;
1477
1478 *output = NULL;
1479
1480 if (imalloc)
1481 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1482 else
1483 writeroutput = heap_alloc(sizeof(*writeroutput));
1484 if (!writeroutput)
1485 return E_OUTOFMEMORY;
1486
1487 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1488 writeroutput->ref = 1;
1489 writeroutput->imalloc = imalloc;
1490 if (imalloc)
1491 IMalloc_AddRef(imalloc);
1492 writeroutput->encoding = encoding;
1493 writeroutput->stream = NULL;
1494 hr = init_output_buffer(writeroutput);
1495 if (FAILED(hr)) {
1496 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1497 return hr;
1498 }
1499
1500 if (encoding_name) {
1501 unsigned int size = (strlenW(encoding_name) + 1) * sizeof(WCHAR);
1502 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
1503 memcpy(writeroutput->encoding_name, encoding_name, size);
1504 }
1505 else
1506 writeroutput->encoding_name = NULL;
1507
1508 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1509
1510 *output = &writeroutput->IXmlWriterOutput_iface;
1511
1512 TRACE("returning iface %p\n", *output);
1513
1514 return S_OK;
1515 }
1516
1517 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1518 IMalloc *imalloc,
1519 LPCWSTR encoding,
1520 IXmlWriterOutput **output)
1521 {
1522 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1523 xml_encoding xml_enc;
1524
1525 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1526
1527 if (!stream || !output) return E_INVALIDARG;
1528
1529 xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1);
1530 return create_writer_output(stream, imalloc, xml_enc, encoding, output);
1531 }
1532
1533 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream,
1534 IMalloc *imalloc,
1535 UINT codepage,
1536 IXmlWriterOutput **output)
1537 {
1538 xml_encoding xml_enc;
1539
1540 TRACE("%p %p %u %p\n", stream, imalloc, codepage, output);
1541
1542 if (!stream || !output) return E_INVALIDARG;
1543
1544 xml_enc = get_encoding_from_codepage(codepage);
1545 return create_writer_output(stream, imalloc, xml_enc, NULL, output);
1546 }