modules_k/jabber/xode.c
31ccf6a2
 /*
  * $Id$
  *
  *  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 "xode.h"
 
 static int _xode_strcmp(const char *a, const char *b)
 {
     if(a == NULL || b == NULL) return -1;
 
     return strcmp(a,b);
 }
 
 /* Internal routines */
 static xode _xode_new(xode_pool p, const char* name, unsigned int type)
 {
     xode result = NULL;
     if (type > XODE_TYPE_LAST)
         return NULL;
 
     if (type != XODE_TYPE_CDATA && name == NULL)
         return NULL;
 
     if (p == NULL)
     {
         p = xode_pool_heap(1*1024);
     }
 
     /* Allocate & zero memory */
     result = (xode)xode_pool_malloc(p, sizeof(_xode));
     memset(result, '\0', sizeof(_xode));
 
     /* Initialize fields */
     if (type != XODE_TYPE_CDATA)
         result->name = xode_pool_strdup(p,name);
     result->type = type;
     result->p = p;
     return result;
 }
 
 static xode _xode_appendsibling(xode lastsibling, const char* name, unsigned int type)
 {
     xode result;
 
     result = _xode_new(xode_get_pool(lastsibling), name, type);
     if (result != NULL)
     {
         /* Setup sibling pointers */
         result->prev = lastsibling;
         lastsibling->next = result;
     }
     return result;
 }
 
 static xode _xode_insert(xode parent, const char* name, unsigned int type)
 {
     xode 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 = _xode_new(parent->p, name, type);
         parent->firstchild = result;
     }
     /* Otherwise, append this to the lastchild */
     else
     {
         result= _xode_appendsibling(parent->lastchild, name, type);
     }
     result->parent = parent;
     parent->lastchild = result;
     return result;
 
 }
 
 static xode _xode_search(xode firstsibling, const char* name, unsigned int type)
 {
     xode current;
 
     /* Walk the sibling list, looking for a XODE_TYPE_TAG xode with
     the specified name */
     current = firstsibling;
     while (current != NULL)
     {
         if (name != NULL && (current->type == type) && (_xode_strcmp(current->name, name) == 0))
             return current;
         else
             current = current->next;
     }
     return NULL;
 }
 
 static char* _xode_merge(xode_pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize)
 {
     char* result;
     result = (char*)xode_pool_malloc(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 xode_pool and subtract it from the size, this is for xmlstream's big-node checking */
     p->size -= destsize;
 
     return result;
 }
 
 static void _xode_hidesibling(xode child)
 {
     if(child == NULL)
         return;
 
     if(child->prev != NULL)
         child->prev->next = child->next;
     if(child->next != NULL)
         child->next->prev = child->prev;
 }
 
 static void _xode_tag2str(xode_spool s, xode node, int flag)
 {
     xode tmp;
 
     if(flag==0 || flag==1)
     {
 	    xode_spooler(s,"<",xode_get_name(node),s);
 	    tmp = xode_get_firstattrib(node);
 	    while(tmp) {
 	        xode_spooler(s," ",xode_get_name(tmp),"='",xode_strescape(xode_get_pool(node),xode_get_data(tmp)),"'",s);
 	        tmp = xode_get_nextsibling(tmp);
 	    }
 	    if(flag==0)
 	        xode_spool_add(s,"/>");
 	    else
 	        xode_spool_add(s,">");
     }
     else
     {
 	    xode_spooler(s,"</",xode_get_name(node),">",s);
     }
 }
 
 static xode_spool _xode_tospool(xode node)
 {
     xode_spool s;
     int level=0,dir=0;
     xode tmp;
 
     if(!node || xode_get_type(node) != XODE_TYPE_TAG)
 	return NULL;
 
     s = xode_spool_newfrompool(xode_get_pool(node));
     if(!s) return(NULL);
 
     while(1)
     {
         if(dir==0)
         {
     	    if(xode_get_type(node) == XODE_TYPE_TAG)
             {
 		        if(xode_has_children(node))
                 {
 		            _xode_tag2str(s,node,1);
         		    node = xode_get_firstchild(node);
 		            level++;
 		            continue;
         		}
                 else
                 {
 		            _xode_tag2str(s,node,0);
 		        }
 	        }
             else
             {
 		        xode_spool_add(s,xode_strescape(xode_get_pool(node),xode_get_data(node)));
 	        }
 	    }
 
     	tmp = xode_get_nextsibling(node);
 	    if(!tmp)
         {
 	        node = xode_get_parent(node);
 	        level--;
 	        if(level>=0) _xode_tag2str(s,node,2);
 	        if(level<1) break;
 	        dir = 1;
 	    }
         else
         {
 	        node = tmp;
 	        dir = 0;
 	    }
     }
 
     return s;
 }
 
 
 /* External routines */
 
 
 /*
  *  xode_new_tag -- create a tag node
  *  Automatically creates a memory xode_pool for the node.
  *
  *  parameters
  *      name -- name of the tag
  *
  *  returns
  *      a pointer to the tag node
  *      or NULL if it was unsuccessful
  */
 xode xode_new(const char* name)
 {
     return _xode_new(NULL, name, XODE_TYPE_TAG);
 }
 
 /*
  * alias for 'xode_new'
  */
 xode xode_new_tag(const char* name)
 {
     return _xode_new(NULL, name, XODE_TYPE_TAG);
 }
 
 /*
  *  xode_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 unsuccessful
  */
 xode xode_new_frompool(xode_pool p, const char* name)
 {
     return _xode_new(p, name, XODE_TYPE_TAG);
 }
 
 
 /*
  *  xode_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 unsuccessful
  */
 xode xode_insert_tag(xode parent, const char* name)
 {
     return _xode_insert(parent, name, XODE_TYPE_TAG);
 }
 
 
 /*
  *  xode_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 unsuccessful
  */
 xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size)
 {
     xode result;
 
     if(CDATA == NULL || parent == NULL)
         return NULL;
 
     if(size == -1)
         size = strlen(CDATA);
 
     if ((parent->lastchild != NULL) && (parent->lastchild->type == XODE_TYPE_CDATA))
     {
         result = parent->lastchild;
         result->data = _xode_merge(result->p, result->data, result->data_sz, CDATA, size);
         result->data_sz = result->data_sz + size;
     }
     else
     {
         result = _xode_insert(parent, "", XODE_TYPE_CDATA);
         if (result != NULL)
         {
             result->data = (char*)xode_pool_malloc(result->p, size + 1);
             memcpy(result->data, CDATA, size);
             result->data[size] = '\0';
             result->data_sz = size;
         }
     }
 
     return result;
 }
 
 
 /*
  *  xode_gettag -- find given tag in an xode 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 unsuccessful
  */
 xode xode_get_tag(xode parent, const char* name)
 {
     char *str, *slash, *qmark, *equals;
     xode step, ret;
 
     if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
 
     if(strstr(name, "/") == NULL && strstr(name,"?") == NULL)
         return _xode_search(parent->firstchild, name, XODE_TYPE_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 = xode_get_nextsibling(step))
         {
             if(xode_get_type(step) != XODE_TYPE_TAG)
                 continue;
 
             if(*str != '\0')
                 if(_xode_strcmp(xode_get_name(step),str) != 0)
                     continue;
 
             if(xode_get_attrib(step,qmark) == NULL)
                 continue;
 
             if(equals != NULL && _xode_strcmp(xode_get_attrib(step,qmark),equals) != 0)
                 continue;
 
             break;
         }
 
         free(str);
         return step;
     }
 
 
     *slash = '\0';
     ++slash;
 
     for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step))
     {
         if(xode_get_type(step) != XODE_TYPE_TAG) continue;
 
         if(_xode_strcmp(xode_get_name(step),str) != 0)
             continue;
 
         ret = xode_get_tag(step, slash);
         if(ret != NULL)
         {
             free(str);
             return ret;
         }
     }
 
     free(str);
     return NULL;
 }
 
 
 /* return the cdata from any tag */
 char *xode_get_tagdata(xode parent, const char *name)
 {
     xode tag;
 
     tag = xode_get_tag(parent, name);
     if(tag == NULL) return NULL;
 
     return xode_get_data(tag);
 }
 
 
 void xode_put_attrib(xode owner, const char* name, const char* value)
 {
     xode attrib;
 
     if(owner == NULL || name == NULL || value == NULL) return;
 
     /* If there are no existing attributes, allocate a new one to start
     the list */
     if (owner->firstattrib == NULL)
     {
         attrib = _xode_new(owner->p, name, XODE_TYPE_ATTRIB);
         owner->firstattrib = attrib;
         owner->lastattrib  = attrib;
     }
     else
     {
         attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
         if(attrib == NULL)
         {
             attrib = _xode_appendsibling(owner->lastattrib, name, XODE_TYPE_ATTRIB);
             owner->lastattrib = attrib;
         }
     }
     /* Update the value of the attribute */
     attrib->data_sz = strlen(value);
     attrib->data    = xode_pool_strdup(owner->p, value);
 
 }
 
 char* xode_get_attrib(xode owner, const char* name)
 {
     xode attrib;
 
     if (owner != NULL && owner->firstattrib != NULL)
     {
         attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
         if (attrib != NULL)
             return (char*)attrib->data;
     }
     return NULL;
 }
 
 void xode_put_vattrib(xode owner, const char* name, void *value)
 {
     xode attrib;
 
     if (owner != NULL)
     {
         attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
         if (attrib == NULL)
         {
             xode_put_attrib(owner, name, "");
             attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
         }
         if (attrib != NULL)
             attrib->firstchild = (xode)value;
     }
 }
 
 void* xode_get_vattrib(xode owner, const char* name)
 {
     xode attrib;
 
     if (owner != NULL && owner->firstattrib != NULL)
     {
         attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
         if (attrib != NULL)
             return (void*)attrib->firstchild;
     }
     return NULL;
 }
 
 xode xode_get_firstattrib(xode parent)
 {
     if (parent != NULL)
         return parent->firstattrib;
     return NULL;
 }
 
 xode xode_get_firstchild(xode parent)
 {
     if (parent != NULL)
         return parent->firstchild;
     return NULL;
 }
 
 xode xode_get_lastchild(xode parent)
 {
     if (parent != NULL)
         return parent->lastchild;
     return NULL;
 }
 
 xode xode_get_nextsibling(xode sibling)
 {
     if (sibling != NULL)
         return sibling->next;
     return NULL;
 }
 
 xode xode_get_prevsibling(xode sibling)
 {
     if (sibling != NULL)
         return sibling->prev;
     return NULL;
 }
 
 xode xode_get_parent(xode node)
 {
     if (node != NULL)
         return node->parent;
     return NULL;
 }
 
 char* xode_get_name(xode node)
 {
     if (node != NULL)
         return node->name;
     return NULL;
 }
 
 char* xode_get_data(xode node)
 {
     xode cur;
 
     if(node == NULL) return NULL;
 
     if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */
     {
         for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur))
             if(xode_get_type(cur) == XODE_TYPE_CDATA)
                 return cur->data;
     }else{
         return node->data;
     }
     return NULL;
 }
 
 int xode_get_datasz(xode node)
 {
 	
     if( node == NULL )
     {
9b8f5200
         return (int)(long)NULL;	    
31ccf6a2
     }	    
     else if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */
     {
     	xode cur;	
         for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur))
             if(xode_get_type(cur) == XODE_TYPE_CDATA)
                 return cur->data_sz;
     }else{
         return node->data_sz;
     }
