Line | Count | Source |
1 | | /* Convert multibyte character to wide character. |
2 | | Copyright (C) 1999-2002, 2005-2026 Free Software Foundation, Inc. |
3 | | Written by Bruno Haible <bruno@clisp.org>, 2008. |
4 | | |
5 | | This file is free software: you can redistribute it and/or modify |
6 | | it under the terms of the GNU Lesser General Public License as |
7 | | published by the Free Software Foundation; either version 2.1 of the |
8 | | License, or (at your option) any later version. |
9 | | |
10 | | This file is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU Lesser General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU Lesser General Public License |
16 | | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
17 | | |
18 | | #include <config.h> |
19 | | |
20 | | /* Specification. */ |
21 | | #include <wchar.h> |
22 | | |
23 | | #if GNULIB_defined_mbstate_t |
24 | | /* Implement mbrtowc() on top of mbtowc() for the non-UTF-8 locales |
25 | | and directly for the UTF-8 locales. */ |
26 | | |
27 | | # include <errno.h> |
28 | | # include <stdint.h> |
29 | | # include <stdlib.h> |
30 | | |
31 | | # if AVOID_ANY_THREADS |
32 | | |
33 | | /* The option '--disable-threads' explicitly requests no locking. */ |
34 | | |
35 | | # elif defined _WIN32 && !defined __CYGWIN__ |
36 | | |
37 | | # define WIN32_LEAN_AND_MEAN /* avoid including junk */ |
38 | | # include <windows.h> |
39 | | |
40 | | # elif HAVE_PTHREAD_API |
41 | | |
42 | | # include <pthread.h> |
43 | | # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS |
44 | | # include <threads.h> |
45 | | # pragma weak thrd_exit |
46 | | # define c11_threads_in_use() (thrd_exit != NULL) |
47 | | # else |
48 | | # define c11_threads_in_use() 0 |
49 | | # endif |
50 | | |
51 | | # elif HAVE_THREADS_H |
52 | | |
53 | | # include <threads.h> |
54 | | |
55 | | # endif |
56 | | |
57 | | # include "attribute.h" |
58 | | # include "lc-charset-dispatch.h" |
59 | | # include "mbtowc-lock.h" |
60 | | |
61 | | static_assert (sizeof (mbstate_t) >= 4); |
62 | | static char internal_state[4]; |
63 | | |
64 | | size_t |
65 | | mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps) |
66 | | { |
67 | | # define FITS_IN_CHAR_TYPE(wc) ((wc) <= WCHAR_MAX) |
68 | | # include "mbrtowc-impl.h" |
69 | | } |
70 | | |
71 | | #else |
72 | | /* Override the system's mbrtowc() function. */ |
73 | | |
74 | | # include <errno.h> |
75 | | # include <stdlib.h> |
76 | | |
77 | | # include "attribute.h" |
78 | | # include "localcharset.h" |
79 | | # include "streq-opt.h" |
80 | | |
81 | | # if MBRTOWC_IN_C_LOCALE_MAYBE_EILSEQ |
82 | | # include "hard-locale.h" |
83 | | # include <locale.h> |
84 | | # endif |
85 | | |
86 | | # if MBRTOWC_INVALID_UTF8_BUG || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__) |
87 | | |
88 | | /* Returns 1 if the current locale is an UTF-8 locale, 0 otherwise. */ |
89 | | static inline int |
90 | | is_locale_utf8 (void) |
91 | | { |
92 | | const char *encoding = locale_charset (); |
93 | | return STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0); |
94 | | } |
95 | | |
96 | | /* Provide a speedup by caching the value of is_locale_utf8. */ |
97 | | static int cached_is_locale_utf8 = -1; |
98 | | static inline int |
99 | | is_locale_utf8_cached (void) |
100 | | { |
101 | | if (cached_is_locale_utf8 < 0) |
102 | | cached_is_locale_utf8 = is_locale_utf8 (); |
103 | | return cached_is_locale_utf8; |
104 | | } |
105 | | |
106 | | # endif |
107 | | |
108 | | # undef mbrtowc |
109 | | |
110 | | size_t |
111 | | rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps) |
112 | 0 | { |
113 | | /* It's simpler to handle the case s == NULL upfront, than to worry about |
114 | | this case later, before every test of pwc and n. */ |
115 | 0 | if (s == NULL) |
116 | 0 | { |
117 | 0 | pwc = NULL; |
118 | 0 | s = ""; |
119 | 0 | n = 1; |
120 | 0 | } |
121 | |
|
122 | | # if (MBRTOWC_EMPTY_INPUT_BUG || MBRTOWC_INVALID_UTF8_BUG \ |
123 | | || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__)) |
124 | | if (n == 0) |
125 | | return (size_t) -2; |
126 | | # endif |
127 | |
|
128 | | # if MBRTOWC_INVALID_UTF8_BUG || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__) |
129 | | /* Optimize the frequent case of an UTF-8 locale. |
130 | | Since here we are in the !GNULIB_defined_mbstate_t case, i.e. we use |
131 | | the system's mbstate_t type and have to provide interoperability with |
132 | | the system's mbsinit() function, this requires knowledge about how the |
133 | | system's UTF-8 mbrtowc() function stores the state. This knowledge is |
134 | | platform-specific. For simplicity, we handle only glibc and NetBSD |
135 | | systems. */ |
136 | | if (is_locale_utf8_cached ()) |
137 | | { |
138 | | static mbstate_t internal_state; |
139 | | if (ps == NULL) |
140 | | ps = &internal_state; |
141 | | #if __GLIBC__ >= 2 |
142 | | /* Structure of mbstate_t = |
143 | | { int __count; union { wint_t __wch; char __wchb[4]; } __value; } |
144 | | (see glibc/iconv/gconv_simple.c function utf8_internal_loop): |
145 | | Bits 2..0 of __count is the number of input bytes already consumed. |
146 | | Bits 31..8 of __count is the number of input bytes expected for the |
147 | | entire byte sequence. |
148 | | __value.__wch is the already inferrable bits of the character, of |
149 | | the form (x << (r*6)) when r bytes are still expected. */ |
150 | | #endif |
151 | | #ifdef __NetBSD__ |
152 | | /* Structure of mbstate_t = |
153 | | union { int64_t __mbstateL; char __mbstate8[128]; } |
154 | | (see src/lib/libc/citrus/modules/citrus_utf8.c): |
155 | | { void *header; char ch[6]; int chlen; }, |
156 | | i.e. ch[0..5] is __mbstate8[sizeof(void*)+0..sizeof(void*)+5], |
157 | | chlen is __mbstate8[sizeof(void*)+8..sizeof(void*)+11]. */ |
158 | | #endif |
159 | | |
160 | | /* Here n > 0. */ |
161 | | |
162 | | size_t nstate; |
163 | | #if __GLIBC__ >= 2 |
164 | | nstate = ps->__count & 7; |
165 | | #endif |
166 | | #ifdef __NetBSD__ |
167 | | nstate = *(int *) &ps->__mbstate8[sizeof (void *) + 8]; |
168 | | #endif |
169 | | char buf[4]; |
170 | | const char *p; |
171 | | size_t m; |
172 | | |
173 | | if (nstate == 0) |
174 | | { |
175 | | p = s; |
176 | | m = n; |
177 | | } |
178 | | else |
179 | | { |
180 | | #if __GLIBC__ >= 2 |
181 | | size_t t = ps->__count >> 8; /* total expected number of bytes */ |
182 | | if (t > nstate && t <= 4) |
183 | | { |
184 | | buf[0] = |
185 | | (0x100 - (0x100 >> t)) | (ps->__value.__wch >> ((t - 1) * 6)); |
186 | | if (nstate >= 2) |
187 | | { |
188 | | buf[1] = |
189 | | 0x80 | ((ps->__value.__wch >> ((t - 2) * 6)) & 0x3F); |
190 | | if (nstate >= 3) |
191 | | { |
192 | | buf[2] = |
193 | | 0x80 | ((ps->__value.__wch >> ((t - 3) * 6)) & 0x3F); |
194 | | } |
195 | | } |
196 | | } |
197 | | else |
198 | | { |
199 | | errno = EINVAL; |
200 | | return (size_t)(-1); |
201 | | } |
202 | | #endif |
203 | | #ifdef __NetBSD__ |
204 | | buf[0] = ps->__mbstate8[sizeof (void *) + 0]; |
205 | | if (nstate >= 2) |
206 | | { |
207 | | buf[1] = ps->__mbstate8[sizeof (void *) + 1]; |
208 | | if (nstate >= 3) |
209 | | { |
210 | | buf[2] = ps->__mbstate8[sizeof (void *) + 2]; |
211 | | } |
212 | | } |
213 | | #endif |
214 | | p = buf; |
215 | | m = nstate; |
216 | | buf[m++] = s[0]; |
217 | | if (n >= 2 && m < 4) |
218 | | { |
219 | | buf[m++] = s[1]; |
220 | | if (n >= 3 && m < 4) |
221 | | buf[m++] = s[2]; |
222 | | } |
223 | | } |
224 | | |
225 | | /* Here m > 0. */ |
226 | | |
227 | | int res; |
228 | | { |
229 | | # define FITS_IN_CHAR_TYPE(wc) ((wc) <= WCHAR_MAX) |
230 | | # include "mbrtowc-impl-utf8.h" |
231 | | } |
232 | | |
233 | | success: |
234 | | /* res >= 0 is the corrected return value of |
235 | | mbtowc_with_lock (&wc, p, m). */ |
236 | | if (nstate >= (res > 0 ? res : 1)) |
237 | | abort (); |
238 | | res -= nstate; |
239 | | #if __GLIBC__ >= 2 |
240 | | ps->__count = 0; |
241 | | #endif |
242 | | #ifdef __NetBSD__ |
243 | | *(int *) &ps->__mbstate8[sizeof (void *) + 8] = 0; |
244 | | #endif |
245 | | return res; |
246 | | |
247 | | incomplete: |
248 | | /* Here 0 < m < 4. */ |
249 | | { |
250 | | #if __GLIBC__ >= 2 |
251 | | unsigned char c = (unsigned char) p[0]; |
252 | | if (c < 0xE0) |
253 | | { |
254 | | ps->__count = (2 << 8) | m; |
255 | | ps->__value.__wch = (c & 0x1F) << 6; |
256 | | } |
257 | | else if (c < 0xF0) |
258 | | { |
259 | | ps->__count = (3 << 8) | m; |
260 | | ps->__value.__wch = |
261 | | ((c & 0x0F) << 12) |
262 | | | (m > 1 ? ((unsigned char) p[1] & 0x3F) << 6 : 0); |
263 | | } |
264 | | else |
265 | | { |
266 | | ps->__count = (4 << 8) | m; |
267 | | ps->__value.__wch = |
268 | | ((c & 0x07) << 18) |
269 | | | (m > 1 ? ((unsigned char) p[1] & 0x3F) << 12 : 0) |
270 | | | (m > 2 ? ((unsigned char) p[2] & 0x3F) << 6 : 0); |
271 | | } |
272 | | #endif |
273 | | #ifdef __NetBSD__ |
274 | | *(int *) &ps->__mbstate8[sizeof (void *) + 8] = m; |
275 | | ps->__mbstate8[sizeof (void *) + 0] = p[0]; |
276 | | if (m > 1) |
277 | | { |
278 | | ps->__mbstate8[sizeof (void *) + 1] = p[1]; |
279 | | if (m > 2) |
280 | | { |
281 | | ps->__mbstate8[sizeof (void *) + 2] = p[2]; |
282 | | } |
283 | | } |
284 | | #endif |
285 | | } |
286 | | return (size_t)(-2); |
287 | | |
288 | | invalid: |
289 | | errno = EILSEQ; |
290 | | /* The conversion state is undefined, says POSIX. */ |
291 | | return (size_t)(-1); |
292 | | } |
293 | | # endif |
294 | |
|
295 | 0 | wchar_t wc; |
296 | 0 | if (! pwc) |
297 | 0 | pwc = &wc; |
298 | |
|
299 | | # if MBRTOWC_RETVAL_BUG |
300 | | { |
301 | | static mbstate_t internal_state; |
302 | | |
303 | | /* Override mbrtowc's internal state. We cannot call mbsinit() on the |
304 | | hidden internal state, but we can call it on our variable. */ |
305 | | if (ps == NULL) |
306 | | ps = &internal_state; |
307 | | |
308 | | if (!mbsinit (ps)) |
309 | | { |
310 | | /* Parse the rest of the multibyte character byte for byte. */ |
311 | | size_t count = 0; |
312 | | for (; n > 0; s++, n--) |
313 | | { |
314 | | size_t ret = mbrtowc (&wc, s, 1, ps); |
315 | | |
316 | | if (ret == (size_t)(-1)) |
317 | | return (size_t)(-1); |
318 | | count++; |
319 | | if (ret != (size_t)(-2)) |
320 | | { |
321 | | /* The multibyte character has been completed. */ |
322 | | *pwc = wc; |
323 | | return (wc == 0 ? 0 : count); |
324 | | } |
325 | | } |
326 | | return (size_t)(-2); |
327 | | } |
328 | | } |
329 | | # endif |
330 | |
|
331 | 0 | size_t ret; |
332 | | # if MBRTOWC_STORES_INCOMPLETE_BUG |
333 | | ret = mbrtowc (&wc, s, n, ps); |
334 | | if (ret < (size_t) -2 && pwc != NULL) |
335 | | *pwc = wc; |
336 | | # else |
337 | 0 | ret = mbrtowc (pwc, s, n, ps); |
338 | 0 | # endif |
339 | |
|
340 | | # if MBRTOWC_NUL_RETVAL_BUG |
341 | | if (ret < (size_t) -2 && !*pwc) |
342 | | return 0; |
343 | | # endif |
344 | |
|
345 | 0 | # if MBRTOWC_IN_C_LOCALE_MAYBE_EILSEQ |
346 | 0 | if ((size_t) -2 <= ret && n != 0 && ! hard_locale (LC_CTYPE)) |
347 | 0 | { |
348 | 0 | unsigned char uc = *s; |
349 | 0 | *pwc = uc; |
350 | 0 | return 1; |
351 | 0 | } |
352 | 0 | # endif |
353 | | |
354 | 0 | return ret; |
355 | 0 | } |
356 | | |
357 | | #endif |