diff options
author | Spencer Low <CompareAndSwap@gmail.com> | 2015-10-21 22:22:50 -0700 |
---|---|---|
committer | Spencer Low <CompareAndSwap@gmail.com> | 2015-11-10 15:48:54 -0800 |
commit | ac9514a4524f98b2029dbcda9326a763d25492b1 (patch) | |
tree | 9d33845468022540c4f8debe60e82a06ea8cb628 /base/utf8.cpp | |
parent | 48f2e1ed317f6a330b129c788bd19dac2a4df8ad (diff) | |
download | system_core-ac9514a4524f98b2029dbcda9326a763d25492b1.tar.gz system_core-ac9514a4524f98b2029dbcda9326a763d25492b1.tar.bz2 system_core-ac9514a4524f98b2029dbcda9326a763d25492b1.zip |
adb/base: fix adb push of Unicode filenames on Win32
ae5a6c06cdd9ae1a0a7cdc42711f0a5594e54dcd made adb push use
android::base::ReadFileToString() for small files, but that API did not
support UTF-8 filenames on Windows, until this fix which does the
following:
- Add android::base::{WideToUTF8,UTF8ToWide}() which are only available
on Windows. The signatures are based on Chromium's APIs of the same
name.
- Add the namespace android::base::utf8 which has versions of APIs that
take UTF-8 strings. To use this, make sure your code is in a namespace
and then do "using namespace android::base::utf8;". On Windows, this will
make calls to open() call android::base::utf8::open(), and on other
platforms, it will just call the regular ::open().
- Make ReadFileToString() and WriteStringToFile() use utf8::open() and
utf8::unlink().
- Adapt unittests from Chromium.
- fastboot needs to link with libcutils because it links with libbase
which depends on libcutils for gettid() for logging.
Change-Id: I1aeac40ff358331d7a1ff457ce894bfb17863904
Signed-off-by: Spencer Low <CompareAndSwap@gmail.com>
Diffstat (limited to 'base/utf8.cpp')
-rwxr-xr-x | base/utf8.cpp | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/base/utf8.cpp b/base/utf8.cpp new file mode 100755 index 000000000..62a118f56 --- /dev/null +++ b/base/utf8.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <windows.h> + +#include "base/utf8.h" + +#include <fcntl.h> + +#include <string> + +#include "base/logging.h" + +namespace android { +namespace base { + +bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) { + utf8->clear(); + + if (size == 0) { + return true; + } + + // TODO: Consider using std::wstring_convert once libcxx is supported on + // Windows. + + // Only Vista or later has this flag that causes WideCharToMultiByte() to + // return an error on invalid characters. + const DWORD flags = +#if (WINVER >= 0x0600) + WC_ERR_INVALID_CHARS; +#else + 0; +#endif + + const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size, + NULL, 0, NULL, NULL); + if (chars_required <= 0) { + return false; + } + + // This could potentially throw a std::bad_alloc exception. + utf8->resize(chars_required); + + const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size, + &(*utf8)[0], chars_required, NULL, + NULL); + if (result != chars_required) { + CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result + << " chars to buffer of " << chars_required << " chars"; + utf8->clear(); + return false; + } + + return true; +} + +bool WideToUTF8(const wchar_t* utf16, std::string* utf8) { + // Compute string length of NULL-terminated string with wcslen(). + return WideToUTF8(utf16, wcslen(utf16), utf8); +} + +bool WideToUTF8(const std::wstring& utf16, std::string* utf8) { + // Use the stored length of the string which allows embedded NULL characters + // to be converted. + return WideToUTF8(utf16.c_str(), utf16.length(), utf8); +} + +// Internal helper function that takes MultiByteToWideChar() flags. +static bool _UTF8ToWideWithFlags(const char* utf8, const size_t size, + std::wstring* utf16, const DWORD flags) { + utf16->clear(); + + if (size == 0) { + return true; + } + + // TODO: Consider using std::wstring_convert once libcxx is supported on + // Windows. + const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size, + NULL, 0); + if (chars_required <= 0) { + return false; + } + + // This could potentially throw a std::bad_alloc exception. + utf16->resize(chars_required); + + const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size, + &(*utf16)[0], chars_required); + if (result != chars_required) { + CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result + << " chars to buffer of " << chars_required << " chars"; + utf16->clear(); + return false; + } + + return true; +} + +bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) { + // If strictly interpreting as UTF-8 succeeds, return success. + if (_UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) { + return true; + } + + // Fallback to non-strict interpretation, allowing invalid characters and + // converting as best as possible, and return false to signify a problem. + (void)_UTF8ToWideWithFlags(utf8, size, utf16, 0); + return false; +} + +bool UTF8ToWide(const char* utf8, std::wstring* utf16) { + // Compute string length of NULL-terminated string with strlen(). + return UTF8ToWide(utf8, strlen(utf8), utf16); +} + +bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) { + // Use the stored length of the string which allows embedded NULL characters + // to be converted. + return UTF8ToWide(utf8.c_str(), utf8.length(), utf16); +} + +// Versions of standard library APIs that support UTF-8 strings. +namespace utf8 { + +int open(const char* name, int flags, ...) { + std::wstring name_utf16; + if (!UTF8ToWide(name, &name_utf16)) { + errno = EINVAL; + return -1; + } + + int mode = 0; + if ((flags & O_CREAT) != 0) { + va_list args; + va_start(args, flags); + mode = va_arg(args, int); + va_end(args); + } + + return _wopen(name_utf16.c_str(), flags, mode); +} + +int unlink(const char* name) { + std::wstring name_utf16; + if (!UTF8ToWide(name, &name_utf16)) { + errno = EINVAL; + return -1; + } + + return _wunlink(name_utf16.c_str()); +} + +} // namespace utf8 +} // namespace base +} // namespace android |