[PATCH v4 1/7] ucrtbase: Handle the swprintf style termination and return values

Martin Storsjo martin at martin.st
Tue Nov 3 12:40:35 CST 2015


When neither the "legacy vsprintf null termination" nor the
"standard snprintf behaviour" flags are specified, the functions
return -2 when the output string didn't fit (similar to old style
msvcrt _snprintf that returned -1), but they are null terminated
(similar to the C99 snprintf style behaviour). This is the same
way as the C99 swprintf function behaves.

Signed-off-by: Martin Storsjo <martin at martin.st>
---
v2: Adjusted the handling of the flags, as suggested by Piotr.
v3: Unchanged, previously approved by Piotr.
v4: Unchanged, previously approved by Piotr.
---
 dlls/msvcrt/msvcrt.h         |  3 +++
 dlls/msvcrt/wcs.c            | 40 ++++++++++++++++++++++++++--------------
 dlls/ucrtbase/tests/printf.c | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 14 deletions(-)

diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h
index a3b2595..f11d0a8 100644
--- a/dlls/msvcrt/msvcrt.h
+++ b/dlls/msvcrt/msvcrt.h
@@ -1130,6 +1130,9 @@ 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_THREE_DIGIT_EXPONENTS     (0x0010)
 
+#define UCRTBASE_PRINTF_TERMINATION_MASK                 (UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION | \
+                                                          UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR)
+
 #define UCRTBASE_SCANF_SECURECRT                         (0x0001)
 #define UCRTBASE_SCANF_LEGACY_WIDE_SPECIFIERS            (0x0002)
 #define UCRTBASE_SCANF_LEGACY_MSVCRT_COMPATIBILITY       (0x0004)
diff --git a/dlls/msvcrt/wcs.c b/dlls/msvcrt/wcs.c
index 0bfddbb..169012c 100644
--- a/dlls/msvcrt/wcs.c
+++ b/dlls/msvcrt/wcs.c
@@ -701,10 +701,10 @@ static int puts_clbk_str_c99_a(void *ctx, int len, const char *str)
     if(!out->buf)
         return len;
 
-    if(out->len - 1 < len) {
-        memcpy(out->buf, str, out->len - 1);
-        out->buf += out->len - 1;
-        out->len = 1;
+    if(out->len < len) {
+        memcpy(out->buf, str, out->len);
+        out->buf += out->len;
+        out->len = 0;
         return len;
     }
 
@@ -724,12 +724,18 @@ 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_LEGACY_VSPRINTF_NULL_TERMINATION &&
-        options != UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR)
+    if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK)
         FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
-    ret = pf_printf_a(options & UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR ? puts_clbk_str_c99_a : puts_clbk_str_a,
+    ret = pf_printf_a(puts_clbk_str_c99_a,
             &ctx, format, locale, FALSE, FALSE, arg_clbk_valist, NULL, &valist);
     puts_clbk_str_a(&ctx, 1, &nullbyte);
+
+    if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION)
+        return ret>len ? -1 : ret;
+    if(ret>=len) {
+        if(len) str[len-1] = 0;
+        return (options & UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR) ? ret : -2;
+    }
     return ret;
 }
 
@@ -1148,10 +1154,10 @@ static int puts_clbk_str_c99_w(void *ctx, int len, const MSVCRT_wchar_t *str)
     if(!out->buf)
         return len;
 
