freeswitch/libs/iksemel/src/iks.c

766 lines
14 KiB
C

/* iksemel (XML parser for Jabber)
** Copyright (C) 2000-2007 Gurer Ozen <madcat@e-kolay.net>
** This code is free software; you can redistribute it and/or
** modify it under the terms of GNU Lesser General Public License.
*/
#include "common.h"
#include "iksemel.h"
#define IKS_COMMON \
struct iks_struct *next, *prev; \
struct iks_struct *parent; \
enum ikstype type; \
ikstack *s
struct iks_struct {
IKS_COMMON;
};
struct iks_tag {
IKS_COMMON;
struct iks_struct *children, *last_child;
struct iks_struct *attribs, *last_attrib;
char *name;
};
#define IKS_TAG_NAME(x) ((struct iks_tag *) (x) )->name
#define IKS_TAG_CHILDREN(x) ((struct iks_tag *) (x) )->children
#define IKS_TAG_LAST_CHILD(x) ((struct iks_tag *) (x) )->last_child
#define IKS_TAG_ATTRIBS(x) ((struct iks_tag *) (x) )->attribs
#define IKS_TAG_LAST_ATTRIB(x) ((struct iks_tag *) (x) )->last_attrib
struct iks_cdata {
IKS_COMMON;
char *cdata;
size_t len;
};
#define IKS_CDATA_CDATA(x) ((struct iks_cdata *) (x) )->cdata
#define IKS_CDATA_LEN(x) ((struct iks_cdata *) (x) )->len
struct iks_attrib {
IKS_COMMON;
char *name;
char *value;
};
#define IKS_ATTRIB_NAME(x) ((struct iks_attrib *) (x) )->name
#define IKS_ATTRIB_VALUE(x) ((struct iks_attrib *) (x) )->value
/***** Node Creating & Deleting *****/
iks *
iks_new (const char *name)
{
ikstack *s;
iks *x;
s = iks_stack_new (sizeof (struct iks_tag) * 6, 256);
if (!s) return NULL;
x = iks_new_within (name, s);
if (!x) {
iks_stack_delete (&s);
return NULL;
}
return x;
}
iks *
iks_new_within (const char *name, ikstack *s)
{
iks *x;
size_t len;
if (name) len = sizeof (struct iks_tag); else len = sizeof (struct iks_cdata);
x = iks_stack_alloc (s, len);
if (!x) return NULL;
memset (x, 0, len);
x->s = s;
x->type = IKS_TAG;
if (name) {
IKS_TAG_NAME (x) = iks_stack_strdup (s, name, 0);
if (!IKS_TAG_NAME (x)) return NULL;
}
return x;
}
iks *
iks_insert (iks *x, const char *name)
{
iks *y;
if (!x) return NULL;
y = iks_new_within (name, x->s);
if (!y) return NULL;
y->parent = x;
if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
if (IKS_TAG_LAST_CHILD (x)) {
IKS_TAG_LAST_CHILD (x)->next = y;
y->prev = IKS_TAG_LAST_CHILD (x);
}
IKS_TAG_LAST_CHILD (x) = y;
return y;
}
iks *
iks_insert_cdata (iks *x, const char *data, size_t len)
{
iks *y;
if(!x || !data) return NULL;
if(len == 0) len = strlen (data);
y = IKS_TAG_LAST_CHILD (x);
if (y && y->type == IKS_CDATA) {
IKS_CDATA_CDATA (y) = iks_stack_strcat (x->s, IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y), data, len);
IKS_CDATA_LEN (y) += len;
} else {
y = iks_insert (x, NULL);
if (!y) return NULL;
y->type = IKS_CDATA;
IKS_CDATA_CDATA (y) = iks_stack_strdup (x->s, data, len);
if (!IKS_CDATA_CDATA (y)) return NULL;
IKS_CDATA_LEN (y) = len;
}
return y;
}
iks *
iks_insert_attrib (iks *x, const char *name, const char *value)
{
iks *y;
if (!x) return NULL;
y = IKS_TAG_ATTRIBS (x);
while (y) {
if (strcmp (name, IKS_ATTRIB_NAME (y)) == 0) break;
y = y->next;
}
if (NULL == y) {
if (!value) return NULL;
y = iks_stack_alloc (x->s, sizeof (struct iks_attrib));
if (!y) return NULL;
memset (y, 0, sizeof (struct iks_attrib));
y->type = IKS_ATTRIBUTE;
y->s = x->s;
IKS_ATTRIB_NAME (y) = iks_stack_strdup (x->s, name, 0);
if (!IKS_ATTRIB_NAME (y)) return NULL;
y->parent = x;
if (!IKS_TAG_ATTRIBS (x)) IKS_TAG_ATTRIBS (x) = y;
if (IKS_TAG_LAST_ATTRIB (x)) {
IKS_TAG_LAST_ATTRIB (x)->next = y;
y->prev = IKS_TAG_LAST_ATTRIB (x);
}
IKS_TAG_LAST_ATTRIB (x) = y;
}
if (value) {
IKS_ATTRIB_VALUE (y) = iks_stack_strdup (x->s, value, 0);
if (!IKS_ATTRIB_VALUE (y)) return NULL;
} else {
if (y->next) y->next->prev = y->prev;
if (y->prev) y->prev->next = y->next;
if (IKS_TAG_ATTRIBS (x) == y) IKS_TAG_ATTRIBS (x) = y->next;
if (IKS_TAG_LAST_ATTRIB (x) == y) IKS_TAG_LAST_ATTRIB (x) = y->prev;
}
return y;
}
iks *
iks_insert_node (iks *x, iks *y)
{
y->parent = x;
if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
if (IKS_TAG_LAST_CHILD (x)) {
IKS_TAG_LAST_CHILD (x)->next = y;
y->prev = IKS_TAG_LAST_CHILD (x);
}
IKS_TAG_LAST_CHILD (x) = y;
return y;
}
iks *
iks_append (iks *x, const char *name)
{
iks *y;
if (!x) return NULL;
y = iks_new_within (name, x->s);
if (!y) return NULL;
if (x->next) {
x->next->prev = y;
} else {
IKS_TAG_LAST_CHILD (x->parent) = y;
}
y->next = x->next;
x->next = y;
y->parent = x->parent;
y->prev = x;
return y;
}
iks *
iks_prepend (iks *x, const char *name)
{
iks *y;
if (!x) return NULL;
y = iks_new_within (name, x->s);
if (!y) return NULL;
if (x->prev) {
x->prev->next = y;
} else {
IKS_TAG_CHILDREN (x->parent) = y;
}
y->prev = x->prev;
x->prev = y;
y->parent = x->parent;
y->next = x;
return y;
}
iks *
iks_append_cdata (iks *x, const char *data, size_t len)
{
iks *y;
if (!x || !data) return NULL;
if (len == 0) len = strlen (data);
y = iks_new_within (NULL, x->s);
if (!y) return NULL;
y->type = IKS_CDATA;
IKS_CDATA_CDATA (y) = iks_stack_strdup (x->s, data, len);
if (!IKS_CDATA_CDATA (y)) return NULL;
IKS_CDATA_LEN (y) = len;
if (x->next) {
x->next->prev = y;
} else {
IKS_TAG_LAST_CHILD (x->parent) = y;
}
y->next = x->next;
x->next = y;
y->parent = x->parent;
y->prev = x;
return y;
}
iks *
iks_prepend_cdata (iks *x, const char *data, size_t len)
{
iks *y;
if (!x || !data) return NULL;
if (len == 0) len = strlen (data);
y = iks_new_within (NULL, x->s);
if (!y) return NULL;
y->type = IKS_CDATA;
IKS_CDATA_CDATA(y) = iks_stack_strdup (x->s, data, len);
if (!IKS_CDATA_CDATA (y)) return NULL;
IKS_CDATA_LEN (y) = len;
if (x->prev) {
x->prev->next = y;
} else {
IKS_TAG_CHILDREN (x->parent) = y;
}
y->prev = x->prev;
x->prev = y;
y->parent = x->parent;
y->next = x;
return y;
}
void
iks_hide (iks *x)
{
iks *y;
if (!x) return;
if (x->prev) x->prev->next = x->next;
if (x->next) x->next->prev = x->prev;
y = x->parent;
if (y) {
if (IKS_TAG_CHILDREN (y) == x) IKS_TAG_CHILDREN (y) = x->next;
if (IKS_TAG_LAST_CHILD (y) == x) IKS_TAG_LAST_CHILD (y) = x->prev;
}
}
void
iks_delete (iks *x)
{
if (x) iks_stack_delete (&x->s);
}
/***** Node Traversing *****/
iks *
iks_next (iks *x)
{
if (x) return x->next;
return NULL;
}
iks *
iks_next_tag (iks *x)
{
if (x) {
while (1) {
x = x->next;
if (NULL == x) break;
if (IKS_TAG == x->type) return x;
}
}
return NULL;
}
iks *
iks_prev (iks *x)
{
if (x) return x->prev;
return NULL;
}
iks *
iks_prev_tag (iks *x)
{
if (x) {
while (1) {
x = x->prev;
if (NULL == x) break;
if (IKS_TAG == x->type) return x;
}
}
return NULL;
}
iks *
iks_parent (iks *x)
{
if (x) return x->parent;
return NULL;
}
iks *
iks_root (iks *x)
{
if (x) {
while (x->parent)
x = x->parent;
}
return x;
}
iks *
iks_child (iks *x)
{
if (x && IKS_TAG == x->type) return IKS_TAG_CHILDREN (x);
return NULL;
}
iks *
iks_first_tag (iks *x)
{
if (x) {
x = IKS_TAG_CHILDREN (x);
while (x) {
if (IKS_TAG == x->type) return x;
x = x->next;
}
}
return NULL;
}
iks *
iks_attrib (iks *x)
{
if (x) return IKS_TAG_ATTRIBS (x);
return NULL;
}
iks *
iks_find (iks *x, const char *name)
{
iks *y;
if (!x) return NULL;
y = IKS_TAG_CHILDREN (x);
while (y) {
if (IKS_TAG == y->type && IKS_TAG_NAME (y) && strcmp (IKS_TAG_NAME (y), name) == 0) return y;
y = y->next;
}
return NULL;
}
char *
iks_find_cdata (iks *x, const char *name)
{
iks *y;
y = iks_find (x, name);
if (!y) return NULL;
y = IKS_TAG_CHILDREN (y);
if (!y || IKS_CDATA != y->type) return NULL;
return IKS_CDATA_CDATA (y);
}
char *
iks_find_attrib (iks *x, const char *name)
{
iks *y;
if (!x) return NULL;
y = IKS_TAG_ATTRIBS (x);
while (y) {
if (IKS_ATTRIB_NAME (y) && strcmp (IKS_ATTRIB_NAME (y), name) == 0)
return IKS_ATTRIB_VALUE (y);
y = y->next;
}
return NULL;
}
iks *
iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value)
{
iks *y;
if (NULL == x) return NULL;
if (tagname) {
for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
if (IKS_TAG == y->type
&& strcmp (IKS_TAG_NAME (y), tagname) == 0
&& iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
return y;
}
}
} else {
for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
if (IKS_TAG == y->type
&& iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
return y;
}
}
}
return NULL;
}
/***** Node Information *****/
ikstack *
iks_stack (iks *x)
{
if (x) return x->s;
return NULL;
}
enum ikstype
iks_type (iks *x)
{
if (x) return x->type;
return IKS_NONE;
}
char *
iks_name (iks *x)
{
if (x) {
if (IKS_TAG == x->type)
return IKS_TAG_NAME (x);
else
return IKS_ATTRIB_NAME (x);
}
return NULL;
}
char *
iks_cdata (iks *x)
{
if (x) {
if (IKS_CDATA == x->type)
return IKS_CDATA_CDATA (x);
else
return IKS_ATTRIB_VALUE (x);
}
return NULL;
}
size_t
iks_cdata_size (iks *x)
{
if (x) return IKS_CDATA_LEN (x);
return 0;
}
int
iks_has_children (iks *x)
{
if (x && IKS_TAG == x->type && IKS_TAG_CHILDREN (x)) return 1;
return 0;
}
int
iks_has_attribs (iks *x)
{
if (x && IKS_TAG == x->type && IKS_TAG_ATTRIBS (x)) return 1;
return 0;
}
/***** Serializing *****/
static size_t
escape_size (char *src, size_t len)
{
size_t sz;
char c;
int i;
sz = 0;
for (i = 0; i < len; i++) {
c = src[i];
switch (c) {
case '&': sz += 5; break;
case '\'': sz += 6; break;
case '"': sz += 6; break;
case '<': sz += 4; break;
case '>': sz += 4; break;
default: sz++; break;
}
}
return sz;
}
static char *
my_strcat (char *dest, char *src, size_t len)
{
if (0 == len) len = strlen (src);
memcpy (dest, src, len);
return dest + len;
}
static char *
escape (char *dest, char *src, size_t len)
{
char c;
int i;
int j = 0;
for (i = 0; i < len; i++) {
c = src[i];
if ('&' == c || '<' == c || '>' == c || '\'' == c || '"' == c) {
if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
j = i + 1;
switch (c) {
case '&': dest = my_strcat (dest, "&amp;", 5); break;
case '\'': dest = my_strcat (dest, "&apos;", 6); break;
case '"': dest = my_strcat (dest, "&quot;", 6); break;
case '<': dest = my_strcat (dest, "&lt;", 4); break;
case '>': dest = my_strcat (dest, "&gt;", 4); break;
}
}
}
if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
return dest;
}
char *
iks_string (ikstack *s, iks *x)
{
size_t size;
int level, dir;
iks *y, *z;
char *ret, *t;
if (!x) return NULL;
if (x->type == IKS_CDATA) {
if (s) {
return iks_stack_strdup (s, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
} else {
ret = iks_malloc (IKS_CDATA_LEN (x));
memcpy (ret, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
return ret;
}
}
size = 0;
level = 0;
dir = 0;
y = x;
while (1) {
if (dir==0) {
if (y->type == IKS_TAG) {
size++;
size += strlen (IKS_TAG_NAME (y));
for (z = IKS_TAG_ATTRIBS (y); z; z = z->next) {
if (z->type == IKS_NONE) {
continue;
}
size += 4 + strlen (IKS_ATTRIB_NAME (z))
+ escape_size (IKS_ATTRIB_VALUE (z), strlen (IKS_ATTRIB_VALUE (z)));
}
if (IKS_TAG_CHILDREN (y)) {
size++;
y = IKS_TAG_CHILDREN (y);
level++;
continue;
} else {
size += 2;
}
} else {
size += escape_size (IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y));
}
}
z = y->next;
if (z) {
if (0 == level) {
if (IKS_TAG_CHILDREN (y)) size += 3 + strlen (IKS_TAG_NAME (y));
break;
}
y = z;
dir = 0;
} else {
y = y->parent;
level--;
if (level >= 0) size += 3 + strlen (IKS_TAG_NAME (y));
if (level < 1) break;
dir = 1;
}
}
if (s) ret = iks_stack_alloc (s, size + 1);
else ret = iks_malloc (size + 1);
if (!ret) return NULL;
t = ret;
level = 0;
dir = 0;
while (1) {
if (dir==0) {
if (x->type == IKS_TAG) {
*t++ = '<';
t = my_strcat (t, IKS_TAG_NAME (x), 0);
y = IKS_TAG_ATTRIBS (x);
while (y) {
*t++ = ' ';
t = my_strcat (t, IKS_ATTRIB_NAME (y), 0);
*t++ = '=';
*t++ = '\'';
t = escape (t, IKS_ATTRIB_VALUE (y), strlen (IKS_ATTRIB_VALUE (y)));
*t++ = '\'';
y = y->next;
}
if (IKS_TAG_CHILDREN (x)) {
*t++ = '>';
x = IKS_TAG_CHILDREN (x);
level++;
continue;
} else {
*t++ = '/';
*t++ = '>';
}
} else {
t = escape (t, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
}
}
y = x->next;
if (y) {
if (0 == level) {
if (IKS_TAG_CHILDREN (x)) {
*t++ = '<';
*t++ = '/';
t = my_strcat (t, IKS_TAG_NAME (x), 0);
*t++ = '>';
}
break;
}
x = y;
dir = 0;
} else {
x = x->parent;
level--;
if (level >= 0) {
*t++ = '<';
*t++ = '/';
t = my_strcat (t, IKS_TAG_NAME (x), 0);
*t++ = '>';
}
if (level < 1) break;
dir = 1;
}
}
*t = '\0';
return ret;
}
/***** Copying *****/
iks *
iks_copy_within (iks *x, ikstack *s)
{
int level=0, dir=0;
iks *copy = NULL;
iks *cur = NULL;
iks *y;
while (1) {
if (dir == 0) {
if (x->type == IKS_TAG) {
if (copy == NULL) {
copy = iks_new_within (IKS_TAG_NAME (x), s);
cur = copy;
} else {
cur = iks_insert (cur, IKS_TAG_NAME (x));
}
for (y = IKS_TAG_ATTRIBS (x); y; y = y->next) {
iks_insert_attrib (cur, IKS_ATTRIB_NAME (y), IKS_ATTRIB_VALUE (y));
}
if (IKS_TAG_CHILDREN (x)) {
x = IKS_TAG_CHILDREN (x);
level++;
continue;
} else {
cur = cur->parent;
}
} else {
iks_insert_cdata (cur, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
}
}
y = x->next;
if (y) {
if (0 == level) break;
x = y;
dir = 0;
} else {
if (level < 2) break;
level--;
x = x->parent;
cur = cur->parent;
dir = 1;
}
}
return copy;
}
iks *
iks_copy (iks *x)
{
return iks_copy_within (x, iks_stack_new (sizeof (struct iks_tag) * 6, 256));
}