Reactos
at master 417 lines 13 kB view raw
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)) 39static 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)) 83static 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 281extern "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 292extern "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 327static 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 394extern "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 407extern "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}