[PATCH v2] ucrtbase: Implement some printf functions

Martin Storsjo martin at martin.st
Mon Oct 26 07:42:06 CDT 2015


Not all functions are implemented (yet not all variants are
implemented in the normal msvcrt either).

The new functions are more generalized and are C99 compliant.
They take an options parameter that e.g. for the snprintf
family of functions indicate whether the truncation and return
value should be handled as before or in the standards compliant
way.

Some rudimentary tests are added (based on the msvcrt printf tests),
testing to make sure the snprintf truncation/return values are
handled correctly.

These new functions also should support the C99 conversion
specifiers; this is not yet implemented.

Signed-off-by: Martin Storsjo <martin at martin.st>
---
Fixed test failures on 64 bit, by adding the necessary __cdecl
declaration to vararg functions in the tests.

Implemented the swprintf funtion as well.
---
 .../api-ms-win-crt-stdio-l1-1-0.spec               |  10 +-
 dlls/msvcrt/file.c                                 |  28 +++
 dlls/msvcrt/printf.h                               |  20 ++
 dlls/msvcrt/wcs.c                                  |  34 +++
 dlls/ucrtbase/tests/Makefile.in                    |   1 +
 dlls/ucrtbase/tests/printf.c                       | 259 +++++++++++++++++++++
 dlls/ucrtbase/ucrtbase.spec                        |  10 +-
 7 files changed, 352 insertions(+), 10 deletions(-)
 create mode 100644 dlls/ucrtbase/tests/printf.c

diff --git a/dlls/api-ms-win-crt-stdio-l1-1-0/api-ms-win-crt-stdio-l1-1-0.spec b/dlls/api-ms-win-crt-stdio-l1-1-0/api-ms-win-crt-stdio-l1-1-0.spec
index 5516364..9d3bea7 100644
--- a/dlls/api-ms-win-crt-stdio-l1-1-0/api-ms-win-crt-stdio-l1-1-0.spec
+++ b/dlls/api-ms-win-crt-stdio-l1-1-0/api-ms-win-crt-stdio-l1-1-0.spec
@@ -1,7 +1,7 @@
-@ stub __acrt_iob_func
+@ cdecl __acrt_iob_func(long) ucrtbase.__acrt_iob_func
 @ cdecl __p__commode() ucrtbase.__p__commode
 @ cdecl __p__fmode() ucrtbase.__p__fmode
-@ stub __stdio_common_vfprintf
+@ cdecl __stdio_common_vfprintf(int64 ptr ptr ptr ptr) ucrtbase.__stdio_common_vfprintf
 @ stub __stdio_common_vfprintf_p
 @ stub __stdio_common_vfprintf_s
 @ stub __stdio_common_vfscanf
@@ -9,11 +9,11 @@
 @ stub __stdio_common_vfwprintf_p
 @ stub __stdio_common_vfwprintf_s
 @ stub __stdio_common_vfwscanf
-@ stub __stdio_common_vsnprintf_s
+@ cdecl __stdio_common_vsnprintf_s(int64 ptr long long ptr ptr ptr) ucrtbase.__stdio_common_vsnprintf_s
 @ stub __stdio_common_vsnwprintf_s
-@ stub __stdio_common_vsprintf
+@ cdecl __stdio_common_vsprintf(int64 ptr long ptr ptr ptr) ucrtbase.__stdio_common_vsprintf
 @ stub __stdio_common_vsprintf_p
-@ stub __stdio_common_vsprintf_s
+@ cdecl __stdio_common_vsprintf_s(int64 ptr long ptr ptr ptr) ucrtbase.__stdio_common_vsprintf_s
 @ stub __stdio_common_vsscanf
 @ stub __stdio_common_vswprintf
 @ stub __stdio_common_vswprintf_p
diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c
index f304ec4..7443ba6 100644
--- a/dlls/msvcrt/file.c
+++ b/dlls/msvcrt/file.c
@@ -758,6 +758,14 @@ MSVCRT_FILE * CDECL MSVCRT___iob_func(void)
 }
 
 /*********************************************************************
+ *		__acrt_iob_func(MSVCRT.@)
+ */
+MSVCRT_FILE * CDECL MSVCRT___acrt_iob_func(unsigned idx)
+{
+ return &MSVCRT__iob[idx];
+}
+
+/*********************************************************************
  *		_access (MSVCRT.@)
  */
 int CDECL MSVCRT__access(const char *filename, int mode)
@@ -4976,6 +4984,26 @@ int CDECL MSVCRT_vfwprintf_s(MSVCRT_FILE* file, const MSVCRT_wchar_t *format, __
 }
 
 /*********************************************************************
+ *              __stdio_common_vfprintf (MSVCRT.@)
+ */
+int CDECL MSVCRT__stdio_common_vfprintf(unsigned __int64 options, MSVCRT_FILE *file, const char *format,
+                                        MSVCRT__locale_t locale, __ms_va_list valist)
+{
+    BOOL tmp_buf;
+    int ret;
+
+    if (!MSVCRT_CHECK_PMT( file != NULL )) return -1;
+
+    MSVCRT__lock_file(file);
+    tmp_buf = add_std_buffer(file);
+    ret = pf_printf_a(puts_clbk_file_a, file, format, locale, FALSE, FALSE, arg_clbk_valist, NULL, &valist);
+    if(tmp_buf) remove_std_buffer(file);
+    MSVCRT__unlock_file(file);
+
+    return ret;
+}
+
+/*********************************************************************
  *              _vfwprintf_l (MSVCRT.@)
  */
 int CDECL MSVCRT__vfwprintf_l(MSVCRT_FILE* file, const MSVCRT_wchar_t *format,
diff --git a/dlls/msvcrt/printf.h b/dlls/msvcrt/printf.h
index 4757931..bd2dbf3 100644
--- a/dlls/msvcrt/printf.h
+++ b/dlls/msvcrt/printf.h
@@ -64,6 +64,26 @@ static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
     return len;
 }
 
+static int FUNC_NAME(puts_clbk_str_c99)(void *ctx, int len, const APICHAR *str)
+{
+    struct FUNC_NAME(_str_ctx) *out = ctx;
+
+    if(!out->buf)
+        return len;
+
+    if(out->len - 1 < len) {
+        memcpy(out->buf, str, (out->len - 1)*sizeof(APICHAR));
+        out->buf += out->len - 1;
+        out->len = 1;
+        return len;
+    }
+
+    memcpy(out->buf, str, len*sizeof(APICHAR));
+    out->buf += len;
+    out->len -= len;
+    return len;
+}
+
 static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
 {
     *val = 0;
diff --git a/dlls/msvcrt/wcs.c b/dlls/msvcrt/wcs.c
index 913b0a7..9202e59 100644
--- a/dlls/msvcrt/wcs.c
+++ b/dlls/msvcrt/wcs.c
@@ -695,6 +695,22 @@ int CDECL MSVCRT_vsnprintf( char *str, MSVCRT_size_t len,
 }
 
 /*********************************************************************
+ *		__stdio_common_vsprintf (MSVCRT.@)
+ */
+int CDECL MSVCRT__stdio_common_vsprintf( unsigned __int64 options, char *str, MSVCRT_size_t len, const char *format,
+                                         MSVCRT__locale_t locale, __ms_va_list valist )
+{
+    static const char nullbyte = '\0';
+    struct _str_ctx_a ctx = {len, str};
+    int ret;
+
+    ret = pf_printf_a(options & 2 ? puts_clbk_str_c99_a : puts_clbk_str_a,
+            &ctx, format, locale, FALSE, FALSE, arg_clbk_valist, NULL, &valist);
+    puts_clbk_str_a(&ctx, 1, &nullbyte);
+    return ret;
+}
+
+/*********************************************************************
  *		_vsnprintf_l (MSVCRT.@)
  */
 int CDECL MSVCRT_vsnprintf_l( char *str, MSVCRT_size_t len, const char *format,
@@ -801,6 +817,24 @@ int CDECL MSVCRT_vsnprintf_s( char *str, MSVCRT_size_t sizeOfBuffer,
 }
 
 /*********************************************************************
+ *		__stdio_common_vsnprintf_s (MSVCRT.@)
+ */
+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);
+}
+
+/*********************************************************************
+ *		__stdio_common_vsprintf_s (MSVCRT.@)
+ */
+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);
+}
+
+/*********************************************************************
  *		vsprintf (MSVCRT.@)
  */
 int CDECL MSVCRT_vsprintf( char *str, const char *format, __ms_va_list valist)
diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in
index 2a57e5e..c4fa7c8 100644
--- a/dlls/ucrtbase/tests/Makefile.in
+++ b/dlls/ucrtbase/tests/Makefile.in
@@ -2,4 +2,5 @@ TESTDLL   = ucrtbase.dll
 APPMODE   = -mno-cygwin
 
 C_SRCS = \
+	printf.c \
 	string.c
diff --git a/dlls/ucrtbase/tests/printf.c b/dlls/ucrtbase/tests/printf.c
new file mode 100644
index 0000000..b4588a1
--- /dev/null
+++ b/dlls/ucrtbase/tests/printf.c
@@ -0,0 +1,259 @@
+/*
+ * Conformance tests for *printf functions.
+ *
+ * Copyright 2002 Uwe Bonnes
+ * Copyright 2004 Aneurin Price
+ * Copyright 2005 Mike McCormack
+ * Copyright 2015 Martin Storsjo
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+
+#include "wine/test.h"
+
+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,
+                                 void *locale, __ms_va_list valist);
+static int (__cdecl *p_vsnprintf_s)(unsigned __int64 options, char *str, size_t sizeOfBuffer, size_t count, const char *format,
+                                    void *locale, __ms_va_list valist);
+static int (__cdecl *p_vsprintf_s)(unsigned __int64 options, char *str, size_t count, const char *format,
+                                    void *locale, __ms_va_list valist);
+
+static FILE *(__cdecl *p_fopen)(const char *name, const char *mode);
+static int (__cdecl *p_fclose)(FILE *file);
+static long (__cdecl *p_ftell)(FILE *file);
+static char *(__cdecl *p_fgets)(char *str, int size, FILE *file);
+
+static BOOL init( void )
+{
+    HMODULE hmod = LoadLibraryA("ucrtbase.dll");
+
+    if (!hmod)
+    {
+        win_skip("ucrtbase.dll not installed\n");
+        return FALSE;
+    }
+
+    p_vfprintf = (void *)GetProcAddress(hmod, "__stdio_common_vfprintf");
+    p_vsprintf = (void *)GetProcAddress(hmod, "__stdio_common_vsprintf");
+    p_vsnprintf_s = (void *)GetProcAddress(hmod, "__stdio_common_vsnprintf_s");
+    p_vsprintf_s = (void *)GetProcAddress(hmod, "__stdio_common_vsprintf_s");
+
+    p_fopen = (void *)GetProcAddress(hmod, "fopen");
+    p_fclose = (void *)GetProcAddress(hmod, "fclose");
+    p_ftell = (void *)GetProcAddress(hmod, "ftell");
+    p_fgets = (void *)GetProcAddress(hmod, "fgets");
+    return TRUE;
+}
+
+static int __cdecl vsprintf_wrapper(unsigned __int64 options, char *str,
+                                    size_t len, const char *format, ...)
+{
+    int ret;
+    __ms_va_list valist;
+    __ms_va_start(valist, format);
+    ret = p_vsprintf(options, str, len, format, NULL, valist);
+    __ms_va_end(valist);
+    return ret;
+}
+
+static void test_snprintf (void)
+{
+    struct snprintf_test {
+        const char *format;
+        int expected;
+    };
+    /* Legacy behaviour, not C99 compliant. */
+    const struct snprintf_test tests_legacy[] = {{"short", 5},
+                                                 {"justfit", 7},
+                                                 {"justfits", 8},
+                                                 {"muchlonger", -1}};
+    /* New C99 compliant behaviour. */
+    const struct snprintf_test tests_standard[] = {{"short", 5},
+                                                   {"justfit", 7},
+                                                   {"justfits", 8},
+                                                   {"muchlonger", 10}};
+    char buffer[8];
+    const int bufsiz = sizeof buffer;
+    unsigned int i;
+
+    for (i = 0; i < sizeof tests_legacy / sizeof tests_legacy[0]; i++) {
+        const char *fmt  = tests_legacy[i].format;
+        const int expect = tests_legacy[i].expected;
+        const int n      = vsprintf_wrapper (1, buffer, bufsiz, fmt);
+        const int valid  = n < 0 ? bufsiz : (n == bufsiz ? n : n+1);
+
+        ok (n == expect, "\"%s\": expected %d, returned %d\n",
+            fmt, expect, n);
+        ok (!memcmp (fmt, buffer, valid),
+            "\"%s\": rendered \"%.*s\"\n", fmt, valid, buffer);
+    }
+
+    for (i = 0; i < sizeof tests_standard / sizeof tests_standard[0]; i++) {
+        const char *fmt  = tests_standard[i].format;
+        const int expect = tests_standard[i].expected;
+        const int n      = vsprintf_wrapper (2, buffer, bufsiz, fmt);
+        const int valid  = n >= bufsiz ? bufsiz - 1 : n < 0 ? 0 : 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 vfprintf_wrapper(FILE *file,
+                                    const char *format, ...)
+{
+    int ret;
+    __ms_va_list valist;
+    __ms_va_start(valist, format);
+    ret = p_vfprintf(0, file, format, NULL, valist);
+    __ms_va_end(valist);
+    return ret;
+}
+
+static void test_fprintf(void)
+{
+    static const char file_name[] = "fprintf.tst";
+
+    FILE *fp = p_fopen(file_name, "wb");
+    char buf[1024];
+    int ret;
+
+    ret = vfprintf_wrapper(fp, "simple test\n");
+    ok(ret == 12, "ret = %d\n", ret);
+    ret = p_ftell(fp);
+    ok(ret == 12, "ftell returned %d\n", ret);
+
+    ret = vfprintf_wrapper(fp, "contains%cnull\n", '\0');
+    ok(ret == 14, "ret = %d\n", ret);
+    ret = p_ftell(fp);
+    ok(ret == 26, "ftell returned %d\n", ret);
+
+    p_fclose(fp);
+
+    fp = p_fopen(file_name, "rb");
+    p_fgets(buf, sizeof(buf), fp);
+    ret = p_ftell(fp);
+    ok(ret == 12, "ftell returned %d\n", ret);
+    ok(!strcmp(buf, "simple test\n"), "buf = %s\n", buf);
+
+    p_fgets(buf, sizeof(buf), fp);
+    ret = p_ftell(fp);
+    ok(ret == 26, "ret = %d\n", ret);
+    ok(!memcmp(buf, "contains\0null\n", 14), "buf = %s\n", buf);
+
+    p_fclose(fp);
+
+    fp = p_fopen(file_name, "wt");
+
+    ret = vfprintf_wrapper(fp, "simple test\n");
+    ok(ret == 12, "ret = %d\n", ret);
+    ret = p_ftell(fp);
+    ok(ret == 13, "ftell returned %d\n", ret);
+
+    ret = vfprintf_wrapper(fp, "contains%cnull\n", '\0');
+    ok(ret == 14, "ret = %d\n", ret);
+    ret = p_ftell(fp);
+    ok(ret == 28, "ftell returned %d\n", ret);
+
+    p_fclose(fp);
+
+    fp = p_fopen(file_name, "rb");
+    p_fgets(buf, sizeof(buf), fp);
+    ret = p_ftell(fp);
+    ok(ret == 13, "ftell returned %d\n", ret);
+    ok(!strcmp(buf, "simple test\r\n"), "buf = %s\n", buf);
+
+    p_fgets(buf, sizeof(buf), fp);
+    ret = p_ftell(fp);
+    ok(ret == 28, "ret = %d\n", ret);
+    ok(!memcmp(buf, "contains\0null\r\n", 15), "buf = %s\n", buf);
+
+    p_fclose(fp);
+    unlink(file_name);
+}
+
+static int __cdecl _vsnprintf_s_wrapper(char *str, size_t sizeOfBuffer,
+                                        size_t count, const char *format, ...)
+{
+    int ret;
+    __ms_va_list valist;
+    __ms_va_start(valist, format);
+    ret = p_vsnprintf_s(0, str, sizeOfBuffer, count, format, NULL, valist);
+    __ms_va_end(valist);
+    return ret;
+}
+
+static void test_vsnprintf_s(void)
+{
+    const char format[] = "AB%uC";
+    const char out7[] = "AB123C";
+    const char out6[] = "AB123";
+    const char out2[] = "A";
+    const char out1[] = "";
+    char buffer[14] = { 0 };
+    int exp, got;
+
+    /* Enough room. */
+    exp = strlen(out7);
+
+    got = _vsnprintf_s_wrapper(buffer, 14, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
+
+    got = _vsnprintf_s_wrapper(buffer, 12, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
+
+    got = _vsnprintf_s_wrapper(buffer, 7, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
+
+    /* Not enough room. */
+    exp = -1;
+
+    got = _vsnprintf_s_wrapper(buffer, 6, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out6, buffer), "buffer wrong, got=%s\n", buffer);
+
+    got = _vsnprintf_s_wrapper(buffer, 2, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out2, buffer), "buffer wrong, got=%s\n", buffer);
+
+    got = _vsnprintf_s_wrapper(buffer, 1, _TRUNCATE, format, 123);
+    ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
+    ok( !strcmp(out1, buffer), "buffer wrong, got=%s\n", buffer);
+}
+
+START_TEST(printf)
+{
+    if (!init()) return;
+
+    test_snprintf();
+    test_fprintf();
+    test_vsnprintf_s();
+}
diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec
index 58c5ee3..a42c7ee 100644
--- a/dlls/ucrtbase/ucrtbase.spec
+++ b/dlls/ucrtbase/ucrtbase.spec
@@ -72,7 +72,7 @@
 @ cdecl ___lc_locale_name_func()
 @ cdecl ___mb_cur_max_func() MSVCRT____mb_cur_max_func
 @ cdecl ___mb_cur_max_l_func(ptr)
-@ stub __acrt_iob_func
+@ cdecl __acrt_iob_func(long) MSVCRT___acrt_iob_func
 @ stub __conio_common_vcprintf
 @ stub __conio_common_vcprintf_p
 @ stub __conio_common_vcprintf_s
@@ -146,7 +146,7 @@
 @ stub __std_type_info_destroy_list
 @ stub __std_type_info_hash
 @ stub __std_type_info_name
-@ stub __stdio_common_vfprintf
+@ cdecl __stdio_common_vfprintf(int64 ptr ptr ptr ptr) MSVCRT__stdio_common_vfprintf
 @ stub __stdio_common_vfprintf_p
 @ stub __stdio_common_vfprintf_s
 @ stub __stdio_common_vfscanf
@@ -154,11 +154,11 @@
 @ stub __stdio_common_vfwprintf_p
 @ stub __stdio_common_vfwprintf_s
 @ stub __stdio_common_vfwscanf
-@ stub __stdio_common_vsnprintf_s
+@ cdecl __stdio_common_vsnprintf_s(int64 ptr long long ptr ptr ptr) MSVCRT__stdio_common_vsnprintf_s
 @ stub __stdio_common_vsnwprintf_s
-@ stub __stdio_common_vsprintf
+@ cdecl __stdio_common_vsprintf(int64 ptr long ptr ptr ptr) MSVCRT__stdio_common_vsprintf
 @ stub __stdio_common_vsprintf_p
-@ stub __stdio_common_vsprintf_s
+@ cdecl __stdio_common_vsprintf_s(int64 ptr long ptr ptr ptr) MSVCRT__stdio_common_vsprintf_s
 @ stub __stdio_common_vsscanf
 @ stub __stdio_common_vswprintf
 @ stub __stdio_common_vswprintf_p
-- 
1.8.1.2




More information about the wine-patches mailing list