9b8f5200
     return (int)(long)NULL;
31ccf6a2
 }
 
 int xode_get_type(xode node)
 {
     if (node != NULL)
     {
         return node->type;
     }
9b8f5200
     return (int)(long)NULL;
31ccf6a2
 }
 
 int xode_has_children(xode node)
 {
     if ((node != NULL) && (node->firstchild != NULL))
         return 1;
     return 0;
 }
 
 int xode_has_attribs(xode node)
 {
     if ((node != NULL) && (node->firstattrib != NULL))
         return 1;
     return 0;
 }
 
 xode_pool xode_get_pool(xode node)
 {
     if (node != NULL)
         return node->p;
     return (xode_pool)NULL;
 }
 
 void xode_hide(xode child)
 {
     xode parent;
 
     if(child == NULL || child->parent == NULL)
         return;
 
     parent = child->parent;
 
     /* first fix up at the child level */
     _xode_hidesibling(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 xode_hide_attrib(xode parent, const char *name)
 {
     xode attrib;
 
     if(parent == NULL || parent->firstattrib == NULL || name == NULL)
         return;
 
     attrib = _xode_search(parent->firstattrib, name, XODE_TYPE_ATTRIB);
     if(attrib == NULL)
         return;
 
     /* first fix up at the child level */
     _xode_hidesibling(attrib);
 
     /* next fix up at the parent level */
     if(parent->firstattrib == attrib)
         parent->firstattrib = attrib->next;
     if(parent->lastattrib == attrib)
         parent->lastattrib = attrib->prev;
 }
 
 
 
 /*
  *  xode2str -- convert given xode tree into a string
  *
  *  parameters
  *      node -- pointer to the xode structure
  *
  *  results
  *      a pointer to the created string
  *      or NULL if it was unsuccessful
  */
 char *xode_to_str(xode node)
 {
      return xode_spool_tostr(_xode_tospool(node));
 }
 
 
 /* loop through both a and b comparing everything, attribs, cdata, children, etc */
 int xode_cmp(xode a, xode b)
 {
     int ret = 0;
 
     while(1)
     {
         if(a == NULL && b == NULL)
             return 0;
 
         if(a == NULL || b == NULL)
             return -1;
 
         if(xode_get_type(a) != xode_get_type(b))
             return -1;
 
         switch(xode_get_type(a))
         {
         case XODE_TYPE_ATTRIB:
             ret = _xode_strcmp(xode_get_name(a), xode_get_name(b));
             if(ret != 0)
                 return -1;
             ret = _xode_strcmp(xode_get_data(a), xode_get_data(b));
             if(ret != 0)
                 return -1;
             break;
         case XODE_TYPE_TAG:
             ret = _xode_strcmp(xode_get_name(a), xode_get_name(b));
             if(ret != 0)
                 return -1;
             ret = xode_cmp(xode_get_firstattrib(a), xode_get_firstattrib(b));
             if(ret != 0)
                 return -1;
             ret = xode_cmp(xode_get_firstchild(a), xode_get_firstchild(b));
             if(ret != 0)
                 return -1;
             break;
         case XODE_TYPE_CDATA:
             ret = _xode_strcmp(xode_get_data(a), xode_get_data(b));
             if(ret != 0)
                 return -1;
         }
         a = xode_get_nextsibling(a);
         b = xode_get_nextsibling(b);
     }
 }
 
 
 xode xode_insert_tagnode(xode parent, xode node)
 {
     xode child;
 
     child = xode_insert_tag(parent, xode_get_name(node));
     if (xode_has_attribs(node))
         xode_insert_node(child, xode_get_firstattrib(node));
     if (xode_has_children(node))
         xode_insert_node(child, xode_get_firstchild(node));
 
     return child;
 }
 
 /* places copy of node and node's siblings in parent */
 void xode_insert_node(xode parent, xode node)
 {
     if(node == NULL || parent == NULL)
         return;
 
     while(node != NULL)
     {
         switch(xode_get_type(node))
         {
         case XODE_TYPE_ATTRIB:
             xode_put_attrib(parent, xode_get_name(node), xode_get_data(node));
             break;
         case XODE_TYPE_TAG:
             xode_insert_tagnode(parent, node);
             break;
         case XODE_TYPE_CDATA:
             xode_insert_cdata(parent, xode_get_data(node), xode_get_datasz(node));
         }
         node = xode_get_nextsibling(node);
     }
 }
 
 
 /* produce full duplicate of x with a new xode_pool, x must be a tag! */
 xode xode_dup(xode x)
 {
     xode x2;
 
     if(x == NULL)
         return NULL;
 
     x2 = xode_new(xode_get_name(x));
 
     if (xode_has_attribs(x))
         xode_insert_node(x2, xode_get_firstattrib(x));
     if (xode_has_children(x))
         xode_insert_node(x2, xode_get_firstchild(x));
 
     return x2;
 }
 
 xode xode_dup_frompool(xode_pool p, xode x)
 {
     xode x2;
 
     if(x == NULL)
         return NULL;
 
     x2 = xode_new_frompool(p, xode_get_name(x));
 
     if (xode_has_attribs(x))
         xode_insert_node(x2, xode_get_firstattrib(x));
     if (xode_has_children(x))
         xode_insert_node(x2, xode_get_firstchild(x));
 
     return x2;
 }
 
 xode xode_wrap(xode x,const char *wrapper)
 {
     xode wrap;
     if(x==NULL||wrapper==NULL) return NULL;
     wrap=xode_new_frompool(xode_get_pool(x),wrapper);
     if(wrap==NULL) return NULL;
     wrap->firstchild=x;
     wrap->lastchild=x;
     x->parent=wrap;
     return wrap;
 }
 
 void xode_free(xode node)
 {
     if(node == NULL)
         return;
 
     xode_pool_free(node->p);
 }
 
 
 void
 _xode_to_prettystr( xode_spool s, xode x, int deep )
 {
 	int i;
 	xode y;
 
 	if(xode_get_type(x) != XODE_TYPE_TAG) return;
 	
 	for(i=0; i<deep; i++) xode_spool_add(s, "\t");	
 
 	xode_spooler( s , "<" , xode_get_name(x) ,  s );
 
 	y = xode_get_firstattrib(x);
 	while( y )
 	{
 		xode_spooler( s , " " , xode_get_name(y) , "='", xode_get_data(y) , "'" , s );
 
 		y = xode_get_nextsibling( y );
 	}
 	xode_spool_add(s,">");
 	xode_spool_add(s,"\n");
 		
 	if( xode_get_data(x))
 	{
 		for(i=0; i<=deep; i++) xode_spool_add(s, "\t");	
 		xode_spool_add( s , xode_get_data(x)); 
 	}
 			
 	y = xode_get_firstchild(x);
 	while( y )
 	{
 		_xode_to_prettystr(s , y, deep+1);
 		y = xode_get_nextsibling(y);
 		xode_spool_add(s,"\n");
 	}
 		
 	for(i=0; i<deep; i++) xode_spool_add(s, "\t");	
 	xode_spooler( s , "</" , xode_get_name(x) , ">" , s );
 
 	return;
 }
 
 char * 
 xode_to_prettystr( xode x )
 {
 	xode_spool s;
 
 	if( !x) return NULL;
 	
 	s = xode_spool_newfrompool( xode_get_pool(x));
 
 	_xode_to_prettystr( s , x, 0 );
 
 	return xode_spool_tostr(s);
 }