d1a0f1ef137d664e0bbdfaa070de553a6e25fe4b
[platform/upstream/glibc.git] / libio / iofwide.c
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 <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.  */
65 struct _IO_codecvt __libio_codecvt =
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.  */
117         *cc = __libio_codecvt;
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;
124         cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
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;
132         cc->__cd_out.__cd.__data[0].__flags = __GCONV_IS_LAST;
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,
176                          (const unsigned char *) from_end, &written, 0, 0);
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,
222                          &written, 1, 0);
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,
269                          from_end, &written, 0, 0);
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,
345                          &written, 0, 0);
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 }