Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
.../api-ms-win-core-path-l1-1-0.spec | 2 +-
dlls/kernelbase/kernelbase.spec | 2 +-
dlls/kernelbase/path.c | 182 +++++++++++
dlls/kernelbase/tests/path.c | 306 ++++++++++++++++++
include/pathcch.h | 2 +
5 files changed, 492 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 725f16448f..9bce0ace95 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
@@ -1,4 +1,4 @@
-@ stub PathAllocCanonicalize
+@ stdcall PathAllocCanonicalize(wstr long ptr) kernelbase.PathAllocCanonicalize
@ stub PathAllocCombine
@ stdcall PathCchAddBackslash(wstr long) kernelbase.PathCchAddBackslash
@ stdcall PathCchAddBackslashEx(wstr long ptr ptr) kernelbase.PathCchAddBackslashEx
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index e7bb62d75c..8273d5f1b1 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1023,7 +1023,7 @@
@ stdcall PathAddBackslashW(wstr) shlwapi.PathAddBackslashW
@ stdcall PathAddExtensionA(str str) shlwapi.PathAddExtensionA
@ stdcall PathAddExtensionW(wstr wstr) shlwapi.PathAddExtensionW
-# @ stub PathAllocCanonicalize
+@ stdcall PathAllocCanonicalize(wstr long ptr)
# @ stub PathAllocCombine
@ stdcall PathAppendA(str str) shlwapi.PathAppendA
@ stdcall PathAppendW(wstr wstr) shlwapi.PathAppendW
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index c3fca8b9cf..659a17bd09 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -126,6 +126,188 @@ static const WCHAR *get_root_end(const WCHAR *path)
return NULL;
}
+HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out)
+{
+ WCHAR *buffer, *dst;
+ const WCHAR *src;
+ const WCHAR *root_end;
+ SIZE_T buffer_size, length;
+
+ TRACE("%s %#x %p\n", debugstr_w(path_in), flags, path_out);
+
+ if (!path_in || !path_out
+ || ((flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) && (flags & PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS))
+ || (flags & (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS)
+ && !(flags & PATHCCH_ALLOW_LONG_PATHS))
+ || ((flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) && (flags & PATHCCH_ALLOW_LONG_PATHS)))
+ {
+ if (path_out) *path_out = NULL;
+ return E_INVALIDARG;
+ }
+
+ length = strlenW(path_in);
+ if ((length + 1 > MAX_PATH && !(flags & (PATHCCH_ALLOW_LONG_PATHS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH)))
+ || (length + 1 > PATHCCH_MAX_CCH))
+ {
+ *path_out = NULL;
+ return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
+ }
+
+ /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */
+ if (flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) flags |= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS;
+
+ /* path length + possible \\?\ addition + possible \ addition + NUL */
+ buffer_size = (length + 6) * sizeof(WCHAR);
+ buffer = LocalAlloc(LMEM_ZEROINIT, buffer_size);
+ if (!buffer)
+ {
+ *path_out = NULL;
+ return E_OUTOFMEMORY;
+ }
+
+ src = path_in;
+ dst = buffer;
+
+ root_end = get_root_end(path_in);
+ if (root_end) root_end = buffer + (root_end - path_in);
+
+ /* Copy path root */
+ if (root_end)
+ {
+ memcpy(dst, src, (root_end - buffer + 1) * sizeof(WCHAR));
+ src += root_end - buffer + 1;
+ if(PathCchStripPrefix(dst, length + 6) == S_OK)
+ {
+ /* Fill in \ in X:\ if the \ is missing */
+ if(isalphaW(dst[0]) && dst[1] == ':' && dst[2]!= '\\')
+ {
+ dst[2] = '\\';
+ dst[3] = 0;
+ }
+ dst = buffer + strlenW(buffer);
+ root_end = dst;
+ }
+ else
+ dst += root_end - buffer + 1;
+ }
+
+ while (*src)
+ {
+ if (src[0] == '.')
+ {
+ if (src[1] == '.')
+ {
+ /* Keep one . after * */
+ if (dst > buffer && dst[-1] == '*')
+ {
+ *dst++ = *src++;
+ continue;
+ }
+
+ /* Keep the . if one of the following is true:
+ * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
+ * 2. in form of a..b
+ */
+ if (dst > buffer
+ && (((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS) && dst[-1] != '\\')
+ || (dst[-1] != '\\' && src[2] != '\\' && src[2])))
+ {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ continue;
+ }
+
+ /* Remove the \ before .. if the \ is not part of root */
+ if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end))
+ {
+ *--dst = '\0';
+ /* Remove characters until a \ is encountered */
+ while (dst > buffer)
+ {
+ if (dst[-1] == '\\')
+ {
+ *--dst = 0;
+ break;
+ }
+ else
+ *--dst = 0;
+ }
+ }
+ /* Remove the extra \ after .. if the \ before .. wasn't deleted */
+ else if (src[2] == '\\')
+ src++;
+
+ src += 2;
+ }
+ else
+ {
+ /* Keep the . if one of the following is true:
+ * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
+ * 2. in form of a.b, which is used in domain names
+ * 3. *.
+ */
+ if (dst > buffer
+ && ((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS && dst[-1] != '\\')
+ || (dst[-1] != '\\' && src[1] != '\\' && src[1]) || (dst[-1] == '*')))
+ {
+ *dst++ = *src++;
+ continue;
+ }
+
+ /* Remove the \ before . if the \ is not part of root */
+ if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) dst--;
+ /* Remove the extra \ after . if the \ before . wasn't deleted */
+ else if (src[1] == '\\')
+ src++;
+
+ src++;
+ }
+
+ /* If X:\ is not complete, then complete it */
+ if (isalphaW(buffer[0]) && buffer[1] == ':' && buffer[2] != '\\')
+ {
+ root_end = buffer + 2;
+ dst = buffer + 3;
+ buffer[2] = '\\';
+ /* If next character is \, use the \ to fill in */
+ if (src[0] == '\\') src++;
+ }
+ }
+ /* Copy over */
+ else
+ *dst++ = *src++;
+ }
+ /* End the path */
+ *dst = 0;
+
+ /* If result path is empty, fill in \ */
+ if (!*buffer)
+ {
+ buffer[0] = '\\';
+ buffer[1] = 0;
+ }
+
+ /* Extend the path if needed */
+ length = strlenW(buffer);
+ if (((length + 1 > MAX_PATH && isalphaW(buffer[0]) && buffer[1] == ':')
+ || (isalphaW(buffer[0]) && buffer[1] == ':' && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))
+ && !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS))
+ {
+ memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR));
+ buffer[0] = '\\';
+ buffer[1] = '\\';
+ buffer[2] = '?';
+ buffer[3] = '\\';
+ }
+
+ /* Add a trailing backslash to the path if needed */
+ if (flags & PATHCCH_ENSURE_TRAILING_SLASH)
+ PathCchAddBackslash(buffer, buffer_size);
+
+ *path_out = buffer;
+ return S_OK;
+}
+
HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size)
{
return PathCchAddBackslashEx(path, size, NULL, NULL);
diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c
index 8981b3a08a..0a55369d99 100644
--- a/dlls/kernelbase/tests/path.c
+++ b/dlls/kernelbase/tests/path.c
@@ -30,6 +30,7 @@
#include "wine/test.h"
+HRESULT (WINAPI *pPathAllocCanonicalize)(const WCHAR *path_in, DWORD flags, WCHAR **path_out);
HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size);
HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining);
HRESULT (WINAPI *pPathCchAddExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension);
@@ -46,6 +47,309 @@ 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);
+struct alloccanonicalize_test
+{
+ const CHAR *path_in;
+ const CHAR *path_out;
+ DWORD flags;
+ HRESULT hr;
+};
+
+static const struct alloccanonicalize_test alloccanonicalize_tests[] =
+{
+ /* Malformed path */
+ {"C:a", "C:a", 0, S_OK},
+ {"\\\\?\\C:", "C:\\", 0, S_OK},
+ {"\\\\?C:\\a", "\\\\?C:\\a", 0, S_OK},
+ {"\\\\?UNC\\a", "\\\\?UNC\\a", 0, S_OK},
+ {"\\\\?\\UNCa", "\\\\?\\UNCa", 0, S_OK},
+ {"\\\\?C:a", "\\\\?C:a", 0, S_OK},
+
+ /* No . */
+ {"", "\\", 0, S_OK},
+ {"C:", "C:", 0, S_OK},
+ {"C:\\", "C:\\", 0, S_OK},
+ {"\\\\?\\C:\\a", "C:\\a", 0, S_OK},
+ {"\\\\?\\UNC\\a", "\\\\a", 0, S_OK},
+
+ /* . */
+ {".", "\\", 0, S_OK},
+ {"..", "\\", 0, S_OK},
+ {"...", "\\", 0, S_OK},
+ {"*.", "*.", 0, S_OK},
+ {"*..", "*.", 0, S_OK},
+ {"*...", "*.", 0, S_OK},
+ {"a.", "a", 0, S_OK},
+ {"a.b", "a.b", 0, S_OK},
+ {"a\\.", "a", 0, S_OK},
+ {"a\\.\\b", "a\\b", 0, S_OK},
+ {"C:.", "C:\\", 0, S_OK},
+ {"C:\\.", "C:\\", 0, S_OK},
+ {"C:\\.\\", "C:\\", 0, S_OK},
+ {"C:\\a.", "C:\\a", 0, S_OK},
+ {"C:\\a\\.", "C:\\a", 0, S_OK},
+ {"C:\\a\\\\.", "C:\\a\\", 0, S_OK},
+ {"C:\\a\\\\\\.", "C:\\a\\\\", 0, S_OK},
+ {"\\.", "\\", 0, S_OK},
+ {"\\\\.", "\\\\", 0, S_OK},
+ {"\\\\.\\", "\\\\", 0, S_OK},
+ {"\\\\\\.", "\\\\", 0, S_OK},
+ {"\\\\.\\\\", "\\\\\\", 0, S_OK},
+ {"\\\\\\\\.", "\\\\\\", 0, S_OK},
+ {"\\?\\.", "\\?", 0, S_OK},
+ {"\\\\?\\.", "\\\\?", 0, S_OK},
+ {"\\192.168.1.1\\a", "\\192.168.1.1\\a", 0, S_OK},
+ {"\\a.168.1.1\\a", "\\a.168.1.1\\a", 0, S_OK},
+ {"\\\\192.168.1.1\\a", "\\\\192.168.1.1\\a", 0, S_OK},
+ {"\\\\a.168.1.1\\b", "\\\\a.168.1.1\\b", 0, S_OK},
+ {"\\\\?\\C:.", "C:\\", 0, S_OK},
+ {"\\\\?\\C:\\.", "C:\\", 0, S_OK},
+ {"\\\\?\\UNC\\.", "\\\\", 0, S_OK},
+ {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\.",
+ "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 0, S_OK},
+
+ /* .. */
+ {"a..", "a", 0, S_OK},
+ {"a..b", "a..b", 0, S_OK},
+ {"a\\..", "\\", 0, S_OK},
+ {"a\\..\\", "\\", 0, S_OK},
+ {"a\\..\\b", "\\b", 0, S_OK},
+ {"C:..", "C:\\", 0, S_OK},
+ {"C:\\..", "C:\\", 0, S_OK},
+ {"C:\\\\..", "C:\\", 0, S_OK},
+ {"C:\\..\\", "C:\\", 0, S_OK},
+ {"C:\\a\\..", "C:\\", 0, S_OK},
+ {"C:\\a\\\\..", "C:\\a", 0, S_OK},
+ {"C:\\a\\\\\\..", "C:\\a\\", 0, S_OK},
+ {"C:\\a\\..\\b", "C:\\b", 0, S_OK},
+ {"C:\\a\\..\\\\b", "C:\\\\b", 0, S_OK},
+ {"\\..", "\\", 0, S_OK},
+ {"\\\\..", "\\\\", 0, S_OK},
+ {"\\\\\\..", "\\", 0, S_OK},
+ {"\\\\..\\", "\\\\", 0, S_OK},
+ {"\\\\\\..", "\\", 0, S_OK},
+ {"\\\\..\\\\", "\\\\\\", 0, S_OK},
+ {"\\\\\\\\..", "\\\\", 0, S_OK},
+ {"\\?\\..", "\\", 0, S_OK},
+ {"\\a\\..", "\\", 0, S_OK},
+ {"\\\\?\\..", "\\", 0, S_OK},
+ {"\\\\a\\..", "\\", 0, S_OK},
+ {"\\a\\..\\b", "\\b", 0, S_OK},
+ {"\\a\\b\\..", "\\a", 0, S_OK},
+ {"\\?\\UNC\\..", "\\?", 0, S_OK},
+ {"\\?\\C:\\..", "\\?", 0, S_OK},
+ {"\\\\?\\C:..", "C:\\", 0, S_OK},
+ {"\\\\?\\C:\\..", "C:\\", 0, S_OK},
+ {"\\\\?\\UNC\\..", "\\\\", 0, S_OK},
+ {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}..",
+ "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", 0, S_OK},
+ {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\..",
+ "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 0, S_OK},
+ {"\\\\?\\UNC\\a\\b\\..", "\\\\a", 0, S_OK},
+ {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a\\b\\..",
+ "\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", 0, S_OK},
+
+ /* . and .. */
+ {"C:\\a\\.\\b\\..\\", "C:\\a\\", 0, S_OK},
+ {"\\a\\.\\b\\..\\", "\\a\\", 0, S_OK},
+ {"\\?\\a\\.\\b\\..\\", "\\?\\a\\", 0, S_OK},
+ {"\\\\.\\a\\.\\b\\..\\", "\\\\a\\", 0, S_OK},
+ {"\\\\?\\a\\.\\b\\..\\", "\\\\?\\a\\", 0, S_OK},
+ {"\\\\.\\..", "\\\\", 0, S_OK},
+
+ /* PATHCCH_ALLOW_LONG_PATHS */
+ /* Input path with prefix \\?\ and length of MAXPATH + 1, HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) = 0x800700ce */
+ {"\\\\?\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", NULL, 0, 0x800700ce},
+ /* Input path with prefix C:\ and length of MAXPATH + 1 */
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", NULL, 0, 0x800700ce},
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\\\?\\C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+ /* Input path with prefix C: and length of MAXPATH + 1 */
+ {"C:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\\\?\\C:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+ /* Input path with prefix C:\ and length of MAXPATH + 1 and with .. */
+ {"C:\\..\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+ /* Input path with prefix \\?\ and length of MAXPATH + 1 */
+ {"\\\\?\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\\\?\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+ /* Input path with prefix \ and length of MAXPATH + 1 */
+ {"\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+ /* Input path with length of MAXPATH with PATHCCH_ALLOW_LONG_PATHS disabled*/
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, S_OK},
+ /* Input path with length of MAXPATH */
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK},
+
+ /* Flags added after Windows 10 1709 */
+ /* PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS */
+ /* PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS without PATHCCH_ALLOW_LONG_PATHS */
+ {"", NULL, PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, E_INVALIDARG},
+ /* Input path with prefix C:\ and length of MAXPATH + 1 and PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS */
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, S_OK},
+
+ /* PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */
+ /* PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS without PATHCCH_ALLOW_LONG_PATHS */
+ {"", NULL, PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, E_INVALIDARG},
+ /* Both PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */
+ {"", "\\", PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS,
+ E_INVALIDARG},
+ /* Input path with prefix C:\ and length of MAXPATH + 1 and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\\\?\\C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, S_OK},
+ /* Input path with prefix C:\ and length of MAXPATH and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */
+ {"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, S_OK},
+
+ /* PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */
+ /* No effect for spaces */
+ {"C:\\a \\", "C:\\a \\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a\\ ", "C:\\a\\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a ", "C:\\a ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a ", "C:\\a ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a. ", "C:\\a. ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"\\a \\", "\\a \\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"\\a\\ ", "\\a\\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"\\\\a \\", "\\\\a \\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"\\\\a\\ ", "\\\\a\\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"\\\\?\\ ", "\\\\?\\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ /* Keep trailing dot */
+ {"*..", "*..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {".", "\\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"..", "\\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:.", "C:.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:..", "C:..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a\\.", "C:\\a", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a\\..", "C:\\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a.", "C:\\a.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+ {"C:\\a..", "C:\\a..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},
+
+ /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH */
+ {"C:\\a\\", "\\\\?\\C:\\a\\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK},
+ {"", NULL, PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_ALLOW_LONG_PATHS, E_INVALIDARG},
+ {"\\a\\", "\\a\\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK},
+ {"\\\\?\\C:\\a\\", "\\\\?\\C:\\a\\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK},
+ /* Implication of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS by PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH */
+ {"\\a.", "\\a.", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK},
+
+ /* PATHCCH_ENSURE_TRAILING_SLASH */
+ {"\\", "\\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK},
+ {"C:\\", "C:\\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK},
+ {"C:\\a\\.", "C:\\a\\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK},
+ {"C:\\a", "C:\\a\\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK}
+};
+
+static void test_PathAllocCanonicalize(void)
+{
+ WCHAR path_inW[1024], path_maxW[PATHCCH_MAX_CCH + 1];
+ WCHAR *path_outW;
+ CHAR path_outA[1024];
+ BOOL skip_new_flags = TRUE;
+ HRESULT hr;
+ INT i;
+
+ if (!pPathAllocCanonicalize)
+ {
+ win_skip("PathAllocCanonicalize() is not available.\n");
+ return;
+ }
+
+ /* No NULL check for path on Windows */
+ if (0)
+ {
+ hr = pPathAllocCanonicalize(NULL, 0, &path_outW);
+ ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr);
+ }
+
+ MultiByteToWideChar(CP_ACP, 0, "C:\\", -1, path_inW, ARRAY_SIZE(path_inW));
+ hr = pPathAllocCanonicalize(path_inW, 0, NULL);
+ ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr);
+
+ /* Test longest path */
+ for (i = 0; i < ARRAY_SIZE(path_maxW) - 1; i++) path_maxW[i] = 'a';
+ path_maxW[PATHCCH_MAX_CCH] = '\0';
+ path_outW = (WCHAR *)0xdeadbeef;
+ hr = pPathAllocCanonicalize(path_maxW, PATHCCH_ALLOW_LONG_PATHS, &path_outW);
+ ok(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), "expect hr %#x, got %#x\n",
+ HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
+ ok(path_outW == NULL, "expect path_outW null, got %p\n", path_outW);
+
+ path_maxW[PATHCCH_MAX_CCH - 1] = '\0';
+ hr = pPathAllocCanonicalize(path_maxW, PATHCCH_ALLOW_LONG_PATHS, &path_outW);
+ ok(hr == S_OK, "expect hr %#x, got %#x\n", S_OK, hr);
+
+ /* Check if flags added after Windows 10 1709 are supported */
+ MultiByteToWideChar(CP_ACP, 0, "C:\\", -1, path_inW, ARRAY_SIZE(path_inW));
+ hr = pPathAllocCanonicalize(path_inW, PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, &path_outW);
+ if (hr == E_INVALIDARG) skip_new_flags = FALSE;
+
+ for (i = 0; i < ARRAY_SIZE(alloccanonicalize_tests); i++)
+ {
+ const struct alloccanonicalize_test *t = alloccanonicalize_tests + i;
+
+ if (((PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS
+ | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
+ | PATHCCH_ENSURE_TRAILING_SLASH)
+ & t->flags)
+ && skip_new_flags)
+ {
+ win_skip("Skip testing new flags added after Windows 10 1709\n");
+ return;
+ }
+
+ MultiByteToWideChar(CP_ACP, 0, t->path_in, -1, path_inW, ARRAY_SIZE(path_inW));
+ hr = pPathAllocCanonicalize(path_inW, t->flags, &path_outW);
+ ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path_in, t->hr, hr);
+ if (SUCCEEDED(hr))
+ {
+ WideCharToMultiByte(CP_ACP, 0, path_outW, -1, path_outA, ARRAY_SIZE(path_outA), NULL, NULL);
+ ok(!lstrcmpA(path_outA, t->path_out), "path \"%s\" expect output path \"%s\", got \"%s\"\n", t->path_in,
+ t->path_out, path_outA);
+ LocalFree(path_outW);
+ }
+ }
+}
+
static const struct
{
const char *path1;
@@ -1386,6 +1690,7 @@ START_TEST(path)
{
HMODULE hmod = LoadLibraryA("kernelbase.dll");
+ pPathAllocCanonicalize = (void *)GetProcAddress(hmod, "PathAllocCanonicalize");
pPathCchCombineEx = (void *)GetProcAddress(hmod, "PathCchCombineEx");
pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash");
pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx");
@@ -1402,6 +1707,7 @@ START_TEST(path)
pPathCchStripToRoot = (void *)GetProcAddress(hmod, "PathCchStripToRoot");
pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
+ test_PathAllocCanonicalize();
test_PathCchCombineEx();
test_PathCchAddBackslash();
test_PathCchAddBackslashEx();
diff --git a/include/pathcch.h b/include/pathcch.h
index 12a6fc5511..b1d6be249d 100644
--- a/include/pathcch.h
+++ b/include/pathcch.h
@@ -22,9 +22,11 @@
#define PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS 0x04
#define PATHCCH_DO_NOT_NORMALIZE_SEGMENTS 0x08
#define PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH 0x10
+#define PATHCCH_ENSURE_TRAILING_SLASH 0x20
#define PATHCCH_MAX_CCH 0x8000
+HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out);
HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size);
HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining);
HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension);
--
2.19.1