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