1 | /* Copyright (C) 2011-2021 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Ulrich Drepper <drepper@gmail.com>, 2011. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library 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 GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <assert.h> |
20 | #include <dlfcn.h> |
21 | #include <errno.h> |
22 | #include <gconv.h> |
23 | #include <uchar.h> |
24 | #include <wcsmbsload.h> |
25 | |
26 | #include <sysdep.h> |
27 | |
28 | #ifndef EILSEQ |
29 | # define EILSEQ EINVAL |
30 | #endif |
31 | |
32 | |
33 | /* This is the private state used if PS is NULL. */ |
34 | static mbstate_t state; |
35 | |
36 | size_t |
37 | mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps) |
38 | { |
39 | if (ps == NULL) |
40 | ps = &state; |
41 | |
42 | /* The standard text does not say that S being NULL means the state |
43 | is reset even if the second half of a surrogate still have to be |
44 | returned. In fact, the error code description indicates |
45 | otherwise. Therefore always first try to return a second |
46 | half. */ |
47 | if (ps->__count & 0x80000000) |
48 | { |
49 | /* We have to return the second word for a surrogate. */ |
50 | ps->__count &= 0x7fffffff; |
51 | *pc16 = ps->__value.__wch; |
52 | ps->__value.__wch = L'\0'; |
53 | return (size_t) -3; |
54 | } |
55 | |
56 | wchar_t wc; |
57 | struct __gconv_step_data data; |
58 | int status; |
59 | size_t result; |
60 | size_t dummy; |
61 | const unsigned char *inbuf, *endbuf; |
62 | unsigned char *outbuf = (unsigned char *) &wc; |
63 | const struct gconv_fcts *fcts; |
64 | |
65 | /* Set information for this step. */ |
66 | data.__invocation_counter = 0; |
67 | data.__internal_use = 1; |
68 | data.__flags = __GCONV_IS_LAST; |
69 | data.__statep = ps; |
70 | |
71 | /* A first special case is if S is NULL. This means put PS in the |
72 | initial state. */ |
73 | if (s == NULL) |
74 | { |
75 | pc16 = NULL; |
76 | s = "" ; |
77 | n = 1; |
78 | } |
79 | |
80 | if (n == 0) |
81 | return (size_t) -2; |
82 | |
83 | /* Tell where we want the result. */ |
84 | data.__outbuf = outbuf; |
85 | data.__outbufend = outbuf + sizeof (wchar_t); |
86 | |
87 | /* Get the conversion functions. */ |
88 | fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); |
89 | |
90 | /* Do a normal conversion. */ |
91 | inbuf = (const unsigned char *) s; |
92 | endbuf = inbuf + n; |
93 | if (__glibc_unlikely (endbuf < inbuf)) |
94 | { |
95 | endbuf = (const unsigned char *) ~(uintptr_t) 0; |
96 | if (endbuf == inbuf) |
97 | goto ilseq; |
98 | } |
99 | __gconv_fct fct = fcts->towc->__fct; |
100 | #ifdef PTR_DEMANGLE |
101 | if (fcts->towc->__shlib_handle != NULL) |
102 | PTR_DEMANGLE (fct); |
103 | #endif |
104 | |
105 | status = DL_CALL_FCT (fct, (fcts->towc, &data, &inbuf, endbuf, |
106 | NULL, &dummy, 0, 1)); |
107 | |
108 | /* There must not be any problems with the conversion but illegal input |
109 | characters. The output buffer must be large enough, otherwise the |
110 | definition of MB_CUR_MAX is not correct. All the other possible |
111 | errors also must not happen. */ |
112 | assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT |
113 | || status == __GCONV_ILLEGAL_INPUT |
114 | || status == __GCONV_INCOMPLETE_INPUT |
115 | || status == __GCONV_FULL_OUTPUT); |
116 | |
117 | if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT |
118 | || status == __GCONV_FULL_OUTPUT) |
119 | { |
120 | result = inbuf - (const unsigned char *) s; |
121 | |
122 | if (wc < 0x10000) |
123 | { |
124 | if (pc16 != NULL) |
125 | *pc16 = wc; |
126 | |
127 | if (data.__outbuf != outbuf && wc == L'\0') |
128 | { |
129 | /* The converted character is the NUL character. */ |
130 | assert (__mbsinit (data.__statep)); |
131 | result = 0; |
132 | } |
133 | } |
134 | else |
135 | { |
136 | /* This is a surrogate. */ |
137 | if (pc16 != NULL) |
138 | *pc16 = 0xd7c0 + (wc >> 10); |
139 | |
140 | ps->__count |= 0x80000000; |
141 | ps->__value.__wch = 0xdc00 + (wc & 0x3ff); |
142 | } |
143 | } |
144 | else if (status == __GCONV_INCOMPLETE_INPUT) |
145 | result = (size_t) -2; |
146 | else |
147 | { |
148 | ilseq: |
149 | result = (size_t) -1; |
150 | __set_errno (EILSEQ); |
151 | } |
152 | |
153 | return result; |
154 | } |
155 | |