1 /***
2 *wcstombs.c - Convert wide char string to multibyte char string.
3 *
4 * Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 * Convert a wide char string into the equivalent multibyte char string.
8 *
9 *******************************************************************************/
10 #include <corecrt_internal_mbstring.h>
11 #include <corecrt_internal_ptd_propagation.h>
12 #include <corecrt_internal_securecrt.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <locale.h>
16 #include <stdint.h>
17 #include <stdlib.h>
18
19 /***
20 *size_t __cdecl wcsncnt - count wide characters in a string, up to n.
21 *
22 *Purpose:
23 * Internal local support function. Counts characters in string including nullptr.
24 * If nullptr not found in n chars, then return n.
25 *
26 *Entry:
27 * const wchar_t *string - start of string
28 * size_t n - character count
29 *
30 *Exit:
31 * returns number of wide characters from start of string to
32 * nullptr (inclusive), up to n.
33 *
34 *Exceptions:
35 *
36 *******************************************************************************/
37
38 _Post_satisfies_(return <= cnt && return <= _String_length_(string + 1))
wcsncnt(_In_reads_or_z_ (cnt)const wchar_t * string,_In_ size_t cnt)39 static size_t __cdecl wcsncnt (
40 _In_reads_or_z_(cnt) const wchar_t *string,
41 _In_ size_t cnt
42 )
43 {
44 size_t n = cnt+1;
45 wchar_t *cp = (wchar_t *)string;
46
47 while (--n && *cp)
48 cp++;
49
50 if (n && !*cp)
51 return cp - string + 1;
52 return cnt;
53 }
54
55 /***
56 *size_t wcstombs() - Convert wide char string to multibyte char string.
57 *
58 *Purpose:
59 * Convert a wide char string into the equivalent multibyte char string,
60 * according to the LC_CTYPE category of the current locale.
61 * [ANSI].
62 *
63 * The destination string is null terminated only if the null terminator
64 * is copied from the source string.
65 *
66 *Entry:
67 * char *s = pointer to destination multibyte char string
68 * const wchar_t *pwc = pointer to source wide character string
69 * size_t n = maximum number of bytes to store in s
70 *
71 *Exit:
72 * If s != nullptr, returns (size_t)-1 (if a wchar cannot be converted)
73 * Otherwise: Number of bytes modified (<=n), not including
74 * the terminating NUL, if any.
75 *
76 *Exceptions:
77 * Returns (size_t)-1 if an error is encountered.
78 * Input parameters are validated. Refer to the validation section of the function.
79 *
80 *******************************************************************************/
81
82 _Success_(return != static_cast<size_t>(-1))
_wcstombs_l_helper(_Out_writes_ (n)char * s,_In_z_ const wchar_t * pwcs,_In_ size_t n,_Inout_ __crt_cached_ptd_host & ptd)83 static size_t __cdecl _wcstombs_l_helper(
84 _Out_writes_(n) char * s,
85 _In_z_ const wchar_t * pwcs,
86 _In_ size_t n,
87 _Inout_ __crt_cached_ptd_host& ptd
88 )
89 {
90 size_t count = 0;
91 int i, retval;
92 char buffer[MB_LEN_MAX];
93 BOOL defused = 0;
94
95 if (s && n == 0)
96 /* dest string exists, but 0 bytes converted */
97 return 0;
98
99 /* validation section */
100 _UCRT_VALIDATE_RETURN(ptd, pwcs != nullptr, EINVAL, (size_t)-1);
101
102
103 /* if destination string exists, fill it in */
104 const _locale_t locale = ptd.get_locale();
105
106 if (locale->locinfo->_public._locale_lc_codepage == CP_UTF8)
107 {
108 mbstate_t state{};
109 return __crt_mbstring::__wcsrtombs_utf8(s, &pwcs, n, &state, ptd);
110 }
111
112 if (s)
113 {
114 if ( locale->locinfo->locale_name[LC_CTYPE] == nullptr )
115 {
116 /* C locale: easy and fast */
117 /* Actually, there are such wchar_t characters which are > 255,
118 * but they can be transformed to a valid single byte char
119 * (i.e. a char in the C locale case). Like, for example,
120 * alternative digits in unicode like Arabic-Indic U+0660..U+0669.
121 * The problem is that WideCharToMultiByte() does not translate those
122 * wchar_t unless we pass the correct codepage (1256, Arabic).
123 * See bug VSW:192653.
124 */
125 while(count < n)
126 {
127 if (static_cast<uint16_t>(*pwcs) > 0xFF) /* validate high byte */
128 {
129 ptd.get_errno().set(EILSEQ);
130 return (size_t)-1; /* error */
131 }
132 s[count] = (char) *pwcs;
133 if (*pwcs++ == L'\0')
134 {
135 return count;
136 }
137 count++;
138 }
139 return count;
140 }
141 else
142 {
143
144 if (1 == locale->locinfo->_public._locale_mb_cur_max)
145 {
146 /* If SBCS, one wchar_t maps to one char */
147
148 /* WideCharToMultiByte will compare past nullptr - reset n */
149 if (n > 0)
150 {
151 n = wcsncnt(pwcs, n);
152 }
153 if ( ((count = __acrt_WideCharToMultiByte( locale->locinfo->_public._locale_lc_codepage,
154 0,
155 pwcs,
156 (int)n,
157 s,
158 (int)n,
159 nullptr,
160 &defused )) != 0) &&
161 (!defused) )
162 {
163 if (s[count - 1] == '\0')
164 {
165 count--; /* don't count NUL */
166 }
167
168 return count;
169 }
170
171 ptd.get_errno().set(EILSEQ);
172 return (size_t)-1;
173 }
174 else
175 {
176
177 /* If MBCS, wchar_t to char mapping unknown */
178
179 /* Assume that usually the buffer is large enough */
180 if ( ((count = __acrt_WideCharToMultiByte( locale->locinfo->_public._locale_lc_codepage,
181 0,
182 pwcs,
183 -1,
184 s,
185 (int)n,
186 nullptr,
187 &defused )) != 0) &&
188 (!defused) )
189 {
190 return count - 1; /* don't count NUL */
191 }
192
193 if (defused || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
194 {
195 ptd.get_errno().set(EILSEQ);
196 return (size_t)-1;
197 }
198
199 /* buffer not large enough, must do char by char */
200 while (count < n)
201 {
202 int mb_cur_max = locale->locinfo->_public._locale_mb_cur_max;
203 if ( ((retval = __acrt_WideCharToMultiByte( locale->locinfo->_public._locale_lc_codepage,
204 0,
205 pwcs,
206 1,
207 buffer,
208 __min(MB_LEN_MAX, mb_cur_max),
209 nullptr,
210 &defused )) == 0)
211 || defused )
212 {
213 ptd.get_errno().set(EILSEQ);
214 return (size_t)-1;
215 }
216
217 /* enforce this for prefast */
218 if (retval < 0 ||
219 retval > _countof(buffer))
220 {
221 ptd.get_errno().set(EILSEQ);
222 return (size_t)-1;
223 }
224
225 if (count + retval > n)
226 return count;
227
228 for (i = 0; i < retval; i++, count++) /* store character */
229 {
230 if((s[count] = buffer[i])=='\0')
231 {
232 return count;
233 }
234 }
235
236 pwcs++;
237 }
238
239 return count;
240 }
241 }
242 }
243 else
244 { /* s == nullptr, get size only, pwcs must be NUL-terminated */
245 if ( locale->locinfo->locale_name[LC_CTYPE] == nullptr )
246 {
247 size_t len = 0;
248 for (wchar_t *pw = (wchar_t *)pwcs; *pw != 0; pw++) /* validate high byte */
249 {
250 if (*pw > 255) /* validate high byte */
251 {
252 ptd.get_errno().set(EILSEQ);
253 return (size_t)-1; /* error */
254 }
255 ++len;
256 }
257
258 return len;
259 }
260 else
261 {
262 if ( ((count = __acrt_WideCharToMultiByte( locale->locinfo->_public._locale_lc_codepage,
263 0,
264 pwcs,
265 -1,
266 nullptr,
267 0,
268 nullptr,
269 &defused )) == 0) ||
270 (defused) )
271 {
272 ptd.get_errno().set(EILSEQ);
273 return (size_t)-1;
274 }
275
276 return count - 1;
277 }
278 }
279 }
280
_wcstombs_l(char * s,const wchar_t * pwcs,size_t n,_locale_t plocinfo)281 extern "C" size_t __cdecl _wcstombs_l(
282 char * s,
283 const wchar_t * pwcs,
284 size_t n,
285 _locale_t plocinfo
286 )
287 {
288 __crt_cached_ptd_host ptd(plocinfo);
289 return _wcstombs_l_helper(s, pwcs, n, ptd);
290 }
291
wcstombs(char * s,const wchar_t * pwcs,size_t n)292 extern "C" size_t __cdecl wcstombs(
293 char * s,
294 const wchar_t * pwcs,
295 size_t n
296 )
297 {
298 __crt_cached_ptd_host ptd;
299 return _wcstombs_l_helper(s, pwcs, n, ptd);
300 }
301
302 /***
303 *errno_t wcstombs_s() - Convert wide char string to multibyte char string.
304 *
305 *Purpose:
306 * Convert a wide char string into the equivalent multibyte char string,
307 * according to the LC_CTYPE category of the current locale.
308 *
309 * The destination string is always null terminated.
310 *
311 *Entry:
312 * size_t *pConvertedChars = Number of bytes modified including the terminating nullptr
313 * This pointer can be nullptr.
314 * char *dst = pointer to destination multibyte char string
315 * size_t sizeInBytes = size of the destination buffer
316 * const wchar_t *src = pointer to source wide character string
317 * size_t n = maximum number of bytes to store in s (not including the terminating nullptr)
318 *
319 *Exit:
320 * The error code.
321 *
322 *Exceptions:
323 * Input parameters are validated. Refer to the validation section of the function.
324 *
325 *******************************************************************************/
326
_wcstombs_internal(size_t * pConvertedChars,char * dst,size_t sizeInBytes,const wchar_t * src,size_t n,__crt_cached_ptd_host & ptd)327 static errno_t __cdecl _wcstombs_internal (
328 size_t * pConvertedChars,
329 char * dst,
330 size_t sizeInBytes,
331 const wchar_t * src,
332 size_t n,
333 __crt_cached_ptd_host& ptd
334 )
335 {
336 size_t retsize;
337 errno_t retvalue = 0;
338
339 /* validation section */
340 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, (dst != nullptr && sizeInBytes > 0) || (dst == nullptr && sizeInBytes == 0), EINVAL);
341 if (dst != nullptr)
342 {
343 _RESET_STRING(dst, sizeInBytes);
344 }
345
346 if (pConvertedChars != nullptr)
347 {
348 *pConvertedChars = 0;
349 }
350
351 size_t bufferSize = n > sizeInBytes ? sizeInBytes : n;
352 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, bufferSize <= INT_MAX, EINVAL);
353
354 retsize = _wcstombs_l_helper(dst, src, bufferSize, ptd);
355
356 if (retsize == (size_t)-1)
357 {
358 if (dst != nullptr)
359 {
360 _RESET_STRING(dst, sizeInBytes);
361 }
362 return ptd.get_errno().value_or(0);
363 }
364
365 /* count the null terminator */
366 retsize++;
367
368 if (dst != nullptr)
369 {
370 /* return error if the string does not fit, unless n == _TRUNCATE */
371 if (retsize > sizeInBytes)
372 {
373 if (n != _TRUNCATE)
374 {
375 _RESET_STRING(dst, sizeInBytes);
376 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, sizeInBytes > retsize, ERANGE);
377 }
378 retsize = sizeInBytes;
379 retvalue = STRUNCATE;
380 }
381
382 /* ensure the string is null terminated */
383 dst[retsize - 1] = '\0';
384 }
385
386 if (pConvertedChars != nullptr)
387 {
388 *pConvertedChars = retsize;
389 }
390
391 return retvalue;
392 }
393
_wcstombs_s_l(size_t * pConvertedChars,char * dst,size_t sizeInBytes,const wchar_t * src,size_t n,_locale_t plocinfo)394 extern "C" errno_t __cdecl _wcstombs_s_l (
395 size_t *pConvertedChars,
396 char * dst,
397 size_t sizeInBytes,
398 const wchar_t * src,
399 size_t n,
400 _locale_t plocinfo
401 )
402 {
403 __crt_cached_ptd_host ptd(plocinfo);
404 return _wcstombs_internal(pConvertedChars, dst, sizeInBytes, src, n, ptd);
405 }
406
wcstombs_s(size_t * pConvertedChars,char * dst,size_t sizeInBytes,const wchar_t * src,size_t n)407 extern "C" errno_t __cdecl wcstombs_s (
408 size_t *pConvertedChars,
409 char * dst,
410 size_t sizeInBytes,
411 const wchar_t * src,
412 size_t n
413 )
414 {
415 __crt_cached_ptd_host ptd;
416 return _wcstombs_internal(pConvertedChars, dst, sizeInBytes, src, n, ptd);
417 }
418