* Sync up to trunk head (r64716).
[reactos.git] / dll / win32 / xmllite / writer.c
1 /*
2 * IXmlWriter implementation
3 *
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014 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 closepiW[] = {'?','>'};
34 static const WCHAR ltW[] = {'<'};
35 static const WCHAR gtW[] = {'>'};
36
37 struct output_buffer
38 {
39 char *data;
40 unsigned int allocated;
41 unsigned int written;
42 UINT codepage;
43 };
44
45 typedef enum
46 {
47 XmlWriterState_Initial, /* output is not set yet */
48 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
49 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
50 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
51 XmlWriterState_ElemStarted, /* writing element */
52 XmlWriterState_Content /* content is accepted at this point */
53 } XmlWriterState;
54
55 typedef struct
56 {
57 IXmlWriterOutput IXmlWriterOutput_iface;
58 LONG ref;
59 IUnknown *output;
60 ISequentialStream *stream;
61 IMalloc *imalloc;
62 xml_encoding encoding;
63 struct output_buffer buffer;
64 } xmlwriteroutput;
65
66 static const struct IUnknownVtbl xmlwriteroutputvtbl;
67
68 struct element
69 {
70 struct list entry;
71 WCHAR *qname;
72 unsigned int len; /* qname length in chars */
73 };
74
75 typedef struct _xmlwriter
76 {
77 IXmlWriter IXmlWriter_iface;
78 LONG ref;
79 IMalloc *imalloc;
80 xmlwriteroutput *output;
81 BOOL indent;
82 BOOL bom;
83 BOOL omitxmldecl;
84 XmlConformanceLevel conformance;
85 XmlWriterState state;
86 BOOL bomwritten;
87 BOOL starttagopen;
88 struct list elements;
89 } xmlwriter;
90
91 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
92 {
93 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
94 }
95
96 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
97 {
98 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
99 }
100
101 static const char *debugstr_writer_prop(XmlWriterProperty prop)
102 {
103 static const char * const prop_names[] =
104 {
105 "MultiLanguage",
106 "Indent",
107 "ByteOrderMark",
108 "OmitXmlDeclaration",
109 "ConformanceLevel"
110 };
111
112 if (prop > _XmlWriterProperty_Last)
113 return wine_dbg_sprintf("unknown property=%d", prop);
114
115 return prop_names[prop];
116 }
117
118 /* writer output memory allocation functions */
119 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
120 {
121 return m_alloc(output->imalloc, len);
122 }
123
124 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
125 {
126 m_free(output->imalloc, mem);
127 }
128
129 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
130 {
131 return m_realloc(output->imalloc, mem, len);
132 }
133
134 /* writer memory allocation functions */
135 static inline void *writer_alloc(xmlwriter *writer, size_t len)
136 {
137 return m_alloc(writer->imalloc, len);
138 }
139
140 static inline void writer_free(xmlwriter *writer, void *mem)
141 {
142 m_free(writer->imalloc, mem);
143 }
144
145 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
146 {
147 struct element *ret;
148 int len;
149
150 ret = writer_alloc(writer, sizeof(*ret));
151 if (!ret) return ret;
152
153 len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
154 len += strlenW(local);
155
156 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
157 ret->len = len;
158 if (prefix) {
159 static const WCHAR colonW[] = {':',0};
160 strcpyW(ret->qname, prefix);
161 strcatW(ret->qname, colonW);
162 }
163 else
164 ret->qname[0] = 0;
165 strcatW(ret->qname, local);
166
167 return ret;
168 }
169
170 static void free_element(xmlwriter *writer, struct element *element)
171 {
172 writer_free(writer, element->qname);
173 writer_free(writer, element);
174 }
175
176 static void push_element(xmlwriter *writer, struct element *element)
177 {
178 list_add_head(&writer->elements, &element->entry);
179 }
180
181 static struct element *pop_element(xmlwriter *writer)
182 {
183 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
184
185 if (element)
186 list_remove(&element->entry);
187
188 return element;
189 }
190
191 static HRESULT init_output_buffer(xmlwriteroutput *output)
192 {
193 struct output_buffer *buffer = &output->buffer;
194 const int initial_len = 0x2000;
195 HRESULT hr;
196 UINT cp;
197
198 hr = get_code_page(output->encoding, &cp);
199 if (FAILED(hr)) return hr;
200
201 buffer->data = writeroutput_alloc(output, initial_len);
202 if (!buffer->data) return E_OUTOFMEMORY;
203
204 memset(buffer->data, 0, 4);
205 buffer->allocated = initial_len;
206 buffer->written = 0;
207 buffer->codepage = cp;
208
209 return S_OK;
210 }
211
212 static void free_output_buffer(xmlwriteroutput *output)
213 {
214 struct output_buffer *buffer = &output->buffer;
215 writeroutput_free(output, buffer->data);
216 buffer->data = NULL;
217 buffer->allocated = 0;
218 buffer->written = 0;
219 }
220
221 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
222 {
223 struct output_buffer *buffer = &output->buffer;
224 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
225 if (buffer->allocated < buffer->written + length + 4) {
226 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
227 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
228 if (!ptr) return E_OUTOFMEMORY;
229 buffer->data = ptr;
230 buffer->allocated = grown_size;
231 }
232
233 return S_OK;
234 }
235
236 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
237 {
238 struct output_buffer *buffer = &output->buffer;
239 int length;
240 HRESULT hr;
241 char *ptr;
242
243 if (buffer->codepage != ~0) {
244 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
245 hr = grow_output_buffer(output, length);
246 if (FAILED(hr)) return hr;
247 ptr = buffer->data + buffer->written;
248 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
249 buffer->written += len == -1 ? length-1 : length;
250 }
251 else {
252 /* WCHAR data just copied */
253 length = len == -1 ? strlenW(data) : len;
254 if (length) {
255 length *= sizeof(WCHAR);
256
257 hr = grow_output_buffer(output, length);
258 if (FAILED(hr)) return hr;
259 ptr = buffer->data + buffer->written;
260
261 memcpy(ptr, data, length);
262 buffer->written += length;
263 ptr += length;
264 /* null termination */
265 *(WCHAR*)ptr = 0;
266 }
267 }
268
269 return S_OK;
270 }
271
272 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
273 {
274 static const WCHAR quoteW[] = {'"'};
275 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
276 write_output_buffer(output, data, len);
277 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
278 return S_OK;
279 }
280
281 /* TODO: test if we need to validate char range */
282 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
283 {
284 if (prefix) {
285 static const WCHAR colW[] = {':'};
286 write_output_buffer(output, prefix, -1);
287 write_output_buffer(output, colW, ARRAY_SIZE(colW));
288 }
289
290 write_output_buffer(output, local_name, -1);
291
292 return S_OK;
293 }
294
295 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
296 {
297 if (writeroutput->stream) {
298 ISequentialStream_Release(writeroutput->stream);
299 writeroutput->stream = NULL;
300 }
301 }
302
303 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
304 {
305 HRESULT hr;
306
307 writeroutput_release_stream(writeroutput);
308 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
309 if (hr != S_OK)
310 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
311
312 return hr;
313 }
314
315 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
316 {
317 struct output_buffer *buffer;
318 ULONG written, offset = 0;
319 HRESULT hr;
320
321 if (!output || !output->stream)
322 return S_OK;
323
324 buffer = &output->buffer;
325
326 /* It will loop forever until everything is written or an error occured. */
327 do {
328 written = 0;
329 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
330 if (FAILED(hr)) {
331 WARN("write to stream failed (0x%08x)\n", hr);
332 buffer->written = 0;
333 return hr;
334 }
335
336 offset += written;
337 buffer->written -= written;
338 } while (buffer->written > 0);
339
340 return S_OK;
341 }
342
343 static HRESULT write_encoding_bom(xmlwriter *writer)
344 {
345 if (!writer->bom || writer->bomwritten) return S_OK;
346
347 if (writer->output->encoding == XmlEncoding_UTF16) {
348 static const char utf16bom[] = {0xff, 0xfe};
349 struct output_buffer *buffer = &writer->output->buffer;
350 int len = sizeof(utf16bom);
351 HRESULT hr;
352
353 hr = grow_output_buffer(writer->output, len);
354 if (FAILED(hr)) return hr;
355 memcpy(buffer->data + buffer->written, utf16bom, len);
356 buffer->written += len;
357 }
358
359 writer->bomwritten = TRUE;
360 return S_OK;
361 }
362
363 static HRESULT writer_close_starttag(xmlwriter *writer)
364 {
365 HRESULT hr;
366
367 if (!writer->starttagopen) return S_OK;
368 hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
369 writer->starttagopen = FALSE;
370 writer->state = XmlWriterState_Content;
371 return hr;
372 }
373
374 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
375 {
376 xmlwriter *This = impl_from_IXmlWriter(iface);
377
378 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
379
380 if (IsEqualGUID(riid, &IID_IUnknown) ||
381 IsEqualGUID(riid, &IID_IXmlWriter))
382 {
383 *ppvObject = iface;
384 }
385
386 IXmlWriter_AddRef(iface);
387
388 return S_OK;
389 }
390
391 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
392 {
393 xmlwriter *This = impl_from_IXmlWriter(iface);
394 TRACE("%p\n", This);
395 return InterlockedIncrement(&This->ref);
396 }
397
398 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
399 {
400 xmlwriter *This = impl_from_IXmlWriter(iface);
401 LONG ref;
402
403 TRACE("%p\n", This);
404
405 ref = InterlockedDecrement(&This->ref);
406 if (ref == 0) {
407 struct element *element, *element2;
408 IMalloc *imalloc = This->imalloc;
409
410 IXmlWriter_Flush(iface);
411 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
412
413 /* element stack */
414 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
415 list_remove(&element->entry);
416 free_element(This, element);
417 }
418
419 writer_free(This, This);
420 if (imalloc) IMalloc_Release(imalloc);
421 }
422
423 return ref;
424 }
425
426 /*** IXmlWriter methods ***/
427 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
428 {
429 xmlwriter *This = impl_from_IXmlWriter(iface);
430 IXmlWriterOutput *writeroutput;
431 HRESULT hr;
432
433 TRACE("(%p)->(%p)\n", This, output);
434
435 if (This->output) {
436 writeroutput_release_stream(This->output);
437 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
438 This->output = NULL;
439 This->bomwritten = FALSE;
440 }
441
442 /* just reset current output */
443 if (!output) {
444 This->state = XmlWriterState_Initial;
445 return S_OK;
446 }
447
448 /* now try IXmlWriterOutput, ISequentialStream, IStream */
449 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
450 if (hr == S_OK) {
451 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
452 This->output = impl_from_IXmlWriterOutput(writeroutput);
453 else {
454 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
455 writeroutput, writeroutput->lpVtbl);
456 IUnknown_Release(writeroutput);
457 return E_FAIL;
458
459 }
460 }
461
462 if (hr != S_OK || !writeroutput) {
463 /* create IXmlWriterOutput basing on supplied interface */
464 hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
465 if (hr != S_OK) return hr;
466 This->output = impl_from_IXmlWriterOutput(writeroutput);
467 }
468
469 This->state = XmlWriterState_Ready;
470 return writeroutput_query_for_stream(This->output);
471 }
472
473 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
474 {
475 xmlwriter *This = impl_from_IXmlWriter(iface);
476
477 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
478
479 if (!value) return E_INVALIDARG;
480
481 switch (property)
482 {
483 case XmlWriterProperty_Indent:
484 *value = This->indent;
485 break;
486 case XmlWriterProperty_ByteOrderMark:
487 *value = This->bom;
488 break;
489 case XmlWriterProperty_OmitXmlDeclaration:
490 *value = This->omitxmldecl;
491 break;
492 case XmlWriterProperty_ConformanceLevel:
493 *value = This->conformance;
494 break;
495 default:
496 FIXME("Unimplemented property (%u)\n", property);
497 return E_NOTIMPL;
498 }
499
500 return S_OK;
501 }
502
503 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
504 {
505 xmlwriter *This = impl_from_IXmlWriter(iface);
506
507 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
508
509 switch (property)
510 {
511 case XmlWriterProperty_ByteOrderMark:
512 This->bom = !!value;
513 break;
514 case XmlWriterProperty_OmitXmlDeclaration:
515 This->omitxmldecl = !!value;
516 break;
517 default:
518 FIXME("Unimplemented property (%u)\n", property);
519 return E_NOTIMPL;
520 }
521
522 return S_OK;
523 }
524
525 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
526 BOOL fWriteDefaultAttributes)
527 {
528 xmlwriter *This = impl_from_IXmlWriter(iface);
529
530 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
531
532 return E_NOTIMPL;
533 }
534
535 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR pwszPrefix,
536 LPCWSTR pwszLocalName, LPCWSTR pwszNamespaceUri,
537 LPCWSTR pwszValue)
538 {
539 xmlwriter *This = impl_from_IXmlWriter(iface);
540
541 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszPrefix), wine_dbgstr_w(pwszLocalName),
542 wine_dbgstr_w(pwszNamespaceUri), wine_dbgstr_w(pwszValue));
543
544 return E_NOTIMPL;
545 }
546
547 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR pwszText)
548 {
549 xmlwriter *This = impl_from_IXmlWriter(iface);
550
551 FIXME("%p %s\n", This, wine_dbgstr_w(pwszText));
552
553 return E_NOTIMPL;
554 }
555
556 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR wch)
557 {
558 return E_NOTIMPL;
559 }
560
561 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
562 {
563 xmlwriter *This = impl_from_IXmlWriter(iface);
564
565 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
566
567 return E_NOTIMPL;
568 }
569
570 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR pwszComment)
571 {
572 return E_NOTIMPL;
573 }
574
575 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
576 LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
577 {
578 xmlwriter *This = impl_from_IXmlWriter(iface);
579
580 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
581 wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
582
583 return E_NOTIMPL;
584 }
585
586 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
587 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
588 {
589 xmlwriter *This = impl_from_IXmlWriter(iface);
590
591 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
592 wine_dbgstr_w(uri), wine_dbgstr_w(value));
593
594 switch (This->state)
595 {
596 case XmlWriterState_Initial:
597 return E_UNEXPECTED;
598 case XmlWriterState_ElemStarted:
599 writer_close_starttag(This);
600 break;
601 default:
602 ;
603 }
604
605 write_encoding_bom(This);
606 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
607 write_output_qname(This->output, prefix, local_name);
608 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
609
610 if (value)
611 write_output_buffer(This->output, value, -1);
612
613 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
614 write_output_qname(This->output, prefix, local_name);
615 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
616 This->state = XmlWriterState_Content;
617
618 return S_OK;
619 }
620
621 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
622 {
623 xmlwriter *This = impl_from_IXmlWriter(iface);
624
625 FIXME("%p\n", This);
626
627 return E_NOTIMPL;
628 }
629
630 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
631 {
632 xmlwriter *This = impl_from_IXmlWriter(iface);
633 struct element *element;
634
635 TRACE("%p\n", This);
636
637 element = pop_element(This);
638 if (!element)
639 return WR_E_INVALIDACTION;
640
641 if (This->starttagopen) {
642 static WCHAR closetagW[] = {' ','/','>'};
643 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
644 This->starttagopen = FALSE;
645 }
646 else {
647 /* write full end tag */
648 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
649 write_output_buffer(This->output, element->qname, element->len);
650 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
651 }
652
653 return S_OK;
654 }
655
656 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
657 {
658 xmlwriter *This = impl_from_IXmlWriter(iface);
659
660 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
661
662 return E_NOTIMPL;
663 }
664
665 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
666 {
667 xmlwriter *This = impl_from_IXmlWriter(iface);
668 struct element *element;
669
670 TRACE("%p\n", This);
671
672 element = pop_element(This);
673 if (!element)
674 return WR_E_INVALIDACTION;
675
676 /* write full end tag */
677 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
678 write_output_buffer(This->output, element->qname, element->len);
679 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
680 This->starttagopen = FALSE;
681
682 return S_OK;
683 }
684
685 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
686 {
687 xmlwriter *This = impl_from_IXmlWriter(iface);
688
689 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
690
691 return E_NOTIMPL;
692 }
693
694 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
695 {
696 xmlwriter *This = impl_from_IXmlWriter(iface);
697
698 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
699
700 return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
704 BOOL fWriteDefaultAttributes)
705 {
706 xmlwriter *This = impl_from_IXmlWriter(iface);
707
708 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
709
710 return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
714 BOOL fWriteDefaultAttributes)
715 {
716 xmlwriter *This = impl_from_IXmlWriter(iface);
717
718 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
719
720 return E_NOTIMPL;
721 }
722
723 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
724 LPCWSTR text)
725 {
726 xmlwriter *This = impl_from_IXmlWriter(iface);
727 static const WCHAR xmlW[] = {'x','m','l',0};
728 static const WCHAR openpiW[] = {'<','?'};
729 static const WCHAR spaceW[] = {' '};
730
731 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
732
733 switch (This->state)
734 {
735 case XmlWriterState_Initial:
736 return E_UNEXPECTED;
737 case XmlWriterState_DocStarted:
738 if (!strcmpW(name, xmlW))
739 return WR_E_INVALIDACTION;
740 break;
741 case XmlWriterState_ElemStarted:
742 return WR_E_INVALIDACTION;
743 default:
744 ;
745 }
746
747 write_encoding_bom(This);
748 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
749 write_output_buffer(This->output, name, -1);
750 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
751 write_output_buffer(This->output, text, -1);
752 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
753
754 if (!strcmpW(name, xmlW))
755 This->state = XmlWriterState_PIDocStarted;
756
757 return S_OK;
758 }
759
760 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
761 LPCWSTR pwszNamespaceUri)
762 {
763 xmlwriter *This = impl_from_IXmlWriter(iface);
764
765 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
766
767 return E_NOTIMPL;
768 }
769
770 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR pwszData)
771 {
772 xmlwriter *This = impl_from_IXmlWriter(iface);
773
774 FIXME("%p %s\n", This, wine_dbgstr_w(pwszData));
775
776 return E_NOTIMPL;
777 }
778
779 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
780 {
781 xmlwriter *This = impl_from_IXmlWriter(iface);
782
783 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
784
785 return E_NOTIMPL;
786 }
787
788 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
789 {
790 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
791 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
792 xmlwriter *This = impl_from_IXmlWriter(iface);
793
794 TRACE("(%p)->(%d)\n", This, standalone);
795
796 switch (This->state)
797 {
798 case XmlWriterState_Initial:
799 return E_UNEXPECTED;
800 case XmlWriterState_PIDocStarted:
801 This->state = XmlWriterState_DocStarted;
802 return S_OK;
803 case XmlWriterState_DocStarted:
804 case XmlWriterState_ElemStarted:
805 return WR_E_INVALIDACTION;
806 default:
807 ;
808 }
809
810 write_encoding_bom(This);
811 This->state = XmlWriterState_DocStarted;
812 if (This->omitxmldecl) return S_OK;
813
814 /* version */
815 write_output_buffer(This->output, versionW, ARRAY_SIZE(versionW));
816
817 /* encoding */
818 write_output_buffer(This->output, encodingW, ARRAY_SIZE(encodingW));
819 write_output_buffer_quoted(This->output, get_encoding_name(This->output->encoding), -1);
820
821 /* standalone */
822 if (standalone == XmlStandalone_Omit)
823 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
824 else {
825 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
826 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
827 static const WCHAR noW[] = {'n','o','\"','?','>'};
828
829 write_output_buffer(This->output, standaloneW, ARRAY_SIZE(standaloneW));
830 if (standalone == XmlStandalone_Yes)
831 write_output_buffer(This->output, yesW, ARRAY_SIZE(yesW));
832 else
833 write_output_buffer(This->output, noW, ARRAY_SIZE(noW));
834 }
835
836 return S_OK;
837 }
838
839 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
840 {
841 xmlwriter *This = impl_from_IXmlWriter(iface);
842 struct element *element;
843
844 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
845
846 if (This->state == XmlWriterState_Initial)
847 return E_UNEXPECTED;
848
849 if (!local_name)
850 return E_INVALIDARG;
851
852 /* close pending element */
853 if (This->starttagopen)
854 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
855
856 element = alloc_element(This, prefix, local_name);
857 if (!element)
858 return E_OUTOFMEMORY;
859
860 write_encoding_bom(This);
861 This->state = XmlWriterState_ElemStarted;
862 This->starttagopen = TRUE;
863
864 push_element(This, element);
865
866 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
867 write_output_qname(This->output, prefix, local_name);
868
869 return S_OK;
870 }
871
872 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, LPCWSTR pwszText)
873 {
874 xmlwriter *This = impl_from_IXmlWriter(iface);
875
876 FIXME("%p %s\n", This, wine_dbgstr_w(pwszText));
877
878 return E_NOTIMPL;
879 }
880
881 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
882 {
883 xmlwriter *This = impl_from_IXmlWriter(iface);
884
885 FIXME("%p %d %d\n", This, wchLow, wchHigh);
886
887 return E_NOTIMPL;
888 }
889
890 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
891 {
892 xmlwriter *This = impl_from_IXmlWriter(iface);
893
894 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
895
896 return E_NOTIMPL;
897 }
898
899 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
900 {
901 xmlwriter *This = impl_from_IXmlWriter(iface);
902
903 TRACE("%p\n", This);
904
905 return writeroutput_flush_stream(This->output);
906 }
907
908 static const struct IXmlWriterVtbl xmlwriter_vtbl =
909 {
910 xmlwriter_QueryInterface,
911 xmlwriter_AddRef,
912 xmlwriter_Release,
913 xmlwriter_SetOutput,
914 xmlwriter_GetProperty,
915 xmlwriter_SetProperty,
916 xmlwriter_WriteAttributes,
917 xmlwriter_WriteAttributeString,
918 xmlwriter_WriteCData,
919 xmlwriter_WriteCharEntity,
920 xmlwriter_WriteChars,
921 xmlwriter_WriteComment,
922 xmlwriter_WriteDocType,
923 xmlwriter_WriteElementString,
924 xmlwriter_WriteEndDocument,
925 xmlwriter_WriteEndElement,
926 xmlwriter_WriteEntityRef,
927 xmlwriter_WriteFullEndElement,
928 xmlwriter_WriteName,
929 xmlwriter_WriteNmToken,
930 xmlwriter_WriteNode,
931 xmlwriter_WriteNodeShallow,
932 xmlwriter_WriteProcessingInstruction,
933 xmlwriter_WriteQualifiedName,
934 xmlwriter_WriteRaw,
935 xmlwriter_WriteRawChars,
936 xmlwriter_WriteStartDocument,
937 xmlwriter_WriteStartElement,
938 xmlwriter_WriteString,
939 xmlwriter_WriteSurrogateCharEntity,
940 xmlwriter_WriteWhitespace,
941 xmlwriter_Flush
942 };
943
944 /** IXmlWriterOutput **/
945 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
946 {
947 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
948
949 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
950
951 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
952 IsEqualGUID(riid, &IID_IUnknown))
953 {
954 *ppvObject = iface;
955 }
956 else
957 {
958 WARN("interface %s not implemented\n", debugstr_guid(riid));
959 *ppvObject = NULL;
960 return E_NOINTERFACE;
961 }
962
963 IUnknown_AddRef(iface);
964
965 return S_OK;
966 }
967
968 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
969 {
970 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
971 ULONG ref = InterlockedIncrement(&This->ref);
972 TRACE("(%p)->(%d)\n", This, ref);
973 return ref;
974 }
975
976 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
977 {
978 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
979 LONG ref = InterlockedDecrement(&This->ref);
980
981 TRACE("(%p)->(%d)\n", This, ref);
982
983 if (ref == 0)
984 {
985 IMalloc *imalloc = This->imalloc;
986 if (This->output) IUnknown_Release(This->output);
987 if (This->stream) ISequentialStream_Release(This->stream);
988 free_output_buffer(This);
989 writeroutput_free(This, This);
990 if (imalloc) IMalloc_Release(imalloc);
991 }
992
993 return ref;
994 }
995
996 static const struct IUnknownVtbl xmlwriteroutputvtbl =
997 {
998 xmlwriteroutput_QueryInterface,
999 xmlwriteroutput_AddRef,
1000 xmlwriteroutput_Release
1001 };
1002
1003 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1004 {
1005 xmlwriter *writer;
1006
1007 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1008
1009 if (!IsEqualGUID(riid, &IID_IXmlWriter))
1010 {
1011 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1012 return E_FAIL;
1013 }
1014
1015 if (imalloc)
1016 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1017 else
1018 writer = heap_alloc(sizeof(*writer));
1019 if(!writer) return E_OUTOFMEMORY;
1020
1021 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1022 writer->ref = 1;
1023 writer->imalloc = imalloc;
1024 if (imalloc) IMalloc_AddRef(imalloc);
1025 writer->output = NULL;
1026 writer->indent = FALSE;
1027 writer->bom = TRUE;
1028 writer->omitxmldecl = FALSE;
1029 writer->conformance = XmlConformanceLevel_Document;
1030 writer->state = XmlWriterState_Initial;
1031 writer->bomwritten = FALSE;
1032 writer->starttagopen = FALSE;
1033 list_init(&writer->elements);
1034
1035 *obj = &writer->IXmlWriter_iface;
1036
1037 TRACE("returning iface %p\n", *obj);
1038
1039 return S_OK;
1040 }
1041
1042 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1043 IMalloc *imalloc,
1044 LPCWSTR encoding,
1045 IXmlWriterOutput **output)
1046 {
1047 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1048 xmlwriteroutput *writeroutput;
1049 HRESULT hr;
1050
1051 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1052
1053 if (!stream || !output) return E_INVALIDARG;
1054
1055 *output = NULL;
1056
1057 if (imalloc)
1058 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1059 else
1060 writeroutput = heap_alloc(sizeof(*writeroutput));
1061 if(!writeroutput) return E_OUTOFMEMORY;
1062
1063 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1064 writeroutput->ref = 1;
1065 writeroutput->imalloc = imalloc;
1066 if (imalloc) IMalloc_AddRef(imalloc);
1067 writeroutput->encoding = parse_encoding_name(encoding ? encoding : utf8W, -1);
1068 writeroutput->stream = NULL;
1069 hr = init_output_buffer(writeroutput);
1070 if (FAILED(hr)) {
1071 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1072 return hr;
1073 }
1074
1075 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1076
1077 *output = &writeroutput->IXmlWriterOutput_iface;
1078
1079 TRACE("returning iface %p\n", *output);
1080
1081 return S_OK;
1082 }