static const char rcsid[] = "#(@) $Id$";
#include "ext/xml/expat_compat.h"
#ifdef _WIN32
#include "xmlrpc_win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "xml_element.h"
#include "queue.h"
#include "encodings.h"
#define my_free(thing) if(thing) {free(thing); thing = NULL;}
#define XML_DECL_START "<?xml"
#define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1
#define XML_DECL_VERSION "version=\"1.0\""
#define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1
#define XML_DECL_ENCODING_ATTR "encoding"
#define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1
#define XML_DECL_ENCODING_DEFAULT "utf-8"
#define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1
#define XML_DECL_END "?>"
#define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1
#define START_TOKEN_BEGIN "<"
#define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1
#define START_TOKEN_END ">"
#define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1
#define EMPTY_START_TOKEN_END "/>"
#define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1
#define END_TOKEN_BEGIN "</"
#define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1
#define END_TOKEN_END ">"
#define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1
#define ATTR_DELIMITER "\""
#define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1
#define CDATA_BEGIN "<![CDATA["
#define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1
#define CDATA_END "]]>"
#define CDATA_END_LEN sizeof(CDATA_END) - 1
#define EQUALS "="
#define EQUALS_LEN sizeof(EQUALS) - 1
#define WHITESPACE " "
#define WHITESPACE_LEN sizeof(WHITESPACE) - 1
#define NEWLINE "\n"
#define NEWLINE_LEN sizeof(NEWLINE) - 1
#define MAX_VAL_BUF 144
#define SCALAR_STR "SCALAR"
#define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1
#define VECTOR_STR "VECTOR"
#define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1
#define RESPONSE_STR "RESPONSE"
#define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1
void xml_elem_free_non_recurse(xml_element* root) {
if(root) {
xml_element_attr* attrs = Q_Head(&root->attrs);
while(attrs) {
my_free(attrs->key);
my_free(attrs->val);
my_free(attrs);
attrs = Q_Next(&root->attrs);
}
Q_Destroy(&root->children);
Q_Destroy(&root->attrs);
if(root->name) {
free((char *)root->name);
root->name = NULL;
}
simplestring_free(&root->text);
my_free(root);
}
}
void xml_elem_free(xml_element* root) {
if(root) {
xml_element* kids = Q_Head(&root->children);
while(kids) {
xml_elem_free(kids);
kids = Q_Next(&root->children);
}
xml_elem_free_non_recurse(root);
}
}
xml_element* xml_elem_new() {
xml_element* elem = calloc(1, sizeof(xml_element));
if(elem) {
Q_Init(&elem->children);
Q_Init(&elem->attrs);
simplestring_init(&elem->text);
simplestring_addn(&elem->text, "", 0);
}
return elem;
}
static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
{
return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
}
static int create_xml_escape(char *pString, unsigned char c)
{
int counter = 0;
pString[counter++] = '&';
pString[counter++] = '#';
if(c >= 100) {
pString[counter++] = c / 100 + '0';
c = c % 100;
}
pString[counter++] = c / 10 + '0';
c = c % 10;
pString[counter++] = c + '0';
pString[counter++] = ';';
return counter;
}
#define non_ascii(c) (c > 127)
#define non_print(c) (!isprint(c))
#define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
#define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3;
static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
char *pRetval = 0;
int iNewBufLen=0;
#define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
((flag & xml_elem_non_print_escaping) && non_print(c)) )
if(buf && *buf) {
const unsigned char *bufcopy;
char *NewBuffer;
int ToBeXmlEscaped=0;
int iLength;
bufcopy = buf;
iLength= old_len ? old_len : strlen(buf);
while(*bufcopy) {
if( should_escape(*bufcopy, flags) ) {
iLength += entity_length(*bufcopy);
ToBeXmlEscaped=1;
}
bufcopy++;
}
if(ToBeXmlEscaped) {
NewBuffer= malloc(iLength+1);
if(NewBuffer) {
bufcopy=buf;
while(*bufcopy) {
if(should_escape(*bufcopy, flags)) {
iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
}
else {
NewBuffer[iNewBufLen++]=*bufcopy;
}
bufcopy++;
}
NewBuffer[iNewBufLen] = 0;
pRetval = NewBuffer;
}
}
}
if(newlen) {
*newlen = iNewBufLen;
}
return pRetval;
}
static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth)
{
int i;
static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
static char whitespace[] = " "
" "
" ";
depth++;
if(!el) {
return;
}
if(!options) {
options = &default_opts;
}
if(depth == 1) {
xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
if(options->encoding && *options->encoding) {
xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
xml_elem_writefunc(fptr, options->encoding, data, 0);
xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
}
xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
if(options->verbosity != xml_elem_no_white_space) {
xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
}
}
if(options->verbosity == xml_elem_pretty && depth > 2) {
xml_elem_writefunc(fptr, whitespace, data, depth - 2);
}
xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
if(el->name) {
xml_elem_writefunc(fptr, el->name, data, 0);
if(Q_Size(&el->attrs)) {
xml_element_attr* iter = Q_Head(&el->attrs);
while( iter ) {
xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
xml_elem_writefunc(fptr, iter->key, data, 0);
xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
xml_elem_writefunc(fptr, iter->val, data, 0);
xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
iter = Q_Next(&el->attrs);
}
}
}
else {
xml_elem_writefunc(fptr, "None", data, 0);
}
if(!el->text.len && !Q_Size(&el->children)) {
xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
}
else {
xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
if(el->text.len) {
char* escaped_str = el->text.str;
int buflen = el->text.len;
if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
if(!escaped_str) {
escaped_str = el->text.str;
}
}
if(options->escaping & xml_elem_cdata_escaping) {
xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
}
xml_elem_writefunc(fptr, escaped_str, data, buflen);
if(escaped_str != el->text.str) {
my_free(escaped_str);
}
if(options->escaping & xml_elem_cdata_escaping) {
xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
}
}
else {
xml_element *kids = Q_Head(&el->children);
i = 0;
while( kids ) {
if(i++ == 0) {
if(options->verbosity != xml_elem_no_white_space) {
xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
}
}
xml_element_serialize(kids, fptr, data, options, depth);
kids = Q_Next(&el->children);
}
if(i) {
if(options->verbosity == xml_elem_pretty && depth > 2) {
xml_elem_writefunc(fptr, whitespace, data, depth - 2);
}
}
}
xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
}
if(options->verbosity != xml_elem_no_white_space) {
xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
}
}
static int file_out_fptr(void *f, const char *text, int size)
{
fputs(text, (FILE *)f);
return 0;
}
static int simplestring_out_fptr(void *f, const char *text, int size)
{
simplestring* buf = (simplestring*)f;
if(buf) {
simplestring_addn(buf, text, size);
}
return 0;
}
char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
{
simplestring buf;
simplestring_init(&buf);
xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
if(buf_len) {
*buf_len = buf.len;
}
return buf.str;
}
void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
{
xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
}
typedef struct _xml_elem_data {
xml_element* root;
xml_element* current;
XML_ELEM_INPUT_OPTIONS input_options;
int needs_enc_conversion;
} xml_elem_data;
static void _xmlrpc_startElement(void *userData, const char *name, const char **attrs)
{
xml_element *c;
xml_elem_data* mydata = (xml_elem_data*)userData;
const char** p = attrs;
if(mydata) {
c = mydata->current;
mydata->current = xml_elem_new();
mydata->current->name = (char*)strdup(name);
mydata->current->parent = c;
while(p && *p) {
xml_element_attr* attr = malloc(sizeof(xml_element_attr));
if(attr) {
attr->key = strdup(*p);
attr->val = strdup(*(p+1));
Q_PushTail(&mydata->current->attrs, attr);
p += 2;
}
}
}
}
static void _xmlrpc_endElement(void *userData, const char *name)
{
xml_elem_data* mydata = (xml_elem_data*)userData;
if(mydata && mydata->current && mydata->current->parent) {
Q_PushTail(&mydata->current->parent->children, mydata->current);
mydata->current = mydata->current->parent;
}
}
static void _xmlrpc_charHandler(void *userData,
const char *s,
int len)
{
xml_elem_data* mydata = (xml_elem_data*)userData;
if(mydata && mydata->current) {
if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
int new_len = 0;
char* add_text = utf8_decode(s, len, &new_len, mydata->input_options->encoding);
if(add_text) {
len = new_len;
simplestring_addn(&mydata->current->text, add_text, len);
free(add_text);
return;
}
}
simplestring_addn(&mydata->current->text, s, len);
}
}
xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
{
xml_element* xReturn = NULL;
char buf[100] = "";
static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
if(!options) {
options = &default_opts;
}
if(in_buf) {
XML_Parser parser;
xml_elem_data mydata = {0};
parser = XML_ParserCreate(NULL);
mydata.root = xml_elem_new();
mydata.current = mydata.root;
mydata.input_options = options;
mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
XML_SetElementHandler(parser, (XML_StartElementHandler)_xmlrpc_startElement, (XML_EndElementHandler)_xmlrpc_endElement);
XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_xmlrpc_charHandler);
XML_SetUserData(parser, (void*)&mydata);
if(!len) {
len = strlen(in_buf);
}
if(XML_Parse(parser, in_buf, len, 1) == 0) {
enum XML_Error err_code = XML_GetErrorCode(parser);
int line_num = XML_GetCurrentLineNumber(parser);
int col_num = XML_GetCurrentColumnNumber(parser);
long byte_idx = XML_GetCurrentByteIndex(parser);
const char * error_str = XML_ErrorString(err_code);
if(byte_idx >= 0) {
snprintf(buf,
sizeof(buf),
"\n\tdata beginning %ld before byte index: %s\n",
byte_idx > 10 ? 10 : byte_idx,
in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
}
if(error) {
error->parser_code = (long)err_code;
error->line = line_num;
error->column = col_num;
error->byte_index = byte_idx;
error->parser_error = error_str;
}
}
else {
xReturn = (xml_element*)Q_Head(&mydata.root->children);
xReturn->parent = NULL;
}
XML_ParserFree(parser);
xml_elem_free_non_recurse(mydata.root);
}
return xReturn;
}