From 2d4e7e7df7904459413f52eed059d1ec012e8561 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 20 Nov 2018 19:06:11 +0800 Subject: [PATCH] kernelbase: Implement PathCchAddExtension. 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 | 52 +++++++++ dlls/kernelbase/tests/path.c | 108 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 163 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 7b812eb49d1..fed23cbac11 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 2372db821e5..27d10ce7311 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 b051986031d..85f3b6ca15f 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -29,6 +29,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(path); +static SIZE_T strnlenW(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 = strnlenW(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 4bef964e10e..4414f4ef659 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); @@ -247,6 +248,111 @@ 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; + + if (!pPathCchAddExtension) + { + win_skip("PathCchAddExtension() is not available.\n"); + return; + } + + /* 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; @@ -372,10 +478,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 6bb760ccdbb..5a189bc0e70 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);