From 51d934abbc5d483794bc5d1377093ca3d060dd1d Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 30 May 2017 10:09:05 +0200 Subject: [PATCH] webservices: Add support for dictionary strings in the writer. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/msg.c | 18 +++-- dlls/webservices/reader.c | 26 ++++-- dlls/webservices/tests/Makefile.in | 2 +- dlls/webservices/tests/writer.c | 140 +++++++++++++++++++++++++++++++++ dlls/webservices/webservices_private.h | 1 + dlls/webservices/writer.c | 114 +++++++++++++++++++++------ 6 files changed, 263 insertions(+), 38 deletions(-) diff --git a/dlls/webservices/msg.c b/dlls/webservices/msg.c index ac955bd4f13..2c6667ac6bc 100644 --- a/dlls/webservices/msg.c +++ b/dlls/webservices/msg.c @@ -467,17 +467,21 @@ static HRESULT get_env_namespace( WS_ENVELOPE_VERSION ver, WS_XML_STRING *str ) case WS_ENVELOPE_VERSION_SOAP_1_1: str->bytes = (BYTE *)ns_env_1_1; str->length = sizeof(ns_env_1_1)/sizeof(ns_env_1_1[0]) - 1; - return S_OK; + break; case WS_ENVELOPE_VERSION_SOAP_1_2: str->bytes = (BYTE *)ns_env_1_2; str->length = sizeof(ns_env_1_2)/sizeof(ns_env_1_2[0]) - 1; - return S_OK; + break; default: ERR( "unhandled envelope version %u\n", ver ); return E_NOTIMPL; } + + str->dictionary = NULL; + str->id = 0; + return S_OK; } static HRESULT get_addr_namespace( WS_ADDRESSING_VERSION ver, WS_XML_STRING *str ) @@ -487,22 +491,26 @@ static HRESULT get_addr_namespace( WS_ADDRESSING_VERSION ver, WS_XML_STRING *str case WS_ADDRESSING_VERSION_0_9: str->bytes = (BYTE *)ns_addr_0_9; str->length = sizeof(ns_addr_0_9)/sizeof(ns_addr_0_9[0]) - 1; - return S_OK; + break; case WS_ADDRESSING_VERSION_1_0: str->bytes = (BYTE *)ns_addr_1_0; str->length = sizeof(ns_addr_1_0)/sizeof(ns_addr_1_0[0]) - 1; - return S_OK; + break; case WS_ADDRESSING_VERSION_TRANSPORT: str->bytes = NULL; str->length = 0; - return S_OK; + break; default: ERR( "unhandled addressing version %u\n", ver ); return E_NOTIMPL; } + + str->dictionary = NULL; + str->id = 0; + return S_OK; } static const WS_XML_STRING *get_header_name( WS_HEADER_TYPE type ) diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index 7624eb9b5ac..6dd6de45db5 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -161,9 +161,9 @@ static WS_XML_ATTRIBUTE *dup_attribute( const WS_XML_ATTRIBUTE *src ) dst->isXmlNs = src->isXmlNs; if (!prefix) dst->prefix = NULL; - else if (!(dst->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) goto error; - if (!(dst->localName = alloc_xml_string( localname->bytes, localname->length ))) goto error; - if (!(dst->ns = alloc_xml_string( ns->bytes, ns->length ))) goto error; + else if (!(dst->prefix = dup_xml_string( prefix ))) goto error; + if (!(dst->localName = dup_xml_string( localname ))) goto error; + if (!(dst->ns = dup_xml_string( ns ))) goto error; if (text) { @@ -214,9 +214,9 @@ static struct node *dup_element_node( const WS_XML_ELEMENT_NODE *src ) if (count && !(dst->attributes = dup_attributes( attrs, count ))) goto error; dst->attributeCount = count; - if (prefix && !(dst->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) goto error; - if (localname && !(dst->localName = alloc_xml_string( localname->bytes, localname->length ))) goto error; - if (ns && !(dst->ns = alloc_xml_string( ns->bytes, ns->length ))) goto error; + if (prefix && !(dst->prefix = dup_xml_string( prefix ))) goto error; + if (localname && !(dst->localName = dup_xml_string( localname ))) goto error; + if (ns && !(dst->ns = dup_xml_string( ns ))) goto error; return node; error: @@ -797,6 +797,20 @@ WS_XML_STRING *alloc_xml_string( const unsigned char *data, ULONG len ) return ret; } +WS_XML_STRING *dup_xml_string( const WS_XML_STRING *src ) +{ + WS_XML_STRING *ret; + + if (!src->dictionary) return alloc_xml_string( src->bytes, src->length ); + + if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL; + ret->length = src->length; + ret->bytes = src->bytes; + ret->dictionary = src->dictionary; + ret->id = src->id; + return ret; +} + WS_XML_UTF8_TEXT *alloc_utf8_text( const unsigned char *data, ULONG len ) { WS_XML_UTF8_TEXT *ret; diff --git a/dlls/webservices/tests/Makefile.in b/dlls/webservices/tests/Makefile.in index 1adfecc272e..9b2e68812b9 100644 --- a/dlls/webservices/tests/Makefile.in +++ b/dlls/webservices/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = webservices.dll -IMPORTS = webservices user32 ws2_32 +IMPORTS = webservices user32 rpcrt4 ws2_32 C_SRCS = \ channel.c \ diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index 2102c153b26..3bf0886079f 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -18,6 +18,7 @@ #include #include "windows.h" +#include "rpc.h" #include "webservices.h" #include "wine/test.h" @@ -3592,6 +3593,144 @@ static void test_namespaces(void) WsFreeWriter( writer ); } +static const WS_XML_STRING *init_xmlstring_dict( WS_XML_DICTIONARY *dict, ULONG id, WS_XML_STRING *str ) +{ + if (id >= dict->stringCount) return NULL; + str->length = dict->strings[id].length; + str->bytes = dict->strings[id].bytes; + str->dictionary = dict; + str->id = id; + return str; +} + +static void test_dictionary(void) +{ + static const char res[] = + {0x42,0x04,0x01}; + static const char res2[] = + {0x42,0x06,0x01}; + static const char res3[] = + {0x53,0x06,0x0b,0x01,'p',0x0a,0x01}; + static const char res4[] = + {0x43,0x02,'p','2',0x06,0x0b,0x02,'p','2',0x0a,0x01}; + static const char res100[] = + {0x42,0x06,0x06,0x06,0x98,0x00,0x01}; + static const char res101[] = + {0x42,0x06,0x1b,0x06,0x98,0x00,0x0b,0x01,'p',0x0a,0x01}; + static const char res102[] = + {0x42,0x06,0x07,0x02,'p','2',0x06,0x98,0x00,0x0b,0x02,'p','2',0x0a,0x01}; + WS_XML_WRITER_BINARY_ENCODING bin = {{WS_XML_WRITER_ENCODING_TYPE_BINARY}}; + WS_XML_WRITER_BUFFER_OUTPUT buf = {{WS_XML_WRITER_OUTPUT_TYPE_BUFFER}}; + WS_XML_STRING prefix, localname, ns, strings[6]; + const WS_XML_STRING *prefix_ptr, *localname_ptr, *ns_ptr; + WS_XML_DICTIONARY dict; + WS_XML_WRITER *writer; + HRESULT hr; + ULONG i; + static const struct + { + ULONG prefix; + ULONG localname; + ULONG ns; + const char *result; + int len_result; + } + elem_tests[] = + { + { ~0u, 2, 0, res, sizeof(res) }, /* short dictionary element, invalid dict id */ + { ~0u, 3, 0, res2, sizeof(res2) }, /* short dictionary element */ + { 1, 3, 5, res3, sizeof(res3) }, /* single character prefix dictionary element */ + { 4, 3, 5, res4, sizeof(res4) }, /* dictionary element */ + }; + static const struct + { + ULONG prefix; + ULONG localname; + ULONG ns; + const char *result; + int len_result; + } + attr_tests[] = + { + { ~0u, 3, 0, res100, sizeof(res100) }, /* short dictionary attribute */ + { 1, 3, 5, res101, sizeof(res101) }, /* single character prefix dictionary attribute */ + { 4, 3, 5, res102, sizeof(res102) }, /* dictionary attribute */ + }; + + hr = WsCreateWriter( NULL, 0, &writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + strings[0].length = 0; + strings[0].bytes = NULL; + strings[0].dictionary = &dict; + strings[0].id = 0; + strings[1].length = 1; + strings[1].bytes = (BYTE *)"p"; + strings[1].dictionary = &dict; + strings[1].id = 1; + strings[2].length = 1; + strings[2].bytes = (BYTE *)"t"; + strings[2].dictionary = &dict; + strings[2].id = ~0u; + strings[3].length = 1; + strings[3].bytes = (BYTE *)"u"; + strings[3].dictionary = &dict; + strings[3].id = 3; + strings[4].length = 2; + strings[4].bytes = (BYTE *)"p2"; + strings[4].dictionary = &dict; + strings[4].id = 4; + strings[5].length = 2; + strings[5].bytes = (BYTE *)"ns"; + strings[5].dictionary = &dict; + strings[5].id = 5; + + UuidCreate( &dict.guid ); + dict.strings = strings; + dict.stringCount = sizeof(strings)/sizeof(strings[0]); + dict.isConst = TRUE; + + bin.staticDictionary = &dict; + + for (i = 0; i < sizeof(elem_tests)/sizeof(elem_tests[0]); i++) + { + hr = WsSetOutput( writer, &bin.encoding, &buf.output, NULL, 0, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + prefix_ptr = init_xmlstring_dict( &dict, elem_tests[i].prefix, &prefix ); + localname_ptr = init_xmlstring_dict( &dict, elem_tests[i].localname, &localname ); + ns_ptr = init_xmlstring_dict( &dict, elem_tests[i].ns, &ns ); + + hr = WsWriteStartElement( writer, prefix_ptr, localname_ptr, ns_ptr, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + if (hr == S_OK) check_output_bin( writer, elem_tests[i].result, elem_tests[i].len_result, __LINE__ ); + } + + for (i = 0; i < sizeof(attr_tests)/sizeof(attr_tests[0]); i++) + { + hr = WsSetOutput( writer, &bin.encoding, &buf.output, NULL, 0, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + + prefix_ptr = init_xmlstring_dict( &dict, attr_tests[i].prefix, &prefix ); + localname_ptr = init_xmlstring_dict( &dict, attr_tests[i].localname, &localname ); + ns_ptr = init_xmlstring_dict( &dict, attr_tests[i].ns, &ns ); + + hr = WsWriteStartElement( writer, NULL, &strings[3], &strings[0], NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteStartAttribute( writer, prefix_ptr, localname_ptr, ns_ptr, FALSE, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + hr = WsWriteEndAttribute( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "%u: got %08x\n", i, hr ); + if (hr == S_OK) check_output_bin( writer, attr_tests[i].result, attr_tests[i].len_result, __LINE__ ); + } + + WsFreeWriter( writer ); +} + START_TEST(writer) { test_WsCreateWriter(); @@ -3631,4 +3770,5 @@ START_TEST(writer) test_WsWriteCharsUtf8(); test_binary_encoding(); test_namespaces(); + test_dictionary(); } diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h index b1b2caa0eb4..d117ebaf048 100644 --- a/dlls/webservices/webservices_private.h +++ b/dlls/webservices/webservices_private.h @@ -35,6 +35,7 @@ void free_xmlbuf( struct xmlbuf * ) DECLSPEC_HIDDEN; const char *debugstr_xmlstr( const WS_XML_STRING * ) DECLSPEC_HIDDEN; WS_XML_STRING *alloc_xml_string( const unsigned char *, ULONG ) DECLSPEC_HIDDEN; +WS_XML_STRING *dup_xml_string( const WS_XML_STRING * ) DECLSPEC_HIDDEN; WS_XML_UTF8_TEXT *alloc_utf8_text( const unsigned char *, ULONG ) DECLSPEC_HIDDEN; HRESULT append_attribute( WS_XML_ELEMENT_NODE *, WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN; void free_attribute( WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN; diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index bbd969d6118..d22d5e49f33 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -566,20 +566,12 @@ static HRESULT write_attribute_text( struct writer *writer, const WS_XML_ATTRIBU return hr; } -static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr ) -{ - if (!attr->prefix || !attr->prefix->length) return RECORD_SHORT_ATTRIBUTE; - if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z') - { - return RECORD_PREFIX_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a'; - } - return RECORD_ATTRIBUTE; -}; - static HRESULT write_int31( struct writer *writer, ULONG len ) { HRESULT hr; + if (len > 0x7fffffff) return E_INVALIDARG; + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; if (len < 0x80) { @@ -630,6 +622,14 @@ static HRESULT write_string( struct writer *writer, const BYTE *bytes, ULONG len return S_OK; } +static HRESULT write_dict_string( struct writer *writer, ULONG id ) +{ + HRESULT hr; + if (id > 0x3fffffff) return E_INVALIDARG; + if ((hr = write_int31( writer, id << 1 )) != S_OK) return hr; + return S_OK; +} + static enum record_type get_text_record_type( const WS_XML_TEXT *text, BOOL attr ) { const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; @@ -660,11 +660,27 @@ static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TE return S_OK; default: - ERR( "unhandled record type %u\n", type ); + ERR( "unhandled record type %02x\n", type ); return WS_E_NOT_SUPPORTED; } } +static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr ) +{ + if (!attr->prefix || !attr->prefix->length) + { + if (attr->localName->dictionary) return RECORD_SHORT_DICTIONARY_ATTRIBUTE; + return RECORD_SHORT_ATTRIBUTE; + } + if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z') + { + if (attr->localName->dictionary) return RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a'; + return RECORD_PREFIX_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a'; + } + if (attr->localName->dictionary) return RECORD_DICTIONARY_ATTRIBUTE; + return RECORD_ATTRIBUTE; +}; + static HRESULT write_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUTE *attr ) { enum record_type type = get_attr_record_type( attr ); @@ -678,6 +694,11 @@ static HRESULT write_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUT if ((hr = write_string( writer, attr->localName->bytes, attr->localName->length )) != S_OK) return hr; return write_attribute_value_bin( writer, attr->value ); } + if (type >= RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A && type <= RECORD_PREFIX_DICTIONARY_ATTRIBUTE_Z) + { + if ((hr = write_dict_string( writer, attr->localName->id )) != S_OK) return hr; + return write_attribute_value_bin( writer, attr->value ); + } switch (type) { @@ -690,8 +711,17 @@ static HRESULT write_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUT if ((hr = write_string( writer, attr->localName->bytes, attr->localName->length )) != S_OK) return hr; break; + case RECORD_SHORT_DICTIONARY_ATTRIBUTE: + if ((hr = write_dict_string( writer, attr->localName->id )) != S_OK) return hr; + break; + + case RECORD_DICTIONARY_ATTRIBUTE: + if ((hr = write_string( writer, attr->prefix->bytes, attr->prefix->length )) != S_OK) return hr; + if ((hr = write_dict_string( writer, attr->localName->id )) != S_OK) return hr; + break; + default: - ERR( "unhandled record type %u\n", type ); + ERR( "unhandled record type %02x\n", type ); return WS_E_NOT_SUPPORTED; } @@ -789,7 +819,12 @@ static HRESULT write_namespace_attribute_text( struct writer *writer, const WS_X static enum record_type get_xmlns_record_type( const WS_XML_ATTRIBUTE *attr ) { - if (!attr->prefix || !attr->prefix->length) return RECORD_SHORT_XMLNS_ATTRIBUTE; + if (!attr->prefix || !attr->prefix->length) + { + if (attr->ns->dictionary) return RECORD_SHORT_DICTIONARY_XMLNS_ATTRIBUTE; + return RECORD_SHORT_XMLNS_ATTRIBUTE; + } + if (attr->ns->dictionary) return RECORD_DICTIONARY_XMLNS_ATTRIBUTE; return RECORD_XMLNS_ATTRIBUTE; }; @@ -810,8 +845,15 @@ static HRESULT write_namespace_attribute_bin( struct writer *writer, const WS_XM if ((hr = write_string( writer, attr->prefix->bytes, attr->prefix->length )) != S_OK) return hr; break; + case RECORD_SHORT_DICTIONARY_XMLNS_ATTRIBUTE: + return write_dict_string( writer, attr->ns->id ); + + case RECORD_DICTIONARY_XMLNS_ATTRIBUTE: + if ((hr = write_string( writer, attr->prefix->bytes, attr->prefix->length )) != S_OK) return hr; + return write_dict_string( writer, attr->ns->id ); + default: - ERR( "unhandled record type %u\n", type ); + ERR( "unhandled record type %02x\n", type ); return WS_E_NOT_SUPPORTED; } @@ -841,12 +883,12 @@ static HRESULT add_namespace_attribute( struct writer *writer, const WS_XML_STRI attr->singleQuote = !!single; attr->isXmlNs = 1; - if (prefix && !(attr->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) + if (prefix && !(attr->prefix = dup_xml_string( prefix ))) { free_attribute( attr ); return E_OUTOFMEMORY; } - if (!(attr->ns = alloc_xml_string( ns->bytes, ns->length ))) + if (!(attr->ns = dup_xml_string( ns ))) { free_attribute( attr ); return E_OUTOFMEMORY; @@ -889,7 +931,7 @@ static BOOL namespace_in_scope( const WS_XML_ELEMENT_NODE *elem, const WS_XML_ST static HRESULT set_current_namespace( struct writer *writer, const WS_XML_STRING *ns ) { WS_XML_STRING *str; - if (!(str = alloc_xml_string( ns->bytes, ns->length ))) return E_OUTOFMEMORY; + if (!(str = dup_xml_string( ns ))) return E_OUTOFMEMORY; heap_free( writer->current_ns ); writer->current_ns = str; return S_OK; @@ -989,11 +1031,17 @@ static HRESULT write_startelement_text( struct writer *writer ) static enum record_type get_elem_record_type( const WS_XML_ELEMENT_NODE *elem ) { - if (!elem->prefix || !elem->prefix->length) return RECORD_SHORT_ELEMENT; + if (!elem->prefix || !elem->prefix->length) + { + if (elem->localName->dictionary) return RECORD_SHORT_DICTIONARY_ELEMENT; + return RECORD_SHORT_ELEMENT; + } if (elem->prefix->length == 1 && elem->prefix->bytes[0] >= 'a' && elem->prefix->bytes[0] <= 'z') { + if (elem->localName->dictionary) return RECORD_PREFIX_DICTIONARY_ELEMENT_A + elem->prefix->bytes[0] - 'a'; return RECORD_PREFIX_ELEMENT_A + elem->prefix->bytes[0] - 'a'; } + if (elem->localName->dictionary) return RECORD_DICTIONARY_ELEMENT; return RECORD_ELEMENT; }; @@ -1011,6 +1059,11 @@ static HRESULT write_startelement_bin( struct writer *writer ) if ((hr = write_string( writer, elem->localName->bytes, elem->localName->length )) != S_OK) return hr; return write_attributes( writer, elem ); } + if (type >= RECORD_PREFIX_DICTIONARY_ELEMENT_A && type <= RECORD_PREFIX_DICTIONARY_ELEMENT_Z) + { + if ((hr = write_dict_string( writer, elem->localName->id )) != S_OK) return hr; + return write_attributes( writer, elem ); + } switch (type) { @@ -1023,8 +1076,17 @@ static HRESULT write_startelement_bin( struct writer *writer ) if ((hr = write_string( writer, elem->localName->bytes, elem->localName->length )) != S_OK) return hr; break; + case RECORD_SHORT_DICTIONARY_ELEMENT: + if ((hr = write_dict_string( writer, elem->localName->id )) != S_OK) return hr; + break; + + case RECORD_DICTIONARY_ELEMENT: + if ((hr = write_string( writer, elem->prefix->bytes, elem->prefix->length )) != S_OK) return hr; + if ((hr = write_dict_string( writer, elem->localName->id )) != S_OK) return hr; + break; + default: - ERR( "unhandled record type %u\n", type ); + ERR( "unhandled record type %02x\n", type ); return WS_E_NOT_SUPPORTED; } @@ -1234,17 +1296,17 @@ static HRESULT write_add_attribute( struct writer *writer, const WS_XML_STRING * if (!prefix && ns->length) prefix = elem->prefix; attr->singleQuote = !!single; - if (prefix && !(attr->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) + if (prefix && !(attr->prefix = dup_xml_string( prefix ))) { free_attribute( attr ); return E_OUTOFMEMORY; } - if (!(attr->localName = alloc_xml_string( localname->bytes, localname->length ))) + if (!(attr->localName = dup_xml_string( localname ))) { free_attribute( attr ); return E_OUTOFMEMORY; } - if (!(attr->ns = alloc_xml_string( ns->bytes, ns->length ))) + if (!(attr->ns = dup_xml_string( ns ))) { free_attribute( attr ); return E_OUTOFMEMORY; @@ -1439,17 +1501,17 @@ static HRESULT write_add_element_node( struct writer *writer, const WS_XML_STRIN if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY; elem = &node->hdr; - if (prefix && !(elem->prefix = alloc_xml_string( prefix->bytes, prefix->length ))) + if (prefix && !(elem->prefix = dup_xml_string( prefix ))) { free_node( node ); return E_OUTOFMEMORY; } - if (!(elem->localName = alloc_xml_string( localname->bytes, localname->length ))) + if (!(elem->localName = dup_xml_string( localname ))) { free_node( node ); return E_OUTOFMEMORY; } - if (!(elem->ns = alloc_xml_string( ns->bytes, ns->length ))) + if (!(elem->ns = dup_xml_string( ns ))) { free_node( node ); return E_OUTOFMEMORY; @@ -1999,7 +2061,7 @@ static HRESULT write_text_bin( struct writer *writer, const WS_XML_TEXT *text, U return S_OK; default: - FIXME( "unhandled record type %u\n", type ); + FIXME( "unhandled record type %02x\n", type ); return WS_E_NOT_SUPPORTED; } } -- 2.11.4.GIT