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 } 2017