[XMLLITE] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / xmllite / writer.c
1 /*
2 * IXmlWriter implementation
3 *
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014-2018 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 <assert.h>
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "xmllite.h"
30 #include "xmllite_private.h"
31 #ifdef __REACTOS__
32 #include <wchar.h>
33 #include <winnls.h>
34 #endif
35 #include "initguid.h"
36
37 #include "wine/debug.h"
38 #include "wine/list.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
41
42 /* not defined in public headers */
43 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
44
45 static const WCHAR closeelementW[] = {'<','/'};
46 static const WCHAR closetagW[] = {' ','/','>'};
47 static const WCHAR closepiW[] = {'?','>'};
48 static const WCHAR xmlnsW[] = {' ','x','m','l','n','s'};
49 static const WCHAR xmlnsuriW[] = {'h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','2','0','0','0','/','x','m','l','n','s','/',0};
50
51 struct output_buffer
52 {
53 char *data;
54 unsigned int allocated;
55 unsigned int written;
56 UINT codepage;
57 };
58
59 typedef enum
60 {
61 XmlWriterState_Initial, /* output is not set yet */
62 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
63 XmlWriterState_InvalidEncoding, /* SetOutput() was called, but output had invalid encoding */
64 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
65 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
66 XmlWriterState_ElemStarted, /* writing element */
67 XmlWriterState_Content, /* content is accepted at this point */
68 XmlWriterState_DocClosed /* WriteEndDocument was called */
69 } XmlWriterState;
70
71 typedef struct
72 {
73 IXmlWriterOutput IXmlWriterOutput_iface;
74 LONG ref;
75 IUnknown *output;
76 ISequentialStream *stream;
77 IMalloc *imalloc;
78 xml_encoding encoding;
79 WCHAR *encoding_name; /* exactly as specified on output creation */
80 struct output_buffer buffer;
81 DWORD written : 1;
82 } xmlwriteroutput;
83
84 static const struct IUnknownVtbl xmlwriteroutputvtbl;
85
86 struct element
87 {
88 struct list entry;
89 WCHAR *qname;
90 unsigned int len; /* qname length in chars */
91 struct list ns;
92 };
93
94 struct ns
95 {
96 struct list entry;
97 WCHAR *prefix;
98 int prefix_len;
99 WCHAR *uri;
100 BOOL emitted;
101 struct element *element;
102 };
103
104 typedef struct _xmlwriter
105 {
106 IXmlWriter IXmlWriter_iface;
107 LONG ref;
108 IMalloc *imalloc;
109 xmlwriteroutput *output;
110 unsigned int indent_level;
111 BOOL indent;
112 BOOL bom;
113 BOOL omitxmldecl;
114 XmlConformanceLevel conformance;
115 XmlWriterState state;
116 struct list elements;
117 DWORD bomwritten : 1;
118 DWORD starttagopen : 1;
119 DWORD textnode : 1;
120 } xmlwriter;
121
122 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
123 {
124 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
125 }
126
127 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
128 {
129 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
130 }
131
132 static const char *debugstr_writer_prop(XmlWriterProperty prop)
133 {
134 static const char * const prop_names[] =
135 {
136 "MultiLanguage",
137 "Indent",
138 "ByteOrderMark",
139 "OmitXmlDeclaration",
140 "ConformanceLevel"
141 };
142
143 if (prop > _XmlWriterProperty_Last)
144 return wine_dbg_sprintf("unknown property=%d", prop);
145
146 return prop_names[prop];
147 }
148
149 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
150 const WCHAR *encoding_name, xmlwriteroutput **out);
151
152 /* writer output memory allocation functions */
153 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
154 {
155 return m_alloc(output->imalloc, len);
156 }
157
158 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
159 {
160 m_free(output->imalloc, mem);
161 }
162
163 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
164 {
165 return m_realloc(output->imalloc, mem, len);
166 }
167
168 /* writer memory allocation functions */
169 static inline void *writer_alloc(const xmlwriter *writer, size_t len)
170 {
171 return m_alloc(writer->imalloc, len);
172 }
173
174 static inline void writer_free(const xmlwriter *writer, void *mem)
175 {
176 m_free(writer->imalloc, mem);
177 }
178
179 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
180 {
181 struct element *ret;
182 int len;
183
184 ret = writer_alloc(writer, sizeof(*ret));
185 if (!ret) return ret;
186
187 len = prefix ? lstrlenW(prefix) + 1 /* ':' */ : 0;
188 len += lstrlenW(local);
189
190 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
191 ret->len = len;
192 if (prefix) {
193 static const WCHAR colonW[] = {':',0};
194 lstrcpyW(ret->qname, prefix);
195 lstrcatW(ret->qname, colonW);
196 }
197 else
198 ret->qname[0] = 0;
199 lstrcatW(ret->qname, local);
200 list_init(&ret->ns);
201
202 return ret;
203 }
204
205 static void writer_free_element(xmlwriter *writer, struct element *element)
206 {
207 struct ns *ns, *ns2;
208
209 LIST_FOR_EACH_ENTRY_SAFE(ns, ns2, &element->ns, struct ns, entry)
210 {
211 list_remove(&ns->entry);
212 writer_free(writer, ns->prefix);
213 writer_free(writer, ns->uri);
214 writer_free(writer, ns);
215 }
216
217 writer_free(writer, element->qname);
218 writer_free(writer, element);
219 }
220
221 static void writer_free_element_stack(xmlwriter *writer)
222 {
223 struct element *element, *element2;
224
225 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &writer->elements, struct element, entry)
226 {
227 list_remove(&element->entry);
228 writer_free_element(writer, element);
229 }
230 }
231
232 static void writer_push_element(xmlwriter *writer, struct element *element)
233 {
234 list_add_head(&writer->elements, &element->entry);
235 }
236
237 static struct element *pop_element(xmlwriter *writer)
238 {
239 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
240
241 if (element)
242 list_remove(&element->entry);
243
244 return element;
245 }
246
247 static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len)
248 {
249 size_t size;
250 WCHAR *ret;
251
252 if (!str)
253 return NULL;
254
255 if (len == -1)
256 len = lstrlenW(str);
257
258 size = (len + 1) * sizeof(WCHAR);
259 ret = writer_alloc(writer, size);
260 memcpy(ret, str, size);
261 return ret;
262 }
263
264 static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
265 {
266 return writer_strndupW(writer, str, -1);
267 }
268
269 static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
270 {
271 struct element *element;
272 struct ns *ns;
273
274 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
275 if (!element)
276 return NULL;
277
278 if ((ns = writer_alloc(writer, sizeof(*ns))))
279 {
280 ns->prefix = writer_strndupW(writer, prefix, prefix_len);
281 ns->prefix_len = prefix_len;
282 ns->uri = writer_strdupW(writer, uri);
283 ns->emitted = FALSE;
284 ns->element = element;
285 list_add_tail(&element->ns, &ns->entry);
286 }
287
288 return ns;
289 }
290
291 static BOOL is_empty_string(const WCHAR *str)
292 {
293 return !str || !*str;
294 }
295
296 static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
297 {
298 struct element *element;
299 struct ns *ns;
300
301 if (is_empty_string(prefix) || is_empty_string(uri))
302 return NULL;
303
304 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
305
306 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
307 {
308 if (!wcscmp(uri, ns->uri) && !wcscmp(prefix, ns->prefix))
309 return ns;
310 }
311
312 return NULL;
313 }
314
315 static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
316 {
317 struct element *element;
318 struct ns *ns;
319
320 if (is_empty_string(prefix) && is_empty_string(uri))
321 return NULL;
322
323 LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
324 {
325 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
326 {
327 if (!uri)
328 {
329 if (!ns->prefix) continue;
330 if (!wcscmp(ns->prefix, prefix))
331 return ns;
332 }
333 else if (!wcscmp(uri, ns->uri))
334 {
335 if (prefix && !*prefix)
336 return NULL;
337 if (!prefix || !wcscmp(prefix, ns->prefix))
338 return ns;
339 }
340 }
341 }
342
343 return NULL;
344 }
345
346 static HRESULT is_valid_ncname(const WCHAR *str, int *out)
347 {
348 int len = 0;
349
350 *out = 0;
351
352 if (!str || !*str)
353 return S_OK;
354
355 while (*str)
356 {
357 if (!is_ncnamechar(*str))
358 return WC_E_NAMECHARACTER;
359 len++;
360 str++;
361 }
362
363 *out = len;
364 return S_OK;
365 }
366
367 static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
368 {
369 unsigned int len = 1;
370
371 *out = 0;
372
373 if (!str || !*str)
374 return S_OK;
375
376 if (!is_namestartchar(*str++))
377 return WC_E_NAMECHARACTER;
378
379 while (*str++)
380 {
381 if (!is_namechar(*str))
382 return WC_E_NAMECHARACTER;
383 len++;
384 }
385
386 *out = len;
387 return S_OK;
388 }
389
390 static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
391 {
392 unsigned int len = 0;
393
394 *out = 0;
395
396 if (!str || !*str)
397 return S_OK;
398
399 while (*str)
400 {
401 if (!is_pubchar(*str++))
402 return WC_E_PUBLICID;
403 len++;
404 }
405
406 *out = len;
407
408 return S_OK;
409 }
410
411 static HRESULT init_output_buffer(xmlwriteroutput *output)
412 {
413 struct output_buffer *buffer = &output->buffer;
414 const int initial_len = 0x2000;
415 UINT cp = ~0u;
416 HRESULT hr;
417
418 if (FAILED(hr = get_code_page(output->encoding, &cp)))
419 WARN("Failed to get code page for specified encoding.\n");
420
421 buffer->data = writeroutput_alloc(output, initial_len);
422 if (!buffer->data) return E_OUTOFMEMORY;
423
424 memset(buffer->data, 0, 4);
425 buffer->allocated = initial_len;
426 buffer->written = 0;
427 buffer->codepage = cp;
428
429 return S_OK;
430 }
431
432 static void free_output_buffer(xmlwriteroutput *output)
433 {
434 struct output_buffer *buffer = &output->buffer;
435 writeroutput_free(output, buffer->data);
436 buffer->data = NULL;
437 buffer->allocated = 0;
438 buffer->written = 0;
439 }
440
441 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
442 {
443 struct output_buffer *buffer = &output->buffer;
444 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
445 if (buffer->allocated < buffer->written + length + 4) {
446 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
447 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
448 if (!ptr) return E_OUTOFMEMORY;
449 buffer->data = ptr;
450 buffer->allocated = grown_size;
451 }
452
453 return S_OK;
454 }
455
456 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
457 {
458 struct output_buffer *buffer = &output->buffer;
459 int length;
460 HRESULT hr;
461 char *ptr;
462
463 if (buffer->codepage == 1200) {
464 /* For UTF-16 encoding just copy. */
465 length = len == -1 ? lstrlenW(data) : len;
466 if (length) {
467 length *= sizeof(WCHAR);
468
469 hr = grow_output_buffer(output, length);
470 if (FAILED(hr)) return hr;
471 ptr = buffer->data + buffer->written;
472
473 memcpy(ptr, data, length);
474 buffer->written += length;
475 ptr += length;
476 /* null termination */
477 *(WCHAR*)ptr = 0;
478 }
479 }
480 else {
481 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
482 hr = grow_output_buffer(output, length);
483 if (FAILED(hr)) return hr;
484 ptr = buffer->data + buffer->written;
485 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
486 buffer->written += len == -1 ? length-1 : length;
487 }
488 output->written = length != 0;
489
490 return S_OK;
491 }
492
493 static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch)
494 {
495 return write_output_buffer(output, &ch, 1);
496 }
497
498 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
499 {
500 write_output_buffer_char(output, '"');
501 if (!is_empty_string(data))
502 write_output_buffer(output, data, len);
503 write_output_buffer_char(output, '"');
504 return S_OK;
505 }
506
507 /* TODO: test if we need to validate char range */
508 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len,
509 const WCHAR *local_name, int local_len)
510 {
511 assert(prefix_len >= 0 && local_len >= 0);
512
513 if (prefix_len)
514 write_output_buffer(output, prefix, prefix_len);
515
516 if (prefix_len && local_len)
517 write_output_buffer_char(output, ':');
518
519 write_output_buffer(output, local_name, local_len);
520
521 return S_OK;
522 }
523
524 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
525 {
526 if (writeroutput->stream) {
527 ISequentialStream_Release(writeroutput->stream);
528 writeroutput->stream = NULL;
529 }
530 }
531
532 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
533 {
534 HRESULT hr;
535
536 writeroutput_release_stream(writeroutput);
537 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
538 if (hr != S_OK)
539 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
540
541 return hr;
542 }
543
544 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
545 {
546 struct output_buffer *buffer;
547 ULONG written, offset = 0;
548 HRESULT hr;
549
550 if (!output || !output->stream)
551 return S_OK;
552
553 buffer = &output->buffer;
554
555 /* It will loop forever until everything is written or an error occurred. */
556 do {
557 written = 0;
558 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
559 if (FAILED(hr)) {
560 WARN("write to stream failed (0x%08x)\n", hr);
561 buffer->written = 0;
562 return hr;
563 }
564
565 offset += written;
566 buffer->written -= written;
567 } while (buffer->written > 0);
568
569 return S_OK;
570 }
571
572 static HRESULT write_encoding_bom(xmlwriter *writer)
573 {
574 if (!writer->bom || writer->bomwritten) return S_OK;
575
576 if (writer->output->encoding == XmlEncoding_UTF16) {
577 static const char utf16bom[] = {0xff, 0xfe};
578 struct output_buffer *buffer = &writer->output->buffer;
579 int len = sizeof(utf16bom);
580 HRESULT hr;
581
582 hr = grow_output_buffer(writer->output, len);
583 if (FAILED(hr)) return hr;
584 memcpy(buffer->data + buffer->written, utf16bom, len);
585 buffer->written += len;
586 }
587
588 writer->bomwritten = TRUE;
589 return S_OK;
590 }
591
592 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
593 {
594 if (output->encoding_name)
595 return output->encoding_name;
596
597 return get_encoding_name(output->encoding);
598 }
599
600 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
601 {
602 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
603 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
604
605 write_encoding_bom(writer);
606 writer->state = XmlWriterState_DocStarted;
607 if (writer->omitxmldecl) return S_OK;
608
609 /* version */
610 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
611
612 /* encoding */
613 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
614 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
615
616 /* standalone */
617 if (standalone == XmlStandalone_Omit)
618 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
619 else {
620 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
621 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
622 static const WCHAR noW[] = {'n','o','\"','?','>'};
623
624 write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
625 if (standalone == XmlStandalone_Yes)
626 write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
627 else
628 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
629 }
630
631 return S_OK;
632 }
633
634 static void writer_output_ns(xmlwriter *writer, struct element *element)
635 {
636 struct ns *ns;
637
638 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
639 {
640 if (ns->emitted)
641 continue;
642
643 write_output_qname(writer->output, xmlnsW, ARRAY_SIZE(xmlnsW), ns->prefix, ns->prefix_len);
644 write_output_buffer_char(writer->output, '=');
645 write_output_buffer_quoted(writer->output, ns->uri, -1);
646 }
647 }
648
649 static HRESULT writer_close_starttag(xmlwriter *writer)
650 {
651 HRESULT hr;
652
653 if (!writer->starttagopen) return S_OK;
654
655 writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry));
656 hr = write_output_buffer_char(writer->output, '>');
657 writer->starttagopen = 0;
658 return hr;
659 }
660
661 static void writer_inc_indent(xmlwriter *writer)
662 {
663 writer->indent_level++;
664 }
665
666 static void writer_dec_indent(xmlwriter *writer)
667 {
668 if (writer->indent_level)
669 writer->indent_level--;
670 }
671
672 static void write_node_indent(xmlwriter *writer)
673 {
674 static const WCHAR dblspaceW[] = {' ',' '};
675 static const WCHAR crlfW[] = {'\r','\n'};
676 unsigned int indent_level = writer->indent_level;
677
678 if (!writer->indent || writer->textnode)
679 {
680 writer->textnode = 0;
681 return;
682 }
683
684 /* Do state check to prevent newline inserted after BOM. It is assumed that
685 state does not change between writing BOM and inserting indentation. */
686 if (writer->output->written && writer->state != XmlWriterState_Ready)
687 write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW));
688 while (indent_level--)
689 write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW));
690
691 writer->textnode = 0;
692 }
693
694 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
695 {
696 xmlwriter *This = impl_from_IXmlWriter(iface);
697
698 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
699
700 if (IsEqualGUID(riid, &IID_IXmlWriter) ||
701 IsEqualGUID(riid, &IID_IUnknown))
702 {
703 *ppvObject = iface;
704 }
705 else
706 {
707 FIXME("interface %s is not supported\n", debugstr_guid(riid));
708 *ppvObject = NULL;
709 return E_NOINTERFACE;
710 }
711
712 IXmlWriter_AddRef(iface);
713
714 return S_OK;
715 }
716
717 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
718 {
719 xmlwriter *This = impl_from_IXmlWriter(iface);
720 ULONG ref = InterlockedIncrement(&This->ref);
721 TRACE("(%p)->(%u)\n", This, ref);
722 return ref;
723 }
724
725 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
726 {
727 xmlwriter *This = impl_from_IXmlWriter(iface);
728 ULONG ref = InterlockedDecrement(&This->ref);
729
730 TRACE("(%p)->(%u)\n", This, ref);
731
732 if (ref == 0) {
733 IMalloc *imalloc = This->imalloc;
734
735 writeroutput_flush_stream(This->output);
736 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
737
738 writer_free_element_stack(This);
739
740 writer_free(This, This);
741 if (imalloc) IMalloc_Release(imalloc);
742 }
743
744 return ref;
745 }
746
747 /*** IXmlWriter methods ***/
748 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
749 {
750 xmlwriter *This = impl_from_IXmlWriter(iface);
751 IXmlWriterOutput *writeroutput;
752 HRESULT hr;
753
754 TRACE("(%p)->(%p)\n", This, output);
755
756 if (This->output) {
757 writeroutput_release_stream(This->output);
758 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
759 This->output = NULL;
760 This->bomwritten = 0;
761 This->textnode = 0;
762 This->indent_level = 0;
763 writer_free_element_stack(This);
764 }
765
766 /* just reset current output */
767 if (!output) {
768 This->state = XmlWriterState_Initial;
769 return S_OK;
770 }
771
772 /* now try IXmlWriterOutput, ISequentialStream, IStream */
773 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
774 if (hr == S_OK) {
775 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
776 This->output = impl_from_IXmlWriterOutput(writeroutput);
777 else {
778 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
779 writeroutput, writeroutput->lpVtbl);
780 IUnknown_Release(writeroutput);
781 return E_FAIL;
782 }
783 }
784
785 if (hr != S_OK || !writeroutput) {
786 /* Create output for given stream. */
787 hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
788 if (hr != S_OK)
789 return hr;
790 }
791
792 if (This->output->encoding == XmlEncoding_Unknown)
793 This->state = XmlWriterState_InvalidEncoding;
794 else
795 This->state = XmlWriterState_Ready;
796 return writeroutput_query_for_stream(This->output);
797 }
798
799 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
800 {
801 xmlwriter *This = impl_from_IXmlWriter(iface);
802
803 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
804
805 if (!value) return E_INVALIDARG;
806
807 switch (property)
808 {
809 case XmlWriterProperty_Indent:
810 *value = This->indent;
811 break;
812 case XmlWriterProperty_ByteOrderMark:
813 *value = This->bom;
814 break;
815 case XmlWriterProperty_OmitXmlDeclaration:
816 *value = This->omitxmldecl;
817 break;
818 case XmlWriterProperty_ConformanceLevel:
819 *value = This->conformance;
820 break;
821 default:
822 FIXME("Unimplemented property (%u)\n", property);
823 return E_NOTIMPL;
824 }
825
826 return S_OK;
827 }
828
829 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
830 {
831 xmlwriter *This = impl_from_IXmlWriter(iface);
832
833 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
834
835 switch (property)
836 {
837 case XmlWriterProperty_Indent:
838 This->indent = !!value;
839 break;
840 case XmlWriterProperty_ByteOrderMark:
841 This->bom = !!value;
842 break;
843 case XmlWriterProperty_OmitXmlDeclaration:
844 This->omitxmldecl = !!value;
845 break;
846 default:
847 FIXME("Unimplemented property (%u)\n", property);
848 return E_NOTIMPL;
849 }
850
851 return S_OK;
852 }
853
854 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
855 BOOL fWriteDefaultAttributes)
856 {
857 xmlwriter *This = impl_from_IXmlWriter(iface);
858
859 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
860
861 return E_NOTIMPL;
862 }
863
864 static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
865 const WCHAR *local, int local_len, const WCHAR *value)
866 {
867 write_output_buffer_char(writer->output, ' ');
868 write_output_qname(writer->output, prefix, prefix_len, local, local_len);
869 write_output_buffer_char(writer->output, '=');
870 write_output_buffer_quoted(writer->output, value, -1);
871 }
872
873 static BOOL is_valid_xml_space_value(const WCHAR *value)
874 {
875 static const WCHAR preserveW[] = {'p','r','e','s','e','r','v','e',0};
876 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
877
878 if (!value)
879 return FALSE;
880
881 return !wcscmp(value, preserveW) || !wcscmp(value, defaultW);
882 }
883
884 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
885 LPCWSTR local, LPCWSTR uri, LPCWSTR value)
886 {
887 static const WCHAR spaceattrW[] = {'s','p','a','c','e',0};
888 static const WCHAR xmlnsW[] = {'x','m','l','n','s',0};
889 static const WCHAR xmlW[] = {'x','m','l',0};
890 xmlwriter *This = impl_from_IXmlWriter(iface);
891 BOOL is_xmlns_prefix, is_xmlns_local;
892 int prefix_len, local_len;
893 struct ns *ns;
894 HRESULT hr;
895
896 TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
897
898 switch (This->state)
899 {
900 case XmlWriterState_Initial:
901 return E_UNEXPECTED;
902 case XmlWriterState_Ready:
903 case XmlWriterState_DocClosed:
904 This->state = XmlWriterState_DocClosed;
905 return WR_E_INVALIDACTION;
906 case XmlWriterState_InvalidEncoding:
907 return MX_E_ENCODING;
908 default:
909 ;
910 }
911
912 /* Prefix "xmlns" */
913 is_xmlns_prefix = prefix && !wcscmp(prefix, xmlnsW);
914 if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
915 return WR_E_NSPREFIXDECLARED;
916
917 if (!local)
918 return E_INVALIDARG;
919
920 /* Validate prefix and local name */
921 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
922 return hr;
923
924 if (FAILED(hr = is_valid_ncname(local, &local_len)))
925 return hr;
926
927 is_xmlns_local = !wcscmp(local, xmlnsW);
928
929 /* Trivial case, no prefix. */
930 if (prefix_len == 0 && is_empty_string(uri))
931 {
932 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
933 return S_OK;
934 }
935
936 /* Predefined "xml" prefix. */
937 if (prefix_len && !wcscmp(prefix, xmlW))
938 {
939 /* Valid "space" value is enforced. */
940 if (!wcscmp(local, spaceattrW) && !is_valid_xml_space_value(value))
941 return WR_E_INVALIDXMLSPACE;
942
943 /* Redefinition is not allowed. */
944 if (!is_empty_string(uri))
945 return WR_E_XMLPREFIXDECLARATION;
946
947 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
948
949 return S_OK;
950 }
951
952 if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
953 {
954 if (prefix_len && !is_empty_string(uri))
955 return WR_E_XMLNSPREFIXDECLARATION;
956
957 /* Look for exact match defined in current element, and write it out. */
958 if (!(ns = writer_find_ns_current(This, prefix, value)))
959 ns = writer_push_ns(This, local, local_len, value);
960 ns->emitted = TRUE;
961
962 write_output_attribute(This, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, local, local_len, value);
963
964 return S_OK;
965 }
966
967 /* Ignore prefix is URI wasn't specified. */
968 if (is_xmlns_local && is_empty_string(uri))
969 {
970 write_output_attribute(This, NULL, 0, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, value);
971 return S_OK;
972 }
973
974 if (!(ns = writer_find_ns(This, prefix, uri)))
975 {
976 if (is_empty_string(prefix) && !is_empty_string(uri))
977 {
978 FIXME("Prefix autogeneration is not implemented.\n");
979 return E_NOTIMPL;
980 }
981 if (!is_empty_string(uri))
982 ns = writer_push_ns(This, prefix, prefix_len, uri);
983 }
984
985 if (ns)
986 write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
987 else
988 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
989
990 return S_OK;
991 }
992
993 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
994 {
995 static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
996 static const WCHAR cdatacloseW[] = {']',']','>'};
997 write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
998 if (data)
999 write_output_buffer(output, data, len);
1000 write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
1001 }
1002
1003 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
1004 {
1005 xmlwriter *This = impl_from_IXmlWriter(iface);
1006 int len;
1007
1008 TRACE("%p %s\n", This, debugstr_w(data));
1009
1010 switch (This->state)
1011 {
1012 case XmlWriterState_Initial:
1013 return E_UNEXPECTED;
1014 case XmlWriterState_ElemStarted:
1015 writer_close_starttag(This);
1016 break;
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 len = data ? lstrlenW(data) : 0;
1028
1029 write_node_indent(This);
1030 if (!len)
1031 write_cdata_section(This->output, NULL, 0);
1032 else {
1033 static const WCHAR cdatacloseW[] = {']',']','>',0};
1034 while (len) {
1035 const WCHAR *str = wcsstr(data, cdatacloseW);
1036 if (str) {
1037 str += 2;
1038 write_cdata_section(This->output, data, str - data);
1039 len -= str - data;
1040 data = str;
1041 }
1042 else {
1043 write_cdata_section(This->output, data, len);
1044 break;
1045 }
1046 }
1047 }
1048
1049 return S_OK;
1050 }
1051
1052 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1053 {
1054 static const WCHAR fmtW[] = {'&','#','x','%','x',';',0};
1055 xmlwriter *This = impl_from_IXmlWriter(iface);
1056 WCHAR bufW[16];
1057
1058 TRACE("%p %#x\n", This, ch);
1059
1060 switch (This->state)
1061 {
1062 case XmlWriterState_Initial:
1063 return E_UNEXPECTED;
1064 case XmlWriterState_InvalidEncoding:
1065 return MX_E_ENCODING;
1066 case XmlWriterState_ElemStarted:
1067 writer_close_starttag(This);
1068 break;
1069 case XmlWriterState_DocClosed:
1070 return WR_E_INVALIDACTION;
1071 default:
1072 ;
1073 }
1074
1075 swprintf(bufW, fmtW, ch);
1076 write_output_buffer(This->output, bufW, -1);
1077
1078 return S_OK;
1079 }
1080
1081 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1082 {
1083 xmlwriter *This = impl_from_IXmlWriter(iface);
1084
1085 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1086
1087 switch (This->state)
1088 {
1089 case XmlWriterState_Initial:
1090 return E_UNEXPECTED;
1091 case XmlWriterState_InvalidEncoding:
1092 return MX_E_ENCODING;
1093 case XmlWriterState_DocClosed:
1094 return WR_E_INVALIDACTION;
1095 default:
1096 ;
1097 }
1098
1099 return E_NOTIMPL;
1100 }
1101
1102
1103 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1104 {
1105 static const WCHAR copenW[] = {'<','!','-','-'};
1106 static const WCHAR ccloseW[] = {'-','-','>'};
1107 xmlwriter *This = impl_from_IXmlWriter(iface);
1108
1109 TRACE("%p %s\n", This, debugstr_w(comment));
1110
1111 switch (This->state)
1112 {
1113 case XmlWriterState_Initial:
1114 return E_UNEXPECTED;
1115 case XmlWriterState_InvalidEncoding:
1116 return MX_E_ENCODING;
1117 case XmlWriterState_ElemStarted:
1118 writer_close_starttag(This);
1119 break;
1120 case XmlWriterState_DocClosed:
1121 return WR_E_INVALIDACTION;
1122 default:
1123 ;
1124 }
1125
1126 write_node_indent(This);
1127 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
1128 if (comment) {
1129 int len = lstrlenW(comment), i;
1130
1131 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1132 comment string */
1133 if (len > 1) {
1134 for (i = 0; i < len; i++) {
1135 write_output_buffer(This->output, comment + i, 1);
1136 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1137 write_output_buffer_char(This->output, ' ');
1138 }
1139 }
1140 else
1141 write_output_buffer(This->output, comment, len);
1142
1143 if (len && comment[len-1] == '-')
1144 write_output_buffer_char(This->output, ' ');
1145 }
1146 write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
1147
1148 return S_OK;
1149 }
1150
1151 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
1152 LPCWSTR sysid, LPCWSTR subset)
1153 {
1154 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
1155 static const WCHAR publicW[] = {' ','P','U','B','L','I','C',' '};
1156 static const WCHAR systemW[] = {' ','S','Y','S','T','E','M',' '};
1157 xmlwriter *This = impl_from_IXmlWriter(iface);
1158 unsigned int name_len, pubid_len;
1159 HRESULT hr;
1160
1161 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
1162 wine_dbgstr_w(subset));
1163
1164 switch (This->state)
1165 {
1166 case XmlWriterState_Initial:
1167 return E_UNEXPECTED;
1168 case XmlWriterState_InvalidEncoding:
1169 return MX_E_ENCODING;
1170 case XmlWriterState_Content:
1171 case XmlWriterState_DocClosed:
1172 return WR_E_INVALIDACTION;
1173 default:
1174 ;
1175 }
1176
1177 if (is_empty_string(name))
1178 return E_INVALIDARG;
1179
1180 if (FAILED(hr = is_valid_name(name, &name_len)))
1181 return hr;
1182
1183 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1184 return hr;
1185
1186 write_output_buffer(This->output, doctypeW, ARRAY_SIZE(doctypeW));
1187 write_output_buffer(This->output, name, name_len);
1188
1189 if (pubid)
1190 {
1191 write_output_buffer(This->output, publicW, ARRAY_SIZE(publicW));
1192 write_output_buffer_quoted(This->output, pubid, pubid_len);
1193 write_output_buffer_char(This->output, ' ');
1194 write_output_buffer_quoted(This->output, sysid, -1);
1195 }
1196 else if (sysid)
1197 {
1198 write_output_buffer(This->output, systemW, ARRAY_SIZE(systemW));
1199 write_output_buffer_quoted(This->output, sysid, -1);
1200 }
1201
1202 if (subset)
1203 {
1204 write_output_buffer_char(This->output, ' ');
1205 write_output_buffer_char(This->output, '[');
1206 write_output_buffer(This->output, subset, -1);
1207 write_output_buffer_char(This->output, ']');
1208 }
1209 write_output_buffer_char(This->output, '>');
1210
1211 This->state = XmlWriterState_Content;
1212
1213 return S_OK;
1214 }
1215
1216 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
1217 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1218 {
1219 xmlwriter *This = impl_from_IXmlWriter(iface);
1220 int prefix_len, local_len;
1221 struct ns *ns;
1222 HRESULT hr;
1223
1224 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
1225 wine_dbgstr_w(uri), wine_dbgstr_w(value));
1226
1227 switch (This->state)
1228 {
1229 case XmlWriterState_Initial:
1230 return E_UNEXPECTED;
1231 case XmlWriterState_InvalidEncoding:
1232 return MX_E_ENCODING;
1233 case XmlWriterState_ElemStarted:
1234 writer_close_starttag(This);
1235 break;
1236 case XmlWriterState_DocClosed:
1237 return WR_E_INVALIDACTION;
1238 default:
1239 ;
1240 }
1241
1242 if (!local_name)
1243 return E_INVALIDARG;
1244
1245 /* Validate prefix and local name */
1246 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1247 return hr;
1248
1249 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1250 return hr;
1251
1252 ns = writer_find_ns(This, prefix, uri);
1253 if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
1254 return WR_E_NSPREFIXWITHEMPTYNSURI;
1255
1256 if (uri && !wcscmp(uri, xmlnsuriW))
1257 {
1258 if (!prefix)
1259 return WR_E_XMLNSPREFIXDECLARATION;
1260
1261 if (!is_empty_string(prefix))
1262 return WR_E_XMLNSURIDECLARATION;
1263 }
1264
1265 write_encoding_bom(This);
1266 write_node_indent(This);
1267
1268 write_output_buffer_char(This->output, '<');
1269 if (ns)
1270 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1271 else
1272 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1273
1274 if (!ns && (prefix_len || !is_empty_string(uri)))
1275 {
1276 write_output_qname(This->output, xmlnsW, ARRAY_SIZE(xmlnsW), prefix, prefix_len);
1277 write_output_buffer_char(This->output, '=');
1278 write_output_buffer_quoted(This->output, uri, -1);
1279 }
1280
1281 if (value)
1282 {
1283 write_output_buffer_char(This->output, '>');
1284 write_output_buffer(This->output, value, -1);
1285 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
1286 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1287 write_output_buffer_char(This->output, '>');
1288 }
1289 else
1290 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
1291
1292 This->state = XmlWriterState_Content;
1293
1294 return S_OK;
1295 }
1296
1297 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
1298 {
1299 xmlwriter *This = impl_from_IXmlWriter(iface);
1300
1301 TRACE("%p\n", This);
1302
1303 switch (This->state)
1304 {
1305 case XmlWriterState_Initial:
1306 return E_UNEXPECTED;
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 /* empty element stack */
1318 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1319 ;
1320
1321 This->state = XmlWriterState_DocClosed;
1322 return S_OK;
1323 }
1324
1325 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
1326 {
1327 xmlwriter *This = impl_from_IXmlWriter(iface);
1328 struct element *element;
1329
1330 TRACE("%p\n", This);
1331
1332 switch (This->state)
1333 {
1334 case XmlWriterState_Initial:
1335 return E_UNEXPECTED;
1336 case XmlWriterState_Ready:
1337 case XmlWriterState_DocClosed:
1338 This->state = XmlWriterState_DocClosed;
1339 return WR_E_INVALIDACTION;
1340 case XmlWriterState_InvalidEncoding:
1341 return MX_E_ENCODING;
1342 default:
1343 ;
1344 }
1345
1346 element = pop_element(This);
1347 if (!element)
1348 return WR_E_INVALIDACTION;
1349
1350 writer_dec_indent(This);
1351
1352 if (This->starttagopen)
1353 {
1354 writer_output_ns(This, element);
1355 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
1356 This->starttagopen = 0;
1357 }
1358 else
1359 {
1360 /* Write full end tag. */
1361 write_node_indent(This);
1362 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
1363 write_output_buffer(This->output, element->qname, element->len);
1364 write_output_buffer_char(This->output, '>');
1365 }
1366 writer_free_element(This, element);
1367
1368 return S_OK;
1369 }
1370
1371 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
1372 {
1373 xmlwriter *This = impl_from_IXmlWriter(iface);
1374
1375 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1376
1377 switch (This->state)
1378 {
1379 case XmlWriterState_Initial:
1380 return E_UNEXPECTED;
1381 case XmlWriterState_InvalidEncoding:
1382 return MX_E_ENCODING;
1383 case XmlWriterState_DocClosed:
1384 return WR_E_INVALIDACTION;
1385 default:
1386 ;
1387 }
1388
1389 return E_NOTIMPL;
1390 }
1391
1392 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
1393 {
1394 xmlwriter *This = impl_from_IXmlWriter(iface);
1395 struct element *element;
1396
1397 TRACE("%p\n", This);
1398
1399 switch (This->state)
1400 {
1401 case XmlWriterState_Initial:
1402 return E_UNEXPECTED;
1403 case XmlWriterState_Ready:
1404 case XmlWriterState_DocClosed:
1405 This->state = XmlWriterState_DocClosed;
1406 return WR_E_INVALIDACTION;
1407 case XmlWriterState_InvalidEncoding:
1408 return MX_E_ENCODING;
1409 case XmlWriterState_ElemStarted:
1410 writer_close_starttag(This);
1411 break;
1412 default:
1413 ;
1414 }
1415
1416 element = pop_element(This);
1417 if (!element)
1418 return WR_E_INVALIDACTION;
1419
1420 writer_dec_indent(This);
1421
1422 /* don't force full end tag to the next line */
1423 if (This->state == XmlWriterState_ElemStarted)
1424 {
1425 This->state = XmlWriterState_Content;
1426 This->textnode = 0;
1427 }
1428 else
1429 write_node_indent(This);
1430
1431 /* write full end tag */
1432 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
1433 write_output_buffer(This->output, element->qname, element->len);
1434 write_output_buffer_char(This->output, '>');
1435
1436 writer_free_element(This, element);
1437
1438 return S_OK;
1439 }
1440
1441 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1442 {
1443 xmlwriter *This = impl_from_IXmlWriter(iface);
1444
1445 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1446
1447 switch (This->state)
1448 {
1449 case XmlWriterState_Initial:
1450 return E_UNEXPECTED;
1451 case XmlWriterState_Ready:
1452 case XmlWriterState_DocClosed:
1453 This->state = XmlWriterState_DocClosed;
1454 return WR_E_INVALIDACTION;
1455 case XmlWriterState_InvalidEncoding:
1456 return MX_E_ENCODING;
1457 default:
1458 ;
1459 }
1460
1461 return E_NOTIMPL;
1462 }
1463
1464 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1465 {
1466 xmlwriter *This = impl_from_IXmlWriter(iface);
1467
1468 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1469
1470 switch (This->state)
1471 {
1472 case XmlWriterState_Initial:
1473 return E_UNEXPECTED;
1474 case XmlWriterState_Ready:
1475 case XmlWriterState_DocClosed:
1476 This->state = XmlWriterState_DocClosed;
1477 return WR_E_INVALIDACTION;
1478 case XmlWriterState_InvalidEncoding:
1479 return MX_E_ENCODING;
1480 default:
1481 ;
1482 }
1483
1484 return E_NOTIMPL;
1485 }
1486
1487 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1488 BOOL fWriteDefaultAttributes)
1489 {
1490 xmlwriter *This = impl_from_IXmlWriter(iface);
1491
1492 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1493
1494 return E_NOTIMPL;
1495 }
1496
1497 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1498 BOOL fWriteDefaultAttributes)
1499 {
1500 xmlwriter *This = impl_from_IXmlWriter(iface);
1501
1502 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1503
1504 return E_NOTIMPL;
1505 }
1506
1507 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1508 LPCWSTR text)
1509 {
1510 xmlwriter *This = impl_from_IXmlWriter(iface);
1511 static const WCHAR xmlW[] = {'x','m','l',0};
1512 static const WCHAR openpiW[] = {'<','?'};
1513
1514 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1515
1516 switch (This->state)
1517 {
1518 case XmlWriterState_Initial:
1519 return E_UNEXPECTED;
1520 case XmlWriterState_InvalidEncoding:
1521 return MX_E_ENCODING;
1522 case XmlWriterState_DocStarted:
1523 if (!wcscmp(name, xmlW))
1524 return WR_E_INVALIDACTION;
1525 break;
1526 case XmlWriterState_ElemStarted:
1527 case XmlWriterState_DocClosed:
1528 return WR_E_INVALIDACTION;
1529 default:
1530 ;
1531 }
1532
1533 write_encoding_bom(This);
1534 write_node_indent(This);
1535 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
1536 write_output_buffer(This->output, name, -1);
1537 write_output_buffer_char(This->output, ' ');
1538 write_output_buffer(This->output, text, -1);
1539 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1540
1541 if (!wcscmp(name, xmlW))
1542 This->state = XmlWriterState_PIDocStarted;
1543
1544 return S_OK;
1545 }
1546
1547 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1548 LPCWSTR pwszNamespaceUri)
1549 {
1550 xmlwriter *This = impl_from_IXmlWriter(iface);
1551
1552 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1553
1554 switch (This->state)
1555 {
1556 case XmlWriterState_Initial:
1557 return E_UNEXPECTED;
1558 case XmlWriterState_InvalidEncoding:
1559 return MX_E_ENCODING;
1560 case XmlWriterState_DocClosed:
1561 return WR_E_INVALIDACTION;
1562 default:
1563 ;
1564 }
1565
1566 return E_NOTIMPL;
1567 }
1568
1569 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1570 {
1571 xmlwriter *This = impl_from_IXmlWriter(iface);
1572
1573 TRACE("%p %s\n", This, debugstr_w(data));
1574
1575 if (!data)
1576 return S_OK;
1577
1578 switch (This->state)
1579 {
1580 case XmlWriterState_Initial:
1581 return E_UNEXPECTED;
1582 case XmlWriterState_Ready:
1583 write_xmldecl(This, XmlStandalone_Omit);
1584 /* fallthrough */
1585 case XmlWriterState_DocStarted:
1586 case XmlWriterState_PIDocStarted:
1587 break;
1588 case XmlWriterState_InvalidEncoding:
1589 return MX_E_ENCODING;
1590 default:
1591 This->state = XmlWriterState_DocClosed;
1592 return WR_E_INVALIDACTION;
1593 }
1594
1595 write_output_buffer(This->output, data, -1);
1596 return S_OK;
1597 }
1598
1599 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1600 {
1601 xmlwriter *This = impl_from_IXmlWriter(iface);
1602
1603 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1604
1605 switch (This->state)
1606 {
1607 case XmlWriterState_Initial:
1608 return E_UNEXPECTED;
1609 case XmlWriterState_InvalidEncoding:
1610 return MX_E_ENCODING;
1611 case XmlWriterState_DocClosed:
1612 return WR_E_INVALIDACTION;
1613 default:
1614 ;
1615 }
1616
1617 return E_NOTIMPL;
1618 }
1619
1620 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1621 {
1622 xmlwriter *This = impl_from_IXmlWriter(iface);
1623
1624 TRACE("(%p)->(%d)\n", This, standalone);
1625
1626 switch (This->state)
1627 {
1628 case XmlWriterState_Initial:
1629 return E_UNEXPECTED;
1630 case XmlWriterState_PIDocStarted:
1631 This->state = XmlWriterState_DocStarted;
1632 return S_OK;
1633 case XmlWriterState_Ready:
1634 break;
1635 case XmlWriterState_InvalidEncoding:
1636 return MX_E_ENCODING;
1637 default:
1638 This->state = XmlWriterState_DocClosed;
1639 return WR_E_INVALIDACTION;
1640 }
1641
1642 return write_xmldecl(This, standalone);
1643 }
1644
1645 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1646 {
1647 xmlwriter *This = impl_from_IXmlWriter(iface);
1648 int prefix_len, local_len;
1649 struct element *element;
1650 struct ns *ns;
1651 HRESULT hr;
1652
1653 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1654
1655 if (!local_name)
1656 return E_INVALIDARG;
1657
1658 switch (This->state)
1659 {
1660 case XmlWriterState_Initial:
1661 return E_UNEXPECTED;
1662 case XmlWriterState_InvalidEncoding:
1663 return MX_E_ENCODING;
1664 case XmlWriterState_DocClosed:
1665 return WR_E_INVALIDACTION;
1666 case XmlWriterState_ElemStarted:
1667 writer_close_starttag(This);
1668 break;
1669 default:
1670 ;
1671 }
1672
1673 /* Validate prefix and local name */
1674 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1675 return hr;
1676
1677 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1678 return hr;
1679
1680 if (uri && !wcscmp(uri, xmlnsuriW))
1681 {
1682 if (!prefix)
1683 return WR_E_XMLNSPREFIXDECLARATION;
1684
1685 if (!is_empty_string(prefix))
1686 return WR_E_XMLNSURIDECLARATION;
1687 }
1688
1689 ns = writer_find_ns(This, prefix, uri);
1690
1691 element = alloc_element(This, prefix, local_name);
1692 if (!element)
1693 return E_OUTOFMEMORY;
1694
1695 write_encoding_bom(This);
1696 write_node_indent(This);
1697
1698 This->state = XmlWriterState_ElemStarted;
1699 This->starttagopen = 1;
1700
1701 writer_push_element(This, element);
1702
1703 if (!ns && uri)
1704 writer_push_ns(This, prefix, prefix_len, uri);
1705
1706 write_output_buffer_char(This->output, '<');
1707 if (ns)
1708 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1709 else
1710 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1711 writer_inc_indent(This);
1712
1713 return S_OK;
1714 }
1715
1716 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1717 {
1718 static const WCHAR ampW[] = {'&','a','m','p',';'};
1719 static const WCHAR ltW[] = {'&','l','t',';'};
1720 static const WCHAR gtW[] = {'&','g','t',';'};
1721
1722 while (*string)
1723 {
1724 switch (*string)
1725 {
1726 case '<':
1727 write_output_buffer(writer->output, ltW, ARRAY_SIZE(ltW));
1728 break;
1729 case '&':
1730 write_output_buffer(writer->output, ampW, ARRAY_SIZE(ampW));
1731 break;
1732 case '>':
1733 write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
1734 break;
1735 default:
1736 write_output_buffer(writer->output, string, 1);
1737 }
1738
1739 string++;
1740 }
1741 }
1742
1743 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1744 {
1745 xmlwriter *This = impl_from_IXmlWriter(iface);
1746
1747 TRACE("%p %s\n", This, debugstr_w(string));
1748
1749 if (!string)
1750 return S_OK;
1751
1752 switch (This->state)
1753 {
1754 case XmlWriterState_Initial:
1755 return E_UNEXPECTED;
1756 case XmlWriterState_ElemStarted:
1757 writer_close_starttag(This);
1758 break;
1759 case XmlWriterState_Ready:
1760 case XmlWriterState_DocClosed:
1761 This->state = XmlWriterState_DocClosed;
1762 return WR_E_INVALIDACTION;
1763 case XmlWriterState_InvalidEncoding:
1764 return MX_E_ENCODING;
1765 default:
1766 ;
1767 }
1768
1769 This->textnode = 1;
1770 write_escaped_string(This, string);
1771 return S_OK;
1772 }
1773
1774 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1775 {
1776 xmlwriter *This = impl_from_IXmlWriter(iface);
1777
1778 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1779
1780 return E_NOTIMPL;
1781 }
1782
1783 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1784 {
1785 xmlwriter *This = impl_from_IXmlWriter(iface);
1786
1787 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1788
1789 return E_NOTIMPL;
1790 }
1791
1792 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1793 {
1794 xmlwriter *This = impl_from_IXmlWriter(iface);
1795
1796 TRACE("%p\n", This);
1797
1798 return writeroutput_flush_stream(This->output);
1799 }
1800
1801 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1802 {
1803 xmlwriter_QueryInterface,
1804 xmlwriter_AddRef,
1805 xmlwriter_Release,
1806 xmlwriter_SetOutput,
1807 xmlwriter_GetProperty,
1808 xmlwriter_SetProperty,
1809 xmlwriter_WriteAttributes,
1810 xmlwriter_WriteAttributeString,
1811 xmlwriter_WriteCData,
1812 xmlwriter_WriteCharEntity,
1813 xmlwriter_WriteChars,
1814 xmlwriter_WriteComment,
1815 xmlwriter_WriteDocType,
1816 xmlwriter_WriteElementString,
1817 xmlwriter_WriteEndDocument,
1818 xmlwriter_WriteEndElement,
1819 xmlwriter_WriteEntityRef,
1820 xmlwriter_WriteFullEndElement,
1821 xmlwriter_WriteName,
1822 xmlwriter_WriteNmToken,
1823 xmlwriter_WriteNode,
1824 xmlwriter_WriteNodeShallow,
1825 xmlwriter_WriteProcessingInstruction,
1826 xmlwriter_WriteQualifiedName,
1827 xmlwriter_WriteRaw,
1828 xmlwriter_WriteRawChars,
1829 xmlwriter_WriteStartDocument,
1830 xmlwriter_WriteStartElement,
1831 xmlwriter_WriteString,
1832 xmlwriter_WriteSurrogateCharEntity,
1833 xmlwriter_WriteWhitespace,
1834 xmlwriter_Flush
1835 };
1836
1837 /** IXmlWriterOutput **/
1838 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1839 {
1840 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1841
1842 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1843
1844 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1845 IsEqualGUID(riid, &IID_IUnknown))
1846 {
1847 *ppvObject = iface;
1848 }
1849 else
1850 {
1851 WARN("interface %s not implemented\n", debugstr_guid(riid));
1852 *ppvObject = NULL;
1853 return E_NOINTERFACE;
1854 }
1855
1856 IUnknown_AddRef(iface);
1857
1858 return S_OK;
1859 }
1860
1861 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1862 {
1863 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1864 ULONG ref = InterlockedIncrement(&This->ref);
1865 TRACE("(%p)->(%d)\n", This, ref);
1866 return ref;
1867 }
1868
1869 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1870 {
1871 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1872 LONG ref = InterlockedDecrement(&This->ref);
1873
1874 TRACE("(%p)->(%d)\n", This, ref);
1875
1876 if (ref == 0)
1877 {
1878 IMalloc *imalloc = This->imalloc;
1879 if (This->output) IUnknown_Release(This->output);
1880 if (This->stream) ISequentialStream_Release(This->stream);
1881 free_output_buffer(This);
1882 writeroutput_free(This, This->encoding_name);
1883 writeroutput_free(This, This);
1884 if (imalloc) IMalloc_Release(imalloc);
1885 }
1886
1887 return ref;
1888 }
1889
1890 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1891 {
1892 xmlwriteroutput_QueryInterface,
1893 xmlwriteroutput_AddRef,
1894 xmlwriteroutput_Release
1895 };
1896
1897 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1898 {
1899 xmlwriter *writer;
1900 HRESULT hr;
1901
1902 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1903
1904 if (imalloc)
1905 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1906 else
1907 writer = heap_alloc(sizeof(*writer));
1908 if (!writer)
1909 return E_OUTOFMEMORY;
1910
1911 memset(writer, 0, sizeof(*writer));
1912
1913 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1914 writer->ref = 1;
1915 writer->imalloc = imalloc;
1916 if (imalloc) IMalloc_AddRef(imalloc);
1917 writer->bom = TRUE;
1918 writer->conformance = XmlConformanceLevel_Document;
1919 writer->state = XmlWriterState_Initial;
1920 list_init(&writer->elements);
1921
1922 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
1923 IXmlWriter_Release(&writer->IXmlWriter_iface);
1924
1925 TRACE("returning iface %p, hr %#x\n", *obj, hr);
1926
1927 return hr;
1928 }
1929
1930 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1931 const WCHAR *encoding_name, xmlwriteroutput **out)
1932 {
1933 xmlwriteroutput *writeroutput;
1934 HRESULT hr;
1935
1936 *out = NULL;
1937
1938 if (imalloc)
1939 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1940 else
1941 writeroutput = heap_alloc(sizeof(*writeroutput));
1942 if (!writeroutput)
1943 return E_OUTOFMEMORY;
1944
1945 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1946 writeroutput->ref = 1;
1947 writeroutput->imalloc = imalloc;
1948 if (imalloc)
1949 IMalloc_AddRef(imalloc);
1950 writeroutput->encoding = encoding;
1951 writeroutput->stream = NULL;
1952 hr = init_output_buffer(writeroutput);
1953 if (FAILED(hr)) {
1954 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1955 return hr;
1956 }
1957
1958 if (encoding_name) {
1959 unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
1960 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
1961 memcpy(writeroutput->encoding_name, encoding_name, size);
1962 }
1963 else
1964 writeroutput->encoding_name = NULL;
1965 writeroutput->written = 0;
1966
1967 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1968
1969 *out = writeroutput;
1970
1971 TRACE("Created writer output %p\n", *out);
1972
1973 return S_OK;
1974 }
1975
1976 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
1977 IXmlWriterOutput **out)
1978 {
1979 xmlwriteroutput *output;
1980 xml_encoding xml_enc;
1981 HRESULT hr;
1982
1983 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
1984
1985 if (!stream || !out)
1986 return E_INVALIDARG;
1987
1988 *out = NULL;
1989
1990 xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
1991 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
1992 *out = &output->IXmlWriterOutput_iface;
1993
1994 return hr;
1995 }
1996
1997 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
1998 IXmlWriterOutput **out)
1999 {
2000 xmlwriteroutput *output;
2001 xml_encoding xml_enc;
2002 HRESULT hr;
2003
2004 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
2005
2006 if (!stream || !out)
2007 return E_INVALIDARG;
2008
2009 *out = NULL;
2010
2011 xml_enc = get_encoding_from_codepage(codepage);
2012 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
2013 *out = &output->IXmlWriterOutput_iface;
2014
2015 return hr;
2016 }