1 /*
2  * Copyright (c) 2003-2004, Artem B. Bityuckiy
3  * Copyright (c) 1999,2000, Konstantin Chuguev. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28 FUNCTION
29 <<iconv>>, <<iconv_open>>, <<iconv_close>>---charset conversion routines
30 
31 INDEX
32 	iconv
33 INDEX
34 	iconv_open
35 INDEX
36 	iconv_close
37 
38 SYNOPSIS
39 	#include <iconv.h>
40 	iconv_t iconv_open (const char *<[to]>, const char *<[from]>);
41 	int iconv_close (iconv_t <[cd]>);
42         size_t iconv (iconv_t <[cd]>, char **restrict <[inbuf]>,
43 	              size_t *restrict <[inbytesleft]>,
44 		      char **restrict <[outbuf]>,
45                       size_t *restrict <[outbytesleft]>);
46 
47 DESCRIPTION
48 The function <<iconv>> converts characters from <[in]> which are in one
49 encoding to characters of another encoding, outputting them to <[out]>.
50 The value <[inleft]> specifies the number of input bytes to convert whereas
51 the value <[outleft]> specifies the size remaining in the <[out]> buffer.
52 The conversion descriptor <[cd]> specifies the conversion being performed
53 and is created via <<iconv_open>>.
54 
55 An <<iconv>> conversion stops if: the input bytes are exhausted, the output
56 buffer is full, an invalid input character sequence occurs, or the
57 conversion specifier is invalid.
58 
59 The function <<iconv_open>> is used to specify a conversion from one
60 encoding: <[from]> to another: <[to]>.  The result of the call is
61 to create a conversion specifier that can be used with <<iconv>>.
62 
63 The function <<iconv_close>> is used to close a conversion specifier after
64 it is no longer needed.
65 
66 RETURNS
67 The <<iconv>> function returns the number of non-identical conversions
68 performed.  If an error occurs, (size_t)-1 is returned and <<errno>>
69 is set appropriately.  The values of <[inleft]>, <[in]>, <[out]>,
70 and <[outleft]> are modified to indicate how much input was processed
71 and how much output was created.
72 
73 The <<iconv_open>> function returns either a valid conversion specifier
74 or (iconv_t)-1 to indicate failure.  If failure occurs, <<errno>> is set
75 appropriately.
76 
77 The <<iconv_close>> function returns 0 on success or -1 on failure.
78 If failure occurs <<errno>> is set appropriately.
79 
80 PORTABILITY
81 <<iconv>>, <<iconv_open>>, and <<iconv_close>> are non-ANSI and are specified
82 by the Single Unix specification.
83 
84 No supporting OS subroutine calls are required.
85 */
86 #include <sys/types.h>
87 #include <errno.h>
88 #include <string.h>
89 #include <stdlib.h>
90 #include <iconv.h>
91 #include <wchar.h>
92 #include <sys/iconvnls.h>
93 #include "local.h"
94 #include "conv.h"
95 #include "ucsconv.h"
96 
97 /*
98  * iconv interface functions as specified by Single Unix specification.
99  */
100 
101 iconv_t
iconv_open(const char * to,const char * from)102 iconv_open (
103                       const char *to,
104                       const char *from)
105 {
106   iconv_conversion_t *ic;
107 
108   if (to == NULL || from == NULL || *to == '\0' || *from == '\0')
109     return (iconv_t)-1;
110 
111   if ((to = (const char *)_iconv_resolve_encoding_name (to)) == NULL)
112     return (iconv_t)-1;
113 
114   if ((from = (const char *)_iconv_resolve_encoding_name (from)) == NULL)
115     {
116       free ((void *)to);
117       return (iconv_t)-1;
118     }
119 
120   ic = (iconv_conversion_t *)malloc (sizeof (iconv_conversion_t));
121   if (ic == NULL)
122     return (iconv_t)-1;
123 
124   /* Select which conversion type to use */
125   if (strcmp (from, to) == 0)
126     {
127       /* Use null conversion */
128       ic->handlers = &_iconv_null_conversion_handlers;
129       ic->data = ic->handlers->open (to, from);
130     }
131   else
132     {
133       /* Use UCS-based conversion */
134       ic->handlers = &_iconv_ucs_conversion_handlers;
135       ic->data = ic->handlers->open (to, from);
136     }
137 
138   free ((void *)to);
139   free ((void *)from);
140 
141   if (ic->data == NULL)
142     {
143       free ((void *)ic);
144       return (iconv_t)-1;
145     }
146 
147   return (void *)ic;
148 }
149 
150 size_t
iconv(iconv_t cd,char ** __restrict inbuf,size_t * __restrict inbytesleft,char ** __restrict outbuf,size_t * __restrict outbytesleft)151 iconv (iconv_t cd,
152        char **__restrict inbuf,
153        size_t *__restrict inbytesleft,
154        char **__restrict outbuf,
155        size_t *__restrict outbytesleft)
156 {
157   iconv_conversion_t *ic = (iconv_conversion_t *)cd;
158 
159   if ((void *)cd == NULL || cd == (iconv_t)-1 || ic->data == NULL
160        || (ic->handlers != &_iconv_null_conversion_handlers
161            && ic->handlers != &_iconv_ucs_conversion_handlers))
162     {
163       _REENT_ERRNO (rptr) = EBADF;
164       return (size_t)-1;
165     }
166 
167   if (inbuf == NULL || *inbuf == NULL)
168     {
169       mbstate_t state_null = ICONV_ZERO_MB_STATE_T;
170 
171       if (!ic->handlers->is_stateful(ic->data, 1))
172         return (size_t)0;
173 
174       if (outbuf == NULL || *outbuf == NULL)
175         {
176           /* Reset shift state */
177           ic->handlers->set_state (ic->data, &state_null, 1);
178 
179           return (size_t)0;
180         }
181 
182       if (outbytesleft != NULL)
183         {
184           mbstate_t state_save = ICONV_ZERO_MB_STATE_T;
185 
186           /* Save current shift state */
187           ic->handlers->get_state (ic->data, &state_save, 1);
188 
189           /* Reset shift state */
190           ic->handlers->set_state (ic->data, &state_null, 1);
191 
192           /* Get initial shift state sequence and it's length */
193           ic->handlers->get_state (ic->data, &state_null, 1);
194 
195           if (*outbytesleft >= (size_t) state_null.__count)
196             {
197               memcpy ((void *)(*outbuf), (void *)&state_null, state_null.__count);
198 
199               *outbuf += state_null.__count;
200               *outbytesleft -= state_null.__count;
201 
202               return (size_t)0;
203             }
204 
205            /* Restore shift state if output buffer is too small */
206            ic->handlers->set_state (ic->data, &state_save, 1);
207         }
208 
209       _REENT_ERRNO (rptr) = E2BIG;
210       return (size_t)-1;
211     }
212 
213   if (*inbytesleft == 0)
214     {
215       _REENT_ERRNO (rptr) = EINVAL;
216       return (size_t)-1;
217     }
218 
219   if (*outbytesleft == 0 || *outbuf == NULL)
220     {
221       _REENT_ERRNO (rptr) = E2BIG;
222       return (size_t)-1;
223     }
224 
225   return ic->handlers->convert (
226                                 ic->data,
227                                 (const unsigned char**)inbuf,
228                                 inbytesleft,
229                                 (unsigned char**)outbuf,
230                                 outbytesleft,
231                                 0);
232 }
233 
234 
235 int
iconv_close(iconv_t cd)236 iconv_close (iconv_t cd)
237 {
238   int res;
239   iconv_conversion_t *ic = (iconv_conversion_t *)cd;
240 
241   if ((void *)cd == NULL || cd == (iconv_t)-1 || ic->data == NULL
242        || (ic->handlers != &_iconv_null_conversion_handlers
243            && ic->handlers != &_iconv_ucs_conversion_handlers))
244     {
245       _REENT_ERRNO (rptr) = EBADF;
246       return -1;
247     }
248 
249   res = (int)ic->handlers->close (ic->data);
250 
251   free ((void *)cd);
252 
253   return res;
254 }
255