Home | History | Annotate | Download | only in base
      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().
     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 
     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 
     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 
     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.
     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 
    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 
    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 
    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 
    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 
    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