kernelbase: Implement PathCchSkipRoot.
Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
2ec414c8a1
commit
03d19f3e25
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue