[PATCH v30 0/2] MR9414: shlwapi: Handling of "C:\...xx" in PathCanonicalize[AW].
## Description This is intended to solve https://bugs.winehq.org/show_bug.cgi?id=58129 Add tests for verification, which pass on my locale win10 machine. ## Todo - [x] Removed UNC path check which seems okay, but i am not sure why it was introduced. git blame did not help me, therefore any hints or examples to test are welcome. ## Results UNC path handling is done in UrlCanonicalizeW, therefore i asume, that UNC part can be safly ignored here. -- v30: shlwapi: Handling of "C:\...xx" in PathCanonicalize[AW]. shlwapi/tests: Add additional tests for PathCanonicalize. https://gitlab.winehq.org/wine/wine/-/merge_requests/9414
From: Thomas Csovcsity <thc.fr13nd@gmail.com> --- dlls/shlwapi/tests/path.c | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/dlls/shlwapi/tests/path.c b/dlls/shlwapi/tests/path.c index fb8d3f1b9c1..fc09db9cd6c 100644 --- a/dlls/shlwapi/tests/path.c +++ b/dlls/shlwapi/tests/path.c @@ -700,6 +700,15 @@ static void test_PathCombineA(void) ok(!lstrcmpA(str, "C:\\"), "Expected C:\\, got %s\n", str); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + /* try ...xx as file */ + /* try forward slashes */ + SetLastError(0xdeadbeef); + lstrcpyA(dest, "control"); + str = PathCombineA(dest, "C:\\", "...xx"); + ok(str == dest, "Expected str == dest, got %p\n", str); + todo_wine ok(!lstrcmpA(str, "C:\\...xx"), "Expected C:\\...xx, got %s\n", str); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + /* try relative paths */ /* try forward slashes */ SetLastError(0xdeadbeef); @@ -1076,6 +1085,85 @@ static void test_PathCanonicalizeA(void) "Expected 0xdeadbeef or ERROR_FILENAME_EXCED_RANGE, got %ld\n", GetLastError()); } ok(lstrlenA(too_long) == LONG_LEN - 1, "Expected length LONG_LEN - 1, got %i\n", lstrlenA(too_long)); + + /* try C:\.. */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\.."); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(!lstrcmpA(dest, "C:\\"), "C:\\, got %s\n", dest); + + /* try C:\...x */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\...x"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + todo_wine ok(!lstrcmpA(dest, "C:\\...x"), "C:\\...x, got %s\n", dest); + + /* try C:\test\...x */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\test\\...x"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + todo_wine ok(!lstrcmpA(dest, "C:\\test\\...x"), "C:\\test\\...x, got %s\n", dest); + + /* try C:\test\...\x */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\test\\...\\x"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + todo_wine ok(!lstrcmpA(dest, "C:\\test\\...\\x"), "C:\\test\\...\\x, got %s\n", dest); + + /* try C:\test\...\x */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\test\\....\\x"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + todo_wine ok(!lstrcmpA(dest, "C:\\test\\....\\x"), "C:\\test\\....\\x, got %s\n", dest); + + /* try C:\\..\\something */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:\\..\\something"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(!lstrcmpA(dest, "C:\\something"), "C:\\, got %s\n", dest); + + /* try rooted and relative path */ + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:..\\something"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(!lstrcmpA(dest, "C:..\\something"), "C:..\\something, got %s\n", dest); + + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:..\\"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(!lstrcmpA(dest, "C:..\\"), "C:..\\, got %s\n", dest); + + memset(dest, 0, LONG_LEN + MAX_PATH); + lstrcpyA(dest, "test"); + SetLastError(0xdeadbeef); + res = PathCanonicalizeA(dest, "C:something"); + ok(res, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(!lstrcmpA(dest, "C:something"), "C:something, got %s\n", dest); } static void test_PathFindExtensionA(void) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9414
From: Thomas Csovcsity <thc.fr13nd@gmail.com> --- dlls/kernelbase/path.c | 63 ++++++++++++++++++++++----------------- dlls/shlwapi/tests/path.c | 10 +++---- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index ece259ebe7b..bd7c96bf6dc 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -1280,15 +1280,15 @@ BOOL WINAPI PathCanonicalizeW(WCHAR *buffer, const WCHAR *path) TRACE("%p, %s\n", buffer, wine_dbgstr_w(path)); - if (dst) - *dst = '\0'; - if (!dst || !path) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + if (dst) + *dst = '\0'; + if (!*path) { *buffer++ = '\\'; @@ -1315,42 +1315,51 @@ BOOL WINAPI PathCanonicalizeW(WCHAR *buffer, const WCHAR *path) { if (*src == '.') { + /* Skip './' if at beginning of path and always ':./', '/./' */ if (src[1] == '\\' && (src == path || src[-1] == '\\' || src[-1] == ':')) { src += 2; /* Skip .\ */ } - else if (src[1] == '.' && dst != buffer && dst[-1] == '\\') + else if ( src[1] == '.' && src[2] != '.' ) { - /* \.. backs up a directory, over the root if it has no \ following X:. - * .. is ignored if it would remove a UNC server name or initial \\ - */ - if (dst != buffer) + if (dst != buffer && dst[-1] == '\\') { - *dst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ - if (dst > buffer + 1 && dst[-1] == '\\' && (dst[-2] != '\\' || dst > buffer + 2)) + /* \.. backs up a directory, over the root if it has no \ following X:. + * .. is ignored if it would remove a UNC server name or initial \\ + */ + if (dst != buffer) { - if (dst[-2] == ':' && (dst > buffer + 3 || dst[-3] == ':')) + *dst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ + if ((src[2] == '\\' || src[2] == ':' || src[2] == '\0' ) && dst > buffer + 1 && dst[-1] == '\\' && (dst[-2] != '\\' || dst > buffer + 2)) { - dst -= 2; - while (dst > buffer && *dst != '\\') - dst--; - if (*dst == '\\') - dst++; /* Reset to last '\' */ - else - dst = buffer; /* Start path again from new root */ + if (dst[-2] == ':' && (dst > buffer + 3 || dst[-3] == ':')) + { + dst -= 2; + while (dst > buffer && *dst != '\\') + dst--; + if (*dst == '\\') + dst++; /* Reset to last '\' */ + else + dst = buffer; /* Start path again from new root */ + } + else if (dst[-2] != ':' && !PathIsUNCServerShareW(buffer)) { + dst -= 2; + } + } + while (dst > buffer && *dst != '\\') + dst--; + if (dst == buffer) + { + *dst++ = '\\'; + src++; } - else if (dst[-2] != ':' && !PathIsUNCServerShareW(buffer)) - dst -= 2; } - while (dst > buffer && *dst != '\\') - dst--; - if (dst == buffer) - { + if (*dst != '\\') *dst++ = '\\'; - src++; - } + src += 2; /* Skip .. in src path */ } - src += 2; /* Skip .. in src path */ + else + *dst++ = *src++; } else *dst++ = *src++; diff --git a/dlls/shlwapi/tests/path.c b/dlls/shlwapi/tests/path.c index fc09db9cd6c..4d32948db23 100644 --- a/dlls/shlwapi/tests/path.c +++ b/dlls/shlwapi/tests/path.c @@ -706,7 +706,7 @@ static void test_PathCombineA(void) lstrcpyA(dest, "control"); str = PathCombineA(dest, "C:\\", "...xx"); ok(str == dest, "Expected str == dest, got %p\n", str); - todo_wine ok(!lstrcmpA(str, "C:\\...xx"), "Expected C:\\...xx, got %s\n", str); + ok(!lstrcmpA(str, "C:\\...xx"), "Expected C:\\...xx, got %s\n", str); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); /* try relative paths */ @@ -1102,7 +1102,7 @@ static void test_PathCanonicalizeA(void) res = PathCanonicalizeA(dest, "C:\\...x"); ok(res, "Expected success\n"); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); - todo_wine ok(!lstrcmpA(dest, "C:\\...x"), "C:\\...x, got %s\n", dest); + ok(!lstrcmpA(dest, "C:\\...x"), "C:\\...x, got %s\n", dest); /* try C:\test\...x */ memset(dest, 0, LONG_LEN + MAX_PATH); @@ -1111,7 +1111,7 @@ static void test_PathCanonicalizeA(void) res = PathCanonicalizeA(dest, "C:\\test\\...x"); ok(res, "Expected success\n"); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); - todo_wine ok(!lstrcmpA(dest, "C:\\test\\...x"), "C:\\test\\...x, got %s\n", dest); + ok(!lstrcmpA(dest, "C:\\test\\...x"), "C:\\test\\...x, got %s\n", dest); /* try C:\test\...\x */ memset(dest, 0, LONG_LEN + MAX_PATH); @@ -1120,7 +1120,7 @@ static void test_PathCanonicalizeA(void) res = PathCanonicalizeA(dest, "C:\\test\\...\\x"); ok(res, "Expected success\n"); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); - todo_wine ok(!lstrcmpA(dest, "C:\\test\\...\\x"), "C:\\test\\...\\x, got %s\n", dest); + ok(!lstrcmpA(dest, "C:\\test\\...\\x"), "C:\\test\\...\\x, got %s\n", dest); /* try C:\test\...\x */ memset(dest, 0, LONG_LEN + MAX_PATH); @@ -1129,7 +1129,7 @@ static void test_PathCanonicalizeA(void) res = PathCanonicalizeA(dest, "C:\\test\\....\\x"); ok(res, "Expected success\n"); ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", GetLastError()); - todo_wine ok(!lstrcmpA(dest, "C:\\test\\....\\x"), "C:\\test\\....\\x, got %s\n", dest); + ok(!lstrcmpA(dest, "C:\\test\\....\\x"), "C:\\test\\....\\x, got %s\n", dest); /* try C:\\..\\something */ memset(dest, 0, LONG_LEN + MAX_PATH); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9414
On Thu Jan 22 18:52:52 2026 +0000, Thomas Csovcsity wrote:
changed this line in [version 30 of the diff](/wine/wine/-/merge_requests/9414/diffs?diff_id=239442&start_sha=4085296e67b454f1170c32744d50d299e0d41f66#51274abdbe6f8d33e4fbee326942be6388d93666_1327_1325) rework done
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9414#note_127701
participants (2)
-
Thomas Csovcsity -
Thomas Csovcsity (@thc13)