2002-11-18 21:39:47 +01:00
|
|
|
/* $Id$ */
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2004-03-21 21:27:09 +01:00
|
|
|
#include <string.h>
|
2002-11-18 21:39:47 +01:00
|
|
|
#include <erl_driver.h>
|
|
|
|
#include <ei.h>
|
|
|
|
#include <expat.h>
|
|
|
|
|
2003-10-20 20:23:30 +02:00
|
|
|
#define EI_ENCODE_STRING_BUG
|
|
|
|
|
|
|
|
#ifdef EI_ENCODE_STRING_BUG
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Workaround for EI encode_string bug
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define put8(s,n) do { \
|
|
|
|
(s)[0] = (char)((n) & 0xff); \
|
|
|
|
(s) += 1; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define put16be(s,n) do { \
|
|
|
|
(s)[0] = ((n) >> 8) & 0xff; \
|
|
|
|
(s)[1] = (n) & 0xff; \
|
|
|
|
(s) += 2; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define put32be(s,n) do { \
|
|
|
|
(s)[0] = ((n) >> 24) & 0xff; \
|
|
|
|
(s)[1] = ((n) >> 16) & 0xff; \
|
|
|
|
(s)[2] = ((n) >> 8) & 0xff; \
|
|
|
|
(s)[3] = (n) & 0xff; \
|
|
|
|
(s) += 4; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
int ei_encode_string_len_fixed(char *buf, int *index, const char *p, int len)
|
|
|
|
{
|
|
|
|
char *s = buf + *index;
|
|
|
|
char *s0 = s;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (len <= 0xffff) {
|
|
|
|
if (!buf) s += 3;
|
|
|
|
else {
|
|
|
|
put8(s,ERL_STRING_EXT);
|
|
|
|
put16be(s,len);
|
|
|
|
memmove(s,p,len); /* unterminated string */
|
|
|
|
}
|
|
|
|
s += len;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!buf) s += 6 + (2*len);
|
|
|
|
else {
|
|
|
|
/* strings longer than 65535 are encoded as lists */
|
|
|
|
put8(s,ERL_LIST_EXT);
|
|
|
|
put32be(s,len);
|
|
|
|
|
|
|
|
for (i=0; i<len; i++) {
|
|
|
|
put8(s,ERL_SMALL_INTEGER_EXT);
|
|
|
|
put8(s,p[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
put8(s,ERL_NIL_EXT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*index += s-s0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ei_encode_string_fixed(char *buf, int *index, const char *p)
|
|
|
|
{
|
|
|
|
return ei_encode_string_len_fixed(buf, index, p, strlen(p));
|
|
|
|
}
|
|
|
|
|
|
|
|
int ei_x_encode_string_len_fixed(ei_x_buff* x, const char* s, int len)
|
|
|
|
{
|
|
|
|
int i = x->index;
|
|
|
|
ei_encode_string_len_fixed(NULL, &i, s, len);
|
|
|
|
if (!x_fix_buff(x, i))
|
|
|
|
return -1;
|
|
|
|
return ei_encode_string_len_fixed(x->buff, &x->index, s, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ei_x_encode_string_fixed(ei_x_buff* x, const char* s)
|
|
|
|
{
|
|
|
|
return ei_x_encode_string_len_fixed(x, s, strlen(s));
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define ei_encode_string_len_fixed(buf, index, p, len) \
|
|
|
|
ei_encode_string_len(buf, index, p, len)
|
|
|
|
#define ei_encode_string_fixed(buf, index, p) \
|
|
|
|
ei_encode_string(buf, index, p)
|
|
|
|
#define ei_x_encode_string_len_fixed(x, s, len) \
|
|
|
|
ei_x_encode_string_len(x, s, len)
|
|
|
|
#define ei_x_encode_string_fixed(x, s) \
|
|
|
|
ei_x_encode_string(x, s)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define XML_START 0
|
|
|
|
#define XML_END 1
|
|
|
|
#define XML_CDATA 2
|
|
|
|
#define XML_ERROR 3
|
|
|
|
|
2004-12-01 23:48:53 +01:00
|
|
|
#define PARSE_COMMAND 0
|
2004-12-05 21:54:55 +01:00
|
|
|
#define PARSE_FINAL_COMMAND 1
|
2004-12-01 23:48:53 +01:00
|
|
|
|
|
|
|
ei_x_buff event_buf;
|
2003-10-20 20:23:30 +02:00
|
|
|
|
2002-11-18 21:39:47 +01:00
|
|
|
typedef struct {
|
|
|
|
ErlDrvPort port;
|
|
|
|
XML_Parser parser;
|
|
|
|
} expat_data;
|
|
|
|
|
|
|
|
void *erlXML_StartElementHandler(expat_data *d,
|
|
|
|
const XML_Char *name,
|
|
|
|
const XML_Char **atts)
|
|
|
|
{
|
|
|
|
int i;
|
2004-12-01 23:48:53 +01:00
|
|
|
|
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_long(&event_buf, XML_START);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_string_fixed(&event_buf, name);
|
|
|
|
|
2002-11-18 21:39:47 +01:00
|
|
|
for (i = 0; atts[i]; i += 2) {}
|
|
|
|
|
2004-12-01 23:48:53 +01:00
|
|
|
if (i > 0)
|
2002-11-18 21:39:47 +01:00
|
|
|
{
|
2004-12-01 23:48:53 +01:00
|
|
|
ei_x_encode_list_header(&event_buf, i/2);
|
|
|
|
|
|
|
|
for (i = 0; atts[i]; i += 2)
|
|
|
|
{
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_string_fixed(&event_buf, atts[i]);
|
|
|
|
ei_x_encode_string_fixed(&event_buf, atts[i+1]);
|
|
|
|
}
|
2002-11-18 21:39:47 +01:00
|
|
|
}
|
2004-12-01 23:48:53 +01:00
|
|
|
|
|
|
|
ei_x_encode_empty_list(&event_buf);
|
|
|
|
|
2002-11-18 21:39:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *erlXML_EndElementHandler(expat_data *d,
|
|
|
|
const XML_Char *name)
|
|
|
|
{
|
2004-12-01 23:48:53 +01:00
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_long(&event_buf, XML_END);
|
|
|
|
ei_x_encode_string_fixed(&event_buf, name);
|
2002-11-18 21:39:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *erlXML_CharacterDataHandler(expat_data *d,
|
|
|
|
const XML_Char *s,
|
|
|
|
int len)
|
|
|
|
{
|
2004-12-01 23:48:53 +01:00
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_long(&event_buf, XML_CDATA);
|
2006-04-06 01:56:16 +02:00
|
|
|
ei_x_encode_binary(&event_buf, s, len);
|
2002-11-18 21:39:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
|
|
|
{
|
|
|
|
expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
|
|
|
|
d->port = port;
|
|
|
|
d->parser = XML_ParserCreate("UTF-8");
|
|
|
|
XML_SetUserData(d->parser, d);
|
|
|
|
|
2004-12-01 23:48:53 +01:00
|
|
|
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
|
|
|
|
2002-11-18 21:39:47 +01:00
|
|
|
XML_SetStartElementHandler(
|
|
|
|
d->parser, (XML_StartElementHandler)erlXML_StartElementHandler);
|
|
|
|
XML_SetEndElementHandler(
|
|
|
|
d->parser, (XML_EndElementHandler)erlXML_EndElementHandler);
|
|
|
|
XML_SetCharacterDataHandler(
|
|
|
|
d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler);
|
|
|
|
|
|
|
|
|
|
|
|
return (ErlDrvData)d;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void expat_erl_stop(ErlDrvData handle)
|
|
|
|
{
|
2002-11-20 21:19:20 +01:00
|
|
|
XML_ParserFree(((expat_data *)handle)->parser);
|
2002-11-18 21:39:47 +01:00
|
|
|
driver_free((char*)handle);
|
|
|
|
}
|
|
|
|
|
2004-12-01 23:48:53 +01:00
|
|
|
static int expat_erl_control(ErlDrvData drv_data,
|
|
|
|
unsigned int command,
|
|
|
|
char *buf, int len,
|
|
|
|
char **rbuf, int rlen)
|
2002-11-18 21:39:47 +01:00
|
|
|
{
|
2004-12-01 23:48:53 +01:00
|
|
|
expat_data* d = (expat_data*)drv_data;
|
2002-11-18 21:39:47 +01:00
|
|
|
int res, errcode;
|
|
|
|
char *errstring;
|
2004-12-01 23:48:53 +01:00
|
|
|
ErlDrvBinary *b;
|
|
|
|
size_t size;
|
2002-11-18 21:39:47 +01:00
|
|
|
|
2004-12-01 23:48:53 +01:00
|
|
|
switch (command)
|
2002-11-18 21:39:47 +01:00
|
|
|
{
|
2004-12-01 23:48:53 +01:00
|
|
|
case PARSE_COMMAND:
|
2004-12-05 21:54:55 +01:00
|
|
|
case PARSE_FINAL_COMMAND:
|
2004-12-01 23:48:53 +01:00
|
|
|
ei_x_new_with_version(&event_buf);
|
2004-12-05 21:54:55 +01:00
|
|
|
res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND);
|
2004-12-01 23:48:53 +01:00
|
|
|
|
|
|
|
if(!res)
|
|
|
|
{
|
|
|
|
errcode = XML_GetErrorCode(d->parser);
|
|
|
|
errstring = (char *)XML_ErrorString(errcode);
|
|
|
|
|
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_long(&event_buf, XML_ERROR);
|
|
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
|
|
ei_x_encode_long(&event_buf, errcode);
|
|
|
|
ei_x_encode_string_fixed(&event_buf, errstring);
|
|
|
|
}
|
|
|
|
|
|
|
|
ei_x_encode_empty_list(&event_buf);
|
|
|
|
|
|
|
|
size = event_buf.index;
|
|
|
|
|
|
|
|
b = driver_alloc_binary(size);
|
|
|
|
memcpy(b->orig_bytes, event_buf.buff, size);
|
|
|
|
|
|
|
|
ei_x_free(&event_buf);
|
|
|
|
|
|
|
|
*rbuf = (char *)b;
|
|
|
|
return size;
|
|
|
|
default:
|
|
|
|
return 0;
|
2002-11-18 21:39:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ErlDrvEntry expat_driver_entry = {
|
2004-12-01 23:48:53 +01:00
|
|
|
NULL, /* F_PTR init, N/A */
|
|
|
|
expat_erl_start, /* L_PTR start, called when port is opened */
|
|
|
|
expat_erl_stop, /* F_PTR stop, called when port is closed */
|
|
|
|
NULL, /* F_PTR output, called when erlang has sent */
|
|
|
|
NULL, /* F_PTR ready_input, called when input descriptor ready */
|
|
|
|
NULL, /* F_PTR ready_output, called when output descriptor ready */
|
|
|
|
"expat_erl", /* char *driver_name, the argument to open_port */
|
|
|
|
NULL, /* F_PTR finish, called when unloaded */
|
|
|
|
NULL, /* handle */
|
|
|
|
expat_erl_control, /* F_PTR control, port_command callback */
|
|
|
|
NULL, /* F_PTR timeout, reserved */
|
|
|
|
NULL /* F_PTR outputv, reserved */
|
2002-11-18 21:39:47 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
DRIVER_INIT(expat_erl) /* must match name in driver_entry */
|
|
|
|
{
|
2004-04-15 21:55:38 +02:00
|
|
|
return &expat_driver_entry;
|
2002-11-18 21:39:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|