Loading base/include/android-base/utf8.h +19 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ #else // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace. #include <fcntl.h> // open #include <stdio.h> // fopen #include <sys/stat.h> // mkdir #include <unistd.h> // unlink #endif Loading Loading @@ -53,6 +55,19 @@ bool UTF8ToWide(const char* utf8, std::wstring* utf16); // Convert a UTF-8 std::string (including any embedded NULL characters) to // UTF-16. Returns whether the conversion was done successfully. bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); // Convert a file system path, represented as a NULL-terminated string of // UTF-8 characters, to a UTF-16 string representing the same file system // path using the Windows extended-lengh path representation. // // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH: // ```The Windows API has many functions that also have Unicode versions to // permit an extended-length path for a maximum total path length of 32,767 // characters. To specify an extended-length path, use the "\\?\" prefix. // For example, "\\?\D:\very long path".``` // // Returns whether the conversion was done successfully. bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16); #endif // The functions in the utf8 namespace take UTF-8 strings. For Windows, these Loading @@ -73,9 +88,13 @@ bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); namespace utf8 { #ifdef _WIN32 FILE* fopen(const char* name, const char* mode); int mkdir(const char* name, mode_t mode); int open(const char* name, int flags, ...); int unlink(const char* name); #else using ::fopen; using ::mkdir; using ::open; using ::unlink; #endif Loading base/utf8.cpp +50 −2 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ #include "android-base/utf8.h" #include <fcntl.h> #include <stdio.h> #include <algorithm> #include <string> #include "android-base/logging.h" Loading Loading @@ -153,12 +155,58 @@ bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) { return UTF8ToWide(utf8.c_str(), utf8.length(), utf16); } static bool isDriveLetter(wchar_t c) { return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z'); } bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) { if (!UTF8ToWide(utf8, utf16)) { return false; } // Note: Although most Win32 File I/O API are limited to MAX_PATH (260 // characters), the CreateDirectory API is limited to 248 characters. if (utf16->length() >= 248) { // If path is of the form "x:\" or "x:/" if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' && ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) { // Append long path prefix, and make sure there are no unix-style // separators to ensure a fully compliant Win32 long path string. utf16->insert(0, LR"(\\?\)"); std::replace(utf16->begin(), utf16->end(), L'/', L'\\'); } } return true; } // Versions of standard library APIs that support UTF-8 strings. namespace utf8 { FILE* fopen(const char* name, const char* mode) { std::wstring name_utf16; if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return nullptr; } std::wstring mode_utf16; if (!UTF8ToWide(mode, &mode_utf16)) { return nullptr; } return _wfopen(name_utf16.c_str(), mode_utf16.c_str()); } int mkdir(const char* name, mode_t mode) { std::wstring name_utf16; if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } return _wmkdir(name_utf16.c_str()); } int open(const char* name, int flags, ...) { std::wstring name_utf16; if (!UTF8ToWide(name, &name_utf16)) { if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } Loading @@ -175,7 +223,7 @@ int open(const char* name, int flags, ...) { int unlink(const char* name) { std::wstring name_utf16; if (!UTF8ToWide(name, &name_utf16)) { if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } Loading base/utf8_test.cpp +76 −0 Original line number Diff line number Diff line Loading @@ -18,7 +18,12 @@ #include <gtest/gtest.h> #include <fcntl.h> #include <stdlib.h> #include "android-base/macros.h" #include "android-base/test_utils.h" #include "android-base/unique_fd.h" namespace android { namespace base { Loading Loading @@ -408,5 +413,76 @@ TEST(SysStrings, SysUTF8ToWide) { EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); } TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) { std::string utf8 = "c:\\mypath\\myfile.txt"; std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)")); } TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) { std::string utf8 = "c:\\mypath"; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "\\mypathsegment"; } std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(0U, wide.find(LR"(\\?\)")); EXPECT_EQ(std::string::npos, wide.find(L"/")); } TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) { std::string utf8 = "c:/mypath"; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "/mypathsegment"; } std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(0U, wide.find(LR"(\\?\)")); EXPECT_EQ(std::string::npos, wide.find(L"/")); } namespace utf8 { TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) { TemporaryDir td; // Create long directory path std::string utf8 = td.path; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "\\mypathsegment"; EXPECT_EQ(0, mkdir(utf8.c_str(), 0)); } // Create file utf8 += "\\test-file.bin"; int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; int mode = 0666; android::base::unique_fd fd(open(utf8.c_str(), flags, mode)); EXPECT_NE(-1, fd.get()); // Close file fd.reset(); EXPECT_EQ(-1, fd.get()); // Open file with fopen FILE* file = fopen(utf8.c_str(), "rb"); EXPECT_NE(nullptr, file); if (file) { fclose(file); } // Delete file EXPECT_EQ(0, unlink(utf8.c_str())); } } // namespace utf8 } // namespace base } // namespace android Loading
base/include/android-base/utf8.h +19 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ #else // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace. #include <fcntl.h> // open #include <stdio.h> // fopen #include <sys/stat.h> // mkdir #include <unistd.h> // unlink #endif Loading Loading @@ -53,6 +55,19 @@ bool UTF8ToWide(const char* utf8, std::wstring* utf16); // Convert a UTF-8 std::string (including any embedded NULL characters) to // UTF-16. Returns whether the conversion was done successfully. bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); // Convert a file system path, represented as a NULL-terminated string of // UTF-8 characters, to a UTF-16 string representing the same file system // path using the Windows extended-lengh path representation. // // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH: // ```The Windows API has many functions that also have Unicode versions to // permit an extended-length path for a maximum total path length of 32,767 // characters. To specify an extended-length path, use the "\\?\" prefix. // For example, "\\?\D:\very long path".``` // // Returns whether the conversion was done successfully. bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16); #endif // The functions in the utf8 namespace take UTF-8 strings. For Windows, these Loading @@ -73,9 +88,13 @@ bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); namespace utf8 { #ifdef _WIN32 FILE* fopen(const char* name, const char* mode); int mkdir(const char* name, mode_t mode); int open(const char* name, int flags, ...); int unlink(const char* name); #else using ::fopen; using ::mkdir; using ::open; using ::unlink; #endif Loading
base/utf8.cpp +50 −2 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ #include "android-base/utf8.h" #include <fcntl.h> #include <stdio.h> #include <algorithm> #include <string> #include "android-base/logging.h" Loading Loading @@ -153,12 +155,58 @@ bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) { return UTF8ToWide(utf8.c_str(), utf8.length(), utf16); } static bool isDriveLetter(wchar_t c) { return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z'); } bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) { if (!UTF8ToWide(utf8, utf16)) { return false; } // Note: Although most Win32 File I/O API are limited to MAX_PATH (260 // characters), the CreateDirectory API is limited to 248 characters. if (utf16->length() >= 248) { // If path is of the form "x:\" or "x:/" if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' && ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) { // Append long path prefix, and make sure there are no unix-style // separators to ensure a fully compliant Win32 long path string. utf16->insert(0, LR"(\\?\)"); std::replace(utf16->begin(), utf16->end(), L'/', L'\\'); } } return true; } // Versions of standard library APIs that support UTF-8 strings. namespace utf8 { FILE* fopen(const char* name, const char* mode) { std::wstring name_utf16; if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return nullptr; } std::wstring mode_utf16; if (!UTF8ToWide(mode, &mode_utf16)) { return nullptr; } return _wfopen(name_utf16.c_str(), mode_utf16.c_str()); } int mkdir(const char* name, mode_t mode) { std::wstring name_utf16; if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } return _wmkdir(name_utf16.c_str()); } int open(const char* name, int flags, ...) { std::wstring name_utf16; if (!UTF8ToWide(name, &name_utf16)) { if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } Loading @@ -175,7 +223,7 @@ int open(const char* name, int flags, ...) { int unlink(const char* name) { std::wstring name_utf16; if (!UTF8ToWide(name, &name_utf16)) { if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } Loading
base/utf8_test.cpp +76 −0 Original line number Diff line number Diff line Loading @@ -18,7 +18,12 @@ #include <gtest/gtest.h> #include <fcntl.h> #include <stdlib.h> #include "android-base/macros.h" #include "android-base/test_utils.h" #include "android-base/unique_fd.h" namespace android { namespace base { Loading Loading @@ -408,5 +413,76 @@ TEST(SysStrings, SysUTF8ToWide) { EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); } TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) { std::string utf8 = "c:\\mypath\\myfile.txt"; std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)")); } TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) { std::string utf8 = "c:\\mypath"; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "\\mypathsegment"; } std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(0U, wide.find(LR"(\\?\)")); EXPECT_EQ(std::string::npos, wide.find(L"/")); } TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) { std::string utf8 = "c:/mypath"; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "/mypathsegment"; } std::wstring wide; EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); EXPECT_EQ(0U, wide.find(LR"(\\?\)")); EXPECT_EQ(std::string::npos, wide.find(L"/")); } namespace utf8 { TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) { TemporaryDir td; // Create long directory path std::string utf8 = td.path; while (utf8.length() < 300 /* MAX_PATH is 260 */) { utf8 += "\\mypathsegment"; EXPECT_EQ(0, mkdir(utf8.c_str(), 0)); } // Create file utf8 += "\\test-file.bin"; int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; int mode = 0666; android::base::unique_fd fd(open(utf8.c_str(), flags, mode)); EXPECT_NE(-1, fd.get()); // Close file fd.reset(); EXPECT_EQ(-1, fd.get()); // Open file with fopen FILE* file = fopen(utf8.c_str(), "rb"); EXPECT_NE(nullptr, file); if (file) { fclose(file); } // Delete file EXPECT_EQ(0, unlink(utf8.c_str())); } } // namespace utf8 } // namespace base } // namespace android