1 | /* Copyright (C) 1999-2023 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library 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 GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. |
17 | |
18 | As a special exception, if you link the code in this file with |
19 | files compiled with a GNU compiler to produce an executable, |
20 | that does not cause the resulting executable to be covered by |
21 | the GNU Lesser General Public License. This exception does not |
22 | however invalidate any other reasons why the executable file |
23 | might be covered by the GNU Lesser General Public License. |
24 | This exception applies to code released by its copyright holders |
25 | in files containing the exception. */ |
26 | |
27 | #include <libioP.h> |
28 | #include <dlfcn.h> |
29 | #include <wchar.h> |
30 | #include <assert.h> |
31 | #include <stdlib.h> |
32 | #include <string.h> |
33 | |
34 | #include <langinfo.h> |
35 | #include <locale/localeinfo.h> |
36 | #include <wcsmbs/wcsmbsload.h> |
37 | #include <iconv/gconv_int.h> |
38 | #include <shlib-compat.h> |
39 | #include <pointer_guard.h> |
40 | |
41 | |
42 | /* Return orientation of stream. If mode is nonzero try to change |
43 | the orientation first. */ |
44 | #undef _IO_fwide |
45 | int |
46 | _IO_fwide (FILE *fp, int mode) |
47 | { |
48 | /* Normalize the value. */ |
49 | mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1); |
50 | |
51 | #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) |
52 | if (__glibc_unlikely (&_IO_stdin_used == NULL) && _IO_legacy_file (fp)) |
53 | /* This is for a stream in the glibc 2.0 format. */ |
54 | return -1; |
55 | #endif |
56 | |
57 | /* The orientation already has been determined. */ |
58 | if (fp->_mode != 0 |
59 | /* Or the caller simply wants to know about the current orientation. */ |
60 | || mode == 0) |
61 | return fp->_mode; |
62 | |
63 | /* Set the orientation appropriately. */ |
64 | if (mode > 0) |
65 | { |
66 | struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt; |
67 | |
68 | fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; |
69 | fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base; |
70 | |
71 | /* Get the character conversion functions based on the currently |
72 | selected locale for LC_CTYPE. */ |
73 | { |
74 | /* Clear the state. We start all over again. */ |
75 | memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); |
76 | memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t)); |
77 | |
78 | struct gconv_fcts fcts; |
79 | __wcsmbs_clone_conv (&fcts); |
80 | assert (fcts.towc_nsteps == 1); |
81 | assert (fcts.tomb_nsteps == 1); |
82 | |
83 | cc->__cd_in.step = fcts.towc; |
84 | |
85 | cc->__cd_in.step_data.__invocation_counter = 0; |
86 | cc->__cd_in.step_data.__internal_use = 1; |
87 | cc->__cd_in.step_data.__flags = __GCONV_IS_LAST; |
88 | cc->__cd_in.step_data.__statep = &fp->_wide_data->_IO_state; |
89 | |
90 | cc->__cd_out.step = fcts.tomb; |
91 | |
92 | cc->__cd_out.step_data.__invocation_counter = 0; |
93 | cc->__cd_out.step_data.__internal_use = 1; |
94 | cc->__cd_out.step_data.__flags = __GCONV_IS_LAST | __GCONV_TRANSLIT; |
95 | cc->__cd_out.step_data.__statep = &fp->_wide_data->_IO_state; |
96 | } |
97 | |
98 | /* From now on use the wide character callback functions. */ |
99 | _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable; |
100 | } |
101 | |
102 | /* Set the mode now. */ |
103 | fp->_mode = mode; |
104 | |
105 | return mode; |
106 | } |
107 | |
108 | |
109 | enum __codecvt_result |
110 | __libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep, |
111 | const wchar_t *from_start, const wchar_t *from_end, |
112 | const wchar_t **from_stop, char *to_start, char *to_end, |
113 | char **to_stop) |
114 | { |
115 | enum __codecvt_result result; |
116 | |
117 | struct __gconv_step *gs = codecvt->__cd_out.step; |
118 | int status; |
119 | size_t dummy; |
120 | const unsigned char *from_start_copy = (unsigned char *) from_start; |
121 | |
122 | codecvt->__cd_out.step_data.__outbuf = (unsigned char *) to_start; |
123 | codecvt->__cd_out.step_data.__outbufend = (unsigned char *) to_end; |
124 | codecvt->__cd_out.step_data.__statep = statep; |
125 | |
126 | __gconv_fct fct = gs->__fct; |
127 | if (gs->__shlib_handle != NULL) |
128 | PTR_DEMANGLE (fct); |
129 | |
130 | status = DL_CALL_FCT (fct, |
131 | (gs, &codecvt->__cd_out.step_data, &from_start_copy, |
132 | (const unsigned char *) from_end, NULL, |
133 | &dummy, 0, 0)); |
134 | |
135 | *from_stop = (wchar_t *) from_start_copy; |
136 | *to_stop = (char *) codecvt->__cd_out.step_data.__outbuf; |
137 | |
138 | switch (status) |
139 | { |
140 | case __GCONV_OK: |
141 | case __GCONV_EMPTY_INPUT: |
142 | result = __codecvt_ok; |
143 | break; |
144 | |
145 | case __GCONV_FULL_OUTPUT: |
146 | case __GCONV_INCOMPLETE_INPUT: |
147 | result = __codecvt_partial; |
148 | break; |
149 | |
150 | default: |
151 | result = __codecvt_error; |
152 | break; |
153 | } |
154 | |
155 | return result; |
156 | } |
157 | |
158 | |
159 | enum __codecvt_result |
160 | __libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep, |
161 | const char *from_start, const char *from_end, |
162 | const char **from_stop, |
163 | wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop) |
164 | { |
165 | enum __codecvt_result result; |
166 | |
167 | struct __gconv_step *gs = codecvt->__cd_in.step; |
168 | int status; |
169 | size_t dummy; |
170 | const unsigned char *from_start_copy = (unsigned char *) from_start; |
171 | |
172 | codecvt->__cd_in.step_data.__outbuf = (unsigned char *) to_start; |
173 | codecvt->__cd_in.step_data.__outbufend = (unsigned char *) to_end; |
174 | codecvt->__cd_in.step_data.__statep = statep; |
175 | |
176 | __gconv_fct fct = gs->__fct; |
177 | if (gs->__shlib_handle != NULL) |
178 | PTR_DEMANGLE (fct); |
179 | |
180 | status = DL_CALL_FCT (fct, |
181 | (gs, &codecvt->__cd_in.step_data, &from_start_copy, |
182 | (const unsigned char *) from_end, NULL, |
183 | &dummy, 0, 0)); |
184 | |
185 | *from_stop = (const char *) from_start_copy; |
186 | *to_stop = (wchar_t *) codecvt->__cd_in.step_data.__outbuf; |
187 | |
188 | switch (status) |
189 | { |
190 | case __GCONV_OK: |
191 | case __GCONV_EMPTY_INPUT: |
192 | result = __codecvt_ok; |
193 | break; |
194 | |
195 | case __GCONV_FULL_OUTPUT: |
196 | case __GCONV_INCOMPLETE_INPUT: |
197 | result = __codecvt_partial; |
198 | break; |
199 | |
200 | default: |
201 | result = __codecvt_error; |
202 | break; |
203 | } |
204 | |
205 | return result; |
206 | } |
207 | |
208 | |
209 | int |
210 | __libio_codecvt_encoding (struct _IO_codecvt *codecvt) |
211 | { |
212 | /* See whether the encoding is stateful. */ |
213 | if (codecvt->__cd_in.step->__stateful) |
214 | return -1; |
215 | /* Fortunately not. Now determine the input bytes for the conversion |
216 | necessary for each wide character. */ |
217 | if (codecvt->__cd_in.step->__min_needed_from |
218 | != codecvt->__cd_in.step->__max_needed_from) |
219 | /* Not a constant value. */ |
220 | return 0; |
221 | |
222 | return codecvt->__cd_in.step->__min_needed_from; |
223 | } |
224 | |
225 | |
226 | int |
227 | __libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, |
228 | const char *from_start, const char *from_end, |
229 | size_t max) |
230 | { |
231 | int result; |
232 | const unsigned char *cp = (const unsigned char *) from_start; |
233 | wchar_t to_buf[max]; |
234 | struct __gconv_step *gs = codecvt->__cd_in.step; |
235 | size_t dummy; |
236 | |
237 | codecvt->__cd_in.step_data.__outbuf = (unsigned char *) to_buf; |
238 | codecvt->__cd_in.step_data.__outbufend = (unsigned char *) &to_buf[max]; |
239 | codecvt->__cd_in.step_data.__statep = statep; |
240 | |
241 | __gconv_fct fct = gs->__fct; |
242 | if (gs->__shlib_handle != NULL) |
243 | PTR_DEMANGLE (fct); |
244 | |
245 | DL_CALL_FCT (fct, |
246 | (gs, &codecvt->__cd_in.step_data, &cp, |
247 | (const unsigned char *) from_end, NULL, |
248 | &dummy, 0, 0)); |
249 | |
250 | result = cp - (const unsigned char *) from_start; |
251 | |
252 | return result; |
253 | } |
254 | |