From 053d9715076860b393956ca3c1451bcd578bf3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sun, 5 May 2024 18:50:38 +0200 Subject: [PATCH] misc/path_utils: normalize win32 paths --- meson.build | 5 +++++ misc/path_utils.c | 34 +++++++++++++++++++++++++++++++++- test/meson.build | 3 +++ test/paths.c | 23 ++++++++++++++++++++++- 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index b33eef90ea..be0b5c89cd 100644 --- a/meson.build +++ b/meson.build @@ -495,6 +495,11 @@ endif features += {'win32-desktop': win32 and not uwp.found()} if features['win32-desktop'] + pathcch = cc.find_library('pathcch', required: false) + features += {'pathcch': pathcch.found()} + if features['pathcch'] + dependencies += pathcch + endif win32_desktop_libs = [cc.find_library('avrt'), cc.find_library('dwmapi'), cc.find_library('gdi32'), diff --git a/misc/path_utils.c b/misc/path_utils.c index 7fbd1084b8..a1484a9046 100644 --- a/misc/path_utils.c +++ b/misc/path_utils.c @@ -34,6 +34,11 @@ #include "misc/ctype.h" #include "misc/path_utils.h" +#if defined(HAVE_PATHCCH) && HAVE_PATHCCH +#include +#include +#endif + char *mp_basename(const char *path) { char *s; @@ -164,7 +169,34 @@ char *mp_normalize_path(void *talloc_ctx, const char *path) path = mp_path_join(talloc_ctx, cwd, path); } -#if HAVE_DOS_PATHS +#if defined(HAVE_PATHCCH) && HAVE_PATHCCH + wchar_t *pathw = mp_from_utf8(NULL, path); + wchar_t *read = pathw, *write = pathw; + wchar_t prev = '\0'; + // preserve leading double backslashes + if (read[0] == '\\' && read[1] == '\\') { + prev = '\\'; + write += 2; + read += 2; + } + wchar_t curr; + while ((curr = *read)) { + if (curr == '/') + curr = '\\'; + if (curr != '\\' || prev != '\\') + *write++ = curr; + prev = curr; + read++; + } + *write = '\0'; + size_t max_size = wcslen(pathw) + 1; + wchar_t *pathc = talloc_array(NULL, wchar_t, max_size); + HRESULT hr = PathCchCanonicalizeEx(pathc, max_size, pathw, PATHCCH_ALLOW_LONG_PATHS); + char *ret = SUCCEEDED(hr) ? mp_to_utf8(talloc_ctx, pathc) : talloc_strdup(talloc_ctx, path); + talloc_free(pathw); + talloc_free(pathc); + return ret; +#elif HAVE_DOS_PATHS return talloc_strdup(talloc_ctx, path); #else char *result = talloc_strdup(talloc_ctx, ""); diff --git a/test/meson.build b/test/meson.build index e34d72ca02..f77ddf3092 100644 --- a/test/meson.build +++ b/test/meson.build @@ -53,6 +53,9 @@ endif if features['win32-desktop'] test_utils_deps += cc.find_library('imm32') test_utils_deps += cc.find_library('ntdll') + if features['pathcch'] + test_utils_deps += cc.find_library('pathcch') + endif endif test_utils_objects = libmpv.extract_objects(test_utils_files) test_utils = static_library('test-utils', 'test_utils.c', include_directories: incdir, diff --git a/test/paths.c b/test/paths.c index e18c9dfb1c..13b3b26779 100644 --- a/test/paths.c +++ b/test/paths.c @@ -81,15 +81,36 @@ int main(void) #endif TEST_NORMALIZE("https://foo", "https://foo"); +#if !HAVE_DOS_PATHS TEST_NORMALIZE("/foo", "/foo"); +#endif void *ctx = talloc_new(NULL); bstr dst = bstr0(mp_getcwd(ctx)); bstr_xappend(ctx, &dst, bstr0("/foo")); +#if HAVE_DOS_PATHS + char *p = dst.start; + while (*p) { + *p = *p == '/' ? '\\' : *p; + p++; + } +#endif TEST_NORMALIZE(dst.start, "foo"); talloc_free(ctx); -#if (!HAVE_DOS_PATHS) +#if HAVE_DOS_PATHS + TEST_NORMALIZE("C:\\foo\\baz", "C:/foo/bar/../baz"); + TEST_NORMALIZE("C:\\", "C:/foo/../.."); + TEST_NORMALIZE("C:\\foo\\baz", "C:/foo/bar/./../baz"); + TEST_NORMALIZE("C:\\foo\\bar\\baz", "C:/foo//bar/./baz"); + TEST_NORMALIZE("C:\\foo\\bar\\baz", "C:/foo\\./bar\\/baz"); + TEST_NORMALIZE("C:\\file.mkv", "\\\\?\\C:\\folder\\..\\file.mkv"); + TEST_NORMALIZE("C:\\dir", "\\\\?\\C:\\dir\\subdir\\..\\."); + TEST_NORMALIZE("D:\\newfile.txt", "\\\\?\\D:\\\\new\\subdir\\..\\..\\newfile.txt"); + TEST_NORMALIZE("\\\\server\\share\\path", "\\\\?\\UNC/server/share/path/."); + TEST_NORMALIZE("C:\\", "C:/."); + TEST_NORMALIZE("C:\\", "C:/../"); +#else TEST_NORMALIZE("/foo/bar", "/foo//bar"); TEST_NORMALIZE("/foo/bar", "/foo///bar"); TEST_NORMALIZE("/foo/bar", "/foo/bar/");