-    if(out->len - 1 < len) {
-        memcpy(out->buf, str, (out->len - 1)*sizeof(MSVCRT_wchar_t));
-        out->buf += out->len - 1;
-        out->len = 1;
+    if(out->len < len) {
+        memcpy(out->buf, str, out->len*sizeof(MSVCRT_wchar_t));
+        out->buf += out->len;
+        out->len = 0;
         return len;
     }
 
@@ -1171,12 +1177,18 @@ int CDECL MSVCRT__stdio_common_vswprintf( unsigned __int64 options, MSVCRT_wchar
     struct _str_ctx_w ctx = {len, str};
     int ret;
 
-    if (options != UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION &&
-        options != UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR)
+    if (options & ~UCRTBASE_PRINTF_TERMINATION_MASK)
         FIXME("options %s not handled\n", wine_dbgstr_longlong(options));
-    ret = pf_printf_w(options & UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR ? puts_clbk_str_c99_w : puts_clbk_str_w,
+    ret = pf_printf_w(puts_clbk_str_c99_w,
             &ctx, format, locale, FALSE, FALSE, arg_clbk_valist, NULL, &valist);
     puts_clbk_str_w(&ctx, 1, &nullbyte);
+
+    if(options & UCRTBASE_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION)
+        return ret>len ? -1 : ret;
+    if(ret>=len) {
+        if(len) str[len-1] = 0;
+        return (options & UCRTBASE_PRINTF_STANDARD_SNPRINTF_BEHAVIOUR) ? ret : -2;
+    }
     return ret;
 }
 
diff --git a/dlls/ucrtbase/tests/printf.c b/dlls/ucrtbase/tests/printf.c
index 99d756e..ce47e2a 100644
--- a/dlls/ucrtbase/tests/printf.c
+++ b/dlls/ucrtbase/tests/printf.c
@@ -120,6 +120,21 @@ static void test_snprintf (void)
         ok (buffer[valid] == '\0',
             "\"%s\": Missing null termination (ret %d) - is %d\n", fmt, n, buffer[valid]);
     }
+
+    /* swprintf style termination */
+    for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+        const char *fmt  = tests[i];
+        const int expect = strlen(fmt) >= bufsiz ? -2 : strlen(fmt);
+        const int n      = vsprintf_wrapper (0, buffer, bufsiz, fmt);
+        const int valid  = n < 0 ? bufsiz - 1 : n;
+
+        ok (n == expect, "\"%s\": expected %d, returned %d\n",
+            fmt, expect, n);
+        ok (!memcmp (fmt, buffer, valid),
+            "\"%s\": rendered \"%.*s\"\n", fmt, valid, buffer);
+        ok (buffer[valid] == '\0',
+            "\"%s\": Missing null termination (ret %d) - is %d\n", fmt, n, buffer[valid]);
+    }
 }
 
 static int __cdecl vswprintf_wrapper(unsigned __int64 options, wchar_t *str,
@@ -177,6 +192,23 @@ static void test_swprintf (void)
         ok (buffer[valid] == '\0',
             "\"%s\": Missing null termination (ret %d) - is %d\n", narrow_fmt, n, buffer[valid]);
     }
+
+    /* swprintf style termination */
+    for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+        const wchar_t *fmt = tests[i];
+        const int expect   = wcslen(fmt) >= bufsiz ? -2 : wcslen(fmt);
+        const int n        = vswprintf_wrapper (0, buffer, bufsiz, fmt);
+        const int valid    = n < 0 ? bufsiz - 1 : n;
+
+        WideCharToMultiByte (CP_ACP, 0, buffer, -1, narrow, sizeof(narrow), NULL, NULL);
+        WideCharToMultiByte (CP_ACP, 0, fmt, -1, narrow_fmt, sizeof(narrow_fmt), NULL, NULL);
+        ok (n == expect, "\"%s\": expected %d, returned %d\n",
+            narrow_fmt, expect, n);
+        ok (!memcmp (fmt, buffer, valid * sizeof(wchar_t)),
+            "\"%s\": rendered \"%.*s\"\n", narrow_fmt, valid, narrow);
+        ok (buffer[valid] == '\0',
+            "\"%s\": Missing null termination (ret %d) - is %d\n", narrow_fmt, n, buffer[valid]);
+    }
 }
 
 static int __cdecl vfprintf_wrapper(FILE *file,
-- 
1.8.1.2




More information about the wine-patches mailing list