|
3 | 3 |
|
4 | 4 | /** |
5 | 5 | * @file |
6 | | - * @note |
7 | | - * char32_t implementation with c32rtomb() could be more compatible since it |
8 | | - * should be supported since C11/C++11 but its implementation is unfortuntately |
9 | | - * missing from macOS (up to including 26) and the pass-through workaround won't |
10 | | - * work with UTF-32 data. |
| 6 | + * helper function around wcsrtombs() to handle conversion failures |
| 7 | + * (fallback) or where is the MBS output may be unsupported (eg. in |
| 8 | + * Windows if neither in Terminal nor in MSYS2 window). |
11 | 9 | */ |
12 | 10 |
|
13 | 11 | #include "utils/unicode.h" |
14 | 12 |
|
15 | | -#ifdef HAVE_CONFIG_H |
16 | | -#include "config.h" // for HAVE_C8RTOMB |
17 | | -#endif |
18 | | - |
19 | | -#include <assert.h> // for assert |
20 | | -#include <limits.h> // for INT_MAX, PATH_MAX |
21 | 13 | #include <locale.h> // for setlocale |
22 | | -#include <string.h> // for memcpy, strlen, strpbrk |
23 | | -#ifdef HAVE_C8RTOMB |
24 | | -#include <uchar.h> // for c8rtomb |
| 14 | +#include <string.h> // for memcpy, strlen, strpbrk, strstr |
25 | 15 | #include <wchar.h> // for mbstate_t |
26 | | -#endif |
27 | 16 |
|
28 | 17 | #include "compat/c23.h" // IWYU pragma: keep |
29 | 18 | #include "debug.h" |
30 | 19 |
|
31 | 20 | #define MOD_NAME "[utils/unicode] " |
32 | 21 |
|
33 | | -static bool utf8_terminal = false; |
| 22 | +#ifdef _WIN32 |
| 23 | +static bool win_utf8_terminal = false; |
| 24 | +#endif |
| 25 | + |
34 | 26 | void |
35 | 27 | u8_out_init(bool is_win_utf8_terminal) |
36 | 28 | { |
37 | 29 | #ifdef _WIN32 |
38 | | - utf8_terminal = is_win_utf8_terminal; |
| 30 | + win_utf8_terminal = is_win_utf8_terminal; |
| 31 | + // this is necessary for wcsrtombs work with 2 wchar_t character |
| 32 | + // conversion (eg. W_LARGE_RED_CIRCLE) |
| 33 | + const char *lc_ctype = setlocale(LC_CTYPE, ".UTF8"); |
39 | 34 | #else |
| 35 | + (void) is_win_utf8_terminal; |
40 | 36 | const char *lc_ctype = setlocale(LC_CTYPE, ""); |
| 37 | +#endif |
41 | 38 | if (lc_ctype == nullptr) { |
42 | 39 | MSG(WARNING, "Cannot set locale."); |
43 | 40 | } else { |
44 | 41 | MSG(DEBUG, "LC_CTYPE set to: %s\n", lc_ctype); |
45 | | - utf8_terminal = strstr(lc_ctype, ".UTF-8") != nullptr; |
46 | 42 | } |
47 | | - (void) is_win_utf8_terminal; |
48 | | -#endif |
49 | 43 | } |
50 | 44 |
|
51 | 45 | /** |
52 | | - * Tries to convert utf-8 string to locale-specific multibyte string. If |
53 | | - * c8rtomb not present and UTF-8 terminal detected, copies the UTF-8 string. |
54 | | - * Otherwise, out_fallback is kept untouched (should contain fallback text). |
| 46 | + * Tries to convert wide string to locale-specific multibyte string. If |
| 47 | + * conversion fails out_fallback is returned untouched (should contain fallback |
| 48 | + * text). |
55 | 49 | * |
56 | 50 | * @param buflen out_fallback buffer length long enough to hold the converted |
57 | | - string with some headroom (MB_LEN_MAX-1) |
| 51 | + string |
58 | 52 | * @param[in,out] out_fallback NUL-terminated fallback string. If conversion |
59 | 53 | * succeeds, it is rewritten by the u8_str converted to MBS |
60 | | - * @returns out_fallback with converted data if we have c8rtomb |
61 | | - * @returns u8_str if it is safe to pass-through |
62 | | - * @returns out_fallback unchanged if u8_str not convertible and terminal not in |
63 | | - UTF-8 |
| 54 | + * @returns out_fallback with converted data if conversion suceeds |
| 55 | + * @returns out_fallback unchanged if wstr not convertible |
64 | 56 | * |
65 | | - * u8_to_mb_init() must be called otherwise fallback is always ret |
| 57 | + * u8_out_init() must be called otherwise fallback is always ret |
66 | 58 | */ |
67 | 59 | const char * |
68 | | -u8s_to_mbs_buf(const unsigned char *u8_str, size_t buflen, char *out_fallback) |
| 60 | +wcs_to_mbs_buf(const wchar_t *wstr, size_t buflen, char *out_fallback) |
69 | 61 | { |
70 | | -#if defined HAVE_C8RTOMB && !defined _WIN32 |
71 | | - mbstate_t ps = { 0 }; |
72 | | - const char8_t *in_ptr = u8_str; |
73 | | - // check convertibility first |
74 | | - do { |
75 | | - char discard[MB_LEN_MAX]; |
76 | | - size_t ret = c8rtomb(discard, *in_ptr, &ps); |
77 | | - if (ret == (size_t) -1) { // not convertible |
78 | | - return out_fallback; |
79 | | - } |
80 | | - } while (*in_ptr++ != '\0'); |
81 | | - // actual conversion |
82 | | - in_ptr = u8_str; |
83 | | - char *out_ptr = out_fallback; |
84 | | - do { |
85 | | - if (buflen < MB_LEN_MAX || buflen == 1) { |
86 | | - MSG(WARNING, "utf-8 string truncated.\n"); |
87 | | - assert(buflen >= 1); |
88 | | - *out_ptr = '\0'; |
89 | | - break; |
90 | | - } |
91 | | - size_t ret = c8rtomb(out_ptr, *in_ptr, &ps); |
92 | | - assert(ret != (size_t) -1); |
93 | | - out_ptr += ret; |
94 | | - buflen -= ret; |
95 | | - } while (*in_ptr++ != '\0'); |
96 | | - return out_fallback; |
97 | | -#else |
98 | | - (void) buflen; |
99 | | - if (!utf8_terminal) { |
| 62 | +#ifdef _WIN32 |
| 63 | + if (!win_utf8_terminal) { |
100 | 64 | return out_fallback; |
101 | 65 | } |
102 | | - return (const char *) u8_str; |
103 | 66 | #endif |
| 67 | + |
| 68 | + mbstate_t ps = { 0 }; |
| 69 | + |
| 70 | + // check convertibility first |
| 71 | + size_t ret = wcsrtombs(nullptr, &wstr, 0, &ps); |
| 72 | + if (ret == (size_t) -1) { // not convertible |
| 73 | + return out_fallback; |
| 74 | + } |
| 75 | + |
| 76 | + // actual conversion |
| 77 | + (void) wcsrtombs(out_fallback, &wstr, buflen, &ps); |
| 78 | + if (wstr != nullptr) { |
| 79 | + MSG(WARNING, "%s: wide string truncated.\n", __func__); |
| 80 | + } |
| 81 | + return out_fallback; |
104 | 82 | } |
0 commit comments