diff --git a/dlls/msvcr100/msvcr100.spec b/dlls/msvcr100/msvcr100.spec index 608bfc6d5dc..42d8310899d 100644 --- a/dlls/msvcr100/msvcr100.spec +++ b/dlls/msvcr100/msvcr100.spec @@ -1293,7 +1293,7 @@ @ stub _wcsicoll_l @ cdecl _wcslwr(wstr) msvcrt._wcslwr @ stub _wcslwr_l -@ stub _wcslwr_s +@ cdecl _wcslwr_s(wstr long) msvcrt._wcslwr_s @ stub _wcslwr_s_l @ stub _wcsncoll @ stub _wcsncoll_l diff --git a/dlls/msvcr80/msvcr80.spec b/dlls/msvcr80/msvcr80.spec index 58b855c0594..f9eaaf0e22c 100644 --- a/dlls/msvcr80/msvcr80.spec +++ b/dlls/msvcr80/msvcr80.spec @@ -1146,7 +1146,7 @@ @ stub _wcsicoll_l @ cdecl _wcslwr(wstr) msvcrt._wcslwr @ stub _wcslwr_l -@ stub _wcslwr_s +@ cdecl _wcslwr_s(wstr long) msvcrt._wcslwr_s @ stub _wcslwr_s_l @ stub _wcsncoll @ stub _wcsncoll_l diff --git a/dlls/msvcr90/msvcr90.spec b/dlls/msvcr90/msvcr90.spec index d76acc89a9f..b7288da5ffe 100644 --- a/dlls/msvcr90/msvcr90.spec +++ b/dlls/msvcr90/msvcr90.spec @@ -1133,7 +1133,7 @@ @ stub _wcsicoll_l @ cdecl _wcslwr(wstr) msvcrt._wcslwr @ stub _wcslwr_l -@ stub _wcslwr_s +@ cdecl _wcslwr_s(wstr long) msvcrt._wcslwr_s @ stub _wcslwr_s_l @ stub _wcsncoll @ stub _wcsncoll_l diff --git a/dlls/msvcrt/msvcrt.spec b/dlls/msvcrt/msvcrt.spec index c7f2120b974..f75c0d92490 100644 --- a/dlls/msvcrt/msvcrt.spec +++ b/dlls/msvcrt/msvcrt.spec @@ -1067,7 +1067,7 @@ # stub _wcsicoll_l @ cdecl _wcslwr(wstr) ntdll._wcslwr # stub _wcslwr_l -# stub _wcslwr_s +@ cdecl _wcslwr_s(wstr long) MSVCRT__wcslwr_s # stub _wcslwr_s_l @ stub _wcsncoll #(wstr wstr long) # stub _wcsncoll_l diff --git a/dlls/msvcrt/tests/string.c b/dlls/msvcrt/tests/string.c index c532fce3ecf..dfa38e62e24 100644 --- a/dlls/msvcrt/tests/string.c +++ b/dlls/msvcrt/tests/string.c @@ -75,6 +75,7 @@ static errno_t (__cdecl *p_ultoa_s)(__msvcrt_ulong,char*,size_t,int); static int *p__mb_cur_max; static unsigned char *p_mbctype; static _invalid_parameter_handler (__cdecl *p_set_invalid_parameter_handler)(_invalid_parameter_handler); +static int (__cdecl *p_wcslwr_s)(wchar_t*,size_t); #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) @@ -731,6 +732,93 @@ static void test__wcsupr_s(void) ok(!wcscmp(testBuffer, expectedString), "Expected the string to be fully upper-case\n"); } +static void test__wcslwr_s(void) +{ + static const WCHAR mixedString[] = {'M', 'i', 'X', 'e', 'D', 'l', 'o', 'w', + 'e', 'r', 'U', 'P', 'P', 'E', 'R', 0}; + static const WCHAR expectedString[] = {'m', 'i', 'x', 'e', 'd', 'l', 'o', + 'w', 'e', 'r', 'u', 'p', 'p', 'e', + 'r', 0}; + WCHAR buffer[2*sizeof(mixedString)/sizeof(WCHAR)]; + int ret; + + if (!p_wcslwr_s) + { + win_skip("_wcslwr_s not found\n"); + return; + } + + /* Test NULL input string and invalid size. */ + errno = EBADF; + ret = p_wcslwr_s(NULL, 0); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno); + + /* Test NULL input string and valid size. */ + errno = EBADF; + ret = p_wcslwr_s(NULL, sizeof(buffer)/sizeof(wchar_t)); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno); + + /* Test empty string with zero size. */ + errno = EBADF; + buffer[0] = 'a'; + ret = p_wcslwr_s(buffer, 0); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno); + ok(buffer[0] == 0, "expected empty string\n"); + + /* Test empty string with size of one. */ + buffer[0] = 0; + ret = p_wcslwr_s(buffer, 1); + ok(ret == 0, "got %d\n", ret); + ok(buffer[0] == 0, "expected buffer to be unchanged\n"); + + /* Test one-byte buffer with zero size. */ + errno = EBADF; + buffer[0] = 'x'; + ret = p_wcslwr_s(buffer, 0); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno to be EINVAL, got %d\n", errno); + ok(buffer[0] == '\0', "expected empty string\n"); + + /* Test one-byte buffer with size of one. */ + errno = EBADF; + buffer[0] = 'x'; + ret = p_wcslwr_s(buffer, 1); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno to be EINVAL, got %d\n", errno); + ok(buffer[0] == '\0', "expected empty string\n"); + + /* Test invalid size. */ + wcscpy(buffer, mixedString); + errno = EBADF; + ret = p_wcslwr_s(buffer, 0); + ok(ret == EINVAL, "Expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno to be EINVAL, got %d\n", errno); + ok(buffer[0] == '\0', "expected empty string\n"); + + /* Test normal string uppercasing. */ + wcscpy(buffer, mixedString); + ret = p_wcslwr_s(buffer, sizeof(mixedString)/sizeof(WCHAR)); + ok(ret == 0, "expected 0, got %d\n", ret); + ok(!wcscmp(buffer, expectedString), "expected lowercase\n"); + + /* Test uppercasing with a shorter buffer size count. */ + wcscpy(buffer, mixedString); + errno = EBADF; + ret = p_wcslwr_s(buffer, sizeof(mixedString)/sizeof(WCHAR) - 1); + ok(ret == EINVAL, "expected EINVAL, got %d\n", ret); + ok(errno == EINVAL, "expected errno to be EINVAL, got %d\n", errno); + ok(buffer[0] == '\0', "expected empty string\n"); + + /* Test uppercasing with a longer buffer size count. */ + wcscpy(buffer, mixedString); + ret = p_wcslwr_s(buffer, sizeof(buffer)/sizeof(WCHAR)); + ok(ret == 0, "expected 0, got %d\n", ret); + ok(!wcscmp(buffer, expectedString), "expected lowercase\n"); +} + static void test_mbcjisjms(void) { /* List of value-pairs to test. The test assumes the last pair to be {0, ..} */ @@ -1732,6 +1820,7 @@ START_TEST(string) p_strlwr_s = (void *)GetProcAddress(hMsvcrt, "_strlwr_s"); p_ultoa_s = (void *)GetProcAddress(hMsvcrt, "_ultoa_s"); p_set_invalid_parameter_handler = (void *) GetProcAddress(hMsvcrt, "_set_invalid_parameter_handler"); + p_wcslwr_s = (void*)GetProcAddress(hMsvcrt, "_wcslwr_s"); /* MSVCRT memcpy behaves like memmove for overlapping moves, MFC42 CString::Insert seems to rely on that behaviour */ @@ -1771,4 +1860,5 @@ START_TEST(string) test_wcsncat_s(); test__mbsnbcat_s(); test__ultoa_s(); + test__wcslwr_s(); } diff --git a/dlls/msvcrt/wcs.c b/dlls/msvcrt/wcs.c index b01e60b09fc..d45094a53fc 100644 --- a/dlls/msvcrt/wcs.c +++ b/dlls/msvcrt/wcs.c @@ -131,6 +131,34 @@ INT CDECL MSVCRT__wcsupr_s( MSVCRT_wchar_t* str, MSVCRT_size_t n ) return MSVCRT_EINVAL; } +/****************************************************************** + * _wcslwr_s (MSVCRT.@) + */ +int CDECL MSVCRT__wcslwr_s( MSVCRT_wchar_t* str, MSVCRT_size_t n ) +{ + MSVCRT_wchar_t* ptr = str; + + if (!str || !n) + { + if (str) *str = '\0'; + *MSVCRT__errno() = MSVCRT_EINVAL; + return MSVCRT_EINVAL; + } + + while (n--) + { + if (!*ptr) return 0; + *ptr = tolowerW(*ptr); + ptr++; + } + + /* MSDN claims that the function should return and set errno to + * ERANGE, which doesn't seem to be true based on the tests. */ + *str = '\0'; + *MSVCRT__errno() = MSVCRT_EINVAL; + return MSVCRT_EINVAL; +} + /********************************************************************* * _wcstod_l - not exported in native msvcrt */