1 /* Convert 32-bit wide character to multibyte character.
2 Copyright (C) 2020-2021 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.h"
30
31 size_t
32 c32rtomb (char *s, char32_t wc, mbstate_t *ps)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
33 #undef c32rtomb
34 {
35 #if HAVE_WORKING_MBRTOC32
36
37 # if C32RTOMB_RETVAL_BUG
38 if (s == NULL)
39 /* We know the NUL wide character corresponds to the NUL character. */
40 return 1;
41 # endif
42
43 return c32rtomb (s, wc, ps);
44
45 #elif _GL_LARGE_CHAR32_T
46
47 if (s == NULL)
48 return wcrtomb (NULL, 0, ps);
49 else
50 {
51 /* Special-case all encodings that may produce wide character values
52 > WCHAR_MAX. */
53 const char *encoding = locale_charset ();
54 if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
55 {
56 /* Special-case the UTF-8 encoding. Assume that the wide-character
57 encoding in a UTF-8 locale is UCS-2 or, equivalently, UTF-16. */
58 if (wc < 0x80)
59 {
60 s[0] = (unsigned char) wc;
61 return 1;
62 }
63 else
64 {
65 int count;
66
67 if (wc < 0x800)
68 count = 2;
69 else if (wc < 0x10000)
70 {
71 if (wc < 0xd800 || wc >= 0xe000)
72 count = 3;
73 else
74 {
75 errno = EILSEQ;
76 return (size_t)(-1);
77 }
78 }
79 else if (wc < 0x110000)
80 count = 4;
81 else
82 {
83 errno = EILSEQ;
84 return (size_t)(-1);
85 }
86
87 switch (count) /* note: code falls through cases! */
88 {
89 case 4: s[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000;
90 FALLTHROUGH;
91 case 3: s[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800;
92 FALLTHROUGH;
93 case 2: s[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0;
94 /*case 1:*/ s[0] = wc;
95 }
96 return count;
97 }
98 }
99 else
100 {
101 if ((wchar_t) wc == wc)
102 return wcrtomb (s, (wchar_t) wc, ps);
103 else
104 {
105 errno = EILSEQ;
106 return (size_t)(-1);
107 }
108 }
109 }
110
111 #else
112
113 /* char32_t and wchar_t are equivalent. */
114 return wcrtomb (s, (wchar_t) wc, ps);
115
116 #endif
117 }