From 4b33a339cc5328b1c117f5ed0cbde1a6c4f6b7f5 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Sun, 7 Dec 2014 22:55:31 +0100 Subject: [PATCH] shlwapi: Add implementation for StrCatChainW. Based on a patch by Huw Campbell. --- .../api-ms-win-downlevel-shlwapi-l1-1-0.spec | 2 +- dlls/shlwapi/shlwapi.spec | 1 + dlls/shlwapi/string.c | 41 +++++++ dlls/shlwapi/tests/string.c | 122 +++++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) diff --git a/dlls/api-ms-win-downlevel-shlwapi-l1-1-0/api-ms-win-downlevel-shlwapi-l1-1-0.spec b/dlls/api-ms-win-downlevel-shlwapi-l1-1-0/api-ms-win-downlevel-shlwapi-l1-1-0.spec index 9d266faa586..77366452239 100644 --- a/dlls/api-ms-win-downlevel-shlwapi-l1-1-0/api-ms-win-downlevel-shlwapi-l1-1-0.spec +++ b/dlls/api-ms-win-downlevel-shlwapi-l1-1-0/api-ms-win-downlevel-shlwapi-l1-1-0.spec @@ -86,7 +86,7 @@ @ stdcall SHRegSetUSValueW( wstr wstr long ptr long long) shlwapi.SHRegSetUSValueW @ stdcall StrCatBuffA(str str long) shlwapi.StrCatBuffA @ stdcall StrCatBuffW(wstr wstr long) shlwapi.StrCatBuffW -@ stub StrCatChainW +@ stdcall StrCatChainW(ptr long long wstr) shlwapi.StrCatChainW @ stdcall StrChrA(str long) shlwapi.StrChrA @ stdcall StrChrIA(str long) shlwapi.StrChrIA @ stdcall StrChrIW(wstr long) shlwapi.StrChrIW diff --git a/dlls/shlwapi/shlwapi.spec b/dlls/shlwapi/shlwapi.spec index b7aac08a52f..6b6ead26968 100644 --- a/dlls/shlwapi/shlwapi.spec +++ b/dlls/shlwapi/shlwapi.spec @@ -759,6 +759,7 @@ @ stdcall StrCSpnW (wstr wstr) @ stdcall StrCatBuffA (str str long) @ stdcall StrCatBuffW (wstr wstr long) +@ stdcall StrCatChainW (ptr long long wstr) @ stdcall StrCatW (ptr wstr) @ stdcall StrChrA (str long) @ stdcall StrChrIA (str long) diff --git a/dlls/shlwapi/string.c b/dlls/shlwapi/string.c index 9ba603804a8..ca2f9b9c50c 100644 --- a/dlls/shlwapi/string.c +++ b/dlls/shlwapi/string.c @@ -459,6 +459,47 @@ LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc) } /************************************************************************* + * StrCatChainW [SHLWAPI.@] + * + * Concatenates two unicode strings. + * + * PARAMS + * lpszStr [O] Initial string + * cchMax [I] Length of destination buffer + * ichAt [I] Offset from the destination buffer to begin concatenation + * lpszCat [I] String to concatenate + * + * RETURNS + * The offset from the beginning of pszDst to the terminating NULL. + */ +DWORD WINAPI StrCatChainW(LPWSTR lpszStr, DWORD cchMax, DWORD ichAt, LPCWSTR lpszCat) +{ + TRACE("(%s,%u,%d,%s)\n", debugstr_w(lpszStr), cchMax, ichAt, debugstr_w(lpszCat)); + + if (ichAt == -1) + ichAt = strlenW(lpszStr); + + if (!cchMax) + return ichAt; + + if (ichAt == cchMax) + ichAt--; + + if (lpszCat && ichAt < cchMax) + { + lpszStr += ichAt; + while (ichAt < cchMax - 1 && *lpszCat) + { + *lpszStr++ = *lpszCat++; + ichAt++; + } + *lpszStr = 0; + } + + return ichAt; +} + +/************************************************************************* * StrCpyW [SHLWAPI.@] * * Copy a string to another string. diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index c72b77408fe..ac14db06503 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -48,6 +48,7 @@ static DWORD (WINAPI *pSHAnsiToAnsi)(LPCSTR,LPSTR,int); static DWORD (WINAPI *pSHUnicodeToUnicode)(LPCWSTR,LPWSTR,int); static LPSTR (WINAPI *pStrCatBuffA)(LPSTR,LPCSTR,INT); static LPWSTR (WINAPI *pStrCatBuffW)(LPWSTR,LPCWSTR,INT); +static DWORD (WINAPI *pStrCatChainW)(LPWSTR,DWORD,DWORD,LPCWSTR); static LPSTR (WINAPI *pStrCpyNXA)(LPSTR,LPCSTR,int); static LPWSTR (WINAPI *pStrCpyNXW)(LPWSTR,LPCWSTR,int); static LPSTR (WINAPI *pStrFormatByteSize64A)(LONGLONG,LPSTR,UINT); @@ -1481,6 +1482,125 @@ static void test_StrStrNIW(void) } } +static void test_StrCatChainW(void) +{ + static const WCHAR deadbeefW[] = {'D','e','A','d','B','e','E','f',0}; + static const WCHAR deadW[] = {'D','e','A','d',0}; + static const WCHAR beefW[] = {'B','e','E','f',0}; + + WCHAR buf[32 + 1]; + DWORD ret; + + if (!pStrCatChainW) + { + win_skip("StrCatChainW is not available\n"); + return; + } + + /* Test with NULL buffer */ + ret = pStrCatChainW(NULL, 0, 0, beefW); + ok(ret == 0, "Expected StrCatChainW to return 0, got %u\n", ret); + + /* Test with empty buffer */ + memset(buf, 0x11, sizeof(buf)); + ret = pStrCatChainW(buf, 0, 0, beefW); + ok(ret == 0, "Expected StrCatChainW to return 0, got %u\n", ret); + ok(buf[0] == 0x1111, "Expected buf[0] = 0x1111, got %x\n", buf[0]); + + memcpy(buf, deadbeefW, sizeof(deadbeefW)); + ret = pStrCatChainW(buf, 0, -1, beefW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + + /* Append data to existing string with offset = -1 */ + memset(buf, 0x11, sizeof(buf)); + ret = pStrCatChainW(buf, 32, 0, deadW); + ok(ret == 4, "Expected StrCatChainW to return 4, got %u\n", ret); + ok(!memcmp(buf, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + ret = pStrCatChainW(buf, 32, -1, beefW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + + /* Append data at a fixed offset */ + memset(buf, 0x11, sizeof(buf)); + ret = pStrCatChainW(buf, 32, 0, deadW); + ok(ret == 4, "Expected StrCatChainW to return 4, got %u\n", ret); + ok(!memcmp(buf, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + ret = pStrCatChainW(buf, 32, 4, beefW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + + /* Buffer exactly sufficient for string + terminating null */ + memset(buf, 0x11, sizeof(buf)); + ret = pStrCatChainW(buf, 5, 0, deadW); + ok(ret == 4, "Expected StrCatChainW to return 4, got %u\n", ret); + ok(!memcmp(buf, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + /* Buffer too small, string will be truncated */ + memset(buf, 0x11, sizeof(buf)); + ret = pStrCatChainW(buf, 4, 0, deadW); + if (ret == 4) + { + /* Windows 2000 and XP uses a slightly different implementation + * for StrCatChainW, which doesn't ensure that strings are null- + * terminated. Skip test if we detect such an implementation. */ + win_skip("Windows2000/XP behaviour detected for StrCatChainW, skipping tests\n"); + return; + } + ok(ret == 3, "Expected StrCatChainW to return 3, got %u\n", ret); + ok(!memcmp(buf, deadW, 3 * sizeof(WCHAR)), "Buffer contains wrong data\n"); + ok(!buf[3], "String is not nullterminated\n"); + ok(buf[4] == 0x1111, "Expected buf[4] = 0x1111, got %x\n", buf[4]); + + /* Overwrite part of an existing string */ + ret = pStrCatChainW(buf, 4, 1, beefW); + ok(ret == 3, "Expected StrCatChainW to return 3, got %u\n", ret); + ok(buf[0] == 'D', "Expected buf[0] = 'D', got %x\n", buf[0]); + ok(buf[1] == 'B', "Expected buf[1] = 'B', got %x\n", buf[1]); + ok(buf[2] == 'e', "Expected buf[2] = 'e', got %x\n", buf[2]); + ok(!buf[3], "String is not nullterminated\n"); + ok(buf[4] == 0x1111, "Expected buf[4] = 0x1111, got %x\n", buf[4]); + + /* Test appending to full buffer */ + memset(buf, 0x11, sizeof(buf)); + memcpy(buf, deadbeefW, sizeof(deadbeefW)); + memcpy(buf + 9, deadW, sizeof(deadW)); + ret = pStrCatChainW(buf, 9, 8, beefW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + ok(!memcmp(buf + 9, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + /* Offset points at the end of the buffer */ + ret = pStrCatChainW(buf, 9, 9, beefW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + ok(!memcmp(buf + 9, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + /* Offset points outside of the buffer */ + ret = pStrCatChainW(buf, 9, 10, beefW); + ok(ret == 10, "Expected StrCatChainW to return 10, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + ok(!memcmp(buf + 9, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + + /* The same but without nullterminated string */ + memcpy(buf, deadbeefW, sizeof(deadbeefW)); + ret = pStrCatChainW(buf, 5, -1, deadW); + ok(ret == 8, "Expected StrCatChainW to return 8, got %u\n", ret); + ok(!memcmp(buf, deadbeefW, sizeof(deadbeefW)), "Buffer contains wrong data\n"); + + ret = pStrCatChainW(buf, 5, 5, deadW); + ok(ret == 4, "Expected StrCatChainW to return 4, got %u\n", ret); + ok(!memcmp(buf, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + ok(buf[5] == 'e', "Expected buf[5] = 'e', got %x\n", buf[5]); + + ret = pStrCatChainW(buf, 5, 6, deadW); + ok(ret == 6, "Expected StrCatChainW to return 6, got %u\n", ret); + ok(!memcmp(buf, deadW, sizeof(deadW)), "Buffer contains wrong data\n"); + ok(buf[5] == 'e', "Expected buf[5] = 'e', got %x\n", buf[5]); +} + START_TEST(string) { HMODULE hShlwapi; @@ -1500,6 +1620,7 @@ START_TEST(string) pSHUnicodeToUnicode = (void *)GetProcAddress(hShlwapi, (LPSTR)346); pStrCatBuffA = (void *)GetProcAddress(hShlwapi, "StrCatBuffA"); pStrCatBuffW = (void *)GetProcAddress(hShlwapi, "StrCatBuffW"); + pStrCatChainW = (void *)GetProcAddress(hShlwapi, "StrCatChainW"); pStrCpyNXA = (void *)GetProcAddress(hShlwapi, (LPSTR)399); pStrCpyNXW = (void *)GetProcAddress(hShlwapi, (LPSTR)400); pStrChrNW = (void *)GetProcAddress(hShlwapi, "StrChrNW"); @@ -1565,6 +1686,7 @@ START_TEST(string) test_StrStrIW(); test_StrStrNW(); test_StrStrNIW(); + test_StrCatChainW(); CoUninitialize(); } -- 2.11.4.GIT