1 /*
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
19 #include "android-base/utf8.h"
20
21 #include <fcntl.h>
22
23 #include <string>
24
25 #include "android-base/logging.h"
26
27 namespace android {
28 namespace base {
29
30 // Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
SetErrnoFromLastError()31 static 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
WideToUTF8(const wchar_t * utf16,const size_t size,std::string * utf8)42 bool 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) {
64 SetErrnoFromLastError();
65 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) {
75 SetErrnoFromLastError();
76 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
WideToUTF8(const wchar_t * utf16,std::string * utf8)85 bool 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
WideToUTF8(const std::wstring & utf16,std::string * utf8)90 bool 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.
UTF8ToWideWithFlags(const char * utf8,const size_t size,std::wstring * utf16,const DWORD flags)97 static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
98 const DWORD flags) {
99 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) {
110 SetErrnoFromLastError();
111 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) {
120 SetErrnoFromLastError();
121 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
UTF8ToWide(const char * utf8,const size_t size,std::wstring * utf16)130 bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
131 // If strictly interpreting as UTF-8 succeeds, return success.
132 if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
133 return true;
134 }
135
136 const int saved_errno = errno;
137
138 // Fallback to non-strict interpretation, allowing invalid characters and
139 // converting as best as possible, and return false to signify a problem.
140 (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
141 errno = saved_errno;
142 return false;
143 }
144
UTF8ToWide(const char * utf8,std::wstring * utf16)145 bool 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
UTF8ToWide(const std::string & utf8,std::wstring * utf16)150 bool 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.
157 namespace utf8 {
158
open(const char * name,int flags,...)159 int open(const char* name, int flags, ...) {
160 std::wstring name_utf16;
161 if (!UTF8ToWide(name, &name_utf16)) {
162 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
unlink(const char * name)176 int unlink(const char* name) {
177 std::wstring name_utf16;
178 if (!UTF8ToWide(name, &name_utf16)) {
179 return -1;
180 }
181
182 return _wunlink(name_utf16.c_str());
183 }
184
185 } // namespace utf8
186 } // namespace base
187 } // namespace android
188