From: "Iván Matellanes" Subject: [v2 1/3] msvcirt: Implement istream::getdouble Message-Id: <1469095012-6244-1-git-send-email-matellanes.ivan@gmail.com> Date: Thu, 21 Jul 2016 10:56:50 +0100 v2: fix test failures Signed-off-by: Iván Matellanes --- dlls/msvcirt/msvcirt.c | 63 +++++++++++++++++++++-- dlls/msvcirt/tests/msvcirt.c | 117 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 3 deletions(-) diff --git a/dlls/msvcirt/msvcirt.c b/dlls/msvcirt/msvcirt.c index 94f9a31..9361d6b 100644 --- a/dlls/msvcirt/msvcirt.c +++ b/dlls/msvcirt/msvcirt.c @@ -3479,10 +3479,67 @@ int __thiscall istream_getint(istream *this, char *str) /* ?getdouble@istream@@AAEHPADH@Z */ /* ?getdouble@istream@@AEAAHPEADH@Z */ DEFINE_THISCALL_WRAPPER(istream_getdouble, 12) -int __thiscall istream_getdouble(istream *this, char *str, int n) +int __thiscall istream_getdouble(istream *this, char *str, int count) { - FIXME("(%p %p %d) stub\n", this, str, n); - return 0; + ios *base = istream_get_ios(this); + int ch, i = 0; + BOOL scan_sign = TRUE, scan_dot = TRUE, scan_exp = TRUE, + valid_mantissa = FALSE, valid_exponent = FALSE; + + TRACE("(%p %p %d)\n", this, str, count); + + if (istream_ipfx(this, 0)) { + if (!count) { + /* can't output anything */ + base->state |= IOSTATE_failbit; + i = -1; + } else { + /* valid mantissas: +d. +.d +d.d (where d are sequences of digits and the sign is optional) */ + /* valid exponents: e+d E+d (where d are sequences of digits and the sign is optional) */ + for (ch = streambuf_sgetc(base->sb); i < count; ch = streambuf_snextc(base->sb)) { + if ((ch == '+' || ch == '-') && scan_sign) { + /* no additional sign allowed */ + scan_sign = FALSE; + } else if (ch == '.' && scan_dot) { + /* no sign or additional dot allowed */ + scan_sign = scan_dot = FALSE; + } else if ((ch == 'e' || ch == 'E') && scan_exp) { + /* sign is allowed again but not dots or exponents */ + scan_sign = TRUE; + scan_dot = scan_exp = FALSE; + } else if (isdigit(ch)) { + if (scan_exp) + valid_mantissa = TRUE; + else + valid_exponent = TRUE; + /* no sign allowed after a digit */ + scan_sign = FALSE; + } else { + /* unexpected character, stop scanning */ + /* check whether the result is a valid double */ + if (!scan_exp && !valid_exponent) { + /* put the last character back into the stream, usually the 'e' or 'E' */ + if (streambuf_sputbackc(base->sb, str[i--]) == EOF) + base->state |= IOSTATE_badbit; /* characters have been lost for good */ + } else if (ch == EOF) + base->state |= IOSTATE_eofbit; + if (!valid_mantissa) + base->state |= IOSTATE_failbit; + break; + } + str[i++] = ch; + } + /* check if character limit has been reached */ + if (i == count) { + base->state |= IOSTATE_failbit; + i--; + } + /* append a null terminator */ + str[i] = 0; + } + istream_isfx(this); + } + return i; } /* ?ws@@YAAAVistream@@AAV1@@Z */ diff --git a/dlls/msvcirt/tests/msvcirt.c b/dlls/msvcirt/tests/msvcirt.c index 99fd087..a921b27 100644 --- a/dlls/msvcirt/tests/msvcirt.c +++ b/dlls/msvcirt/tests/msvcirt.c @@ -326,6 +326,7 @@ static istream* (*__thiscall p_istream_seekg_offset)(istream*, streamoff, ios_se static int (*__thiscall p_istream_sync)(istream*); static streampos (*__thiscall p_istream_tellg)(istream*); static int (*__thiscall p_istream_getint)(istream*, char*); +static int (*__thiscall p_istream_getdouble)(istream*, char*, int); /* Emulate a __thiscall */ #ifdef __i386__ @@ -540,6 +541,7 @@ static BOOL init(void) SET(p_istream_sync, "?sync@istream@@QEAAHXZ"); SET(p_istream_tellg, "?tellg@istream@@QEAAJXZ"); SET(p_istream_getint, "?getint@istream@@AEAAHPEAD@Z"); + SET(p_istream_getdouble, "?getdouble@istream@@AEAAHPEADH@Z"); } else { p_operator_new = (void*)GetProcAddress(msvcrt, "??2@YAPAXI@Z"); p_operator_delete = (void*)GetProcAddress(msvcrt, "??3@YAXPAX@Z"); @@ -676,6 +678,7 @@ static BOOL init(void) SET(p_istream_sync, "?sync@istream@@QAEHXZ"); SET(p_istream_tellg, "?tellg@istream@@QAEJXZ"); SET(p_istream_getint, "?getint@istream@@AAEHPAD@Z"); + SET(p_istream_getdouble, "?getdouble@istream@@AAEHPADH@Z"); } SET(p_ios_static_lock, "?x_lockc@ios@@0U_CRT_CRITICAL_SECTION@@A"); SET(p_ios_lockc, "?lockc@ios@@KAXXZ"); @@ -4735,6 +4738,119 @@ static void test_istream_getint(void) call_func1(p_strstreambuf_dtor, &ssb); } +static void test_istream_getdouble(void) +{ + istream is, *pis; + strstreambuf ssb, *pssb; + int i, len, ret; + char buffer[32]; + + struct istream_getdouble_test { + const char *stream_content; + int count; + ios_io_state initial_state; + ios_flags flags; + int expected_return; + ios_io_state expected_state; + int expected_offset; + const char *expected_buffer; + BOOL broken; + } tests[] = { + {"", 32, IOSTATE_badbit, FLAGS_skipws, 0, IOSTATE_badbit|IOSTATE_failbit, 0, "", FALSE}, + {"", 0, IOSTATE_badbit, FLAGS_skipws, 0, IOSTATE_badbit|IOSTATE_failbit, 0, "", FALSE}, + {"", 32, IOSTATE_eofbit, FLAGS_skipws, 0, IOSTATE_eofbit|IOSTATE_failbit, 0, "", FALSE}, + {"", 32, IOSTATE_goodbit, FLAGS_skipws, 0, IOSTATE_eofbit|IOSTATE_failbit, 0, "", FALSE}, + {" ", 32, IOSTATE_goodbit, FLAGS_skipws, 0, IOSTATE_eofbit|IOSTATE_failbit, 1, "", FALSE}, + {" 0", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_eofbit, 2, "0", FALSE}, + {"156", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_eofbit, 3, "156", FALSE}, + {"123 ", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_goodbit, 3, "123", FALSE}, + {"+4 5", 32, IOSTATE_goodbit, FLAGS_skipws, 2, IOSTATE_goodbit, 2, "+4", FALSE}, + {"-88a", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_goodbit, 3, "-88", FALSE}, + {"-+5", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_failbit, 1, "-", FALSE}, + {"++7", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_failbit, 1, "+", FALSE}, + {"+", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_eofbit|IOSTATE_failbit, 1, "+", FALSE}, + {"abc", 32, IOSTATE_goodbit, FLAGS_skipws, 0, IOSTATE_failbit, 0, "", FALSE}, + {"0xabc", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_goodbit, 1, "0", FALSE}, + {"01", 32, IOSTATE_goodbit, FLAGS_skipws, 2, IOSTATE_eofbit, 2, "01", FALSE}, + {"10000000000000000000", 32, IOSTATE_goodbit, FLAGS_skipws, 20, IOSTATE_eofbit, 20, "10000000000000000000", FALSE}, + {"1.2", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_eofbit, 3, "1.2", FALSE}, + {"\t-0.4444f", 32, IOSTATE_goodbit, FLAGS_skipws, 7, IOSTATE_goodbit, 8, "-0.4444", FALSE}, + {"3.-14159", 32, IOSTATE_goodbit, FLAGS_skipws, 2, IOSTATE_goodbit, 2, "3.", FALSE}, + {"3.14.159", 32, IOSTATE_goodbit, FLAGS_skipws, 4, IOSTATE_goodbit, 4, "3.14", FALSE}, + {"3.000", 32, IOSTATE_goodbit, FLAGS_skipws, 5, IOSTATE_eofbit, 5, "3.000", FALSE}, + {".125f", 32, IOSTATE_goodbit, FLAGS_skipws, 4, IOSTATE_goodbit, 4, ".125", FALSE}, + {"-.125f", 32, IOSTATE_goodbit, FLAGS_skipws, 5, IOSTATE_goodbit, 5, "-.125", FALSE}, + {"5L", 32, IOSTATE_goodbit, FLAGS_skipws, 1, IOSTATE_goodbit, 1, "5", FALSE}, + {"1.", 32, IOSTATE_goodbit, FLAGS_skipws, 2, IOSTATE_eofbit, 2, "1.", FALSE}, + {"55.!", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_goodbit, 3, "55.", FALSE}, + {"99.99999999999", 32, IOSTATE_goodbit, FLAGS_skipws, 14, IOSTATE_eofbit, 14, "99.99999999999", FALSE}, + {"9.9999999999999999999", 32, IOSTATE_goodbit, FLAGS_skipws, 21, IOSTATE_eofbit, 21, "9.9999999999999999999", FALSE}, + {"0.0000000000000f", 32, IOSTATE_goodbit, FLAGS_skipws, 15, IOSTATE_goodbit, 15, "0.0000000000000", FALSE}, + {"1.0000e5 ", 32, IOSTATE_goodbit, FLAGS_skipws, 8, IOSTATE_goodbit, 8, "1.0000e5", FALSE}, + {"-2.12345e1000 ", 32, IOSTATE_goodbit, FLAGS_skipws, 13, IOSTATE_goodbit, 13, "-2.12345e1000", FALSE}, + {" 8E1", 32, IOSTATE_goodbit, FLAGS_skipws, 3, IOSTATE_eofbit, 5, "8E1", FALSE}, + {"99.99E-99E5", 32, IOSTATE_goodbit, FLAGS_skipws, 9, IOSTATE_goodbit, 9, "99.99E-99", FALSE}, + {"0e0", 32, IOSTATE_goodbit, FLAGS_skipws|FLAGS_uppercase, 3, IOSTATE_eofbit, 3, "0e0", FALSE}, + {"1.e8.5", 32, IOSTATE_goodbit, 0, 4, IOSTATE_goodbit, 4, "1.e8", FALSE}, + {"1.0e-1000000000000000000 ", 32, IOSTATE_goodbit, 0, 24, IOSTATE_goodbit, 24, "1.0e-1000000000000000000", FALSE}, + {"1.e+f", 32, IOSTATE_goodbit, 0, 3, IOSTATE_goodbit, 3, "1.e", FALSE}, + {"1.ef", 32, IOSTATE_goodbit, 0, 2, IOSTATE_goodbit, 2, "1.", FALSE}, + {"1.E-z", 32, IOSTATE_goodbit, 0, 3, IOSTATE_goodbit, 3, "1.E", FALSE}, + {".", 32, IOSTATE_goodbit, 0, 1, IOSTATE_eofbit|IOSTATE_failbit, 1, ".", FALSE}, + {".e", 32, IOSTATE_goodbit, 0, 1, IOSTATE_failbit, 1, ".", FALSE}, + {".e.", 32, IOSTATE_goodbit, 0, 1, IOSTATE_failbit, 1, ".", FALSE}, + {".e5", 32, IOSTATE_goodbit, 0, 3, IOSTATE_eofbit|IOSTATE_failbit, 3, ".e5", FALSE}, + {".2e5", 32, IOSTATE_goodbit, 0, 4, IOSTATE_eofbit, 4, ".2e5", FALSE}, + {"9.e", 32, IOSTATE_goodbit, 0, 2, IOSTATE_goodbit, 2, "9.", FALSE}, + {"0.0e-0", 32, IOSTATE_goodbit, 0, 6, IOSTATE_eofbit, 6, "0.0e-0", FALSE}, + {"e5.2", 32, IOSTATE_goodbit, 0, 2, IOSTATE_failbit, 2, "e5", FALSE}, + {"1.0000", 5, IOSTATE_goodbit, 0, 4, IOSTATE_failbit, 5, "1.00", TRUE}, + {"-123456", 5, IOSTATE_goodbit, 0, 4, IOSTATE_failbit, 5, "-123", TRUE}, + {"3.5e2", 5, IOSTATE_goodbit, 0, 4, IOSTATE_failbit, 5, "3.5e", TRUE}, + {"3.e25", 5, IOSTATE_goodbit, 0, 4, IOSTATE_failbit, 5, "3.e2", TRUE}, + {"1.11f", 5, IOSTATE_goodbit, 0, 4, IOSTATE_goodbit, 4, "1.11", FALSE}, + {".5e-5", 5, IOSTATE_goodbit, 0, 4, IOSTATE_failbit, 5, ".5e-", TRUE}, + {".5e-", 5, IOSTATE_goodbit, 0, 3, IOSTATE_goodbit, 3, ".5e", FALSE}, + {".5e2", 5, IOSTATE_goodbit, 0, 4, IOSTATE_eofbit, 4, ".5e2", FALSE}, + {"1", 0, IOSTATE_goodbit, 0, -1, IOSTATE_failbit, 0, "", TRUE}, + {"x", 0, IOSTATE_goodbit, 0, -1, IOSTATE_failbit, 0, "", TRUE}, + {"", 0, IOSTATE_goodbit, 0, -1, IOSTATE_failbit, 0, "", TRUE}, + {"", 1, IOSTATE_goodbit, 0, 0, IOSTATE_eofbit|IOSTATE_failbit, 0, "", FALSE}, + {"1.0", 1, IOSTATE_goodbit, 0, 0, IOSTATE_failbit, 1, "", TRUE}, + {"1.0", 2, IOSTATE_goodbit, 0, 1, IOSTATE_failbit, 2, "1", TRUE} + }; + + pssb = call_func2(p_strstreambuf_dynamic_ctor, &ssb, 64); + ok(pssb == &ssb, "wrong return, expected %p got %p\n", &ssb, pssb); + ret = (int) call_func1(p_streambuf_allocate, &ssb.base); + ok(ret == 1, "expected 1 got %d\n", ret); + pis = call_func3(p_istream_sb_ctor, &is, &ssb.base, TRUE); + ok(pis == &is, "wrong return, expected %p got %p\n", &is, pis); + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + len = strlen(tests[i].stream_content); + is.base_ios.state = tests[i].initial_state; + is.base_ios.flags = tests[i].flags; + ssb.base.eback = ssb.base.gptr = ssb.base.base; + ssb.base.egptr = ssb.base.base + len; + memcpy(ssb.base.base, tests[i].stream_content, len); + + ret = (int) call_func3(p_istream_getdouble, &is, buffer, tests[i].count); + ok(ret == tests[i].expected_return || + /* xp, 2k3 */ broken(tests[i].broken && ret == tests[i].expected_return + 1), + "Test %d: wrong return, expected %d got %d\n", i, tests[i].expected_return, ret); + ok(is.base_ios.state == tests[i].expected_state, "Test %d: expected %d got %d\n", i, + tests[i].expected_state, is.base_ios.state); + ok(ssb.base.gptr == ssb.base.base + tests[i].expected_offset, "Test %d: expected %p got %p\n", + i, ssb.base.base + tests[i].expected_offset, ssb.base.gptr); + ok(!strncmp(buffer, tests[i].expected_buffer, strlen(tests[i].expected_buffer)), + "Test %d: unexpected buffer content, got '%s'\n", i, buffer); + } + + call_func1(p_istream_vbase_dtor, &is); + call_func1(p_strstreambuf_dtor, &ssb); +} + START_TEST(msvcirt) { if(!init()) @@ -4749,6 +4865,7 @@ START_TEST(msvcirt) test_ostream_print(); test_istream(); test_istream_getint(); + test_istream_getdouble(); FreeLibrary(msvcrt); FreeLibrary(msvcirt); -- 2.7.4