diff --git a/common/FileSystem.cpp b/common/FileSystem.cpp index b550b8d3b4..e1b2af6142 100644 --- a/common/FileSystem.cpp +++ b/common/FileSystem.cpp @@ -190,6 +190,27 @@ void Path::SanitizeFileName(std::string* str, bool strip_slashes /* = true */) #endif } +bool Path::IsValidFileName(const std::string_view& str, bool allow_slashes) +{ + const size_t len = str.length(); + size_t pos = 0; + while (pos < len) + { + char32_t ch; + pos += StringUtil::DecodeUTF8(str.data() + pos, pos - len, &ch); + if (!FileSystemCharacterIsSane(ch, !allow_slashes)) + return false; + } + +#ifdef _WIN32 + // Windows: Can't end filename with a period. + if (len > 0 && str.back() == '.') + return false; +#endif + + return true; +} + bool Path::IsAbsolute(const std::string_view& path) { #ifdef _WIN32 diff --git a/common/Path.h b/common/Path.h index 564d0ec174..99f07992ee 100644 --- a/common/Path.h +++ b/common/Path.h @@ -41,6 +41,9 @@ namespace Path std::string SanitizeFileName(const std::string_view& str, bool strip_slashes = true); void SanitizeFileName(std::string* str, bool strip_slashes = true); + /// Returns true if the specified filename is valid on this operating system. + bool IsValidFileName(const std::string_view& str, bool allow_slashes = false); + /// Returns true if the specified path is an absolute path (C:\Path on Windows or /path on Unix). bool IsAbsolute(const std::string_view& path); diff --git a/tests/ctest/common/path_tests.cpp b/tests/ctest/common/path_tests.cpp index 5472c1402c..ed4e0a112f 100644 --- a/tests/ctest/common/path_tests.cpp +++ b/tests/ctest/common/path_tests.cpp @@ -41,6 +41,29 @@ TEST(FileSystem, ToNativePath) #endif } +TEST(FileSystem, IsValidFileName) +{ +#if defined(_WIN32) || defined(__APPLE__) + ASSERT_FALSE(Path::IsValidFileName("foo:bar", false)); + ASSERT_FALSE(Path::IsValidFileName("baz\\foo:bar", false)); + ASSERT_FALSE(Path::IsValidFileName("baz/foo:bar", false)); + ASSERT_FALSE(Path::IsValidFileName("baz\\foo:bar", true)); + ASSERT_FALSE(Path::IsValidFileName("baz/foo:bar", true)); +#endif +#ifdef _WIN32 + ASSERT_TRUE(Path::IsValidFileName("baz\\foo", true)); + ASSERT_FALSE(Path::IsValidFileName("baz\\foo", false)); + ASSERT_FALSE(Path::IsValidFileName("foo.", true)); + ASSERT_FALSE(Path::IsValidFileName("foo\\.", true)); +#else + ASSERT_FALSE(Path::IsValidFileName("foo\\*", true)); + ASSERT_FALSE(Path::IsValidFileName("foo*", true)); +#endif + + ASSERT_TRUE(Path::IsValidFileName("baz/foo", true)); + ASSERT_FALSE(Path::IsValidFileName("baz/foo", false)); +} + TEST(FileSystem, IsAbsolute) { ASSERT_FALSE(Path::IsAbsolute(""));