[PATCH 3/5] kernelbase: Implement PathCchSkipRoot.

Zhiyi Zhang zzhang at codeweavers.com
Thu Nov 22 00:46:39 CST 2018


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 .../api-ms-win-core-path-l1-1-0.spec          |   2 +-
 dlls/kernelbase/kernelbase.spec               |   2 +-
 dlls/kernelbase/path.c                        | 110 +++++++++++++++
 dlls/kernelbase/tests/path.c                  | 128 ++++++++++++++++++
 include/pathcch.h                             |   1 +
 5 files changed, 241 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 53f7b987ca..e07fe02a51 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
@@ -16,7 +16,7 @@
 @ stdcall PathCchRemoveExtension(wstr long) kernelbase.PathCchRemoveExtension
 @ stub PathCchRemoveFileSpec
 @ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension
-@ stub PathCchSkipRoot
+@ stdcall PathCchSkipRoot(wstr ptr) kernelbase.PathCchSkipRoot
 @ stdcall PathCchStripPrefix(wstr long) kernelbase.PathCchStripPrefix
 @ stub PathCchStripToRoot
 @ stdcall PathIsUNCEx(wstr ptr) kernelbase.PathIsUNCEx
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 8f4136eb6b..eae67be147 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1045,7 +1045,7 @@
 @ stdcall PathCchRemoveExtension(wstr long)
 # @ stub PathCchRemoveFileSpec
 @ stdcall PathCchRenameExtension(wstr long wstr)
-# @ stub PathCchSkipRoot
+@ stdcall PathCchSkipRoot(wstr ptr)
 @ stdcall PathCchStripPrefix(wstr long)
 # @ stub PathCchStripToRoot
 @ stdcall PathCombineA(ptr str str) shlwapi.PathCombineA
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index 366efa41e1..168edd0610 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -50,6 +50,84 @@ static BOOL is_prefixed_disk(const WCHAR *string)
     return !memcmp(string, prefix, sizeof(prefix)) && isalphaW(string[4]) && string[5] == ':';
 }
 
+static BOOL is_prefixed_volume(const WCHAR *string)
+{
+    static const WCHAR prefixed_volume[] = {'\\', '\\', '?', '\\', 'V', 'o', 'l', 'u', 'm', 'e'};
+    const WCHAR *guid;
+    INT ret;
+    INT i = 0;
+
+    ret = memicmpW(string, prefixed_volume, ARRAY_SIZE(prefixed_volume));
+    if (ret) return FALSE;
+
+    guid = string + ARRAY_SIZE(prefixed_volume);
+
+    while (i <= 37)
+    {
+        switch (i)
+        {
+        case 0:
+            if (guid[i] != '{') return FALSE;
+            break;
+        case 9:
+        case 14:
+        case 19:
+        case 24:
+            if (guid[i] != '-') return FALSE;
+            break;
+        case 37:
+            if (guid[i] != '}') return FALSE;
+            break;
+        default:
+            if (!isalnumW(guid[i])) return FALSE;
+            break;
+        }
+        i++;
+    }
+
+    return TRUE;
+}
+
+/* Get the next character beyond end of the segment.
+   Return TRUE if the last segment ends with a backslash */
+static BOOL get_next_segment(const WCHAR *next, const WCHAR **next_segment)
+{
+    while (*next && *next != '\\') next++;
+    if (*next == '\\')
+    {
+        *next_segment = next + 1;
+        return TRUE;
+    }
+    else
+    {
+        *next_segment = next;
+        return FALSE;
+    }
+}
+
+/* Find the last character of the root in a path, if there is one, without any segments */
+static const WCHAR *get_root_end(const WCHAR *path)
+{
+    /* Find path root */
+    if (is_prefixed_volume(path))
+        return path[48] == '\\' ? path + 48 : path + 47;
+    else if (is_prefixed_unc(path))
+        return path + 7;
+    else if (is_prefixed_disk(path))
+        return path[6] == '\\' ? path + 6 : path + 5;
+    /* \\ */
+    else if (path[0] == '\\' && path[1] == '\\')
+        return path + 1;
+    /* \ */
+    else if (path[0] == '\\')
+        return path;
+    /* X:\ */
+    else if (isalphaW(path[0]) && path[1] == ':')
+        return path[2] == '\\' ? path + 2 : path + 1;
+    else
+        return NULL;
+}
+
 HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size)
 {
     return PathCchAddBackslashEx(path, size, NULL, NULL);
@@ -196,6 +274,38 @@ HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *ext
     return FAILED(hr) ? hr : S_OK;
 }
 
+HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end)
+{
+    static const WCHAR unc_prefix[] = {'\\', '\\', '?'};
+
+    TRACE("%s %p\n", debugstr_w(path), root_end);
+
+    if (!path || !path[0] || !root_end
+        || (!memicmpW(unc_prefix, path, ARRAY_SIZE(unc_prefix)) && !is_prefixed_volume(path) && !is_prefixed_unc(path)
+            && !is_prefixed_disk(path)))
+        return E_INVALIDARG;
+
+    *root_end = get_root_end(path);
+    if (*root_end)
+    {
+        (*root_end)++;
+        if (is_prefixed_unc(path))
+        {
+            get_next_segment(*root_end, root_end);
+            get_next_segment(*root_end, root_end);
+        }
+        else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?')
+        {
+            /* Skip share server */
+            get_next_segment(*root_end, root_end);
+            /* If mount point is empty, don't skip over mount point */
+            if (**root_end != '\\') get_next_segment(*root_end, root_end);
+        }
+    }
+
+    return *root_end ? S_OK : E_INVALIDARG;
+}
+
 HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size)
 {
     TRACE("%s %lu\n", wine_dbgstr_w(path), size);
diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c
index af15e83d60..f54e77be46 100644
--- a/dlls/kernelbase/tests/path.c
+++ b/dlls/kernelbase/tests/path.c
@@ -37,6 +37,7 @@ HRESULT (WINAPI *pPathCchCombineEx)(WCHAR *out, SIZE_T size, const WCHAR *path1,
 HRESULT (WINAPI *pPathCchFindExtension)(const WCHAR *path, SIZE_T size, const WCHAR **extension);
 HRESULT (WINAPI *pPathCchRemoveExtension)(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);
 BOOL    (WINAPI *pPathIsUNCEx)(const WCHAR *path, const WCHAR **server);
 
@@ -634,6 +635,131 @@ static void test_PathCchRenameExtension(void)
     }
 }
 
