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
|
@ stdcall PathCchRemoveExtension(wstr long) kernelbase.PathCchRemoveExtension
|
||||||
@ stub PathCchRemoveFileSpec
|
@ stub PathCchRemoveFileSpec
|
||||||
@ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension
|
@ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension
|
||||||
@ stub PathCchSkipRoot
|
@ stdcall PathCchSkipRoot(wstr ptr) kernelbase.PathCchSkipRoot
|
||||||
@ stdcall PathCchStripPrefix(wstr long) kernelbase.PathCchStripPrefix
|
@ stdcall PathCchStripPrefix(wstr long) kernelbase.PathCchStripPrefix
|
||||||
@ stub PathCchStripToRoot
|
@ stub PathCchStripToRoot
|
||||||
@ stdcall PathIsUNCEx(wstr ptr) kernelbase.PathIsUNCEx
|
@ stdcall PathIsUNCEx(wstr ptr) kernelbase.PathIsUNCEx
|
||||||
|
|
|
@ -1045,7 +1045,7 @@
|
||||||
@ stdcall PathCchRemoveExtension(wstr long)
|
@ stdcall PathCchRemoveExtension(wstr long)
|
||||||
# @ stub PathCchRemoveFileSpec
|
# @ stub PathCchRemoveFileSpec
|
||||||
@ stdcall PathCchRenameExtension(wstr long wstr)
|
@ stdcall PathCchRenameExtension(wstr long wstr)
|
||||||
# @ stub PathCchSkipRoot
|
@ stdcall PathCchSkipRoot(wstr ptr)
|
||||||
@ stdcall PathCchStripPrefix(wstr long)
|
@ stdcall PathCchStripPrefix(wstr long)
|
||||||
# @ stub PathCchStripToRoot
|
# @ stub PathCchStripToRoot
|
||||||
@ stdcall PathCombineA(ptr str str) shlwapi.PathCombineA
|
@ 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] == ':';
|
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)
|
HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size)
|
||||||
{
|
{
|
||||||
return PathCchAddBackslashEx(path, size, NULL, NULL);
|
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;
|
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)
|
HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size)
|
||||||
{
|
{
|
||||||
TRACE("%s %lu\n", wine_dbgstr_w(path), 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 *pPathCchFindExtension)(const WCHAR *path, SIZE_T size, const WCHAR **extension);
|
||||||
HRESULT (WINAPI *pPathCchRemoveExtension)(WCHAR *path, SIZE_T size);
|
HRESULT (WINAPI *pPathCchRemoveExtension)(WCHAR *path, SIZE_T size);
|
||||||
HRESULT (WINAPI *pPathCchRenameExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension);
|
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);
|
HRESULT (WINAPI *pPathCchStripPrefix)(WCHAR *path, SIZE_T size);
|
||||||
BOOL (WINAPI *pPathIsUNCEx)(const WCHAR *path, const WCHAR **server);
|
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
|
struct stripprefix_test
|
||||||
{
|
{
|
||||||
const CHAR *path;
|
const CHAR *path;
|
||||||
|
@ -789,6 +915,7 @@ START_TEST(path)
|
||||||
pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension");
|
pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension");
|
||||||
pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension");
|
pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension");
|
||||||
pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension");
|
pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension");
|
||||||
|
pPathCchSkipRoot = (void *)GetProcAddress(hmod, "PathCchSkipRoot");
|
||||||
pPathCchStripPrefix = (void *)GetProcAddress(hmod, "PathCchStripPrefix");
|
pPathCchStripPrefix = (void *)GetProcAddress(hmod, "PathCchStripPrefix");
|
||||||
pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
|
pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
|
||||||
|
|
||||||
|
@ -799,6 +926,7 @@ START_TEST(path)
|
||||||
test_PathCchFindExtension();
|
test_PathCchFindExtension();
|
||||||
test_PathCchRemoveExtension();
|
test_PathCchRemoveExtension();
|
||||||
test_PathCchRenameExtension();
|
test_PathCchRenameExtension();
|
||||||
|
test_PathCchSkipRoot();
|
||||||
test_PathCchStripPrefix();
|
test_PathCchStripPrefix();
|
||||||
test_PathIsUNCEx();
|
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 PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension);
|
||||||
HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size);
|
HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size);
|
||||||
HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension);
|
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);
|
HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size);
|
||||||
BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server);
|
BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server);
|
||||||
|
|
Loading…
Reference in New Issue