#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cx/string.h>
#include <cx/map.h>
#include <cx/hash_map.h>
#include <cx/buffer.h>
#include <cx/linked_list.h>
#include "../util/util.h"
#include "../util/pool.h"
#include "xml.h"
static CxHashKey xml_namespace_key(CxAllocator *a, WSNamespace *ns) {
cxmutstr key_data = cx_strcat_a(a,
3,
ns->prefix ? cx_str((
char*)ns->prefix) : cx_strn(
"\0",
1),
cx_strn(
"\0",
1),
cx_str((
char*)ns->href));
return cx_hash_key_bytes((
unsigned char*)key_data.ptr, key_data.length);
}
typedef struct StackElm {
WSXmlNode *node;
int endonly;
struct StackElm *next;
} StackElm;
#define STACK_PUSH(stack, elm)
if(stack) { elm->next = stack; } stack = elm;
int wsxml_iterator(
pool_handle_t *pool,
WSXmlNode *node,
wsxml_func begincb,
wsxml_func endcb,
void *udata)
{
if(!node) {
return 0;
}
StackElm *stack = pool_malloc(pool,
sizeof(StackElm));
if(!stack) {
return 1;
}
stack->next =
NULL;
stack->node = node;
stack->endonly =
0;
int ret =
0;
int br =
0;
while(stack) {
StackElm *cur = stack;
WSXmlNode *xmlnode = cur->node;
stack = cur->next;
cur->next =
NULL;
while(xmlnode && !cur->endonly) {
if(begincb(xmlnode, udata)) {
br =
1;
break;
}
if(xmlnode->children) {
StackElm *newelm = pool_malloc(pool,
sizeof(StackElm));
if(!newelm) {
ret =
1;
br =
1;
break;
}
newelm->next =
NULL;
newelm->node = xmlnode->children;
newelm->endonly =
0;
if(xmlnode->next) {
StackElm *nextelm = pool_malloc(pool,
sizeof(StackElm));
if(!nextelm) {
ret =
1;
br =
1;
break;
}
nextelm->node = xmlnode->next;
nextelm->next =
NULL;
nextelm->endonly =
0;
STACK_PUSH(stack, nextelm);
}
cur->node = xmlnode;
cur->endonly =
1;
STACK_PUSH(stack, cur);
cur =
NULL;
STACK_PUSH(stack, newelm);
break;
}
else {
cur->node =
NULL;
if(endcb(xmlnode, udata)) {
br =
1;
break;
}
}
xmlnode = xmlnode->next;
}
if(br) {
break;
}
if(cur && cur->node) {
xmlNode *endNode = cur->node;
if(endcb(endNode, udata)) {
break;
}
pool_free(pool, cur);
}
}
StackElm *elm = stack;
while(elm) {
StackElm *next = elm->next;
pool_free(pool, elm);
elm = next;
}
return ret;
}
typedef struct WSNsCollector {
CxAllocator *a;
CxMap *nsmap;
WebdavNSList *def;
int error;
} WSNsCollector;
static int nslist_node_begin(xmlNode *node,
void *userdata) {
WSNsCollector *col = userdata;
if(node->type ==
XML_ELEMENT_NODE && node->ns) {
CxHashKey nskey = xml_namespace_key(col->a, node->ns);
if(!nskey.data) {
col->error =
1;
return 1;
}
if(cxMapPut(col->nsmap, nskey, node->ns)) {
col->error =
1;
return 1;
}
WSNamespace *def = node->nsDef;
while(def) {
WebdavNSList *newdef = cxMalloc(col->a,
sizeof(WebdavNSList));
if(!newdef) {
col->error =
1;
return 1;
}
newdef->namespace = def;
newdef->prev =
NULL;
newdef->next =
NULL;
if(col->def) {
newdef->next = col->def;
col->def->prev = newdef;
}
col->def = newdef;
def = def->next;
}
}
return 0;
}
static int nslist_node_end(xmlNode *node,
void *userdata) {
return 0;
}
WebdavNSList* wsxml_get_required_namespaces(
pool_handle_t *pool,
WSXmlNode *node,
int *error)
{
if(error) *error =
0;
CxAllocator *a = pool_allocator(pool);
CxMap *nsmap = cxHashMapCreate(a,
CX_STORE_POINTERS,
16);
if(!nsmap) {
if(error) *error =
1;
return NULL;
}
WSNsCollector col;
col.a = a;
col.nsmap = nsmap;
col.def =
NULL;
WebdavNSList *list =
NULL;
WebdavNSList *end =
NULL;
if(wsxml_iterator(pool, node, nslist_node_begin, nslist_node_end, &col)) {
if(error) *error =
1;
}
else {
WebdavNSList *def = col.def;
while(def) {
CxHashKey nskey = xml_namespace_key(a, def->namespace);
if(!nskey.data) {
if(error) *error =
1;
break;
}
cxMapRemove(nsmap, nskey);
def = def->next;
}
CxIterator i = cxMapIteratorValues(nsmap);
WSNamespace *ns;
cx_foreach(WSNamespace *, ns, i) {
WebdavNSList *newelm = pool_malloc(pool,
sizeof(WebdavNSList));
if(!newelm) {
if(error) *error =
1;
list =
NULL;
break;
}
newelm->namespace = ns;
newelm->next =
NULL;
newelm->prev =
NULL;
cx_linked_list_add((
void**)&list, (
void**)&end, offsetof(WebdavNSList, prev), offsetof(WebdavNSList, next), newelm);
}
}
cxMapDestroy(nsmap);
return list;
}
static ssize_t buf_writefunc(
void *buf,
const char *s,
size_t len) {
int w = cxBufferWrite(s,
1, len, buf);
return w ==
0 ?
IO_ERROR : w;
}
WSXmlData* wsxml_node2data(
pool_handle_t *pool,
WSXmlNode *node)
{
CxBuffer buf;
if(cxBufferInit(&buf,
NULL,
1024, pool_allocator(pool),
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS)) {
return NULL;
}
int error =
0;
WebdavNSList *nslist = wsxml_get_required_namespaces(pool, node, &error);
if(error) {
return NULL;
}
Writer writer;
char buffer[
512];
writer_init_with_stream(&writer, &buf, buf_writefunc, buffer,
512);
WSXmlData *data =
NULL;
if(!wsxml_write_nodes(pool, &writer,
NULL, node) && !writer_flush(&writer)) {
data = pool_malloc(pool,
sizeof(WSXmlData));
if(data) {
data->data = pool_malloc(pool, buf.size +
1);
if(data->data) {
memcpy(data->data, buf.space, buf.size);
data->data[buf.size] =
'\0';
data->length = buf.size;
data->namespaces = nslist;
}
}
}
cxBufferDestroy(&buf);
return data;
}
char* wsxml_nslist2string(
pool_handle_t *pool, WebdavNSList *nslist) {
if(!nslist)
return NULL;
size_t len =
0;
WebdavNSList *elm = nslist;
while(elm) {
WSNamespace *ns = elm->namespace;
if(ns) {
if(ns->prefix) len += strlen((
const char*)ns->prefix);
if(ns->href) len += strlen((
const char*)ns->href);
len +=
2;
}
elm = elm->next;
}
char *str = pool_malloc(pool, len);
if(!str) {
return NULL;
}
char *pos = str;
elm = nslist;
while(elm) {
WSNamespace *ns = elm->namespace;
if(ns) {
if(ns->prefix) {
size_t prefixlen = strlen((
const char*)ns->prefix);
memcpy(pos, ns->prefix, prefixlen);
pos[prefixlen] =
':';
pos += prefixlen +
1;
}
else {
pos[
0] =
':';
pos++;
}
if(ns->href) {
size_t hreflen = strlen((
const char*)ns->href);
memcpy(pos, ns->href, hreflen);
pos[hreflen] = elm->next ?
'\n' :
'\0';
pos += hreflen +
1;
}
else {
pos[
0] = elm->next ?
'\n' :
'\0';
pos++;
}
}
elm = elm->next;
}
return str;
}
WebdavNSList* wsxml_string2nslist(
pool_handle_t *pool,
char *nsliststr) {
if(!nsliststr)
return NULL;
size_t len = strlen(nsliststr);
WebdavNSList *list_start =
NULL;
WebdavNSList *list_current =
NULL;
char *prefix = nsliststr;
size_t prefix_start =
0;
size_t prefix_len =
0;
char *href =
NULL;
size_t href_start = len;
size_t i;
for(i=
0;i<=len;i++) {
char c = nsliststr[i];
if(c ==
'\n' || c ==
'\0') {
if(i > href_start) {
WebdavNSList *elm = pool_malloc(pool,
sizeof(WebdavNSList));
if(!elm) {
break;
}
elm->prev = list_current;
elm->next =
NULL;
WSNamespace *ns = pool_malloc(pool,
sizeof(WSNamespace));
elm->namespace = ns;
if(!ns) {
break;
}
memset(ns,
0,
sizeof(WSNamespace));
ns->prefix = prefix_len >
0 ? (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(prefix, prefix_len)).ptr :
NULL;
ns->href = (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(href, i-href_start)).ptr;
if(list_current) {
list_current->next = elm;
}
else {
list_start = elm;
}
list_current = elm;
}
prefix_start = i +
1;
prefix = nsliststr + prefix_start;
prefix_len =
0;
href_start = len;
href =
NULL;
}
else if(!href && c ==
':') {
prefix_len = i - prefix_start;
href_start = i +
1;
href = nsliststr + href_start;
}
}
if(i < len) {
while(list_start) {
if(list_start->namespace) {
WSNamespace *ns = list_start->namespace;
if(ns->prefix) {
pool_free(pool, (
char*)ns->prefix);
}
if(ns->href) {
pool_free(pool, (
char*)ns->href);
}
pool_free(pool, ns);
}
WebdavNSList *next = list_start->next;
pool_free(pool, list_start);
list_start = next;
}
list_start =
NULL;
}
return list_start;
}
typedef struct XmlWriter {
pool_handle_t *pool;
Writer *out;
CxMap *namespaces;
WSBool define_namespaces;
} XmlWriter;
static void xml_ser_text(Writer *out,
int type,
const char *text) {
size_t start =
0;
size_t i;
cxstring entityref = {
NULL,
0 };
for(i=
0;text[i]!=
'\0';i++) {
char c = text[i];
if(c ==
'&') {
entityref = (cxstring)
CX_STR(
"&");
}
else if(type ==
0) {
if(c ==
'<') {
entityref = (cxstring)
CX_STR(
"<");
}
else if(c ==
'>') {
entityref = (cxstring)
CX_STR(
">");
}
}
else {
if(c ==
'\"') {
entityref = (cxstring)
CX_STR(
""");
}
else if(c ==
'\'') {
entityref = (cxstring)
CX_STR(
"'");
}
}
if(entityref.ptr) {
size_t len = i-start;
if(len >
0) {
writer_put(out, text+start, len);
}
writer_puts(out, entityref);
entityref.ptr =
NULL;
entityref.length =
0;
start = i+
1;
}
}
size_t len = i-start;
if(len >
0) {
writer_put(out, text+start, len);
}
}
static void xml_ser_element(XmlWriter *xw, xmlNode *node) {
Writer *out = xw->out;
writer_putc(out,
'<');
if(node->ns && node->ns->prefix) {
writer_puts(out, cx_str((
char*)node->ns->prefix));
writer_putc(out,
':');
}
writer_puts(out, cx_str((
char*)node->name));
if(xw->define_namespaces) {
xmlNs *nsdef = node->nsDef;
while(nsdef) {
if(!nsdef->prefix) {
writer_put_lit(out,
" xmlns=\"");
writer_put_str(out, (
char*)nsdef->href);
writer_putc (out,
'""');
}
else {
WSNamespace *n = xw->namespaces ?
cxMapGet(xw->namespaces, cx_hash_key_str((
const char*)nsdef->prefix)) :
NULL;
if(!n) {
writer_put_lit(out,
" xmlns:");
writer_put_str(out, (
const char*)nsdef->prefix);
writer_put_lit(out,
"=\"");
writer_put_str(out, (
const char*)nsdef->href);
writer_putc (out,
'""');
}
}
nsdef = nsdef->next;
}
}
xmlAttr *attr = node->properties;
while(attr) {
writer_putc(out,
' ');
if(attr->ns && attr->ns->prefix) {
writer_puts(out, cx_str((
char*)attr->ns->prefix));
writer_putc(out,
':');
}
writer_put_str(out, (
char*)attr->name);
writer_put_lit(out,
"=\"");
xmlNode *value = attr->children;
while(value) {
if(value->content) {
xml_ser_text(out,
1, (
const char*)value->content);
}
value = value->next;
}
writer_putc(out,
'""');
attr = attr->next;
}
if(node->children) {
writer_putc(out,
'>');
}
else {
writer_put_lit(out,
"/>");
}
}
static int xml_ser_node_begin(xmlNode *node,
void *userdata) {
XmlWriter *xw = userdata;
switch(node->type) {
case XML_ELEMENT_NODE: xml_ser_element(xw, node);
break;
case XML_ATTRIBUTE_NODE:
break;
case XML_TEXT_NODE: {
xml_ser_text(xw->out,
0, (
const char*)node->content);
break;
}
case XML_CDATA_SECTION_NODE: {
break;
}
case XML_ENTITY_REF_NODE:
break;
case XML_ENTITY_NODE:
break;
case XML_PI_NODE:
break;
case XML_COMMENT_NODE:
break;
case XML_DOCUMENT_NODE:
break;
case XML_DOCUMENT_TYPE_NODE:
break;
case XML_DOCUMENT_FRAG_NODE:
break;
case XML_NOTATION_NODE:
break;
case XML_HTML_DOCUMENT_NODE:
break;
case XML_DTD_NODE:
break;
case XML_ELEMENT_DECL:
break;
case XML_ATTRIBUTE_DECL:
break;
case XML_ENTITY_DECL:
break;
case XML_NAMESPACE_DECL:
break;
case XML_XINCLUDE_START:
break;
case XML_XINCLUDE_END:
break;
default:
break;
}
return 0;
}
static int xml_ser_node_end(xmlNode *node,
void *userdata) {
XmlWriter *xw = userdata;
Writer *out = xw->out;
if(node->type ==
XML_ELEMENT_NODE) {
if(node->children) {
writer_put_lit(xw->out,
"</");
if(node->ns && node->ns->prefix) {
writer_puts(out, cx_str((
char*)node->ns->prefix));
writer_putc(out,
':');
}
writer_puts(out, cx_str((
char*)node->name));
writer_putc(out,
'>');
}
}
return 0;
}
static int xml_write_nodes(
pool_handle_t *pool,
Writer *out,
CxMap *nsdefs,
WSBool createdefs,
xmlNode *node)
{
XmlWriter xmlwriter;
xmlwriter.pool = pool;
xmlwriter.out = out;
xmlwriter.namespaces = nsdefs;
xmlwriter.define_namespaces = createdefs;
int err = wsxml_iterator(
pool,
node,
xml_ser_node_begin,
xml_ser_node_end,
&xmlwriter);
if(err) {
return -
1;
}
return out->error;
}
int wsxml_write_nodes(
pool_handle_t *pool,
Writer *out,
CxMap *nsdefs,
xmlNode *node)
{
return xml_write_nodes(pool, out, nsdefs,
TRUE, node);
}
int wsxml_write_nodes_without_nsdef(
pool_handle_t *pool,
Writer *out,
xmlNode *node)
{
return xml_write_nodes(pool, out,
NULL,
FALSE, node);
}