[PATCH 07/10] ucrtbase: Implement the new printf corner case behaviour

Martin Storsjo martin at martin.st
Mon Nov 2 04:08:41 CST 2015


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
(positioknal params, invoke invalid param handler).

Signed-off-by: Martin Storsjo <martin at martin.st>
---
 dlls/msvcrt/file.c           |  3 +-
 dlls/msvcrt/msvcrt.h         |  2 +
 dlls/msvcrt/printf.h         | 51 +++++++++++++++++++------
 dlls/msvcrt/wcs.c            | 31 ++++++++-------
 dlls/ucrtbase/tests/printf.c | 90 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 150 insertions(+), 27 deletions(-)

diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c
index 5e58d0b..156c86b 100644
--- a/dlls/msvcrt/file.c
+++ b/dlls/msvcrt/file.c
@@ -4996,7 +4996,8 @@ int CDECL MSVCRT__stdio_common_vfprintf(unsigned __int64 options, MSVCRT_FILE *f
 
     MSVCRT__lock_file(file);
     tmp_buf = add_std_buffer(file);
-    ret = pf_printf_a(puts_clbk_file_a, file, format, locale, 0, arg_clbk_valist, NULL, &valist);
+    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);
     MSVCRT__unlock_file(file);
 
diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h
index 81aea52..7e71687 100644
--- a/dlls/msvcrt/msvcrt.h
+++ b/dlls/msvcrt/msvcrt.h
@@ -1133,6 +1133,8 @@ extern char* __cdecl __unDName(char *,const char*,int,malloc_func_t,free_func_t,
 #define UCRTBASE_PRINTF_TERMINATION_MASK                 (UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION | \
                                                           UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR)
 
+#define UCRTBASE_PRINTF_MASK                             (0x001F)
+
 #define MSVCRT_PRINTF_POSITIONAL_PARAMS                  (0x0100)
 #define MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER       (0x0200)
 
diff --git a/dlls/msvcrt/printf.h b/dlls/msvcrt/printf.h
index a724404..1e215aa 100644
--- a/dlls/msvcrt/printf.h
+++ b/dlls/msvcrt/printf.h
@@ -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;
 
-    if(flags->Sign && !strchr("diaeEfgG", flags->Format))
+    if(flags->Sign && !strchr("diaeEfFgG", flags->Format))
         flags->Sign = 0;
 
     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,
-        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
     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')
         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);
     else
         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;
 
@@ -313,7 +314,11 @@ static inline void FUNC_NAME(pf_fixup_exponent)(char *buf)
 
     if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
             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);
+#endif
 
         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;
     BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS;
     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));
 
@@ -461,7 +473,7 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
                     p++;
             } else if(*p == 'w')
                 flags.WideString = *p++;
-            else if(*p == 'F' || *p == 'N')
+            else if((*p == 'F' || *p == 'N') && legacy_msvcrt_compat)
                 p++; /* ignore */
             else
                 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') {
             i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
                     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') {
             int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
 
             if((ch&0xff) != ch)
                 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') {
             flags.Format = 'X';
             flags.PadZero = '0';
@@ -540,7 +552,7 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
 #endif
             if(tmp != buf)
                 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;
             int len = flags.Precision + 10;
             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)
                     i = 1;
                 else
@@ -594,9 +606,24 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API
                 val = -val;
             }
 
-            sprintf(tmp, float_fmt, val);
-            if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G')
-                FUNC_NAME(pf_fixup_exponent)(tmp);
+            if((inf || nan || ind) && !legacy_msvcrt_compat) {
+                static const char inf_str[] = "inf";
+                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, '.');
             if(decimal_point) {
diff --git a/dlls/msvcrt/wcs.c b/dlls/msvcrt/wcs.c
index d94bcd5..e41bbd7 100644
--- a/dlls/msvcrt/wcs.c
+++ b/dlls/msvcrt/wcs.c
@@ -724,10 +724,8 @@ int CDECL MSVCRT__stdio_common_vsprintf( unsigned __int64 options, char *str, MS
     struct _str_ctx_a ctx = {len, str};
     int ret;
 
-    if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK)
-        FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
     ret = pf_printf_a(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION ? puts_clbk_str_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);
     if ((options & UCRTBASE_PRINTF_TERMINATION_MASK) == 0 && ret >= len)
         ret = -2;
@@ -773,11 +771,8 @@ int CDECL MSVCRT_sprintf_l(char *str, const char *format,
     return retval;
 }
 
-/*********************************************************************
- *		_vsnprintf_s_l (MSVCRT.@)
- */
-int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
-        MSVCRT_size_t count, const char *format,
+static int CDECL MSVCRT_vsnprintf_s_l_opt( char *str, MSVCRT_size_t sizeOfBuffer,
+        MSVCRT_size_t count, const char *format, DWORD options,
         MSVCRT__locale_t locale, __ms_va_list valist )
 {
     static const char nullbyte = '\0';
@@ -791,7 +786,7 @@ int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
 
     ctx.len = len;
     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);
     puts_clbk_str_a(&ctx, 1, &nullbyte);
 
@@ -809,6 +804,16 @@ int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
 }
 
 /*********************************************************************
+ *		_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.@)
  */
 int CDECL MSVCRT_vsprintf_s_l( char *str, MSVCRT_size_t count, const char *format,
@@ -846,7 +851,7 @@ int CDECL MSVCRT_vsnprintf_s( char *str, MSVCRT_size_t sizeOfBuffer,
 int CDECL MSVCRT__stdio_common_vsnprintf_s( unsigned __int64 options, 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(str, sizeOfBuffer, count, format, locale, valist);
+    return MSVCRT_vsnprintf_s_l_opt(str, sizeOfBuffer, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist);
 }
 
 /*********************************************************************
@@ -855,7 +860,7 @@ int CDECL MSVCRT__stdio_common_vsnprintf_s( unsigned __int64 options, char *str,
 int CDECL MSVCRT__stdio_common_vsprintf_s( unsigned __int64 options, char *str, MSVCRT_size_t count, const char *format,
                                       MSVCRT__locale_t locale, __ms_va_list valist )
 {
-    return MSVCRT_vsnprintf_s_l(str, INT_MAX, count, format, locale, valist);
+    return MSVCRT_vsnprintf_s_l_opt(str, INT_MAX, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist);
 }
 
 /*********************************************************************
@@ -1174,10 +1179,8 @@ int CDECL MSVCRT__stdio_common_vswprintf( unsigned __int64 options, MSVCRT_wchar
     struct _str_ctx_w ctx = {len, str};
     int ret;
 
-    if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK)
-        FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
     ret = pf_printf_w(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION ? puts_clbk_str_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);
     if ((options & UCRTBASE_PRINTF_TERMINATION_MASK) == 0 && ret >= len)
         ret = -2;
diff --git a/dlls/ucrtbase/tests/printf.c b/dlls/ucrtbase/tests/printf.c
index ce47e2a..ff6eb01 100644
--- a/dlls/ucrtbase/tests/printf.c
+++ b/dlls/ucrtbase/tests/printf.c
@@ -36,6 +36,27 @@
 #define UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY      (0x0008)
 #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,
                                  void *locale, __ms_va_list valist);
 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);
 }
 
+static void test_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_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_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)
 {
     if (!init()) return;
@@ -344,4 +431,7 @@ START_TEST(printf)
     test_swprintf();
     test_fprintf();
     test_vsnprintf_s();
+    test_legacy_wide();
+    test_legacy_msvcrt();
+    test_legacy_three_digit_exp();
 }
-- 
1.8.1.2




More information about the wine-patches mailing list