From d381263ae524c78e85b22787db3def64730e4f06 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 23 Nov 2018 11:17:53 +0800 Subject: [PATCH] kernelbase: Implement PathCchRemoveFileSpec. Signed-off-by: Zhiyi Zhang Signed-off-by: Alexandre Julliard --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 37 +++++ dlls/kernelbase/tests/path.c | 126 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 166 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 fcc3d7ffad4..725f16448fd 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 @@ -14,7 +14,7 @@ @ stdcall PathCchRemoveBackslash(wstr long) kernelbase.PathCchRemoveBackslash @ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) kernelbase.PathCchRemoveBackslashEx @ stdcall PathCchRemoveExtension(wstr long) kernelbase.PathCchRemoveExtension -@ stub PathCchRemoveFileSpec +@ stdcall PathCchRemoveFileSpec(wstr long) kernelbase.PathCchRemoveFileSpec @ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension @ stdcall PathCchSkipRoot(wstr ptr) kernelbase.PathCchSkipRoot @ stdcall PathCchStripPrefix(wstr long) kernelbase.PathCchStripPrefix diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 252f47c5c79..e7bb62d75c1 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1043,7 +1043,7 @@ @ stdcall PathCchRemoveBackslash(wstr long) @ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) @ stdcall PathCchRemoveExtension(wstr long) -# @ stub PathCchRemoveFileSpec +@ stdcall PathCchRemoveFileSpec(wstr long) @ stdcall PathCchRenameExtension(wstr long wstr) @ stdcall PathCchSkipRoot(wstr ptr) @ stdcall PathCchStripPrefix(wstr long) diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index b28abc437c0..c3fca8b9cfa 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -346,6 +346,43 @@ HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size) return next == extension ? S_FALSE : S_OK; } +HRESULT WINAPI PathCchRemoveFileSpec(WCHAR *path, SIZE_T size) +{ + const WCHAR *root_end = NULL; + SIZE_T length; + WCHAR *last; + + TRACE("%s %lu\n", wine_dbgstr_w(path), size); + + if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + if (PathCchIsRoot(path)) return S_FALSE; + + PathCchSkipRoot(path, &root_end); + + /* The backslash at the end of UNC and \\* are not considered part of root in this case */ + if (root_end && root_end > path && root_end[-1] == '\\' + && (is_prefixed_unc(path) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?'))) + root_end--; + + length = strlenW(path); + last = path + length - 1; + while (last >= path && (!root_end || last >= root_end)) + { + if (last - path >= size) return E_INVALIDARG; + + if (*last == '\\') + { + *last-- = 0; + break; + } + + *last-- = 0; + } + + return last != path + length - 1 ? S_OK : S_FALSE; +} + HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) { HRESULT hr; diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 965d9f10624..2faf9e9ae1e 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -39,6 +39,7 @@ BOOL (WINAPI *pPathCchIsRoot)(const WCHAR *path); HRESULT (WINAPI *pPathCchRemoveBackslash)(WCHAR *path, SIZE_T path_size); 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 *pPathCchRemoveFileSpec)(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); HRESULT (WINAPI *pPathCchStripPrefix)(WCHAR *path, SIZE_T size); @@ -787,6 +788,129 @@ static void test_PathCchRemoveExtension(void) } } +struct removefilespec_test +{ + const CHAR *path; + const CHAR *expected; + HRESULT hr; + SIZE_T size; +}; + +static const struct removefilespec_test removefilespec_tests[] = +{ + {"", "", S_FALSE}, + {"a", "", S_OK}, + {"a\\", "a", S_OK}, + {"a\\b", "a", S_OK}, + + {"\\", "\\", S_FALSE}, + {"\\a", "\\", S_OK}, + {"\\a\\", "\\a", S_OK}, + {"\\a\\b", "\\a", S_OK}, + + {"\\\\", "\\\\", S_FALSE}, + {"\\\\a", "\\\\a", S_FALSE}, + {"\\\\a\\", "\\\\a", S_OK}, + {"\\\\a\\b", "\\\\a\\b", S_FALSE}, + {"\\\\a\\b\\", "\\\\a\\b", S_OK}, + {"\\\\a\\b\\c", "\\\\a\\b", S_OK}, + + {"C:", "C:", S_FALSE}, + {"C:a", "C:", S_OK}, + {"C:a\\", "C:a", S_OK}, + {"C:a\\b", "C:a", S_OK}, + + {"C:\\", "C:\\", S_FALSE}, + {"C:\\a", "C:\\", S_OK}, + {"C:\\a\\", "C:\\a", S_OK}, + {"C:\\a\\b", "C:\\a", S_OK}, + + {"\\\\?\\", "\\\\?", S_OK}, + {"\\\\?\\a", "\\\\?", S_OK}, + {"\\\\?\\a\\", "\\\\?\\a", S_OK}, + {"\\\\?\\a\\b", "\\\\?\\a", S_OK}, + + {"\\\\?\\C:", "\\\\?\\C:", S_FALSE}, + {"\\\\?\\C:a", "\\\\?\\C:", S_OK}, + {"\\\\?\\C:a\\", "\\\\?\\C:a", S_OK}, + {"\\\\?\\C:a\\b", "\\\\?\\C:a", S_OK}, + + {"\\\\?\\C:\\", "\\\\?\\C:\\", S_FALSE}, + {"\\\\?\\C:\\a", "\\\\?\\C:\\", S_OK}, + {"\\\\?\\C:\\a\\", "\\\\?\\C:\\a", S_OK}, + {"\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", S_OK}, + + {"\\\\?\\UNC\\", "\\\\?\\UNC\\", S_FALSE}, + {"\\\\?\\UNC\\a", "\\\\?\\UNC\\a", S_FALSE}, + {"\\\\?\\UNC\\a\\", "\\\\?\\UNC\\a", S_OK}, + {"\\\\?\\UNC\\a\\b", "\\\\?\\UNC\\a\\b", S_FALSE}, + {"\\\\?\\UNC\\a\\b\\", "\\\\?\\UNC\\a\\b", S_OK}, + {"\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b", S_OK}, + + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", S_FALSE}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a\\", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a", S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a\\b", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a", S_OK}, + + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", S_FALSE}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a\\", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a\\b", + "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", S_OK}, + + /* Size tests */ + {"C:\\a", NULL, E_INVALIDARG, PATHCCH_MAX_CCH + 1}, + {"C:\\a", "C:\\", S_OK, PATHCCH_MAX_CCH}, + /* Size < original path length + 1, read beyond size */ + {"C:\\a", "C:\\", S_OK, ARRAY_SIZE("C:\\a") - 1}, + /* Size < result path length + 1 */ + {"C:\\a", NULL, E_INVALIDARG, ARRAY_SIZE("C:\\") - 1} +}; + +static void test_PathCchRemoveFileSpec(void) +{ + WCHAR pathW[PATHCCH_MAX_CCH] = {0}; + CHAR pathA[PATHCCH_MAX_CCH]; + SIZE_T size; + HRESULT hr; + INT i; + + if (!pPathCchRemoveFileSpec) + { + win_skip("PathCchRemoveFileSpec() is not available.\n"); + return; + } + + /* Null arguments */ + hr = pPathCchRemoveFileSpec(NULL, ARRAY_SIZE(pathW)); + ok(hr == E_INVALIDARG, "expect %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchRemoveFileSpec(pathW, 0); + ok(hr == E_INVALIDARG, "expect %#x, got %#x\n", E_INVALIDARG, hr); + + for (i = 0; i < ARRAY_SIZE(removefilespec_tests); i++) + { + const struct removefilespec_test *t = removefilespec_tests + i; + + MultiByteToWideChar(CP_ACP, 0, t->path, -1, pathW, ARRAY_SIZE(pathW)); + size = t->size ? t->size : ARRAY_SIZE(pathW); + hr = pPathCchRemoveFileSpec(pathW, size); + ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path, 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 expect stripped path %s, got %s\n", t->path, t->expected, pathA); + } + } +} + struct renameextension_test { const CHAR *path; @@ -1271,6 +1395,7 @@ START_TEST(path) pPathCchRemoveBackslash = (void *)GetProcAddress(hmod, "PathCchRemoveBackslash"); pPathCchRemoveBackslashEx = (void *)GetProcAddress(hmod, "PathCchRemoveBackslashEx"); pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension"); + pPathCchRemoveFileSpec = (void *)GetProcAddress(hmod, "PathCchRemoveFileSpec"); pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension"); pPathCchSkipRoot = (void *)GetProcAddress(hmod, "PathCchSkipRoot"); pPathCchStripPrefix = (void *)GetProcAddress(hmod, "PathCchStripPrefix"); @@ -1286,6 +1411,7 @@ START_TEST(path) test_PathCchRemoveBackslash(); test_PathCchRemoveBackslashEx(); test_PathCchRemoveExtension(); + test_PathCchRemoveFileSpec(); test_PathCchRenameExtension(); test_PathCchSkipRoot(); test_PathCchStripPrefix(); diff --git a/include/pathcch.h b/include/pathcch.h index f4c83770593..12a6fc5511d 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -34,6 +34,7 @@ BOOL WINAPI PathCchIsRoot(const WCHAR *path); HRESULT WINAPI PathCchRemoveBackslash(WCHAR *path, SIZE_T path_size); 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 PathCchRemoveFileSpec(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); HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size);