FreeRDP
Loading...
Searching...
No Matches
unicode.c
1
22#include <winpr/config.h>
23#include <winpr/assert.h>
24
25#include <errno.h>
26#include <wctype.h>
27
28#include <winpr/crt.h>
29#include <winpr/error.h>
30#include <winpr/print.h>
31#include <winpr/endian.h>
32
33#ifndef _WIN32
34
35#include "unicode.h"
36
150#if !defined(WITH_WINPR_DEPRECATED)
151static
152#endif
153 int
154 MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
155 LPWSTR lpWideCharStr, int cchWideChar)
156{
157 return int_MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr,
158 cchWideChar);
159}
160
201#if !defined(WITH_WINPR_DEPRECATED)
202static
203#endif
204 int
205 WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
206 LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
207 LPBOOL lpUsedDefaultChar)
208{
209 return int_WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr,
210 cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
211}
212
213#endif
214
227#if defined(WITH_WINPR_DEPRECATED)
228int ConvertToUnicode(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
229 LPWSTR* lpWideCharStr, int cchWideChar)
230{
231 int status = 0;
232 BOOL allocate = FALSE;
233
234 if (!lpMultiByteStr)
235 return 0;
236
237 if (!lpWideCharStr)
238 return 0;
239
240 if (cbMultiByte == -1)
241 {
242 size_t len = strnlen(lpMultiByteStr, INT_MAX);
243 if (len >= INT_MAX)
244 return 0;
245 cbMultiByte = (int)(len + 1);
246 }
247
248 if (cchWideChar == 0)
249 {
250 cchWideChar =
251 MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, nullptr, 0);
252 allocate = TRUE;
253 }
254 else if (!(*lpWideCharStr))
255 allocate = TRUE;
256
257 if (cchWideChar < 1)
258 return 0;
259
260 if (allocate)
261 {
262 *lpWideCharStr = (LPWSTR)calloc((size_t)cchWideChar + 1ull, sizeof(WCHAR));
263
264 if (!(*lpWideCharStr))
265 {
266 // SetLastError(ERROR_INSUFFICIENT_BUFFER);
267 return 0;
268 }
269 }
270
271 status = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, *lpWideCharStr,
272 cchWideChar);
273
274 if (status != cchWideChar)
275 {
276 if (allocate)
277 {
278 free(*lpWideCharStr);
279 *lpWideCharStr = nullptr;
280 status = 0;
281 }
282 }
283
284 return status;
285}
286#endif
287
300#if defined(WITH_WINPR_DEPRECATED)
301int ConvertFromUnicode(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
302 LPSTR* lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
303 LPBOOL lpUsedDefaultChar)
304{
305 int status = 0;
306 BOOL allocate = FALSE;
307
308 if (!lpWideCharStr)
309 return 0;
310
311 if (!lpMultiByteStr)
312 return 0;
313
314 if (cchWideChar == -1)
315 cchWideChar = (int)(_wcslen(lpWideCharStr) + 1);
316
317 if (cbMultiByte == 0)
318 {
319 cbMultiByte = WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, nullptr, 0,
320 nullptr, nullptr);
321 allocate = TRUE;
322 }
323 else if (!(*lpMultiByteStr))
324 allocate = TRUE;
325
326 if (cbMultiByte < 1)
327 return 0;
328
329 if (allocate)
330 {
331 *lpMultiByteStr = (LPSTR)calloc(1, (size_t)cbMultiByte + 1ull);
332
333 if (!(*lpMultiByteStr))
334 {
335 // SetLastError(ERROR_INSUFFICIENT_BUFFER);
336 return 0;
337 }
338 }
339
340 status = WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, *lpMultiByteStr,
341 cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
342
343 if ((status != cbMultiByte) && allocate)
344 {
345 status = 0;
346 }
347
348 if ((status <= 0) && allocate)
349 {
350 free(*lpMultiByteStr);
351 *lpMultiByteStr = nullptr;
352 }
353
354 return status;
355}
356#endif
357
362const WCHAR* ByteSwapUnicode(WCHAR* wstr, size_t length)
363{
364 WINPR_ASSERT(wstr || (length == 0));
365
366 for (size_t x = 0; x < length; x++)
367 wstr[x] = _byteswap_ushort(wstr[x]);
368 return wstr;
369}
370
371SSIZE_T ConvertWCharToUtf8(const WCHAR* wstr, char* str, size_t len)
372{
373 if (!wstr)
374 {
375 if (str && len)
376 str[0] = 0;
377 return 0;
378 }
379
380 const size_t wlen = _wcslen(wstr);
381 return ConvertWCharNToUtf8(wstr, wlen + 1, str, len);
382}
383
384SSIZE_T ConvertWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len)
385{
386 BOOL isNullTerminated = FALSE;
387 if (wlen == 0)
388 return 0;
389
390 WINPR_ASSERT(wstr);
391 size_t iwlen = _wcsnlen(wstr, wlen);
392
393 if ((len > INT32_MAX) || (wlen > INT32_MAX))
394 {
395 SetLastError(ERROR_INVALID_PARAMETER);
396 return -1;
397 }
398
399 if (iwlen < wlen)
400 {
401 isNullTerminated = TRUE;
402 iwlen++;
403 }
404 WINPR_PRAGMA_DIAG_PUSH
405 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
406 const int rc =
407 WideCharToMultiByte(CP_UTF8, 0, wstr, (int)iwlen, str, (int)len, nullptr, nullptr);
408 WINPR_PRAGMA_DIAG_POP
409 if ((rc <= 0) || ((len > 0) && ((size_t)rc > len)))
410 return -1;
411 else if (!isNullTerminated)
412 {
413 if (str && ((size_t)rc < len))
414 str[rc] = '\0';
415 return rc;
416 }
417 else if ((size_t)rc == len)
418 {
419 if (str && (str[rc - 1] != '\0'))
420 return rc;
421 }
422 return rc - 1;
423}
424
425SSIZE_T ConvertMszWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len)
426{
427 if (wlen == 0)
428 return 0;
429
430 WINPR_ASSERT(wstr);
431
432 if ((len > INT32_MAX) || (wlen > INT32_MAX))
433 {
434 SetLastError(ERROR_INVALID_PARAMETER);
435 return -1;
436 }
437
438 const int iwlen = (int)len;
439 WINPR_PRAGMA_DIAG_PUSH
440 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
441 const int rc = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)wlen, str, iwlen, nullptr, nullptr);
442 WINPR_PRAGMA_DIAG_POP
443 if ((rc <= 0) || ((len > 0) && (rc > iwlen)))
444 return -1;
445
446 return rc;
447}
448
449SSIZE_T ConvertUtf8ToWChar(const char* str, WCHAR* wstr, size_t wlen)
450{
451 if (!str)
452 {
453 if (wstr && wlen)
454 wstr[0] = 0;
455 return 0;
456 }
457
458 const size_t len = strlen(str);
459 return ConvertUtf8NToWChar(str, len + 1, wstr, wlen);
460}
461
462SSIZE_T ConvertUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen)
463{
464 size_t ilen = strnlen(str, len);
465 BOOL isNullTerminated = FALSE;
466 if (len == 0)
467 return 0;
468
469 WINPR_ASSERT(str);
470
471 if ((len > INT32_MAX) || (wlen > INT32_MAX))
472 {
473 SetLastError(ERROR_INVALID_PARAMETER);
474 return -1;
475 }
476 if (ilen < len)
477 {
478 isNullTerminated = TRUE;
479 ilen++;
480 }
481
482 const int iwlen = (int)wlen;
483 WINPR_PRAGMA_DIAG_PUSH
484 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
485 const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)ilen, wstr, iwlen);
486 WINPR_PRAGMA_DIAG_POP
487 if ((rc <= 0) || ((wlen > 0) && (rc > iwlen)))
488 return -1;
489 if (!isNullTerminated)
490 {
491 if (wstr && (rc < iwlen))
492 wstr[rc] = '\0';
493 return rc;
494 }
495 else if (rc == iwlen)
496 {
497 if (wstr && (wstr[rc - 1] != '\0'))
498 return rc;
499 }
500 return rc - 1;
501}
502
503SSIZE_T ConvertMszUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen)
504{
505 if (len == 0)
506 return 0;
507
508 WINPR_ASSERT(str);
509
510 if ((len > INT32_MAX) || (wlen > INT32_MAX))
511 {
512 SetLastError(ERROR_INVALID_PARAMETER);
513 return -1;
514 }
515
516 const int iwlen = (int)wlen;
517 WINPR_PRAGMA_DIAG_PUSH
518 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
519 const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)len, wstr, iwlen);
520 WINPR_PRAGMA_DIAG_POP
521 if ((rc <= 0) || ((wlen > 0) && (rc > iwlen)))
522 return -1;
523
524 return rc;
525}
526
527char* ConvertWCharToUtf8Alloc(const WCHAR* wstr, size_t* pUtfCharLength)
528{
529 char* tmp = nullptr;
530 const SSIZE_T rc = ConvertWCharToUtf8(wstr, nullptr, 0);
531 if (pUtfCharLength)
532 *pUtfCharLength = 0;
533 if (rc < 0)
534 return nullptr;
535 tmp = calloc((size_t)rc + 1ull, sizeof(char));
536 if (!tmp)
537 return nullptr;
538 const SSIZE_T rc2 = ConvertWCharToUtf8(wstr, tmp, (size_t)rc + 1ull);
539 if (rc2 < 0)
540 {
541 free(tmp);
542 return nullptr;
543 }
544 WINPR_ASSERT(rc == rc2);
545 if (pUtfCharLength)
546 *pUtfCharLength = (size_t)rc2;
547 return tmp;
548}
549
550char* ConvertWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength)
551{
552 char* tmp = nullptr;
553 const SSIZE_T rc = ConvertWCharNToUtf8(wstr, wlen, nullptr, 0);
554
555 if (pUtfCharLength)
556 *pUtfCharLength = 0;
557 if (rc < 0)
558 return nullptr;
559 tmp = calloc((size_t)rc + 1ull, sizeof(char));
560 if (!tmp)
561 return nullptr;
562 const SSIZE_T rc2 = ConvertWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull);
563 if (rc2 < 0)
564 {
565 free(tmp);
566 return nullptr;
567 }
568 WINPR_ASSERT(rc == rc2);
569 if (pUtfCharLength)
570 *pUtfCharLength = (size_t)rc2;
571 return tmp;
572}
573
574char* ConvertMszWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength)
575{
576 char* tmp = nullptr;
577 const SSIZE_T rc = ConvertMszWCharNToUtf8(wstr, wlen, nullptr, 0);
578
579 if (pUtfCharLength)
580 *pUtfCharLength = 0;
581 if (rc < 0)
582 return nullptr;
583 tmp = calloc((size_t)rc + 1ull, sizeof(char));
584 if (!tmp)
585 return nullptr;
586 const SSIZE_T rc2 = ConvertMszWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull);
587 if (rc2 < 0)
588 {
589 free(tmp);
590 return nullptr;
591 }
592 WINPR_ASSERT(rc == rc2);
593 if (pUtfCharLength)
594 *pUtfCharLength = (size_t)rc2;
595 return tmp;
596}
597
598WCHAR* ConvertUtf8ToWCharAlloc(const char* str, size_t* pSize)
599{
600 WCHAR* tmp = nullptr;
601 const SSIZE_T rc = ConvertUtf8ToWChar(str, nullptr, 0);
602 if (pSize)
603 *pSize = 0;
604 if (rc < 0)
605 return nullptr;
606 tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
607 if (!tmp)
608 return nullptr;
609 const SSIZE_T rc2 = ConvertUtf8ToWChar(str, tmp, (size_t)rc + 1ull);
610 if (rc2 < 0)
611 {
612 free(tmp);
613 return nullptr;
614 }
615 WINPR_ASSERT(rc == rc2);
616 if (pSize)
617 *pSize = (size_t)rc2;
618 return tmp;
619}
620
621WCHAR* ConvertUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize)
622{
623 WCHAR* tmp = nullptr;
624 const SSIZE_T rc = ConvertUtf8NToWChar(str, len, nullptr, 0);
625 if (pSize)
626 *pSize = 0;
627 if (rc < 0)
628 return nullptr;
629 tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
630 if (!tmp)
631 return nullptr;
632 const SSIZE_T rc2 = ConvertUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull);
633 if (rc2 < 0)
634 {
635 free(tmp);
636 return nullptr;
637 }
638 WINPR_ASSERT(rc == rc2);
639 if (pSize)
640 *pSize = (size_t)rc2;
641 return tmp;
642}
643
644WCHAR* ConvertMszUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize)
645{
646 WCHAR* tmp = nullptr;
647 const SSIZE_T rc = ConvertMszUtf8NToWChar(str, len, nullptr, 0);
648 if (pSize)
649 *pSize = 0;
650 if (rc < 0)
651 return nullptr;
652 tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR));
653 if (!tmp)
654 return nullptr;
655 const SSIZE_T rc2 = ConvertMszUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull);
656 if (rc2 < 0)
657 {
658 free(tmp);
659 return nullptr;
660 }
661 WINPR_ASSERT(rc == rc2);
662 if (pSize)
663 *pSize = (size_t)rc2;
664 return tmp;
665}
666
667WINPR_ATTR_NODISCARD
668static INT8 nnibble(char c)
669{
670 if ((c >= '0') && (c <= '9'))
671 return (INT8)(c - '0');
672 if ((c >= 'a') && (c <= 'f'))
673 return (INT8)((c - 'a') + 0xa);
674 if ((c >= 'A') && (c <= 'F'))
675 return (INT8)((c - 'A') + 0xa);
676 return -1;
677}
678
679WINPR_ATTR_NODISCARD
680static INT32 escapeToNumber(char* str, char** next)
681{
682 if (strncmp(str, "\\u", 2) != 0)
683 return -1;
684
685 size_t offset = 2;
686 INT32 code = 0;
687 for (size_t x = 0; x < 4; x++)
688 {
689 const INT8 val = nnibble(str[offset++]);
690 if (val < 0)
691 return -1;
692
693 code <<= 4;
694 code |= val;
695 }
696 *next = &str[offset];
697 return code;
698}
699
700SSIZE_T winpr_utfEscapedStringToUtf8(char* str, size_t len)
701{
702 char* cur = str;
703 size_t clen = len;
704 while ((cur = winpr_strnstr(cur, "\\u", clen)))
705 {
706 char* next = nullptr;
707 const INT32 code = escapeToNumber(cur, &next);
708 if (code < 0)
709 {
710 /* Not a \uXXXX escape (e.g. "C:\users"): keep it as literal text */
711 cur += 2;
712 clen = len - (size_t)(cur - str);
713 continue;
714 }
715
716 WCHAR wc[2] = WINPR_C_ARRAY_INIT;
717 wc[0] = ((WCHAR)code);
718
719 // Check for high surrogate
720 if ((wc[0] & 0xd800) == 0xd800)
721 {
722 const INT32 code2 = escapeToNumber(next, &next);
723 if (code2 < 0)
724 return -1;
725
726 // low surrogate check. must match
727 if ((code2 & 0xdc00) != 0xdc00)
728 return -1;
729
730 wc[1] = (WCHAR)code2;
731 }
732
733 char utf8[8] = WINPR_C_ARRAY_INIT;
734 const SSIZE_T rc = ConvertWCharNToUtf8(wc, ARRAYSIZE(wc), utf8, ARRAYSIZE(utf8));
735 if (rc < 0)
736 return -1;
737
738 strncpy(cur, utf8, (size_t)rc);
739
740 const size_t diff = (size_t)(next - cur);
741 if (diff > (size_t)rc)
742 {
743 const size_t rlen = strnlen(next, len);
744 memmove(&cur[(size_t)rc], next, rlen + 1);
745 }
746
747 /* Continue after the decoded bytes, so they are never decoded again */
748 cur += rc;
749 clen = len - (size_t)(cur - str);
750 }
751 return (SSIZE_T)strnlen(str, len);
752}
753
754char* winpr_utf8ToUtfEscapedString(char* str, size_t len, size_t* pDstLen)
755{
756 WINPR_ASSERT(str || (len == 0));
757
758 if (pDstLen)
759 *pDstLen = 0;
760
761 if (len >= SIZE_MAX / 12)
762 return nullptr;
763
764 char* escaped = nullptr;
765 size_t offset = 0;
766 size_t wlen = 0;
767 WCHAR* wstr = ConvertUtf8NToWCharAlloc(str, len, &wlen);
768 if (!wstr || (wlen == 0))
769 goto fail;
770
771 const size_t esclen = len * 12ull;
772 escaped = calloc(len + 1, 12);
773 if (!escaped)
774 goto fail;
775
776 for (size_t x = 0; x < wlen; x++)
777 {
778 const WCHAR wc = winpr_Data_Get_UINT16(&wstr[x]);
779 if (wc < 0x80)
780 escaped[offset++] = (char)wc;
781 else
782 {
783 const int rc = snprintf(&escaped[offset], esclen - offset, "\\u%04x", wc);
784 if (rc < 0)
785 break;
786 offset += (size_t)rc;
787 }
788 }
789 escaped[offset] = '\0';
790
791fail:
792 if (pDstLen)
793 *pDstLen = offset;
794
795 winpr_znfree(wstr, wlen * sizeof(WCHAR));
796 return escaped;
797}