Update.
[jlayton/glibc.git] / libio / iofwide.c
1 /* Copyright (C) 1999, 2000, 2001 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>
30 #endif
31 #include <stdlib.h>
32 #include <string.h>
33
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
41
42
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);
65
66
67 /* The functions used in `codecvt' for libio are always the same.  */
68 struct _IO_codecvt __libio_codecvt =
69 {
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
78 };
79
80
81 #ifdef _LIBC
82 static struct __gconv_trans_data libio_translit =
83 {
84   .__trans_fct = __gconv_transliterate
85 };
86 #endif
87
88
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;
96 {
97   /* Normalize the value.  */
98   mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1);
99
100   if (mode == 0)
101     /* The caller simply wants to know about the current orientation.  */
102     return fp->_mode;
103
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
111
112   if (fp->_mode != 0)
113     /* The orientation already has been determined.  */
114     return fp->_mode;
115
116   /* Set the orientation appropriately.  */
117   if (mode > 0)
118     {
119       struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt;
120
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;
123
124       /* Get the character conversion functions based on the currently
125          selected locale for LC_CTYPE.  */
126 #ifdef _LIBC
127       {
128         struct gconv_fcts fcts;
129
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));
133
134         __wcsmbs_clone_conv (&fcts);
135
136         /* The functions are always the same.  */
137         *cc = __libio_codecvt;
138
139         cc->__cd_in.__cd.__nsteps = 1; /* Only one step allowed.  */
140         cc->__cd_in.__cd.__steps = fcts.towc;
141
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;
146
147         /* XXX For now no transliteration.  */
148         cc->__cd_in.__cd.__data[0].__trans = NULL;
149
150         cc->__cd_out.__cd.__nsteps = 1; /* Only one step allowed.  */
151         cc->__cd_out.__cd.__steps = fcts.tomb;
152
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;
157
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
164       }
165 #else
166 # ifdef _GLIBCPP_USE_WCHAR_T
167       {
168         /* Determine internal and external character sets.
169
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;
177
178 #  ifdef HAVE_NL_LANGINFO
179         external_ccs = nl_langinfo (CODESET);
180 #  endif
181         if (external_ccs == NULL)
182           external_ccs = "ISO-8859-1";
183
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);
187
188         if (cc->__cd_in == (iconv_t) -1 || cc->__cd_out == (iconv_t) -1)
189           /* XXX */
190           abort ();
191       }
192 # else
193 #  error "somehow determine this from LC_CTYPE"
194 # endif
195 #endif
196
197       /* From now on use the wide character callback functions.  */
198       ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable;
199     }
200
201   /* Set the mode now.  */
202   fp->_mode = mode;
203
204   return mode;
205 }
206
207
208 static enum __codecvt_result
209 do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
210         const wchar_t *from_start, const wchar_t *from_end,
211         const wchar_t **from_stop, char *to_start, char *to_end,
212         char **to_stop)
213 {
214   enum __codecvt_result result;
215
216 #ifdef _LIBC
217   struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
218   int status;
219   size_t dummy;
220   const unsigned char *from_start_copy = (unsigned char *) from_start;
221
222   codecvt->__cd_out.__cd.__data[0].__outbuf = to_start;
223   codecvt->__cd_out.__cd.__data[0].__outbufend = to_end;
224   codecvt->__cd_out.__cd.__data[0].__statep = statep;
225
226   status = DL_CALL_FCT (gs->__fct,
227                         (gs, codecvt->__cd_out.__cd.__data, &from_start_copy,
228                          (const unsigned char *) from_end, NULL,
229                          &dummy, 0, 0));
230
231   *from_stop = (wchar_t *) from_start_copy;
232   *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf;
233
234   switch (status)
235     {
236     case __GCONV_OK:
237     case __GCONV_EMPTY_INPUT:
238       result = __codecvt_ok;
239       break;
240
241     case __GCONV_FULL_OUTPUT:
242     case __GCONV_INCOMPLETE_INPUT:
243       result = __codecvt_partial;
244       break;
245
246     default:
247       result = __codecvt_error;
248       break;
249     }
250 #else
251 # ifdef _GLIBCPP_USE_WCHAR_T
252   size_t res;
253   const char *from_start_copy = (const char *) from_start;
254   size_t from_len = from_end - from_start;
255   char *to_start_copy = to_start;
256   size_t to_len = to_end - to_start;
257   res = iconv (codecvt->__cd_out, &from_start_copy, &from_len,
258                &to_start_copy, &to_len);
259
260   if (res == 0 || from_len == 0)
261     result = __codecvt_ok;
262   else if (to_len < codecvt->__codecvt_do_max_length (codecvt))
263     result = __codecvt_partial;
264   else
265     result = __codecvt_error;
266
267 # else
268   /* Decide what to do.  */
269   result = __codecvt_error;
270 # endif
271 #endif
272
273   return result;
274 }
275
276
277 static enum __codecvt_result
278 do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep,
279             char *to_start, char *to_end, char **to_stop)
280 {
281   enum __codecvt_result result;
282
283 #ifdef _LIBC
284   struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps;
285   int status;
286   size_t dummy;
287
288   codecvt->__cd_out.__cd.__data[0].__outbuf = to_start;
289   codecvt->__cd_out.__cd.__data[0].__outbufend = to_end;
290   codecvt->__cd_out.__cd.__data[0].__statep = statep;
291
292   status = DL_CALL_FCT (gs->__fct,
293                         (gs, codecvt->__cd_out.__cd.__data, NULL, NULL,
294                          NULL, &dummy, 1, 0));
295
296   *to_stop = codecvt->__cd_out.__cd.__data[0].__outbuf;
297
298   switch (status)
299     {
300     case __GCONV_OK:
301     case __GCONV_EMPTY_INPUT:
302       result = __codecvt_ok;
303       break;
304
305     case __GCONV_FULL_OUTPUT:
306     case __GCONV_INCOMPLETE_INPUT:
307       result = __codecvt_partial;
308       break;
309
310     default:
311       result = __codecvt_error;
312       break;
313     }
314 #else
315 # ifdef _GLIBCPP_USE_WCHAR_T
316   size_t res;
317   char *to_start_copy = (char *) to_start;
318   size_t to_len = to_end - to_start;
319
320   res = iconv (codecvt->__cd_out, NULL, NULL, &to_start_copy, &to_len);
321
322   if (res == 0)
323     result = __codecvt_ok;
324   else if (to_len < codecvt->__codecvt_do_max_length (codecvt))
325     result = __codecvt_partial;
326   else
327     result = __codecvt_error;
328 # else
329   /* Decide what to do.  */
330   result = __codecvt_error;
331 # endif
332 #endif
333
334   return result;
335 }
336
337
338 static enum __codecvt_result
339 do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
340        const char *from_start, const char *from_end, const char **from_stop,
341        wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
342 {
343   enum __codecvt_result result;
344
345 #ifdef _LIBC
346   struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
347   int status;
348   size_t dummy;
349   const unsigned char *from_start_copy = (unsigned char *) from_start;
350
351   codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_start;
352   codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) to_end;
353   codecvt->__cd_in.__cd.__data[0].__statep = statep;
354
355   status = DL_CALL_FCT (gs->__fct,
356                         (gs, codecvt->__cd_in.__cd.__data, &from_start_copy,
357                          from_end, NULL, &dummy, 0, 0));
358
359   *from_stop = from_start_copy;
360   *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf;
361
362   switch (status)
363     {
364     case __GCONV_OK:
365     case __GCONV_EMPTY_INPUT:
366       result = __codecvt_ok;
367       break;
368
369     case __GCONV_FULL_OUTPUT:
370     case __GCONV_INCOMPLETE_INPUT:
371       result = __codecvt_partial;
372       break;
373
374     default:
375       result = __codecvt_error;
376       break;
377     }
378 #else
379 # ifdef _GLIBCPP_USE_WCHAR_T
380   size_t res;
381   const char *from_start_copy = (const char *) from_start;
382   size_t from_len = from_end - from_start;
383   char *to_start_copy = (char *) from_start;
384   size_t to_len = to_end - to_start;
385
386   res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
387                &to_start_copy, &to_len);
388
389   if (res == 0)
390     result = __codecvt_ok;
391   else if (to_len == 0)
392     result = __codecvt_partial;
393   else if (from_len < codecvt->__codecvt_do_max_length (codecvt))
394     result = __codecvt_partial;
395   else
396     result = __codecvt_error;
397 # else
398   /* Decide what to do.  */
399   result = __codecvt_error;
400 # endif
401 #endif
402
403   return result;
404 }
405
406
407 static int
408 do_encoding (struct _IO_codecvt *codecvt)
409 {
410 #ifdef _LIBC
411   /* See whether the encoding is stateful.  */
412   if (codecvt->__cd_in.__cd.__steps[0].__stateful)
413     return -1;
414   /* Fortunately not.  Now determine the input bytes for the conversion
415      necessary for each wide character.  */
416   if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from
417       != codecvt->__cd_in.__cd.__steps[0].__max_needed_from)
418     /* Not a constant value.  */
419     return 0;
420
421   return codecvt->__cd_in.__cd.__steps[0].__min_needed_from;
422 #else
423   /* Worst case scenario.  */
424   return -1;
425 #endif
426 }
427
428
429 static int
430 do_always_noconv (struct _IO_codecvt *codecvt)
431 {
432   return 0;
433 }
434
435
436 static int
437 do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
438            const char *from_start, const char *from_end, _IO_size_t max)
439 {
440   int result;
441 #ifdef _LIBC
442   const unsigned char *cp = (const unsigned char *) from_start;
443   wchar_t to_buf[max];
444   struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
445   int status;
446   size_t dummy;
447
448   codecvt->__cd_in.__cd.__data[0].__outbuf = (char *) to_buf;
449   codecvt->__cd_in.__cd.__data[0].__outbufend = (char *) &to_buf[max];
450   codecvt->__cd_in.__cd.__data[0].__statep = statep;
451
452   status = DL_CALL_FCT (gs->__fct,
453                         (gs, codecvt->__cd_in.__cd.__data, &cp, from_end,
454                          NULL, &dummy, 0, 0));
455
456   result = cp - (const unsigned char *) from_start;
457 #else
458 # ifdef _GLIBCPP_USE_WCHAR_T
459   const char *from_start_copy = (const char *) from_start;
460   size_t from_len = from_end - from_start;
461   wchar_t to_buf[max];
462   size_t res;
463   char *to_start = (char *) to_buf;
464
465   res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
466                &to_start, &max);
467
468   result = from_start_copy - (char *) from_start;
469 # else
470   /* Decide what to do.  */
471   result = 0;
472 # endif
473 #endif
474
475   return result;
476 }
477
478
479 static int
480 do_max_length (struct _IO_codecvt *codecvt)
481 {
482 #ifdef _LIBC
483   return codecvt->__cd_in.__cd.__steps[0].__max_needed_from;
484 #else
485   return MB_CUR_MAX;
486 #endif
487 }