1 /*-
2  * Copyright (c) 1999,2000
3  *    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  *    iconv (Charset Conversion Library) v2.0
27  */
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <reent.h>
33 #include <limits.h>
34 #include "local.h"
35 
36 static iconv_converter_t *
37 _DEFUN(converter_init, (rptr, conv_func, close_func, extra),
38                         struct _reent *rptr      _AND
39                         iconv_conv_t conv_func   _AND
40                         iconv_close_t close_func _AND
41                         size_t extra)
42 {
43     iconv_converter_t *res = _malloc_r(rptr, sizeof(iconv_converter_t) + extra);
44         memset(res, 0, sizeof(iconv_converter_t) + extra);
45     if (res) {
46         res->convert = conv_func;
47         res->close = close_func;
48     }
49     return res;
50 }
51 
52 static int
53 _DEFUN(unicode_close, (rptr, data),
54                       struct _reent *rptr _AND
55                       _VOID_PTR data)
56 {
57     int res;
58     unicode_converter_t *uc = (unicode_converter_t *)data;
59 
60     res = ICONV_CES_CLOSE(rptr, &(uc->from));
61     res = ICONV_CES_CLOSE(rptr, &(uc->to)) || res;
62     return res;
63 }
64 
65 static size_t
66 _DEFUN(unicode_conv, (rptr, data, inbuf, inbytesleft, outbuf, outbytesleft),
67                      struct _reent *rptr          _AND
68                      _VOID_PTR data               _AND
69                      _CONST unsigned char **inbuf _AND
70                      size_t *inbytesleft          _AND
71                      unsigned char **outbuf       _AND
72                      size_t *outbytesleft)
73 {
74         size_t res = 0;
75         unicode_converter_t *uc = (unicode_converter_t *)data;
76 
77         if (inbuf == NULL || *inbuf == NULL) {
78             if (ICONV_CES_CONVERT_FROM_UCS(&(uc->to), UCS_CHAR_NONE,
79                                            outbuf, outbytesleft) <= 0) {
80             __errno_r(rptr) = E2BIG;
81             return (size_t)(-1);
82         }
83         ICONV_CES_RESET(&(uc->from));
84         ICONV_CES_RESET(&(uc->to));
85         return res;
86     }
87     if (inbytesleft == NULL || *inbytesleft == 0)
88         return 0;
89     while (*inbytesleft > 0) {
90         ssize_t size;
91         _CONST unsigned char *ptr = *inbuf;
92         ucs_t ch = ICONV_CES_CONVERT_TO_UCS(&(uc->from), inbuf,
93                                             inbytesleft);
94 
95         if (ch == UCS_CHAR_NONE) {
96             /* Incomplete character in input buffer */
97             __errno_r(rptr) = EINVAL;
98             return (size_t)(-1);
99         }
100         if (ch == UCS_CHAR_INVALID) {
101             /* Invalid character in source buffer */
102             *inbytesleft += *inbuf - ptr;
103             *inbuf = ptr;
104             __errno_r(rptr) = EILSEQ;
105             return (size_t)(-1);
106         }
107         size = ICONV_CES_CONVERT_FROM_UCS(&(uc->to), ch,
108                                           outbuf, outbytesleft);
109 
110         if (size < 0) {
111             /* No equivalent in destination charset. */
112 
113             size = ICONV_CES_CONVERT_FROM_UCS(&(uc->to),
114                                                uc->missing,
115                                                outbuf, outbytesleft);
116             if (size)
117                 res ++;
118         }
119         if (!size) {
120             /* Not enough space in output buffer */
121             *inbytesleft += *inbuf - ptr;
122             *inbuf = ptr;
123             __errno_r(rptr) = E2BIG;
124             return (size_t)(-1);
125         }
126     }
127     return res;
128 }
129 
130 iconv_converter_t *
131 _DEFUN(_iconv_unicode_conv_init, (rptr, to, from),
132                                  struct _reent *rptr _AND
133                                  _CONST char *to     _AND
134                                  _CONST char *from)
135 {
136     unicode_converter_t *uc;
137     iconv_converter_t *ic = converter_init(rptr, unicode_conv, unicode_close,
138                                          sizeof(unicode_converter_t));
139 
140     if (ic == NULL)
141         return NULL;
142     uc = (unicode_converter_t *)(ic + 1);
143     if (!_iconv_ces_init(rptr, &(uc->from), from)) {
144         if(!_iconv_ces_init(rptr, &(uc->to), to))
145         {
146             uc->missing = '_';
147             return ic;
148         }
149         ICONV_CES_CLOSE(rptr, &(uc->from));
150     }
151     _free_r(rptr, ic);
152     return NULL;
153 }
154 
155 static int
156 _DEFUN(null_close, (rptr, data),
157                    struct _reent *rptr _AND
158                    _VOID_PTR data)
159 {
160     return 0;
161 }
162 
163 static size_t
164 _DEFUN(null_conv, (rptr, data, inbuf, inbytesleft, outbuf, outbytesleft),
165                   struct _reent *rptr          _AND
166                   _VOID_PTR data               _AND
167                   _CONST unsigned char **inbuf _AND
168                   size_t *inbytesleft          _AND
169                   unsigned char **outbuf       _AND
170                   size_t *outbytesleft)
171 {
172     if (inbuf && *inbuf && inbytesleft && *inbytesleft > 0 && outbuf
173                                          && *outbuf && outbytesleft) {
174         size_t result, len;
175         if (*inbytesleft < *outbytesleft) {
176             result = 0;
177             len = *inbytesleft;
178         } else {
179             result = (size_t)(-1);
180             __errno_r(rptr) = E2BIG;
181             len = *outbytesleft;
182         }
183         bcopy(*inbuf, *outbuf, len);
184         *inbuf += len;
185         *inbytesleft -= len;
186         *outbuf += len;
187         *outbytesleft -= len;
188 
189         return result;
190     }
191 
192     return 0;
193 }
194 
195 iconv_converter_t *
196 _DEFUN(_iconv_null_conv_init, (rptr, to, from),
197                               struct _reent *rptr _AND
198                               _CONST char *to     _AND
199                               _CONST char *from)
200 {
201     return converter_init(rptr, null_conv, null_close, 0);
202 }
203 
204