/* $Id$ * * This is free software, you may use it and distribute it under the same terms as * Perl itself. * * Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas */ #include "dom.h" #include "perl-libxml-mm.h" /* #define warn(string) fprintf(stderr, string) */ #ifdef XS_WARNINGS #define xs_warn(string) warn("%s",string) #else #define xs_warn(string) #endif void domClearPSVIInList(xmlNodePtr list); void domClearPSVI(xmlNodePtr tree) { xmlAttrPtr prop; if (tree == NULL) return; if (tree->type == XML_ELEMENT_NODE) { tree->psvi = NULL; prop = tree->properties; while (prop != NULL) { if (tree->type == XML_ATTRIBUTE_NODE) ((xmlAttrPtr) prop)->psvi = NULL; domClearPSVIInList(prop->children); prop = prop->next; } } else if (tree->type == XML_DOCUMENT_NODE) { ((xmlDocPtr) tree)->psvi = NULL; } if (tree->children != NULL) domClearPSVIInList(tree->children); } void domClearPSVIInList(xmlNodePtr list) { xmlNodePtr cur; if (list == NULL) return; cur = list; while (cur != NULL) { domClearPSVI(cur); cur = cur->next; } } /** * Name: domReconcileNs * Synopsis: void domReconcileNs( xmlNodePtr tree ); * @tree: the tree to reconcile * * Reconciles namespacing on a tree by removing declarations * of element and attribute namespaces that are already * declared in the scope of the corresponding node. **/ void domAddNsDef(xmlNodePtr tree, xmlNsPtr ns) { xmlNsPtr i = tree->nsDef; while(i != NULL && i != ns) i = i->next; if( i == NULL ) { ns->next = tree->nsDef; tree->nsDef = ns; } } char domRemoveNsDef(xmlNodePtr tree, xmlNsPtr ns) { xmlNsPtr i = tree->nsDef; if( ns == tree->nsDef ) { tree->nsDef = tree->nsDef->next; ns->next = NULL; return(1); } while( i != NULL ) { if( i->next == ns ) { i->next = ns->next; ns->next = NULL; return(1); } i = i->next; } return(0); } /* ns->next must be NULL, or bad things could happen */ xmlNsPtr _domAddNsChain(xmlNsPtr c, xmlNsPtr ns) { if( c == NULL ) return(ns); else { xmlNsPtr i = c; while(i != NULL && i != ns) i = i->next; if(i == NULL) { ns->next = c; return(ns); } } return(c); } /* We need to be smarter with attributes, because the declaration is on the parent element */ void _domReconcileNsAttr(xmlAttrPtr attr, xmlNsPtr * unused) { xmlNodePtr tree = attr->parent; if (tree == NULL) return; if( attr->ns != NULL ) { xmlNsPtr ns; if ((attr->ns->prefix != NULL) && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) { /* prefix 'xml' has no visible declaration */ ns = xmlSearchNsByHref(tree->doc, tree, XML_XML_NAMESPACE); attr->ns = ns; return; } else { ns = xmlSearchNs( tree->doc, tree->parent, attr->ns->prefix ); } if( ns != NULL && ns->href != NULL && attr->ns->href != NULL && xmlStrcmp(ns->href,attr->ns->href) == 0 ) { /* Remove the declaration from the element */ if( domRemoveNsDef(tree, attr->ns) ) /* Queue up this namespace for freeing */ *unused = _domAddNsChain(*unused, attr->ns); /* Replace the namespace with the one found */ attr->ns = ns; } else { /* If the declaration is here, we don't need to do anything */ if( domRemoveNsDef(tree, attr->ns) ) domAddNsDef(tree, attr->ns); else { /* Replace/Add the namespace declaration on the element */ attr->ns = xmlCopyNamespace(attr->ns); if (attr->ns) { domAddNsDef(tree, attr->ns); } } } } } void _domReconcileNs(xmlNodePtr tree, xmlNsPtr * unused) { if( tree->ns != NULL && ((tree->type == XML_ELEMENT_NODE) || (tree->type == XML_ATTRIBUTE_NODE))) { xmlNsPtr ns = xmlSearchNs( tree->doc, tree->parent, tree->ns->prefix ); if( ns != NULL && ns->href != NULL && tree->ns->href != NULL && xmlStrcmp(ns->href,tree->ns->href) == 0 ) { /* Remove the declaration (if present) */ if( domRemoveNsDef(tree, tree->ns) ) /* Queue the namespace for freeing */ *unused = _domAddNsChain(*unused, tree->ns); /* Replace the namespace with the one found */ tree->ns = ns; } else { /* If the declaration is here, we don't need to do anything */ if( domRemoveNsDef(tree, tree->ns) ) { domAddNsDef(tree, tree->ns); } else { /* Restart the namespace at this point */ tree->ns = xmlCopyNamespace(tree->ns); domAddNsDef(tree, tree->ns); } } } /* Fix attribute namespacing */ if( tree->type == XML_ELEMENT_NODE ) { xmlElementPtr ele = (xmlElementPtr) tree; /* attributes is set to xmlAttributePtr, but is an xmlAttrPtr??? */ xmlAttrPtr attr = (xmlAttrPtr) ele->attributes; while( attr != NULL ) { _domReconcileNsAttr(attr, unused); attr = attr->next; } } { /* Recurse through all child nodes */ xmlNodePtr child = tree->children; while( child != NULL ) { _domReconcileNs(child, unused); child = child->next; } } } void domReconcileNs(xmlNodePtr tree) { xmlNsPtr unused = NULL; _domReconcileNs(tree, &unused); if( unused != NULL ) xmlFreeNsList(unused); } /** * NAME domParseChar * TYPE function * SYNOPSIS * int utf8char = domParseChar( curchar, &len ); * * The current char value, if using UTF-8 this may actually span * multiple bytes in the given string. This function parses an utf8 * character from a string into a UTF8 character (an integer). It uses * a slightly modified version of libxml2's character parser. libxml2 * itself does not provide any function to parse characters dircetly * from a string and test if they are valid utf8 characters. * * XML::LibXML uses this function rather than perls native UTF8 * support for two reasons: * 1) perls UTF8 handling functions often lead to encoding errors, * which partly comes, that they are badly documented. * 2) not all perl versions XML::LibXML intends to run with have native * UTF8 support. * * domParseChar() allows to use the very same code with all versions * of perl :) * * Returns the current char value and its length * * NOTE: If the character passed to this function is not a UTF * character, the return value will be 0 and the length of the * character is -1! */ int domParseChar( xmlChar *cur, int *len ) { unsigned char c; unsigned int val; /* * We are supposed to handle UTF8, check it's valid * From rfc2044: encoding of the Unicode values on UTF-8: * * UCS-4 range (hex.) UTF-8 octet sequence (binary) * 0000 0000-0000 007F 0xxxxxxx * 0000 0080-0000 07FF 110xxxxx 10xxxxxx * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx * * Check for the 0x110000 limit too */ if ( cur == NULL || *cur == 0 ) { *len = 0; return(0); } c = *cur; if ( c & 0x80 ) { if ((c & 0xe0) == 0xe0) { if ((c & 0xf0) == 0xf0) { /* 4-byte code */ *len = 4; val = (cur[0] & 0x7) << 18; val |= (cur[1] & 0x3f) << 12; val |= (cur[2] & 0x3f) << 6; val |= cur[3] & 0x3f; } else { /* 3-byte code */ *len = 3; val = (cur[0] & 0xf) << 12; val |= (cur[1] & 0x3f) << 6; val |= cur[2] & 0x3f; } } else { /* 2-byte code */ *len = 2; val = (cur[0] & 0x1f) << 6; val |= cur[1] & 0x3f; } if ( !IS_CHAR(val) ) { *len = -1; return(0); } return(val); } else { /* 1-byte code */ *len = 1; return((int)c); } } /** * Name: domReadWellBalancedString * Synopsis: xmlNodePtr domReadWellBalancedString( xmlDocPtr doc, xmlChar *string ) * @doc: the document, the string should belong to * @string: the string to parse * * this function is pretty neat, since you can read in well balanced * strings and get a list of nodes, which can be added to any other node. * (sure - this should return a doucment_fragment, but still it doesn't) * * the code is pretty heavy i think, but deep in my heard i believe it's * worth it :) (e.g. if you like to read a chunk of well-balanced code * from a databasefield) * * in 99% the cases i believe it is faster than to create the dom by hand, * and skip the parsing job which has to be done here. * * the repair flag will not be recognized with the current libxml2 **/ xmlNodePtr domReadWellBalancedString( xmlDocPtr doc, xmlChar* block, int repair ) { int retCode = -1; xmlNodePtr nodes = NULL; if ( block ) { /* read and encode the chunk */ retCode = xmlParseBalancedChunkMemory( doc, NULL, NULL, 0, block, &nodes ); /* retCode = xmlParseBalancedChunkMemoryRecover( doc, */ /* NULL, */ /* NULL, */ /* 0, */ /* block, */ /* &nodes, */ /* repair ); */ /* error handling */ if ( retCode != 0 && repair == 0 ) { /* if the code was not well balanced, we will not return * a bad node list, but we have to free the nodes */ xmlFreeNodeList( nodes ); nodes = NULL; } else { xmlSetListDoc(nodes,doc); } } return nodes; } /** * internal helper: insert node to nodelist * synopsis: xmlNodePtr insert_node_to_nodelist( leader, insertnode, followup ); * while leader and followup are already list nodes. both may be NULL * if leader is null the parents children will be reset * if followup is null the parent last will be reset. * leader and followup has to be followups in the nodelist!!! * the function returns the node inserted. if a fragment was inserted, * the first node of the list will returned * * i ran into a misconception here. there should be a normalization function * for the DOM, so sequences of text nodes can get replaced by a single * text node. as i see DOM Level 1 does not allow text node sequences, while * Level 2 and 3 do. **/ int domAddNodeToList(xmlNodePtr cur, xmlNodePtr leader, xmlNodePtr followup) { xmlNodePtr c1 = NULL, c2 = NULL, p = NULL; if ( cur ) { c1 = c2 = cur; if( leader ) { p = leader->parent; } else if( followup ) { p = followup->parent; } else { return 0; /* can't insert */ } if ( cur->type == XML_DOCUMENT_FRAG_NODE ) { c1 = cur->children; while ( c1 ){ c1->parent = p; c1 = c1->next; } c1 = cur->children; c2 = cur->last; cur->last = cur->children = NULL; } else { cur->parent = p; } if (c1 && c2 && c1!=leader) { if ( leader ) { leader->next = c1; c1->prev = leader; } else if ( p ) { p->children = c1; } if ( followup ) { followup->prev = c2; c2->next = followup; } else if ( p ) { p->last = c2; } } return 1; } return 0; } /** * domIsParent tests, if testnode is parent of the reference * node. this test is very important to avoid circular constructs in * trees. if the ref is a parent of the cur node the * function returns 1 (TRUE), otherwise 0 (FALSE). **/ int domIsParent( xmlNodePtr cur, xmlNodePtr refNode ) { xmlNodePtr helper = NULL; if ( cur == NULL || refNode == NULL) return 0; if (refNode==cur) return 1; if ( cur->doc != refNode->doc || refNode->children == NULL || cur->parent == (xmlNodePtr)cur->doc || cur->parent == NULL ) { return 0; } if( refNode->type == XML_DOCUMENT_NODE ) { return 1; } helper= cur; while ( helper && (xmlDocPtr) helper != cur->doc ) { if( helper == refNode ) { return 1; } helper = helper->parent; } return 0; } int domTestHierarchy(xmlNodePtr cur, xmlNodePtr refNode) { if ( !refNode || !cur ) { return 0; } if (cur->type == XML_ATTRIBUTE_NODE) { switch ( refNode->type ){ case XML_TEXT_NODE: case XML_ENTITY_REF_NODE: return 1; break; default: return 0; break; } } switch ( refNode->type ){ case XML_ATTRIBUTE_NODE: case XML_DOCUMENT_NODE: return 0; break; default: break; } if ( domIsParent( cur, refNode ) ) { return 0; } return 1; } int domTestDocument(xmlNodePtr cur, xmlNodePtr refNode) { if ( cur->type == XML_DOCUMENT_NODE ) { switch ( refNode->type ) { case XML_ATTRIBUTE_NODE: case XML_ELEMENT_NODE: case XML_ENTITY_NODE: case XML_ENTITY_REF_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_NAMESPACE_DECL: return 0; break; default: break; } } return 1; } void domUnlinkNode( xmlNodePtr node ) { if ( node == NULL || ( node->prev == NULL && node->next == NULL && node->parent == NULL ) ) { return; } if (node->type == XML_DTD_NODE) { /* This clears the doc->intSubset pointer. */ xmlUnlinkNode(node); return; } if ( node->prev != NULL ) { node->prev->next = node->next; } if ( node->next != NULL ) { node->next->prev = node->prev; } if ( node->parent != NULL ) { if ( node == node->parent->last ) { node->parent->last = node->prev; } if ( node == node->parent->children ) { node->parent->children = node->next; } } node->prev = NULL; node->next = NULL; node->parent = NULL; } xmlNodePtr domImportNode( xmlDocPtr doc, xmlNodePtr node, int move, int reconcileNS ) { xmlNodePtr return_node = node; if ( move ) { return_node = node; domUnlinkNode( node ); } else { if ( node->type == XML_DTD_NODE ) { return_node = (xmlNodePtr) xmlCopyDtd((xmlDtdPtr) node); } else { return_node = xmlDocCopyNode( node, doc, 1 ); } } /* tell all children about the new boss */ if ( node && node->doc != doc ) { /* if the source document contained psvi, mark the current document as psvi tainted */ if (PmmIsPSVITainted(node->doc)) PmmInvalidatePSVI(doc); xmlSetTreeDoc(return_node, doc); } if ( reconcileNS && doc && return_node && return_node->type != XML_ENTITY_REF_NODE ) { domReconcileNs(return_node); } return return_node; } /** * Name: domName * Synopsis: string = domName( node ); * * domName returns the full name for the current node. * If the node belongs to a namespace it returns the prefix and * the local name. otherwise only the local name is returned. **/ xmlChar* domName(xmlNodePtr node) { const xmlChar *prefix = NULL; const xmlChar *name = NULL; xmlChar *qname = NULL; if ( node == NULL ) { return NULL; } switch ( node->type ) { case XML_XINCLUDE_START : case XML_XINCLUDE_END : case XML_ENTITY_REF_NODE : case XML_ENTITY_NODE : case XML_DTD_NODE : case XML_ENTITY_DECL : case XML_DOCUMENT_TYPE_NODE : case XML_PI_NODE : case XML_NOTATION_NODE : case XML_NAMESPACE_DECL : name = node->name; break; case XML_COMMENT_NODE : name = (const xmlChar *) "#comment"; break; case XML_CDATA_SECTION_NODE : name = (const xmlChar *) "#cdata-section"; break; case XML_TEXT_NODE : name = (const xmlChar *) "#text"; break; case XML_DOCUMENT_NODE : case XML_HTML_DOCUMENT_NODE : case XML_DOCB_DOCUMENT_NODE : name = (const xmlChar *) "#document"; break; case XML_DOCUMENT_FRAG_NODE : name = (const xmlChar *) "#document-fragment"; break; case XML_ELEMENT_NODE : case XML_ATTRIBUTE_NODE : if ( node->ns != NULL ) { prefix = node->ns->prefix; } name = node->name; break; case XML_ELEMENT_DECL : prefix = ((xmlElementPtr) node)->prefix; name = node->name; break; case XML_ATTRIBUTE_DECL : prefix = ((xmlAttributePtr) node)->prefix; name = node->name; break; } if ( prefix != NULL ) { qname = xmlStrdup( prefix ); qname = xmlStrcat( qname , (const xmlChar *) ":" ); qname = xmlStrcat( qname , name ); } else { qname = xmlStrdup( name ); } return qname; } /** * Name: domAppendChild * Synopsis: xmlNodePtr domAppendChild( xmlNodePtr par, xmlNodePtr newCld ); * @par: the node to append to * @newCld: the node to append * * Returns newCld on success otherwise NULL * The function will unbind newCld first if nesseccary. As well the * function will fail, if par or newCld is a Attribute Node OR if newCld * is a parent of par. * * If newCld belongs to a different DOM the node will be imported * implicit before it gets appended. **/ xmlNodePtr domAppendChild( xmlNodePtr self, xmlNodePtr newChild ){ xmlNodePtr fragment = NULL; if ( self == NULL ) { return newChild; } if ( !(domTestHierarchy(self, newChild) && domTestDocument(self, newChild))){ croak("appendChild: HIERARCHY_REQUEST_ERR\n"); return NULL; } if ( newChild->doc == self->doc ){ domUnlinkNode( newChild ); } else { xs_warn("WRONG_DOCUMENT_ERR - non conform implementation\n"); /* xmlGenericError(xmlGenericErrorContext,"WRONG_DOCUMENT_ERR\n"); */ newChild = domImportNode( self->doc, newChild, 1, 0 ); } if ( self->children != NULL ) { if (newChild->type == XML_DOCUMENT_FRAG_NODE ) fragment = newChild->children; domAddNodeToList( newChild, self->last, NULL ); } else if (newChild->type == XML_DOCUMENT_FRAG_NODE ) { xmlNodePtr c1 = NULL; self->children = newChild->children; fragment = newChild->children; c1 = fragment; while ( c1 ){ c1->parent = self; c1 = c1->next; } self->last = newChild->last; newChild->last = newChild->children = NULL; } else { self->children = newChild; self->last = newChild; newChild->parent= self; } if ( fragment ) { /* we must reconcile all nodes in the fragment */ newChild = fragment; /* return the first node in the fragment */ while ( fragment ) { domReconcileNs(fragment); fragment = fragment->next; } } else if ( newChild->type != XML_ENTITY_REF_NODE ) { domReconcileNs(newChild); } return newChild; } xmlNodePtr domRemoveChild( xmlNodePtr self, xmlNodePtr old ) { if ( self == NULL || old == NULL ) { return NULL; } if ( old->type == XML_ATTRIBUTE_NODE || old->type == XML_NAMESPACE_DECL ) { return NULL; } if ( self != old->parent ) { /* not a child! */ return NULL; } domUnlinkNode( old ); if ( old->type == XML_ELEMENT_NODE ) { domReconcileNs( old ); } return old ; } xmlNodePtr domReplaceChild( xmlNodePtr self, xmlNodePtr new, xmlNodePtr old ) { xmlNodePtr fragment = NULL; xmlNodePtr fragment_next = NULL; if ( self== NULL ) return NULL; if ( new == old ) return NULL; if ( new == NULL ) { /* level2 sais nothing about this case :( */ return domRemoveChild( self, old ); } if ( old == NULL ) { domAppendChild( self, new ); return old; } if ( !(domTestHierarchy(self, new) && domTestDocument(self, new))){ croak("replaceChild: HIERARCHY_REQUEST_ERR\n"); return NULL; } if ( new->doc == self->doc ) { domUnlinkNode( new ); } else { /* WRONG_DOCUMENT_ERR - non conform implementation */ new = domImportNode( self->doc, new, 1, 1 ); } if( old == self->children && old == self->last ) { domRemoveChild( self, old ); domAppendChild( self, new ); } else if ( new->type == XML_DOCUMENT_FRAG_NODE && new->children == NULL ) { /* want to replace with an empty fragment, then remove ... */ fragment = new->children; fragment_next = old->next; domRemoveChild( self, old ); } else { domAddNodeToList(new, old->prev, old->next ); old->parent = old->next = old->prev = NULL; } if ( fragment ) { while ( fragment && fragment != fragment_next ) { domReconcileNs(fragment); fragment = fragment->next; } } else if ( new->type != XML_ENTITY_REF_NODE ) { domReconcileNs(new); } return old; } xmlNodePtr domInsertBefore( xmlNodePtr self, xmlNodePtr newChild, xmlNodePtr refChild ){ xmlNodePtr fragment = NULL; if ( refChild == newChild ) { return newChild; } if ( self == NULL || newChild == NULL ) { return NULL; } if ( refChild != NULL ) { if ( refChild->parent != self || ( newChild->type == XML_DOCUMENT_FRAG_NODE && newChild->children == NULL ) ) { /* NOT_FOUND_ERR */ xmlGenericError(xmlGenericErrorContext,"NOT_FOUND_ERR\n"); return NULL; } } if ( self->children == NULL ) { return domAppendChild( self, newChild ); } if ( !(domTestHierarchy( self, newChild ) && domTestDocument( self, newChild ))) { croak("insertBefore/insertAfter: HIERARCHY_REQUEST_ERR\n"); return NULL; } if ( self->doc == newChild->doc ){ domUnlinkNode( newChild ); } else { newChild = domImportNode( self->doc, newChild, 1, 0 ); } if ( newChild->type == XML_DOCUMENT_FRAG_NODE ) { fragment = newChild->children; } if ( refChild == NULL ) { domAddNodeToList(newChild, self->last, NULL); } else { domAddNodeToList(newChild, refChild->prev, refChild); } if ( fragment ) { newChild = fragment; /* return the first node in the fragment */ while ( fragment && fragment != refChild ) { domReconcileNs(fragment); fragment = fragment->next; } } else if ( newChild->type != XML_ENTITY_REF_NODE ) { domReconcileNs(newChild); } return newChild; } /* * this function does not exist in the spec although it's useful */ xmlNodePtr domInsertAfter( xmlNodePtr self, xmlNodePtr newChild, xmlNodePtr refChild ){ if ( refChild == NULL ) { return domInsertBefore( self, newChild, NULL ); } return domInsertBefore( self, newChild, refChild->next ); } xmlNodePtr domReplaceNode( xmlNodePtr oldNode, xmlNodePtr newNode ) { xmlNodePtr prev = NULL, next = NULL, par = NULL, fragment = NULL; if ( oldNode == NULL || newNode == NULL ) { /* NOT_FOUND_ERROR */ return NULL; } if ( oldNode->type == XML_ATTRIBUTE_NODE || newNode->type == XML_ATTRIBUTE_NODE || newNode->type == XML_DOCUMENT_NODE || domIsParent( newNode, oldNode ) ) { /* HIERARCHY_REQUEST_ERR * wrong node type * new node is parent of itself */ croak("replaceNode: HIERARCHY_REQUEST_ERR\n"); return NULL; } par = oldNode->parent; prev = oldNode->prev; next = oldNode->next; if ( oldNode->_private == NULL ) { xmlUnlinkNode( oldNode ); } else { domUnlinkNode( oldNode ); } if ( newNode->type == XML_DOCUMENT_FRAG_NODE ) { fragment = newNode->children; } if( prev == NULL && next == NULL ) { /* oldNode was the only child */ domAppendChild( par , newNode ); } else { domAddNodeToList( newNode, prev, next ); } if ( fragment ) { while ( fragment && fragment != next ) { domReconcileNs(fragment); fragment = fragment->next; } } else if ( newNode->type != XML_ENTITY_REF_NODE ) { domReconcileNs(newNode); } return oldNode; } xmlChar* domGetNodeValue( xmlNodePtr n ) { xmlChar * retval = NULL; if( n != NULL ) { switch ( n->type ) { case XML_ATTRIBUTE_NODE: case XML_ENTITY_DECL: case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_ENTITY_REF_NODE: break; default: return retval; break; } if ( n->type != XML_ENTITY_DECL ) { retval = xmlXPathCastNodeToString(n); } else { if ( n->content != NULL ) { xs_warn(" dublicate content\n" ); retval = xmlStrdup(n->content); } else if ( n->children != NULL ) { xmlNodePtr cnode = n->children; xs_warn(" use child content\n" ); /* ok then toString in this case ... */ while (cnode) { xmlBufferPtr buffer = xmlBufferCreate(); /* buffer = xmlBufferCreate(); */ xmlNodeDump( buffer, n->doc, cnode, 0, 0 ); if ( buffer->content != NULL ) { xs_warn( "add item" ); if ( retval != NULL ) { retval = xmlStrcat( retval, buffer->content ); } else { retval = xmlStrdup( buffer->content ); } } xmlBufferFree( buffer ); cnode = cnode->next; } } } } return retval; } void domSetNodeValue( xmlNodePtr n , xmlChar* val ){ if ( n == NULL ) return; if ( val == NULL ){ val = (xmlChar *) ""; } if( n->type == XML_ATTRIBUTE_NODE ){ /* can't use xmlNodeSetContent - for Attrs it parses entities */ if ( n->children != NULL ) { n->last = NULL; xmlFreeNodeList( n->children ); } n->children = xmlNewText( val ); n->children->parent = n; n->children->doc = n->doc; n->last = n->children; } else { xmlNodeSetContent( n, val ); } } xmlNodeSetPtr domGetElementsByTagName( xmlNodePtr n, xmlChar* name ){ xmlNodeSetPtr rv = NULL; xmlNodePtr cld = NULL; if ( n != NULL && name != NULL ) { cld = n->children; while ( cld != NULL ) { if ( xmlStrcmp( name, cld->name ) == 0 ){ if ( rv == NULL ) { rv = xmlXPathNodeSetCreate( cld ) ; } else { xmlXPathNodeSetAdd( rv, cld ); } } cld = cld->next; } } return rv; } xmlNodeSetPtr domGetElementsByTagNameNS( xmlNodePtr n, xmlChar* nsURI, xmlChar* name ){ xmlNodeSetPtr rv = NULL; if ( nsURI == NULL ) { return domGetElementsByTagName( n, name ); } if ( n != NULL && name != NULL ) { xmlNodePtr cld = n->children; while ( cld != NULL ) { if ( xmlStrcmp( name, cld->name ) == 0 && cld->ns != NULL && xmlStrcmp( nsURI, cld->ns->href ) == 0 ){ if ( rv == NULL ) { rv = xmlXPathNodeSetCreate( cld ) ; } else { xmlXPathNodeSetAdd( rv, cld ); } } cld = cld->next; } } return rv; } xmlNsPtr domNewNs ( xmlNodePtr elem , xmlChar *prefix, xmlChar *href ) { xmlNsPtr ns = NULL; if (elem != NULL) { ns = xmlSearchNs( elem->doc, elem, prefix ); } /* prefix is not in use */ if (ns == NULL) { ns = xmlNewNs( elem , href , prefix ); } else { /* prefix is in use; if it has same URI, let it go, otherwise it's an error */ if (!xmlStrEqual(href, ns->href)) { ns = NULL; } } return ns; } xmlAttrPtr domGetAttrNode(xmlNodePtr node, const xmlChar *qname) { xmlChar * prefix = NULL; xmlChar * localname = NULL; xmlAttrPtr ret = NULL; xmlNsPtr ns = NULL; if ( qname == NULL || node == NULL ) return NULL; /* first try qname without namespace */ ret = xmlHasNsProp(node, qname, NULL); if ( ret == NULL ) { localname = xmlSplitQName2(qname, &prefix); if ( localname != NULL ) { ns = xmlSearchNs( node->doc, node, prefix ); if ( ns != NULL ) { /* then try localname with the namespace bound to prefix */ ret = xmlHasNsProp( node, localname, ns->href ); } if ( prefix != NULL) { xmlFree( prefix ); } xmlFree( localname ); } } if (ret && ret->type != XML_ATTRIBUTE_NODE) { return NULL; /* we don't want fixed attribute decls */ } else { return ret; } } xmlAttrPtr domSetAttributeNode( xmlNodePtr node, xmlAttrPtr attr ) { if ( node == NULL || attr == NULL ) { return attr; } if ( attr != NULL && attr->type != XML_ATTRIBUTE_NODE ) return NULL; if ( node == attr->parent ) { return attr; /* attribute is already part of the node */ } if ( attr->doc != node->doc ){ attr = (xmlAttrPtr) domImportNode( node->doc, (xmlNodePtr) attr, 1, 1 ); } else { xmlUnlinkNode( (xmlNodePtr) attr ); } /* stolen from libxml2 */ if ( attr != NULL ) { if (node->properties == NULL) { node->properties = attr; } else { xmlAttrPtr prev = node->properties; while (prev->next != NULL) prev = prev->next; prev->next = attr; attr->prev = prev; } } return attr; } void domAttrSerializeContent(xmlBufferPtr buffer, xmlAttrPtr attr) { xmlNodePtr children; children = attr->children; while (children != NULL) { switch (children->type) { case XML_TEXT_NODE: xmlAttrSerializeTxtContent(buffer, attr->doc, attr, children->content); break; case XML_ENTITY_REF_NODE: xmlBufferAdd(buffer, BAD_CAST "&", 1); xmlBufferAdd(buffer, children->name, xmlStrlen(children->name)); xmlBufferAdd(buffer, BAD_CAST ";", 1); break; default: /* should not happen unless we have a badly built tree */ break; } children = children->next; } } int domNodeNormalize( xmlNodePtr node ); int domNodeNormalizeList( xmlNodePtr nodelist ) { while ( nodelist ){ if ( domNodeNormalize( nodelist ) == 0 ) return(0); nodelist = nodelist->next; } return(1); } int domNodeNormalize( xmlNodePtr node ) { xmlNodePtr next = NULL; if ( node == NULL ) return(0); switch ( node->type ) { case XML_TEXT_NODE: while ( node->next && node->next->type == XML_TEXT_NODE ) { next = node->next; xmlNodeAddContent(node, next->content); xmlUnlinkNode( next ); /** * keep only nodes that are referred by perl (or GDOME) */ if ( !next->_private ) xmlFreeNode( next ); } break; case XML_ELEMENT_NODE: domNodeNormalizeList( (xmlNodePtr) node->properties ); case XML_ATTRIBUTE_NODE: case XML_DOCUMENT_NODE: return( domNodeNormalizeList( node->children ) ); break; default: break; } return(1); } int domRemoveNsRefs(xmlNodePtr tree, xmlNsPtr ns) { xmlAttrPtr attr; xmlNodePtr node = tree; if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) return(0); while (node != NULL) { if (node->ns == ns) node->ns = NULL; /* remove namespace reference */ attr = node->properties; while (attr != NULL) { if (attr->ns == ns) attr->ns = NULL; /* remove namespace reference */ attr = attr->next; } /* * Browse the full subtree, deep first */ if (node->children != NULL && node->type != XML_ENTITY_REF_NODE) { /* deep first */ node = node->children; } else if ((node != tree) && (node->next != NULL)) { /* then siblings */ node = node->next; } else if (node != tree) { /* go up to parents->next if needed */ while (node != tree) { if (node->parent != NULL) node = node->parent; if ((node != tree) && (node->next != NULL)) { node = node->next; break; } if (node->parent == NULL) { node = NULL; break; } } /* exit condition */ if (node == tree) node = NULL; } else break; } return(1); }