kernelbase: Implement PathCchSkipRoot.

Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2018-11-22 14:46:39 +08:00 committed by Alexandre Julliard
parent 2ec414c8a1
commit 03d19f3e25
5 changed files with 239 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -50,6 +50,82 @@ static BOOL is_prefixed_disk(const WCHAR *string)
return !strncmpW(string, prefix, ARRAY_SIZE(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 i = 0;
if (strncmpiW(string, prefixed_volume, ARRAY_SIZE(prefixed_volume))) 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 +272,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);

View File

@ -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 %ld\n", t->path, t->root_offset,
(INT_PTR)(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();
}

View File

@ -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);