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 |
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.__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 | |
112 | enum __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 | |
164 | enum __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 | |
216 | int |
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 | |
233 | int |
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 | |