Path: Add IsFileNameValid()

This commit is contained in:
Stenzek
2025-09-12 23:27:54 +10:00
parent a8bbd2c4a9
commit 1cdde2cab6
3 changed files with 57 additions and 0 deletions

View File

@@ -6,6 +6,10 @@
#include <gtest/gtest.h>
#include <string_view>
using namespace std::string_view_literals;
TEST(Path, ToNativePath)
{
ASSERT_EQ(Path::ToNativePath(""), "");
@@ -234,6 +238,7 @@ TEST(Path, SanitizeFileName)
ASSERT_EQ(Path::SanitizeFileName("abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}"),
"abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}");
ASSERT_EQ(Path::SanitizeFileName("some*path**with*asterisks"), "some_path__with_asterisks");
ASSERT_EQ(Path::SanitizeFileName("foo\0bar"sv), "foo_bar");
#ifdef _WIN32
ASSERT_EQ(Path::SanitizeFileName("foo:"), "foo_");
ASSERT_EQ(Path::SanitizeFileName("foo:bar."), "foo_bar_");
@@ -244,6 +249,31 @@ TEST(Path, SanitizeFileName)
ASSERT_EQ(Path::SanitizeFileName("foo/bar", false), "foo/bar");
}
TEST(Path, IsFileNameValid)
{
ASSERT_TRUE(Path::IsFileNameValid("foo"sv));
ASSERT_TRUE(Path::IsFileNameValid("foo_bar-0123456789+&=_[]{}"sv));
ASSERT_TRUE(Path::IsFileNameValid("f🙃o"sv));
ASSERT_TRUE(Path::IsFileNameValid("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤ∩₲ ₱⟑♰⫳🐱"sv));
ASSERT_TRUE(Path::IsFileNameValid("foo/bar"sv, true));
ASSERT_TRUE(Path::IsFileNameValid("foo\\bar"sv, true));
ASSERT_FALSE(Path::IsFileNameValid("foo/bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo\0bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo\nbar"sv));
#ifdef _WIN32
ASSERT_FALSE(Path::IsFileNameValid("foo\\bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo:bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo*bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo?bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo\"bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo<bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo>bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foo|bar"sv));
ASSERT_FALSE(Path::IsFileNameValid("foobar.txt."sv));
ASSERT_FALSE(Path::IsFileNameValid("foobar."sv));
#endif
}
TEST(Path, RemoveLengthLimits)
{
#ifdef _WIN32

View File

@@ -67,6 +67,10 @@ static bool IsUNCPath(const T& path)
static inline bool FileSystemCharacterIsSane(char32_t c, bool strip_slashes)
{
// no null bytes
if (c == 0)
return false;
#ifdef _WIN32
// https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#naming-conventions
if ((c == U'/' || c == U'\\') && strip_slashes)
@@ -149,6 +153,26 @@ void Path::SanitizeFileName(std::string* str, bool strip_slashes /* = true */)
#endif
}
bool Path::IsFileNameValid(std::string_view str, bool allow_slashes)
{
size_t pos = 0;
while (pos < str.length())
{
char32_t ch;
pos += StringUtil::DecodeUTF8(str, pos, &ch);
if (!FileSystemCharacterIsSane(ch, !allow_slashes))
return false;
}
#ifdef _WIN32
// Windows: Can't end filename with a period.
if (str.length() > 0 && str.back() == '.')
return false;
#endif
return true;
}
std::string Path::RemoveLengthLimits(std::string_view str)
{
std::string ret;

View File

@@ -36,6 +36,9 @@ void Canonicalize(std::string* path);
std::string SanitizeFileName(std::string_view str, bool strip_slashes = true);
void SanitizeFileName(std::string* str, bool strip_slashes = true);
/// Returns true if the given filename contains any invalid characters.
bool IsFileNameValid(std::string_view str, bool allow_slashes = false);
/// Mutates the path to remove any MAX_PATH limits (for Windows).
std::string RemoveLengthLimits(std::string_view str);
void RemoveLengthLimits(std::string* path);