(_IO_fwide): If compatibility with glibc 2.0 is needed tst for such an old structure...
[glibc.git] / libio / iofwide.c
blob2e2854aa624a308e59e5983f8ac51297e80c7854
1 /* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
2 This file is part of the GNU IO Library.
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.
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.
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.
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. */
26 #include <libioP.h>
27 #ifdef _LIBC
28 # include <dlfcn.h>
29 # include <wchar.h>
30 #endif
31 #include <stdlib.h>
32 #include <string.h>
34 #ifdef _LIBC
35 # include <langinfo.h>
36 # include <locale/localeinfo.h>
37 # include <wcsmbs/wcsmbsload.h>
38 # include <iconv/gconv_int.h>
39 # include <shlib-compat.h>
40 #endif
43 /* Prototypes of libio's codecvt functions. */
44 static enum __codecvt_result do_out (struct _IO_codecvt *codecvt,
45 __mbstate_t *statep,
46 const wchar_t *from_start,
47 const wchar_t *from_end,
48 const wchar_t **from_stop, char *to_start,
49 char *to_end, char **to_stop);
50 static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt,
51 __mbstate_t *statep, char *to_start,
52 char *to_end, char **to_stop);
53 static enum __codecvt_result do_in (struct _IO_codecvt *codecvt,
54 __mbstate_t *statep,
55 const char *from_start,
56 const char *from_end,
57 const char **from_stop, wchar_t *to_start,
58 wchar_t *to_end, wchar_t **to_stop);
59 static int do_encoding (struct _IO_codecvt *codecvt);
60 static int do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
61 const char *from_start,
62 const char *from_end, _IO_size_t max);
63 static int do_max_length (struct _IO_codecvt *codecvt);
64 static int do_always_noconv (struct _IO_codecvt *codecvt);
67 /* The functions used in `codecvt' for libio are always the same. */
68 struct _IO_codecvt __libio_codecvt =
70 .__codecvt_destr = NULL, /* Destructor, never used. */
71 .__codecvt_do_out = do_out,
72 .__codecvt_do_unshift = do_unshift,
73 .__codecvt_do_in = do_in,
74 .__codecvt_do_encoding = do_encoding,
75 .__codecvt_do_always_noconv = do_always_noconv,
76 .__codecvt_do_length = do_length,
77 .__codecvt_do_max_length = do_max_length
81 #ifdef _LIBC
82 static struct __gconv_trans_data libio_translit =
84 .__trans_fct = __gconv_transliterate
86 #endif
89 /* Return orientation of stream. If mode is nonzero try to change
90 the orientation first. */
91 #undef _IO_fwide
92 int
93 _IO_fwide (fp, mode)
94 _IO_FILE *fp;
95 int mode;
97 /* Normalize the value. */
98 mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1);
100 if (mode == 0)
101 /* The caller simply wants to know about the current orientation. */
102 return fp->_mode;
104 #if defined SHARED && defined _LIBC \
105 && SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
106 if (__builtin_expect (&_IO_stdin_used == NULL, 0)
107 && (fp == _IO_stdin || fp == _IO_stdout || fp == _IO_stderr))
108 /* This is for a stream in the glibc 2.0 format. */
109 return -1;
110 #endif
112 if (fp->_mode != 0)
113 /* The orientation already has been determined. */
114 return fp->_mode;
116 /* Set the orientation appropriately. */
117 if (mode > 0)
119 struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt;
121 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
122 fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;
124 /* Get the character conversion functions based on the currently
125 selected locale for LC_CTYPE. */
126 #ifdef _LIBC
128 struct gconv_fcts fcts;
130 /* Clear the state. We start all over again. */
131 memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t));
132 memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
134 __wcsmbs_clone_conv (&fcts);
136 /* The functions are always the same. */
137 *cc = __libio_codecvt;
139 cc->__cd_in.__cd.__nsteps = 1; /* Only one step allowed. */
140 cc->__cd_in.__cd.__steps = fcts.towc;
142 cc->__cd_in.__cd.__data[0].__invocation_counter = 0;
143 cc->__cd_in.__cd.__data[0].__internal_use = 1;
144 cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
145 cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
147 /* XXX For now no transliteration. */
148 cc->__cd_in.__cd.__data[0].__trans = NULL;
150 cc->__cd_out.__cd.__nsteps = 1; /* Only one step allowed. */
151 cc->__cd_out.__cd.__steps = fcts.tomb;
153 cc->__cd_out.__cd.__data[0].__invocation_counter = 0;
154 cc->__cd_out.__cd.__data[0].__internal_use = 1;
155 cc->__cd_out.__cd.__data[0].__flags = __GCONV_IS_LAST;
156 cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state;
158 /* And now the transliteration. */
159 #ifdef _LIBC
160 cc->__cd_out.__cd.__data[0].__trans = &libio_translit;
161 #else
162 cc->__cd_out.__cd.__data[0].__trans = NULL;
163 #endif
165 #else
166 # ifdef _GLIBCPP_USE_WCHAR_T
168 /* Determine internal and external character sets.
170 XXX For now we make our life easy: we assume a fixed internal
171 encoding (as most sane systems have; hi HP/UX!). If somebody
172 cares about systems which changing internal charsets they
173 should come up with a solution for the determination of the
174 currently used internal character set. */
175 const char *internal_ccs = _G_INTERNAL_CCS;
176 const char *external_ccs = NULL;
178 # ifdef HAVE_NL_LANGINFO
179 external_ccs = nl_langinfo (CODESET);
180 # endif
181 if (external_ccs == NULL)
182 external_ccs = "ISO-8859-1";
184 cc->__cd_in = iconv_open (internal_ccs, external_ccs);
185 if (cc->__cd_in != (iconv_t) -1)
186 cc->__cd_out = iconv_open (external_ccs, internal_ccs);
188 if (cc->__cd_in == (iconv_t) -1 || cc->__cd_out == (iconv_t) -1)
189 /* XXX */
190 abort ();
192 # else
193 # error "somehow determine this from LC_CTYPE"
194 # endif
195 #endif
197 /* From now on use the wide character callback functions. */
198 ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable;
201 /* Set the mode now. */
202 fp->_mode = mode;
204 return mode;
207 #ifdef weak_alias
208 weak_alias (_IO_fwide, fwide)
209 #endif
212 static enum __codecvt_result
213 do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
214 const wchar_t *from_start, const wchar_t *from_end,
215 const wchar_t **from_stop, char *to_start, char *to_end,
216 char **to_stop)
218 enum __codecvt_result result;
220 #ifdef _LIBC
221 struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
222 int status;
223 size_t dummy;
224 const unsigned char *from_start_copy = (unsigned char *) from_start;
226 codecvt->__cd_out.__cd.__data[0].__outbuf = to_start;
227 codecvt->__cd_out.__cd.__data[0].__outbufend = to_end;
228 codecvt->__cd_out.__cd.__data[0].__statep = statep;
230 status = DL_CALL_FCT (gs->__fct,
231 (gs, codecvt->__cd_out.__cd.__data, &from_start_copy,
232 (const unsigned char *) from_end, NULL,
233 &dummy, 0, 0));
235 *from_stop = (wchar_t *) from_start_copy;
236 *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf;
238 switch (status)
240 case __GCONV_OK:
241 case __GCONV_EMPTY_INPUT:
242 result = __codecvt_ok;
243 break;
245 case __GCONV_FULL_OUTPUT:
246 case __GCONV_INCOMPLETE_INPUT:
247 result = __codecvt_partial;
248 break;
250 default:
251 result = __codecvt_error;
252 break;
254 #else
255 # ifdef _GLIBCPP_USE_WCHAR_T
256 size_t res;
257 const char *from_start_copy = (const char *) from_start;
258 size_t from_len = from_end - from_start;
259 char *to_start_copy = to_start;
260 size_t to_len = to_end - to_start;
261 res = iconv (codecvt->__cd_out, &from_start_copy, &from_len,
262 &to_start_copy, &to_len);
264 if (res == 0 || from_len == 0)
265 result = __codecvt_ok;
266 else if (to_len < codecvt->__codecvt_do_max_length (codecvt))
267 result = __codecvt_partial;
268 else
269 result = __codecvt_error;
271 # else
272 /* Decide what to do. */
273 result = __codecvt_error;
274 # endif
275 #endif
277 return result;
281 static enum __codecvt_result
282 do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep,
283 char *to_start, char *to_end, char **to_stop)
285 enum __codecvt_result result;
287 #ifdef _LIBC
288 struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
289 int status;
290 size_t dummy;
292 codecvt->__cd_out.__cd.__data[0].__outbuf = to_start;
293 codecvt->__cd_out.__cd.__data[0].__outbufend = to_end;
294 codecvt->__cd_out.__cd.__data[0].__statep = statep;
296 status = DL_CALL_FCT (gs->__fct,
297 (gs, codecvt->__cd_out.__cd.__data, NULL, NULL,
298 NULL, &dummy, 1, 0));
300 *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf;
302 switch (status)
304 case __GCONV_OK:
305 case __GCONV_EMPTY_INPUT:
306 result = __codecvt_ok;
307 break;
309 case __GCONV_FULL_OUTPUT:
310 case __GCONV_INCOMPLETE_INPUT:
311 result = __codecvt_partial;
312 break;
314 default:
315 result = __codecvt_error;
316 break;
318 #else
319 # ifdef _GLIBCPP_USE_WCHAR_T
320 size_t res;
321 char *to_start_copy = (char *) to_start;
322 size_t to_len = to_end - to_start;
324 res = iconv (codecvt->__cd_out, NULL, NULL, &to_start_copy, &to_len);
326 if (res == 0)
327 result = __codecvt_ok;
328 else if (to_len < codecvt->__codecvt_do_max_length (codecvt))
329 result = __codecvt_partial;
330 else
331 result = __codecvt_error;
332 # else
333 /* Decide what to do. */
334 result = __codecvt_error;
335 # endif
336 #endif
338 return result;
342 static enum __codecvt_result
343 do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
344 const char *from_start, const char *from_end, const char **from_stop,
345 wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
347 enum __codecvt_result result;
349 #ifdef _LIBC
350 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
351 int status;
352 size_t dummy;
353 const unsigned char *from_start_copy = (unsigned char *) from_start;
355 codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_start;
356 codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) to_end;
357 codecvt->__cd_in.__cd.__data[0].__statep = statep;
359 status = DL_CALL_FCT (gs->__fct,
360 (gs, codecvt->__cd_in.__cd.__data, &from_start_copy,
361 from_end, NULL, &dummy, 0, 0));
363 *from_stop = from_start_copy;
364 *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf;
366 switch (status)
368 case __GCONV_OK:
369 case __GCONV_EMPTY_INPUT:
370 result = __codecvt_ok;
371 break;
373 case __GCONV_FULL_OUTPUT:
374 case __GCONV_INCOMPLETE_INPUT:
375 result = __codecvt_partial;
376 break;
378 default:
379 result = __codecvt_error;
380 break;
382 #else
383 # ifdef _GLIBCPP_USE_WCHAR_T
384 size_t res;
385 const char *from_start_copy = (const char *) from_start;
386 size_t from_len = from_end - from_start;
387 char *to_start_copy = (char *) from_start;
388 size_t to_len = to_end - to_start;
390 res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
391 &to_start_copy, &to_len);
393 if (res == 0)
394 result = __codecvt_ok;
395 else if (to_len == 0)
396 result = __codecvt_partial;
397 else if (from_len < codecvt->__codecvt_do_max_length (codecvt))
398 result = __codecvt_partial;
399 else
400 result = __codecvt_error;
401 # else
402 /* Decide what to do. */
403 result = __codecvt_error;
404 # endif
405 #endif
407 return result;
411 static int
412 do_encoding (struct _IO_codecvt *codecvt)
414 #ifdef _LIBC
415 /* See whether the encoding is stateful. */
416 if (codecvt->__cd_in.__cd.__steps[0].__stateful)
417 return -1;
418 /* Fortunately not. Now determine the input bytes for the conversion
419 necessary for each wide character. */
420 if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from
421 != codecvt->__cd_in.__cd.__steps[0].__max_needed_from)
422 /* Not a constant value. */
423 return 0;
425 return codecvt->__cd_in.__cd.__steps[0].__min_needed_from;
426 #else
427 /* Worst case scenario. */
428 return -1;
429 #endif
433 static int
434 do_always_noconv (struct _IO_codecvt *codecvt)
436 return 0;
440 static int
441 do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
442 const char *from_start, const char *from_end, _IO_size_t max)
444 int result;
445 #ifdef _LIBC
446 const unsigned char *cp = (const unsigned char *) from_start;
447 wchar_t to_buf[max];
448 struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
449 int status;
450 size_t dummy;
452 codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_buf;
453 codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) &to_buf[max];
454 codecvt->__cd_in.__cd.__data[0].__statep = statep;
456 status = DL_CALL_FCT (gs->__fct,
457 (gs, codecvt->__cd_in.__cd.__data, &cp, from_end,
458 NULL, &dummy, 0, 0));
460 result = cp - (const unsigned char *) from_start;
461 #else
462 # ifdef _GLIBCPP_USE_WCHAR_T
463 const char *from_start_copy = (const char *) from_start;
464 size_t from_len = from_end - from_start;
465 wchar_t to_buf[max];
466 size_t res;
467 char *to_start = (char *) to_buf;
469 res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
470 &to_start, &max);
472 result = from_start_copy - (char *) from_start;
473 # else
474 /* Decide what to do. */
475 result = 0;
476 # endif
477 #endif
479 return result;
483 static int
484 do_max_length (struct _IO_codecvt *codecvt)
486 #ifdef _LIBC
487 return codecvt->__cd_in.__cd.__steps[0].__max_needed_from;
488 #else
489 return MB_CUR_MAX;
490 #endif