Merge branch 'pu'
[jungerl.git] / lib / iconv / c_src / iconv_drv.c
blobb03a8a1b4786bf3cb75a204c430c87640d698a8a
1 /* Created : 23 Mar 2004 by Tobbe <tobbe@bluetail.com>
2 * Description : iconv driver - conversion between character sets
3 *
4 * $Id$
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <iconv.h>
10 #include <errno.h>
12 #include "erl_driver.h"
13 #ifndef ERL_DRV_NIL
14 #include "erl_driver_compat.h"
15 #endif
17 /* op codes */
18 #define IV_OPEN 'o'
19 #define IV_CONV 'v'
20 #define IV_CLOSE 'c'
22 /* convert buffers */
23 #define INBUF_SZ 512
24 #define OUTBUF_SZ INBUF_SZ*4
25 static char inbuf[INBUF_SZ];
26 static char outbuf[OUTBUF_SZ];
29 /* these should really be defined in driver.h */
30 #define LOAD_ATOM(vec, i, atom) \
31 (((vec)[(i)] = ERL_DRV_ATOM), \
32 ((vec)[(i)+1] = (atom)), \
33 (i+2))
35 #define LOAD_INT(vec, i, val) \
36 (((vec)[(i)] = ERL_DRV_INT), \
37 ((vec)[(i)+1] = (ErlDrvTermData)(val)), \
38 (i+2))
40 #define LOAD_PORT(vec, i, port) \
41 (((vec)[(i)] = ERL_DRV_PORT), \
42 ((vec)[(i)+1] = (port)), \
43 (i+2))
45 #define LOAD_PID(vec, i, pid) \
46 (((vec)[(i)] = ERL_DRV_PID), \
47 ((vec)[(i)+1] = (pid)), \
48 (i+2))
50 #define LOAD_BINARY(vec, i, bin, offs, len) \
51 (((vec)[(i)] = ERL_DRV_BINARY), \
52 ((vec)[(i)+1] = (ErlDrvTermData)(bin)), \
53 ((vec)[(i)+2] = (len)), \
54 ((vec)[(i)+3] = (offs)), \
55 (i+4))
57 #define LOAD_STRING(vec, i, str, len) \
58 (((vec)[(i)] = ERL_DRV_STRING), \
59 ((vec)[(i)+1] = (ErlDrvTermData)(str)), \
60 ((vec)[(i)+2] = (len)), \
61 (i+3))
63 #define LOAD_STRING_CONS(vec, i, str, len) \
64 (((vec)[(i)] = ERL_DRV_STRING_CONS), \
65 ((vec)[(i)+1] = (ErlDrvTermData)(str)), \
66 ((vec)[(i)+2] = (len)), \
67 (i+3))
69 #define LOAD_TUPLE(vec, i, size) \
70 (((vec)[(i)] = ERL_DRV_TUPLE), \
71 ((vec)[(i)+1] = (size)), \
72 (i+2))
74 #define LOAD_LIST(vec, i, size) \
75 (((vec)[(i)] = ERL_DRV_LIST), \
76 ((vec)[(i)+1] = (size)), \
77 (i+2))
79 static int driver_send_bin();
81 /* atoms which are sent to erlang */
82 static ErlDrvTermData am_ok;
83 static ErlDrvTermData am_value;
84 static ErlDrvTermData am_error;
85 static ErlDrvTermData am_enomem;
86 static ErlDrvTermData am_einval;
87 static ErlDrvTermData am_eilseq;
88 static ErlDrvTermData am_e2big;
89 static ErlDrvTermData am_unknown;
91 static ErlDrvEntry iconvdrv_driver_entry;
93 typedef struct t_iconvdrv {
94 ErlDrvPort port;
95 ErlDrvTermData dport; /* the port identifier as ErlDrvTermData */
96 unsigned char digest[16];
97 } t_iconvdrv;
100 static ErlDrvData iconvdrv_start(ErlDrvPort port, char *buf)
102 t_iconvdrv *iconv = (t_iconvdrv*) driver_alloc(sizeof(t_iconvdrv));
104 if (iconv == NULL) return (ErlDrvData) -1;
105 iconv->port = port;
106 iconv->dport = driver_mk_port(port);
107 return (ErlDrvData) iconv;
110 static void iconvdrv_stop(ErlDrvData drv_data)
112 t_iconvdrv *iv = (t_iconvdrv*) drv_data;
113 driver_free(iv);
117 /* send {P, value, Bin} to caller */
118 static int driver_send_bin(t_iconvdrv *iv, ErlDrvBinary *bin, int len)
120 int i = 0;
121 ErlDrvTermData to, spec[10];
123 to = driver_caller(iv->port);
125 i = LOAD_PORT(spec, i, iv->dport);
126 i = LOAD_ATOM(spec, i, am_value);
127 i = LOAD_BINARY(spec, i, bin, 0, len);
128 i = LOAD_TUPLE(spec, i, 3);
130 return driver_send_term(iv->port, to, spec, i);
133 /* send {P, ok} to caller */
134 static int driver_send_ok(t_iconvdrv *iv)
136 int i = 0;
137 ErlDrvTermData to, spec[10];
139 to = driver_caller(iv->port);
141 i = LOAD_PORT(spec, i, iv->dport);
142 i = LOAD_ATOM(spec, i, am_ok);
143 i = LOAD_TUPLE(spec, i, 2);
145 return driver_send_term(iv->port, to, spec, i);
148 /* send {P, error, Error} to caller */
149 static int driver_send_error(t_iconvdrv *iv, ErlDrvTermData *am)
151 int i = 0;
152 ErlDrvTermData to, spec[8];
154 to = driver_caller(iv->port);
156 i = LOAD_PORT(spec, i, iv->dport);
157 i = LOAD_ATOM(spec, i, am_error);
158 i = LOAD_ATOM(spec, i, *am);
159 i = LOAD_TUPLE(spec, i, 3);
161 return driver_send_term(iv->port, to, spec, i);
164 #define CODE_STR_SZ 64
166 #define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \
167 (((unsigned char*) (s))[1]))
170 #define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
171 ((unsigned char*)(s))[1] = (i) & 0xff;}
173 static void iv_open(t_iconvdrv *iv, char *tocode, char *fromcode)
175 int len;
176 iconv_t cd;
177 ErlDrvBinary *bin;
179 if ((cd = iconv_open(tocode, fromcode)) == (iconv_t) -1) {
180 driver_send_error(iv, &am_einval);
182 else {
183 len = sizeof(iconv_t);
184 if (!(bin = driver_alloc_binary(len))) {
185 iconv_close(cd);
186 driver_send_error(iv, &am_enomem);
188 else {
189 memcpy(bin->orig_bytes, &cd, len);
190 driver_send_bin(iv, bin, len);
191 driver_free_binary(bin);
195 return;
198 static void iv_conv(t_iconvdrv *iv, iconv_t cd, char *ip, int ileft)
200 int oleft=OUTBUF_SZ;
201 char *op;
202 int len;
203 ErlDrvBinary *bin;
205 op = &outbuf[0];
207 /* Reset cd to initial state */
208 iconv(cd, NULL, NULL, NULL, NULL);
210 if (iconv(cd, &ip, &ileft, &op, &oleft) == (size_t) -1) {
211 if (errno == EILSEQ)
212 driver_send_error(iv, &am_eilseq);
213 else if (errno == EINVAL)
214 driver_send_error(iv, &am_einval);
215 else if (errno == E2BIG)
216 driver_send_error(iv, &am_e2big);
217 else
218 driver_send_error(iv, &am_unknown);
220 else if (ileft == 0) {
221 len = OUTBUF_SZ - oleft;
222 if (!(bin = driver_alloc_binary(len))) {
223 driver_send_error(iv, &am_enomem);
225 else {
226 memcpy(bin->orig_bytes, &outbuf[0], len);
227 driver_send_bin(iv, bin, len);
228 driver_free_binary(bin);
232 return;
235 static void iv_close(t_iconvdrv *iv, iconv_t cd)
237 iconv_close(cd);
238 driver_send_ok(iv);
239 return;
242 static void iconvdrv_from_erlang(ErlDrvData drv_data, char *buf, int len)
244 t_iconvdrv *iv = (t_iconvdrv *) drv_data;
245 char tocode[CODE_STR_SZ], fromcode[CODE_STR_SZ];
246 char *bp=buf;
247 unsigned int i=0;
248 iconv_t cd;
250 i = bp[0];
251 bp++;
252 switch (i) {
254 case IV_OPEN: {
256 * Format: <to-len:16><tocode><from-len:16><from-buf>
258 i = get_int16(bp);
259 bp += 2;
260 memcpy(tocode, bp, i);
261 tocode[i] = '\0';
262 bp += i;
263 i = get_int16(bp);
264 bp += 2;
265 memcpy(fromcode, bp, i);
266 fromcode[i] = '\0';
268 iv_open(iv, tocode, fromcode);
269 break;
272 case IV_CONV: {
274 * Format: <cd-len:16><cd><buf-len:16><buf>
276 i = get_int16(bp);
277 bp += 2;
278 memcpy(&cd, bp, i);
279 bp += i;
280 i = get_int16(bp);
281 bp += 2;
283 iv_conv(iv, cd, bp, i);
284 break;
287 case IV_CLOSE: {
289 * Format: <cd-len:16><cd>
291 i = get_int16(bp);
292 bp += 2;
293 memcpy(&cd, bp, i);
295 iv_close(iv, cd);
296 break;
299 } /* switch */
301 return;
306 * Initialize and return a driver entry struct
309 DRIVER_INIT(iconvdrv)
311 am_ok = driver_mk_atom("ok");
312 am_value = driver_mk_atom("value");
313 am_error = driver_mk_atom("error");
314 am_enomem = driver_mk_atom("enomem");
315 am_einval = driver_mk_atom("einval");
316 am_eilseq = driver_mk_atom("eilseq");
317 am_e2big = driver_mk_atom("e2big");
318 am_unknown = driver_mk_atom("unknown");
320 iconvdrv_driver_entry.init = NULL; /* Not used */
321 iconvdrv_driver_entry.start = iconvdrv_start;
322 iconvdrv_driver_entry.stop = iconvdrv_stop;
323 iconvdrv_driver_entry.output = iconvdrv_from_erlang;
324 iconvdrv_driver_entry.ready_input = NULL;
325 iconvdrv_driver_entry.ready_output = NULL;
326 iconvdrv_driver_entry.driver_name = "iconv_drv";
327 iconvdrv_driver_entry.finish = NULL;
328 iconvdrv_driver_entry.outputv = NULL;
329 return &iconvdrv_driver_entry;