Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- v2: Fix apply failure due to mail client text encoding.
.../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 36 +++++++ dlls/kernelbase/tests/path.c | 98 +++++++++++++++++++ include/pathcch.h | 3 + 5 files changed, 139 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 287c5d61d9..7b812eb49d 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 @@ -9,7 +9,7 @@ @ stub PathCchCanonicalizeEx @ stub PathCchCombine @ stub PathCchCombineEx -@ stub PathCchFindExtension +@ stdcall PathCchFindExtension(wstr long ptr) kernelbase.PathCchFindExtension @ stub PathCchIsRoot @ stub PathCchRemoveBackslash @ stub PathCchRemoveBackslashEx diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index f3b4fd131f..2372db821e 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1038,7 +1038,7 @@ # @ stub PathCchCanonicalizeEx # @ stub PathCchCombine # @ stub PathCchCombineEx -# @ stub PathCchFindExtension +@ stdcall PathCchFindExtension(wstr long ptr) # @ stub PathCchIsRoot # @ stub PathCchRemoveBackslash # @ stub PathCchRemoveBackslashEx diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 373c34e979..0e391ecff0 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -1,5 +1,6 @@ /* * Copyright 2018 Nikolay Sivov + * Copyright 2018 Zhiyi Zhang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -65,3 +66,38 @@ HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, S
return S_OK; } + +HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension) +{ + const WCHAR *lastpoint = NULL; + const WCHAR *next; + SIZE_T length; + + TRACE("%s %lu %p\n", wine_dbgstr_w(path), size, extension); + + if (!path || !size || size > PATHCCH_MAX_CCH) + { + *extension = NULL; + return E_INVALIDARG; + } + + length = strlenW(path); + if (length + 1 > PATHCCH_MAX_CCH) + { + *extension = NULL; + return E_INVALIDARG; + } + + next = path; + while (*next) + { + if (*next == '\' || *next == ' ') + lastpoint = NULL; + else if (*next == '.') + lastpoint = next; + next++; + } + + *extension = lastpoint ? lastpoint : path + length; + return S_OK; +} diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index cdba51bf3f..5be05a0a67 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -2,6 +2,7 @@ * Path tests for kernelbase.dll * * Copyright 2017 Michael Müller + * Copyright 2018 Zhiyi Zhang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -32,6 +33,7 @@ HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size); HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining); 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);
static const struct { @@ -245,6 +247,100 @@ static void test_PathCchAddBackslashEx(void) } }
+struct findextension_test +{ + const CHAR *path; + INT extension_offset; +}; + +static const struct findextension_test findextension_tests[] = +{ + {"1.exe", 1}, + {"C:1.exe", 3}, + {"C:\1.exe", 4}, + {"\1.exe", 2}, + {"\\1.exe", 3}, + {"\\?\C:1.exe", 7}, + {"\\?\C:\1.exe", 8}, + {"\\?\UNC\1.exe", 9}, + {"\\?\UNC\192.168.1.1\1.exe", 21}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\1.exe", 50}, + + /* Malformed */ + {"", 0}, + {" ", 1}, + {".", 0}, + {"..", 1}, + {"a", 1}, + {"a.", 1}, + {".a.b.", 4}, + {"a. ", 3}, + {"a.\", 3}, + {"\\?\UNC\192.168.1.1", 17}, + {"\\?\UNC\192.168.1.1\", 20}, + {"\\?\UNC\192.168.1.1\a", 21} +}; + +static void test_PathCchFindExtension(void) +{ + WCHAR pathW[PATHCCH_MAX_CCH + 1] = {0}; + const WCHAR *extension; + HRESULT hr; + INT i; + + if (!pPathCchFindExtension) + { + win_skip("PathCchFindExtension() is not available.\n"); + return; + } + + /* Arguments check */ + extension = (const WCHAR *)0xdeadbeef; + hr = pPathCchFindExtension(NULL, PATHCCH_MAX_CCH, &extension); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + ok(extension == NULL, "Expect extension null, got %p\n", extension); + + extension = (const WCHAR *)0xdeadbeef; + hr = pPathCchFindExtension(pathW, 0, &extension); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + ok(extension == NULL, "Expect extension null, got %p\n", extension); + + /* Crashed on Windows */ + if (0) + { + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH, NULL); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + } + + /* Path length check */ + MultiByteToWideChar(CP_ACP, 0, "C:\1.exe", -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH + 1, &extension); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH, &extension); + ok(hr == S_OK, "expect result %#x, got %#x\n", S_OK, hr); + + for (i = 0; i < ARRAY_SIZE(pathW) - 1; i++) pathW[i] = 'a'; + pathW[PATHCCH_MAX_CCH] = 0; + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH, &extension); + ok(hr == E_INVALIDARG, "expect result %#x, got %#x\n", E_INVALIDARG, hr); + + pathW[PATHCCH_MAX_CCH - 1] = 0; + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH, &extension); + ok(hr == S_OK, "expect result %#x, got %#x\n", S_OK, hr); + + for (i = 0; i < ARRAY_SIZE(findextension_tests); i++) + { + const struct findextension_test *t = findextension_tests + i; + MultiByteToWideChar(CP_ACP, 0, t->path, -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchFindExtension(pathW, PATHCCH_MAX_CCH, &extension); + ok(hr == S_OK, "path %s expect result %#x, got %#x\n", t->path, S_OK, hr); + if (SUCCEEDED(hr)) + ok(extension - pathW == t->extension_offset, "path %s expect extension offset %d, got %d\n", t->path, + t->extension_offset, extension - pathW); + } +} + START_TEST(path) { HMODULE hmod = LoadLibraryA("kernelbase.dll"); @@ -252,8 +348,10 @@ START_TEST(path) pPathCchCombineEx = (void *)GetProcAddress(hmod, "PathCchCombineEx"); pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash"); pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx"); + pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension");
test_PathCchCombineEx(); test_PathCchAddBackslash(); test_PathCchAddBackslashEx(); + test_PathCchFindExtension(); } diff --git a/include/pathcch.h b/include/pathcch.h index 2b2aed4c8f..6bb760ccdb 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -23,6 +23,9 @@ #define PATHCCH_DO_NOT_NORMALIZE_SEGMENTS 0x08 #define PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH 0x10
+#define PATHCCH_MAX_CCH 0x8000 + HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining); 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);
On 11/19/18 6:11 AM, Zhiyi Zhang wrote:
- length = strlenW(path);
- if (length + 1 > PATHCCH_MAX_CCH)
- {
*extension = NULL;
return E_INVALIDARG;
- }
This doesn't look right. If it takes buffer length, that's the limit we should use. So if you have to iterate you should probably stop at this give limit.
- next = path;
- while (*next)
- {
if (*next == '\\' || *next == ' ')
lastpoint = NULL;
else if (*next == '.')
lastpoint = next;
next++;
- }
We'll need a test for size < length, and probably for forward slash too.
- if (!pPathCchFindExtension)
- {
win_skip("PathCchFindExtension() is not available.\n");
return;
- }
Maybe we can get rid of this, because it will never trigger if kernelbase is available. Probably 'hmod' check is enough to skip everything.
On Mon 11 19 15:25 PM, Nikolay Sivov wrote:
On 11/19/18 6:11 AM, Zhiyi Zhang wrote:
+ length = strlenW(path); + if (length + 1 > PATHCCH_MAX_CCH) + { + *extension = NULL; + return E_INVALIDARG; + }
This doesn't look right. If it takes buffer length, that's the limit we should use. So if you have to iterate you should probably stop at this give limit.
Right. Thanks. We also need to implement StringCchLengthW and friends instead of using strlenW to avoid buffer overrun.
+ next = path; + while (*next) + { + if (*next == '\' || *next == ' ') + lastpoint = NULL; + else if (*next == '.') + lastpoint = next; + next++; + }
We'll need a test for size < length, and probably for forward slash too.
+ if (!pPathCchFindExtension) + { + win_skip("PathCchFindExtension() is not available.\n"); + return; + }
Maybe we can get rid of this, because it will never trigger if kernelbase is available. Probably 'hmod' check is enough to skip everything.