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/* Return orientation of stream. If mode is nonzero try to change
43 the orientation first. */
44#undef _IO_fwide
45int
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.__cd.__nsteps = fcts.towc_nsteps;
84 cc->__cd_in.__cd.__steps = fcts.towc;
85
86 cc->__cd_in.__cd.__data[0].__invocation_counter = 0;
87 cc->__cd_in.__cd.__data[0].__internal_use = 1;
88 cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
89 cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
90
91 cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps;
92 cc->__cd_out.__cd.__steps = fcts.tomb;
93
94 cc->__cd_out.__cd.__data[0].__invocation_counter = 0;
95 cc->__cd_out.__cd.__data[0].__internal_use = 1;
96 cc->__cd_out.__cd.__data[0].__flags
97 = __GCONV_IS_LAST | __GCONV_TRANSLIT;
98 cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
99 }
100
101 /* From now on use the wide character callback functions. */
102 _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
103 }
104
105 /* Set the mode now. */
106 fp->_mode = mode;
107
108 return mode;
109}
110
111
112enum __codecvt_result
113__libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
114 const wchar_t *from_start, const wchar_t *from_end,
115 const wchar_t **from_stop, char *to_start, char *to_end,
116 char **to_stop)
117{
118 enum __codecvt_result result;
119
120 struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
121 int status;
122 size_t dummy;
123 const unsigned char *from_start_copy = (unsigned char *) from_start;
124
125 codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start;
126 codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end;
127 codecvt->__cd_out.__cd.__data[0].__statep = statep;
128
129 __gconv_fct fct = gs->__fct;
130#ifdef PTR_DEMANGLE
131 if (gs->__shlib_handle != NULL)
132 PTR_DEMANGLE (fct);
133#endif
134
135 status = DL_CALL_FCT (fct,
136 (gs, codecvt->__cd_out.__cd.__data, &from_start_copy,
137 (const unsigned char *) from_end, NULL,
138 &dummy, 0, 0));
139
140 *from_stop = (wchar_t *) from_start_copy;
141 *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf;
142
143 switch (status)
144 {
145 case __GCONV_OK:
146 case __GCONV_EMPTY_INPUT:
147 result = __codecvt_ok;
148 break;
149
150 case __GCONV_FULL_OUTPUT:
151 case __GCONV_INCOMPLETE_INPUT:
152 result = __codecvt_partial;
153 break;
154
155 default:
156 result = __codecvt_error;
157 break;
158 }
159
160 return result;
161}
162
163
164enum __codecvt_result
165__libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
166 const char *from_start, const char *from_end,
167 const char **from_stop,
168 wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
169{
170 enum __codecvt_result result;
171
172 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
173 int status;
174 size_t dummy;
175 const unsigned char *from_start_copy = (unsigned char *) from_start;
176
177 codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_start;
178 codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) to_end;
179 codecvt->__cd_in.__cd.__data[0].__statep = statep;
180
181 __gconv_fct fct = gs->__fct;
182#ifdef PTR_DEMANGLE
183 if (gs->__shlib_handle != NULL)
184 PTR_DEMANGLE (fct);
185#endif
186
187 status = DL_CALL_FCT (fct,
188 (gs, codecvt->__cd_in.__cd.__data, &from_start_copy,
189 (const unsigned char *) from_end, NULL,
190 &dummy, 0, 0));
191
192 *from_stop = (const char *) from_start_copy;
193 *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf;
194
195 switch (status)
196 {
197 case __GCONV_OK:
198 case __GCONV_EMPTY_INPUT:
199 result = __codecvt_ok;
200 break;
201
202 case __GCONV_FULL_OUTPUT:
203 case __GCONV_INCOMPLETE_INPUT:
204 result = __codecvt_partial;
205 break;
206
207 default:
208 result = __codecvt_error;
209 break;
210 }
211
212 return result;
213}
214
215
216int
217__libio_codecvt_encoding (struct _IO_codecvt *codecvt)
218{
219 /* See whether the encoding is stateful. */
220 if (codecvt->__cd_in.__cd.__steps[0].__stateful)
221 return -1;
222 /* Fortunately not. Now determine the input bytes for the conversion
223 necessary for each wide character. */
224 if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from
225 != codecvt->__cd_in.__cd.__steps[0].__max_needed_from)
226 /* Not a constant value. */
227 return 0;
228
229 return codecvt->__cd_in.__cd.__steps[0].__min_needed_from;
230}
231
232
233int
234__libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
235 const char *from_start, const char *from_end,
236 size_t max)
237{
238 int result;
239 const unsigned char *cp = (const unsigned char *) from_start;
240 wchar_t to_buf[max];
241 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
242 size_t dummy;
243
244 codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_buf;
245 codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) &to_buf[max];
246 codecvt->__cd_in.__cd.__data[0].__statep = statep;
247
248 __gconv_fct fct = gs->__fct;
249#ifdef PTR_DEMANGLE
250 if (gs->__shlib_handle != NULL)
251 PTR_DEMANGLE (fct);
252#endif
253
254 DL_CALL_FCT (fct,
255 (gs, codecvt->__cd_in.__cd.__data, &cp,
256 (const unsigned char *) from_end, NULL,
257 &dummy, 0, 0));
258
259 result = cp - (const unsigned char *) from_start;
260
261 return result;
262}
263