+struct skiproot_test
+{
+    const char *path;
+    int root_offset;
+    HRESULT hr;
+};
+
+static const struct skiproot_test skiproot_tests [] =
+{
+    /* Basic combination */
+    {"", 0, E_INVALIDARG},
+    {"C:\\", 3, S_OK},
+    {"\\", 1, S_OK},
+    {"\\\\.\\", 4, S_OK},
+    {"\\\\?\\UNC\\", 8, S_OK},
+    {"\\\\?\\C:\\", 7, S_OK},
+
+    /* Basic + \ */
+    {"C:\\\\", 3, S_OK},
+    {"\\\\", 2, S_OK},
+    {"\\\\.\\\\", 4, S_OK},
+    {"\\\\?\\UNC\\\\", 9, S_OK},
+    {"\\\\?\\C:\\\\", 7, S_OK},
+
+    /* Basic + a */
+    {"a", 0, E_INVALIDARG},
+    {"C:\\a", 3, S_OK},
+    {"\\a", 1, S_OK},
+    {"\\\\.\\a", 5, S_OK},
+    {"\\\\?\\UNC\\a", 9, S_OK},
+
+    /* Basic + \a */
+    {"\\a", 1, S_OK},
+    {"C:\\\\a", 3, S_OK},
+    {"\\\\a", 3, S_OK},
+    {"\\\\.\\\\a", 4, S_OK},
+    {"\\\\?\\UNC\\\\a", 10, S_OK},
+    {"\\\\?\\C:\\\\a", 7, S_OK},
+
+    /* Basic + a\ */
+    {"a\\", 0, E_INVALIDARG},
+    {"C:\\a\\", 3, S_OK},
+    {"\\a\\", 1, S_OK},
+    {"\\\\.\\a\\", 6, S_OK},
+    {"\\\\?\\UNC\\a\\", 10, S_OK},
+    {"\\\\?\\C:\\a\\", 7, S_OK},
+
+    /* Network share */
+    {"\\\\\\\\", 3, S_OK},
+    {"\\\\a\\", 4, S_OK},
+    {"\\\\a\\b", 5, S_OK},
+    {"\\\\a\\b\\", 6, S_OK},
+    {"\\\\a\\b\\\\", 6, S_OK},
+    {"\\\\a\\b\\\\c", 6, S_OK},
+    {"\\\\a\\b\\c", 6, S_OK},
+    {"\\\\a\\b\\c\\", 6, S_OK},
+    {"\\\\a\\b\\c\\d", 6, S_OK},
+    {"\\\\a\\\\b\\c\\", 4, S_OK},
+    {"\\\\aa\\bb\\cc\\", 8, S_OK},
+
+    /* UNC */
+    {"\\\\?\\UNC\\\\", 9, S_OK},
+    {"\\\\?\\UNC\\a\\b", 11, S_OK},
+    {"\\\\?\\UNC\\a\\b", 11, S_OK},
+    {"\\\\?\\UNC\\a\\b\\", 12, S_OK},
+    {"\\\\?\\UNC\\a\\b\\c", 12, S_OK},
+    {"\\\\?\\UNC\\a\\b\\c\\", 12, S_OK},
+    {"\\\\?\\UNC\\a\\b\\c\\d", 12, S_OK},
+    {"\\\\?\\C:", 6, S_OK},
+    {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", 48, S_OK},
+    {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 49, S_OK},
+    {"\\\\?\\unc\\a\\b", 11, S_OK},
+    {"\\\\?\\volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 49, S_OK},
+    {"\\\\?\\volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", 49, S_OK},
+
+    /* Malformed */
+    {"C:", 2, S_OK},
+    {":", 0, E_INVALIDARG},
+    {":\\", 0, E_INVALIDARG},
+    {"C\\", 0, E_INVALIDARG},
+    {"\\?", 1, S_OK},
+    {"\\?\\UNC", 1, S_OK},
+    {"\\\\?\\", 0, E_INVALIDARG},
+    {"\\\\?\\UNC", 0, E_INVALIDARG},
+    {"\\\\?\\::\\", 0, E_INVALIDARG},
+    {"\\\\?\\Volume", 0, E_INVALIDARG},
+    {"\\.", 1, S_OK},
+    {"\\\\..", 4, S_OK},
+    {"\\\\..a", 5, S_OK}
+};
+
+static void test_PathCchSkipRoot(void)
+{
+    WCHAR pathW[MAX_PATH];
+    const WCHAR *root_end;
+    HRESULT hr;
+    INT i;
+
+    if (!pPathCchSkipRoot)
+    {
+        win_skip("PathCchSkipRoot() is not available.\n");
+        return;
+    }
+
+    root_end = (const WCHAR *)0xdeadbeef;
+    hr = pPathCchSkipRoot(NULL, &root_end);
+    ok(hr == E_INVALIDARG, "Expect result %#x, got %#x\n", E_INVALIDARG, hr);
+    ok(root_end == (const WCHAR *)0xdeadbeef, "Expect root_end 0xdeadbeef, got %p\n", root_end);
+
+    MultiByteToWideChar(CP_ACP, 0, "C:\\", -1, pathW, ARRAY_SIZE(pathW));
+    hr = pPathCchSkipRoot(pathW, NULL);
+    ok(hr == E_INVALIDARG, "Expect result %#x, got %#x\n", E_INVALIDARG, hr);
+
+    for (i = 0; i < ARRAY_SIZE(skiproot_tests); i++)
+    {
+        const struct skiproot_test *t = skiproot_tests + i;
+        MultiByteToWideChar(CP_ACP, 0, t->path, -1, pathW, ARRAY_SIZE(pathW));
+        hr = pPathCchSkipRoot(pathW, &root_end);
+        ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path, t->hr, hr);
+        if (SUCCEEDED(hr))
+            ok(root_end - pathW == t->root_offset, "path %s expect root offset %d, got %d\n", t->path, t->root_offset,
+               root_end - pathW);
+    }
+}
+
 struct stripprefix_test
 {
     const CHAR *path;
@@ -789,6 +915,7 @@ START_TEST(path)
     pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension");
     pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension");
     pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension");
+    pPathCchSkipRoot = (void *)GetProcAddress(hmod, "PathCchSkipRoot");
     pPathCchStripPrefix = (void *)GetProcAddress(hmod, "PathCchStripPrefix");
     pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
 
@@ -799,6 +926,7 @@ START_TEST(path)
     test_PathCchFindExtension();
     test_PathCchRemoveExtension();
     test_PathCchRenameExtension();
+    test_PathCchSkipRoot();
     test_PathCchStripPrefix();
     test_PathIsUNCEx();
 }
diff --git a/include/pathcch.h b/include/pathcch.h
index 1ff9e24648..42bae4edd0 100644
--- a/include/pathcch.h
+++ b/include/pathcch.h
@@ -32,5 +32,6 @@ HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, con
 HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension);
 HRESULT WINAPI PathCchRemoveExtension(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);
 BOOL    WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server);
-- 
2.19.1





More information about the wine-devel mailing list