diff --git a/dlls/msvcp90/locale.c b/dlls/msvcp90/locale.c index e5b9d6ae9fb..1725900e425 100644 --- a/dlls/msvcp90/locale.c +++ b/dlls/msvcp90/locale.c @@ -4408,11 +4408,12 @@ static int num_get__Getffld(const num_get *this, char *dest, istreambuf_iterator istreambuf_iterator_wchar *last, const locale *loc, numpunct_wchar *numpunct) { basic_string_char grouping_bstr; + basic_string_char groups_found; int i, groups_no = 0, cur_group = 0, exp = 0; - char *dest_beg = dest, *num_end = dest+25, *exp_end = dest+31, *groups = NULL; + char *dest_beg = dest, *num_end = dest+25, *exp_end = dest+31; wchar_t sep, digits[11], *digits_pos; - const char *grouping; - BOOL error = FALSE, got_digit = FALSE, dest_empty = TRUE; + const char *grouping, *groups; + BOOL error = FALSE, got_digit = FALSE, got_nonzero = FALSE; TRACE("(%p %p %p %p)\n", dest, first, last, loc); @@ -4424,7 +4425,11 @@ static int num_get__Getffld(const num_get *this, char *dest, istreambuf_iterator grouping = MSVCP_basic_string_char_c_str(&grouping_bstr); sep = grouping[0] ? numpunct_wchar_thousands_sep(numpunct) : (wchar_t)0; + if(sep) + MSVCP_basic_string_char_ctor(&groups_found); + istreambuf_iterator_wchar_val(first); + /* get sign */ if(first->strbuf && first->val==mb_to_wc('-', &this->cvt)) { *dest++ = '-'; istreambuf_iterator_wchar_inc(first); @@ -4433,45 +4438,99 @@ static int num_get__Getffld(const num_get *this, char *dest, istreambuf_iterator istreambuf_iterator_wchar_inc(first); } - if(sep) { - groups_no = strlen(grouping)+2; - groups = calloc(groups_no, sizeof(char)); - } - + /* read possibly grouped numbers before decimal */ for(; first->strbuf; istreambuf_iterator_wchar_inc(first)) { if(!(digits_pos = wcschr(digits, first->val))) { if(sep && first->val==sep) { - if(cur_group == groups_no+1) { - if(groups[1] != groups[2]) { - error = TRUE; - break; - }else { - memmove(groups+1, groups+2, groups_no); - groups[cur_group] = 0; - } - }else { - cur_group++; - } + if(!groups_no) break; /* empty group - stop parsing */ + MSVCP_basic_string_char_append_ch(&groups_found, groups_no); + groups_no = 0; + ++cur_group; }else { break; } }else { - got_digit = TRUE; - if(dest_empty && first->val == digits[0]) + got_digit = TRUE; /* found a digit, zero or non-zero */ + /* write digit to dest if not a leading zero (to not waste dest buffer) */ + if(!got_nonzero && first->val == digits[0]) + { + ++groups_no; continue; - dest_empty = FALSE; + } + got_nonzero = TRUE; if(dest < num_end) *dest++ = '0'+digits_pos-digits; else - exp++; - if(sep && groups[cur_group]strbuf && first->val==numpunct_wchar_decimal_point(numpunct)) { + if(dest < num_end) + *dest++ = *localeconv()->decimal_point; + istreambuf_iterator_wchar_inc(first); + } + + /* read non-grouped after decimal */ + for(; first->strbuf; istreambuf_iterator_wchar_inc(first)) { + if(!(digits_pos = wcschr(digits, first->val))) + break; + else if(deststrbuf && (first->val==mb_to_wc('e', &this->cvt) || first->val==mb_to_wc('E', &this->cvt))) { + *dest++ = 'e'; + istreambuf_iterator_wchar_inc(first); + + if(first->strbuf && first->val==mb_to_wc('-', &this->cvt)) { + *dest++ = '-'; + istreambuf_iterator_wchar_inc(first); + }else if(first->strbuf && first->val==mb_to_wc('+', &this->cvt)) { + *dest++ = '+'; + istreambuf_iterator_wchar_inc(first); + } + + got_digit = got_nonzero = FALSE; error = TRUE; - else if(!cur_group) + /* skip any leading zeroes */ + for(; first->strbuf && first->val==digits[0]; istreambuf_iterator_wchar_inc(first)) + error = FALSE; + + for(; first->strbuf && (digits_pos = wcschr(digits, first->val)); istreambuf_iterator_wchar_inc(first)) { + got_digit = got_nonzero = TRUE; /* leading zeroes would have been skipped, so first digit is non-zero */ + error = FALSE; + if(dest=0 && !error; cur_group--) { @@ -4488,75 +4547,13 @@ static int num_get__Getffld(const num_get *this, char *dest, istreambuf_iterator } } MSVCP_basic_string_char_dtor(&grouping_bstr); - free(groups); + if(sep) + MSVCP_basic_string_char_dtor(&groups_found); if(error) { *dest_beg = '\0'; return 0; - }else if(dest_empty) { - *dest++ = '0'; } - - if(first->strbuf && first->val==numpunct_wchar_decimal_point(numpunct)) { - if(dest < num_end) - *dest++ = *localeconv()->decimal_point; - istreambuf_iterator_wchar_inc(first); - - if(dest_empty) { - for(; first->strbuf && first->val==digits[0]; istreambuf_iterator_wchar_inc(first)) { - got_digit = TRUE; - exp--; - } - - if(!first->strbuf || !wcschr(digits, first->val)) - dest--; - } - } - - for(; first->strbuf; istreambuf_iterator_wchar_inc(first)) { - if(!(digits_pos = wcschr(digits, first->val))) - break; - else if(deststrbuf && (first->val==mb_to_wc('e', &this->cvt) || first->val==mb_to_wc('E', &this->cvt))) { - *dest++ = 'e'; - istreambuf_iterator_wchar_inc(first); - - if(first->strbuf && first->val==mb_to_wc('-', &this->cvt)) { - *dest++ = '-'; - istreambuf_iterator_wchar_inc(first); - }else if(first->strbuf && first->val==mb_to_wc('+', &this->cvt)) { - *dest++ = '+'; - istreambuf_iterator_wchar_inc(first); - } - - error = dest_empty = TRUE; - for(; first->strbuf && first->val==digits[0]; istreambuf_iterator_wchar_inc(first)) - error = FALSE; - - for(; first->strbuf && (digits_pos = wcschr(digits, first->val)); istreambuf_iterator_wchar_inc(first)) { - error = dest_empty = FALSE; - if(deststrbuf && (first->val=='-' || first->val=='+')) { *dest++ = first->val; istreambuf_iterator_char_inc(first); } - if(sep) { - groups_no = strlen(grouping)+2; - groups = calloc(groups_no, sizeof(char)); - } - + /* read possibly grouped numbers before decimal */ for(; first->strbuf; istreambuf_iterator_char_inc(first)) { if(first->val<'0' || first->val>'9') { if(sep && first->val==sep) { - if(cur_group == groups_no+1) { - if(groups[1] != groups[2]) { - error = TRUE; - break; - }else { - memmove(groups+1, groups+2, groups_no); - groups[cur_group] = 0; - } - }else { - cur_group++; - } + if(!groups_no) break; /* empty group - stop parsing */ + MSVCP_basic_string_char_append_ch(&groups_found, groups_no); + groups_no = 0; + ++cur_group; }else { break; } }else { - got_digit = TRUE; - if(dest_empty && first->val == '0') + got_digit = TRUE; /* found a digit, zero or non-zero */ + /* write digit to dest if not a leading zero (to not waste dest buffer) */ + if(!got_nonzero && first->val == '0') + { + ++groups_no; continue; - dest_empty = FALSE; + } + got_nonzero = TRUE; if(dest < num_end) *dest++ = first->val; else - exp++; - if(sep && groups[cur_group]strbuf && first->val==numpunct_char_decimal_point(numpunct)) { + if(dest < num_end) + *dest++ = *localeconv()->decimal_point; + istreambuf_iterator_char_inc(first); + } + + /* read non-grouped after decimal */ + for(; first->strbuf; istreambuf_iterator_char_inc(first)) { + if(first->val<'0' || first->val>'9') + break; + else if(destval; + } + } + + /* read exponent, if any */ + if(first->strbuf && (first->val=='e' || first->val=='E')) { + *dest++ = first->val; + istreambuf_iterator_char_inc(first); + + if(first->strbuf && (first->val=='-' || first->val=='+')) { + *dest++ = first->val; + istreambuf_iterator_char_inc(first); + } + + got_digit = got_nonzero = FALSE; error = TRUE; - else if(!cur_group) + /* skip any leading zeroes */ + for(; first->strbuf && first->val=='0'; istreambuf_iterator_char_inc(first)) + got_digit = TRUE; + + for(; first->strbuf && first->val>='0' && first->val<='9'; istreambuf_iterator_char_inc(first)) { + got_digit = got_nonzero = TRUE; /* leading zeroes would have been skipped, so first digit is non-zero */ + error = FALSE; + if(destval; + } + + /* if just found zeroes for exponent, use that */ + if(got_digit && !got_nonzero) + { + error = FALSE; + if(dest=0 && !error; cur_group--) { @@ -5604,72 +5657,13 @@ int __cdecl num_get_char__Getffld(const num_get *this, char *dest, istreambuf_it } } MSVCP_basic_string_char_dtor(&grouping_bstr); - free(groups); + if(sep) + MSVCP_basic_string_char_dtor(&groups_found); if(error) { *dest_beg = '\0'; return 0; - }else if(dest_empty) { - *dest++ = '0'; } - - if(first->strbuf && first->val==numpunct_char_decimal_point(numpunct)) { - if(dest < num_end) - *dest++ = *localeconv()->decimal_point; - istreambuf_iterator_char_inc(first); - - if(dest_empty) { - for(; first->strbuf && first->val=='0'; istreambuf_iterator_char_inc(first)) { - got_digit = TRUE; - exp--; - } - - if(!first->strbuf || first->val<'1' || first->val>'9') - dest--; - } - } - - for(; first->strbuf; istreambuf_iterator_char_inc(first)) { - if(first->val<'0' || first->val>'9') - break; - else if(destval; - } - } - - if(!got_digit) { - *dest_beg = '\0'; - return 0; - } - - if(first->strbuf && (first->val=='e' || first->val=='E')) { - *dest++ = first->val; - istreambuf_iterator_char_inc(first); - - if(first->strbuf && (first->val=='-' || first->val=='+')) { - *dest++ = first->val; - istreambuf_iterator_char_inc(first); - } - - error = dest_empty = TRUE; - for(; first->strbuf && first->val=='0'; istreambuf_iterator_char_inc(first)) - error = FALSE; - - for(; first->strbuf && first->val>='0' && first->val<='9'; istreambuf_iterator_char_inc(first)) { - error = dest_empty = FALSE; - if(destval; - } - - if(error) { - *dest_beg = '\0'; - return 0; - }else if(dest_empty) { - *dest++ = '0'; - } - } - *dest++ = '\0'; return exp; } diff --git a/dlls/msvcp90/tests/ios.c b/dlls/msvcp90/tests/ios.c index 7f50eceb5ca..075333ae7b9 100644 --- a/dlls/msvcp90/tests/ios.c +++ b/dlls/msvcp90/tests/ios.c @@ -348,9 +348,11 @@ static void (*__thiscall p_basic_stringstream_wchar_vbase_dtor)(basic_stringstre /* istream */ static basic_istream_char* (*__thiscall p_basic_istream_char_read_uint64)(basic_istream_char*, unsigned __int64*); +static basic_istream_char* (*__thiscall p_basic_istream_char_read_double)(basic_istream_char*, double*); static int (*__thiscall p_basic_istream_char_get)(basic_istream_char*); static basic_istream_wchar* (*__thiscall p_basic_istream_wchar_read_uint64)(basic_istream_wchar*, unsigned __int64*); +static basic_istream_wchar* (*__thiscall p_basic_istream_wchar_read_double)(basic_istream_wchar*, double *); static int (*__thiscall p_basic_istream_wchar_get)(basic_istream_wchar*); /* basic_ios */ @@ -477,11 +479,15 @@ static BOOL init(void) SET(p_basic_istream_char_read_uint64, "??5?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@AEA_K@Z"); + SET(p_basic_istream_char_read_double, + "??5?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@AEAN@Z"); SET(p_basic_istream_char_get, "?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAHXZ"); SET(p_basic_istream_wchar_read_uint64, "??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QEAAAEAV01@AEA_K@Z"); + SET(p_basic_istream_wchar_read_double, + "??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QEAAAEAV01@AEAN@Z"); SET(p_basic_istream_wchar_get, "?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QEAAGXZ"); @@ -523,11 +529,15 @@ static BOOL init(void) SET(p_basic_istream_char_read_uint64, "??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_K@Z"); + SET(p_basic_istream_char_read_double, + "??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAN@Z"); SET(p_basic_istream_char_get, "?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ"); SET(p_basic_istream_wchar_read_uint64, "??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_K@Z"); + SET(p_basic_istream_wchar_read_double, + "??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAN@Z"); SET(p_basic_istream_wchar_get, "?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ"); @@ -707,12 +717,162 @@ static void test_num_get_get_uint64(void) } } + +static void test_num_get_get_double(void) +{ + unsigned short testus, nextus; + basic_stringstream_wchar wss; + basic_stringstream_char ss; + basic_string_wchar wstr; + basic_string_char str; + IOSB_iostate state; + locale lcl, retlcl; + wchar_t wide[64]; + int i, next; + double val; + + /* makes tables narrower */ + const IOSB_iostate IOSTATE_faileof = IOSTATE_failbit|IOSTATE_eofbit; + + struct _test_num_get { + const char *str; + const char *lcl; + IOSB_iostate state; + double val; + int next; + } tests[] = { + /* simple cases */ + { "0", NULL, IOSTATE_eofbit, 0.0, EOF }, + { "10", NULL, IOSTATE_eofbit, 10.0, EOF }, + { "+10", NULL, IOSTATE_eofbit, 10.0, EOF }, + { "-10", NULL, IOSTATE_eofbit, -10.0, EOF }, + { "+010", NULL, IOSTATE_eofbit, 10.0, EOF }, /* leading zero */ + + /* test grouping - default/"C" has no grouping, named English/German locales do */ + { "1,000", NULL, IOSTATE_goodbit, 1.0, ',' }, /* with comma */ + { "1,000", "English", IOSTATE_eofbit, 1000.0, EOF }, + { "1,000", "German", IOSTATE_eofbit, 1.0, EOF }, + + { "1.000", NULL, IOSTATE_eofbit, 1.0, EOF }, /* with period */ + { "1.000", "English", IOSTATE_eofbit, 1.0, EOF }, + { "1.000", "German", IOSTATE_eofbit, 1000.0, EOF }, + + { "1,234.", NULL, IOSTATE_goodbit, 1.0, ',' }, + { "1,234.", "English", IOSTATE_eofbit, 1234.0, EOF }, /* trailing decimal */ + { "1,234.", "German", IOSTATE_goodbit, 1.234, '.' }, + { "1,234.5", "English", IOSTATE_eofbit, 1234.5, EOF }, /* group + decimal */ + { "1,234.5", "German", IOSTATE_goodbit, 1.234, '.' }, + + { "1,234,567,890", NULL, IOSTATE_goodbit, 1.0, ',' }, /* more groups */ + { "1,234,567,890", "English", IOSTATE_eofbit, 1234567890.0, EOF }, + { "1,234,567,890", "German", IOSTATE_goodbit, 1.234, ',' }, + { "1.234.567.890", "German", IOSTATE_eofbit, 1234567890.0, EOF }, + + /* extra digits and stuff */ + { "00000.123456", NULL, IOSTATE_eofbit, 0.123456, EOF }, + { "0.1234560000", NULL, IOSTATE_eofbit, 0.123456, EOF }, + { "100aaaa", NULL, IOSTATE_goodbit, 100.0, 'a' }, + + /* exponent */ + { "10e10", NULL, IOSTATE_eofbit, 10e10, EOF }, /* lowercase e */ + { "10E10", NULL, IOSTATE_eofbit, 10E10, EOF }, /* uppercase E */ + { "10e+10", NULL, IOSTATE_eofbit, 10e10, EOF }, /* sign */ + { "10e-10", NULL, IOSTATE_eofbit, 10e-10, EOF }, + { "10.e10", NULL, IOSTATE_eofbit, 10e10, EOF }, /* trailing decimal before exponent */ + { "-10.e-10", NULL, IOSTATE_eofbit, -10e-10, EOF }, + { "-12.345e-10", NULL, IOSTATE_eofbit, -12.345e-10, EOF }, + { "1,234e10", NULL, IOSTATE_goodbit, 1.0, ',' }, + { "1,234e10", "English", IOSTATE_eofbit, 1234.0e10, EOF }, + { "1,234e10", "German", IOSTATE_eofbit, 1.234e10, EOF }, + { "1.0e999", NULL, IOSTATE_faileof, 42.0, EOF }, /* too big */ + { "1.0e-999", NULL, IOSTATE_faileof, 42.0, EOF }, /* too small */ + + /* bad form */ + { "1,000,", NULL, IOSTATE_goodbit, 1.0, ',' }, /* trailing group */ + { "1,000,", "English", IOSTATE_faileof, 42.0, EOF }, + { "1.000.", "German", IOSTATE_faileof, 42.0, EOF }, + + { "1,,000", NULL, IOSTATE_goodbit, 1.0, ',' }, /* empty group */ + { "1,,000", "English", IOSTATE_failbit, 42.0, EOF }, + { "1..000", "German", IOSTATE_failbit, 42.0, EOF }, + + { "1.0,00", "English", IOSTATE_goodbit, 1.0, ',' }, + { "1.0,00", "German", IOSTATE_faileof, 42.0, EOF }, + + { "1.0ee10", NULL, IOSTATE_failbit, 42.0, EOF }, /* dup exp */ + { "1.0e1.0", NULL, IOSTATE_goodbit, 10.0, '.' }, /* decimal in exponent */ + { "1.0e1,0", NULL, IOSTATE_goodbit, 10.0, ',' }, /* group in exponent */ + }; + + for(i=0; i::_Ipfx will handle empty string */ + todo_wine ok(tests[i].state == state, "wrong state, expected = %x found = %x\n", tests[i].state, state); + } else + ok(tests[i].state == state, "wrong state, expected = %x found = %x\n", tests[i].state, state); + ok(tests[i].val == val, "wrong val, expected = %g found %g\n", tests[i].val, val); + ok(tests[i].next == next, "wrong next, expected = %c (%i) found = %c (%i)\n", tests[i].next, tests[i].next, next, next); + + if(tests[i].lcl) + call_func1(p_locale_dtor, &lcl); + + call_func1(p_basic_stringstream_char_vbase_dtor, &ss); + call_func1(p_basic_string_char_dtor, &str); + + /* wchar_t version */ + AtoW(wide, tests[i].str, strlen(tests[i].str)); + call_func2(p_basic_string_wchar_ctor_cstr, &wstr, wide); + call_func4(p_basic_stringstream_wchar_ctor_str, &wss, &wstr, OPENMODE_out|OPENMODE_in, TRUE); + + if(tests[i].lcl) { + call_func3(p_locale_ctor_cstr, &lcl, tests[i].lcl, 0x3f /* FIXME: support categories */); + call_func3(p_basic_ios_wchar_imbue, &wss.basic_ios, &retlcl, &lcl); + } + + val = 42.0; + call_func2(p_basic_istream_wchar_read_double, &wss.base.base1, &val); + state = (IOSB_iostate)call_func1(p_ios_base_rdstate, &wss.basic_ios.base); + nextus = (unsigned short)(int)call_func1(p_basic_istream_wchar_get, &wss.base.base1); + + if(strlen(tests[i].str) == 0) { + /* a later patch to istream<>::_Ipfx will handle empty string */ + todo_wine ok(tests[i].state == state, "wrong state, expected = %x found = %x\n", tests[i].state, state); + } else + ok(tests[i].state == state, "wrong state, expected = %x found = %x\n", tests[i].state, state); + ok(tests[i].val == val, "wrong val, expected = %g found %g\n", tests[i].val, val); + testus = tests[i].next == EOF ? WEOF : (unsigned short)tests[i].next; + ok(testus == nextus, "wrong next, expected = %c (%i) found = %c (%i)\n", testus, testus, nextus, nextus); + + if(tests[i].lcl) + call_func1(p_locale_dtor, &lcl); + + call_func1(p_basic_stringstream_wchar_vbase_dtor, &wss); + call_func1(p_basic_string_wchar_dtor, &wstr); + } +} + + START_TEST(ios) { if(!init()) return; test_num_get_get_uint64(); + test_num_get_get_double(); ok(!invalid_parameter, "invalid_parameter_handler was invoked too many times\n"); }