[1/5] webservices: Implement WsReadToStartElement.

Hans Leidekker hans at codeweavers.com
Fri Oct 9 03:19:28 CDT 2015


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/webservices/reader.c         | 274 ++++++++++++++++++++++++++++++++++++++
 dlls/webservices/tests/reader.c   | 149 +++++++++++++++++++++
 dlls/webservices/webservices.spec |   2 +-
 3 files changed, 424 insertions(+), 1 deletion(-)

diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c
index a9219ee..0f2bc00 100644
--- a/dlls/webservices/reader.c
+++ b/dlls/webservices/reader.c
@@ -28,6 +28,12 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(webservices);
 
+static const char *debugstr_xmlstr( const WS_XML_STRING *str )
+{
+    if (!str) return "(null)";
+    return debugstr_an( (const char *)str->bytes, str->length );
+}
+
 static inline void *heap_alloc( SIZE_T size )
 {
     return HeapAlloc( GetProcessHeap(), 0, size );
@@ -334,10 +340,22 @@ reader_props[] =
     { sizeof(ULONG), FALSE },      /* WS_XML_READER_PROPERTY_MAX_NAMESPACES */
 };
 
+enum reader_state
+{
+    READER_STATE_INITIAL,
+    READER_STATE_BOF,
+    READER_STATE_STARTELEMENT,
+    READER_STATE_TEXT,
+    READER_STATE_ENDELEMENT,
+    READER_STATE_EOF
+};
+
 struct reader
 {
     ULONG                   read_size;
     ULONG                   read_pos;
+    const char             *read_bufptr;
+    enum reader_state       state;
     struct list             nodes;
     struct node            *current;
     const char             *input_data;
@@ -428,6 +446,7 @@ HRESULT WINAPI WsCreateReader( const WS_XML_READER_PROPERTY *properties, ULONG c
     list_init( &reader->nodes );
     list_add_tail( &reader->nodes, &node->entry );
     reader->current = node;
+    reader->state   = READER_STATE_INITIAL;
 
     *handle = (WS_XML_READER *)reader;
     return S_OK;
@@ -525,6 +544,259 @@ HRESULT WINAPI WsGetReaderProperty( WS_XML_READER *handle, WS_XML_READER_PROPERT
     return get_reader_prop( reader, id, buf, size );
 }
 
+static WS_XML_STRING *alloc_xml_string( const char *data, ULONG len )
+{
+    WS_XML_STRING *ret;
+
+    if (!(ret = heap_alloc( sizeof(*ret) + len ))) return NULL;
+    ret->length     = len;
+    ret->bytes      = (BYTE *)(ret + 1);
+    ret->dictionary = NULL;
+    ret->id         = 0;
+    if (data) memcpy( ret->bytes, data, len );
+    return ret;
+}
+
+static inline BOOL read_end_of_data( struct reader *reader )
+{
+    return reader->read_pos >= reader->read_size;
+}
+
+static inline const char *read_current_ptr( struct reader *reader )
+{
+    return &reader->read_bufptr[reader->read_pos];
+}
+
+/* UTF-8 support based on libs/wine/utf8.c */
+
+/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
+static const char utf8_length[128] =
+{
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80-0x8f */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90-0x9f */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0-0xaf */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb0-0xbf */
+    0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xc0-0xcf */
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xd0-0xdf */
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xe0-0xef */
+    3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0  /* 0xf0-0xff */
+};
+
+/* first byte mask depending on UTF-8 sequence length */
+static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
+
+/* minimum Unicode value depending on UTF-8 sequence length */
+static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 };
+
+static inline unsigned int read_utf8_char( struct reader *reader, unsigned int *skip )
+{
+    unsigned int len, res;
+    unsigned char ch = reader->read_bufptr[reader->read_pos];
+    const char *end;
+
+    if (reader->read_pos >= reader->read_size) return 0;
+
+    if (ch < 0x80)
+    {
+        *skip = 1;
+        return ch;
+    }
+    len = utf8_length[ch - 0x80];
+    if (reader->read_pos + len >= reader->read_size) return 0;
+    end = reader->read_bufptr + reader->read_pos + len;
+    res = ch & utf8_mask[len];
+
+    switch (len)
+    {
+    case 3:
+        if ((ch = end[-3] ^ 0x80) >= 0x40) break;
+        res = (res << 6) | ch;
+    case 2:
+        if ((ch = end[-2] ^ 0x80) >= 0x40) break;
+        res = (res << 6) | ch;
+    case 1:
+        if ((ch = end[-1] ^ 0x80) >= 0x40) break;
+        res = (res << 6) | ch;
+        if (res < utf8_minval[len]) break;
+        *skip = len + 1;
+        return res;
+    }
+
+    return 0;
+}
+
+static inline void read_skip( struct reader *reader, unsigned int count )
+{
+    while (reader->read_pos < reader->read_size && count)
+    {
+        reader->read_pos++;
+        count--;
+    }
+}
+
+static inline BOOL read_isnamechar( unsigned int ch )
+{
+    /* FIXME: incomplete */
+    return (ch >= 'A' && ch <= 'Z') ||
+           (ch >= 'a' && ch <= 'z') ||
+           (ch >= '0' && ch <= '9') ||
+           ch == '_' || ch == '-' || ch == '.' || ch == ':';
+}
+
+static inline BOOL read_isspace( unsigned int ch )
+{
+    return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
+}
+
+static inline void read_skip_whitespace( struct reader *reader )
+{
+    while (reader->read_pos < reader->read_size && read_isspace( reader->read_bufptr[reader->read_pos] ))
+        reader->read_pos++;
+}
+
+static inline int read_cmp( struct reader *reader, const char *str, int len )
+{
+    const char *ptr = read_current_ptr( reader );
+
+    if (len < 0) len = strlen( str );
+    if (reader->read_pos + len > reader->read_size) return -1;
+    while (len--)
+    {
+        if (*str != *ptr) return *ptr - *str;
+        str++; ptr++;
+    }
+    return 0;
+}
+
+static HRESULT read_xmldecl( struct reader *reader )
+{
+    if (!reader->read_size) return WS_E_INVALID_FORMAT;
+
+    if (read_cmp( reader, "<", 1 ) || read_cmp( reader, "<?", 2 ))
+    {
+        reader->state = READER_STATE_BOF;
+        return S_OK;
+    }
+    if (read_cmp( reader, "<?xml ", 6 )) return WS_E_INVALID_FORMAT;
+    read_skip( reader, 6 );
+
+    /* FIXME: parse attributes */
+    while (reader->read_pos < reader->read_size && reader->read_bufptr[reader->read_pos] != '?')
+        reader->read_pos++;
+
+    if (read_cmp( reader, "?>", 2 )) return WS_E_INVALID_FORMAT;
+    read_skip( reader, 2 );
+
+    reader->state = READER_STATE_BOF;
+    return S_OK;
+}
+
+static HRESULT read_element( struct reader *reader )
+{
+    unsigned int len = 0, ch, skip;
+    const char *start;
+    struct node *node;
+    WS_XML_ELEMENT_NODE *elem;
+    HRESULT hr = WS_E_INVALID_FORMAT;
+
+    if (read_end_of_data( reader ))
+    {
+        reader->current = LIST_ENTRY( list_tail( &reader->nodes ), struct node, entry );
+        reader->state   = READER_STATE_EOF;
+        return S_OK;
+    }
+
+    if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY;
+    elem = (WS_XML_ELEMENT_NODE *)node;
+
+    if (read_cmp( reader, "<", 1 )) goto error;
+    read_skip( reader, 1 );
+
+    start = read_current_ptr( reader );
+    for (;;)
+    {
+        if (!(ch = read_utf8_char( reader, &skip ))) goto error;
+        if (!read_isnamechar( ch )) break;
+        read_skip( reader, skip );
+        len += skip;
+    }
+    if (!len) goto error;
+
+    hr = E_OUTOFMEMORY;
+    if (!(elem->localName = alloc_xml_string( start, len ))) goto error;
+    if (!(elem->prefix = alloc_xml_string( NULL, 0 ))) goto error;
+    elem->prefix->bytes = NULL;
+    if (!(elem->ns = alloc_xml_string( NULL, 0 ))) goto error;
+
+    /* FIXME: parse attributes */
+    while (reader->read_pos < reader->read_size && reader->read_bufptr[reader->read_pos] != '>')
+        reader->read_pos++;
+
+    if (read_cmp( reader, ">", 1 ))
+    {
+        hr = WS_E_INVALID_FORMAT;
+        goto error;
+    }
+    read_skip( reader, 1 );
+
+    list_add_after( &reader->current->entry, &node->entry );
+    reader->current = node;
+    reader->state   = READER_STATE_STARTELEMENT;
+    return S_OK;
+
+error:
+    free_node( node );
+    return hr;
+}
+
+static HRESULT read_to_startelement( struct reader *reader, BOOL *found )
+{
+    HRESULT hr;
+
+    switch (reader->state)
+    {
+    case READER_STATE_INITIAL:
+    {
+        if ((hr = read_xmldecl( reader )) != S_OK) return hr;
+        break;
+    }
+    case READER_STATE_STARTELEMENT:
+        if (found) *found = TRUE;
+        return S_OK;
+
+    default:
+        break;
+    }
+
+    read_skip_whitespace( reader );
+    if ((hr = read_element( reader )) == S_OK && found)
+    {
+        if (reader->state == READER_STATE_STARTELEMENT)
+            *found = TRUE;
+        else
+            *found = FALSE;
+    }
+
+    return hr;
+}
+
+/**************************************************************************
+ *          WsReadToStartElement		[webservices.@]
+ */
+HRESULT WINAPI WsReadToStartElement( WS_XML_READER *handle, const WS_XML_STRING *localname,
+                                     const WS_XML_STRING *ns, BOOL *found, WS_ERROR *error )
+{
+    struct reader *reader = (struct reader *)handle;
+
+    TRACE( "%p %s %s %p %p\n", handle, debugstr_xmlstr(localname), debugstr_xmlstr(ns), found, error );
+    if (error) FIXME( "ignoring error parameter\n" );
+
+    if (!reader) return E_INVALIDARG;
+    if (localname || ns) FIXME( "name and/or namespace not verified\n" );
+
+    return read_to_startelement( reader, found );
+}
+
 /**************************************************************************
  *          WsSetErrorProperty		[webservices.@]
  */
@@ -579,6 +851,7 @@ HRESULT WINAPI WsSetInput( WS_XML_READER *handle, const WS_XML_READER_ENCODING *
             WS_XML_READER_BUFFER_INPUT *buf = (WS_XML_READER_BUFFER_INPUT *)input;
             reader->input_data = buf->encodedData;
             reader->input_size = buf->encodedDataSize;
+            reader->read_bufptr = reader->input_data;
             break;
         }
         default:
@@ -595,6 +868,7 @@ HRESULT WINAPI WsSetInput( WS_XML_READER *handle, const WS_XML_READER_ENCODING *
     if (!(node = alloc_node( WS_XML_NODE_TYPE_BOF ))) return E_OUTOFMEMORY;
     list_add_head( &reader->nodes, &node->entry );
     reader->current = node;
+    reader->state   = READER_STATE_INITIAL;
 
     return S_OK;
 }
diff --git a/dlls/webservices/tests/reader.c b/dlls/webservices/tests/reader.c
index e2ab39a..d31a182 100644
--- a/dlls/webservices/tests/reader.c
+++ b/dlls/webservices/tests/reader.c
@@ -24,6 +24,27 @@
 static const char data1[] =
     "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
 
+static const char data2[] =
+    "<text>test</text>";
+
+static const char data3[] =
+    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+    "<text>test</text>";
+
+static const char data4[] =
+    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
+    "<o:OfficeConfig xmlns:o=\"urn:schemas-microsoft-com:office:office\">\r\n"
+    " <o:services o:GenerationTime=\"2015-09-03T18:47:54\">\r\n"
+    "  <!--Build: 16.0.6202.6852-->\r\n"
+    "  <o:default>\r\n"
+    "   <o:ticket o:headerName=\"Authorization\" o:headerValue=\"{}\" />\r\n"
+    "  </o:default>\r\n"
+    "  <o:service o:name=\"LiveOAuthLoginStart\">\r\n"
+    "   <o:url>https://login.[Live.WebHost]/oauth20_authorize.srf</o:url>\r\n"
+    "  </o:service>\r\n"
+    "</o:services>\r\n"
+    "</o:OfficeConfig>\r\n";
+
 static void test_WsCreateError(void)
 {
     HRESULT hr;
@@ -450,6 +471,133 @@ static void test_WsFillReader(void)
     WsFreeReader( reader );
 }
 
+static void test_WsReadToStartElement(void)
+{
+    HRESULT hr;
+    WS_XML_READER *reader;
+    const WS_XML_NODE *node, *node2;
+    int found;
+
+    hr = WsCreateReader( NULL, 0, &reader, NULL ) ;
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = set_input( reader, data1, sizeof(data1) - 1 );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsFillReader( reader, sizeof(data1) - 1, NULL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsGetReaderNode( reader, &node, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    if (node) ok( node->nodeType == WS_XML_NODE_TYPE_BOF, "got %u\n", node->nodeType );
+
+    hr = WsFillReader( reader, sizeof(data1) - 1, NULL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsReadToStartElement( NULL, NULL, NULL, NULL, NULL );
+    ok( hr == E_INVALIDARG, "got %08x\n", hr );
+
+    found = -1;
+    hr = WsReadToStartElement( reader, NULL, NULL, &found, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( found == FALSE, "got %d\n", found );
+
+    hr = WsGetReaderNode( reader, &node, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    if (node) ok( node->nodeType == WS_XML_NODE_TYPE_EOF, "got %u\n", node->nodeType );
+
+    hr = set_input( reader, data2, sizeof(data2) - 1 );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsFillReader( reader, sizeof(data2) - 1, NULL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsGetReaderNode( reader, &node, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    if (node) ok( node->nodeType == WS_XML_NODE_TYPE_BOF, "got %u\n", node->nodeType );
+
+    found = -1;
+    hr = WsReadToStartElement( reader, NULL, NULL, &found, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( found == TRUE, "got %d\n", found );
+
+    hr = WsGetReaderNode( reader, &node, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    if (node)
+    {
+        WS_XML_ELEMENT_NODE *elem = (WS_XML_ELEMENT_NODE *)node;
+
+        ok( elem->node.nodeType == WS_XML_NODE_TYPE_ELEMENT, "got %u\n", elem->node.nodeType );
+        ok( elem->prefix != NULL, "prefix not set\n" );
+        if (elem->prefix)
+        {
+            ok( !elem->prefix->length, "got %u\n", elem->prefix->length );
+        }
+        ok( elem->localName != NULL, "localName not set\n" );
+        if (elem->localName)
+        {
+            ok( elem->localName->length == 4, "got %u\n", elem->localName->length );
+            ok( !memcmp( elem->localName->bytes, "text", 4 ), "wrong data\n" );
+        }
+        ok( elem->ns != NULL, "ns not set\n" );
+        if (elem->ns)
+        {
+            ok( !elem->ns->length, "got %u\n", elem->ns->length );
+        }
+        ok( !elem->attributeCount, "got %u\n", elem->attributeCount );
+        ok( elem->attributes == NULL, "attributes set\n" );
+        ok( !elem->isEmpty, "isEmpty not zero\n" );
+    }
+
+    found = -1;
+    hr = WsReadToStartElement( reader, NULL, NULL, &found, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( found == TRUE, "got %d\n", found );
+
+    node2 = NULL;
+    hr = WsGetReaderNode( reader, &node2, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( node2 == node, "different node\n" );
+
+    hr = set_input( reader, data3, sizeof(data3) - 1 );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsFillReader( reader, sizeof(data3) - 1, NULL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    found = -1;
+    hr = WsReadToStartElement( reader, NULL, NULL, &found, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( found == TRUE, "got %d\n", found );
+
+    hr = WsGetReaderNode( reader, &node, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    if (node)
+    {
+        WS_XML_ELEMENT_NODE *elem = (WS_XML_ELEMENT_NODE *)node;
+
+        ok( elem->node.nodeType == WS_XML_NODE_TYPE_ELEMENT, "got %u\n", elem->node.nodeType );
+        ok( elem->localName != NULL, "localName not set\n" );
+        if (elem->localName)
+        {
+            ok( elem->localName->length == 4, "got %u\n", elem->localName->length );
+            ok( !memcmp( elem->localName->bytes, "text", 4 ), "wrong data\n" );
+        }
+    }
+
+    hr = set_input( reader, data4, sizeof(data4) - 1 );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsFillReader( reader, sizeof(data4) - 1, NULL, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    found = -1;
+    hr = WsReadToStartElement( reader, NULL, NULL, &found, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( found == TRUE, "got %d\n", found );
+    WsFreeReader( reader );
+}
+
 START_TEST(reader)
 {
     test_WsCreateError();
@@ -457,4 +605,5 @@ START_TEST(reader)
     test_WsCreateReader();
     test_WsSetInput();
     test_WsFillReader();
+    test_WsReadToStartElement();
 }
diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec
index e8a0c7f..791d4be 100644
--- a/dlls/webservices/webservices.spec
+++ b/dlls/webservices/webservices.spec
@@ -120,7 +120,7 @@
 @ stub WsReadQualifiedName
 @ stub WsReadStartAttribute
 @ stub WsReadStartElement
-@ stub WsReadToStartElement
+@ stdcall WsReadToStartElement(ptr ptr ptr ptr ptr)
 @ stub WsReadType
 @ stub WsReadValue
 @ stub WsReadXmlBuffer
-- 
2.6.1




More information about the wine-patches mailing list