1 /* This code was written by Dino Leonardo Sangoi <SANGOID@lloydadriatico.it> */
2
3 #include "config.h"
4
5 #include <stdio.h>
6 #include <slang.h>
7 #include <string.h>
8 #include <iconv.h>
9 #include <errno.h>
10
11 SLANG_MODULE(iconv);
12
13 #ifndef ICONV_CONST
14 # define ICONV_CONST
15 #endif
16
17 static int ICONV_Type_Id = 0;
18
19 typedef struct
20 {
21 iconv_t cd;
22 }
23 ICONV_Type;
24
free_iconv_type(ICONV_Type * it)25 static void free_iconv_type (ICONV_Type *it)
26 {
27 SLfree ((char *) it);
28 }
29
allocate_iconv_type(iconv_t cd)30 static SLang_MMT_Type *allocate_iconv_type (iconv_t cd)
31 {
32 ICONV_Type *it;
33 SLang_MMT_Type *mmt;
34
35 it = (ICONV_Type *) SLmalloc (sizeof (ICONV_Type));
36 if (it == NULL)
37 return NULL;
38
39 it->cd = cd;
40
41 if (NULL == (mmt = SLang_create_mmt (ICONV_Type_Id, (VOID_STAR) it)))
42 {
43 free_iconv_type (it);
44 return NULL;
45 }
46 return mmt;
47 }
48
_iconv_open(char * tocode,char * fromcode)49 static void _iconv_open(char *tocode, char *fromcode)
50 {
51 iconv_t cd;
52 SLang_MMT_Type *mmt;
53
54 cd = iconv_open(tocode, fromcode);
55 if (cd == (iconv_t)(-1))
56 {
57 SLang_verror (SL_INTRINSIC_ERROR, "Error preparing iconv to convert from '%s' to '%s'.", fromcode, tocode);
58 return;
59 }
60
61 if (NULL == (mmt = allocate_iconv_type (cd)))
62 {
63 iconv_close(cd);
64 return;
65 }
66
67 if (-1 == SLang_push_mmt (mmt))
68 {
69 SLang_free_mmt (mmt);
70 return;
71 }
72 return;
73 }
74
_iconv_close(ICONV_Type * it)75 static void _iconv_close(ICONV_Type *it)
76 {
77 if (it->cd != (iconv_t)-1)
78 {
79 iconv_close(it->cd);
80 it->cd = (iconv_t)-1;
81 }
82 }
83
destroy_iconv(SLtype type,VOID_STAR f)84 static void destroy_iconv (SLtype type, VOID_STAR f)
85 {
86 ICONV_Type *it;
87 (void) type;
88
89 it = (ICONV_Type *) f;
90 _iconv_close(it);
91 free_iconv_type (it);
92 }
93
_iconv_reset(ICONV_Type * it)94 static void _iconv_reset(ICONV_Type *it)
95 {
96 iconv(it->cd, NULL, NULL, NULL, NULL);
97 }
98
99 #define SHIFT_BUF_LEN 64U
_iconv_reset_shift(ICONV_Type * it)100 static void _iconv_reset_shift(ICONV_Type *it)
101 {
102 size_t n = SHIFT_BUF_LEN;
103 char buf[SHIFT_BUF_LEN], *p = buf;
104 SLang_BString_Type *bstr;
105 size_t rc;
106
107 rc = iconv(it->cd, NULL, NULL, &p, &n);
108 if (rc == (size_t)(-1))
109 {
110 /* Since no input was provided, errno must be E2BIG */
111 SLang_verror (SL_Internal_Error, "Internal error: shift buffer too small in iconv_reset_shift!");
112 return;
113 }
114 buf[SHIFT_BUF_LEN-n] = '\0';
115 bstr = SLbstring_create((unsigned char *)buf, SHIFT_BUF_LEN-n);
116 if (bstr == NULL)
117 return;
118
119 (void) SLang_push_bstring(bstr);
120 SLbstring_free (bstr);
121 }
122
_iconv(ICONV_Type * it,SLang_BString_Type * bstr)123 static void _iconv(ICONV_Type *it, SLang_BString_Type *bstr)
124 {
125 char *buf = NULL;
126 size_t rc;
127 char ICONV_CONST *instr;
128 char *outstr;
129 size_t inn, outn, bufn;
130 size_t fail = (size_t)-1;
131 SLstrlen_Type bstrlen;
132
133 if (NULL == (instr = (char ICONV_CONST *)SLbstring_get_pointer(bstr, &bstrlen)))
134 return;
135 inn = (size_t) bstrlen;
136
137 bufn = outn = 2*inn + 2;
138 if (NULL == (buf = (char *)SLmalloc(bufn)))
139 return;
140
141 outstr = buf;
142
143 while (1)
144 {
145 errno = 0;
146 rc = iconv(it->cd, &instr, &inn, &outstr, &outn);
147 if (rc != (size_t)-1)
148 break; /* ok! */
149
150 if (fail == inn)
151 {
152 SLang_verror (SL_Unknown_Error, "Unknown error in iconv");
153 goto error;
154 }
155
156 fail = inn;
157 switch (errno)
158 {
159 case EILSEQ:
160 SLang_verror (SL_InvalidUTF8_Error, "Invalid multibyte sequence or unable to convert to the target encoding");
161 goto error;
162 case EINVAL:
163 SLang_verror (SL_InvalidUTF8_Error, "Incomplete multibyte sequence");
164 goto error;
165 case 0:
166 /* drop */
167 /* grrrr
168 * At least on windows, libiconv returns with errno = 0
169 * (or unmodified?) when there's no more room on outstr
170 * if so, fallback
171 */
172 case E2BIG:
173 {
174 char *p;
175 long outdelta;
176
177 outdelta = outstr - buf;
178 outn += bufn;
179 bufn += bufn;
180 p = (char *)SLrealloc(buf, bufn);
181 if (p == NULL)
182 goto error;
183 buf = p;
184 outstr = buf + outdelta;
185 }
186 break;
187 default:
188 SLang_verror (SL_Unknown_Error, "Unknown iconv error");
189 goto error;
190 }
191 }
192
193 bstr = SLbstring_create((unsigned char *) buf, outstr - buf);
194 if (bstr != NULL)
195 (void) SLang_push_bstring(bstr);
196 SLbstring_free (bstr);
197 /* drop */
198 error:
199 SLfree(buf);
200 }
201
202 #define DUMMY_ICONV_TYPE 255
203 #define P DUMMY_ICONV_TYPE
204 #define V SLANG_VOID_TYPE
205 #define S SLANG_STRING_TYPE
206 #define B SLANG_BSTRING_TYPE
207 static SLang_Intrin_Fun_Type ICONV_Intrinsics [] =
208 {
209 MAKE_INTRINSIC_2("iconv_open", _iconv_open, V, S, S),
210 MAKE_INTRINSIC_1("iconv_close", _iconv_close, V, P),
211 MAKE_INTRINSIC_1("iconv_reset", _iconv_reset, V, P),
212 MAKE_INTRINSIC_1("iconv_reset_shift", _iconv_reset_shift, V, P),
213 MAKE_INTRINSIC_2("iconv", _iconv, V, P, B),
214 SLANG_END_INTRIN_FUN_TABLE
215 };
216
217 #undef B
218 #undef S
219 #undef V
220 #undef P
221
register_iconv_type(void)222 static int register_iconv_type (void)
223 {
224 SLang_Class_Type *cl;
225
226 if (ICONV_Type_Id != 0)
227 return 0;
228
229 if (NULL == (cl = SLclass_allocate_class ("ICONV_Type")))
230 return -1;
231
232 if (-1 == SLclass_set_destroy_function (cl, destroy_iconv))
233 return -1;
234
235 /* By registering as SLANG_VOID_TYPE, slang will dynamically allocate a
236 * type.
237 */
238 if (-1 == SLclass_register_class (cl, SLANG_VOID_TYPE, sizeof (ICONV_Type), SLANG_CLASS_TYPE_MMT))
239 return -1;
240
241 ICONV_Type_Id = SLclass_get_class_id (cl);
242 if (-1 == SLclass_patch_intrin_fun_table1 (ICONV_Intrinsics, DUMMY_ICONV_TYPE, ICONV_Type_Id))
243 return -1;
244
245 return 0;
246 }
247
init_iconv_module_ns(char * ns_name)248 int init_iconv_module_ns (char *ns_name)
249 {
250 SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
251 if (ns == NULL)
252 return -1;
253
254 if (-1 == register_iconv_type ())
255 return -1;
256
257 if (-1 == SLns_add_intrin_fun_table (ns, ICONV_Intrinsics, "__ICONV__"))
258 return -1;
259
260 return 0;
261 }
262
263 /* This function is optional */
deinit_iconv_module(void)264 void deinit_iconv_module (void)
265 {
266 }
267