]>
Commit | Line | Data |
---|---|---|
20427c6b BK |
1 | /* Copyright (C) 1999, 2000 Free Software Foundation, Inc. |
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 <dlfcn.h> | |
29 | # include <wchar.h> | |
ae658d47 BK |
30 | # include <locale/localeinfo.h> |
31 | # include <wcsmbs/wcsmbsload.h> | |
32 | # include <iconv/gconv_int.h> | |
20427c6b BK |
33 | #endif |
34 | #include <stdlib.h> | |
35 | #include <string.h> | |
36 | ||
ae658d47 | 37 | #if defined(_LIBC) || defined(_GLIBCPP_USE_WCHAR_T) |
20427c6b | 38 | # include <langinfo.h> |
20427c6b BK |
39 | #endif |
40 | ||
3c97056f | 41 | #ifdef _GLIBCPP_USE_WCHAR_T |
20427c6b BK |
42 | /* Prototypes of libio's codecvt functions. */ |
43 | static enum __codecvt_result do_out (struct _IO_codecvt *codecvt, | |
dfb1d76f | 44 | __c_mbstate_t *statep, |
20427c6b BK |
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); | |
49 | static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt, | |
dfb1d76f | 50 | __c_mbstate_t *statep, char *to_start, |
20427c6b BK |
51 | char *to_end, char **to_stop); |
52 | static enum __codecvt_result do_in (struct _IO_codecvt *codecvt, | |
dfb1d76f | 53 | __c_mbstate_t *statep, |
20427c6b BK |
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); | |
58 | static int do_encoding (struct _IO_codecvt *codecvt); | |
dfb1d76f | 59 | static int do_length (struct _IO_codecvt *codecvt, __c_mbstate_t *statep, |
20427c6b BK |
60 | const char *from_start, |
61 | const char *from_end, _IO_size_t max); | |
62 | static int do_max_length (struct _IO_codecvt *codecvt); | |
63 | static int do_always_noconv (struct _IO_codecvt *codecvt); | |
64 | ||
65 | ||
66 | /* The functions used in `codecvt' for libio are always the same. */ | |
67 | 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 | ||
18acf3ba | 80 | #ifdef _LIBC |
d04703d6 | 81 | static struct __gconv_trans_data libio_translit = |
18acf3ba BK |
82 | { |
83 | .__trans_fct = __gconv_transliterate | |
84 | }; | |
85 | #endif | |
3c97056f | 86 | #endif /* defined(GLIBCPP_USE_WCHAR_T) */ |
d04703d6 | 87 | |
20427c6b BK |
88 | /* Return orientation of stream. If mode is nonzero try to change |
89 | the orientation first. */ | |
90 | #undef _IO_fwide | |
91 | int | |
92 | _IO_fwide (fp, mode) | |
93 | _IO_FILE *fp; | |
94 | int mode; | |
95 | { | |
96 | /* Normalize the value. */ | |
97 | mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1); | |
98 | ||
99 | if (mode == 0 || fp->_mode != 0) | |
100 | /* The caller simply wants to know about the current orientation | |
101 | or the orientation already has been determined. */ | |
102 | return fp->_mode; | |
103 | ||
20427c6b BK |
104 | /* Set the orientation appropriately. */ |
105 | if (mode > 0) | |
106 | { | |
3c97056f | 107 | #ifdef _GLIBCPP_USE_WCHAR_T |
d04703d6 | 108 | struct _IO_codecvt *cc = fp->_codecvt; |
20427c6b BK |
109 | |
110 | fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; | |
111 | fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base; | |
112 | ||
8901ac21 | 113 | #ifdef _LIBC |
20427c6b BK |
114 | /* Get the character conversion functions based on the currently |
115 | selected locale for LC_CTYPE. */ | |
20427c6b BK |
116 | { |
117 | struct gconv_fcts fcts; | |
118 | ||
119 | /* Clear the state. We start all over again. */ | |
dfb1d76f PE |
120 | memset (&fp->_wide_data->_IO_state, '\0', sizeof (__c_mbstate_t)); |
121 | memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__c_mbstate_t)); | |
20427c6b BK |
122 | |
123 | __wcsmbs_clone_conv (&fcts); | |
124 | ||
d04703d6 BK |
125 | /* The functions are always the same. */ |
126 | *cc = __libio_codecvt; | |
127 | ||
20427c6b BK |
128 | cc->__cd_in.__cd.__nsteps = 1; /* Only one step allowed. */ |
129 | cc->__cd_in.__cd.__steps = fcts.towc; | |
130 | ||
131 | cc->__cd_in.__cd.__data[0].__invocation_counter = 0; | |
132 | cc->__cd_in.__cd.__data[0].__internal_use = 1; | |
133 | cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST; | |
134 | cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; | |
135 | ||
136 | /* XXX For now no transliteration. */ | |
137 | cc->__cd_in.__cd.__data[0].__trans = NULL; | |
138 | ||
139 | cc->__cd_out.__cd.__nsteps = 1; /* Only one step allowed. */ | |
140 | cc->__cd_out.__cd.__steps = fcts.tomb; | |
141 | ||
142 | cc->__cd_out.__cd.__data[0].__invocation_counter = 0; | |
143 | cc->__cd_out.__cd.__data[0].__internal_use = 1; | |
144 | cc->__cd_out.__cd.__data[0].__flags = __GCONV_IS_LAST; | |
145 | cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; | |
146 | ||
d04703d6 | 147 | /* And now the transliteration. */ |
18acf3ba | 148 | cc->__cd_out.__cd.__data[0].__trans = &libio_translit; |
20427c6b BK |
149 | } |
150 | #else | |
151 | # ifdef _GLIBCPP_USE_WCHAR_T | |
152 | { | |
153 | /* Determine internal and external character sets. | |
20427c6b BK |
154 | XXX For now we make our life easy: we assume a fixed internal |
155 | encoding (as most sane systems have; hi HP/UX!). If somebody | |
156 | cares about systems which changing internal charsets they | |
157 | should come up with a solution for the determination of the | |
158 | currently used internal character set. */ | |
ffe9f785 | 159 | #if 0 |
20427c6b | 160 | const char *internal_ccs = _G_INTERNAL_CCS; |
ae658d47 | 161 | const char *external_ccs = nl_langinfo(CODESET); |
20427c6b | 162 | |
20427c6b BK |
163 | if (external_ccs == NULL) |
164 | external_ccs = "ISO-8859-1"; | |
165 | ||
166 | cc->__cd_in = iconv_open (internal_ccs, external_ccs); | |
167 | if (cc->__cd_in != (iconv_t) -1) | |
168 | cc->__cd_out = iconv_open (external_ccs, internal_ccs); | |
ffe9f785 | 169 | #endif |
20427c6b BK |
170 | } |
171 | # else | |
172 | # error "somehow determine this from LC_CTYPE" | |
173 | # endif | |
174 | #endif | |
175 | ||
176 | /* From now on use the wide character callback functions. */ | |
177 | ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable; | |
3c97056f MM |
178 | #else /* !defined(_GLIBCPP_USE_WCHAR_T) */ |
179 | mode = fp->_mode; | |
180 | #endif /* !defined(_GLIBCPP_USE_WCHAR_T) */ | |
20427c6b BK |
181 | } |
182 | ||
183 | /* Set the mode now. */ | |
184 | fp->_mode = mode; | |
185 | ||
20427c6b BK |
186 | return mode; |
187 | } | |
188 | ||
189 | #ifdef weak_alias | |
190 | weak_alias (_IO_fwide, fwide) | |
191 | #endif | |
192 | ||
3c97056f | 193 | #ifdef _GLIBCPP_USE_WCHAR_T |
d04703d6 | 194 | |
20427c6b | 195 | static enum __codecvt_result |
dfb1d76f | 196 | do_out (struct _IO_codecvt *codecvt, __c_mbstate_t *statep, |
20427c6b BK |
197 | const wchar_t *from_start, const wchar_t *from_end, |
198 | const wchar_t **from_stop, char *to_start, char *to_end, | |
199 | char **to_stop) | |
200 | { | |
201 | enum __codecvt_result result; | |
202 | ||
203 | #ifdef _LIBC | |
204 | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | |
205 | int status; | |
206 | size_t dummy; | |
207 | const unsigned char *from_start_copy = (unsigned char *) from_start; | |
208 | ||
209 | codecvt->__cd_out.__cd.__data[0].__outbuf = to_start; | |
210 | codecvt->__cd_out.__cd.__data[0].__outbufend = to_end; | |
211 | codecvt->__cd_out.__cd.__data[0].__statep = statep; | |
212 | ||
213 | status = DL_CALL_FCT (gs->__fct, | |
214 | (gs, codecvt->__cd_out.__cd.__data, &from_start_copy, | |
215 | (const unsigned char *) from_end, NULL, | |
216 | &dummy, 0, 0)); | |
217 | ||
218 | *from_stop = (wchar_t *) from_start_copy; | |
219 | *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf; | |
220 | ||
221 | switch (status) | |
222 | { | |
223 | case __GCONV_OK: | |
224 | case __GCONV_EMPTY_INPUT: | |
225 | result = __codecvt_ok; | |
226 | break; | |
227 | ||
228 | case __GCONV_FULL_OUTPUT: | |
229 | case __GCONV_INCOMPLETE_INPUT: | |
230 | result = __codecvt_partial; | |
231 | break; | |
232 | ||
233 | default: | |
234 | result = __codecvt_error; | |
235 | break; | |
236 | } | |
237 | #else | |
238 | # ifdef _GLIBCPP_USE_WCHAR_T | |
239 | size_t res; | |
240 | const char *from_start_copy = (const char *) from_start; | |
241 | size_t from_len = from_end - from_start; | |
8901ac21 | 242 | char *to_start_copy = to_start; |
20427c6b | 243 | size_t to_len = to_end - to_start; |
20427c6b BK |
244 | res = iconv (codecvt->__cd_out, &from_start_copy, &from_len, |
245 | &to_start_copy, &to_len); | |
8901ac21 | 246 | |
20427c6b BK |
247 | if (res == 0 || from_len == 0) |
248 | result = __codecvt_ok; | |
249 | else if (to_len < codecvt->__codecvt_do_max_length (codecvt)) | |
250 | result = __codecvt_partial; | |
251 | else | |
252 | result = __codecvt_error; | |
8901ac21 | 253 | |
20427c6b BK |
254 | # else |
255 | /* Decide what to do. */ | |
256 | result = __codecvt_error; | |
257 | # endif | |
258 | #endif | |
259 | ||
260 | return result; | |
261 | } | |
262 | ||
263 | ||
264 | static enum __codecvt_result | |
dfb1d76f | 265 | do_unshift (struct _IO_codecvt *codecvt, __c_mbstate_t *statep, |
20427c6b BK |
266 | char *to_start, char *to_end, char **to_stop) |
267 | { | |
268 | enum __codecvt_result result; | |
269 | ||
270 | #ifdef _LIBC | |
271 | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | |
272 | int status; | |
273 | size_t dummy; | |
274 | ||
275 | codecvt->__cd_out.__cd.__data[0].__outbuf = to_start; | |
276 | codecvt->__cd_out.__cd.__data[0].__outbufend = to_end; | |
277 | codecvt->__cd_out.__cd.__data[0].__statep = statep; | |
278 | ||
279 | status = DL_CALL_FCT (gs->__fct, | |
280 | (gs, codecvt->__cd_out.__cd.__data, NULL, NULL, | |
281 | NULL, &dummy, 1, 0)); | |
282 | ||
283 | *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf; | |
284 | ||
285 | switch (status) | |
286 | { | |
287 | case __GCONV_OK: | |
288 | case __GCONV_EMPTY_INPUT: | |
289 | result = __codecvt_ok; | |
290 | break; | |
291 | ||
292 | case __GCONV_FULL_OUTPUT: | |
293 | case __GCONV_INCOMPLETE_INPUT: | |
294 | result = __codecvt_partial; | |
295 | break; | |
296 | ||
297 | default: | |
298 | result = __codecvt_error; | |
299 | break; | |
300 | } | |
301 | #else | |
302 | # ifdef _GLIBCPP_USE_WCHAR_T | |
303 | size_t res; | |
304 | char *to_start_copy = (char *) to_start; | |
305 | size_t to_len = to_end - to_start; | |
306 | ||
307 | res = iconv (codecvt->__cd_out, NULL, NULL, &to_start_copy, &to_len); | |
d04703d6 | 308 | |
20427c6b BK |
309 | if (res == 0) |
310 | result = __codecvt_ok; | |
311 | else if (to_len < codecvt->__codecvt_do_max_length (codecvt)) | |
312 | result = __codecvt_partial; | |
313 | else | |
314 | result = __codecvt_error; | |
315 | # else | |
316 | /* Decide what to do. */ | |
317 | result = __codecvt_error; | |
318 | # endif | |
319 | #endif | |
320 | ||
321 | return result; | |
322 | } | |
323 | ||
324 | ||
325 | static enum __codecvt_result | |
dfb1d76f | 326 | do_in (struct _IO_codecvt *codecvt, __c_mbstate_t *statep, |
20427c6b BK |
327 | const char *from_start, const char *from_end, const char **from_stop, |
328 | wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop) | |
329 | { | |
330 | enum __codecvt_result result; | |
331 | ||
332 | #ifdef _LIBC | |
333 | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | |
334 | int status; | |
335 | size_t dummy; | |
336 | const unsigned char *from_start_copy = (unsigned char *) from_start; | |
337 | ||
338 | codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_start; | |
339 | codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) to_end; | |
340 | codecvt->__cd_in.__cd.__data[0].__statep = statep; | |
341 | ||
342 | status = DL_CALL_FCT (gs->__fct, | |
343 | (gs, codecvt->__cd_in.__cd.__data, &from_start_copy, | |
344 | from_end, NULL, &dummy, 0, 0)); | |
345 | ||
346 | *from_stop = from_start_copy; | |
347 | *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf; | |
348 | ||
349 | switch (status) | |
350 | { | |
351 | case __GCONV_OK: | |
352 | case __GCONV_EMPTY_INPUT: | |
353 | result = __codecvt_ok; | |
354 | break; | |
355 | ||
356 | case __GCONV_FULL_OUTPUT: | |
357 | case __GCONV_INCOMPLETE_INPUT: | |
358 | result = __codecvt_partial; | |
359 | break; | |
360 | ||
361 | default: | |
362 | result = __codecvt_error; | |
363 | break; | |
364 | } | |
365 | #else | |
366 | # ifdef _GLIBCPP_USE_WCHAR_T | |
367 | size_t res; | |
368 | const char *from_start_copy = (const char *) from_start; | |
369 | size_t from_len = from_end - from_start; | |
370 | char *to_start_copy = (char *) from_start; | |
371 | size_t to_len = to_end - to_start; | |
372 | ||
373 | res = iconv (codecvt->__cd_in, &from_start_copy, &from_len, | |
374 | &to_start_copy, &to_len); | |
d04703d6 | 375 | |
20427c6b BK |
376 | if (res == 0) |
377 | result = __codecvt_ok; | |
378 | else if (to_len == 0) | |
379 | result = __codecvt_partial; | |
380 | else if (from_len < codecvt->__codecvt_do_max_length (codecvt)) | |
381 | result = __codecvt_partial; | |
382 | else | |
383 | result = __codecvt_error; | |
384 | # else | |
385 | /* Decide what to do. */ | |
386 | result = __codecvt_error; | |
387 | # endif | |
388 | #endif | |
389 | ||
390 | return result; | |
391 | } | |
392 | ||
393 | ||
394 | static int | |
395 | do_encoding (struct _IO_codecvt *codecvt) | |
396 | { | |
397 | #ifdef _LIBC | |
398 | /* See whether the encoding is stateful. */ | |
399 | if (codecvt->__cd_in.__cd.__steps[0].__stateful) | |
400 | return -1; | |
401 | /* Fortunately not. Now determine the input bytes for the conversion | |
402 | necessary for each wide character. */ | |
403 | if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from | |
404 | != codecvt->__cd_in.__cd.__steps[0].__max_needed_from) | |
405 | /* Not a constant value. */ | |
406 | return 0; | |
407 | ||
408 | return codecvt->__cd_in.__cd.__steps[0].__min_needed_from; | |
409 | #else | |
410 | /* Worst case scenario. */ | |
411 | return -1; | |
412 | #endif | |
413 | } | |
414 | ||
415 | ||
416 | static int | |
417 | do_always_noconv (struct _IO_codecvt *codecvt) | |
418 | { | |
419 | return 0; | |
420 | } | |
421 | ||
422 | ||
423 | static int | |
dfb1d76f | 424 | do_length (struct _IO_codecvt *codecvt, __c_mbstate_t *statep, |
20427c6b BK |
425 | const char *from_start, const char *from_end, _IO_size_t max) |
426 | { | |
427 | int result; | |
428 | #ifdef _LIBC | |
429 | const unsigned char *cp = (const unsigned char *) from_start; | |
430 | wchar_t to_buf[max]; | |
431 | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | |
432 | int status; | |
433 | size_t dummy; | |
434 | ||
435 | codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_buf; | |
436 | codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) &to_buf[max]; | |
437 | codecvt->__cd_in.__cd.__data[0].__statep = statep; | |
438 | ||
439 | status = DL_CALL_FCT (gs->__fct, | |
440 | (gs, codecvt->__cd_in.__cd.__data, &cp, from_end, | |
441 | NULL, &dummy, 0, 0)); | |
442 | ||
443 | result = cp - (const unsigned char *) from_start; | |
444 | #else | |
445 | # ifdef _GLIBCPP_USE_WCHAR_T | |
446 | const char *from_start_copy = (const char *) from_start; | |
447 | size_t from_len = from_end - from_start; | |
448 | wchar_t to_buf[max]; | |
449 | size_t res; | |
450 | char *to_start = (char *) to_buf; | |
451 | ||
452 | res = iconv (codecvt->__cd_in, &from_start_copy, &from_len, | |
453 | &to_start, &max); | |
d04703d6 | 454 | |
20427c6b BK |
455 | result = from_start_copy - (char *) from_start; |
456 | # else | |
457 | /* Decide what to do. */ | |
458 | result = 0; | |
459 | # endif | |
460 | #endif | |
461 | ||
462 | return result; | |
463 | } | |
464 | ||
465 | ||
466 | static int | |
467 | do_max_length (struct _IO_codecvt *codecvt) | |
468 | { | |
469 | #ifdef _LIBC | |
470 | return codecvt->__cd_in.__cd.__steps[0].__max_needed_from; | |
471 | #else | |
472 | return MB_CUR_MAX; | |
473 | #endif | |
474 | } | |
3c97056f MM |
475 | |
476 | #endif /* defined(_GLIBCPP_USE_WCHAR_T) */ |