ucrtbase: Implement the new printf corner case behaviour.

Check the option flags whether the new or old legacy behaviours are
wanted.

For _MSVCR_VER < 140, don't check the option flags but hardcode them to
TRUE. (This avoids having to manually add all three flags into every
caller of pf_printf.) Mask out any other flags, to avoid other
out-of-range flags to be interpreted as the other flags (positional
params, invoke invalid param handler).

Signed-off-by: Martin Storsjo <martin@martin.st>
Signed-off-by: Piotr Caban <piotr@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
oldstable
Martin Storsjo 2015-11-03 20:40:38 +02:00 committed by Alexandre Julliard
parent 1d17e85026
commit b9d0f5d562
5 changed files with 158 additions and 27 deletions

View File

@ -4998,7 +4998,11 @@ int CDECL MSVCRT__stdio_common_vfprintf(unsigned __int64 options, MSVCRT_FILE *f
MSVCRT__lock_file(file); MSVCRT__lock_file(file);
tmp_buf = add_std_buffer(file); tmp_buf = add_std_buffer(file);
ret = pf_printf_a(puts_clbk_file_a, file, format, locale, 0, arg_clbk_valist, NULL, &valist);
if (options & ~UCRTBASE_PRINTF_MASK)
FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
ret = pf_printf_a(puts_clbk_file_a, file, format, locale,
options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist);
if(tmp_buf) remove_std_buffer(file); if(tmp_buf) remove_std_buffer(file);
MSVCRT__unlock_file(file); MSVCRT__unlock_file(file);

View File

@ -1130,8 +1130,7 @@ extern char* __cdecl __unDName(char *,const char*,int,malloc_func_t,free_func_t,
#define UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY (0x0008) #define UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY (0x0008)
#define UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS (0x0010) #define UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS (0x0010)
#define UCRTBASE_PRINTF_TERMINATION_MASK (UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION | \ #define UCRTBASE_PRINTF_MASK (0x001F)
UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR)
#define MSVCRT_PRINTF_POSITIONAL_PARAMS (0x0100) #define MSVCRT_PRINTF_POSITIONAL_PARAMS (0x0100)
#define MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER (0x0200) #define MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER (0x0200)

View File

@ -82,7 +82,7 @@ static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ct
{ {
int i, r = 0, written; int i, r = 0, written;
if(flags->Sign && !strchr("diaeEfgG", flags->Format)) if(flags->Sign && !strchr("diaeEfFgG", flags->Format))
flags->Sign = 0; flags->Sign = 0;
if(left && flags->Sign) { if(left && flags->Sign) {
@ -206,8 +206,9 @@ static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts,
} }
static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo) const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo, BOOL legacy_wide)
{ {
BOOL complement_is_narrow = legacy_wide ? (sizeof(APICHAR)==sizeof(MSVCRT_wchar_t)) : FALSE;
#ifdef PRINTF_WIDE #ifdef PRINTF_WIDE
static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0}; static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
@ -223,7 +224,7 @@ static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void
if(flags->IntegerLength == 'h') if(flags->IntegerLength == 'h')
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo); return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
if((flags->Format=='S' || flags->Format=='C') == (sizeof(APICHAR)==sizeof(MSVCRT_wchar_t))) if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow)
return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo); return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
else else
return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo); return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
@ -304,7 +305,7 @@ static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, int buf_len,
} }
} }
static inline void FUNC_NAME(pf_fixup_exponent)(char *buf) static inline void FUNC_NAME(pf_fixup_exponent)(char *buf, BOOL three_digit_exp)
{ {
char* tmp = buf; char* tmp = buf;
@ -313,7 +314,11 @@ static inline void FUNC_NAME(pf_fixup_exponent)(char *buf)
if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') && if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
isdigit(tmp[2]) && isdigit(tmp[3])) { isdigit(tmp[2]) && isdigit(tmp[3])) {
#if _MSVCR_VER >= 140
BOOL two_digit_exp = !three_digit_exp;
#else
BOOL two_digit_exp = (MSVCRT__get_output_format() == MSVCRT__TWO_DIGIT_EXPONENT); BOOL two_digit_exp = (MSVCRT__get_output_format() == MSVCRT__TWO_DIGIT_EXPONENT);
#endif
tmp += 2; tmp += 2;
if(isdigit(tmp[2])) { if(isdigit(tmp[2])) {
@ -346,6 +351,13 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
FUNC_NAME(pf_flags) flags; FUNC_NAME(pf_flags) flags;
BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS; BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER; BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER;
#if _MSVCR_VER >= 140
BOOL legacy_wide = options & UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS;
BOOL legacy_msvcrt_compat = options & UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
BOOL three_digit_exp = options & UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS;
#else
BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE, three_digit_exp = TRUE;
#endif
TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt)); TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
@ -461,7 +473,7 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
p++; p++;
} else if(*p == 'w') } else if(*p == 'w')
flags.WideString = *p++; flags.WideString = *p++;
else if(*p == 'F' || *p == 'N') else if((*p == 'F' || *p == 'N') && legacy_msvcrt_compat)
p++; /* ignore */ p++; /* ignore */
else else
break; break;
@ -472,14 +484,14 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
if(flags.Format == 's' || flags.Format == 'S') { if(flags.Format == 's' || flags.Format == 'S') {
i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
pf_args(args_ctx, pos, VT_PTR, valist).get_ptr, pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
-1, &flags, locinfo); -1, &flags, locinfo, legacy_wide);
} else if(flags.Format == 'c' || flags.Format == 'C') { } else if(flags.Format == 'c' || flags.Format == 'C') {
int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int; int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
if((ch&0xff) != ch) if((ch&0xff) != ch)
FIXME("multibyte characters printing not supported\n"); FIXME("multibyte characters printing not supported\n");
i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locinfo); i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locinfo, legacy_wide);
} else if(flags.Format == 'p') { } else if(flags.Format == 'p') {
flags.Format = 'X'; flags.Format = 'X';
flags.PadZero = '0'; flags.PadZero = '0';
@ -540,7 +552,7 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
#endif #endif
if(tmp != buf) if(tmp != buf)
HeapFree(GetProcessHeap(), 0, tmp); HeapFree(GetProcessHeap(), 0, tmp);
} else if(flags.Format && strchr("aeEfgG", flags.Format)) { } else if(flags.Format && strchr("aeEfFgG", flags.Format)) {
char float_fmt[20], buf_a[32], *tmp = buf_a, *decimal_point; char float_fmt[20], buf_a[32], *tmp = buf_a, *decimal_point;
int len = flags.Precision + 10; int len = flags.Precision + 10;
double val = pf_args(args_ctx, pos, VT_R8, valist).get_double; double val = pf_args(args_ctx, pos, VT_R8, valist).get_double;
@ -571,7 +583,7 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
} }
} }
if(flags.Format=='f') { if(flags.Format=='f' || flags.Format=='F') {
if(val>-10.0 && val<10.0) if(val>-10.0 && val<10.0)
i = 1; i = 1;
else else
@ -594,9 +606,24 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
val = -val; val = -val;
} }
sprintf(tmp, float_fmt, val); if((inf || nan || ind) && !legacy_msvcrt_compat) {
if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G') static const char inf_str[] = "inf";
FUNC_NAME(pf_fixup_exponent)(tmp); static const char ind_str[] = "nan(ind)";
static const char nan_str[] = "nan";
if(inf)
sprintf(tmp, inf_str);
else if(ind)
sprintf(tmp, ind_str);
else
sprintf(tmp, nan_str);
if (strchr("EFG", flags.Format))
for(i=0; tmp[i]; i++)
tmp[i] = toupper(tmp[i]);
} else {
sprintf(tmp, float_fmt, val);
if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G')
FUNC_NAME(pf_fixup_exponent)(tmp, three_digit_exp);
}
decimal_point = strchr(tmp, '.'); decimal_point = strchr(tmp, '.');
if(decimal_point) { if(decimal_point) {

View File

@ -724,10 +724,10 @@ int CDECL MSVCRT__stdio_common_vsprintf( unsigned __int64 options, char *str, MS
struct _str_ctx_a ctx = {len, str}; struct _str_ctx_a ctx = {len, str};
int ret; int ret;
if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK) if (options & ~UCRTBASE_PRINTF_MASK)
FIXME("options %s not handled\n", wine_dbgstr_longlong(options)); FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
ret = pf_printf_a(puts_clbk_str_c99_a, ret = pf_printf_a(puts_clbk_str_c99_a,
&ctx, format, locale, 0, arg_clbk_valist, NULL, &valist); &ctx, format, locale, options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist);
puts_clbk_str_a(&ctx, 1, &nullbyte); puts_clbk_str_a(&ctx, 1, &nullbyte);
if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION) if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION)
@ -778,11 +778,8 @@ int CDECL MSVCRT_sprintf_l(char *str, const char *format,
return retval; return retval;
} }
/********************************************************************* static int CDECL MSVCRT_vsnprintf_s_l_opt( char *str, MSVCRT_size_t sizeOfBuffer,
* _vsnprintf_s_l (MSVCRT.@) MSVCRT_size_t count, const char *format, DWORD options,
*/
int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
MSVCRT_size_t count, const char *format,
MSVCRT__locale_t locale, __ms_va_list valist ) MSVCRT__locale_t locale, __ms_va_list valist )
{ {
static const char nullbyte = '\0'; static const char nullbyte = '\0';
@ -796,7 +793,7 @@ int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
ctx.len = len; ctx.len = len;
ctx.buf = str; ctx.buf = str;
ret = pf_printf_a(puts_clbk_str_a, &ctx, format, locale, MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, ret = pf_printf_a(puts_clbk_str_a, &ctx, format, locale, MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options,
arg_clbk_valist, NULL, &valist); arg_clbk_valist, NULL, &valist);
puts_clbk_str_a(&ctx, 1, &nullbyte); puts_clbk_str_a(&ctx, 1, &nullbyte);
@ -813,6 +810,16 @@ int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
return ret; return ret;
} }
/*********************************************************************
* _vsnprintf_s_l (MSVCRT.@)
*/
int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
MSVCRT_size_t count, const char *format,
MSVCRT__locale_t locale, __ms_va_list valist )
{
return MSVCRT_vsnprintf_s_l_opt(str, sizeOfBuffer, count, format, 0, locale, valist);
}
/********************************************************************* /*********************************************************************
* _vsprintf_s_l (MSVCRT.@) * _vsprintf_s_l (MSVCRT.@)
*/ */
@ -852,7 +859,9 @@ int CDECL MSVCRT__stdio_common_vsnprintf_s( unsigned __int64 options,
char *str, MSVCRT_size_t sizeOfBuffer, MSVCRT_size_t count, char *str, MSVCRT_size_t sizeOfBuffer, MSVCRT_size_t count,
const char *format, MSVCRT__locale_t locale, __ms_va_list valist ) const char *format, MSVCRT__locale_t locale, __ms_va_list valist )
{ {
return MSVCRT_vsnprintf_s_l(str, sizeOfBuffer, count, format, locale, valist); if (options & ~UCRTBASE_PRINTF_MASK)
FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
return MSVCRT_vsnprintf_s_l_opt(str, sizeOfBuffer, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist);
} }
/********************************************************************* /*********************************************************************
@ -862,7 +871,9 @@ int CDECL MSVCRT__stdio_common_vsprintf_s( unsigned __int64 options,
char *str, MSVCRT_size_t count, const char *format, char *str, MSVCRT_size_t count, const char *format,
MSVCRT__locale_t locale, __ms_va_list valist ) MSVCRT__locale_t locale, __ms_va_list valist )
{ {
return MSVCRT_vsnprintf_s_l(str, INT_MAX, count, format, locale, valist); if (options & ~UCRTBASE_PRINTF_MASK)
FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
return MSVCRT_vsnprintf_s_l_opt(str, INT_MAX, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist);
} }
/********************************************************************* /*********************************************************************
@ -1183,10 +1194,10 @@ int CDECL MSVCRT__stdio_common_vswprintf( unsigned __int64 options,
struct _str_ctx_w ctx = {len, str}; struct _str_ctx_w ctx = {len, str};
int ret; int ret;
if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK) if (options & ~UCRTBASE_PRINTF_MASK)
FIXME("options %s not handled\n", wine_dbgstr_longlong(options)); FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
ret = pf_printf_w(puts_clbk_str_c99_w, ret = pf_printf_w(puts_clbk_str_c99_w,
&ctx, format, locale, 0, arg_clbk_valist, NULL, &valist); &ctx, format, locale, options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist);
puts_clbk_str_w(&ctx, 1, &nullbyte); puts_clbk_str_w(&ctx, 1, &nullbyte);
if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION) if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION)

View File

@ -36,6 +36,27 @@
#define UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY (0x0008) #define UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY (0x0008)
#define UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS (0x0010) #define UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS (0x0010)
static inline float __port_infinity(void)
{
static const unsigned __inf_bytes = 0x7f800000;
return *(const float *)&__inf_bytes;
}
#define INFINITY __port_infinity()
static inline float __port_nan(void)
{
static const unsigned __nan_bytes = 0x7fc00000;
return *(const float *)&__nan_bytes;
}
#define NAN __port_nan()
static inline float __port_ind(void)
{
static const unsigned __ind_bytes = 0xffc00000;
return *(const float *)&__ind_bytes;
}
#define IND __port_ind()
static int (__cdecl *p_vfprintf)(unsigned __int64 options, FILE *file, const char *format, static int (__cdecl *p_vfprintf)(unsigned __int64 options, FILE *file, const char *format,
void *locale, __ms_va_list valist); void *locale, __ms_va_list valist);
static int (__cdecl *p_vsprintf)(unsigned __int64 options, char *str, size_t len, const char *format, static int (__cdecl *p_vsprintf)(unsigned __int64 options, char *str, size_t len, const char *format,
@ -336,6 +357,72 @@ static void test_vsnprintf_s(void)
ok( !strcmp(out1, buffer), "buffer wrong, got=%s\n", buffer); ok( !strcmp(out1, buffer), "buffer wrong, got=%s\n", buffer);
} }
static void test_printf_legacy_wide(void)
{
const wchar_t wide[] = {'A','B','C','D',0};
const char narrow[] = "abcd";
const char out[] = "abcd ABCD";
/* The legacy wide flag doesn't affect narrow printfs, so the same
* format should behave the same both with and without the flag. */
const char narrow_fmt[] = "%s %ls";
/* The standard behaviour is to use the same format as for the narrow
* case, while the legacy case has got a different meaning for %s. */
const wchar_t std_wide_fmt[] = {'%','s',' ','%','l','s',0};
const wchar_t legacy_wide_fmt[] = {'%','h','s',' ','%','s',0};
char buffer[20];
wchar_t wbuffer[20];
vsprintf_wrapper(0, buffer, sizeof(buffer), narrow_fmt, narrow, wide);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS, buffer, sizeof(buffer), narrow_fmt, narrow, wide);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vswprintf_wrapper(0, wbuffer, sizeof(wbuffer), std_wide_fmt, narrow, wide);
WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), NULL, NULL);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vswprintf_wrapper(UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS, wbuffer, sizeof(wbuffer), legacy_wide_fmt, narrow, wide);
WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), NULL, NULL);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
}
static void test_printf_legacy_msvcrt(void)
{
char buf[50];
/* In standard mode, %F is a float format conversion, while it is a
* length modifier in legacy msvcrt mode. In legacy mode, N is also
* a length modifier. */
vsprintf_wrapper(0, buf, sizeof(buf), "%F", 1.23);
ok(!strcmp(buf, "1.230000"), "buf = %s\n", buf);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%Fd %Nd", 123, 456);
ok(!strcmp(buf, "123 456"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F %f %e %E %g %G", INFINITY, INFINITY, -INFINITY, INFINITY, INFINITY, INFINITY, INFINITY);
ok(!strcmp(buf, "inf INF -inf inf INF inf INF"), "buf = %s\n", buf);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", INFINITY);
ok(!strcmp(buf, "1.#INF00"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F", NAN, NAN);
ok(!strcmp(buf, "nan NAN"), "buf = %s\n", buf);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", NAN);
ok(!strcmp(buf, "1.#QNAN0"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F", IND, IND);
ok(!strcmp(buf, "-nan(ind) -NAN(IND)"), "buf = %s\n", buf);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", IND);
ok(!strcmp(buf, "-1.#IND00"), "buf = %s\n", buf);
}
static void test_printf_legacy_three_digit_exp(void)
{
char buf[20];
vsprintf_wrapper(0, buf, sizeof(buf), "%E", 1.23);
ok(!strcmp(buf, "1.230000E+00"), "buf = %s\n", buf);
vsprintf_wrapper(UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS, buf, sizeof(buf), "%E", 1.23);
ok(!strcmp(buf, "1.230000E+000"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%E", 1.23e+123);
ok(!strcmp(buf, "1.230000E+123"), "buf = %s\n", buf);
}
START_TEST(printf) START_TEST(printf)
{ {
if (!init()) return; if (!init()) return;
@ -344,4 +431,7 @@ START_TEST(printf)
test_swprintf(); test_swprintf();
test_fprintf(); test_fprintf();
test_vsnprintf_s(); test_vsnprintf_s();
test_printf_legacy_wide();
test_printf_legacy_msvcrt();
test_printf_legacy_three_digit_exp();
} }