Line | Count | Source |
1 | | /* Convert 32-bit wide character to multibyte character. |
2 | | Copyright (C) 2020-2026 Free Software Foundation, Inc. |
3 | | |
4 | | This file is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU Lesser General Public License as |
6 | | published by the Free Software Foundation; either version 2.1 of the |
7 | | License, or (at your option) any later version. |
8 | | |
9 | | This file is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU Lesser General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU Lesser General Public License |
15 | | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
16 | | |
17 | | /* Written by Bruno Haible <bruno@clisp.org>, 2020. */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | /* Specification. */ |
22 | | #include <uchar.h> |
23 | | |
24 | | #include <errno.h> |
25 | | #include <wchar.h> |
26 | | |
27 | | #include "attribute.h" /* FALLTHROUGH */ |
28 | | #include "localcharset.h" |
29 | | #include "streq-opt.h" |
30 | | |
31 | | #if GL_CHAR32_T_IS_UNICODE |
32 | | # include "lc-charset-unicode.h" |
33 | | #endif |
34 | | |
35 | | #if (MBRTOC32_IN_C_LOCALE_MAYBE_LIKE_ISO_8859 \ |
36 | | || MBRTOC32_IN_C_LOCALE_MAYBE_EILSEQ \ |
37 | | || (HAVE_WORKING_MBRTOC32 && HAVE_WORKING_C32RTOMB && !_GL_WCHAR_T_IS_UCS4) \ |
38 | | || _GL_SMALL_WCHAR_T \ |
39 | | || (GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION)) |
40 | | # include "hard-locale.h" |
41 | | # include <locale.h> |
42 | | #endif |
43 | | |
44 | | size_t |
45 | | c32rtomb (char *s, char32_t wc, mbstate_t *ps) |
46 | | #undef c32rtomb |
47 | 0 | { |
48 | 0 | #if !GNULIB_defined_mbstate_t && HAVE_WORKING_MBRTOC32 && HAVE_WORKING_C32RTOMB |
49 | |
|
50 | | # if C32RTOMB_RETVAL_BUG |
51 | | if (s == NULL) |
52 | | /* We know the NUL wide character corresponds to the NUL character. */ |
53 | | return 1; |
54 | | # endif |
55 | |
|
56 | 0 | # if MBRTOC32_IN_C_LOCALE_MAYBE_LIKE_ISO_8859 /* OpenBSD */ \ |
57 | | || MBRTOC32_IN_C_LOCALE_MAYBE_EILSEQ /* glibc */ \ |
58 | | || !_GL_WCHAR_T_IS_UCS4 /* NetBSD ≥ 11 */ |
59 | 0 | if ((wc >= 0xDF80 && wc <= 0xDFFF) && !hard_locale (LC_CTYPE)) |
60 | 0 | { |
61 | | /* In the "C" locale, map the code points U+DF80..U+DFFF back to the bytes |
62 | | 0x80..0xFF, for consistency with the mbrtoc32 and btoc32 functions. */ |
63 | 0 | s[0] = (unsigned char) (wc - 0xDF00); |
64 | 0 | return 1; |
65 | 0 | } |
66 | 0 | # endif |
67 | | |
68 | 0 | return c32rtomb (s, wc, ps); |
69 | |
|
70 | | #elif !GNULIB_defined_mbstate_t && _GL_SMALL_WCHAR_T /* Cygwin, mingw, MSVC */ |
71 | | |
72 | | if (s == NULL) |
73 | | return wcrtomb (NULL, 0, ps); |
74 | | else |
75 | | { |
76 | | /* Special-case all encodings that may produce wide character values |
77 | | > WCHAR_MAX. */ |
78 | | const char *encoding = locale_charset (); |
79 | | if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0)) |
80 | | { |
81 | | /* Special-case the UTF-8 encoding. Assume that the wide-character |
82 | | encoding in a UTF-8 locale is UCS-2 or, equivalently, UTF-16. */ |
83 | | if (wc < 0x80) |
84 | | { |
85 | | s[0] = (unsigned char) wc; |
86 | | return 1; |
87 | | } |
88 | | else |
89 | | { |
90 | | int count; |
91 | | |
92 | | if (wc < 0x800) |
93 | | count = 2; |
94 | | else if (wc < 0x10000) |
95 | | { |
96 | | if (wc < 0xd800 || wc >= 0xe000) |
97 | | count = 3; |
98 | | else |
99 | | { |
100 | | errno = EILSEQ; |
101 | | return (size_t)(-1); |
102 | | } |
103 | | } |
104 | | else if (wc < 0x110000) |
105 | | count = 4; |
106 | | else |
107 | | { |
108 | | errno = EILSEQ; |
109 | | return (size_t)(-1); |
110 | | } |
111 | | |
112 | | switch (count) /* note: code falls through cases! */ |
113 | | { |
114 | | case 4: s[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000; |
115 | | FALLTHROUGH; |
116 | | case 3: s[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800; |
117 | | FALLTHROUGH; |
118 | | case 2: s[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0; |
119 | | /*case 1:*/ s[0] = wc; |
120 | | } |
121 | | return count; |
122 | | } |
123 | | } |
124 | | else if (!hard_locale (LC_CTYPE)) |
125 | | { |
126 | | /* In the "C" locale, map the code points U+DF80..U+DFFF back |
127 | | to the bytes 0x80..0xFF, for consistency with the mbrtoc32 and |
128 | | btoc32 functions. */ |
129 | | if (wc >= 0x00 && wc <= 0x7F) |
130 | | { |
131 | | s[0] = (unsigned char) wc; |
132 | | return 1; |
133 | | } |
134 | | else if (wc >= 0xDF80 && wc <= 0xDFFF) |
135 | | { |
136 | | s[0] = (unsigned char) (wc - 0xDF00); |
137 | | return 1; |
138 | | } |
139 | | else |
140 | | { |
141 | | errno = EILSEQ; |
142 | | return (size_t)(-1); |
143 | | } |
144 | | } |
145 | | else |
146 | | { |
147 | | if ((wchar_t) wc == wc) |
148 | | return wcrtomb (s, (wchar_t) wc, ps); |
149 | | else |
150 | | { |
151 | | errno = EILSEQ; |
152 | | return (size_t)(-1); |
153 | | } |
154 | | } |
155 | | } |
156 | | |
157 | | #else |
158 | | |
159 | | /* char32_t and wchar_t are equivalent. */ |
160 | | # if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION |
161 | | if ((wc >= 0xDF80 && wc <= 0xDFFF) && !hard_locale (LC_CTYPE)) |
162 | | { |
163 | | /* In the "C" locale, map the code points U+DF80..U+DFFF back to the bytes |
164 | | 0x80..0xFF, for consistency with the mbrtoc32 and btoc32 functions. */ |
165 | | s[0] = (unsigned char) (wc - 0xDF00); |
166 | | return 1; |
167 | | } |
168 | | if (wc != 0) |
169 | | { |
170 | | wc = unicode_to_locale_encoding (wc); |
171 | | if (wc == 0) |
172 | | { |
173 | | errno = EILSEQ; |
174 | | return (size_t)(-1); |
175 | | } |
176 | | } |
177 | | # endif |
178 | | return wcrtomb (s, (wchar_t) wc, ps); |
179 | | |
180 | | #endif |
181 | 0 | } |