diff mcabber/libjabber/xmlnode.c @ 25:bf3d6e241714

[/trunk] Changeset 41 by mikael * Add libjabber to trunk. Let the game begin! :-)
author mikael
date Sun, 27 Mar 2005 20:18:21 +0000
parents
children c3ae9251c197
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/libjabber/xmlnode.c	Sun Mar 27 20:18:21 2005 +0000
@@ -0,0 +1,795 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Jabber
+ *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#include "libxode.h"
+
+/* Internal routines */
+xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
+{
+    xmlnode result = NULL;
+    if (type > NTYPE_LAST)
+        return NULL;
+
+    if (type != NTYPE_CDATA && name == NULL)
+        return NULL;
+
+    if (p == NULL)
+    {
+        p = pool_heap(1*1024);
+    }
+
+    /* Allocate & zero memory */
+    result = (xmlnode)pmalloc(p, sizeof(_xmlnode));
+    memset(result, '\0', sizeof(_xmlnode));
+
+    /* Initialize fields */
+    if (type != NTYPE_CDATA)
+        result->name = pstrdup(p,name);
+    result->type = type;
+    result->p = p;
+    return result;
+}
+
+static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
+{
+    xmlnode result;
+
+    result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
+    if (result != NULL)
+    {
+        /* Setup sibling pointers */
+        result->prev = lastsibling;
+        lastsibling->next = result;
+    }
+    return result;
+}
+
+static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
+{
+    xmlnode result;
+
+    if(parent == NULL || name == NULL) return NULL;
+
+    /* If parent->firstchild is NULL, simply create a new node for the first child */
+    if (parent->firstchild == NULL)
+    {
+        result = _xmlnode_new(parent->p, name, type);
+        parent->firstchild = result;
+    }
+    /* Otherwise, append this to the lastchild */
+    else
+    {
+        result= _xmlnode_append_sibling(parent->lastchild, name, type);
+    }
+    result->parent = parent;
+    parent->lastchild = result;
+    return result;
+
+}
+
+static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
+{
+    xmlnode current;
+
+    /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
+    the specified name */
+    current = firstsibling;
+    while (current != NULL)
+    {
+        if ((current->type == type) && (j_strcmp(current->name, name) == 0))
+            return current;
+        else
+            current = current->next;
+    }
+    return NULL;
+}
+
+static char* _xmlnode_merge(pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize)
+{
+    char* result;
+    result = (char*)pmalloc(p, destsize + srcsize + 1);
+    memcpy(result, dest, destsize);
+    memcpy(result+destsize, src, srcsize);
+    result[destsize + srcsize] = '\0';
+
+    /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the pool and subtract it from the size, this is for xmlstream's big-node checking */
+    p->size -= destsize;
+
+    return result;
+}
+
+static void _xmlnode_hide_sibling(xmlnode child)
+{
+    if(child == NULL)
+        return;
+
+    if(child->prev != NULL)
+        child->prev->next = child->next;
+    if(child->next != NULL)
+        child->next->prev = child->prev;
+}
+
+void _xmlnode_tag2str(spool s, xmlnode node, int flag)
+{
+    xmlnode tmp;
+
+    if(flag==0 || flag==1)
+    {
+	    spooler(s,"<",xmlnode_get_name(node),s);
+	    tmp = xmlnode_get_firstattrib(node);
+	    while(tmp) {
+	        spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
+	        tmp = xmlnode_get_nextsibling(tmp);
+	    }
+	    if(flag==0)
+	        spool_add(s,"/>");
+	    else
+	        spool_add(s,">");
+    }
+    else
+    {
+	    spooler(s,"</",xmlnode_get_name(node),">",s);
+    }
+}
+
+spool _xmlnode2spool(xmlnode node)
+{
+    spool s;
+    int level=0,dir=0;
+    xmlnode tmp;
+
+    if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
+	return NULL;
+
+    s = spool_new(xmlnode_pool(node));
+    if(!s) return(NULL);
+
+    while(1)
+    {
+        if(dir==0)
+        {
+    	    if(xmlnode_get_type(node) == NTYPE_TAG)
+            {
+		        if(xmlnode_has_children(node))
+                {
+		            _xmlnode_tag2str(s,node,1);
+        		    node = xmlnode_get_firstchild(node);
+		            level++;
+		            continue;
+        		}
+                else
+                {
+		            _xmlnode_tag2str(s,node,0);
+		        }
+	        }
+            else
+            {
+		        spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
+	        }
+	    }
+
+    	tmp = xmlnode_get_nextsibling(node);
+	    if(!tmp)
+        {
+	        node = xmlnode_get_parent(node);
+	        level--;
+	        if(level>=0) _xmlnode_tag2str(s,node,2);
+	        if(level<1) break;
+	        dir = 1;
+	    }
+        else
+        {
+	        node = tmp;
+	        dir = 0;
+	    }
+    }
+
+    return s;
+}
+
+
+/* External routines */
+
+
+/*
+ *  xmlnode_new_tag -- create a tag node
+ *  Automatically creates a memory pool for the node.
+ *
+ *  parameters
+ *      name -- name of the tag
+ *
+ *  returns
+ *      a pointer to the tag node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_new_tag(const char* name)
+{
+    return _xmlnode_new(NULL, name, NTYPE_TAG);
+}
+
+
+/*
+ *  xmlnode_new_tag_pool -- create a tag node within given pool
+ *
+ *  parameters
+ *      p -- previously created memory pool
+ *      name -- name of the tag
+ *
+ *  returns
+ *      a pointer to the tag node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_new_tag_pool(pool p, const char* name)
+{
+    return _xmlnode_new(p, name, NTYPE_TAG);
+}
+
+
+/*
+ *  xmlnode_insert_tag -- append a child tag to a tag
+ *
+ *  parameters
+ *      parent -- pointer to the parent tag
+ *      name -- name of the child tag
+ *
+ *  returns
+ *      a pointer to the child tag node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
+{
+    return _xmlnode_insert(parent, name, NTYPE_TAG);
+}
+
+
+/*
+ *  xmlnode_insert_cdata -- append character data to a tag
+ *  If last child of the parent is CDATA, merges CDATA nodes. Otherwise
+ *  creates a CDATA node, and appends it to the parent's child list.
+ *
+ *  parameters
+ *      parent -- parent tag
+ *      CDATA -- character data
+ *      size -- size of CDATA
+ *              or -1 for null-terminated CDATA strings
+ *
+ *  returns
+ *      a pointer to the child CDATA node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
+{
+    xmlnode result;
+
+    if(CDATA == NULL || parent == NULL)
+        return NULL;
+
+    if(size == -1)
+        size = strlen(CDATA);
+
+    if ((parent->lastchild != NULL) && (parent->lastchild->type == NTYPE_CDATA))
+    {
+        result = parent->lastchild;
+        result->data = _xmlnode_merge(result->p, result->data, result->data_sz, CDATA, size);
+        result->data_sz = result->data_sz + size;
+    }
+    else
+    {
+        result = _xmlnode_insert(parent, "", NTYPE_CDATA);
+        if (result != NULL)
+        {
+            result->data = (char*)pmalloc(result->p, size + 1);
+            memcpy(result->data, CDATA, size);
+            result->data[size] = '\0';
+            result->data_sz = size;
+        }
+    }
+
+    return result;
+}
+
+
+/*
+ *  xmlnode_get_tag -- find given tag in an xmlnode tree
+ *
+ *  parameters
+ *      parent -- pointer to the parent tag
+ *      name -- "name" for the child tag of that name
+ *              "name/name" for a sub child (recurses)
+ *              "?attrib" to match the first tag with that attrib defined
+ *              "?attrib=value" to match the first tag with that attrib and value
+ *              or any combination: "name/name/?attrib", etc
+ *
+ *  results
+ *      a pointer to the tag matching search criteria
+ *      or NULL if search was unsuccessfull
+ */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
+{
+    char *str, *slash, *qmark, *equals;
+    xmlnode step, ret;
+
+    if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
+
+    if(strstr(name, "/") == NULL && strstr(name,"?") == NULL)
+        return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
+
+    /* jer's note: why can't I modify the name directly, why do I have to strdup it?  damn c grrr! */
+    str = strdup(name);
+    slash = strstr(str, "/");
+    qmark = strstr(str, "?");
+    equals = strstr(str, "=");
+
+    if(qmark != NULL && (slash == NULL || qmark < slash))
+    { /* of type ?attrib */
+
+        *qmark = '\0';
+        qmark++;
+        if(equals != NULL)
+        {
+            *equals = '\0';
+            equals++;
+        }
+
+        for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+        {
+            if(xmlnode_get_type(step) != NTYPE_TAG)
+                continue;
+
+            if(*str != '\0')
+                if(j_strcmp(xmlnode_get_name(step),str) != 0)
+                    continue;
+
+            if(xmlnode_get_attrib(step,qmark) == NULL)
+                continue;
+
+            if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
+                continue;
+
+            break;
+        }
+
+        free(str);
+        return step;
+    }
+
+
+    *slash = '\0';
+    ++slash;
+
+    for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+    {
+        if(xmlnode_get_type(step) != NTYPE_TAG) continue;
+
+        if(j_strcmp(xmlnode_get_name(step),str) != 0)
+            continue;
+
+        ret = xmlnode_get_tag(step, slash);
+        if(ret != NULL)
+        {
+            free(str);
+            return ret;
+        }
+    }
+
+    free(str);
+    return NULL;
+}
+
+
+/* return the cdata from any tag */
+char *xmlnode_get_tag_data(xmlnode parent, const char *name)
+{
+    xmlnode tag;
+
+    tag = xmlnode_get_tag(parent, name);
+    if(tag == NULL) return NULL;
+
+    return xmlnode_get_data(tag);
+}
+
+
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
+{
+    xmlnode attrib;
+
+    if(owner == NULL || name == NULL || value == NULL) return;
+
+    /* If there are no existing attributs, allocate a new one to start
+    the list */
+    if (owner->firstattrib == NULL)
+    {
+        attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
+        owner->firstattrib = attrib;
+        owner->lastattrib  = attrib;
+    }
+    else
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if(attrib == NULL)
+        {
+            attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
+            owner->lastattrib = attrib;
+        }
+    }
+    /* Update the value of the attribute */
+    attrib->data_sz = strlen(value);
+    attrib->data    = pstrdup(owner->p, value);
+
+}
+
+char* xmlnode_get_attrib(xmlnode owner, const char* name)
+{
+    xmlnode attrib;
+
+    if (owner != NULL && owner->firstattrib != NULL)
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if (attrib != NULL)
+            return (char*)attrib->data;
+    }
+    return NULL;
+}
+
+void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value)
+{
+    xmlnode attrib;
+
+    if (owner != NULL)
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if (attrib == NULL)
+        {
+            xmlnode_put_attrib(owner, name, "");
+            attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        }
+        if (attrib != NULL)
+            attrib->firstchild = (xmlnode)value;
+    }
+}
+
+void* xmlnode_get_vattrib(xmlnode owner, const char* name)
+{
+    xmlnode attrib;
+
+    if (owner != NULL && owner->firstattrib != NULL)
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if (attrib != NULL)
+            return (void*)attrib->firstchild;
+    }
+    return NULL;
+}
+
+xmlnode xmlnode_get_firstattrib(xmlnode parent)
+{
+    if (parent != NULL)
+        return parent->firstattrib;
+    return NULL;
+}
+
+xmlnode xmlnode_get_firstchild(xmlnode parent)
+{
+    if (parent != NULL)
+        return parent->firstchild;
+    return NULL;
+}
+
+xmlnode xmlnode_get_lastchild(xmlnode parent)
+{
+    if (parent != NULL)
+        return parent->lastchild;
+    return NULL;
+}
+
+xmlnode xmlnode_get_nextsibling(xmlnode sibling)
+{
+    if (sibling != NULL)
+        return sibling->next;
+    return NULL;
+}
+
+xmlnode xmlnode_get_prevsibling(xmlnode sibling)
+{
+    if (sibling != NULL)
+        return sibling->prev;
+    return NULL;
+}
+
+xmlnode xmlnode_get_parent(xmlnode node)
+{
+    if (node != NULL)
+        return node->parent;
+    return NULL;
+}
+
+char* xmlnode_get_name(xmlnode node)
+{
+    if (node != NULL)
+        return node->name;
+    return NULL;
+}
+
+char* xmlnode_get_data(xmlnode node)
+{
+    xmlnode cur;
+
+    if(node == NULL) return NULL;
+
+    if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA */
+    {
+        for(cur = xmlnode_get_firstchild(node); cur != NULL; cur = xmlnode_get_nextsibling(cur))
+            if(xmlnode_get_type(cur) == NTYPE_CDATA)
+                return cur->data;
+    }else{
+        return node->data;
+    }
+    return NULL;
+}
+
+int xmlnode_get_datasz(xmlnode node)
+{
+    if (node != NULL)
+        return node->data_sz;
+    return (int)NULL;
+}
+
+int xmlnode_get_type(xmlnode node)
+{
+    if (node != NULL)
+        return node->type;
+    return (int)NULL;
+}
+
+int xmlnode_has_children(xmlnode node)
+{
+    if ((node != NULL) && (node->firstchild != NULL))
+        return 1;
+    return 0;
+}
+
+int xmlnode_has_attribs(xmlnode node)
+{
+    if ((node != NULL) && (node->firstattrib != NULL))
+        return 1;
+    return 0;
+}
+
+pool xmlnode_pool(xmlnode node)
+{
+    if (node != NULL)
+        return node->p;
+    return (pool)NULL;
+}
+
+void xmlnode_hide(xmlnode child)
+{
+    xmlnode parent;
+
+    if(child == NULL || child->parent == NULL)
+        return;
+
+    parent = child->parent;
+
+    /* first fix up at the child level */
+    _xmlnode_hide_sibling(child);
+
+    /* next fix up at the parent level */
+    if(parent->firstchild == child)
+        parent->firstchild = child->next;
+    if(parent->lastchild == child)
+        parent->lastchild = child->prev;
+}
+
+void xmlnode_hide_attrib(xmlnode parent, const char *name)
+{
+    xmlnode attrib;
+
+    if(parent == NULL || parent->firstattrib == NULL || name == NULL)
+        return;
+
+    attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
+    if(attrib == NULL)
+        return;
+
+    /* first fix up at the child level */
+    _xmlnode_hide_sibling(attrib);
+
+    /* next fix up at the parent level */
+    if(parent->firstattrib == attrib)
+        parent->firstattrib = attrib->next;
+    if(parent->lastattrib == attrib)
+        parent->lastattrib = attrib->prev;
+}
+
+
+
+/*
+ *  xmlnode2str -- convert given xmlnode tree into a string
+ *
+ *  parameters
+ *      node -- pointer to the xmlnode structure
+ *
+ *  results
+ *      a pointer to the created string
+ *      or NULL if it was unsuccessfull
+ */
+char *xmlnode2str(xmlnode node)
+{
+     return spool_print(_xmlnode2spool(node));
+}
+
+/*
+ *  xmlnode2tstr -- convert given xmlnode tree into a newline terminated string
+ *
+ *  parameters
+ *      node -- pointer to the xmlnode structure
+ *
+ *  results
+ *      a pointer to the created string
+ *      or NULL if it was unsuccessfull
+ */
+char*    xmlnode2tstr(xmlnode node)
+{
+     spool s = _xmlnode2spool(node);
+     if (s != NULL)
+	  spool_add(s, "\n");
+    return spool_print(s);
+}
+
+
+/* loop through both a and b comparing everything, attribs, cdata, children, etc */
+int xmlnode_cmp(xmlnode a, xmlnode b)
+{
+    int ret = 0;
+
+    while(1)
+    {
+        if(a == NULL && b == NULL)
+            return 0;
+
+        if(a == NULL || b == NULL)
+            return -1;
+
+        if(xmlnode_get_type(a) != xmlnode_get_type(b))
+            return -1;
+
+        switch(xmlnode_get_type(a))
+        {
+        case NTYPE_ATTRIB:
+            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+            if(ret != 0)
+                return -1;
+            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+            if(ret != 0)
+                return -1;
+            break;
+        case NTYPE_TAG:
+            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+            if(ret != 0)
+                return -1;
+            ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
+            if(ret != 0)
+                return -1;
+            ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
+            if(ret != 0)
+                return -1;
+            break;
+        case NTYPE_CDATA:
+            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+            if(ret != 0)
+                return -1;
+        }
+        a = xmlnode_get_nextsibling(a);
+        b = xmlnode_get_nextsibling(b);
+    }
+}
+
+
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
+{
+    xmlnode child;
+
+    child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
+    if (xmlnode_has_attribs(node))
+        xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
+    if (xmlnode_has_children(node))
+        xmlnode_insert_node(child, xmlnode_get_firstchild(node));
+
+    return child;
+}
+
+/* places copy of node and node's siblings in parent */
+void xmlnode_insert_node(xmlnode parent, xmlnode node)
+{
+    if(node == NULL || parent == NULL)
+        return;
+
+    while(node != NULL)
+    {
+        switch(xmlnode_get_type(node))
+        {
+        case NTYPE_ATTRIB:
+            xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
+            break;
+        case NTYPE_TAG:
+            xmlnode_insert_tag_node(parent, node);
+            break;
+        case NTYPE_CDATA:
+            xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
+        }
+        node = xmlnode_get_nextsibling(node);
+    }
+}
+
+
+/* produce full duplicate of x with a new pool, x must be a tag! */
+xmlnode xmlnode_dup(xmlnode x)
+{
+    xmlnode x2;
+
+    if(x == NULL)
+        return NULL;
+
+    x2 = xmlnode_new_tag(xmlnode_get_name(x));
+
+    if (xmlnode_has_attribs(x))
+        xmlnode_insert_node(x2, xmlnode_get_firstattrib(x));
+    if (xmlnode_has_children(x))
+        xmlnode_insert_node(x2, xmlnode_get_firstchild(x));
+
+    return x2;
+}
+
+xmlnode xmlnode_dup_pool(pool p, xmlnode x)
+{
+    xmlnode x2;
+
+    if(x == NULL)
+        return NULL;
+
+    x2 = xmlnode_new_tag_pool(p, xmlnode_get_name(x));
+
+    if (xmlnode_has_attribs(x))
+        xmlnode_insert_node(x2, xmlnode_get_firstattrib(x));
+    if (xmlnode_has_children(x))
+        xmlnode_insert_node(x2, xmlnode_get_firstchild(x));
+
+    return x2;
+}
+
+xmlnode xmlnode_wrap(xmlnode x,const char *wrapper)
+{
+    xmlnode wrap;
+    if(x==NULL||wrapper==NULL) return NULL;
+    wrap=xmlnode_new_tag_pool(xmlnode_pool(x),wrapper);
+    if(wrap==NULL) return NULL;
+    wrap->firstchild=x;
+    wrap->lastchild=x;
+    x->parent=wrap;
+    return wrap;
+}
+
+void xmlnode_free(xmlnode node)
+{
+    if(node == NULL)
+        return;
+
+    pool_free(node->p);
+}