1 /* Copyright (C) 1999-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.
17 
18    As a special exception, if you link the code in this file with
19    files compiled with a GNU compiler to produce an executable,
20    that does not cause the resulting executable to be covered by
21    the GNU Lesser General Public License.  This exception does not
22    however invalidate any other reasons why the executable file
23    might be covered by the GNU Lesser General Public License.
24    This exception applies to code released by its copyright holders
25    in files containing the exception.  */
26 
27 #include <libioP.h>
28 #include <dlfcn.h>
29 #include <wchar.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include <langinfo.h>
35 #include <locale/localeinfo.h>
36 #include <wcsmbs/wcsmbsload.h>
37 #include <iconv/gconv_int.h>
38 #include <shlib-compat.h>
39 #include <sysdep.h>
40 
41 
42 /* Return orientation of stream.  If mode is nonzero try to change
43    the orientation first.  */
44 #undef _IO_fwide
45 int
_IO_fwide(FILE * fp,int mode)46 _IO_fwide (FILE *fp, int mode)
47 {
48   /* Normalize the value.  */
49   mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1);
50 
51 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
52   if (__glibc_unlikely (&_IO_stdin_used == NULL) && _IO_legacy_file (fp))
53     /* This is for a stream in the glibc 2.0 format.  */
54     return -1;
55 #endif
56 
57   /* The orientation already has been determined.  */
58   if (fp->_mode != 0
59       /* Or the caller simply wants to know about the current orientation.  */
60       || mode == 0)
61     return fp->_mode;
62 
63   /* Set the orientation appropriately.  */
64   if (mode > 0)
65     {
66       struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt;
67 
68       fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
69       fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;
70 
71       /* Get the character conversion functions based on the currently
72 	 selected locale for LC_CTYPE.  */
73       {
74 	/* Clear the state.  We start all over again.  */
75 	memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t));
76 	memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
77 
78 	struct gconv_fcts fcts;
79 	__wcsmbs_clone_conv (&fcts);
80 	assert (fcts.towc_nsteps == 1);
81 	assert (fcts.tomb_nsteps == 1);
82 
83 	cc->__cd_in.step = fcts.towc;
84 
85 	cc->__cd_in.step_data.__invocation_counter = 0;
86 	cc->__cd_in.step_data.__internal_use = 1;
87 	cc->__cd_in.step_data.__flags = __GCONV_IS_LAST;
88 	cc->__cd_in.step_data.__statep = &fp->_wide_data->_IO_state;
89 
90 	cc->__cd_out.step = fcts.tomb;
91 
92 	cc->__cd_out.step_data.__invocation_counter = 0;
93 	cc->__cd_out.step_data.__internal_use = 1;
94 	cc->__cd_out.step_data.__flags = __GCONV_IS_LAST | __GCONV_TRANSLIT;
95 	cc->__cd_out.step_data.__statep = &fp->_wide_data->_IO_state;
96       }
97 
98       /* From now on use the wide character callback functions.  */
99       _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
100     }
101 
102   /* Set the mode now.  */
103   fp->_mode = mode;
104 
105   return mode;
106 }
107 
108 
109 enum __codecvt_result
__libio_codecvt_out(struct _IO_codecvt * codecvt,__mbstate_t * statep,const wchar_t * from_start,const wchar_t * from_end,const wchar_t ** from_stop,char * to_start,char * to_end,char ** to_stop)110 __libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
111 		     const wchar_t *from_start, const wchar_t *from_end,
112 		     const wchar_t **from_stop, char *to_start, char *to_end,
113 		     char **to_stop)
114 {
115   enum __codecvt_result result;
116 
117   struct __gconv_step *gs = codecvt->__cd_out.step;
118   int status;
119   size_t dummy;
120   const unsigned char *from_start_copy = (unsigned char *) from_start;
121 
122   codecvt->__cd_out.step_data.__outbuf = (unsigned char *) to_start;
123   codecvt->__cd_out.step_data.__outbufend = (unsigned char *) to_end;
124   codecvt->__cd_out.step_data.__statep = statep;
125 
126   __gconv_fct fct = gs->__fct;
127 #ifdef PTR_DEMANGLE
128   if (gs->__shlib_handle != NULL)
129     PTR_DEMANGLE (fct);
130 #endif
131 
132   status = DL_CALL_FCT (fct,
133 			(gs, &codecvt->__cd_out.step_data, &from_start_copy,
134 			 (const unsigned char *) from_end, NULL,
135 			 &dummy, 0, 0));
136 
137   *from_stop = (wchar_t *) from_start_copy;
138   *to_stop = (char *) codecvt->__cd_out.step_data.__outbuf;
139 
140   switch (status)
141     {
142     case __GCONV_OK:
143     case __GCONV_EMPTY_INPUT:
144       result = __codecvt_ok;
145       break;
146 
147     case __GCONV_FULL_OUTPUT:
148     case __GCONV_INCOMPLETE_INPUT:
149       result = __codecvt_partial;
150       break;
151 
152     default:
153       result = __codecvt_error;
154       break;
155     }
156 
157   return result;
158 }
159 
160 
161 enum __codecvt_result
__libio_codecvt_in(struct _IO_codecvt * codecvt,__mbstate_t * statep,const char * from_start,const char * from_end,const char ** from_stop,wchar_t * to_start,wchar_t * to_end,wchar_t ** to_stop)162 __libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
163 		    const char *from_start, const char *from_end,
164 		    const char **from_stop,
165 		    wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
166 {
167   enum __codecvt_result result;
168 
169   struct __gconv_step *gs = codecvt->__cd_in.step;
170   int status;
171   size_t dummy;
172   const unsigned char *from_start_copy = (unsigned char *) from_start;
173 
174   codecvt->__cd_in.step_data.__outbuf = (unsigned char *) to_start;
175   codecvt->__cd_in.step_data.__outbufend = (unsigned char *) to_end;
176   codecvt->__cd_in.step_data.__statep = statep;
177 
178   __gconv_fct fct = gs->__fct;
179 #ifdef PTR_DEMANGLE
180   if (gs->__shlib_handle != NULL)
181     PTR_DEMANGLE (fct);
182 #endif
183 
184   status = DL_CALL_FCT (fct,
185 			(gs, &codecvt->__cd_in.step_data, &from_start_copy,
186 			 (const unsigned char *) from_end, NULL,
187 			 &dummy, 0, 0));
188 
189   *from_stop = (const char *) from_start_copy;
190   *to_stop = (wchar_t *) codecvt->__cd_in.step_data.__outbuf;
191 
192   switch (status)
193     {
194     case __GCONV_OK:
195     case __GCONV_EMPTY_INPUT:
196       result = __codecvt_ok;
197       break;
198 
199     case __GCONV_FULL_OUTPUT:
200     case __GCONV_INCOMPLETE_INPUT:
201       result = __codecvt_partial;
202       break;
203 
204     default:
205       result = __codecvt_error;
206       break;
207     }
208 
209   return result;
210 }
211 
212 
213 int
__libio_codecvt_encoding(struct _IO_codecvt * codecvt)214 __libio_codecvt_encoding (struct _IO_codecvt *codecvt)
215 {
216   /* See whether the encoding is stateful.  */
217   if (codecvt->__cd_in.step->__stateful)
218     return -1;
219   /* Fortunately not.  Now determine the input bytes for the conversion
220      necessary for each wide character.  */
221   if (codecvt->__cd_in.step->__min_needed_from
222       != codecvt->__cd_in.step->__max_needed_from)
223     /* Not a constant value.  */
224     return 0;
225 
226   return codecvt->__cd_in.step->__min_needed_from;
227 }
228 
229 
230 int
__libio_codecvt_length(struct _IO_codecvt * codecvt,__mbstate_t * statep,const char * from_start,const char * from_end,size_t max)231 __libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
232 			const char *from_start, const char *from_end,
233 			size_t max)
234 {
235   int result;
236   const unsigned char *cp = (const unsigned char *) from_start;
237   wchar_t to_buf[max];
238   struct __gconv_step *gs = codecvt->__cd_in.step;
239   size_t dummy;
240 
241   codecvt->__cd_in.step_data.__outbuf = (unsigned char *) to_buf;
242   codecvt->__cd_in.step_data.__outbufend = (unsigned char *) &to_buf[max];
243   codecvt->__cd_in.step_data.__statep = statep;
244 
245   __gconv_fct fct = gs->__fct;
246 #ifdef PTR_DEMANGLE
247   if (gs->__shlib_handle != NULL)
248     PTR_DEMANGLE (fct);
249 #endif
250 
251   DL_CALL_FCT (fct,
252 	       (gs, &codecvt->__cd_in.step_data, &cp,
253 		(const unsigned char *) from_end, NULL,
254 		&dummy, 0, 0));
255 
256   result = cp - (const unsigned char *) from_start;
257 
258   return result;
259 }
260