Reactos
at master 217 lines 6.3 kB view raw
1// 2// wctomb.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Functions to convert a wide character to the equivalent multibyte character, 7// according to the LC_CTYPE category of the current locale. The return value 8// (or *return_value, for the _s-suffixed functions) depends on the destination 9// and destination_count: 10// * If destination == nullptr && count == 0, number of bytes needed for conversion 11// * If destination != nullptr: the number of bytes used for the conversion 12// * If destination == nullptr && count > 0, the state information 13// (0 for no state dependency, 1 if a state dependent encoding) 14// 15// Historically Windows has not supported state-dependent encodings for wctomb, 16// however UTF-8 can possible require more than one input wchar_t and maintain 17// state between calls. 18// 19// The return_value pointer may be null. 20// 21#include <corecrt_internal.h> 22#include <corecrt_internal_mbstring.h> 23#include <corecrt_internal_ptd_propagation.h> 24#include <errno.h> 25#include <limits.h> 26#include <locale.h> 27#include <stdlib.h> 28 29 30extern "C" int __cdecl _wctomb_internal( 31 int* const return_value, 32 char* const destination, 33 size_t const destination_count, 34 wchar_t const wchar, 35 __crt_cached_ptd_host& ptd 36 ) 37{ 38 // Did the caller request if this is a state dependent encoding? 39 if (!destination && destination_count > 0) 40 { 41 // Indicate do not have state-dependent encodings: 42 if (return_value != nullptr) 43 *return_value = 0; 44 45 return 0; 46 } 47 48 if (return_value) 49 *return_value = -1; 50 51 // We need to cast destination_count to int, so we make sure we are not 52 // going to truncate destination_count: 53 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, destination_count <= INT_MAX, EINVAL); 54 _locale_t const locale = ptd.get_locale(); 55 56 if (locale->locinfo->_public._locale_lc_codepage == CP_UTF8) 57 { 58 // Unlike c16rtomb. wctomb/wcrtomb have no ability to process a partial code point. 59 // So, we could call c16rtomb and check for a lone surrogate or other error, or for simplicity 60 // We can instead just call c32rtomb and check for any error. I choose the latter. 61 mbstate_t state{}; 62 int result = static_cast<int>(__crt_mbstring::__c32rtomb_utf8(destination, static_cast<char32_t>(wchar), &state, ptd)); 63 if (return_value != nullptr) 64 { 65 *return_value = result; 66 } 67 if (result <= 4) 68 { 69 return 0; 70 } 71 else 72 { 73 return ptd.get_errno().value_or(0); 74 } 75 } 76 77 // Check for C-locale behavior, which merely casts it to char (if in range) 78 // for ASCII-ish behavior. 79 if (!locale->locinfo->locale_name[LC_CTYPE]) 80 { 81 // See if the WCHAR is > ASCII-ish range 82 if (wchar > 255) // Validate high byte 83 { 84 // Too big, can't convert, clear buffer and return error 85 if (destination != nullptr && destination_count > 0) 86 { 87 memset(destination, 0, destination_count); 88 } 89 90 return ptd.get_errno().set(EILSEQ); 91 } 92 93 // ASCII-ish, just cast to a (char) 94 if (destination != nullptr) 95 { 96 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, destination_count > 0, ERANGE); 97 *destination = static_cast<char>(wchar); 98 } 99 100 if (return_value != nullptr) 101 { 102 // casting one WCHAR emits a single char 103 *return_value = 1; 104 } 105 106 // We're done 107 return 0; 108 } 109 else 110 { 111 BOOL default_used{}; 112 int const size = __acrt_WideCharToMultiByte( 113 locale->locinfo->_public._locale_lc_codepage, 114 0, 115 &wchar, 116 1, 117 destination, 118 static_cast<int>(destination_count), 119 nullptr, 120 &default_used); 121 122 if (size == 0 || default_used) 123 { 124 if (size == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) 125 { 126 if (destination && destination_count > 0) 127 { 128 memset(destination, 0, destination_count); 129 } 130 131 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, ("Buffer too small", 0), ERANGE); 132 } 133 134 return ptd.get_errno().set(EILSEQ); 135 } 136 137 if (return_value) 138 { 139 *return_value = size; 140 } 141 142 return 0; 143 } 144 145 // The last thing was an if/else, so we already returned. 146} 147 148extern "C" int __cdecl _wctomb_s_l( 149 int* const return_value, 150 char* const destination, 151 size_t const destination_count, 152 wchar_t const wchar, 153 _locale_t const locale 154 ) 155{ 156 __crt_cached_ptd_host ptd(locale); 157 return _wctomb_internal(return_value, destination, destination_count, wchar, ptd); 158} 159 160extern "C" errno_t __cdecl wctomb_s ( 161 int* const return_value, 162 char* const destination, 163 size_t const destination_count, 164 wchar_t const wchar 165 ) 166{ 167 __crt_cached_ptd_host ptd; 168 return _wctomb_internal(return_value, destination, destination_count, wchar, ptd); 169} 170 171extern "C" int __cdecl _wctomb_l( 172 char* const destination, 173 wchar_t const wchar, 174 _locale_t const locale 175 ) 176{ 177 __crt_cached_ptd_host ptd(locale); 178 179 int return_value{}; 180 errno_t const e = _wctomb_internal( 181 &return_value, 182 destination, 183 ptd.get_locale()->locinfo->_public._locale_mb_cur_max, 184 wchar, 185 ptd); 186 187 if (e != 0) 188 { 189 return -1; 190 } 191 192 return return_value; 193} 194 195// Disable the OACR error that warns that 'MB_CUR_MAX' doesn't properly constrain buffer 'destination'. 196// wctomb() doesn't take a buffer size, so the function's contract is inherently dangerous. 197__pragma(warning(push)) 198__pragma(warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY)) // 26015 199 200extern "C" int __cdecl wctomb( 201 char* const destination, 202 wchar_t const wchar 203 ) 204{ 205 __crt_cached_ptd_host ptd; 206 207 int return_value{}; 208 errno_t const e = _wctomb_internal(&return_value, destination, MB_CUR_MAX, wchar, ptd); 209 if (e != 0) 210 { 211 return -1; 212 } 213 214 return return_value; 215} 216 217__pragma(warning(pop))