• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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