1/* Copyright (C) 1999-2019 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 <http://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 <sysdep.h>
40
41
42/* Prototypes of libio's codecvt functions. */
43static enum __codecvt_result do_out (struct _IO_codecvt *codecvt,
44 __mbstate_t *statep,
45 const wchar_t *from_start,
46 const wchar_t *from_end,
47 const wchar_t **from_stop, char *to_start,
48 char *to_end, char **to_stop);
49static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt,
50 __mbstate_t *statep, char *to_start,
51 char *to_end, char **to_stop);
52static enum __codecvt_result do_in (struct _IO_codecvt *codecvt,
53 __mbstate_t *statep,
54 const char *from_start,
55 const char *from_end,
56 const char **from_stop, wchar_t *to_start,
57 wchar_t *to_end, wchar_t **to_stop);
58static int do_encoding (struct _IO_codecvt *codecvt);
59static int do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
60 const char *from_start,
61 const char *from_end, size_t max);
62static int do_max_length (struct _IO_codecvt *codecvt);
63static int do_always_noconv (struct _IO_codecvt *codecvt);
64
65
66/* The functions used in `codecvt' for libio are always the same. */
67const struct _IO_codecvt __libio_codecvt =
68{
69 .__codecvt_destr = NULL, /* Destructor, never used. */
70 .__codecvt_do_out = do_out,
71 .__codecvt_do_unshift = do_unshift,
72 .__codecvt_do_in = do_in,
73 .__codecvt_do_encoding = do_encoding,
74 .__codecvt_do_always_noconv = do_always_noconv,
75 .__codecvt_do_length = do_length,
76 .__codecvt_do_max_length = do_max_length
77};
78
79
80/* Return orientation of stream. If mode is nonzero try to change
81 the orientation first. */
82#undef _IO_fwide
83int
84_IO_fwide (FILE *fp, int mode)
85{
86 /* Normalize the value. */
87 mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1);
88
89#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
90 if (__glibc_unlikely (&_IO_stdin_used == NULL) && _IO_legacy_file (fp))
91 /* This is for a stream in the glibc 2.0 format. */
92 return -1;
93#endif
94
95 /* The orientation already has been determined. */
96 if (fp->_mode != 0
97 /* Or the caller simply wants to know about the current orientation. */
98 || mode == 0)
99 return fp->_mode;
100
101 /* Set the orientation appropriately. */
102 if (mode > 0)
103 {
104 struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt;
105
106 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
107 fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;
108
109 /* Get the character conversion functions based on the currently
110 selected locale for LC_CTYPE. */
111 {
112 /* Clear the state. We start all over again. */
113 memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t));
114 memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
115
116 struct gconv_fcts fcts;
117 __wcsmbs_clone_conv (&fcts);
118 assert (fcts.towc_nsteps == 1);
119 assert (fcts.tomb_nsteps == 1);
120
121 /* The functions are always the same. */
122 *cc = __libio_codecvt;
123
124 cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps;
125 cc->__cd_in.__cd.__steps = fcts.towc;
126
127 cc->__cd_in.__cd.__data[0].__invocation_counter = 0;
128 cc->__cd_in.__cd.__data[0].__internal_use = 1;
129 cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
130 cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
131
132 cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps;
133 cc->__cd_out.__cd.__steps = fcts.tomb;
134
135 cc->__cd_out.__cd.__data[0].__invocation_counter = 0;
136 cc->__cd_out.__cd.__data[0].__internal_use = 1;
137 cc->__cd_out.__cd.__data[0].__flags
138 = __GCONV_IS_LAST | __GCONV_TRANSLIT;
139 cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
140 }
141
142 /* From now on use the wide character callback functions. */
143 _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
144 }
145
146 /* Set the mode now. */
147 fp->_mode = mode;
148
149 return mode;
150}
151
152
153static enum __codecvt_result
154do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
155 const wchar_t *from_start, const wchar_t *from_end,
156 const wchar_t **from_stop, char *to_start, char *to_end,
157 char **to_stop)
158{
159 enum __codecvt_result result;
160
161 struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
162 int status;
163 size_t dummy;
164 const unsigned char *from_start_copy = (unsigned char *) from_start;
165
166 codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start;
167 codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end;
168 codecvt->__cd_out.__cd.__data[0].__statep = statep;
169
170 __gconv_fct fct = gs->__fct;
171#ifdef PTR_DEMANGLE
172 if (gs->__shlib_handle != NULL)
173 PTR_DEMANGLE (fct);
174#endif
175
176 status = DL_CALL_FCT (fct,
177 (gs, codecvt->__cd_out.__cd.__data, &from_start_copy,
178 (const unsigned char *) from_end, NULL,
179 &dummy, 0, 0));
180
181 *from_stop = (wchar_t *) from_start_copy;
182 *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf;
183
184 switch (status)
185 {
186 case __GCONV_OK:
187 case __GCONV_EMPTY_INPUT:
188 result = __codecvt_ok;
189 break;
190
191 case __GCONV_FULL_OUTPUT:
192 case __GCONV_INCOMPLETE_INPUT:
193 result = __codecvt_partial;
194 break;
195
196 default:
197 result = __codecvt_error;
198 break;
199 }
200
201 return result;
202}
203
204
205static enum __codecvt_result
206do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep,
207 char *to_start, char *to_end, char **to_stop)
208{
209 enum __codecvt_result result;
210
211 struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
212 int status;
213 size_t dummy;
214
215 codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start;
216 codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end;
217 codecvt->__cd_out.__cd.__data[0].__statep = statep;
218
219 __gconv_fct fct = gs->__fct;
220#ifdef PTR_DEMANGLE
221 if (gs->__shlib_handle != NULL)
222 PTR_DEMANGLE (fct);
223#endif
224
225 status = DL_CALL_FCT (fct,
226 (gs, codecvt->__cd_out.__cd.__data, NULL, NULL,
227 NULL, &dummy, 1, 0));
228
229 *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf;
230
231 switch (status)
232 {
233 case __GCONV_OK:
234 case __GCONV_EMPTY_INPUT:
235 result = __codecvt_ok;
236 break;
237
238 case __GCONV_FULL_OUTPUT:
239 case __GCONV_INCOMPLETE_INPUT:
240 result = __codecvt_partial;
241 break;
242
243 default:
244 result = __codecvt_error;
245 break;
246 }
247
248 return result;
249}
250
251
252static enum __codecvt_result
253do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
254 const char *from_start, const char *from_end, const char **from_stop,
255 wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
256{
257 enum __codecvt_result result;
258
259 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
260 int status;
261 size_t dummy;
262 const unsigned char *from_start_copy = (unsigned char *) from_start;
263
264 codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_start;
265 codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) to_end;
266 codecvt->__cd_in.__cd.__data[0].__statep = statep;
267
268 __gconv_fct fct = gs->__fct;
269#ifdef PTR_DEMANGLE
270 if (gs->__shlib_handle != NULL)
271 PTR_DEMANGLE (fct);
272#endif
273
274 status = DL_CALL_FCT (fct,
275 (gs, codecvt->__cd_in.__cd.__data, &from_start_copy,
276 (const unsigned char *) from_end, NULL,
277 &dummy, 0, 0));
278
279 *from_stop = (const char *) from_start_copy;
280 *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf;
281
282 switch (status)
283 {
284 case __GCONV_OK:
285 case __GCONV_EMPTY_INPUT:
286 result = __codecvt_ok;
287 break;
288
289 case __GCONV_FULL_OUTPUT:
290 case __GCONV_INCOMPLETE_INPUT:
291 result = __codecvt_partial;
292 break;
293
294 default:
295 result = __codecvt_error;
296 break;
297 }
298
299 return result;
300}
301
302
303static int
304do_encoding (struct _IO_codecvt *codecvt)
305{
306 /* See whether the encoding is stateful. */
307 if (codecvt->__cd_in.__cd.__steps[0].__stateful)
308 return -1;
309 /* Fortunately not. Now determine the input bytes for the conversion
310 necessary for each wide character. */
311 if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from
312 != codecvt->__cd_in.__cd.__steps[0].__max_needed_from)
313 /* Not a constant value. */
314 return 0;
315
316 return codecvt->__cd_in.__cd.__steps[0].__min_needed_from;
317}
318
319
320static int
321do_always_noconv (struct _IO_codecvt *codecvt)
322{
323 return 0;
324}
325
326
327static int
328do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
329 const char *from_start, const char *from_end, size_t max)
330{
331 int result;
332 const unsigned char *cp = (const unsigned char *) from_start;
333 wchar_t to_buf[max];
334 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
335 size_t dummy;
336
337 codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_buf;
338 codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) &to_buf[max];
339 codecvt->__cd_in.__cd.__data[0].__statep = statep;
340
341 __gconv_fct fct = gs->__fct;
342#ifdef PTR_DEMANGLE
343 if (gs->__shlib_handle != NULL)
344 PTR_DEMANGLE (fct);
345#endif
346
347 DL_CALL_FCT (fct,
348 (gs, codecvt->__cd_in.__cd.__data, &cp,
349 (const unsigned char *) from_end, NULL,
350 &dummy, 0, 0));
351
352 result = cp - (const unsigned char *) from_start;
353
354 return result;
355}
356
357
358static int
359do_max_length (struct _IO_codecvt *codecvt)
360{
361 return codecvt->__cd_in.__cd.__steps[0].__max_needed_from;
362}
363