ucrtbase._mbsncpy_s is used by Marvel vs Capcom when trying to create multiplayer lobby.
The functions are also present in msvcrt (unlike msvcr70, msvcr71) where I didn't add it because it behaves differently: there is at least one weirdness when it doubles the number of characters to copy ('n' parameter, not buffer size). I suppose we don't need to explore and deal with this specific until something needs those functions from msvcrt.
-- v3: msvcrt: Implement _mbsncpy_s[_l](). msvcr80/tests: Fix errno access in tests.
From: Piotr Caban piotr@codeweavers.com
--- dlls/msvcr80/tests/msvcr80.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/dlls/msvcr80/tests/msvcr80.c b/dlls/msvcr80/tests/msvcr80.c index fa04b024031..6e48ecf2894 100644 --- a/dlls/msvcr80/tests/msvcr80.c +++ b/dlls/msvcr80/tests/msvcr80.c @@ -64,6 +64,11 @@ static int (__cdecl *p_strcmp)(const char *, const char *); static int (__cdecl *p_strncmp)(const char *, const char *, size_t); static int (__cdecl *p_dupenv_s)(char **, size_t *, const char *); static int (__cdecl *p_wdupenv_s)(wchar_t **, size_t *, const wchar_t *); +static int* (__cdecl *p_errno)(void); + +/* make sure we use the correct errno */ +#undef errno +#define errno (*p_errno())
#define SETNOFAIL(x,y) x = (void*)GetProcAddress(hcrt,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -87,6 +92,7 @@ static BOOL init(void) SET(p_strncmp, "strncmp"); SET(p_dupenv_s, "_dupenv_s"); SET(p_wdupenv_s, "_wdupenv_s"); + SET(p_errno, "_errno");
return TRUE; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/msvcr100/msvcr100.spec | 4 +- dlls/msvcr110/msvcr110.spec | 4 +- dlls/msvcr120/msvcr120.spec | 4 +- dlls/msvcr80/msvcr80.spec | 4 +- dlls/msvcr80/tests/msvcr80.c | 247 ++++++++++++++++++++++++++++++++++- dlls/msvcr90/msvcr90.spec | 4 +- dlls/msvcrt/mbcs.c | 77 +++++++++++ dlls/ucrtbase/tests/string.c | 178 +++++++++++++++++++++++++ dlls/ucrtbase/ucrtbase.spec | 8 +- include/msvcrt/mbstring.h | 2 + 10 files changed, 517 insertions(+), 15 deletions(-)
diff --git a/dlls/msvcr100/msvcr100.spec b/dlls/msvcr100/msvcr100.spec index 68e502af597..7e6397051e9 100644 --- a/dlls/msvcr100/msvcr100.spec +++ b/dlls/msvcr100/msvcr100.spec @@ -1157,8 +1157,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr110/msvcr110.spec b/dlls/msvcr110/msvcr110.spec index 560002e59ed..010d222d665 100644 --- a/dlls/msvcr110/msvcr110.spec +++ b/dlls/msvcr110/msvcr110.spec @@ -1514,8 +1514,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr120/msvcr120.spec b/dlls/msvcr120/msvcr120.spec index 46715817932..caddf750038 100644 --- a/dlls/msvcr120/msvcr120.spec +++ b/dlls/msvcr120/msvcr120.spec @@ -1525,8 +1525,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr80/msvcr80.spec b/dlls/msvcr80/msvcr80.spec index 6cce6ee22ee..b4548945236 100644 --- a/dlls/msvcr80/msvcr80.spec +++ b/dlls/msvcr80/msvcr80.spec @@ -829,8 +829,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcr80/tests/msvcr80.c b/dlls/msvcr80/tests/msvcr80.c index 6e48ecf2894..506d3e695e9 100644 --- a/dlls/msvcr80/tests/msvcr80.c +++ b/dlls/msvcr80/tests/msvcr80.c @@ -37,8 +37,48 @@ #define WX_TTY 0x40 #define WX_TEXT 0x80
+#define _MB_CP_SBCS 0 + #define MSVCRT_FD_BLOCK_SIZE 32
+#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT(func) \ + do { \ + CHECK_EXPECT2(func); \ + expect_ ## func = FALSE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +DEFINE_EXPECT(invalid_parameter_handler); + +static void __cdecl test_invalid_parameter_handler(const wchar_t *expression, + const wchar_t *function, const wchar_t *file, + unsigned line, uintptr_t arg) +{ + CHECK_EXPECT(invalid_parameter_handler); + ok(expression == NULL, "expression is not NULL\n"); + ok(function == NULL, "function is not NULL\n"); + ok(file == NULL, "file is not NULL\n"); + ok(line == 0, "line = %u\n", line); + ok(arg == 0, "arg = %Ix\n", arg); +} + typedef struct { HANDLE handle; @@ -57,6 +97,7 @@ typedef struct
static ioinfo **__pioinfo;
+static _invalid_parameter_handler (__cdecl *p__set_invalid_parameter_handler)(_invalid_parameter_handler); static int (WINAPIV *p__open)(const char *, int, ...); static int (__cdecl *p__close)(int); static intptr_t (__cdecl *p__get_osfhandle)(int); @@ -65,6 +106,10 @@ static int (__cdecl *p_strncmp)(const char *, const char *, size_t); static int (__cdecl *p_dupenv_s)(char **, size_t *, const char *); static int (__cdecl *p_wdupenv_s)(wchar_t **, size_t *, const wchar_t *); static int* (__cdecl *p_errno)(void); +static errno_t (__cdecl *p__mbsncpy_s)(unsigned char*,size_t,const unsigned char*,size_t); +static int (__cdecl *p__ismbblead_l)(unsigned int,_locale_t); +static int (__cdecl *p__getmbcp)(void); +static int (__cdecl *p__setmbcp)(int);
/* make sure we use the correct errno */ #undef errno @@ -83,6 +128,8 @@ static BOOL init(void) return FALSE; }
+ SET(p__set_invalid_parameter_handler, "_set_invalid_parameter_handler"); + SET(__pioinfo, "__pioinfo"); SET(p__open,"_open"); SET(p__close,"_close"); @@ -93,7 +140,10 @@ static BOOL init(void) SET(p_dupenv_s, "_dupenv_s"); SET(p_wdupenv_s, "_wdupenv_s"); SET(p_errno, "_errno"); - + SET(p__mbsncpy_s, "_mbsncpy_s"); + SET(p__ismbblead_l, "_ismbblead_l"); + SET(p__getmbcp, "_getmbcp"); + SET(p__setmbcp, "_setmbcp"); return TRUE; }
@@ -222,13 +272,208 @@ static void test_wdupenv_s(void) ok( !tmp, "_wdupenv_s returned pointer is %p\n", tmp ); }
+static char *buf_to_string(const unsigned char *bin, int len, int nr) +{ + static char buf[2][1024]; + char *w = buf[nr]; + int i; + + for (i = 0; i < len; i++) + { + sprintf(w, "%02x ", (unsigned char)bin[i]); + w += strlen(w); + } + return buf[nr]; +} + +#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } +#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); } + +static void test__mbsncpy_s(void) +{ + unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0"; + unsigned char *mbstring2 = (unsigned char *)"\xb0\x0"; + unsigned char buf[16]; + errno_t err; + int oldcp; + + oldcp = p__getmbcp(); + if (p__setmbcp(936)) + { + skip("Code page 936 is not available, skipping test.\n"); + return; + } + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(NULL, 0, mbstring, 0); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 6, mbstring, 1); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\0\xcc", 4); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 6, mbstring, 2); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3\0\xcc", 6); + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = p__mbsncpy_s(buf, 2, mbstring, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\x00\xb1\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 2, mbstring, 1); + ok(errno == err, "got %d.\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 2, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 1, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 0, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 0, mbstring, 0); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, -1, mbstring, 0); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, -1, mbstring, 256); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3Q\xb4\xb5\x0\xcc", 9); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 1, mbstring2, 4); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 2, mbstring2, 4); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 1, mbstring2, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 2, mbstring2, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\x0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 1, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 2, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 3, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = p__mbsncpy_s(buf, 3, mbstring2, 2); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + p__setmbcp(oldcp); +} + START_TEST(msvcr80) { if(!init()) return;
+ ok(p__set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL, + "Invalid parameter handler was already set\n"); + test_ioinfo_flags(); test_strcmp(); test_dupenv_s(); test_wdupenv_s(); + + winetest_push_context("_MB_CP_SBCS"); + p__setmbcp(_MB_CP_SBCS); + test__mbsncpy_s(); + winetest_pop_context(); + if (!p__setmbcp(936)) + { + winetest_push_context("CP 936"); + test__mbsncpy_s(); + winetest_pop_context(); + } + else + { + skip("Code page 936 is not available.\n"); + } } diff --git a/dlls/msvcr90/msvcr90.spec b/dlls/msvcr90/msvcr90.spec index ca22d7460be..c19f7298235 100644 --- a/dlls/msvcr90/msvcr90.spec +++ b/dlls/msvcr90/msvcr90.spec @@ -807,8 +807,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) diff --git a/dlls/msvcrt/mbcs.c b/dlls/msvcrt/mbcs.c index ce8a3115eeb..0bac4c9f8d9 100644 --- a/dlls/msvcrt/mbcs.c +++ b/dlls/msvcrt/mbcs.c @@ -900,6 +900,83 @@ unsigned char* CDECL _mbsncpy_l(unsigned char* dst, const unsigned char* src, si return ret; }
+#if _MSVCR_VER>=80 +errno_t CDECL _mbsncpy_s_l(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n, _locale_t locale) +{ + BOOL truncate = (n == _TRUNCATE); + unsigned char *start = dst, *last; + pthreadmbcinfo mbcinfo; + unsigned int curlen; + + if (!dst && !maxsize && !n) + return 0; + + if (!MSVCRT_CHECK_PMT(dst != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(maxsize != 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) + { + *start = 0; + return EINVAL; + } + + if (!n) + { + *start = 0; + return 0; + } + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + curlen = 0; + last = dst; + while (*src && n && maxsize) + { + if (curlen) + { + --maxsize; + *dst++ = *src++; + if (!--curlen) --n; + continue; + } + last = dst; + if (!(mbcinfo->ismbcodepage && _ismbblead_l(*src, locale))) + { + curlen = 1; + continue; + } + curlen = 2; + if (!truncate && maxsize <= curlen) maxsize = 0; + } + + if (!maxsize && truncate) + { + *last = 0; + return STRUNCATE; + } + if (!truncate && curlen && !src[curlen - 1]) + { + *_errno() = EILSEQ; + *start = 0; + return EILSEQ; + } + if (!maxsize) + { + *start = 0; + if (!MSVCRT_CHECK_PMT_ERR(FALSE, ERANGE)) return ERANGE; + } + *dst = 0; + return 0; +} + +errno_t CDECL _mbsncpy_s(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n) +{ + return _mbsncpy_s_l(dst, maxsize, src, n, NULL); +} +#endif + /********************************************************************* * _mbsncpy(MSVCRT.@) * REMARKS diff --git a/dlls/ucrtbase/tests/string.c b/dlls/ucrtbase/tests/string.c index 6dcd15fb5b9..2c77a3356e1 100644 --- a/dlls/ucrtbase/tests/string.c +++ b/dlls/ucrtbase/tests/string.c @@ -651,6 +651,183 @@ static void test_strcmp(void) ok( ret == 0, "wrong ret %d\n", ret ); }
+static char *buf_to_string(const unsigned char *bin, int len, int nr) +{ + static char buf[2][1024]; + char *w = buf[nr]; + int i; + + for (i = 0; i < len; i++) + { + sprintf(w, "%02x ", (unsigned char)bin[i]); + w += strlen(w); + } + return buf[nr]; +} + +#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } +#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); } + +static void test__mbsncpy_s(void) +{ + unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0"; + unsigned char *mbstring2 = (unsigned char *)"\xb0\x0"; + unsigned char buf[16]; + errno_t err; + int oldcp; + + oldcp = _getmbcp(); + if (_setmbcp(936)) + { + skip("Code page 936 is not available, skipping test.\n"); + return; + } + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(NULL, 0, mbstring, 0); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 6, mbstring, 1); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\0\xcc", 4); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 6, mbstring, 2); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3\0\xcc", 6); + + errno = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + err = _mbsncpy_s(buf, 2, mbstring, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\x00\xb1\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 2, mbstring, 1); + ok(errno == err, "got %d.\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 2, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 1, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == ERANGE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 0, mbstring, 3); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + SET_EXPECT(invalid_parameter_handler); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 0, mbstring, 0); + ok(errno == err, "got %d\n", errno); + CHECK_CALLED(invalid_parameter_handler); + ok(err == EINVAL, "got %d.\n", err); + expect_bin(buf, "\xcc", 1); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, -1, mbstring, 0); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, -1, mbstring, 256); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\xb1\xb2\xb3Q\xb4\xb5\x0\xcc", 9); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 1, mbstring2, 4); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 2, mbstring2, 4); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 1, mbstring2, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(err == STRUNCATE, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 2, mbstring2, _TRUNCATE); + ok(errno == 0xdeadbeef, "got %d\n", errno); + ok(!err, "got %d.\n", err); + expect_bin(buf, "\xb0\x0\xcc", 3); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 1, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 2, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 3, mbstring2, 1); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + memset(buf, 0xcc, sizeof(buf)); + errno = 0xdeadbeef; + err = _mbsncpy_s(buf, 3, mbstring2, 2); + ok(errno == err, "got %d\n", errno); + ok(err == EILSEQ, "got %d.\n", err); + expect_bin(buf, "\x0\xcc", 2); + + _setmbcp(oldcp); +} + START_TEST(string) { ok(_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL, @@ -669,4 +846,5 @@ START_TEST(string) test_SpecialCasing(); test__mbbtype_l(); test_strcmp(); + test__mbsncpy_s(); } diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec index 449b734ad34..054fc4055f4 100644 --- a/dlls/ucrtbase/ucrtbase.spec +++ b/dlls/ucrtbase/ucrtbase.spec @@ -673,8 +673,8 @@ @ stub _mbsncoll_l @ cdecl _mbsncpy(ptr str long) @ cdecl _mbsncpy_l(ptr str long ptr) -@ stub _mbsncpy_s -@ stub _mbsncpy_s_l +@ cdecl _mbsncpy_s(ptr long str long) +@ cdecl _mbsncpy_s_l(ptr long str long ptr) @ cdecl _mbsnextc(str) @ cdecl _mbsnextc_l(str ptr) @ cdecl _mbsnicmp(str str long) @@ -1242,8 +1242,8 @@ @ stub _o__mbsncoll_l @ cdecl _o__mbsncpy(ptr str long) _mbsncpy @ cdecl _o__mbsncpy_l(ptr str long ptr) _mbsncpy_l -@ stub _o__mbsncpy_s -@ stub _o__mbsncpy_s_l +@ cdecl _o__mbsncpy_s(ptr long str long) _mbsncpy_s +@ cdecl _o__mbsncpy_s_l(ptr long str long ptr) _mbsncpy_s_l @ cdecl _o__mbsnextc(str) _mbsnextc @ cdecl _o__mbsnextc_l(str ptr) _mbsnextc_l @ cdecl _o__mbsnicmp(str str long) _mbsnicmp diff --git a/include/msvcrt/mbstring.h b/include/msvcrt/mbstring.h index 28a0e41f10d..f51d8ed6bc3 100644 --- a/include/msvcrt/mbstring.h +++ b/include/msvcrt/mbstring.h @@ -93,6 +93,8 @@ _ACRTIMP size_t __cdecl _mbsnccnt(const unsigned char*,size_t); _ACRTIMP int __cdecl _mbsncmp(const unsigned char*,const unsigned char*,size_t); _ACRTIMP int __cdecl _mbsncoll(const unsigned char*,const unsigned char*,size_t); _ACRTIMP unsigned char* __cdecl _mbsncpy(unsigned char*,const unsigned char*,size_t); +_ACRTIMP errno_t __cdecl _mbsncpy_s(unsigned char*,size_t,const unsigned char*,size_t); +_ACRTIMP errno_t __cdecl _mbsncpy_s_l(unsigned char*,size_t,const unsigned char*,size_t,_locale_t); _ACRTIMP unsigned int __cdecl _mbsnextc(const unsigned char*); _ACRTIMP unsigned int __cdecl _mbsnextc_l(const unsigned char*,_locale_t); _ACRTIMP int __cdecl _mbsnicmp(const unsigned char*,const unsigned char*,size_t);
v3: - add Piotr's patch from https://gitlab.winehq.org/wine/wine/-/merge_requests/5556 without which errno is not correctly tested in msvcr80/tests; - simplify some code bits (removed curlen loop as per Piotr's suggestion, unneeded break in the loop); - test errno thoroughly and fix it in implemetation; - set mv code page in test and test with that code page only; - add test for "all-zero" case and Piotr's diff handling of that.
Piotr Caban (@piotr) commented about dlls/msvcr80/tests/msvcr80.c:
+#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } +#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); }
+static void test__mbsncpy_s(void) +{
- unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0";
- unsigned char *mbstring2 = (unsigned char *)"\xb0\x0";
- unsigned char buf[16];
- errno_t err;
- int oldcp;
- oldcp = p__getmbcp();
- if (p__setmbcp(936))
- {
skip("Code page 936 is not available, skipping test.\n");
```suggestion:-0+0 win_skip("Code page 936 is not available, skipping test.\n"); ```
Piotr Caban (@piotr) commented about dlls/msvcr80/tests/msvcr80.c:
test_wdupenv_s();
- winetest_push_context("_MB_CP_SBCS");
- p__setmbcp(_MB_CP_SBCS);
- test__mbsncpy_s();
- winetest_pop_context();
- if (!p__setmbcp(936))
- {
winetest_push_context("CP 936");
test__mbsncpy_s();
winetest_pop_context();
- }
- else
- {
skip("Code page 936 is not available.\n");
- }
You're setting the codepage in `test__mbsncpy_s`.
Piotr Caban (@piotr) commented about dlls/msvcr80/tests/msvcr80.c:
ok( !tmp, "_wdupenv_s returned pointer is %p\n", tmp );
}
+static char *buf_to_string(const unsigned char *bin, int len, int nr)
Is the output easier to read with `buf_to_string` helper comparing to `debugstr_an`? I'm OK with adding such helper but I'm not sure if it's really useful.
Jacek Caban (@jacek) commented about dlls/msvcr80/tests/msvcr80.c:
static int (__cdecl *p_strncmp)(const char *, const char *, size_t); static int (__cdecl *p_dupenv_s)(char **, size_t *, const char *); static int (__cdecl *p_wdupenv_s)(wchar_t **, size_t *, const wchar_t *); +static int* (__cdecl *p_errno)(void);
+/* make sure we use the correct errno */ +#undef errno +#define errno (*p_errno())
We could also just link tests to msvcr80 instead of msvcrt, something like https://gitlab.winehq.org/jacek/wine/-/commit/49b8bb9503980daff540594ef4d0d1... should do the trick.