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 <_ansi.h>
87 #include <sys/types.h>
88 #include <errno.h>
89 #include <string.h>
90 #include <stdlib.h>
91 #include <iconv.h>
92 #include <wchar.h>
93 #include <sys/iconvnls.h>
94 #include "local.h"
95 #include "conv.h"
96 #include "ucsconv.h"
97 
98 /*
99  * iconv interface functions as specified by Single Unix specification.
100  */
101 
102 #ifndef _REENT_ONLY
103 iconv_t
iconv_open(const char * to,const char * from)104 iconv_open (
105                       const char *to,
106                       const char *from)
107 {
108   iconv_conversion_t *ic;
109 
110   if (to == NULL || from == NULL || *to == '\0' || *from == '\0')
111     return (iconv_t)-1;
112 
113   if ((to = (const char *)_iconv_resolve_encoding_name (to)) == NULL)
114     return (iconv_t)-1;
115 
116   if ((from = (const char *)_iconv_resolve_encoding_name (from)) == NULL)
117     {
118       free ((void *)to);
119       return (iconv_t)-1;
120     }
121 
122   ic = (iconv_conversion_t *)malloc (sizeof (iconv_conversion_t));
123   if (ic == NULL)
124     return (iconv_t)-1;
125 
126   /* Select which conversion type to use */
127   if (strcmp (from, to) == 0)
128     {
129       /* Use null conversion */
130       ic->handlers = &_iconv_null_conversion_handlers;
131       ic->data = ic->handlers->open (to, from);
132     }
133   else
134     {
135       /* Use UCS-based conversion */
136       ic->handlers = &_iconv_ucs_conversion_handlers;
137       ic->data = ic->handlers->open (to, from);
138     }
139 
140   free ((void *)to);
141   free ((void *)from);
142 
143   if (ic->data == NULL)
144     {
145       free ((void *)ic);
146       return (iconv_t)-1;
147     }
148 
149   return (void *)ic;
150 }
151 
152 
153 size_t
iconv(iconv_t cd,const char ** __restrict inbuf,size_t * __restrict inbytesleft,char ** __restrict outbuf,size_t * __restrict outbytesleft)154 iconv (iconv_t cd,
155               const char **__restrict inbuf,
156               size_t *__restrict inbytesleft,
157               char **__restrict outbuf,
158               size_t *__restrict outbytesleft)
159 {
160   iconv_conversion_t *ic = (iconv_conversion_t *)cd;
161 
162   if ((void *)cd == NULL || cd == (iconv_t)-1 || ic->data == NULL
163        || (ic->handlers != &_iconv_null_conversion_handlers
164            && ic->handlers != &_iconv_ucs_conversion_handlers))
165     {
166       _REENT_ERRNO (rptr) = EBADF;
167       return (size_t)-1;
168     }
169 
170   if (inbuf == NULL || *inbuf == NULL)
171     {
172       mbstate_t state_null = ICONV_ZERO_MB_STATE_T;
173 
174       if (!ic->handlers->is_stateful(ic->data, 1))
175         return (size_t)0;
176 
177       if (outbuf == NULL || *outbuf == NULL)
178         {
179           /* Reset shift state */
180           ic->handlers->set_state (ic->data, &state_null, 1);
181 
182           return (size_t)0;
183         }
184 
185       if (outbytesleft != NULL)
186         {
187           mbstate_t state_save = ICONV_ZERO_MB_STATE_T;
188 
189           /* Save current shift state */
190           ic->handlers->get_state (ic->data, &state_save, 1);
191 
192           /* Reset shift state */
193           ic->handlers->set_state (ic->data, &state_null, 1);
194 
195           /* Get initial shift state sequence and it's length */
196           ic->handlers->get_state (ic->data, &state_null, 1);
197 
198           if (*outbytesleft >= (size_t) state_null.__count)
199             {
200               memcpy ((void *)(*outbuf), (void *)&state_null, state_null.__count);
201 
202               *outbuf += state_null.__count;
203               *outbytesleft -= state_null.__count;
204 
205               return (size_t)0;
206             }
207 
208            /* Restore shift state if output buffer is too small */
209            ic->handlers->set_state (ic->data, &state_save, 1);
210         }
211 
212       _REENT_ERRNO (rptr) = E2BIG;
213       return (size_t)-1;
214     }
215 
216   if (*inbytesleft == 0)
217     {
218       _REENT_ERRNO (rptr) = EINVAL;
219       return (size_t)-1;
220     }
221 
222   if (*outbytesleft == 0 || *outbuf == NULL)
223     {
224       _REENT_ERRNO (rptr) = E2BIG;
225       return (size_t)-1;
226     }
227 
228   return ic->handlers->convert (
229                                 ic->data,
230                                 (const unsigned char**)inbuf,
231                                 inbytesleft,
232                                 (unsigned char**)outbuf,
233                                 outbytesleft,
234                                 0);
235 }
236 
237 
238 int
iconv_close(iconv_t cd)239 iconv_close (iconv_t cd)
240 {
241   int res;
242   iconv_conversion_t *ic = (iconv_conversion_t *)cd;
243 
244   if ((void *)cd == NULL || cd == (iconv_t)-1 || ic->data == NULL
245        || (ic->handlers != &_iconv_null_conversion_handlers
246            && ic->handlers != &_iconv_ucs_conversion_handlers))
247     {
248       _REENT_ERRNO (rptr) = EBADF;
249       return -1;
250     }
251 
252   res = (int)ic->handlers->close (ic->data);
253 
254   free ((void *)cd);
255 
256   return res;
257 }
258 #endif /* !_REENT_ONLY */
259