From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 24 +++++++----------------- dlls/kernelbase/tests/path.c | 1 + 2 files changed, 8 insertions(+), 17 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 548982e4e7e..fe4b9cd9cd3 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -915,29 +915,16 @@ HRESULT WINAPI PathCchStripToRoot(WCHAR *path, SIZE_T size) { const WCHAR *root_end; WCHAR *segment_end; - BOOL is_unc;
TRACE("%s %Iu\n", wine_dbgstr_w(path), size);
if (!path || !*path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG;
- /* \\?\UNC\* and \\* have to have at least two extra segments to be striped, - * e.g. \\?\UNC\a\b\c -> \\?\UNC\a\b - * \\a\b\c -> \\a\b */ - if ((is_unc = is_prefixed_unc(path)) || (path[0] == '\' && path[1] == '\' && path[2] != '?')) - { - root_end = is_unc ? path + 8 : path + 3; - if (!get_next_segment(root_end, &root_end)) return S_FALSE; - if (!get_next_segment(root_end, &root_end)) return S_FALSE; - - if (root_end - path >= size) return E_INVALIDARG; - - segment_end = path + (root_end - path) - 1; - *segment_end = 0; - return S_OK; - } - else if (PathCchSkipRoot(path, &root_end) == S_OK) + if (PathCchSkipRoot(path, &root_end) == S_OK) { + if (root_end && root_end > path && root_end[-1] == '\' + && ((is_prefixed_unc(path) && path[8]) || (path[0] == '\' && path[1] == '\' && path[2] && path[2] != '?'))) + root_end--; if (root_end - path >= size) return E_INVALIDARG;
segment_end = path + (root_end - path); @@ -947,7 +934,10 @@ HRESULT WINAPI PathCchStripToRoot(WCHAR *path, SIZE_T size) return S_OK; } else + { + *path = 0; return E_INVALIDARG; + } }
BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server) diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 36e00e0d9ef..59b93b3fcdd 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -2200,6 +2200,7 @@ static const struct striptoroot_test striptoroot_tests[] = {"\\a", "\\a", S_FALSE}, {"\\a\b", "\\a\b", S_FALSE}, {"\\a\b\c", "\\a\b", S_OK}, + {"\\\", "\\", S_OK},
/* UNC */ {"\\?\UNC\", "\\?\UNC\", S_FALSE},
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 35 +++++++++++------------------------ dlls/kernelbase/tests/path.c | 2 ++ 2 files changed, 13 insertions(+), 24 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index fe4b9cd9cd3..10140827ee5 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -809,39 +809,26 @@ HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size)
HRESULT WINAPI PathCchRemoveFileSpec(WCHAR *path, SIZE_T size) { - const WCHAR *root_end = NULL; - SIZE_T length; - WCHAR *last; + WCHAR *last, *root_end;
TRACE("%s %Iu\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); + if (FAILED(PathCchSkipRoot(path, (const WCHAR **)&root_end))) + root_end = path;
/* 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] != '?'))) + if (root_end > path && root_end[-1] == '\' && ((is_prefixed_unc(path) && path[8]) + || (path[0] == '\' && path[1] == '\' && path[2] && path[2] != '?'))) root_end--;
- length = lstrlenW(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; + if (!(last = StrRChrW(root_end, NULL, '\'))) last = root_end; + if (last > root_end && last[-1] == '\' && last[1] != '?') --last; + if (last - path >= size) return E_INVALIDARG; + if (!*last) return S_FALSE; + *last = 0; + return S_OK; }
HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 59b93b3fcdd..4257abb29f0 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -1779,11 +1779,13 @@ static const struct removefilespec_test removefilespec_tests[] = {"\\a\b", "\\a\b", S_FALSE}, {"\\a\b\", "\\a\b", S_OK}, {"\\a\b\c", "\\a\b", S_OK}, + {"\\\\\\", "\\\\", 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:\a\\b", "C:\a", S_OK},
{"C:\", "C:\", S_FALSE}, {"C:\a", "C:\", S_OK},
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 1 - dlls/kernelbase/tests/path.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 10140827ee5..d05e095cc55 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -728,7 +728,6 @@ BOOL WINAPI PathCchIsRoot(const WCHAR *path) /* Has first segment with an ending backslash and has remaining characters*/ else { - next++; /* Second segment must have no backslash and no remaining characters */ return !get_next_segment(next, &next) && !*next; } diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 4257abb29f0..d04bf35ad32 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -1481,6 +1481,8 @@ static const struct isroot_test isroot_tests[] = {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", TRUE}, {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a", FALSE}, {"..\a", FALSE}, + {"\\\\", FALSE}, + {"\\a\\b", FALSE},
/* Wrong MSDN examples */ {"\a", FALSE},
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 67 ++++-------------------------------------- 1 file changed, 6 insertions(+), 61 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index d05e095cc55..33f4a08b3b3 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -1025,77 +1025,22 @@ BOOL WINAPI PathIsUNCServerShareW(const WCHAR *path)
BOOL WINAPI PathIsRootA(const char *path) { + WCHAR pathW[MAX_PATH]; + TRACE("%s\n", wine_dbgstr_a(path));
- if (!path || !*path) + if (!MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH)) return FALSE; + if (is_prefixed_unc(pathW) || is_prefixed_disk(pathW) || is_prefixed_volume(pathW)) return FALSE;
- if (*path == '\') - { - if (!path[1]) - return TRUE; /* \ */ - else if (path[1] == '\') - { - BOOL seen_slash = FALSE; - path += 2; - - /* Check for UNC root path */ - while (*path) - { - if (*path == '\') - { - if (seen_slash) - return FALSE; - seen_slash = TRUE; - } - - path = CharNextA(path); - } - - return TRUE; - } - } - else if (path[1] == ':' && path[2] == '\' && path[3] == '\0') - return TRUE; /* X:\ */ - - return FALSE; + return PathIsRootW(pathW); }
BOOL WINAPI PathIsRootW(const WCHAR *path) { TRACE("%s\n", wine_dbgstr_w(path));
- if (!path || !*path) - return FALSE; - - if (*path == '\') - { - if (!path[1]) - return TRUE; /* \ */ - else if (path[1] == '\') - { - BOOL seen_slash = FALSE; - - path += 2; - /* Check for UNC root path */ - while (*path) - { - if (*path == '\') - { - if (seen_slash) - return FALSE; - seen_slash = TRUE; - } - path++; - } - - return TRUE; - } - } - else if (path[1] == ':' && path[2] == '\' && path[3] == '\0') - return TRUE; /* X:\ */ - - return FALSE; + return PathCchIsRoot(path); }
BOOL WINAPI PathRemoveFileSpecA(char *path)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 33f4a08b3b3..1412efbb739 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -1123,30 +1123,23 @@ BOOL WINAPI PathRemoveFileSpecW(WCHAR *path)
BOOL WINAPI PathStripToRootA(char *path) { - TRACE("%s\n", wine_dbgstr_a(path)); + WCHAR pathW[MAX_PATH];
- if (!path) - return FALSE; + TRACE("%s\n", wine_dbgstr_a(path));
- while (!PathIsRootA(path)) - if (!PathRemoveFileSpecA(path)) - return FALSE; + if (!MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH)) return FALSE;
- return TRUE; + *path = 0; + if (is_prefixed_unc(pathW) || is_prefixed_disk(pathW) || is_prefixed_volume(pathW)) return FALSE; + if (!PathStripToRootW(pathW)) return FALSE; + return !!WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, MAX_PATH, 0, 0); }
BOOL WINAPI PathStripToRootW(WCHAR *path) { TRACE("%s\n", wine_dbgstr_w(path));
- if (!path) - return FALSE; - - while (!PathIsRootW(path)) - if (!PathRemoveFileSpecW(path)) - return FALSE; - - return TRUE; + return SUCCEEDED(PathCchStripToRoot(path, PATHCCH_MAX_CCH)); }
LPSTR WINAPI PathAddBackslashA(char *path)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/path.c | 100 ++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 50 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 1412efbb739..244b65d1755 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -126,6 +126,11 @@ static bool is_slash( char c ) return c == '/' || c == '\'; }
+static BOOL is_drive_specA( const char *str ) +{ + return isalpha( str[0] ) && str[1] == ':'; +} + static BOOL is_drive_spec( const WCHAR *str ) { return isalpha( str[0] ) && str[1] == ':'; @@ -1045,80 +1050,75 @@ BOOL WINAPI PathIsRootW(const WCHAR *path)
BOOL WINAPI PathRemoveFileSpecA(char *path) { - char *filespec = path; - BOOL modified = FALSE; + char *root_end = NULL, *ptr;
- TRACE("%s\n", wine_dbgstr_a(path)); + TRACE("%s\n", debugstr_a(path));
- if (!path) + if (!path || !*path) return FALSE;
- /* Skip directory or UNC path */ - if (*path == '\') - filespec = ++path; - if (*path == '\') - filespec = ++path; - - while (*path) + if (is_drive_specA(path)) { - if (*path == '\') - filespec = path; /* Skip dir */ - else if (*path == ':') + root_end = path + 2; + if (*root_end == '\') ++root_end; + } + else + { + root_end = path; + if (*root_end == '\') ++root_end; + if (root_end[1] != '?') { - filespec = ++path; /* Skip drive */ - if (*path == '\') - filespec++; + if (*root_end == '\') ++root_end; + if (root_end - path > 1 && is_drive_specA(root_end)) root_end += 2; + if (*root_end == '\' && root_end[1] && root_end[1] != '\') ++root_end; } - if (!(path = CharNextA(path))) - break; } - - if (*filespec) + ptr = StrRChrA(root_end, NULL, '\'); + if (ptr && ptr != root_end) { - *filespec = '\0'; - modified = TRUE; + if (ptr[-1] == '\') --ptr; + *ptr = 0; + return TRUE; } - - return modified; + if (!*root_end) return FALSE; + *root_end = 0; + return TRUE; }
BOOL WINAPI PathRemoveFileSpecW(WCHAR *path) { - WCHAR *filespec = path; - BOOL modified = FALSE; + WCHAR *root_end = NULL, *ptr;
- TRACE("%s\n", wine_dbgstr_w(path)); + TRACE("%s\n", debugstr_w(path));
- if (!path) + if (!path || !*path) return FALSE;
- /* Skip directory or UNC path */ - if (*path == '\') - filespec = ++path; - if (*path == '\') - filespec = ++path; - - while (*path) + if (is_prefixed_volume(path)) root_end = path + 48; + else if (is_prefixed_disk(path)) root_end = path + 6; + else if (is_drive_spec(path)) root_end = path + 2; + if (!root_end) { - if (*path == '\') - filespec = path; /* Skip dir */ - else if (*path == ':') + root_end = path; + if (*root_end == '\') ++root_end; + if (root_end[1] != '?') { - filespec = ++path; /* Skip drive */ - if (*path == '\') - filespec++; + if (*root_end == '\') ++root_end; + if (root_end - path > 1 && is_drive_spec(root_end)) root_end += 2; + if (*root_end == '\' && root_end[1] && root_end[1] != '\') ++root_end; } - - path++; } - - if (*filespec) + else if (*root_end == '\') ++root_end; + ptr = StrRChrW(root_end, NULL, '\'); + if (ptr && ptr != root_end) { - *filespec = '\0'; - modified = TRUE; + if (ptr[-1] == '\') --ptr; + *ptr = 0; + return TRUE; } - - return modified; + if (!*root_end) return FALSE; + *root_end = 0; + return TRUE; }
BOOL WINAPI PathStripToRootA(char *path)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/tests/path.c | 237 +++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+)
diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index d04bf35ad32..62a8f7e5b4f 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -53,6 +53,13 @@ HRESULT (WINAPI *pPathCchStripPrefix)(WCHAR *path, SIZE_T size); HRESULT (WINAPI *pPathCchStripToRoot)(WCHAR *path, SIZE_T size); BOOL (WINAPI *pPathIsUNCEx)(const WCHAR *path, const WCHAR **server);
+BOOL (WINAPI *pPathIsRootA)(const char *path); +BOOL (WINAPI *pPathIsRootW)(const WCHAR *path); +BOOL (WINAPI *pPathStripToRootA)(char *path); +BOOL (WINAPI *pPathStripToRootW)(WCHAR *path); +BOOL (WINAPI *pPathRemoveFileSpecA)(char *path); +BOOL (WINAPI *pPathRemoveFileSpecW)(WCHAR *path); + struct alloccanonicalize_test { const CHAR *path_in; @@ -2352,6 +2359,228 @@ static void test_PathIsUNCEx(void) } }
+static void test_path_manipulation(void) +{ + static const struct + { + const char *path; + BOOL is_root; + BOOL stript_to_root_ret; + HRESULT cch_strip_to_root_hr; + const char *strip_to_root; + BOOL remove_file_ret; + const char *remove_file; + BOOL aw_differ, w_version; + } + tests[] = + { + {"Q:\", TRUE, TRUE, S_FALSE, "Q:\", FALSE, "Q:\"}, + {"Q:/", FALSE, TRUE, S_OK, "Q:", TRUE, "Q:"}, + {"Q:", FALSE, TRUE, S_FALSE, "Q:", FALSE, "Q:"}, + {"|:", FALSE, FALSE, E_INVALIDARG, "", TRUE, ""}, + {"Q:\\", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\"}, + {"Q:\\test1", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\"}, + {"Q:\test1\test2", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1"}, + {"Q:\test1\\test2", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1"}, + {"Q:\test1\\\test2", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1\"}, + {"Q:\test1\test2\", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1\test2"}, + {"Q:\test1\\test2\", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1\\test2"}, + {"Q:\test1\test2\\", FALSE, TRUE, S_OK, "Q:\", TRUE, "Q:\test1\test2"}, + {"Q:/test1/test2", FALSE, TRUE, S_OK, "Q:", TRUE, "Q:"}, + {"Q:/test1\test2", FALSE, TRUE, S_OK, "Q:", TRUE, "Q:/test1"}, + {"0:/test1/test2", FALSE, FALSE, E_INVALIDARG, "", TRUE, ""}, + {"test", FALSE, FALSE, E_INVALIDARG, "", TRUE, ""}, + {"\\?\Q:\", FALSE, FALSE, S_OK, "", TRUE, "\\?\Q:", TRUE, FALSE}, + {"\\?\Q:\", TRUE, TRUE, S_FALSE, "\\?\Q:\", FALSE, "\\?\Q:\", TRUE, TRUE}, + {"\\?\Q:\a", FALSE, TRUE, S_OK, "\\?\Q:\", TRUE, "\\?\Q:\", TRUE, TRUE}, + {"\\?\Q:\\", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\Q:", TRUE, FALSE}, + {"\\?\Q:\\", FALSE, TRUE, S_OK, "\\?\Q:\", TRUE, "\\?\Q:\", TRUE, TRUE}, + {"\\?\Q:", FALSE, FALSE, S_FALSE, "", TRUE, "\\?", TRUE, FALSE}, + {"\\?\Q:", FALSE, TRUE, S_FALSE, "\\?\Q:", FALSE, "\\?\Q:", TRUE, TRUE}, + {"\\?\aa\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?\aa"}, + {"\\?\aa", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?"}, + {"\\.\Q:\", FALSE, TRUE, S_OK, "\\.\Q:", TRUE, "\\.\Q:"}, + {"\\.\Q:", TRUE, TRUE, S_FALSE, "\\.\Q:", TRUE, "\\."}, + {"\\.\", FALSE, TRUE, S_OK, "\\.", TRUE, "\\."}, + {"\\?\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?"}, + {"\\?\\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?"}, + {"?", FALSE, FALSE, E_INVALIDARG, "", TRUE, ""}, + {"\?", FALSE, TRUE, S_OK, "\", TRUE, "\"}, + {"\\?", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\"}, + {"\\*", TRUE, TRUE, S_FALSE, "\\*", TRUE, "\\"}, + {"\\.", TRUE, TRUE, S_FALSE, "\\.", TRUE, "\\"}, + {"\\\.", TRUE, TRUE, S_FALSE, "\\\.", TRUE, "\\\"}, + {"\\\a", TRUE, TRUE, S_FALSE, "\\\a", TRUE, "\\\"}, + {"\\\\.", FALSE, TRUE, S_OK, "\\", TRUE, "\\"}, + {"\\!", TRUE, TRUE, S_FALSE, "\\!", TRUE, "\\"}, + {"\\|", TRUE, TRUE, S_FALSE, "\\|", TRUE, "\\"}, + {"\\a", TRUE, TRUE, S_FALSE, "\\a", TRUE, "\\"}, + {"\\a\", FALSE, TRUE, S_OK, "\\a", TRUE, "\\a"}, + {"\\a\Q:\", FALSE, TRUE, S_OK, "\\a\Q:", TRUE, "\\a\Q:"}, + {"\\a\Q:", TRUE, TRUE, S_FALSE, "\\a\Q:", TRUE, "\\a"}, + {"\\aa\Q:\", FALSE, TRUE, S_OK, "\\aa\Q:", TRUE, "\\aa\Q:"}, + {"\\aa\Q:", TRUE, TRUE, S_FALSE, "\\aa\Q:", TRUE, "\\aa"}, + {"\\aa", TRUE, TRUE, S_FALSE, "\\aa", TRUE, "\\"}, + {"\\aa\", FALSE, TRUE, S_OK, "\\aa", TRUE, "\\aa"}, + {"\\aa\b", TRUE, TRUE, S_FALSE, "\\aa\b", TRUE, "\\aa"}, + {"\\aa\|", TRUE, TRUE, S_FALSE, "\\aa\|", TRUE, "\\aa"}, + {"\\aa\b\c", FALSE, TRUE, S_OK, "\\aa\b", TRUE, "\\aa\b"}, + {"\\Q:", TRUE, TRUE, S_FALSE, "\\Q:", FALSE, "\\Q:"}, + {"\\\Q:", TRUE, TRUE, S_FALSE, "\\\Q:", TRUE, "\\\"}, + {"\\Q:\", FALSE, TRUE, S_OK, "\\Q:", TRUE, "\\Q:"}, + {"\\?Q:\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?Q:"}, + {"\??\Q:\", FALSE, TRUE, S_OK, "\", TRUE, "\??\Q:"}, + {"\??\", FALSE, TRUE, S_OK, "\", TRUE, "\??"}, + {"\\o\Q:\aaa", FALSE, TRUE, S_OK, "\\o\Q:", TRUE, "\\o\Q:"}, + {"||||Q:\aaa", FALSE, FALSE, E_INVALIDARG, "", TRUE, "||||Q:"}, + {"\\\\Q:\", FALSE, TRUE, S_OK, "\\", TRUE, "\\\\Q:"}, + {"\\\\Q:", FALSE, TRUE, S_OK, "\\", TRUE, "\\"}, + {"\\", TRUE, TRUE, S_FALSE, "\\", FALSE, "\\"}, + {"\\\", FALSE, TRUE, S_OK, "\\", TRUE, "\\"}, + {"\\\\", FALSE, TRUE, S_OK, "\\", TRUE, "\\"}, + {"\\\\\", FALSE, TRUE, S_OK, "\\", TRUE, "\\\"}, + {"\\\\\\", FALSE, TRUE, S_OK, "\\", TRUE, "\\\\"}, + {"\", TRUE, TRUE, S_FALSE, "\", FALSE, "\"}, + {"\a", FALSE, TRUE, S_OK, "\", TRUE, "\"}, + {"\a\b", FALSE, TRUE, S_OK, "\", TRUE, "\a"}, + {"\\a", TRUE, TRUE, S_FALSE, "\\a", TRUE, "\\"}, + {"\\a\b", TRUE, TRUE, S_FALSE, "\\a\b", TRUE, "\\a"}, + {"\\a\\b", FALSE, TRUE, S_OK, "\\a", TRUE, "\\a"}, + {"\\a\b\", FALSE, TRUE, S_OK, "\\a\b", TRUE, "\\a\b"}, + {"\\a\b\c", FALSE, TRUE, S_OK, "\\a\b", TRUE, "\\a\b"}, + {"", FALSE, FALSE, E_INVALIDARG, "", FALSE, ""}, + {"\\Q:\", FALSE, TRUE, S_OK, "\\Q:", TRUE, "\\Q:"}, + {"\Q:\", FALSE, TRUE, S_OK, "\", TRUE, "\Q:"}, + {"\Q:", FALSE, TRUE, S_OK, "\", TRUE, "\"}, + {"\\?\UNC\", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\UNC", TRUE, FALSE}, + {"\\?\UNC\", TRUE, TRUE, S_FALSE, "\\?\UNC\", TRUE, "\\?\UNC", TRUE, TRUE}, + {"\\?\UNC\a", FALSE, FALSE, S_OK, "", TRUE, "\\?\UNC", TRUE, FALSE}, + {"\\?\UNC\a", TRUE, TRUE, S_FALSE, "\\?\UNC\a", TRUE, "\\?\UNC", TRUE, TRUE}, + {"\\?\UNC\a\", FALSE, FALSE, S_OK, "", TRUE, "\\?\UNC\a", TRUE, FALSE}, + {"\\?\UNC\a\", FALSE, TRUE, S_OK, "\\?\UNC\a", TRUE, "\\?\UNC\a", TRUE, TRUE}, + {"\\?\UNC\a\b", FALSE, FALSE, S_OK, "", TRUE, "\\?\UNC\a", TRUE, FALSE}, + {"\\?\UNC\a\b", TRUE, TRUE, S_FALSE, "\\?\UNC\a\b", TRUE, "\\?\UNC\a", TRUE, TRUE}, + {"\\?\UNC\a\b\c", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\UNC\a\b", TRUE, FALSE}, + {"\\?\UNC\a\b\c", FALSE, TRUE, S_OK, "\\?\UNC\a\b", TRUE, "\\?\UNC\a\b", TRUE, TRUE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", FALSE, FALSE, S_FALSE, "", TRUE, "\\?", TRUE, FALSE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", FALSE, TRUE, S_FALSE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", FALSE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", TRUE, TRUE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", TRUE, FALSE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, TRUE, S_FALSE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", FALSE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, TRUE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\\", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", TRUE, FALSE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\\", FALSE, TRUE, S_OK, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, TRUE}, + {"\\?\Volume{zaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?\Volume{zaaaaaaa-bbbb-cccc-dddd-ffffffffffff}"}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\a", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}", TRUE, FALSE}, + {"\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\a", FALSE, TRUE, S_OK, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-ffffffffffff}\", TRUE, TRUE}, + {"\\?\TST\", FALSE, FALSE, E_INVALIDARG, "", TRUE, "\\?\TST"}, + {"\\?\UNC\Q:\", FALSE, FALSE, S_FALSE, "", TRUE, "\\?\UNC\Q:", TRUE, FALSE}, + {"\\?\UNC\Q:\", FALSE, TRUE, S_OK, "\\?\UNC\Q:", TRUE, "\\?\UNC\Q:", TRUE, TRUE}, + {"\Device\Harddiskvolume1", FALSE, TRUE, S_OK, "\", TRUE, "\Device"}, + {"\Device\Harddiskvolume1\", FALSE, TRUE, S_OK, "\", TRUE, "\Device\Harddiskvolume1"}, + }; + + WCHAR pathW[MAX_PATH], expectedW[MAX_PATH]; + unsigned int i, expected_len; + const WCHAR *end_pfx, *ptr; + HRESULT hr, expected_hr; + char path[MAX_PATH]; + BOOL ret; + + if (!pPathCchStripToRoot || !pPathCchSkipRoot || !pPathCchRemoveFileSpec || !pPathCchIsRoot) + { + win_skip("Functions are not available.\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("%u (%s)", i, debugstr_a(tests[i].path)); + + strcpy(path, tests[i].path); + + if (!tests[i].aw_differ || !tests[i].w_version) + { + ret = pPathIsRootA(path); + ok(ret == tests[i].is_root, "PathIsRootA got %d, expected %d.\n", ret, tests[i].is_root); + ret = pPathStripToRootA(path); + ok(ret == tests[i].stript_to_root_ret, "PathStripToRootA got %d, expected %d.\n", ret, tests[i].stript_to_root_ret); + ok(!strcmp(path, tests[i].strip_to_root), "PathStripToRootA got %s, expected %s.\n", debugstr_a(path), debugstr_a(tests[i].strip_to_root)); + strcpy(path, tests[i].path); + ret = pPathRemoveFileSpecA(path); + ok(ret == tests[i].remove_file_ret, "PathRemoveFileSpecA got %d, expected %d.\n", ret, tests[i].remove_file_ret); + ok(!strcmp(path, tests[i].remove_file), "PathRemoveFileSpecA got %s, expected %s.\n", debugstr_a(path), debugstr_a(tests[i].remove_file)); + } + + if (!tests[i].aw_differ || tests[i].w_version) + { + MultiByteToWideChar(CP_ACP, 0, tests[i].path, -1, pathW, MAX_PATH); + ret = pPathIsRootW(pathW); + ok(ret == tests[i].is_root, "PathIsRootW got %d, expected %d.\n", ret, tests[i].is_root); + ret = pPathCchIsRoot(pathW); + ok(ret == tests[i].is_root, "pPathCchIsRoot got %d, expected %d.\n", ret, tests[i].is_root); + + ret = pPathStripToRootW(pathW); + MultiByteToWideChar(CP_ACP, 0, tests[i].strip_to_root, -1, expectedW, MAX_PATH); + ok(ret == tests[i].stript_to_root_ret, "PathStripToRootW got %d, expected %d.\n", ret, tests[i].stript_to_root_ret); + ok(!wcscmp(pathW, expectedW), "PathStripToRootW got %s, expected %s.\n", debugstr_w(pathW), debugstr_w(expectedW)); + + MultiByteToWideChar(CP_ACP, 0, tests[i].path, -1, pathW, MAX_PATH); + hr = pPathCchStripToRoot(pathW, ARRAY_SIZE(pathW)); + ok(hr == tests[i].cch_strip_to_root_hr, "PathCchStripToRoot got hr %#lx, expected %#lx.\n", hr, tests[i].cch_strip_to_root_hr); + ok(!wcscmp(pathW, expectedW), "PathCchStripToRoot got %s, expected %s.\n", debugstr_w(pathW), debugstr_w(expectedW)); + + MultiByteToWideChar(CP_ACP, 0, tests[i].path, -1, pathW, MAX_PATH); + end_pfx = NULL; + hr = pPathCchSkipRoot(pathW, &end_pfx); + expected_hr = SUCCEEDED(tests[i].cch_strip_to_root_hr) ? S_OK : tests[i].cch_strip_to_root_hr; + ok(hr == expected_hr, "PathCchSkipRoot got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + { + expected_len = wcslen(expectedW); + if (end_pfx && end_pfx > pathW/* && end_pfx[-1] == '\'*/ && pathW[expected_len] == '\' + && (!wcsnicmp(pathW, L"\\?\UNC\", 8 ) || (pathW[0] == '\' && pathW[1] == '\' && pathW[2] && pathW[2] != '?'))) + { + ok(end_pfx[-1] == '\', "PathCchSkipRoot missing trailing backslash (end_pfx %s).\n", debugstr_w(end_pfx)); + end_pfx--; + } + ok(expected_len == end_pfx - pathW, "PathCchSkipRoot got %s, expected %s.\n", + debugstr_wn(pathW, end_pfx - pathW), debugstr_wn(pathW, expected_len)); + } + MultiByteToWideChar(CP_ACP, 0, tests[i].path, -1, pathW, MAX_PATH); + if (FAILED(hr)) + expected_hr = *pathW ? S_OK : S_FALSE; + else + expected_hr = *end_pfx ? S_OK : S_FALSE; + wcscpy(expectedW, pathW); + if (expected_hr != S_FALSE) + { + if (!end_pfx) + end_pfx = pathW; + if ((ptr = wcsrchr(end_pfx, '\'))) + { + if (ptr > end_pfx && ptr[-1] == '\' && ptr[1] != '?') + --ptr; + expectedW[ptr - pathW] = 0; + } + else + { + expectedW[end_pfx - pathW] = 0; + } + } + hr = pPathCchRemoveFileSpec(pathW, ARRAY_SIZE(pathW)); + ok(hr == expected_hr, "PathCchRemoveFileSpec got hr %#lx, expected %#lx.\n", hr, expected_hr); + ok(!wcscmp(pathW, expectedW), "PathCchRemoveFileSpec got %s, expected %s, end_pfx %s.\n", debugstr_w(pathW), debugstr_w(expectedW), debugstr_w(end_pfx)); + + MultiByteToWideChar(CP_ACP, 0, tests[i].path, -1, pathW, MAX_PATH); + ret = pPathRemoveFileSpecW(pathW); + MultiByteToWideChar(CP_ACP, 0, tests[i].remove_file, -1, expectedW, MAX_PATH); + ok(ret == tests[i].remove_file_ret, "PathRemoveFileSpecW got %d, expected %d.\n", ret, tests[i].remove_file_ret); + ok(!wcscmp(pathW, expectedW), "PathRemoveFileSpecW got %s, expected %s.\n", debugstr_w(pathW), debugstr_w(expectedW)); + } + + winetest_pop_context(); + } +} + static void test_actctx(void) { ACTCTX_SECTION_KEYED_DATA data = { sizeof(data) }; @@ -2399,6 +2628,13 @@ START_TEST(path) pPathCchStripToRoot = (void *)GetProcAddress(hmod, "PathCchStripToRoot"); pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
+ pPathIsRootA = (void *)GetProcAddress(hmod, "PathIsRootA"); + pPathIsRootW = (void *)GetProcAddress(hmod, "PathIsRootW"); + pPathStripToRootA = (void *)GetProcAddress(hmod, "PathStripToRootA"); + pPathStripToRootW = (void *)GetProcAddress(hmod, "PathStripToRootW"); + pPathRemoveFileSpecA = (void *)GetProcAddress(hmod, "PathRemoveFileSpecA"); + pPathRemoveFileSpecW = (void *)GetProcAddress(hmod, "PathRemoveFileSpecW"); + test_PathAllocCanonicalize(); test_PathAllocCombine(); test_PathCchAddBackslash(); @@ -2421,5 +2657,6 @@ START_TEST(path) test_PathCchStripPrefix(); test_PathCchStripToRoot(); test_PathIsUNCEx(); + test_path_manipulation(); test_actctx(); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=147855
Your paranoid android.
=== debian11b (64 bit WoW report) ===
ddraw: ddraw2.c:3814: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768). ddraw2.c:3839: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768). ddraw4.c:3969: Test failed: Expected message 0x5, but didn't receive it.
Came across that looking at Hunt Showdown 1896 doing PathStripToRoot() on a paths like "D:/a/b" (forward slashes) and expecting that to return TRUE and path "D:". The game doesn't clearly break now when it behaves differently on Wine but also works a bit differently with files with unknown consequences.
Windows implementation doesn't actually handles forward slashes but due to different specifics ends up with "D:" (which probably makes sense if / is not treated as a special characters, a path like that would represent a relative path on drive D: and D: is a correct root which we currently don't return).
Fixing just that without looking at functions around and checking against newer Cch functions looks not fully sensible to me. The newer PathCchIsRoot, PathCchStripToRoot behave similarly to the older ones (starting from Vista), while PathRemoveFileSpec results look quite different from PathCchRemoveFileSpec so I considered it should be implemented separately.
I hope that since the most of the functional changes (except for PathRemoveFileSpec reimplementation) look as simplification it might be also justify these changes.