Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com> --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 39 ++++++ dlls/kernelbase/tests/path.c | 116 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 158 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 61eb22031e..3caaf80acb 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 @@ -12,7 +12,7 @@ @ stdcall PathCchFindExtension(wstr long ptr) kernelbase.PathCchFindExtension @ stdcall PathCchIsRoot(wstr) kernelbase.PathCchIsRoot @ stub PathCchRemoveBackslash -@ stub PathCchRemoveBackslashEx +@ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) kernelbase.PathCchRemoveBackslashEx @ stdcall PathCchRemoveExtension(wstr long) kernelbase.PathCchRemoveExtension @ stub PathCchRemoveFileSpec @ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index ffb5a95721..a08fd2c046 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1041,7 +1041,7 @@ @ stdcall PathCchFindExtension(wstr long ptr) @ stdcall PathCchIsRoot(wstr) # @ stub PathCchRemoveBackslash -# @ stub PathCchRemoveBackslashEx +@ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) @ stdcall PathCchRemoveExtension(wstr long) # @ stub PathCchRemoveFileSpec @ stdcall PathCchRenameExtension(wstr long wstr) diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 75faf0b79c..05fcc2b265 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -278,6 +278,45 @@ BOOL WINAPI PathCchIsRoot(const WCHAR *path) return FALSE; } +HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T path_size, WCHAR **path_end, SIZE_T *free_size) +{ + const WCHAR *root_end; + SIZE_T path_length; + + TRACE("%s %lu %p %p\n", debugstr_w(path), path_size, path_end, free_size); + + if (!path_size || !path_end || !free_size) + { + if (path_end) *path_end = NULL; + if (free_size) *free_size = 0; + return E_INVALIDARG; + } + + path_length = strnlenW(path, path_size); + if (path_length == path_size && !path[path_length]) return E_INVALIDARG; + + root_end = get_root_end(path); + if (path_length > 0 && path[path_length - 1] == '\\') + { + *path_end = path + path_length - 1; + *free_size = path_size - path_length + 1; + /* If the last character is beyond end of root */ + if (!root_end || path + path_length - 1 > root_end) + { + path[path_length - 1] = 0; + return S_OK; + } + else + return S_FALSE; + } + else + { + *path_end = path + path_length; + *free_size = path_size - path_length; + return S_FALSE; + } +} + HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size) { const WCHAR *extension; diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 2edd49b0af..0050e1a643 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -36,6 +36,7 @@ HRESULT (WINAPI *pPathCchAddExtension)(WCHAR *path, SIZE_T size, const WCHAR *ex 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); BOOL (WINAPI *pPathCchIsRoot)(const WCHAR *path); +HRESULT (WINAPI *pPathCchRemoveBackslashEx)(WCHAR *path, SIZE_T path_size, WCHAR **path_end, SIZE_T *free_size); HRESULT (WINAPI *pPathCchRemoveExtension)(WCHAR *path, SIZE_T size); HRESULT (WINAPI *pPathCchRenameExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension); HRESULT (WINAPI *pPathCchSkipRoot)(const WCHAR *path, const WCHAR **root_end); @@ -544,6 +545,119 @@ static void test_PathCchIsRoot(void) } } +struct removebackslashex_test +{ + const CHAR *path_in; + const CHAR *path_out; + int end_offset; + SIZE_T free_size; + HRESULT hr; +}; + +static const struct removebackslashex_test removebackslashex_tests [] = +{ + {"", "", 0, 1, S_FALSE}, + {"C", "C", 1, 1, S_FALSE}, + {"C\\", "C", 1, 2, S_OK}, + {"C:", "C:", 2, 1, S_FALSE}, + {"C:\\", "C:\\", 2, 2, S_FALSE}, + {"C:\\\\", "C:\\", 3, 2, S_OK}, + {"C:\\a\\", "C:\\a", 4, 2, S_OK}, + {"C:\\a\\\\", "C:\\a\\", 5, 2, S_OK}, + {"\\", "\\", 0, 2, S_FALSE}, + {"\\\\", "\\\\", 1, 2, S_FALSE}, + {"\\?\\", "\\?", 2, 2, S_OK}, + {"\\?\\\\", "\\?\\", 3, 2, S_OK}, + {"\\a\\", "\\a", 2, 2, S_OK}, + {"\\a\\\\", "\\a\\", 3, 2, S_OK}, + {"\\\\a\\", "\\\\a", 3, 2, S_OK}, + {"\\\\a\\b\\", "\\\\a\\b", 5, 2, S_OK}, + {"\\\\a\\\\", "\\\\a\\", 4, 2, S_OK}, + {"\\\\?\\", "\\\\?", 3, 2, S_OK}, + {"\\\\?\\\\", "\\\\?\\", 4, 2, S_OK}, + {"\\\\?\\C:", "\\\\?\\C:", 6, 1, S_FALSE}, + {"\\\\?\\C:\\", "\\\\?\\C:\\", 6, 2, S_FALSE}, + {"\\?\\UNC\\", "\\?\\UNC", 6, 2, S_OK}, + {"\\\\?\\UNC", "\\\\?\\UNC", 7, 1, S_FALSE}, + {"\\\\?\\UNC\\", "\\\\?\\UNC\\", 7, 2, S_FALSE}, + {"\\\\?\\UNC\\a", "\\\\?\\UNC\\a", 9, 1, S_FALSE}, + {"\\\\?\\UNC\\a\\", "\\\\?\\UNC\\a", 9, 2, S_OK}, + {"\\\\?\\UNC\\a\\b\\", "\\\\?\\UNC\\a\\b", 11, 2, S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", 48, 1, S_FALSE}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 48, 2, S_FALSE}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", 50, 1, S_FALSE}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a\\", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", 50, 2, S_OK} +}; + +static void test_PathCchRemoveBackslashEx(void) +{ + WCHAR pathW[PATHCCH_MAX_CCH]; + CHAR pathA[PATHCCH_MAX_CCH]; + WCHAR *path_end; + SIZE_T path_size, free_size; + HRESULT hr; + INT i; + + if (!pPathCchRemoveBackslashEx) + { + win_skip("PathCchRemoveBackslashEx() is not available.\n"); + return; + } + + /* No NULL check for path on Windows */ + if (0) + { + hr = pPathCchRemoveBackslashEx(NULL, 0, &path_end, &path_size); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + } + + path_size = MultiByteToWideChar(CP_ACP, 0, "C:\\a\\", -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchRemoveBackslashEx(pathW, 0, &path_end, &path_size); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + + free_size = 0xdeadbeef; + hr = pPathCchRemoveBackslashEx(pathW, path_size, NULL, &free_size); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + ok(free_size == 0, "expect %d, got %lu\n", 0, free_size); + + path_end = (WCHAR *)0xdeadbeef; + hr = pPathCchRemoveBackslashEx(pathW, path_size, &path_end, NULL); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + ok(path_end == NULL, "expect null, got %p\n", path_end); + + hr = pPathCchRemoveBackslashEx(pathW, PATHCCH_MAX_CCH + 1, &path_end, &free_size); + ok(hr == S_OK, "expect hr %#x, got %#x\n", S_OK, hr); + + hr = pPathCchRemoveBackslashEx(pathW, PATHCCH_MAX_CCH, &path_end, &free_size); + ok(hr == S_FALSE, "expect hr %#x, got %#x\n", S_FALSE, hr); + + /* Size < original path length + 1, don't read beyond size */ + MultiByteToWideChar(CP_ACP, 0, "C:\\a", -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchRemoveBackslashEx(pathW, ARRAY_SIZE("C:\\a") - 1, &path_end, &free_size); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + for (i = 0; i < ARRAY_SIZE(removebackslashex_tests); i++) + { + const struct removebackslashex_test *t = removebackslashex_tests + i; + path_size = MultiByteToWideChar(CP_ACP, 0, t->path_in, -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchRemoveBackslashEx(pathW, path_size, &path_end, &free_size); + ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path_in, t->hr, hr); + if (SUCCEEDED(hr)) + { + ok(path_end - pathW == t->end_offset, "path %s expect end offset %d, got %d\n", t->path_in, t->end_offset, + path_end - pathW); + ok(free_size == t->free_size, "path %s expect free size %lu, got %lu\n", t->path_in, t->free_size, free_size); + WideCharToMultiByte(CP_ACP, 0, pathW, -1, pathA, ARRAY_SIZE(pathA), NULL, NULL); + ok(!lstrcmpA(pathA, t->path_out), "path %s expect output path %s, got %s\n", t->path_in, t->path_out, + pathA); + } + } +} + struct removeextension_test { const CHAR *path; @@ -1107,6 +1221,7 @@ START_TEST(path) pPathCchAddExtension = (void *)GetProcAddress(hmod, "PathCchAddExtension"); pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension"); pPathCchIsRoot = (void *)GetProcAddress(hmod, "PathCchIsRoot"); + pPathCchRemoveBackslashEx = (void *)GetProcAddress(hmod, "PathCchRemoveBackslashEx"); pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension"); pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension"); pPathCchSkipRoot = (void *)GetProcAddress(hmod, "PathCchSkipRoot"); @@ -1120,6 +1235,7 @@ START_TEST(path) test_PathCchAddExtension(); test_PathCchFindExtension(); test_PathCchIsRoot(); + test_PathCchRemoveBackslashEx(); test_PathCchRemoveExtension(); test_PathCchRenameExtension(); test_PathCchSkipRoot(); diff --git a/include/pathcch.h b/include/pathcch.h index 443ccd3a4f..f7de815d16 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -31,6 +31,7 @@ HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extens 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); BOOL WINAPI PathCchIsRoot(const WCHAR *path); +HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T path_size, WCHAR **path_end, SIZE_T *free_size); HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension); HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end); -- 2.19.1