xref: /reactos/sdk/lib/ucrt/convert/wcstombs.cpp (revision 04e0dc4a)
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