[2/4] webservices: Add support for reading date values.

Hans Leidekker hans at codeweavers.com
Fri Apr 29 13:21:51 CDT 2016


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/webservices/reader.c       | 198 ++++++++++++++++++++++++++++++++++++++++
 dlls/webservices/tests/reader.c |  86 +++++++++++++++++
 include/webservices.h           |  20 ++++
 3 files changed, 304 insertions(+)

diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c
index e30864d..d4d4dcc 100644
--- a/dlls/webservices/reader.c
+++ b/dlls/webservices/reader.c
@@ -1881,6 +1881,148 @@ static HRESULT str_to_uint64( const unsigned char *str, ULONG len, UINT64 max, U
     return S_OK;
 }
 
+#define TICKS_PER_SEC   10000000
+#define TICKS_PER_MIN   (60 * (ULONGLONG)TICKS_PER_SEC)
+#define TICKS_PER_HOUR  (3600 * (ULONGLONG)TICKS_PER_SEC)
+#define TICKS_PER_DAY   (86400 * (ULONGLONG)TICKS_PER_SEC)
+#define TICKS_MAX       3155378975999999999
+
+static const int month_offsets[2][12] =
+{
+    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
+};
+
+static const int month_days[2][12] =
+{
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static inline int is_leap_year( int year )
+{
+    return !(year % 4) && (year % 100 || !(year % 400));
+}
+
+static inline int valid_day( int year, int month, int day )
+{
+    return day > 0 && day <= month_days[is_leap_year( year )][month - 1];
+}
+
+static inline int leap_days_before( int year )
+{
+    return (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
+}
+
+static HRESULT str_to_datetime( const unsigned char *bytes, ULONG len, WS_DATETIME *ret )
+{
+    const unsigned char *p = bytes, *q;
+    int year, month, day, hour, min, sec, sec_frac = 0, tz_hour, tz_min, tz_neg;
+
+    while (len && read_isspace( *p )) { p++; len--; }
+    while (len && read_isspace( p[len - 1] )) { len--; }
+
+    q = p;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 4 || !len || *q != '-') return WS_E_INVALID_FORMAT;
+    year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + p[3] - '0';
+    if (year < 1) return WS_E_INVALID_FORMAT;
+
+    p = ++q; len--;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 2 || !len || *q != '-') return WS_E_INVALID_FORMAT;
+    month = (p[0] - '0') * 10 + p[1] - '0';
+    if (month < 1 || month > 12) return WS_E_INVALID_FORMAT;
+
+    p = ++q; len--;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 2 || !len || *q != 'T') return WS_E_INVALID_FORMAT;
+    day = (p[0] - '0') * 10 + p[1] - '0';
+    if (!valid_day( year, month, day )) return WS_E_INVALID_FORMAT;
+
+    p = ++q; len--;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
+    hour = (p[0] - '0') * 10 + p[1] - '0';
+    if (hour > 24) return WS_E_INVALID_FORMAT;
+
+    p = ++q; len--;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
+    min = (p[0] - '0') * 10 + p[1] - '0';
+    if (min > 59 || (min > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
+
+    p = ++q; len--;
+    while (len && isdigit( *q )) { q++; len--; };
+    if (q - p != 2 || !len) return WS_E_INVALID_FORMAT;
+    sec = (p[0] - '0') * 10 + p[1] - '0';
+    if (sec > 59 || (sec > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
+
+    if (*q == '.')
+    {
+        unsigned int i, nb_digits, mul = TICKS_PER_SEC / 10;
+        p = ++q; len--;
+        while (len && isdigit( *q )) { q++; len--; };
+        nb_digits = q - p;
+        if (nb_digits < 1 || nb_digits > 7) return WS_E_INVALID_FORMAT;
+        for (i = 0; i < nb_digits; i++)
+        {
+            sec_frac += (p[i] - '0') * mul;
+            mul /= 10;
+        }
+    }
+    if (*q == 'Z')
+    {
+        if (--len) return WS_E_INVALID_FORMAT;
+        tz_hour = tz_min = tz_neg = 0;
+        ret->format = WS_DATETIME_FORMAT_UTC;
+    }
+    else if (*q == '+' || *q == '-')
+    {
+        tz_neg = (*q == '-') ? 1 : 0;
+
+        p = ++q; len--;
+        while (len && isdigit( *q )) { q++; len--; };
+        if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
+        tz_hour = (p[0] - '0') * 10 + p[1] - '0';
+        if (tz_hour > 14) return WS_E_INVALID_FORMAT;
+
+        p = ++q; len--;
+        while (len && isdigit( *q )) { q++; len--; };
+        if (q - p != 2 || len) return WS_E_INVALID_FORMAT;
+        tz_min = (p[0] - '0') * 10 + p[1] - '0';
+        if (tz_min > 59 || (tz_min > 0 && tz_hour == 14)) return WS_E_INVALID_FORMAT;
+
+        ret->format = WS_DATETIME_FORMAT_LOCAL;
+    }
+    else return WS_E_INVALID_FORMAT;
+
+    ret->ticks = ((year - 1) * 365 + leap_days_before( year )) * TICKS_PER_DAY;
+    ret->ticks += month_offsets[is_leap_year( year )][month - 1] * TICKS_PER_DAY;
+    ret->ticks += (day - 1) * TICKS_PER_DAY;
+    ret->ticks += hour * TICKS_PER_HOUR;
+    ret->ticks += min * TICKS_PER_MIN;
+    ret->ticks += sec * TICKS_PER_SEC;
+    ret->ticks += sec_frac;
+
+    if (tz_neg)
+    {
+        if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN + ret->ticks > TICKS_MAX)
+            return WS_E_INVALID_FORMAT;
+        ret->ticks += tz_hour * TICKS_PER_HOUR;
+        ret->ticks += tz_min * TICKS_PER_MIN;
+    }
+    else
+    {
+        if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN > ret->ticks)
+            return WS_E_INVALID_FORMAT;
+        ret->ticks -= tz_hour * TICKS_PER_HOUR;
+        ret->ticks -= tz_min * TICKS_PER_MIN;
+    }
+
+    return S_OK;
+}
+
 static HRESULT read_get_node_text( struct reader *reader, WS_XML_UTF8_TEXT **ret )
 {
     WS_XML_TEXT_NODE *text;
@@ -2570,6 +2712,53 @@ static HRESULT read_type_enum( struct reader *reader, WS_TYPE_MAPPING mapping,
     return S_OK;
 }
 
+static HRESULT read_type_datetime( struct reader *reader, WS_TYPE_MAPPING mapping,
+                                   const WS_XML_STRING *localname, const WS_XML_STRING *ns,
+                                   const WS_DATETIME_DESCRIPTION *desc, WS_READ_OPTION option,
+                                   WS_HEAP *heap, void *ret, ULONG size )
+{
+    WS_XML_UTF8_TEXT *utf8;
+    HRESULT hr;
+    WS_DATETIME val = {0, WS_DATETIME_FORMAT_UTC};
+    BOOL found;
+
+    if (desc) FIXME( "ignoring description\n" );
+
+    if ((hr = read_get_text( reader, mapping, localname, ns, &utf8, &found )) != S_OK) return hr;
+    if (found && (hr = str_to_datetime( utf8->value.bytes, utf8->value.length, &val )) != S_OK) return hr;
+
+    switch (option)
+    {
+    case WS_READ_REQUIRED_VALUE:
+        if (!found) return WS_E_INVALID_FORMAT;
+        if (size != sizeof(WS_DATETIME)) return E_INVALIDARG;
+        *(WS_DATETIME *)ret = val;
+        break;
+
+    case WS_READ_REQUIRED_POINTER:
+        if (!found) return WS_E_INVALID_FORMAT;
+        /* fall through */
+
+    case WS_READ_OPTIONAL_POINTER:
+    {
+        WS_DATETIME *heap_val = NULL;
+        if (size != sizeof(heap_val)) return E_INVALIDARG;
+        if (found)
+        {
+            if (!(heap_val = ws_alloc( heap, sizeof(*heap_val) ))) return WS_E_QUOTA_EXCEEDED;
+            *heap_val = val;
+        }
+        *(WS_DATETIME **)ret = heap_val;
+        break;
+    }
+    default:
+        FIXME( "read option %u not supported\n", option );
+        return E_NOTIMPL;
+    }
+
+    return S_OK;
+}
+
 static BOOL is_empty_text_node( const struct node *node )
 {
     const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)node;
@@ -2643,6 +2832,9 @@ static ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc )
     case WS_UINT64_TYPE:
         return sizeof(INT64);
 
+    case WS_DATETIME_TYPE:
+        return sizeof(WS_DATETIME);
+
     case WS_WSZ_TYPE:
         return sizeof(WCHAR *);
 
@@ -2746,6 +2938,7 @@ static WS_READ_OPTION map_field_options( WS_TYPE type, ULONG options )
     case WS_UINT32_TYPE:
     case WS_UINT64_TYPE:
     case WS_ENUM_TYPE:
+    case WS_DATETIME_TYPE:
         return WS_READ_REQUIRED_VALUE;
 
     case WS_WSZ_TYPE:
@@ -2979,6 +3172,11 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP
             return hr;
         break;
 
+    case WS_DATETIME_TYPE:
+        if ((hr = read_type_datetime( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK)
+            return hr;
+        break;
+
     default:
         FIXME( "type %u not supported\n", type );
         return E_NOTIMPL;
diff --git a/dlls/webservices/tests/reader.c b/dlls/webservices/tests/reader.c
index 5c94fef..ced8a0f 100644
--- a/dlls/webservices/tests/reader.c
+++ b/dlls/webservices/tests/reader.c
@@ -2924,6 +2924,91 @@ static void test_WsResetHeap(void)
     WsFreeHeap( heap );
 }
 
+static void test_datetime(void)
+{
+    static const struct
+    {
+        const char        *str;
+        HRESULT            hr;
+        __int64            ticks;
+        WS_DATETIME_FORMAT format;
+    }
+    tests[] =
+    {
+        {"<t>0000-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00.0Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.1Z</t>", S_OK, 0x0000f4240, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.01Z</t>", S_OK, 0x0000186a0, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.0000001Z</t>", S_OK, 1, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.9999999Z</t>", S_OK, 0x00098967f, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.0000000Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T00:00:00.00000001Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00Z-</t>", WS_E_INVALID_FORMAT, 0},
+        {"<t>-0001-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-00-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-13-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-12-01T00:00:00Z</t>", S_OK, 0x1067555f88000, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-00T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>2001-01-32T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>2001-01-31T00:00:00Z</t>", S_OK, 0x8c2592fe3794000, WS_DATETIME_FORMAT_UTC},
+        {"<t>1900-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>2000-02-29T00:00:00Z</t>", S_OK, 0x8c1505f0e438000, 0},
+        {"<t>2001-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>2001-02-28T00:00:00Z</t>", S_OK, 0x8c26f30870a4000, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-00-01U00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T24:00:00Z</t>", S_OK, 0xc92a69c000, WS_DATETIME_FORMAT_UTC},
+        {"<t>0001-01-01T24:00:01Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:60:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:60Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00Y</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00+00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0001-01-01T00:00:00-00:01</t>", S_OK, 0x023c34600, WS_DATETIME_FORMAT_LOCAL},
+        {"<t>9999-12-31T24:00:00+00:01</t>", S_OK, 0x2bca2875d073fa00, WS_DATETIME_FORMAT_LOCAL},
+        {"<t>9999-12-31T24:00:00-00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0002-01-01T00:00:00+14:01</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0002-01-01T00:00:00+15:00</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0002-01-01T00:00:00+13:60</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>0002-01-01T00:00:00+13:59</t>", S_OK, 0x11e5c43cc5600, WS_DATETIME_FORMAT_LOCAL},
+        {"<t>0002-01-01T00:00:00+01:00</t>", S_OK, 0x11ec917025800, WS_DATETIME_FORMAT_LOCAL},
+        {"<t>2016-01-01T00:00:00-01:00</t>", S_OK, 0x8d31246dfbba800, WS_DATETIME_FORMAT_LOCAL},
+        {"<t>2016-01-01T00:00:00Z</t>", S_OK, 0x8d3123e7df74000, WS_DATETIME_FORMAT_UTC},
+        {"<t> 2016-01-02T03:04:05Z </t>", S_OK, 0x8d313215fb64080, WS_DATETIME_FORMAT_UTC},
+        {"<t>+2016-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t></t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>01-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
+        {"<t>1601-01-01T00:00:00Z</t>", S_OK, 0x701ce1722770000, WS_DATETIME_FORMAT_UTC},
+    };
+    HRESULT hr;
+    WS_XML_READER *reader;
+    WS_HEAP *heap;
+    WS_DATETIME date;
+    ULONG i;
+
+    hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = WsCreateReader( NULL, 0, &reader, NULL ) ;
+    ok( hr == S_OK, "got %08x\n", hr );
+    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+    {
+        memset( &date, 0, sizeof(date) );
+        prepare_type_test( reader, tests[i].str, strlen(tests[i].str) );
+        hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_DATETIME_TYPE, NULL,
+                         WS_READ_REQUIRED_VALUE, heap, &date, sizeof(date), NULL );
+        ok( hr == tests[i].hr, "%u: got %08x\n", i, hr );
+        if (hr == S_OK)
+        {
+            ok( date.ticks == tests[i].ticks, "%u: got %x%08x\n", i, (ULONG)(date.ticks >> 32), (ULONG)date.ticks );
+            ok( date.format == tests[i].format, "%u: got %u\n", i, date.format );
+        }
+    }
+
+    WsFreeReader( reader );
+    WsFreeHeap( heap );
+}
+
 START_TEST(reader)
 {
     test_WsCreateError();
@@ -2949,4 +3034,5 @@ START_TEST(reader)
     test_complex_struct_type();
     test_repeating_element();
     test_WsResetHeap();
+    test_datetime();
 }
diff --git a/include/webservices.h b/include/webservices.h
index 6e30f50..a55072f 100644
--- a/include/webservices.h
+++ b/include/webservices.h
@@ -57,6 +57,8 @@ typedef struct _WS_PARAMETER_DESCRIPTION WS_PARAMETER_DESCRIPTION;
 typedef struct _WS_OPERATION_CONTEXT WS_OPERATION_CONTEXT;
 typedef struct _WS_CALL_PROPERTY WS_CALL_PROPERTY;
 typedef struct _WS_DOUBLE_DESCRIPTION WS_DOUBLE_DESCRIPTION;
+typedef struct _WS_DATETIME WS_DATETIME;
+typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION;
 
 struct _WS_STRUCT_DESCRIPTION;
 struct _WS_XML_STRING;
@@ -989,6 +991,22 @@ struct _WS_CALL_PROPERTY {
     ULONG valueSize;
 };
 
+typedef enum {
+    WS_DATETIME_FORMAT_UTC,
+    WS_DATETIME_FORMAT_LOCAL,
+    WS_DATETIME_FORMAT_NONE
+} WS_DATETIME_FORMAT;
+
+struct _WS_DATETIME {
+    unsigned __int64 ticks;
+    WS_DATETIME_FORMAT format;
+};
+
+struct _WS_DATETIME_DESCRIPTION {
+    WS_DATETIME minValue;
+    WS_DATETIME maxValue;
+};
+
 HRESULT WINAPI WsAlloc(WS_HEAP*, SIZE_T, void**, WS_ERROR*);
 HRESULT WINAPI WsCall(WS_SERVICE_PROXY*, const WS_OPERATION_DESCRIPTION*, const void**,
                       WS_HEAP*, const WS_CALL_PROPERTY*, const ULONG, const WS_ASYNC_CONTEXT*,
@@ -1015,6 +1033,8 @@ HRESULT WINAPI WsCreateServiceProxyFromTemplate(WS_CHANNEL_TYPE, const WS_PROXY_
 HRESULT WINAPI WsCreateWriter(const WS_XML_WRITER_PROPERTY*, ULONG, WS_XML_WRITER**, WS_ERROR*);
 HRESULT WINAPI WsCreateXmlBuffer(WS_HEAP*, const WS_XML_BUFFER_PROPERTY*, ULONG, WS_XML_BUFFER**,
                                  WS_ERROR*);
+HRESULT WINAPI WsDateTimeToFileTime(const WS_DATETIME*, FILETIME*, WS_ERROR*);
+HRESULT WINAPI WsFileTimeToDateTime(const FILETIME*, WS_DATETIME*, WS_ERROR*);
 HRESULT WINAPI WsFillReader(WS_XML_READER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*);
 HRESULT WINAPI WsFindAttribute(WS_XML_READER*, const WS_XML_STRING*, const WS_XML_STRING*, BOOL,
                                ULONG*, WS_ERROR*);
-- 
2.1.4




More information about the wine-patches mailing list