Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 52 +++++++++ dlls/kernelbase/tests/path.c | 102 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 157 insertions(+), 2 deletions(-)
diff --git a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec index 7b812eb49d..fed23cbac1 100644 --- a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec +++ b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec @@ -2,7 +2,7 @@ @ stub PathAllocCombine @ stdcall PathCchAddBackslash(wstr long) kernelbase.PathCchAddBackslash @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) kernelbase.PathCchAddBackslashEx -@ stub PathCchAddExtension +@ stdcall PathCchAddExtension(wstr long wstr) kernelbase.PathCchAddExtension @ stub PathCchAppend @ stub PathCchAppendEx @ stub PathCchCanonicalize diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 2372db821e..27d10ce731 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1031,7 +1031,7 @@ @ stdcall PathCanonicalizeW(ptr wstr) shlwapi.PathCanonicalizeW @ stdcall PathCchAddBackslash(wstr long) @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) -# @ stub PathCchAddExtension +@ stdcall PathCchAddExtension(wstr long wstr) # @ stub PathCchAppend # @ stub PathCchAppendEx # @ stub PathCchCanonicalize diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index b051986031..2ddc708f07 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -29,6 +29,15 @@
WINE_DEFAULT_DEBUG_CHANNEL(path);
+static SIZE_T wcsnlen(const WCHAR *string, SIZE_T maxlen) +{ + SIZE_T i; + + for (i = 0; i < maxlen; i++) + if (!string[i]) break; + return i; +} + HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) { return PathCchAddBackslashEx(path, size, NULL, NULL); @@ -67,6 +76,49 @@ HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, S return S_OK; }
+HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) +{ + const WCHAR *existing_extension, *next; + SIZE_T path_length, extension_length, dot_length; + BOOL has_dot; + HRESULT hr; + + TRACE("%s %lu %s\n", wine_dbgstr_w(path), size, wine_dbgstr_w(extension)); + + if (!path || !size || size > PATHCCH_MAX_CCH || !extension) return E_INVALIDARG; + + next = extension; + while (*next) + { + if ((*next == '.' && next > extension) || *next == ' ' || *next == '\') return E_INVALIDARG; + next++; + } + + has_dot = extension[0] == '.' ? TRUE : FALSE; + + hr = PathCchFindExtension(path, size, &existing_extension); + if (FAILED(hr)) return hr; + if (*existing_extension) return S_FALSE; + + path_length = wcsnlen(path, size); + dot_length = has_dot ? 0 : 1; + extension_length = strlenW(extension); + + if (path_length + dot_length + extension_length + 1 > size) return STRSAFE_E_INSUFFICIENT_BUFFER; + + /* If extension is empty or only dot, return S_OK with path unchanged */ + if (!extension[0] || (extension[0] == '.' && !extension[1])) return S_OK; + + if (!has_dot) + { + path[path_length] = '.'; + path_length++; + } + + strcpyW(path + path_length, extension); + return S_OK; +} + HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension) { const WCHAR *lastpoint = NULL; diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 80c65a95d1..e6ead730d5 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -32,6 +32,7 @@
HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size); HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining); +HRESULT (WINAPI *pPathCchAddExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension); HRESULT (WINAPI *pPathCchCombineEx)(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags); HRESULT (WINAPI *pPathCchFindExtension)(const WCHAR *path, SIZE_T size, const WCHAR **extension);
@@ -235,6 +236,105 @@ static void test_PathCchAddBackslashEx(void) } }
+struct addextension_test +{ + const CHAR *path; + const CHAR *extension; + const CHAR *expected; + HRESULT hr; +}; + +static const struct addextension_test addextension_tests[] = +{ + /* Normal */ + {"", ".exe", ".exe", S_OK}, + {"C:\", "", "C:\", S_OK}, + {"C:", ".exe", "C:.exe", S_OK}, + {"C:\", ".exe", "C:\.exe", S_OK}, + {"\", ".exe", "\.exe", S_OK}, + {"\\", ".exe", "\\.exe", S_OK}, + {"\\?\C:", ".exe", "\\?\C:.exe", S_OK}, + {"\\?\C:\", ".exe", "\\?\C:\.exe", S_OK}, + {"\\?\UNC\", ".exe", "\\?\UNC\.exe", S_OK}, + {"\\?\UNC\192.168.1.1\", ".exe", "\\?\UNC\192.168.1.1\.exe", S_OK}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", ".exe", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\.exe", S_OK}, + {"C:\", "exe", "C:\.exe", S_OK}, + {"C:\", ".", "C:\", S_OK}, + {"C:\1.exe", ".txt", "C:\1.exe", S_FALSE}, + + /* Extension contains invalid characters but valid for PathCchAddExtension */ + {"C:\", "./", "C:\./", S_OK}, + {"C:\", ".?", "C:\.?", S_OK}, + {"C:\", ".%", "C:\.%", S_OK}, + {"C:\", ".*", "C:\.*", S_OK}, + {"C:\", ".:", "C:\.:", S_OK}, + {"C:\", ".|", "C:\.|", S_OK}, + {"C:\", "."", "C:\."", S_OK}, + {"C:\", ".<", "C:\.<", S_OK}, + {"C:\", ".>", "C:\.>", S_OK}, + + /* Invalid argument for extension */ + {"C:\", " exe", NULL, E_INVALIDARG}, + {"C:\", ". exe", NULL, E_INVALIDARG}, + {"C:\", " ", NULL, E_INVALIDARG}, + {"C:\", "\", NULL, E_INVALIDARG}, + {"C:\", "..", NULL, E_INVALIDARG}, + {"C:\", ". ", NULL, E_INVALIDARG}, + {"C:\", ".\", NULL, E_INVALIDARG}, + {"C:\", ".a.", NULL, E_INVALIDARG}, + {"C:\", ".a ", NULL, E_INVALIDARG}, + {"C:\", ".a\", NULL, E_INVALIDARG}, + {"C:\1.exe", " ", NULL, E_INVALIDARG} +}; + +static void test_PathCchAddExtension(void) +{ + WCHAR pathW[PATHCCH_MAX_CCH + 1]; + CHAR pathA[PATHCCH_MAX_CCH + 1]; + WCHAR extensionW[MAX_PATH]; + HRESULT hr; + INT i; + + /* Arguments check */ + MultiByteToWideChar(CP_ACP, 0, "C:\", -1, pathW, ARRAY_SIZE(pathW)); + MultiByteToWideChar(CP_ACP, 0, ".exe", -1, extensionW, ARRAY_SIZE(extensionW)); + + hr = pPathCchAddExtension(NULL, PATHCCH_MAX_CCH, extensionW); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchAddExtension(pathW, 0, extensionW); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchAddExtension(pathW, PATHCCH_MAX_CCH, NULL); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + /* Path length check */ + hr = pPathCchAddExtension(pathW, ARRAY_SIZE("C:\.exe") - 1, extensionW); + ok(hr == STRSAFE_E_INSUFFICIENT_BUFFER, "expect result %#x, got %#x\n", STRSAFE_E_INSUFFICIENT_BUFFER, hr); + + hr = pPathCchAddExtension(pathW, PATHCCH_MAX_CCH + 1, extensionW); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchAddExtension(pathW, PATHCCH_MAX_CCH, extensionW); + ok(hr == S_OK, "expect result %#x, got %#x\n", S_OK, hr); + + for (i = 0; i < ARRAY_SIZE(addextension_tests); i++) + { + const struct addextension_test *t = addextension_tests + i; + MultiByteToWideChar(CP_ACP, 0, t->path, -1, pathW, ARRAY_SIZE(pathW)); + MultiByteToWideChar(CP_ACP, 0, t->extension, -1, extensionW, ARRAY_SIZE(extensionW)); + hr = pPathCchAddExtension(pathW, PATHCCH_MAX_CCH, extensionW); + ok(hr == t->hr, "path %s extension %s expect result %#x, got %#x\n", t->path, t->extension, t->hr, hr); + if (SUCCEEDED(hr)) + { + WideCharToMultiByte(CP_ACP, 0, pathW, -1, pathA, ARRAY_SIZE(pathA), NULL, NULL); + ok(!lstrcmpA(pathA, t->expected), "path %s extension %s expect output path %s, got %s\n", t->path, + t->extension, t->expected, pathA); + } + } +} + struct findextension_test { const CHAR *path; @@ -359,10 +459,12 @@ START_TEST(path) pPathCchCombineEx = (void *)GetProcAddress(hmod, "PathCchCombineEx"); pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash"); pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx"); + pPathCchAddExtension = (void *)GetProcAddress(hmod, "PathCchAddExtension"); pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension");
test_PathCchCombineEx(); test_PathCchAddBackslash(); test_PathCchAddBackslashEx(); + test_PathCchAddExtension(); test_PathCchFindExtension(); } diff --git a/include/pathcch.h b/include/pathcch.h index 6bb760ccdb..5a189bc0e7 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -27,5 +27,6 @@
HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining); +HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension); HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags); HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension);
Hi,
While running your changed tests on Windows, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=44591
Your paranoid android.
=== w7u (32 bit report) ===
kernelbase: 0130:path: unhandled exception c0000005 at 00000000
=== w7pro64 (32 bit report) ===
kernelbase: 0ab0:path: unhandled exception c0000005 at 00000000
=== w7pro64 (64 bit report) ===
kernelbase: 0a88:path: unhandled exception c0000005 at 0000000000000000