blob: 3cca70026eb8b82a7d2efe5326697f41de5bfa54 [file] [log] [blame]
Elliott Hughesc1fd4922015-11-11 18:02:29 +00001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <windows.h>
18
Elliott Hughes4f713192015-12-04 22:00:26 -080019#include "android-base/utf8.h"
Elliott Hughesc1fd4922015-11-11 18:02:29 +000020
21#include <fcntl.h>
22
23#include <string>
24
Elliott Hughes4f713192015-12-04 22:00:26 -080025#include "android-base/logging.h"
Elliott Hughesc1fd4922015-11-11 18:02:29 +000026
27namespace android {
28namespace base {
29
Spencer Lowd21dc822015-11-12 15:20:15 -080030// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
31static void SetErrnoFromLastError() {
32 switch (GetLastError()) {
33 case ERROR_NO_UNICODE_TRANSLATION:
34 errno = EILSEQ;
35 break;
36 default:
37 errno = EINVAL;
38 break;
39 }
40}
41
Elliott Hughesc1fd4922015-11-11 18:02:29 +000042bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
43 utf8->clear();
44
45 if (size == 0) {
46 return true;
47 }
48
49 // TODO: Consider using std::wstring_convert once libcxx is supported on
50 // Windows.
51
52 // Only Vista or later has this flag that causes WideCharToMultiByte() to
53 // return an error on invalid characters.
54 const DWORD flags =
55#if (WINVER >= 0x0600)
56 WC_ERR_INVALID_CHARS;
57#else
58 0;
59#endif
60
61 const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
62 NULL, 0, NULL, NULL);
63 if (chars_required <= 0) {
Spencer Lowd21dc822015-11-12 15:20:15 -080064 SetErrnoFromLastError();
Elliott Hughesc1fd4922015-11-11 18:02:29 +000065 return false;
66 }
67
68 // This could potentially throw a std::bad_alloc exception.
69 utf8->resize(chars_required);
70
71 const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
72 &(*utf8)[0], chars_required, NULL,
73 NULL);
74 if (result != chars_required) {
Spencer Lowd21dc822015-11-12 15:20:15 -080075 SetErrnoFromLastError();
Elliott Hughesc1fd4922015-11-11 18:02:29 +000076 CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
77 << " chars to buffer of " << chars_required << " chars";
78 utf8->clear();
79 return false;
80 }
81
82 return true;
83}
84
85bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
86 // Compute string length of NULL-terminated string with wcslen().
87 return WideToUTF8(utf16, wcslen(utf16), utf8);
88}
89
90bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
91 // Use the stored length of the string which allows embedded NULL characters
92 // to be converted.
93 return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
94}
95
96// Internal helper function that takes MultiByteToWideChar() flags.
Spencer Lowd21dc822015-11-12 15:20:15 -080097static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
98 const DWORD flags) {
Elliott Hughesc1fd4922015-11-11 18:02:29 +000099 utf16->clear();
100
101 if (size == 0) {
102 return true;
103 }
104
105 // TODO: Consider using std::wstring_convert once libcxx is supported on
106 // Windows.
107 const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
108 NULL, 0);
109 if (chars_required <= 0) {
Spencer Lowd21dc822015-11-12 15:20:15 -0800110 SetErrnoFromLastError();
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000111 return false;
112 }
113
114 // This could potentially throw a std::bad_alloc exception.
115 utf16->resize(chars_required);
116
117 const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
118 &(*utf16)[0], chars_required);
119 if (result != chars_required) {
Spencer Lowd21dc822015-11-12 15:20:15 -0800120 SetErrnoFromLastError();
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000121 CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
122 << " chars to buffer of " << chars_required << " chars";
123 utf16->clear();
124 return false;
125 }
126
127 return true;
128}
129
130bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
131 // If strictly interpreting as UTF-8 succeeds, return success.
Spencer Lowd21dc822015-11-12 15:20:15 -0800132 if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000133 return true;
134 }
135
Spencer Lowd21dc822015-11-12 15:20:15 -0800136 const int saved_errno = errno;
137
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000138 // Fallback to non-strict interpretation, allowing invalid characters and
139 // converting as best as possible, and return false to signify a problem.
Spencer Lowd21dc822015-11-12 15:20:15 -0800140 (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
141 errno = saved_errno;
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000142 return false;
143}
144
145bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
146 // Compute string length of NULL-terminated string with strlen().
147 return UTF8ToWide(utf8, strlen(utf8), utf16);
148}
149
150bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
151 // Use the stored length of the string which allows embedded NULL characters
152 // to be converted.
153 return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
154}
155
156// Versions of standard library APIs that support UTF-8 strings.
157namespace utf8 {
158
159int open(const char* name, int flags, ...) {
160 std::wstring name_utf16;
161 if (!UTF8ToWide(name, &name_utf16)) {
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000162 return -1;
163 }
164
165 int mode = 0;
166 if ((flags & O_CREAT) != 0) {
167 va_list args;
168 va_start(args, flags);
169 mode = va_arg(args, int);
170 va_end(args);
171 }
172
173 return _wopen(name_utf16.c_str(), flags, mode);
174}
175
176int unlink(const char* name) {
177 std::wstring name_utf16;
178 if (!UTF8ToWide(name, &name_utf16)) {
Elliott Hughesc1fd4922015-11-11 18:02:29 +0000179 return -1;
180 }
181
182 return _wunlink(name_utf16.c_str());
183}
184
185} // namespace utf8
186} // namespace base
187} // namespace android