From 7ff2472f0cae6a9344ba50a9136997762cba2925 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Thu, 20 Feb 2025 15:50:19 +0100 Subject: [PATCH 1/2] splitPath: Do not rely on basename Getting the basename is not well defined on Windows for paths with only a drive component, like "C:" or "C:\\". GLib::path_get_basename returns "\\"; both os.path.basename and pathlib.Path.name return an empty string (Python3). As a result, splitPath() did not include the drive letter in the returned path components. Fixes https://gitlab.com/inkscape/inkscape/-/issues/5311 --- src/io/fix-broken-links.cpp | 41 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/io/fix-broken-links.cpp b/src/io/fix-broken-links.cpp index bcd96b7abe..d0840bff7b 100644 --- a/src/io/fix-broken-links.cpp +++ b/src/io/fix-broken-links.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -31,26 +32,32 @@ namespace Inkscape { -std::vector splitPath( std::string const &path ) +std::vector splitPath( std::string const &path_ ) { - std::vector parts; - - std::string prior; - std::string tmp = path; - while ( !tmp.empty() && (tmp != prior) ) { - prior = tmp; - - parts.push_back( Glib::path_get_basename(tmp) ); - tmp = Glib::path_get_dirname(tmp); - } - if ( !parts.empty() ) { - std::reverse(parts.begin(), parts.end()); - if ( (parts[0] == ".") && (path[0] != '.') ) { - parts.erase(parts.begin()); - } + std::string path(path_); + std::vector result; + char **parts; +#ifdef _WIN32 + const char *separator = "\\"; +#else + const char *separator = "/"; +#endif + +#ifdef _WIN32 + std::replace(path.begin(), path.end(), '/', '\\'); +#endif + + parts = g_strsplit (path.c_str(), separator, -1); + + for (char **iter = parts; *iter != nullptr; ++iter) { + char *p = *iter; + if (p[0] != '\0' && std::strcmp (p, ".") != 0) + result.push_back(p); } - return parts; + g_strfreev (parts); + + return result; } /** -- GitLab From 535a677870ea2d33b9c2182b0d376641357b649b Mon Sep 17 00:00:00 2001 From: PBS Date: Thu, 24 Jul 2025 16:48:59 +0200 Subject: [PATCH 2/2] Cppify and add test --- src/io/fix-broken-links.cpp | 31 ++++++++++++++----------- src/io/fix-broken-links.h | 2 +- testfiles/CMakeLists.txt | 1 + testfiles/src/fix-broken-links-test.cpp | 18 ++++++++++++++ 4 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 testfiles/src/fix-broken-links-test.cpp diff --git a/src/io/fix-broken-links.cpp b/src/io/fix-broken-links.cpp index d0840bff7b..9c0f7d2eea 100644 --- a/src/io/fix-broken-links.cpp +++ b/src/io/fix-broken-links.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include "fix-broken-links.h" @@ -32,30 +32,35 @@ namespace Inkscape { -std::vector splitPath( std::string const &path_ ) +std::vector splitPath(std::string path) { - std::string path(path_); - std::vector result; - char **parts; #ifdef _WIN32 - const char *separator = "\\"; + constexpr auto separator = '\\'; #else - const char *separator = "/"; + constexpr auto separator = '/'; #endif #ifdef _WIN32 std::replace(path.begin(), path.end(), '/', '\\'); #endif - parts = g_strsplit (path.c_str(), separator, -1); + std::vector result; - for (char **iter = parts; *iter != nullptr; ++iter) { - char *p = *iter; - if (p[0] != '\0' && std::strcmp (p, ".") != 0) - result.push_back(p); + for (auto part : Glib::Regex::split_simple(std::string{separator}.c_str(), path.c_str())) { + if (!part.empty() && part != ".") { + result.emplace_back(part.release()); + } } - g_strfreev (parts); + // Todo: (C++20) When macOS supports it. + /* + for (auto part : path | std::views::split(separator)) { + auto view = std::string_view{part.begin(), part.end()}; + if (!view.empty() && view != ".") { + result.emplace_back(view); + } + } + */ return result; } diff --git a/src/io/fix-broken-links.h b/src/io/fix-broken-links.h index e443b58332..5fa63a3b3d 100644 --- a/src/io/fix-broken-links.h +++ b/src/io/fix-broken-links.h @@ -14,7 +14,7 @@ class SPDocument; namespace Inkscape { -std::vector splitPath( std::string const &path ); +std::vector splitPath(std::string path); std::string optimizePath(std::string const &path, std::string const &base, unsigned int parents = 2); bool fixBrokenLinks(SPDocument *doc); diff --git a/testfiles/CMakeLists.txt b/testfiles/CMakeLists.txt index 9764424c70..7550e03356 100644 --- a/testfiles/CMakeLists.txt +++ b/testfiles/CMakeLists.txt @@ -95,6 +95,7 @@ set(TEST_SOURCES util-uri-test drag-and-drop-svgz drawing-pattern-test + fix-broken-links-test poppler-utils-test attributes-test dir-util-test diff --git a/testfiles/src/fix-broken-links-test.cpp b/testfiles/src/fix-broken-links-test.cpp new file mode 100644 index 0000000000..07ecd41c43 --- /dev/null +++ b/testfiles/src/fix-broken-links-test.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "io/fix-broken-links.h" + +#include + +namespace Inkscape { + +TEST(FixBrokenLinksTest, SplitPath) +{ +#ifdef _WIN32 + EXPECT_EQ(splitPath("C:\\images\\\\.\\file.svg"), (std::vector{"C:", "images", "file.svg"})); +#else + EXPECT_EQ(splitPath("/home/user//./file.svg"), (std::vector{"home", "user", "file.svg"})); +#endif +} + +} // namespace Inkscape -- GitLab