msvcrt: Implement _mbsnbcpy_s.
This commit is contained in:
parent
9582f5c719
commit
a529ef4875
dlls/msvcrt
|
@ -27,6 +27,7 @@
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
#include "msvcrt/mbctype.h"
|
#include "msvcrt/mbctype.h"
|
||||||
|
#include "msvcrt/errno.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
|
WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
|
||||||
|
|
||||||
|
@ -442,6 +443,71 @@ unsigned char* CDECL _mbsncpy(unsigned char* dst, const unsigned char* src, MSVC
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* _mbsnbcpy_s(MSVCRT.@)
|
||||||
|
* REMARKS
|
||||||
|
* Unlike _mbsnbcpy this function does not pad the rest of the dest
|
||||||
|
* string with 0
|
||||||
|
*/
|
||||||
|
int CDECL _mbsnbcpy_s(unsigned char* dst, MSVCRT_size_t size, const unsigned char* src, MSVCRT_size_t n)
|
||||||
|
{
|
||||||
|
MSVCRT_size_t pos = 0;
|
||||||
|
|
||||||
|
if(!dst || size == 0)
|
||||||
|
return EINVAL;
|
||||||
|
if(!src)
|
||||||
|
{
|
||||||
|
dst[0] = '\0';
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if(!n)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(g_mbcp_is_multibyte)
|
||||||
|
{
|
||||||
|
int is_lead = 0;
|
||||||
|
while (*src && n)
|
||||||
|
{
|
||||||
|
if(pos == size)
|
||||||
|
{
|
||||||
|
dst[0] = '\0';
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
is_lead = (!is_lead && _ismbblead(*src));
|
||||||
|
n--;
|
||||||
|
dst[pos++] = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_lead) /* if string ends with a lead, remove it */
|
||||||
|
dst[pos - 1] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (n)
|
||||||
|
{
|
||||||
|
n--;
|
||||||
|
if(pos == size)
|
||||||
|
{
|
||||||
|
dst[0] = '\0';
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(*src)) break;
|
||||||
|
dst[pos++] = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pos < size)
|
||||||
|
dst[pos] = '\0';
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dst[0] = '\0';
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* _mbsnbcpy(MSVCRT.@)
|
* _mbsnbcpy(MSVCRT.@)
|
||||||
* REMARKS
|
* REMARKS
|
||||||
|
|
|
@ -375,6 +375,7 @@
|
||||||
@ cdecl _mbsnbcnt(ptr long)
|
@ cdecl _mbsnbcnt(ptr long)
|
||||||
@ stub _mbsnbcoll #(str str long)
|
@ stub _mbsnbcoll #(str str long)
|
||||||
@ cdecl _mbsnbcpy(ptr str long)
|
@ cdecl _mbsnbcpy(ptr str long)
|
||||||
|
@ cdecl _mbsnbcpy_s(ptr long str long)
|
||||||
@ cdecl _mbsnbicmp(str str long)
|
@ cdecl _mbsnbicmp(str str long)
|
||||||
@ stub _mbsnbicoll #(str str long)
|
@ stub _mbsnbicoll #(str str long)
|
||||||
@ cdecl _mbsnbset(str long long)
|
@ cdecl _mbsnbset(str long long)
|
||||||
|
|
|
@ -49,6 +49,7 @@ static void* (*pmemcpy)(void *, const void *, size_t n);
|
||||||
static int* (*pmemcmp)(void *, const void *, size_t n);
|
static int* (*pmemcmp)(void *, const void *, size_t n);
|
||||||
static int (*pstrcpy_s)(char *dst, size_t len, const char *src);
|
static int (*pstrcpy_s)(char *dst, size_t len, const char *src);
|
||||||
static int (*pstrcat_s)(char *dst, size_t len, const char *src);
|
static int (*pstrcat_s)(char *dst, size_t len, const char *src);
|
||||||
|
static int (*p_mbsnbcpy_s)(unsigned char * dst, size_t size, const unsigned char * src, size_t count);
|
||||||
|
|
||||||
#define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
|
#define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
|
||||||
#define SET(x,y) SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y)
|
#define SET(x,y) SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y)
|
||||||
|
@ -514,6 +515,53 @@ static void test_strcat_s(void)
|
||||||
ok(ret == EINVAL, "strcat_s: Writing to a NULL string returned %d, expected EINVAL\n", ret);
|
ok(ret == EINVAL, "strcat_s: Writing to a NULL string returned %d, expected EINVAL\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test__mbsnbcpy_s(void)
|
||||||
|
{
|
||||||
|
unsigned char dest[8];
|
||||||
|
const unsigned char big[] = "atoolongstringforthislittledestination";
|
||||||
|
const unsigned char small[] = "small";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if(!p_mbsnbcpy_s)
|
||||||
|
{
|
||||||
|
skip("_mbsnbcpy_s not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(dest, 'X', sizeof(dest));
|
||||||
|
ret = p_mbsnbcpy_s(dest, sizeof(dest), small, sizeof(small));
|
||||||
|
ok(ret == 0, "_mbsnbcpy_s: Copying a string into a big enough destination returned %d, expected 0\n", ret);
|
||||||
|
ok(dest[0] == 's' && dest[1] == 'm' && dest[2] == 'a' && dest[3] == 'l' &&
|
||||||
|
dest[4] == 'l' && dest[5] == '\0'&& dest[6] == 'X' && dest[7] == 'X',
|
||||||
|
"Unexpected return data from _mbsnbcpy_s: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||||
|
dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
|
||||||
|
|
||||||
|
/* WTF? */
|
||||||
|
memset(dest, 'X', sizeof(dest));
|
||||||
|
ret = p_mbsnbcpy_s(dest, sizeof(dest) - 2, big, sizeof(small));
|
||||||
|
ok(ret == ERANGE, "_mbsnbcpy_s: Copying a too long string returned %d, expected ERANGE\n", ret);
|
||||||
|
ok(dest[0] == '\0'&& dest[1] == 't' && dest[2] == 'o' && dest[3] == 'o' &&
|
||||||
|
dest[4] == 'l' && dest[5] == 'o' && dest[6] == 'X' && dest[7] == 'X',
|
||||||
|
"Unexpected return data from _mbsnbcpy_s: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||||
|
dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
|
||||||
|
|
||||||
|
memset(dest, 'X', sizeof(dest));
|
||||||
|
ret = p_mbsnbcpy_s(dest, sizeof(dest) - 2, big, 4);
|
||||||
|
ok(ret == 0, "_mbsnbcpy_s: Copying a too long string with a count cap returned %d, expected 0\n", ret);
|
||||||
|
ok(dest[0] == 'a' && dest[1] == 't' && dest[2] == 'o' && dest[3] == 'o' &&
|
||||||
|
dest[4] == '\0'&& dest[5] == 'X' && dest[6] == 'X' && dest[7] == 'X',
|
||||||
|
"Unexpected return data from _mbsnbcpy_s: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||||
|
dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
|
||||||
|
|
||||||
|
memset(dest, 'X', sizeof(dest));
|
||||||
|
ret = p_mbsnbcpy_s(dest, sizeof(dest) - 2, small, sizeof(small) + 10);
|
||||||
|
ok(ret == 0, "_mbsnbcpy_s: Copying more data than the source string len returned %d, expected 0\n", ret);
|
||||||
|
ok(dest[0] == 's' && dest[1] == 'm' && dest[2] == 'a' && dest[3] == 'l' &&
|
||||||
|
dest[4] == 'l' && dest[5] == '\0'&& dest[6] == 'X' && dest[7] == 'X',
|
||||||
|
"Unexpected return data from _mbsnbcpy_s: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||||
|
dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(string)
|
START_TEST(string)
|
||||||
{
|
{
|
||||||
char mem[100];
|
char mem[100];
|
||||||
|
@ -528,6 +576,7 @@ START_TEST(string)
|
||||||
SET(pmemcmp,"memcmp");
|
SET(pmemcmp,"memcmp");
|
||||||
SET(pstrcpy_s,"strcpy_s");
|
SET(pstrcpy_s,"strcpy_s");
|
||||||
SET(pstrcat_s,"strcat_s");
|
SET(pstrcat_s,"strcat_s");
|
||||||
|
SET(p_mbsnbcpy_s,"_mbsnbcpy_s");
|
||||||
|
|
||||||
/* MSVCRT memcpy behaves like memmove for overlapping moves,
|
/* MSVCRT memcpy behaves like memmove for overlapping moves,
|
||||||
MFC42 CString::Insert seems to rely on that behaviour */
|
MFC42 CString::Insert seems to rely on that behaviour */
|
||||||
|
@ -549,4 +598,5 @@ START_TEST(string)
|
||||||
test_strdup();
|
test_strdup();
|
||||||
test_strcpy_s();
|
test_strcpy_s();
|
||||||
test_strcat_s();
|
test_strcat_s();
|
||||||
|
test__mbsnbcpy_s();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue