Reactos
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))