]>
Commit | Line | Data |
---|---|---|
4e2e9999 | 1 | /* Copyright (C) 1999, 2000 Free Software Foundation, Inc. |
d64b6ad0 UD |
2 | This file is part of the GNU IO Library. |
3 | ||
4 | This library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU General Public License as | |
6 | published by the Free Software Foundation; either version 2, or (at | |
7 | your option) any later version. | |
8 | ||
9 | This library is distributed in the hope that it will be useful, but | |
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this library; see the file COPYING. If not, write to | |
16 | the Free Software Foundation, 59 Temple Place - Suite 330, Boston, | |
17 | MA 02111-1307, USA. | |
18 | ||
19 | As a special exception, if you link this library with files | |
20 | compiled with a GNU compiler to produce an executable, this does | |
21 | not cause the resulting executable to be covered by the GNU General | |
22 | Public License. This exception does not however invalidate any | |
23 | other reasons why the executable file might be covered by the GNU | |
24 | General Public License. */ | |
25 | ||
26 | #include <libioP.h> | |
27 | #ifdef _LIBC | |
28 | # include <wchar.h> | |
29 | #endif | |
30 | #include <stdlib.h> | |
31 | #include <string.h> | |
32 | ||
33 | #ifdef _LIBC | |
34 | # include <langinfo.h> | |
35 | # include <locale/localeinfo.h> | |
36 | # include <wcsmbs/wcsmbsload.h> | |
37 | #endif | |
38 | ||
39 | ||
40 | /* Prototypes of libio's codecvt functions. */ | |
41 | static enum __codecvt_result do_out (struct _IO_codecvt *codecvt, | |
42 | __mbstate_t *statep, | |
43 | const wchar_t *from_start, | |
44 | const wchar_t *from_end, | |
45 | const wchar_t **from_stop, char *to_start, | |
46 | char *to_end, char **to_stop); | |
47 | static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt, | |
48 | __mbstate_t *statep, char *to_start, | |
49 | char *to_end, char **to_stop); | |
50 | static enum __codecvt_result do_in (struct _IO_codecvt *codecvt, | |
51 | __mbstate_t *statep, | |
52 | const char *from_start, | |
53 | const char *from_end, | |
54 | const char **from_stop, wchar_t *to_start, | |
55 | wchar_t *to_end, wchar_t **to_stop); | |
56 | static int do_encoding (struct _IO_codecvt *codecvt); | |
57 | static int do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, | |
58 | const char *from_start, | |
59 | const char *from_end, _IO_size_t max); | |
60 | static int do_max_length (struct _IO_codecvt *codecvt); | |
61 | static int do_always_noconv (struct _IO_codecvt *codecvt); | |
62 | ||
63 | ||
64 | /* The functions used in `codecvt' for libio are always the same. */ | |
4e2e9999 | 65 | struct _IO_codecvt __libio_codecvt = |
d64b6ad0 UD |
66 | { |
67 | .__codecvt_destr = NULL, /* Destructor, never used. */ | |
68 | .__codecvt_do_out = do_out, | |
69 | .__codecvt_do_unshift = do_unshift, | |
70 | .__codecvt_do_in = do_in, | |
71 | .__codecvt_do_encoding = do_encoding, | |
72 | .__codecvt_do_always_noconv = do_always_noconv, | |
73 | .__codecvt_do_length = do_length, | |
74 | .__codecvt_do_max_length = do_max_length | |
75 | }; | |
76 | ||
77 | ||
78 | /* Return orientation of stream. If mode is nonzero try to change | |
79 | the orientation first. */ | |
80 | #undef _IO_fwide | |
81 | int | |
82 | _IO_fwide (fp, mode) | |
83 | _IO_FILE *fp; | |
84 | int mode; | |
85 | { | |
86 | /* Normalize the value. */ | |
87 | mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1); | |
88 | ||
89 | if (mode == 0 || fp->_mode != 0) | |
90 | /* The caller simply wants to know about the current orientation | |
91 | or the orientation already has been determined. */ | |
92 | return fp->_mode; | |
93 | ||
94 | _IO_cleanup_region_start ((void (*) __P ((void *))) _IO_funlockfile, fp); | |
95 | _IO_flockfile (fp); | |
96 | ||
97 | /* Set the orientation appropriately. */ | |
98 | if (mode > 0) | |
99 | { | |
100 | fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; | |
101 | fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base; | |
102 | ||
103 | /* Clear the state. We start all over again. */ | |
104 | memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); | |
105 | memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t)); | |
106 | ||
107 | /* Get the character conversion functions based on the currently | |
108 | selected locale for LC_CTYPE. */ | |
109 | #ifdef _LIBC | |
110 | { | |
111 | struct gconv_fcts fcts; | |
112 | struct _IO_codecvt *cc = &fp->_wide_data->_codecvt; | |
113 | ||
114 | __wcsmbs_clone_conv (&fcts); | |
115 | ||
116 | /* The functions are always the same. */ | |
4e2e9999 | 117 | *cc = __libio_codecvt; |
d64b6ad0 UD |
118 | |
119 | cc->__cd_in.__cd.__nsteps = 1; /* Only one step allowed. */ | |
120 | cc->__cd_in.__cd.__steps = fcts.towc; | |
121 | ||
122 | cc->__cd_in.__cd.__data[0].__invocation_counter = 0; | |
123 | cc->__cd_in.__cd.__data[0].__internal_use = 1; | |
85830c4c | 124 | cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST; |
d64b6ad0 UD |
125 | cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; |
126 | ||
127 | cc->__cd_out.__cd.__nsteps = 1; /* Only one step allowed. */ | |
128 | cc->__cd_out.__cd.__steps = fcts.tomb; | |
129 | ||
130 | cc->__cd_out.__cd.__data[0].__invocation_counter = 0; | |
131 | cc->__cd_out.__cd.__data[0].__internal_use = 1; | |
85830c4c | 132 | cc->__cd_out.__cd.__data[0].__flags = __GCONV_IS_LAST; |
d64b6ad0 UD |
133 | cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; |
134 | } | |
135 | #else | |
136 | # error "somehow determine this from LC_CTYPE" | |
137 | #endif | |
138 | ||
139 | /* From now on use the wide character callback functions. */ | |
140 | ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable; | |
141 | } | |
142 | ||
143 | /* Set the mode now. */ | |
144 | fp->_mode = mode; | |
145 | ||
146 | _IO_funlockfile (fp); | |
147 | _IO_cleanup_region_end (0); | |
148 | ||
149 | return mode; | |
150 | } | |
151 | ||
152 | #ifdef weak_alias | |
153 | weak_alias (_IO_fwide, fwide) | |
154 | #endif | |
155 | ||
156 | ||
157 | static enum __codecvt_result | |
158 | do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep, | |
159 | const wchar_t *from_start, const wchar_t *from_end, | |
160 | const wchar_t **from_stop, char *to_start, char *to_end, | |
161 | char **to_stop) | |
162 | { | |
163 | enum __codecvt_result result; | |
164 | ||
165 | #ifdef _LIBC | |
166 | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | |
167 | int status; | |
168 | size_t written; | |
169 | const unsigned char *from_start_copy = (unsigned char *) from_start; | |
170 | ||
171 | codecvt->__cd_out.__cd.__data[0].__outbuf = to_start; | |
172 | codecvt->__cd_out.__cd.__data[0].__outbufend = to_end; | |
173 | codecvt->__cd_out.__cd.__data[0].__statep = statep; | |
174 | ||
175 | status = (*gs->__fct) (gs, codecvt->__cd_out.__cd.__data, &from_start_copy, | |
fd1b5c0f | 176 | (const unsigned char *) from_end, &written, 0, 0); |
d64b6ad0 UD |
177 | |
178 | *from_stop = (wchar_t *) from_start_copy; | |
179 | *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf; | |
180 | ||
181 | switch (status) | |
182 | { | |
183 | case __GCONV_OK: | |
184 | case __GCONV_EMPTY_INPUT: | |
185 | result = __codecvt_ok; | |
186 | break; | |
187 | ||
188 | case __GCONV_FULL_OUTPUT: | |
189 | case __GCONV_INCOMPLETE_INPUT: | |
190 | result = __codecvt_partial; | |
191 | break; | |
192 | ||
193 | default: | |
194 | result = __codecvt_error; | |
195 | break; | |
196 | } | |
197 | #else | |
198 | /* Decide what to do. */ | |
199 | result = __codecvt_error; | |
200 | #endif | |
201 | ||
202 | return result; | |
203 | } | |
204 | ||
205 | ||
206 | static enum __codecvt_result | |
207 | do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep, | |
208 | char *to_start, char *to_end, char **to_stop) | |
209 | { | |
210 | enum __codecvt_result result; | |
211 | ||
212 | #ifdef _LIBC | |
213 | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | |
214 | int status; | |
215 | size_t written; | |
216 | ||
217 | codecvt->__cd_out.__cd.__data[0].__outbuf = to_start; | |
218 | codecvt->__cd_out.__cd.__data[0].__outbufend = to_end; | |
219 | codecvt->__cd_out.__cd.__data[0].__statep = statep; | |
220 | ||
221 | status = (*gs->__fct) (gs, codecvt->__cd_out.__cd.__data, NULL, NULL, | |
fd1b5c0f | 222 | &written, 1, 0); |
d64b6ad0 UD |
223 | |
224 | *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf; | |
225 | ||
226 | switch (status) | |
227 | { | |
228 | case __GCONV_OK: | |
229 | case __GCONV_EMPTY_INPUT: | |
230 | result = __codecvt_ok; | |
231 | break; | |
232 | ||
233 | case __GCONV_FULL_OUTPUT: | |
234 | case __GCONV_INCOMPLETE_INPUT: | |
235 | result = __codecvt_partial; | |
236 | break; | |
237 | ||
238 | default: | |
239 | result = __codecvt_error; | |
240 | break; | |
241 | } | |
242 | #else | |
243 | /* Decide what to do. */ | |
244 | result = __codecvt_error; | |
245 | #endif | |
246 | ||
247 | return result; | |
248 | } | |
249 | ||
250 | ||
251 | static enum __codecvt_result | |
252 | do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep, | |
253 | const char *from_start, const char *from_end, const char **from_stop, | |
254 | wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop) | |
255 | { | |
256 | enum __codecvt_result result; | |
257 | ||
258 | #ifdef _LIBC | |
259 | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | |
260 | int status; | |
261 | size_t written; | |
262 | const unsigned char *from_start_copy = (unsigned char *) from_start; | |
263 | ||
264 | codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_start; | |
265 | codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) to_end; | |
266 | codecvt->__cd_in.__cd.__data[0].__statep = statep; | |
267 | ||
268 | status = (*gs->__fct) (gs, codecvt->__cd_in.__cd.__data, &from_start_copy, | |
fd1b5c0f | 269 | from_end, &written, 0, 0); |
d64b6ad0 UD |
270 | |
271 | *from_stop = from_start_copy; | |
272 | *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf; | |
273 | ||
274 | switch (status) | |
275 | { | |
276 | case __GCONV_OK: | |
277 | case __GCONV_EMPTY_INPUT: | |
278 | result = __codecvt_ok; | |
279 | break; | |
280 | ||
281 | case __GCONV_FULL_OUTPUT: | |
282 | case __GCONV_INCOMPLETE_INPUT: | |
283 | result = __codecvt_partial; | |
284 | break; | |
285 | ||
286 | default: | |
287 | result = __codecvt_error; | |
288 | break; | |
289 | } | |
290 | #else | |
291 | /* Decide what to do. */ | |
292 | result = __codecvt_error; | |
293 | #endif | |
294 | ||
295 | return result; | |
296 | } | |
297 | ||
298 | ||
299 | static int | |
300 | do_encoding (struct _IO_codecvt *codecvt) | |
301 | { | |
302 | #ifdef _LIBC | |
303 | /* See whether the encoding is stateful. */ | |
304 | if (codecvt->__cd_in.__cd.__steps[0].__stateful) | |
305 | return -1; | |
306 | /* Fortunately not. Now determine the input bytes for the conversion | |
307 | necessary for each wide character. */ | |
308 | if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from | |
309 | != codecvt->__cd_in.__cd.__steps[0].__max_needed_from) | |
310 | /* Not a constant value. */ | |
311 | return 0; | |
312 | ||
313 | return codecvt->__cd_in.__cd.__steps[0].__min_needed_from; | |
314 | #else | |
315 | /* Worst case scenario. */ | |
316 | return -1; | |
317 | #endif | |
318 | } | |
319 | ||
320 | ||
321 | static int | |
322 | do_always_noconv (struct _IO_codecvt *codecvt) | |
323 | { | |
324 | return 0; | |
325 | } | |
326 | ||
327 | ||
328 | static int | |
329 | do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, | |
330 | const char *from_start, const char *from_end, _IO_size_t max) | |
331 | { | |
332 | int result; | |
333 | #ifdef _LIBC | |
334 | const unsigned char *cp = (const unsigned char *) from_start; | |
335 | wchar_t to_buf[max]; | |
336 | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | |
337 | int status; | |
338 | size_t written; | |
339 | ||
340 | codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_buf; | |
341 | codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) &to_buf[max]; | |
342 | codecvt->__cd_in.__cd.__data[0].__statep = statep; | |
343 | ||
344 | status = (*gs->__fct) (gs, codecvt->__cd_in.__cd.__data, &cp, from_end, | |
fd1b5c0f | 345 | &written, 0, 0); |
d64b6ad0 UD |
346 | |
347 | result = cp - (const unsigned char *) from_start; | |
348 | #else | |
349 | /* Decide what to do. */ | |
350 | result = 0; | |
351 | #endif | |
352 | ||
353 | return result; | |
354 | } | |
355 | ||
356 | ||
357 | static int | |
358 | do_max_length (struct _IO_codecvt *codecvt) | |
359 | { | |
360 | #ifdef _LIBC | |
361 | return codecvt->__cd_in.__cd.__steps[0].__max_needed_from; | |
362 | #else | |
363 | return MB_CUR_MAX; | |
364 | #endif | |
365 | } |