1 /* Created : 23 Mar 2004 by Tobbe <tobbe@bluetail.com>
2 * Description : iconv driver - conversion between character sets
12 #include "erl_driver.h"
14 #include "erl_driver_compat.h"
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)), \
35 #define LOAD_INT(vec, i, val) \
36 (((vec)[(i)] = ERL_DRV_INT), \
37 ((vec)[(i)+1] = (ErlDrvTermData)(val)), \
40 #define LOAD_PORT(vec, i, port) \
41 (((vec)[(i)] = ERL_DRV_PORT), \
42 ((vec)[(i)+1] = (port)), \
45 #define LOAD_PID(vec, i, pid) \
46 (((vec)[(i)] = ERL_DRV_PID), \
47 ((vec)[(i)+1] = (pid)), \
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)), \
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)), \
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)), \
69 #define LOAD_TUPLE(vec, i, size) \
70 (((vec)[(i)] = ERL_DRV_TUPLE), \
71 ((vec)[(i)+1] = (size)), \
74 #define LOAD_LIST(vec, i, size) \
75 (((vec)[(i)] = ERL_DRV_LIST), \
76 ((vec)[(i)+1] = (size)), \
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
{
95 ErlDrvTermData dport
; /* the port identifier as ErlDrvTermData */
96 unsigned char digest
[16];
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;
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
;
117 /* send {P, value, Bin} to caller */
118 static int driver_send_bin(t_iconvdrv
*iv
, ErlDrvBinary
*bin
, int len
)
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
)
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
)
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
)
179 if ((cd
= iconv_open(tocode
, fromcode
)) == (iconv_t
) -1) {
180 driver_send_error(iv
, &am_einval
);
183 len
= sizeof(iconv_t
);
184 if (!(bin
= driver_alloc_binary(len
))) {
186 driver_send_error(iv
, &am_enomem
);
189 memcpy(bin
->orig_bytes
, &cd
, len
);
190 driver_send_bin(iv
, bin
, len
);
191 driver_free_binary(bin
);
198 static void iv_conv(t_iconvdrv
*iv
, iconv_t cd
, char *ip
, int ileft
)
207 /* Reset cd to initial state */
208 iconv(cd
, NULL
, NULL
, NULL
, NULL
);
210 if (iconv(cd
, &ip
, &ileft
, &op
, &oleft
) == (size_t) -1) {
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
);
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
);
226 memcpy(bin
->orig_bytes
, &outbuf
[0], len
);
227 driver_send_bin(iv
, bin
, len
);
228 driver_free_binary(bin
);
235 static void iv_close(t_iconvdrv
*iv
, iconv_t cd
)
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
];
256 * Format: <to-len:16><tocode><from-len:16><from-buf>
260 memcpy(tocode
, bp
, i
);
265 memcpy(fromcode
, bp
, i
);
268 iv_open(iv
, tocode
, fromcode
);
274 * Format: <cd-len:16><cd><buf-len:16><buf>
283 iv_conv(iv
, cd
, bp
, i
);
289 * Format: <cd-len:16><cd>
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
;