mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-05 21:43:07 +00:00
Remove duplicated and leading slashes in normalizeFilenameInPlace
To be consistent with correctResourcePath.
This commit is contained in:
parent
96869fd9b9
commit
1b362140ae
3 changed files with 99 additions and 20 deletions
|
|
@ -10,6 +10,49 @@ namespace VFS::Path
|
|||
{
|
||||
using namespace testing;
|
||||
|
||||
struct VFSPathIsNormalizedTest : TestWithParam<std::pair<std::string_view, bool>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(VFSPathIsNormalizedTest, shouldReturnExpectedResult)
|
||||
{
|
||||
EXPECT_EQ(isNormalized(GetParam().first), GetParam().second);
|
||||
}
|
||||
|
||||
const std::pair<std::string_view, bool> isNormalizedTestParams[] = {
|
||||
{ std::string_view(), true },
|
||||
{ "foo", true },
|
||||
{ "foo/bar", true },
|
||||
{ "foo/bar/baz", true },
|
||||
{ "/foo", false },
|
||||
{ "foo//", false },
|
||||
{ "foo\\", false },
|
||||
{ "Foo", false },
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(IsNormalizedTestParams, VFSPathIsNormalizedTest, ValuesIn(isNormalizedTestParams));
|
||||
|
||||
TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveLeadingSeparators)
|
||||
{
|
||||
std::string value("//foo");
|
||||
normalizeFilenameInPlace(value);
|
||||
EXPECT_EQ(value, "foo");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveDuplicatedSeparators)
|
||||
{
|
||||
std::string value("foo//bar///baz");
|
||||
normalizeFilenameInPlace(value);
|
||||
EXPECT_EQ(value, "foo/bar/baz");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveDuplicatedLeadingSeparator)
|
||||
{
|
||||
std::string value("//foo");
|
||||
normalizeFilenameInPlace(value);
|
||||
EXPECT_EQ(value, "foo");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, shouldSupportDefaultConstructor)
|
||||
{
|
||||
const Normalized value;
|
||||
|
|
@ -79,6 +122,13 @@ namespace VFS::Path
|
|||
EXPECT_EQ(value.value(), "foo/bar/baz");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, operatorDivShouldNormalizeSuffix)
|
||||
{
|
||||
Normalized value("foo/bar");
|
||||
value /= std::string_view("\\A\\\\B");
|
||||
EXPECT_EQ(value.value(), "foo/bar/a/b");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot)
|
||||
{
|
||||
Normalized value("foo/bar.a");
|
||||
|
|
@ -86,11 +136,10 @@ namespace VFS::Path
|
|||
EXPECT_EQ(value.value(), "foo/bar.so");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldNormalizeExtension)
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnNotNormalizedExtension)
|
||||
{
|
||||
Normalized value("foo/bar.a");
|
||||
ASSERT_TRUE(value.changeExtension("SO"));
|
||||
EXPECT_EQ(value.value(), "foo/bar.so");
|
||||
EXPECT_THROW(value.changeExtension("\\SO"), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot)
|
||||
|
|
@ -116,7 +165,7 @@ namespace VFS::Path
|
|||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithSeparator)
|
||||
{
|
||||
Normalized value("foo.a");
|
||||
EXPECT_THROW(value.changeExtension("/so"), std::invalid_argument);
|
||||
EXPECT_THROW(value.changeExtension("so/"), std::invalid_argument);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "resourcehelpers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
|
||||
|
|
@ -39,14 +38,6 @@ std::string Misc::ResourceHelpers::correctResourcePath(std::span<const std::stri
|
|||
{
|
||||
std::string correctedPath = VFS::Path::normalizeFilename(resPath);
|
||||
|
||||
// Flatten slashes
|
||||
auto bothSeparators = [](char a, char b) { return a == VFS::Path::separator && b == VFS::Path::separator; };
|
||||
correctedPath.erase(std::unique(correctedPath.begin(), correctedPath.end(), bothSeparators), correctedPath.end());
|
||||
|
||||
// Remove leading separator
|
||||
if (!correctedPath.empty() && correctedPath[0] == VFS::Path::separator)
|
||||
correctedPath.erase(0, 1);
|
||||
|
||||
// Handle top level directory
|
||||
bool needsPrefix = true;
|
||||
for (std::string_view potentialTopLevelDirectory : topLevelDirectories)
|
||||
|
|
|
|||
|
|
@ -14,24 +14,59 @@ namespace VFS::Path
|
|||
inline constexpr char separator = '/';
|
||||
inline constexpr char extensionSeparator = '.';
|
||||
|
||||
inline constexpr char normalize(char c)
|
||||
[[nodiscard]] inline constexpr char normalize(char c)
|
||||
{
|
||||
return c == '\\' ? separator : Misc::StringUtils::toLower(c);
|
||||
}
|
||||
|
||||
inline constexpr bool isNormalized(std::string_view name)
|
||||
[[nodiscard]] inline constexpr bool isNormalized(std::string_view name)
|
||||
{
|
||||
return std::all_of(name.begin(), name.end(), [](char v) { return v == normalize(v); });
|
||||
if (name.empty())
|
||||
return true;
|
||||
|
||||
if (name.front() != normalize(name.front()))
|
||||
return false;
|
||||
|
||||
if (name.front() == separator)
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 1, n = name.size(); i < n; ++i)
|
||||
{
|
||||
if (name[i] != normalize(name[i]))
|
||||
return false;
|
||||
|
||||
if (name[i] == separator && name[i - 1] == name[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void normalizeFilenameInPlace(auto begin, auto end)
|
||||
[[nodiscard]] inline auto removeDuplicatedSeparators(auto begin, auto end)
|
||||
{
|
||||
return std::unique(begin, end, [](char a, char b) { return a == separator && b == separator; });
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto removeLeadingSeparator(auto begin, auto end)
|
||||
{
|
||||
if (begin != end && *begin == separator)
|
||||
return begin + 1;
|
||||
return begin;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto normalizeFilenameInPlace(auto begin, auto end)
|
||||
{
|
||||
std::transform(begin, end, begin, normalize);
|
||||
end = removeDuplicatedSeparators(begin, end);
|
||||
begin = removeLeadingSeparator(begin, end);
|
||||
return std::pair(begin, end);
|
||||
}
|
||||
|
||||
inline void normalizeFilenameInPlace(std::string& name)
|
||||
{
|
||||
normalizeFilenameInPlace(name.begin(), name.end());
|
||||
const auto [begin, end] = normalizeFilenameInPlace(name.begin(), name.end());
|
||||
name.erase(end, name.end());
|
||||
name.erase(name.begin(), begin);
|
||||
}
|
||||
|
||||
/// Normalize the given filename, making slashes/backslashes consistent, and lower-casing.
|
||||
|
|
@ -197,6 +232,8 @@ namespace VFS::Path
|
|||
|
||||
bool changeExtension(std::string_view extension)
|
||||
{
|
||||
if (!isNormalized(extension))
|
||||
throw std::invalid_argument("Not normalized extension: " + std::string(extension));
|
||||
if (findSeparatorOrExtensionSeparator(extension.begin(), extension.end()) != extension.end())
|
||||
throw std::invalid_argument("Invalid extension: " + std::string(extension));
|
||||
const auto it = findSeparatorOrExtensionSeparator(mValue.rbegin(), mValue.rend());
|
||||
|
|
@ -204,7 +241,7 @@ namespace VFS::Path
|
|||
return false;
|
||||
const std::string::difference_type pos = mValue.rend() - it;
|
||||
mValue.replace(pos, mValue.size(), extension);
|
||||
normalizeFilenameInPlace(mValue.begin() + pos, mValue.end());
|
||||
std::transform(mValue.begin() + pos, mValue.end(), mValue.begin() + pos, normalize);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +267,9 @@ namespace VFS::Path
|
|||
mValue += separator;
|
||||
const std::size_t offset = mValue.size();
|
||||
mValue += value;
|
||||
normalizeFilenameInPlace(mValue.begin() + offset, mValue.end());
|
||||
const auto [begin, end] = normalizeFilenameInPlace(mValue.begin() + offset, mValue.end());
|
||||
std::copy(begin, end, mValue.begin() + offset);
|
||||
mValue.resize(offset + (end - begin));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue