1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-10-27 21:55:05 +03:00
libxml2/tree.c
Daniel Veillard d5c2f92df4 modified the existing APIs to handle XHTML1 serialization rules
* tree.c include/libxml/tree.h: modified the existing APIs
  to handle XHTML1 serialization rules automatically, also add
  xmlIsXHTML() to libxml2 API. Some tweaking to make sure
  libxslt serialization uses it when needed without changing
  the library API.
* test/xhtml1 result/noent/xhtml1 result/valid/xhtml1.xhtml
  result/xhtml1: added a new test specifically for xhtml1 output
  and updated the result of one XHTML1 test
Daniel
2002-11-21 14:10:52 +00:00

7825 lines
202 KiB
C

/*
* tree.c : implementation of access function for an XML tree.
*
* References:
* XHTML 1.0 W3C REC: http://www.w3.org/TR/2002/REC-xhtml1-20020801/
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*
*/
#define IN_LIBXML
#include "libxml.h"
#include <string.h> /* for memset() only ! */
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/uri.h>
#include <libxml/entities.h>
#include <libxml/valid.h>
#include <libxml/xmlerror.h>
#include <libxml/parserInternals.h>
#include <libxml/globals.h>
#ifdef LIBXML_HTML_ENABLED
#include <libxml/HTMLtree.h>
#endif
xmlNsPtr xmlNewReconciliedNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns);
/************************************************************************
* *
* A few static variables and macros *
* *
************************************************************************/
/* #undef xmlStringText */
const xmlChar xmlStringText[] = { 't', 'e', 'x', 't', 0 };
/* #undef xmlStringTextNoenc */
const xmlChar xmlStringTextNoenc[] =
{ 't', 'e', 'x', 't', 'n', 'o', 'e', 'n', 'c', 0 };
/* #undef xmlStringComment */
const xmlChar xmlStringComment[] = { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 };
static int xmlCompressMode = 0;
static int xmlCheckDTD = 1;
#define UPDATE_LAST_CHILD_AND_PARENT(n) if ((n) != NULL) { \
xmlNodePtr ulccur = (n)->children; \
if (ulccur == NULL) { \
(n)->last = NULL; \
} else { \
while (ulccur->next != NULL) { \
ulccur->parent = (n); \
ulccur = ulccur->next; \
} \
ulccur->parent = (n); \
(n)->last = ulccur; \
}}
/* #define DEBUG_BUFFER */
/* #define DEBUG_TREE */
/************************************************************************
* *
* Functions to move to entities.c once the *
* API freeze is smoothen and they can be made public. *
* *
************************************************************************/
#include <libxml/hash.h>
/**
* xmlGetEntityFromDtd:
* @dtd: A pointer to the DTD to search
* @name: The entity name
*
* Do an entity lookup in the DTD entity hash table and
* return the corresponding entity, if found.
*
* Returns A pointer to the entity structure or NULL if not found.
*/
static xmlEntityPtr
xmlGetEntityFromDtd(xmlDtdPtr dtd, const xmlChar *name) {
xmlEntitiesTablePtr table;
if((dtd != NULL) && (dtd->entities != NULL)) {
table = (xmlEntitiesTablePtr) dtd->entities;
return((xmlEntityPtr) xmlHashLookup(table, name));
/* return(xmlGetEntityFromTable(table, name)); */
}
return(NULL);
}
/**
* xmlGetParameterEntityFromDtd:
* @dtd: A pointer to the DTD to search
* @name: The entity name
*
* Do an entity lookup in the DTD pararmeter entity hash table and
* return the corresponding entity, if found.
*
* Returns A pointer to the entity structure or NULL if not found.
*/
static xmlEntityPtr
xmlGetParameterEntityFromDtd(xmlDtdPtr dtd, const xmlChar *name) {
xmlEntitiesTablePtr table;
if ((dtd != NULL) && (dtd->pentities != NULL)) {
table = (xmlEntitiesTablePtr) dtd->pentities;
return((xmlEntityPtr) xmlHashLookup(table, name));
/* return(xmlGetEntityFromTable(table, name)); */
}
return(NULL);
}
/************************************************************************
* *
* Allocation and deallocation of basic structures *
* *
************************************************************************/
/**
* xmlSetBufferAllocationScheme:
* @scheme: allocation method to use
*
* Set the buffer allocation method. Types are
* XML_BUFFER_ALLOC_EXACT - use exact sizes, keeps memory usage down
* XML_BUFFER_ALLOC_DOUBLEIT - double buffer when extra needed,
* improves performance
*/
void
xmlSetBufferAllocationScheme(xmlBufferAllocationScheme scheme) {
xmlBufferAllocScheme = scheme;
}
/**
* xmlGetBufferAllocationScheme:
*
* Types are
* XML_BUFFER_ALLOC_EXACT - use exact sizes, keeps memory usage down
* XML_BUFFER_ALLOC_DOUBLEIT - double buffer when extra needed,
* improves performance
*
* Returns the current allocation scheme
*/
xmlBufferAllocationScheme
xmlGetBufferAllocationScheme(void) {
return(xmlBufferAllocScheme);
}
/**
* xmlNewNs:
* @node: the element carrying the namespace
* @href: the URI associated
* @prefix: the prefix for the namespace
*
* Creation of a new Namespace. This function will refuse to create
* a namespace with a similar prefix than an existing one present on this
* node.
* We use href==NULL in the case of an element creation where the namespace
* was not defined.
* Returns a new namespace pointer or NULL
*/
xmlNsPtr
xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix) {
xmlNsPtr cur;
if ((node != NULL) && (node->type != XML_ELEMENT_NODE))
return(NULL);
if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml")))
return(NULL);
/*
* Allocate a new Namespace and fill the fields.
*/
cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNs : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNs));
cur->type = XML_LOCAL_NAMESPACE;
if (href != NULL)
cur->href = xmlStrdup(href);
if (prefix != NULL)
cur->prefix = xmlStrdup(prefix);
/*
* Add it at the end to preserve parsing order ...
* and checks for existing use of the prefix
*/
if (node != NULL) {
if (node->nsDef == NULL) {
node->nsDef = cur;
} else {
xmlNsPtr prev = node->nsDef;
if (((prev->prefix == NULL) && (cur->prefix == NULL)) ||
(xmlStrEqual(prev->prefix, cur->prefix))) {
xmlFreeNs(cur);
return(NULL);
}
while (prev->next != NULL) {
prev = prev->next;
if (((prev->prefix == NULL) && (cur->prefix == NULL)) ||
(xmlStrEqual(prev->prefix, cur->prefix))) {
xmlFreeNs(cur);
return(NULL);
}
}
prev->next = cur;
}
}
return(cur);
}
/**
* xmlSetNs:
* @node: a node in the document
* @ns: a namespace pointer
*
* Associate a namespace to a node, a posteriori.
*/
void
xmlSetNs(xmlNodePtr node, xmlNsPtr ns) {
if (node == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlSetNs: node == NULL\n");
#endif
return;
}
node->ns = ns;
}
/**
* xmlFreeNs:
* @cur: the namespace pointer
*
* Free up the structures associated to a namespace
*/
void
xmlFreeNs(xmlNsPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeNs : ns == NULL\n");
#endif
return;
}
if (cur->href != NULL) xmlFree((char *) cur->href);
if (cur->prefix != NULL) xmlFree((char *) cur->prefix);
xmlFree(cur);
}
/**
* xmlFreeNsList:
* @cur: the first namespace pointer
*
* Free up all the structures associated to the chained namespaces.
*/
void
xmlFreeNsList(xmlNsPtr cur) {
xmlNsPtr next;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeNsList : ns == NULL\n");
#endif
return;
}
while (cur != NULL) {
next = cur->next;
xmlFreeNs(cur);
cur = next;
}
}
/**
* xmlNewDtd:
* @doc: the document pointer
* @name: the DTD name
* @ExternalID: the external ID
* @SystemID: the system ID
*
* Creation of a new DTD for the external subset. To create an
* internal subset, use xmlCreateIntSubset().
*
* Returns a pointer to the new DTD structure
*/
xmlDtdPtr
xmlNewDtd(xmlDocPtr doc, const xmlChar *name,
const xmlChar *ExternalID, const xmlChar *SystemID) {
xmlDtdPtr cur;
if ((doc != NULL) && (doc->extSubset != NULL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewDtd(%s): document %s already have a DTD %s\n",
/* !!! */ (char *) name, doc->name,
/* !!! */ (char *)doc->extSubset->name);
#endif
return(NULL);
}
/*
* Allocate a new DTD and fill the fields.
*/
cur = (xmlDtdPtr) xmlMalloc(sizeof(xmlDtd));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewDtd : malloc failed\n");
return(NULL);
}
memset(cur, 0 , sizeof(xmlDtd));
cur->type = XML_DTD_NODE;
if (name != NULL)
cur->name = xmlStrdup(name);
if (ExternalID != NULL)
cur->ExternalID = xmlStrdup(ExternalID);
if (SystemID != NULL)
cur->SystemID = xmlStrdup(SystemID);
if (doc != NULL)
doc->extSubset = cur;
cur->doc = doc;
return(cur);
}
/**
* xmlGetIntSubset:
* @doc: the document pointer
*
* Get the internal subset of a document
* Returns a pointer to the DTD structure or NULL if not found
*/
xmlDtdPtr
xmlGetIntSubset(xmlDocPtr doc) {
xmlNodePtr cur;
if (doc == NULL)
return(NULL);
cur = doc->children;
while (cur != NULL) {
if (cur->type == XML_DTD_NODE)
return((xmlDtdPtr) cur);
cur = cur->next;
}
return((xmlDtdPtr) doc->intSubset);
}
/**
* xmlCreateIntSubset:
* @doc: the document pointer
* @name: the DTD name
* @ExternalID: the external (PUBLIC) ID
* @SystemID: the system ID
*
* Create the internal subset of a document
* Returns a pointer to the new DTD structure
*/
xmlDtdPtr
xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name,
const xmlChar *ExternalID, const xmlChar *SystemID) {
xmlDtdPtr cur;
if ((doc != NULL) && (xmlGetIntSubset(doc) != NULL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlCreateIntSubset(): document %s already have an internal subset\n",
doc->name);
#endif
return(NULL);
}
/*
* Allocate a new DTD and fill the fields.
*/
cur = (xmlDtdPtr) xmlMalloc(sizeof(xmlDtd));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlCreateIntSubset : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlDtd));
cur->type = XML_DTD_NODE;
if (name != NULL)
cur->name = xmlStrdup(name);
if (ExternalID != NULL)
cur->ExternalID = xmlStrdup(ExternalID);
if (SystemID != NULL)
cur->SystemID = xmlStrdup(SystemID);
if (doc != NULL) {
doc->intSubset = cur;
cur->parent = doc;
cur->doc = doc;
if (doc->children == NULL) {
doc->children = (xmlNodePtr) cur;
doc->last = (xmlNodePtr) cur;
} else {
if (doc->type == XML_HTML_DOCUMENT_NODE) {
xmlNodePtr prev;
prev = doc->children;
prev->prev = (xmlNodePtr) cur;
cur->next = prev;
doc->children = (xmlNodePtr) cur;
} else {
xmlNodePtr next;
next = doc->children;
while ((next != NULL) && (next->type != XML_ELEMENT_NODE))
next = next->next;
if (next == NULL) {
cur->prev = doc->last;
cur->prev->next = (xmlNodePtr) cur;
cur->next = NULL;
doc->last = (xmlNodePtr) cur;
} else {
cur->next = next;
cur->prev = next->prev;
if (cur->prev == NULL)
doc->children = (xmlNodePtr) cur;
else
cur->prev->next = (xmlNodePtr) cur;
next->prev = (xmlNodePtr) cur;
}
}
}
}
return(cur);
}
/**
* xmlFreeDtd:
* @cur: the DTD structure to free up
*
* Free a DTD structure.
*/
void
xmlFreeDtd(xmlDtdPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeDtd : DTD == NULL\n");
#endif
return;
}
if (cur->children != NULL) {
xmlNodePtr next, c = cur->children;
/*
* Cleanup all the DTD comments they are not in the DTD
* indexes.
*/
while (c != NULL) {
next = c->next;
if (c->type == XML_COMMENT_NODE) {
xmlUnlinkNode(c);
xmlFreeNode(c);
}
c = next;
}
}
if (cur->name != NULL) xmlFree((char *) cur->name);
if (cur->SystemID != NULL) xmlFree((char *) cur->SystemID);
if (cur->ExternalID != NULL) xmlFree((char *) cur->ExternalID);
/* TODO !!! */
if (cur->notations != NULL)
xmlFreeNotationTable((xmlNotationTablePtr) cur->notations);
if (cur->elements != NULL)
xmlFreeElementTable((xmlElementTablePtr) cur->elements);
if (cur->attributes != NULL)
xmlFreeAttributeTable((xmlAttributeTablePtr) cur->attributes);
if (cur->entities != NULL)
xmlFreeEntitiesTable((xmlEntitiesTablePtr) cur->entities);
if (cur->pentities != NULL)
xmlFreeEntitiesTable((xmlEntitiesTablePtr) cur->pentities);
xmlFree(cur);
}
/**
* xmlNewDoc:
* @version: xmlChar string giving the version of XML "1.0"
*
* Creates a new XML document
*
* Returns a new document
*/
xmlDocPtr
xmlNewDoc(const xmlChar *version) {
xmlDocPtr cur;
if (version == NULL)
version = (const xmlChar *) "1.0";
/*
* Allocate a new document and fill the fields.
*/
cur = (xmlDocPtr) xmlMalloc(sizeof(xmlDoc));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewDoc : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlDoc));
cur->type = XML_DOCUMENT_NODE;
cur->version = xmlStrdup(version);
cur->standalone = -1;
cur->compression = -1; /* not initialized */
cur->doc = cur;
cur->charset = XML_CHAR_ENCODING_UTF8;
return(cur);
}
/**
* xmlFreeDoc:
* @cur: pointer to the document
*
* Free up all the structures used by a document, tree included.
*/
void
xmlFreeDoc(xmlDocPtr cur) {
xmlDtdPtr extSubset, intSubset;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeDoc : document == NULL\n");
#endif
return;
}
/*
* Do this before freeing the children list to avoid ID lookups
*/
if (cur->ids != NULL) xmlFreeIDTable((xmlIDTablePtr) cur->ids);
cur->ids = NULL;
if (cur->refs != NULL) xmlFreeRefTable((xmlRefTablePtr) cur->refs);
cur->refs = NULL;
extSubset = cur->extSubset;
intSubset = cur->intSubset;
if (intSubset == extSubset)
extSubset = NULL;
if (extSubset != NULL) {
xmlUnlinkNode((xmlNodePtr) cur->extSubset);
cur->extSubset = NULL;
xmlFreeDtd(extSubset);
}
if (intSubset != NULL) {
xmlUnlinkNode((xmlNodePtr) cur->intSubset);
cur->intSubset = NULL;
xmlFreeDtd(intSubset);
}
if (cur->children != NULL) xmlFreeNodeList(cur->children);
if (cur->version != NULL) xmlFree((char *) cur->version);
if (cur->name != NULL) xmlFree((char *) cur->name);
if (cur->encoding != NULL) xmlFree((char *) cur->encoding);
if (cur->oldNs != NULL) xmlFreeNsList(cur->oldNs);
if (cur->URL != NULL) xmlFree((char *) cur->URL);
xmlFree(cur);
}
/**
* xmlStringLenGetNodeList:
* @doc: the document
* @value: the value of the text
* @len: the length of the string value
*
* Parse the value string and build the node list associated. Should
* produce a flat tree with only TEXTs and ENTITY_REFs.
* Returns a pointer to the first child
*/
xmlNodePtr
xmlStringLenGetNodeList(xmlDocPtr doc, const xmlChar *value, int len) {
xmlNodePtr ret = NULL, last = NULL;
xmlNodePtr node;
xmlChar *val;
const xmlChar *cur = value;
const xmlChar *q;
xmlEntityPtr ent;
if (value == NULL) return(NULL);
q = cur;
while ((*cur != 0) && (cur - value < len)) {
if (*cur == '&') {
/*
* Save the current text.
*/
if (cur != q) {
if ((last != NULL) && (last->type == XML_TEXT_NODE)) {
xmlNodeAddContentLen(last, q, cur - q);
} else {
node = xmlNewDocTextLen(doc, q, cur - q);
if (node == NULL) return(ret);
if (last == NULL)
last = ret = node;
else {
last->next = node;
node->prev = last;
last = node;
}
}
}
/*
* Read the entity string
*/
cur++;
q = cur;
while ((*cur != 0) && (cur - value < len) && (*cur != ';')) cur++;
if ((*cur == 0) || (cur - value >= len)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlStringLenGetNodeList: unterminated entity %30s\n", q);
#endif
return(ret);
}
if (cur != q) {
/*
* Predefined entities don't generate nodes
*/
val = xmlStrndup(q, cur - q);
ent = xmlGetDocEntity(doc, val);
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (last == NULL) {
node = xmlNewDocText(doc, ent->content);
last = ret = node;
} else
xmlNodeAddContent(last, ent->content);
} else {
/*
* Create a new REFERENCE_REF node
*/
node = xmlNewReference(doc, val);
if (node == NULL) {
if (val != NULL) xmlFree(val);
return(ret);
}
else if ((ent != NULL) && (ent->children == NULL)) {
xmlNodePtr tmp;
ent->children =
xmlStringGetNodeList(doc, (const xmlChar*)node->content);
tmp = ent->children;
while (tmp) {
tmp->parent = (xmlNodePtr)ent;
tmp = tmp->next;
}
}
if (last == NULL)
last = ret = node;
else {
last->next = node;
node->prev = last;
last = node;
}
}
xmlFree(val);
}
cur++;
q = cur;
} else
cur++;
}
if (cur != q) {
/*
* Handle the last piece of text.
*/
if ((last != NULL) && (last->type == XML_TEXT_NODE)) {
xmlNodeAddContentLen(last, q, cur - q);
} else {
node = xmlNewDocTextLen(doc, q, cur - q);
if (node == NULL) return(ret);
if (last == NULL)
last = ret = node;
else {
last->next = node;
node->prev = last;
last = node;
}
}
}
return(ret);
}
/**
* xmlStringGetNodeList:
* @doc: the document
* @value: the value of the attribute
*
* Parse the value string and build the node list associated. Should
* produce a flat tree with only TEXTs and ENTITY_REFs.
* Returns a pointer to the first child
*/
xmlNodePtr
xmlStringGetNodeList(xmlDocPtr doc, const xmlChar *value) {
xmlNodePtr ret = NULL, last = NULL;
xmlNodePtr node;
xmlChar *val;
const xmlChar *cur = value;
const xmlChar *q;
xmlEntityPtr ent;
if (value == NULL) return(NULL);
q = cur;
while (*cur != 0) {
if (cur[0] == '&') {
int charval = 0;
xmlChar tmp;
/*
* Save the current text.
*/
if (cur != q) {
if ((last != NULL) && (last->type == XML_TEXT_NODE)) {
xmlNodeAddContentLen(last, q, cur - q);
} else {
node = xmlNewDocTextLen(doc, q, cur - q);
if (node == NULL) return(ret);
if (last == NULL)
last = ret = node;
else {
last->next = node;
node->prev = last;
last = node;
}
}
}
q = cur;
if ((cur[1] == '#') && (cur[2] == 'x')) {
cur += 3;
tmp = *cur;
while (tmp != ';') { /* Non input consuming loop */
if ((tmp >= '0') && (tmp <= '9'))
charval = charval * 16 + (tmp - '0');
else if ((tmp >= 'a') && (tmp <= 'f'))
charval = charval * 16 + (tmp - 'a') + 10;
else if ((tmp >= 'A') && (tmp <= 'F'))
charval = charval * 16 + (tmp - 'A') + 10;
else {
xmlGenericError(xmlGenericErrorContext,
"xmlStringGetNodeList: invalid hexadecimal charvalue\n");
charval = 0;
break;
}
cur++;
tmp = *cur;
}
if (tmp == ';')
cur++;
q = cur;
} else if (cur[1] == '#') {
cur += 2;
tmp = *cur;
while (tmp != ';') { /* Non input consuming loops */
if ((tmp >= '0') && (tmp <= '9'))
charval = charval * 10 + (tmp - '0');
else {
xmlGenericError(xmlGenericErrorContext,
"xmlStringGetNodeList: invalid decimal charvalue\n");
charval = 0;
break;
}
cur++;
tmp = *cur;
}
if (tmp == ';')
cur++;
q = cur;
} else {
/*
* Read the entity string
*/
cur++;
q = cur;
while ((*cur != 0) && (*cur != ';')) cur++;
if (*cur == 0) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlStringGetNodeList: unterminated entity %30s\n", q);
#endif
return(ret);
}
if (cur != q) {
/*
* Predefined entities don't generate nodes
*/
val = xmlStrndup(q, cur - q);
ent = xmlGetDocEntity(doc, val);
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (last == NULL) {
node = xmlNewDocText(doc, ent->content);
last = ret = node;
} else if (last->type != XML_TEXT_NODE) {
node = xmlNewDocText(doc, ent->content);
last = xmlAddNextSibling(last, node);
} else
xmlNodeAddContent(last, ent->content);
} else {
/*
* Create a new REFERENCE_REF node
*/
node = xmlNewReference(doc, val);
if (node == NULL) {
if (val != NULL) xmlFree(val);
return(ret);
}
else if ((ent != NULL) && (ent->children == NULL)) {
xmlNodePtr temp;
ent->children = xmlStringGetNodeList(doc,
(const xmlChar*)node->content);
temp = ent->children;
while (temp) {
temp->parent = (xmlNodePtr)ent;
temp = temp->next;
}
}
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
xmlFree(val);
}
cur++;
q = cur;
}
if (charval != 0) {
xmlChar buf[10];
int len;
len = xmlCopyCharMultiByte(buf, charval);
buf[len] = 0;
node = xmlNewDocText(doc, buf);
if (node != NULL) {
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
charval = 0;
}
} else
cur++;
}
if ((cur != q) || (ret == NULL)) {
/*
* Handle the last piece of text.
*/
if ((last != NULL) && (last->type == XML_TEXT_NODE)) {
xmlNodeAddContentLen(last, q, cur - q);
} else {
node = xmlNewDocTextLen(doc, q, cur - q);
if (node == NULL) return(ret);
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
}
return(ret);
}
/**
* xmlNodeListGetString:
* @doc: the document
* @list: a Node list
* @inLine: should we replace entity contents or show their external form
*
* Returns the string equivalent to the text contained in the Node list
* made of TEXTs and ENTITY_REFs
* Returns a pointer to the string copy, the caller must free it with xmlFree().
*/
xmlChar *
xmlNodeListGetString(xmlDocPtr doc, xmlNodePtr list, int inLine)
{
xmlNodePtr node = list;
xmlChar *ret = NULL;
xmlEntityPtr ent;
if (list == NULL)
return (NULL);
while (node != NULL) {
if ((node->type == XML_TEXT_NODE) ||
(node->type == XML_CDATA_SECTION_NODE)) {
if (inLine) {
ret = xmlStrcat(ret, node->content);
} else {
xmlChar *buffer;
buffer = xmlEncodeEntitiesReentrant(doc, node->content);
if (buffer != NULL) {
ret = xmlStrcat(ret, buffer);
xmlFree(buffer);
}
}
} else if (node->type == XML_ENTITY_REF_NODE) {
if (inLine) {
ent = xmlGetDocEntity(doc, node->name);
if (ent != NULL) {
xmlChar *buffer;
/* an entity content can be any "well balanced chunk",
* i.e. the result of the content [43] production:
* http://www.w3.org/TR/REC-xml#NT-content.
* So it can contain text, CDATA section or nested
* entity reference nodes (among others).
* -> we recursive call xmlNodeListGetString()
* which handles these types */
buffer = xmlNodeListGetString(doc, ent->children, 1);
if (buffer != NULL) {
ret = xmlStrcat(ret, buffer);
xmlFree(buffer);
}
} else {
ret = xmlStrcat(ret, node->content);
}
} else {
xmlChar buf[2];
buf[0] = '&';
buf[1] = 0;
ret = xmlStrncat(ret, buf, 1);
ret = xmlStrcat(ret, node->name);
buf[0] = ';';
buf[1] = 0;
ret = xmlStrncat(ret, buf, 1);
}
}
#if 0
else {
xmlGenericError(xmlGenericErrorContext,
"xmlGetNodeListString : invalid node type %d\n",
node->type);
}
#endif
node = node->next;
}
return (ret);
}
/**
* xmlNodeListGetRawString:
* @doc: the document
* @list: a Node list
* @inLine: should we replace entity contents or show their external form
*
* Returns the string equivalent to the text contained in the Node list
* made of TEXTs and ENTITY_REFs, contrary to xmlNodeListGetString()
* this function doesn't do any character encoding handling.
*
* Returns a pointer to the string copy, the caller must free it with xmlFree().
*/
xmlChar *
xmlNodeListGetRawString(xmlDocPtr doc, xmlNodePtr list, int inLine)
{
xmlNodePtr node = list;
xmlChar *ret = NULL;
xmlEntityPtr ent;
if (list == NULL)
return (NULL);
while (node != NULL) {
if ((node->type == XML_TEXT_NODE) ||
(node->type == XML_CDATA_SECTION_NODE)) {
if (inLine) {
ret = xmlStrcat(ret, node->content);
} else {
xmlChar *buffer;
buffer = xmlEncodeSpecialChars(doc, node->content);
if (buffer != NULL) {
ret = xmlStrcat(ret, buffer);
xmlFree(buffer);
}
}
} else if (node->type == XML_ENTITY_REF_NODE) {
if (inLine) {
ent = xmlGetDocEntity(doc, node->name);
if (ent != NULL) {
xmlChar *buffer;
/* an entity content can be any "well balanced chunk",
* i.e. the result of the content [43] production:
* http://www.w3.org/TR/REC-xml#NT-content.
* So it can contain text, CDATA section or nested
* entity reference nodes (among others).
* -> we recursive call xmlNodeListGetRawString()
* which handles these types */
buffer =
xmlNodeListGetRawString(doc, ent->children, 1);
if (buffer != NULL) {
ret = xmlStrcat(ret, buffer);
xmlFree(buffer);
}
} else {
ret = xmlStrcat(ret, node->content);
}
} else {
xmlChar buf[2];
buf[0] = '&';
buf[1] = 0;
ret = xmlStrncat(ret, buf, 1);
ret = xmlStrcat(ret, node->name);
buf[0] = ';';
buf[1] = 0;
ret = xmlStrncat(ret, buf, 1);
}
}
#if 0
else {
xmlGenericError(xmlGenericErrorContext,
"xmlGetNodeListString : invalid node type %d\n",
node->type);
}
#endif
node = node->next;
}
return (ret);
}
/**
* xmlNewProp:
* @node: the holding node
* @name: the name of the attribute
* @value: the value of the attribute
*
* Create a new property carried by a node.
* Returns a pointer to the attribute
*/
xmlAttrPtr
xmlNewProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) {
xmlAttrPtr cur;
xmlDocPtr doc = NULL;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewProp : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new property and fill the fields.
*/
cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewProp : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
cur->type = XML_ATTRIBUTE_NODE;
cur->parent = node;
if (node != NULL) {
doc = node->doc;
cur->doc = doc;
}
cur->name = xmlStrdup(name);
if (value != NULL) {
xmlChar *buffer;
xmlNodePtr tmp;
buffer = xmlEncodeEntitiesReentrant(doc, value);
cur->children = xmlStringGetNodeList(doc, buffer);
cur->last = NULL;
tmp = cur->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) cur;
tmp->doc = doc;
if (tmp->next == NULL)
cur->last = tmp;
tmp = tmp->next;
}
xmlFree(buffer);
}
/*
* Add it at the end to preserve parsing order ...
*/
if (node != NULL) {
if (node->properties == NULL) {
node->properties = cur;
} else {
xmlAttrPtr prev = node->properties;
while (prev->next != NULL) prev = prev->next;
prev->next = cur;
cur->prev = prev;
}
}
return(cur);
}
/**
* xmlNewNsProp:
* @node: the holding node
* @ns: the namespace
* @name: the name of the attribute
* @value: the value of the attribute
*
* Create a new property tagged with a namespace and carried by a node.
* Returns a pointer to the attribute
*/
xmlAttrPtr
xmlNewNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name,
const xmlChar *value) {
xmlAttrPtr cur;
xmlDocPtr doc = NULL;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsProp : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new property and fill the fields.
*/
cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsProp : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
cur->type = XML_ATTRIBUTE_NODE;
cur->parent = node;
if (node != NULL) {
doc = node->doc;
cur->doc = doc;
}
cur->ns = ns;
cur->name = xmlStrdup(name);
if (value != NULL) {
xmlChar *buffer;
xmlNodePtr tmp;
buffer = xmlEncodeEntitiesReentrant(doc, value);
cur->children = xmlStringGetNodeList(doc, buffer);
cur->last = NULL;
tmp = cur->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) cur;
if (tmp->next == NULL)
cur->last = tmp;
tmp = tmp->next;
}
xmlFree(buffer);
}
/*
* Add it at the end to preserve parsing order ...
*/
if (node != NULL) {
if (node->properties == NULL) {
node->properties = cur;
} else {
xmlAttrPtr prev = node->properties;
while (prev->next != NULL) prev = prev->next;
prev->next = cur;
cur->prev = prev;
}
}
return(cur);
}
/**
* xmlNewNsPropEatName:
* @node: the holding node
* @ns: the namespace
* @name: the name of the attribute
* @value: the value of the attribute
*
* Create a new property tagged with a namespace and carried by a node.
* Returns a pointer to the attribute
*/
xmlAttrPtr
xmlNewNsPropEatName(xmlNodePtr node, xmlNsPtr ns, xmlChar *name,
const xmlChar *value) {
xmlAttrPtr cur;
xmlDocPtr doc = NULL;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsPropEatName : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new property and fill the fields.
*/
cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsPropEatName : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
cur->type = XML_ATTRIBUTE_NODE;
cur->parent = node;
if (node != NULL) {
doc = node->doc;
cur->doc = doc;
}
cur->ns = ns;
cur->name = name;
if (value != NULL) {
xmlChar *buffer;
xmlNodePtr tmp;
buffer = xmlEncodeEntitiesReentrant(doc, value);
cur->children = xmlStringGetNodeList(doc, buffer);
cur->last = NULL;
tmp = cur->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) cur;
if (tmp->next == NULL)
cur->last = tmp;
tmp = tmp->next;
}
xmlFree(buffer);
}
/*
* Add it at the end to preserve parsing order ...
*/
if (node != NULL) {
if (node->properties == NULL) {
node->properties = cur;
} else {
xmlAttrPtr prev = node->properties;
while (prev->next != NULL) prev = prev->next;
prev->next = cur;
cur->prev = prev;
}
}
return(cur);
}
/**
* xmlNewDocProp:
* @doc: the document
* @name: the name of the attribute
* @value: the value of the attribute
*
* Create a new property carried by a document.
* Returns a pointer to the attribute
*/
xmlAttrPtr
xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) {
xmlAttrPtr cur;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewDocProp : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new property and fill the fields.
*/
cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewDocProp : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
cur->type = XML_ATTRIBUTE_NODE;
cur->name = xmlStrdup(name);
cur->doc = doc;
if (value != NULL) {
xmlNodePtr tmp;
cur->children = xmlStringGetNodeList(doc, value);
cur->last = NULL;
tmp = cur->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) cur;
if (tmp->next == NULL)
cur->last = tmp;
tmp = tmp->next;
}
}
return(cur);
}
/**
* xmlFreePropList:
* @cur: the first property in the list
*
* Free a property and all its siblings, all the children are freed too.
*/
void
xmlFreePropList(xmlAttrPtr cur) {
xmlAttrPtr next;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreePropList : property == NULL\n");
#endif
return;
}
while (cur != NULL) {
next = cur->next;
xmlFreeProp(cur);
cur = next;
}
}
/**
* xmlFreeProp:
* @cur: an attribute
*
* Free one attribute, all the content is freed too
*/
void
xmlFreeProp(xmlAttrPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeProp : property == NULL\n");
#endif
return;
}
/* Check for ID removal -> leading to invalid references ! */
if ((cur->parent != NULL) && (cur->parent->doc != NULL) &&
((cur->parent->doc->intSubset != NULL) ||
(cur->parent->doc->extSubset != NULL))) {
if (xmlIsID(cur->parent->doc, cur->parent, cur))
xmlRemoveID(cur->parent->doc, cur);
}
if (cur->name != NULL) xmlFree((char *) cur->name);
if (cur->children != NULL) xmlFreeNodeList(cur->children);
xmlFree(cur);
}
/**
* xmlRemoveProp:
* @cur: an attribute
*
* Unlink and free one attribute, all the content is freed too
* Note this doesn't work for namespace definition attributes
*
* Returns 0 if success and -1 in case of error.
*/
int
xmlRemoveProp(xmlAttrPtr cur) {
xmlAttrPtr tmp;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlRemoveProp : cur == NULL\n");
#endif
return(-1);
}
if (cur->parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlRemoveProp : cur->parent == NULL\n");
#endif
return(-1);
}
tmp = cur->parent->properties;
if (tmp == cur) {
cur->parent->properties = cur->next;
xmlFreeProp(cur);
return(0);
}
while (tmp != NULL) {
if (tmp->next == cur) {
tmp->next = cur->next;
if (tmp->next != NULL)
tmp->next->prev = tmp;
xmlFreeProp(cur);
return(0);
}
tmp = tmp->next;
}
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlRemoveProp : attribute not owned by its node\n");
#endif
return(-1);
}
/**
* xmlNewPI:
* @name: the processing instruction name
* @content: the PI content
*
* Creation of a processing instruction element.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewPI(const xmlChar *name, const xmlChar *content) {
xmlNodePtr cur;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewPI : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewPI : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_PI_NODE;
cur->name = xmlStrdup(name);
if (content != NULL) {
cur->content = xmlStrdup(content);
}
return(cur);
}
/**
* xmlNewNode:
* @ns: namespace if any
* @name: the node name
*
* Creation of a new node element. @ns is optional (NULL).
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewNode(xmlNsPtr ns, const xmlChar *name) {
xmlNodePtr cur;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNode : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNode : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ELEMENT_NODE;
cur->name = xmlStrdup(name);
cur->ns = ns;
return(cur);
}
/**
* xmlNewNodeEatName:
* @ns: namespace if any
* @name: the node name
*
* Creation of a new node element. @ns is optional (NULL).
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewNodeEatName(xmlNsPtr ns, xmlChar *name) {
xmlNodePtr cur;
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNode : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNode : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ELEMENT_NODE;
cur->name = name;
cur->ns = ns;
return(cur);
}
/**
* xmlNewDocNode:
* @doc: the document
* @ns: namespace if any
* @name: the node name
* @content: the XML text content if any
*
* Creation of a new node element within a document. @ns and @content
* are optional (NULL).
* NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities
* references, but XML special chars need to be escaped first by using
* xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't
* need entities support.
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocNode(xmlDocPtr doc, xmlNsPtr ns,
const xmlChar *name, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewNode(ns, name);
if (cur != NULL) {
cur->doc = doc;
if (content != NULL) {
cur->children = xmlStringGetNodeList(doc, content);
UPDATE_LAST_CHILD_AND_PARENT(cur)
}
}
return(cur);
}
/**
* xmlNewDocNodeEatName:
* @doc: the document
* @ns: namespace if any
* @name: the node name
* @content: the XML text content if any
*
* Creation of a new node element within a document. @ns and @content
* are optional (NULL).
* NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities
* references, but XML special chars need to be escaped first by using
* xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't
* need entities support.
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocNodeEatName(xmlDocPtr doc, xmlNsPtr ns,
xmlChar *name, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewNodeEatName(ns, name);
if (cur != NULL) {
cur->doc = doc;
if (content != NULL) {
cur->children = xmlStringGetNodeList(doc, content);
UPDATE_LAST_CHILD_AND_PARENT(cur)
}
}
return(cur);
}
/**
* xmlNewDocRawNode:
* @doc: the document
* @ns: namespace if any
* @name: the node name
* @content: the text content if any
*
* Creation of a new node element within a document. @ns and @content
* are optional (NULL).
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocRawNode(xmlDocPtr doc, xmlNsPtr ns,
const xmlChar *name, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewNode(ns, name);
if (cur != NULL) {
cur->doc = doc;
if (content != NULL) {
cur->children = xmlNewDocText(doc, content);
UPDATE_LAST_CHILD_AND_PARENT(cur)
}
}
return(cur);
}
/**
* xmlNewDocFragment:
* @doc: the document owning the fragment
*
* Creation of a new Fragment node.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocFragment(xmlDocPtr doc) {
xmlNodePtr cur;
/*
* Allocate a new DocumentFragment node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewDocFragment : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_DOCUMENT_FRAG_NODE;
cur->doc = doc;
return(cur);
}
/**
* xmlNewText:
* @content: the text content
*
* Creation of a new text node.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewText(const xmlChar *content) {
xmlNodePtr cur;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewText : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_TEXT_NODE;
cur->name = xmlStringText;
if (content != NULL) {
cur->content = xmlStrdup(content);
}
return(cur);
}
/**
* xmlNewTextChild:
* @parent: the parent node
* @ns: a namespace if any
* @name: the name of the child
* @content: the text content of the child if any.
*
* Creation of a new child element, added at the end of @parent children list.
* @ns and @content parameters are optional (NULL). If content is non NULL,
* a child TEXT node will be created containing the string content.
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewTextChild(xmlNodePtr parent, xmlNsPtr ns,
const xmlChar *name, const xmlChar *content) {
xmlNodePtr cur, prev;
if (parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewTextChild : parent == NULL\n");
#endif
return(NULL);
}
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewTextChild : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new node
*/
if (ns == NULL)
cur = xmlNewDocRawNode(parent->doc, parent->ns, name, content);
else
cur = xmlNewDocRawNode(parent->doc, ns, name, content);
if (cur == NULL) return(NULL);
/*
* add the new element at the end of the children list.
*/
cur->type = XML_ELEMENT_NODE;
cur->parent = parent;
cur->doc = parent->doc;
if (parent->children == NULL) {
parent->children = cur;
parent->last = cur;
} else {
prev = parent->last;
prev->next = cur;
cur->prev = prev;
parent->last = cur;
}
return(cur);
}
/**
* xmlNewCharRef:
* @doc: the document
* @name: the char ref string, starting with # or "&# ... ;"
*
* Creation of a new character reference node.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewCharRef(xmlDocPtr doc, const xmlChar *name) {
xmlNodePtr cur;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewCharRef : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ENTITY_REF_NODE;
cur->doc = doc;
if (name[0] == '&') {
int len;
name++;
len = xmlStrlen(name);
if (name[len - 1] == ';')
cur->name = xmlStrndup(name, len - 1);
else
cur->name = xmlStrndup(name, len);
} else
cur->name = xmlStrdup(name);
return(cur);
}
/**
* xmlNewReference:
* @doc: the document
* @name: the reference name, or the reference string with & and ;
*
* Creation of a new reference node.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewReference(xmlDocPtr doc, const xmlChar *name) {
xmlNodePtr cur;
xmlEntityPtr ent;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewReference : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ENTITY_REF_NODE;
cur->doc = doc;
if (name[0] == '&') {
int len;
name++;
len = xmlStrlen(name);
if (name[len - 1] == ';')
cur->name = xmlStrndup(name, len - 1);
else
cur->name = xmlStrndup(name, len);
} else
cur->name = xmlStrdup(name);
ent = xmlGetDocEntity(doc, cur->name);
if (ent != NULL) {
cur->content = ent->content;
/*
* The parent pointer in entity is a DTD pointer and thus is NOT
* updated. Not sure if this is 100% correct.
* -George
*/
cur->children = (xmlNodePtr) ent;
cur->last = (xmlNodePtr) ent;
}
return(cur);
}
/**
* xmlNewDocText:
* @doc: the document
* @content: the text content
*
* Creation of a new text node within a document.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocText(xmlDocPtr doc, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewText(content);
if (cur != NULL) cur->doc = doc;
return(cur);
}
/**
* xmlNewTextLen:
* @content: the text content
* @len: the text len.
*
* Creation of a new text node with an extra parameter for the content's length
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewTextLen(const xmlChar *content, int len) {
xmlNodePtr cur;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewTextLen : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_TEXT_NODE;
cur->name = xmlStringText;
if (content != NULL) {
cur->content = xmlStrndup(content, len);
}
return(cur);
}
/**
* xmlNewDocTextLen:
* @doc: the document
* @content: the text content
* @len: the text len.
*
* Creation of a new text node with an extra content length parameter. The
* text node pertain to a given document.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocTextLen(xmlDocPtr doc, const xmlChar *content, int len) {
xmlNodePtr cur;
cur = xmlNewTextLen(content, len);
if (cur != NULL) cur->doc = doc;
return(cur);
}
/**
* xmlNewComment:
* @content: the comment content
*
* Creation of a new node containing a comment.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewComment(const xmlChar *content) {
xmlNodePtr cur;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewComment : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_COMMENT_NODE;
cur->name = xmlStringComment;
if (content != NULL) {
cur->content = xmlStrdup(content);
}
return(cur);
}
/**
* xmlNewCDataBlock:
* @doc: the document
* @content: the CDATA block content content
* @len: the length of the block
*
* Creation of a new node containing a CDATA block.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len) {
xmlNodePtr cur;
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewCDataBlock : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_CDATA_SECTION_NODE;
cur->doc = doc;
if (content != NULL) {
cur->content = xmlStrndup(content, len);
}
return(cur);
}
/**
* xmlNewDocComment:
* @doc: the document
* @content: the comment content
*
* Creation of a new node containing a comment within a document.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocComment(xmlDocPtr doc, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewComment(content);
if (cur != NULL) cur->doc = doc;
return(cur);
}
/**
* xmlSetTreeDoc:
* @tree: the top element
* @doc: the document
*
* update all nodes under the tree to point to the right document
*/
void
xmlSetTreeDoc(xmlNodePtr tree, xmlDocPtr doc) {
xmlAttrPtr prop;
if (tree == NULL)
return;
if (tree->doc != doc) {
if(tree->type == XML_ELEMENT_NODE) {
prop = tree->properties;
while (prop != NULL) {
prop->doc = doc;
xmlSetListDoc(prop->children, doc);
prop = prop->next;
}
}
if (tree->children != NULL)
xmlSetListDoc(tree->children, doc);
tree->doc = doc;
}
}
/**
* xmlSetListDoc:
* @list: the first element
* @doc: the document
*
* update all nodes in the list to point to the right document
*/
void
xmlSetListDoc(xmlNodePtr list, xmlDocPtr doc) {
xmlNodePtr cur;
if (list == NULL)
return;
cur = list;
while (cur != NULL) {
if (cur->doc != doc)
xmlSetTreeDoc(cur, doc);
cur = cur->next;
}
}
/**
* xmlNewChild:
* @parent: the parent node
* @ns: a namespace if any
* @name: the name of the child
* @content: the XML content of the child if any.
*
* Creation of a new child element, added at the end of @parent children list.
* @ns and @content parameters are optional (NULL). If content is non NULL,
* a child list containing the TEXTs and ENTITY_REFs node will be created.
* NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities
* references, but XML special chars need to be escaped first by using
* xmlEncodeEntitiesReentrant(). Use xmlNewTextChild() if entities
* support is not needed.
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewChild(xmlNodePtr parent, xmlNsPtr ns,
const xmlChar *name, const xmlChar *content) {
xmlNodePtr cur, prev;
if (parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewChild : parent == NULL\n");
#endif
return(NULL);
}
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewChild : name == NULL\n");
#endif
return(NULL);
}
/*
* Allocate a new node
*/
if (parent->type == XML_ELEMENT_NODE) {
if (ns == NULL)
cur = xmlNewDocNode(parent->doc, parent->ns, name, content);
else
cur = xmlNewDocNode(parent->doc, ns, name, content);
} else if ((parent->type == XML_DOCUMENT_NODE) ||
(parent->type == XML_HTML_DOCUMENT_NODE)) {
if (ns == NULL)
cur = xmlNewDocNode((xmlDocPtr) parent, NULL, name, content);
else
cur = xmlNewDocNode((xmlDocPtr) parent, ns, name, content);
} else if (parent->type == XML_DOCUMENT_FRAG_NODE) {
cur = xmlNewDocNode( parent->doc, ns, name, content);
} else {
return(NULL);
}
if (cur == NULL) return(NULL);
/*
* add the new element at the end of the children list.
*/
cur->type = XML_ELEMENT_NODE;
cur->parent = parent;
cur->doc = parent->doc;
if (parent->children == NULL) {
parent->children = cur;
parent->last = cur;
} else {
prev = parent->last;
prev->next = cur;
cur->prev = prev;
parent->last = cur;
}
return(cur);
}
/**
* xmlAddNextSibling:
* @cur: the child node
* @elem: the new node
*
* Add a new node @elem as the next sibling of @cur
* If the new node was already inserted in a document it is
* first unlinked from its existing context.
* As a result of text merging @elem may be freed.
* If the new node is ATTRIBUTE, it is added into properties instead of children.
* If there is an attribute with equal name, it is first destroyed.
*
* Returns the new node or NULL in case of error.
*/
xmlNodePtr
xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddNextSibling : cur == NULL\n");
#endif
return(NULL);
}
if (elem == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddNextSibling : elem == NULL\n");
#endif
return(NULL);
}
xmlUnlinkNode(elem);
if (elem->type == XML_TEXT_NODE) {
if (cur->type == XML_TEXT_NODE) {
xmlNodeAddContent(cur, elem->content);
xmlFreeNode(elem);
return(cur);
}
if ((cur->next != NULL) && (cur->next->type == XML_TEXT_NODE) &&
(cur->name == cur->next->name)) {
xmlChar *tmp;
tmp = xmlStrdup(elem->content);
tmp = xmlStrcat(tmp, cur->next->content);
xmlNodeSetContent(cur->next, tmp);
xmlFree(tmp);
xmlFreeNode(elem);
return(cur->next);
}
} else if (elem->type == XML_ATTRIBUTE_NODE) {
/* check if an attribute with the same name exists */
xmlAttrPtr attr;
if (elem->ns == NULL)
attr = xmlHasProp(cur->parent, elem->name);
else
attr = xmlHasNsProp(cur->parent, elem->name, elem->ns->href);
if ((attr != NULL) && (attr != (xmlAttrPtr) elem)) {
/* different instance, destroy it (attributes must be unique) */
xmlFreeProp(attr);
}
}
if (elem->doc != cur->doc) {
xmlSetTreeDoc(elem, cur->doc);
}
elem->parent = cur->parent;
elem->prev = cur;
elem->next = cur->next;
cur->next = elem;
if (elem->next != NULL)
elem->next->prev = elem;
if ((elem->parent != NULL) && (elem->parent->last == cur) && (elem->type != XML_ATTRIBUTE_NODE))
elem->parent->last = elem;
return(elem);
}
/**
* xmlAddPrevSibling:
* @cur: the child node
* @elem: the new node
*
* Add a new node @elem as the previous sibling of @cur
* merging adjacent TEXT nodes (@elem may be freed)
* If the new node was already inserted in a document it is
* first unlinked from its existing context.
* If the new node is ATTRIBUTE, it is added into properties instead of children.
* If there is an attribute with equal name, it is first destroyed.
*
* Returns the new node or NULL in case of error.
*/
xmlNodePtr
xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddPrevSibling : cur == NULL\n");
#endif
return(NULL);
}
if (elem == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddPrevSibling : elem == NULL\n");
#endif
return(NULL);
}
xmlUnlinkNode(elem);
if (elem->type == XML_TEXT_NODE) {
if (cur->type == XML_TEXT_NODE) {
xmlChar *tmp;
tmp = xmlStrdup(elem->content);
tmp = xmlStrcat(tmp, cur->content);
xmlNodeSetContent(cur, tmp);
xmlFree(tmp);
xmlFreeNode(elem);
return(cur);
}
if ((cur->prev != NULL) && (cur->prev->type == XML_TEXT_NODE) &&
(cur->name == cur->prev->name)) {
xmlNodeAddContent(cur->prev, elem->content);
xmlFreeNode(elem);
return(cur->prev);
}
} else if (elem->type == XML_ATTRIBUTE_NODE) {
/* check if an attribute with the same name exists */
xmlAttrPtr attr;
if (elem->ns == NULL)
attr = xmlHasProp(cur->parent, elem->name);
else
attr = xmlHasNsProp(cur->parent, elem->name, elem->ns->href);
if ((attr != NULL) && (attr != (xmlAttrPtr) elem)) {
/* different instance, destroy it (attributes must be unique) */
xmlFreeProp(attr);
}
}
if (elem->doc != cur->doc) {
xmlSetTreeDoc(elem, cur->doc);
}
elem->parent = cur->parent;
elem->next = cur;
elem->prev = cur->prev;
cur->prev = elem;
if (elem->prev != NULL)
elem->prev->next = elem;
if (elem->parent != NULL) {
if (elem->type == XML_ATTRIBUTE_NODE) {
if (elem->parent->properties == (xmlAttrPtr) cur) {
elem->parent->properties = (xmlAttrPtr) elem;
}
} else {
if (elem->parent->children == cur) {
elem->parent->children = elem;
}
}
}
return(elem);
}
/**
* xmlAddSibling:
* @cur: the child node
* @elem: the new node
*
* Add a new element @elem to the list of siblings of @cur
* merging adjacent TEXT nodes (@elem may be freed)
* If the new element was already inserted in a document it is
* first unlinked from its existing context.
*
* Returns the new element or NULL in case of error.
*/
xmlNodePtr
xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) {
xmlNodePtr parent;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddSibling : cur == NULL\n");
#endif
return(NULL);
}
if (elem == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddSibling : elem == NULL\n");
#endif
return(NULL);
}
/*
* Constant time is we can rely on the ->parent->last to find
* the last sibling.
*/
if ((cur->parent != NULL) &&
(cur->parent->children != NULL) &&
(cur->parent->last != NULL) &&
(cur->parent->last->next == NULL)) {
cur = cur->parent->last;
} else {
while (cur->next != NULL) cur = cur->next;
}
xmlUnlinkNode(elem);
if ((cur->type == XML_TEXT_NODE) && (elem->type == XML_TEXT_NODE)) {
xmlNodeAddContent(cur, elem->content);
xmlFreeNode(elem);
return(cur);
}
if (elem->doc != cur->doc) {
xmlSetTreeDoc(elem, cur->doc);
}
parent = cur->parent;
elem->prev = cur;
elem->next = NULL;
elem->parent = parent;
cur->next = elem;
if (parent != NULL)
parent->last = elem;
return(elem);
}
/**
* xmlAddChildList:
* @parent: the parent node
* @cur: the first node in the list
*
* Add a list of node at the end of the child list of the parent
* merging adjacent TEXT nodes (@cur may be freed)
*
* Returns the last child or NULL in case of error.
*/
xmlNodePtr
xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) {
xmlNodePtr prev;
if (parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddChildList : parent == NULL\n");
#endif
return(NULL);
}
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddChildList : child == NULL\n");
#endif
return(NULL);
}
if ((cur->doc != NULL) && (parent->doc != NULL) &&
(cur->doc != parent->doc)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"Elements moved to a different document\n");
#endif
}
/*
* add the first element at the end of the children list.
*/
if (parent->children == NULL) {
parent->children = cur;
} else {
/*
* If cur and parent->last both are TEXT nodes, then merge them.
*/
if ((cur->type == XML_TEXT_NODE) &&
(parent->last->type == XML_TEXT_NODE) &&
(cur->name == parent->last->name)) {
xmlNodeAddContent(parent->last, cur->content);
/*
* if it's the only child, nothing more to be done.
*/
if (cur->next == NULL) {
xmlFreeNode(cur);
return(parent->last);
}
prev = cur;
cur = cur->next;
xmlFreeNode(prev);
}
prev = parent->last;
prev->next = cur;
cur->prev = prev;
}
while (cur->next != NULL) {
cur->parent = parent;
if (cur->doc != parent->doc) {
xmlSetTreeDoc(cur, parent->doc);
}
cur = cur->next;
}
cur->parent = parent;
cur->doc = parent->doc; /* the parent may not be linked to a doc ! */
parent->last = cur;
return(cur);
}
/**
* xmlAddChild:
* @parent: the parent node
* @cur: the child node
*
* Add a new node to @parent, at the end of the child (or property) list
* merging adjacent TEXT nodes (in which case @cur is freed)
* If the new node was already inserted in a document it is
* first unlinked from its existing context.
* If the new node is ATTRIBUTE, it is added into properties instead of children.
* If there is an attribute with equal name, it is first destroyed.
*
* Returns the child or NULL in case of error.
*/
xmlNodePtr
xmlAddChild(xmlNodePtr parent, xmlNodePtr cur) {
xmlNodePtr prev;
if (parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddChild : parent == NULL\n");
#endif
return(NULL);
}
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddChild : child == NULL\n");
#endif
return(NULL);
}
/*
* If cur is a TEXT node, merge its content with adjacent TEXT nodes
* cur is then freed.
*/
if (cur->type == XML_TEXT_NODE) {
if ((parent->type == XML_TEXT_NODE) &&
(parent->content != NULL)) {
xmlNodeAddContent(parent, cur->content);
xmlFreeNode(cur);
return(parent);
}
if ((parent->last != NULL) && (parent->last->type == XML_TEXT_NODE) &&
(parent->last->name == cur->name)) {
xmlNodeAddContent(parent->last, cur->content);
xmlFreeNode(cur);
return(parent->last);
}
}
/*
* add the new element at the end of the children list.
*/
cur->parent = parent;
if (cur->doc != parent->doc) {
xmlSetTreeDoc(cur, parent->doc);
}
/*
* Coalescing
*/
if ((parent->type == XML_TEXT_NODE) &&
(parent->content != NULL)) {
xmlNodeAddContent(parent, cur->content);
xmlFreeNode(cur);
return(parent);
}
if (cur->type == XML_ATTRIBUTE_NODE) {
if (parent->properties == NULL) {
parent->properties = (xmlAttrPtr) cur;
} else {
/* check if an attribute with the same name exists */
xmlAttrPtr lastattr;
if (cur->ns == NULL)
lastattr = xmlHasProp(parent, cur->name);
else
lastattr = xmlHasNsProp(parent, cur->name, cur->ns->href);
if ((lastattr != NULL) && (lastattr != (xmlAttrPtr) cur)) {
/* different instance, destroy it (attributes must be unique) */
xmlFreeProp(lastattr);
}
/* find the end */
lastattr = parent->properties;
while (lastattr->next != NULL) {
lastattr = lastattr->next;
}
lastattr->next = (xmlAttrPtr) cur;
((xmlAttrPtr) cur)->prev = lastattr;
}
} else {
if (parent->children == NULL) {
parent->children = cur;
parent->last = cur;
} else {
prev = parent->last;
prev->next = cur;
cur->prev = prev;
parent->last = cur;
}
}
return(cur);
}
/**
* xmlGetLastChild:
* @parent: the parent node
*
* Search the last child of a node.
* Returns the last child or NULL if none.
*/
xmlNodePtr
xmlGetLastChild(xmlNodePtr parent) {
if (parent == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlGetLastChild : parent == NULL\n");
#endif
return(NULL);
}
return(parent->last);
}
/**
* xmlFreeNodeList:
* @cur: the first node in the list
*
* Free a node and all its siblings, this is a recursive behaviour, all
* the children are freed too.
*/
void
xmlFreeNodeList(xmlNodePtr cur) {
xmlNodePtr next;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeNodeList : node == NULL\n");
#endif
return;
}
if (cur->type == XML_NAMESPACE_DECL) {
xmlFreeNsList((xmlNsPtr) cur);
return;
}
while (cur != NULL) {
next = cur->next;
/* unroll to speed up freeing the document */
if (cur->type != XML_DTD_NODE) {
if ((cur->children != NULL) &&
(cur->type != XML_ENTITY_REF_NODE))
xmlFreeNodeList(cur->children);
if (cur->properties != NULL)
xmlFreePropList(cur->properties);
if ((cur->type != XML_ELEMENT_NODE) &&
(cur->type != XML_XINCLUDE_START) &&
(cur->type != XML_XINCLUDE_END) &&
(cur->type != XML_ENTITY_REF_NODE)) {
if (cur->content != NULL) xmlFree(cur->content);
}
if (((cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_XINCLUDE_START) ||
(cur->type == XML_XINCLUDE_END)) &&
(cur->nsDef != NULL))
xmlFreeNsList(cur->nsDef);
/*
* When a node is a text node or a comment, it uses a global static
* variable for the name of the node.
*
* The xmlStrEqual comparisons need to be done when (happened with
* XML::libXML and XML::libXSLT) the library is included twice
* statically in the binary and a tree allocated by one occurrence
* of the lib gets freed by the other occurrence, in this case
* the string addresses compare are not sufficient.
*/
if ((cur->name != NULL) &&
(cur->name != xmlStringText) &&
(cur->name != xmlStringTextNoenc) &&
(cur->name != xmlStringComment)) {
if (cur->type == XML_TEXT_NODE) {
if ((!xmlStrEqual(cur->name, xmlStringText)) &&
(!xmlStrEqual(cur->name, xmlStringTextNoenc)))
xmlFree((char *) cur->name);
} else if (cur->type == XML_COMMENT_NODE) {
if (!xmlStrEqual(cur->name, xmlStringComment))
xmlFree((char *) cur->name);
} else
xmlFree((char *) cur->name);
}
/* TODO : derecursivate this function */
xmlFree(cur);
}
cur = next;
}
}
/**
* xmlFreeNode:
* @cur: the node
*
* Free a node, this is a recursive behaviour, all the children are freed too.
* This doesn't unlink the child from the list, use xmlUnlinkNode() first.
*/
void
xmlFreeNode(xmlNodePtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeNode : node == NULL\n");
#endif
return;
}
/* use xmlFreeDtd for DTD nodes */
if (cur->type == XML_DTD_NODE) {
xmlFreeDtd((xmlDtdPtr) cur);
return;
}
if (cur->type == XML_NAMESPACE_DECL) {
xmlFreeNs((xmlNsPtr) cur);
return;
}
if (cur->type == XML_ATTRIBUTE_NODE) {
xmlFreeProp((xmlAttrPtr) cur);
return;
}
if ((cur->children != NULL) &&
(cur->type != XML_ENTITY_REF_NODE))
xmlFreeNodeList(cur->children);
if (cur->properties != NULL)
xmlFreePropList(cur->properties);
if ((cur->type != XML_ELEMENT_NODE) &&
(cur->content != NULL) &&
(cur->type != XML_ENTITY_REF_NODE) &&
(cur->type != XML_XINCLUDE_END) &&
(cur->type != XML_XINCLUDE_START)) {
xmlFree(cur->content);
}
/*
* When a node is a text node or a comment, it uses a global static
* variable for the name of the node.
*
* The xmlStrEqual comparisons need to be done when (happened with
* XML::libXML and XML::libXSLT) the library is included twice statically
* in the binary and a tree allocated by one occurence of the lib gets
* freed by the other occurrence, in this case the string addresses compare
* are not sufficient.
*/
if ((cur->name != NULL) &&
(cur->name != xmlStringText) &&
(cur->name != xmlStringTextNoenc) &&
(cur->name != xmlStringComment)) {
if (cur->type == XML_TEXT_NODE) {
if ((!xmlStrEqual(cur->name, xmlStringText)) &&
(!xmlStrEqual(cur->name, xmlStringTextNoenc)))
xmlFree((char *) cur->name);
} else if (cur->type == XML_COMMENT_NODE) {
if (!xmlStrEqual(cur->name, xmlStringComment))
xmlFree((char *) cur->name);
} else
xmlFree((char *) cur->name);
}
if (cur->nsDef != NULL) xmlFreeNsList(cur->nsDef);
xmlFree(cur);
}
/**
* xmlUnlinkNode:
* @cur: the node
*
* Unlink a node from it's current context, the node is not freed
*/
void
xmlUnlinkNode(xmlNodePtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlUnlinkNode : node == NULL\n");
#endif
return;
}
if (cur->type == XML_DTD_NODE) {
xmlDocPtr doc;
doc = cur->doc;
if (doc->intSubset == (xmlDtdPtr) cur)
doc->intSubset = NULL;
if (doc->extSubset == (xmlDtdPtr) cur)
doc->extSubset = NULL;
}
if (cur->parent != NULL) {
xmlNodePtr parent;
parent = cur->parent;
if (cur->type == XML_ATTRIBUTE_NODE) {
if (parent->properties == (xmlAttrPtr) cur)
parent->properties = ((xmlAttrPtr) cur)->next;
} else {
if (parent->children == cur)
parent->children = cur->next;
if (parent->last == cur)
parent->last = cur->prev;
}
cur->parent = NULL;
}
if (cur->next != NULL)
cur->next->prev = cur->prev;
if (cur->prev != NULL)
cur->prev->next = cur->next;
cur->next = cur->prev = NULL;
}
/**
* xmlReplaceNode:
* @old: the old node
* @cur: the node
*
* Unlink the old node from it's current context, prune the new one
* at the same place. If @cur was already inserted in a document it is
* first unlinked from its existing context.
*
* Returns the @old node
*/
xmlNodePtr
xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) {
if (old == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlReplaceNode : old == NULL\n");
#endif
return(NULL);
}
if (cur == NULL) {
xmlUnlinkNode(old);
return(old);
}
if (cur == old) {
return(old);
}
if ((old->type==XML_ATTRIBUTE_NODE) && (cur->type!=XML_ATTRIBUTE_NODE)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlReplaceNode : Trying to replace attribute node with other node type\n");
#endif
return(old);
}
if ((cur->type==XML_ATTRIBUTE_NODE) && (old->type!=XML_ATTRIBUTE_NODE)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlReplaceNode : Trying to replace a non-attribute node with attribute node\n");
#endif
return(old);
}
if ((old->type==XML_ATTRIBUTE_NODE) && (cur->type!=XML_ATTRIBUTE_NODE)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlReplaceNode : Trying to replace attribute node with other node type\n");
#endif
return(old);
}
if ((cur->type==XML_ATTRIBUTE_NODE) && (old->type!=XML_ATTRIBUTE_NODE)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlReplaceNode : Trying to replace a non-attribute node with attribute node\n");
#endif
return(old);
}
xmlUnlinkNode(cur);
cur->doc = old->doc;
cur->parent = old->parent;
cur->next = old->next;
if (cur->next != NULL)
cur->next->prev = cur;
cur->prev = old->prev;
if (cur->prev != NULL)
cur->prev->next = cur;
if (cur->parent != NULL) {
if (cur->type == XML_ATTRIBUTE_NODE) {
if (cur->parent->properties == (xmlAttrPtr)old)
cur->parent->properties = ((xmlAttrPtr) cur);
} else {
if (cur->parent->children == old)
cur->parent->children = cur;
if (cur->parent->last == old)
cur->parent->last = cur;
}
}
old->next = old->prev = NULL;
old->parent = NULL;
return(old);
}
/************************************************************************
* *
* Copy operations *
* *
************************************************************************/
/**
* xmlCopyNamespace:
* @cur: the namespace
*
* Do a copy of the namespace.
*
* Returns: a new #xmlNsPtr, or NULL in case of error.
*/
xmlNsPtr
xmlCopyNamespace(xmlNsPtr cur) {
xmlNsPtr ret;
if (cur == NULL) return(NULL);
switch (cur->type) {
case XML_LOCAL_NAMESPACE:
ret = xmlNewNs(NULL, cur->href, cur->prefix);
break;
default:
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlCopyNamespace: invalid type %d\n", cur->type);
#endif
return(NULL);
}
return(ret);
}
/**
* xmlCopyNamespaceList:
* @cur: the first namespace
*
* Do a copy of an namespace list.
*
* Returns: a new #xmlNsPtr, or NULL in case of error.
*/
xmlNsPtr
xmlCopyNamespaceList(xmlNsPtr cur) {
xmlNsPtr ret = NULL;
xmlNsPtr p = NULL,q;
while (cur != NULL) {
q = xmlCopyNamespace(cur);
if (p == NULL) {
ret = p = q;
} else {
p->next = q;
p = q;
}
cur = cur->next;
}
return(ret);
}
static xmlNodePtr
xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent);
/**
* xmlCopyProp:
* @target: the element where the attribute will be grafted
* @cur: the attribute
*
* Do a copy of the attribute.
*
* Returns: a new #xmlAttrPtr, or NULL in case of error.
*/
xmlAttrPtr
xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) {
xmlAttrPtr ret;
if (cur == NULL) return(NULL);
if (target != NULL)
ret = xmlNewDocProp(target->doc, cur->name, NULL);
else if (cur->parent != NULL)
ret = xmlNewDocProp(cur->parent->doc, cur->name, NULL);
else if (cur->children != NULL)
ret = xmlNewDocProp(cur->children->doc, cur->name, NULL);
else
ret = xmlNewDocProp(NULL, cur->name, NULL);
if (ret == NULL) return(NULL);
ret->parent = target;
if ((cur->ns != NULL) && (target != NULL)) {
xmlNsPtr ns;
/*
* if (target->doc)
* ns = xmlSearchNs(target->doc, target, cur->ns->prefix);
* else if (cur->doc) / * target may not yet have a doc : KPI * /
* ns = xmlSearchNs(cur->doc, target, cur->ns->prefix);
* else
* ns = NULL;
* ret->ns = ns;
*/
ns = xmlSearchNs(target->doc, target, cur->ns->prefix);
if (ns == NULL) {
/*
* Humm, we are copying an element whose namespace is defined
* out of the new tree scope. Search it in the original tree
* and add it at the top of the new tree
*/
ns = xmlSearchNs(cur->doc, cur->parent, cur->ns->prefix);
if (ns != NULL) {
xmlNodePtr root = target;
xmlNodePtr pred = NULL;
while (root->parent != NULL) {
pred = root;
root = root->parent;
}
if (root == (xmlNodePtr) target->doc) {
/* correct possibly cycling above the document elt */
root = pred;
}
ret->ns = xmlNewNs(root, ns->href, ns->prefix);
}
} else {
/*
* we have to find something appropriate here since
* we cant be sure, that the namespce we found is identified
* by the prefix
*/
if (xmlStrEqual(ns->href, cur->ns->href)) {
/* this is the nice case */
ret->ns = ns;
} else {
/*
* we are in trouble: we need a new reconcilied namespace.
* This is expensive
*/
ret->ns = xmlNewReconciliedNs(target->doc, target, cur->ns);
}
}
} else
ret->ns = NULL;
if (cur->children != NULL) {
xmlNodePtr tmp;
ret->children = xmlStaticCopyNodeList(cur->children, ret->doc, (xmlNodePtr) ret);
ret->last = NULL;
tmp = ret->children;
while (tmp != NULL) {
/* tmp->parent = (xmlNodePtr)ret; */
if (tmp->next == NULL)
ret->last = tmp;
tmp = tmp->next;
}
}
/*
* Try to handle IDs
*/
if ((target!= NULL) && (cur!= NULL) &&
(target->doc != NULL) && (cur->doc != NULL) &&
(cur->doc->ids != NULL) && (cur->parent != NULL)) {
if (xmlIsID(cur->doc, cur->parent, cur)) {
xmlChar *id;
id = xmlNodeListGetString(cur->doc, cur->children, 1);
if (id != NULL) {
xmlAddID(NULL, target->doc, id, ret);
xmlFree(id);
}
}
}
return(ret);
}
/**
* xmlCopyPropList:
* @target: the element where the attributes will be grafted
* @cur: the first attribute
*
* Do a copy of an attribute list.
*
* Returns: a new #xmlAttrPtr, or NULL in case of error.
*/
xmlAttrPtr
xmlCopyPropList(xmlNodePtr target, xmlAttrPtr cur) {
xmlAttrPtr ret = NULL;
xmlAttrPtr p = NULL,q;
while (cur != NULL) {
q = xmlCopyProp(target, cur);
if (p == NULL) {
ret = p = q;
} else {
p->next = q;
q->prev = p;
p = q;
}
cur = cur->next;
}
return(ret);
}
/*
* NOTE about the CopyNode operations !
*
* They are split into external and internal parts for one
* tricky reason: namespaces. Doing a direct copy of a node
* say RPM:Copyright without changing the namespace pointer to
* something else can produce stale links. One way to do it is
* to keep a reference counter but this doesn't work as soon
* as one move the element or the subtree out of the scope of
* the existing namespace. The actual solution seems to add
* a copy of the namespace at the top of the copied tree if
* not available in the subtree.
* Hence two functions, the public front-end call the inner ones
*/
static xmlNodePtr
xmlStaticCopyNode(const xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent,
int recursive) {
xmlNodePtr ret;
if (node == NULL) return(NULL);
switch (node->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ELEMENT_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
break;
case XML_ATTRIBUTE_NODE:
return((xmlNodePtr) xmlCopyProp(parent, (xmlAttrPtr) node));
case XML_NAMESPACE_DECL:
return((xmlNodePtr) xmlCopyNamespaceList((xmlNsPtr) node));
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return((xmlNodePtr) xmlCopyDoc((xmlDocPtr) node, recursive));
case XML_DOCUMENT_TYPE_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
return(NULL);
}
/*
* Allocate a new node and fill the fields.
*/
ret = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlStaticCopyNode : malloc failed\n");
return(NULL);
}
memset(ret, 0, sizeof(xmlNode));
ret->type = node->type;
ret->doc = doc;
ret->parent = parent;
if (node->name == xmlStringText)
ret->name = xmlStringText;
else if (node->name == xmlStringTextNoenc)
ret->name = xmlStringTextNoenc;
else if (node->name == xmlStringComment)
ret->name = xmlStringComment;
else if (node->name != NULL)
ret->name = xmlStrdup(node->name);
if ((node->type != XML_ELEMENT_NODE) &&
(node->content != NULL) &&
(node->type != XML_ENTITY_REF_NODE) &&
(node->type != XML_XINCLUDE_END) &&
(node->type != XML_XINCLUDE_START)) {
ret->content = xmlStrdup(node->content);
}else{
if (node->type == XML_ELEMENT_NODE)
ret->content = (void*)(long) node->content;
}
if (parent != NULL) {
xmlNodePtr tmp;
tmp = xmlAddChild(parent, ret);
/* node could have coalesced */
if (tmp != ret)
return(tmp);
}
if (!recursive) return(ret);
if (node->nsDef != NULL)
ret->nsDef = xmlCopyNamespaceList(node->nsDef);
if (node->ns != NULL) {
xmlNsPtr ns;
ns = xmlSearchNs(doc, ret, node->ns->prefix);
if (ns == NULL) {
/*
* Humm, we are copying an element whose namespace is defined
* out of the new tree scope. Search it in the original tree
* and add it at the top of the new tree
*/
ns = xmlSearchNs(node->doc, node, node->ns->prefix);
if (ns != NULL) {
xmlNodePtr root = ret;
while (root->parent != NULL) root = root->parent;
ret->ns = xmlNewNs(root, ns->href, ns->prefix);
}
} else {
/*
* reference the existing namespace definition in our own tree.
*/
ret->ns = ns;
}
}
if (node->properties != NULL)
ret->properties = xmlCopyPropList(ret, node->properties);
if (node->type == XML_ENTITY_REF_NODE) {
if ((doc == NULL) || (node->doc != doc)) {
/*
* The copied node will go into a separate document, so
* to avoid dangling references to the ENTITY_DECL node
* we cannot keep the reference. Try to find it in the
* target document.
*/
ret->children = (xmlNodePtr) xmlGetDocEntity(doc, ret->name);
} else {
ret->children = node->children;
}
ret->last = ret->children;
} else if (node->children != NULL) {
ret->children = xmlStaticCopyNodeList(node->children, doc, ret);
UPDATE_LAST_CHILD_AND_PARENT(ret)
}
return(ret);
}
static xmlNodePtr
xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) {
xmlNodePtr ret = NULL;
xmlNodePtr p = NULL,q;
while (node != NULL) {
if (node->type == XML_DTD_NODE ) {
if (doc == NULL) {
node = node->next;
continue;
}
if (doc->intSubset == NULL) {
q = (xmlNodePtr) xmlCopyDtd( (xmlDtdPtr) node );
q->doc = doc;
q->parent = parent;
doc->intSubset = (xmlDtdPtr) q;
xmlAddChild(parent, q);
} else {
q = (xmlNodePtr) doc->intSubset;
xmlAddChild(parent, q);
}
} else
q = xmlStaticCopyNode(node, doc, parent, 1);
if (ret == NULL) {
q->prev = NULL;
ret = p = q;
} else if (p != q) {
/* the test is required if xmlStaticCopyNode coalesced 2 text nodes */
p->next = q;
q->prev = p;
p = q;
}
node = node->next;
}
return(ret);
}
/**
* xmlCopyNode:
* @node: the node
* @recursive: if 1 do a recursive copy.
*
* Do a copy of the node.
*
* Returns: a new #xmlNodePtr, or NULL in case of error.
*/
xmlNodePtr
xmlCopyNode(const xmlNodePtr node, int recursive) {
xmlNodePtr ret;
ret = xmlStaticCopyNode(node, NULL, NULL, recursive);
return(ret);
}
/**
* xmlDocCopyNode:
* @node: the node
* @doc: the document
* @recursive: if 1 do a recursive copy.
*
* Do a copy of the node to a given document.
*
* Returns: a new #xmlNodePtr, or NULL in case of error.
*/
xmlNodePtr
xmlDocCopyNode(const xmlNodePtr node, xmlDocPtr doc, int recursive) {
xmlNodePtr ret;
ret = xmlStaticCopyNode(node, doc, NULL, recursive);
return(ret);
}
/**
* xmlCopyNodeList:
* @node: the first node in the list.
*
* Do a recursive copy of the node list.
*
* Returns: a new #xmlNodePtr, or NULL in case of error.
*/
xmlNodePtr xmlCopyNodeList(const xmlNodePtr node) {
xmlNodePtr ret = xmlStaticCopyNodeList(node, NULL, NULL);
return(ret);
}
/**
* xmlCopyDtd:
* @dtd: the dtd
*
* Do a copy of the dtd.
*
* Returns: a new #xmlDtdPtr, or NULL in case of error.
*/
xmlDtdPtr
xmlCopyDtd(xmlDtdPtr dtd) {
xmlDtdPtr ret;
xmlNodePtr cur, p = NULL, q;
if (dtd == NULL) return(NULL);
ret = xmlNewDtd(NULL, dtd->name, dtd->ExternalID, dtd->SystemID);
if (ret == NULL) return(NULL);
if (dtd->entities != NULL)
ret->entities = (void *) xmlCopyEntitiesTable(
(xmlEntitiesTablePtr) dtd->entities);
if (dtd->notations != NULL)
ret->notations = (void *) xmlCopyNotationTable(
(xmlNotationTablePtr) dtd->notations);
if (dtd->elements != NULL)
ret->elements = (void *) xmlCopyElementTable(
(xmlElementTablePtr) dtd->elements);
if (dtd->attributes != NULL)
ret->attributes = (void *) xmlCopyAttributeTable(
(xmlAttributeTablePtr) dtd->attributes);
if (dtd->pentities != NULL)
ret->pentities = (void *) xmlCopyEntitiesTable(
(xmlEntitiesTablePtr) dtd->pentities);
cur = dtd->children;
while (cur != NULL) {
q = NULL;
if (cur->type == XML_ENTITY_DECL) {
xmlEntityPtr tmp = (xmlEntityPtr) cur;
switch (tmp->etype) {
case XML_INTERNAL_GENERAL_ENTITY:
case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
q = (xmlNodePtr) xmlGetEntityFromDtd(ret, tmp->name);
break;
case XML_INTERNAL_PARAMETER_ENTITY:
case XML_EXTERNAL_PARAMETER_ENTITY:
q = (xmlNodePtr)
xmlGetParameterEntityFromDtd(ret, tmp->name);
break;
case XML_INTERNAL_PREDEFINED_ENTITY:
break;
}
} else if (cur->type == XML_ELEMENT_DECL) {
xmlElementPtr tmp = (xmlElementPtr) cur;
q = (xmlNodePtr)
xmlGetDtdQElementDesc(ret, tmp->name, tmp->prefix);
} else if (cur->type == XML_ATTRIBUTE_DECL) {
xmlAttributePtr tmp = (xmlAttributePtr) cur;
q = (xmlNodePtr)
xmlGetDtdQAttrDesc(ret, tmp->elem, tmp->name, tmp->prefix);
} else if (cur->type == XML_COMMENT_NODE) {
q = xmlCopyNode(cur, 0);
}
if (q == NULL) {
cur = cur->next;
continue;
}
if (p == NULL)
ret->children = q;
else
p->next = q;
q->prev = p;
q->parent = (xmlNodePtr) ret;
q->next = NULL;
ret->last = q;
p = q;
cur = cur->next;
}
return(ret);
}
/**
* xmlCopyDoc:
* @doc: the document
* @recursive: if 1 do a recursive copy.
*
* Do a copy of the document info. If recursive, the content tree will
* be copied too as well as DTD, namespaces and entities.
*
* Returns: a new #xmlDocPtr, or NULL in case of error.
*/
xmlDocPtr
xmlCopyDoc(xmlDocPtr doc, int recursive) {
xmlDocPtr ret;
if (doc == NULL) return(NULL);
ret = xmlNewDoc(doc->version);
if (ret == NULL) return(NULL);
if (doc->name != NULL)
ret->name = xmlMemStrdup(doc->name);
if (doc->encoding != NULL)
ret->encoding = xmlStrdup(doc->encoding);
ret->charset = doc->charset;
ret->compression = doc->compression;
ret->standalone = doc->standalone;
if (!recursive) return(ret);
ret->last = NULL;
ret->children = NULL;
if (doc->intSubset != NULL) {
ret->intSubset = xmlCopyDtd(doc->intSubset);
xmlSetTreeDoc((xmlNodePtr)ret->intSubset, ret);
ret->intSubset->parent = ret;
}
if (doc->oldNs != NULL)
ret->oldNs = xmlCopyNamespaceList(doc->oldNs);
if (doc->children != NULL) {
xmlNodePtr tmp;
ret->children = xmlStaticCopyNodeList(doc->children, ret,
(xmlNodePtr)ret);
ret->last = NULL;
tmp = ret->children;
while (tmp != NULL) {
if (tmp->next == NULL)
ret->last = tmp;
tmp = tmp->next;
}
}
return(ret);
}
/************************************************************************
* *
* Content access functions *
* *
************************************************************************/
/**
* xmlGetLineNo:
* @node : valid node
*
* Get line number of node. this requires activation of this option
* before invoking the parser by calling xmlLineNumbersDefault(1)
*
* Returns the line number if successful, -1 otherwise
*/
long
xmlGetLineNo(xmlNodePtr node)
{
long result = -1;
if (!node)
return result;
if (node->type == XML_ELEMENT_NODE)
result = (long) node->content;
else if ((node->prev != NULL) &&
((node->prev->type == XML_ELEMENT_NODE) ||
(node->prev->type == XML_TEXT_NODE)))
result = xmlGetLineNo(node->prev);
else if ((node->parent != NULL) &&
((node->parent->type == XML_ELEMENT_NODE) ||
(node->parent->type == XML_TEXT_NODE)))
result = xmlGetLineNo(node->parent);
return result;
}
/**
* xmlGetNodePath:
* @node: a node
*
* Build a structure based Path for the given node
*
* Returns the new path or NULL in case of error. The caller must free
* the returned string
*/
xmlChar *
xmlGetNodePath(xmlNodePtr node)
{
xmlNodePtr cur, tmp, next;
xmlChar *buffer = NULL, *temp;
size_t buf_len;
xmlChar *buf;
const char *sep;
const char *name;
char nametemp[100];
int occur = 0;
if (node == NULL)
return (NULL);
buf_len = 500;
buffer = (xmlChar *) xmlMalloc(buf_len * sizeof(xmlChar));
if (buffer == NULL)
return (NULL);
buf = (xmlChar *) xmlMalloc(buf_len * sizeof(xmlChar));
if (buf == NULL) {
xmlFree(buffer);
return (NULL);
}
buffer[0] = 0;
cur = node;
do {
name = "";
sep = "?";
occur = 0;
if ((cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE)) {
if (buffer[0] == '/')
break;
sep = "/";
next = NULL;
} else if (cur->type == XML_ELEMENT_NODE) {
sep = "/";
name = (const char *) cur->name;
if (cur->ns) {
snprintf(nametemp, sizeof(nametemp) - 1,
"%s:%s", cur->ns->prefix, cur->name);
nametemp[sizeof(nametemp) - 1] = 0;
name = nametemp;
}
next = cur->parent;
/*
* Thumbler index computation
*/
tmp = cur->prev;
while (tmp != NULL) {
if ((tmp->type == XML_ELEMENT_NODE) &&
(xmlStrEqual(cur->name, tmp->name)))
occur++;
tmp = tmp->prev;
}
if (occur == 0) {
tmp = cur->next;
while (tmp != NULL && occur == 0) {
if ((tmp->type == XML_ELEMENT_NODE) &&
(xmlStrEqual(cur->name, tmp->name)))
occur++;
tmp = tmp->next;
}
if (occur != 0)
occur = 1;
} else
occur++;
} else if (cur->type == XML_COMMENT_NODE) {
sep = "/";
name = "comment()";
next = cur->parent;
/*
* Thumbler index computation
*/
tmp = cur->prev;
while (tmp != NULL) {
if (tmp->type == XML_COMMENT_NODE)
occur++;
tmp = tmp->prev;
}
if (occur == 0) {
tmp = cur->next;
while (tmp != NULL && occur == 0) {
if (tmp->type == XML_COMMENT_NODE)
occur++;
tmp = tmp->next;
}
if (occur != 0)
occur = 1;
} else
occur++;
} else if ((cur->type == XML_TEXT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE)) {
sep = "/";
name = "text()";
next = cur->parent;
/*
* Thumbler index computation
*/
tmp = cur->prev;
while (tmp != NULL) {
if ((cur->type == XML_TEXT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE))
occur++;
tmp = tmp->prev;
}
if (occur == 0) {
tmp = cur->next;
while (tmp != NULL && occur == 0) {
if ((cur->type == XML_TEXT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE))
occur++;
tmp = tmp->next;
}
if (occur != 0)
occur = 1;
} else
occur++;
} else if (cur->type == XML_PI_NODE) {
sep = "/";
snprintf(nametemp, sizeof(nametemp) - 1,
"processing-instruction('%s')", cur->name);
nametemp[sizeof(nametemp) - 1] = 0;
name = nametemp;
next = cur->parent;
/*
* Thumbler index computation
*/
tmp = cur->prev;
while (tmp != NULL) {
if ((tmp->type == XML_PI_NODE) &&
(xmlStrEqual(cur->name, tmp->name)))
occur++;
tmp = tmp->prev;
}
if (occur == 0) {
tmp = cur->next;
while (tmp != NULL && occur == 0) {
if ((tmp->type == XML_PI_NODE) &&
(xmlStrEqual(cur->name, tmp->name)))
occur++;
tmp = tmp->next;
}
if (occur != 0)
occur = 1;
} else
occur++;
} else if (cur->type == XML_ATTRIBUTE_NODE) {
sep = "/@";
name = (const char *) (((xmlAttrPtr) cur)->name);
next = ((xmlAttrPtr) cur)->parent;
} else {
next = cur->parent;
}
/*
* Make sure there is enough room
*/
if (xmlStrlen(buffer) + sizeof(nametemp) + 20 > buf_len) {
buf_len =
2 * buf_len + xmlStrlen(buffer) + sizeof(nametemp) + 20;
temp = (xmlChar *) xmlRealloc(buffer, buf_len);
if (temp == NULL) {
xmlFree(buf);
xmlFree(buffer);
return (NULL);
}
buffer = temp;
temp = (xmlChar *) xmlRealloc(buf, buf_len);
if (temp == NULL) {
xmlFree(buf);
xmlFree(buffer);
return (NULL);
}
buf = temp;
}
if (occur == 0)
snprintf((char *) buf, buf_len, "%s%s%s",
sep, name, (char *) buffer);
else
snprintf((char *) buf, buf_len, "%s%s[%d]%s",
sep, name, occur, (char *) buffer);
snprintf((char *) buffer, buf_len, "%s", buf);
cur = next;
} while (cur != NULL);
xmlFree(buf);
return (buffer);
}
/**
* xmlDocGetRootElement:
* @doc: the document
*
* Get the root element of the document (doc->children is a list
* containing possibly comments, PIs, etc ...).
*
* Returns the #xmlNodePtr for the root or NULL
*/
xmlNodePtr
xmlDocGetRootElement(xmlDocPtr doc) {
xmlNodePtr ret;
if (doc == NULL) return(NULL);
ret = doc->children;
while (ret != NULL) {
if (ret->type == XML_ELEMENT_NODE)
return(ret);
ret = ret->next;
}
return(ret);
}
/**
* xmlDocSetRootElement:
* @doc: the document
* @root: the new document root element
*
* Set the root element of the document (doc->children is a list
* containing possibly comments, PIs, etc ...).
*
* Returns the old root element if any was found
*/
xmlNodePtr
xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root) {
xmlNodePtr old = NULL;
if (doc == NULL) return(NULL);
if (root == NULL)
return(NULL);
xmlUnlinkNode(root);
root->doc = doc;
root->parent = (xmlNodePtr) doc;
old = doc->children;
while (old != NULL) {
if (old->type == XML_ELEMENT_NODE)
break;
old = old->next;
}
if (old == NULL) {
if (doc->children == NULL) {
doc->children = root;
doc->last = root;
} else {
xmlAddSibling(doc->children, root);
}
} else {
xmlReplaceNode(old, root);
}
return(old);
}
/**
* xmlNodeSetLang:
* @cur: the node being changed
* @lang: the language description
*
* Set the language of a node, i.e. the values of the xml:lang
* attribute.
*/
void
xmlNodeSetLang(xmlNodePtr cur, const xmlChar *lang) {
xmlNsPtr ns;
if (cur == NULL) return;
switch(cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_PI_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_NAMESPACE_DECL:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
return;
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
break;
}
ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
if (ns == NULL)
return;
xmlSetNsProp(cur, ns, BAD_CAST "lang", lang);
}
/**
* xmlNodeGetLang:
* @cur: the node being checked
*
* Searches the language of a node, i.e. the values of the xml:lang
* attribute or the one carried by the nearest ancestor.
*
* Returns a pointer to the lang value, or NULL if not found
* It's up to the caller to free the memory with xmlFree().
*/
xmlChar *
xmlNodeGetLang(xmlNodePtr cur) {
xmlChar *lang;
while (cur != NULL) {
lang = xmlGetNsProp(cur, BAD_CAST "lang", XML_XML_NAMESPACE);
if (lang != NULL)
return(lang);
cur = cur->parent;
}
return(NULL);
}
/**
* xmlNodeSetSpacePreserve:
* @cur: the node being changed
* @val: the xml:space value ("0": default, 1: "preserve")
*
* Set (or reset) the space preserving behaviour of a node, i.e. the
* value of the xml:space attribute.
*/
void
xmlNodeSetSpacePreserve(xmlNodePtr cur, int val) {
xmlNsPtr ns;
if (cur == NULL) return;
switch(cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_PI_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return;
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
break;
}
ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
if (ns == NULL)
return;
switch (val) {
case 0:
xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "default");
break;
case 1:
xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "preserve");
break;
}
}
/**
* xmlNodeGetSpacePreserve:
* @cur: the node being checked
*
* Searches the space preserving behaviour of a node, i.e. the values
* of the xml:space attribute or the one carried by the nearest
* ancestor.
*
* Returns -1 if xml:space is not inherited, 0 if "default", 1 if "preserve"
*/
int
xmlNodeGetSpacePreserve(xmlNodePtr cur) {
xmlChar *space;
while (cur != NULL) {
space = xmlGetNsProp(cur, BAD_CAST "space", XML_XML_NAMESPACE);
if (space != NULL) {
if (xmlStrEqual(space, BAD_CAST "preserve")) {
xmlFree(space);
return(1);
}
if (xmlStrEqual(space, BAD_CAST "default")) {
xmlFree(space);
return(0);
}
xmlFree(space);
}
cur = cur->parent;
}
return(-1);
}
/**
* xmlNodeSetName:
* @cur: the node being changed
* @name: the new tag name
*
* Set (or reset) the name of a node.
*/
void
xmlNodeSetName(xmlNodePtr cur, const xmlChar *name) {
if (cur == NULL) return;
if (name == NULL) return;
switch(cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return;
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_PI_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_DTD_NODE:
case XML_DOCUMENT_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
break;
}
if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
cur->name = xmlStrdup(name);
}
/**
* xmlNodeSetBase:
* @cur: the node being changed
* @uri: the new base URI
*
* Set (or reset) the base URI of a node, i.e. the value of the
* xml:base attribute.
*/
void
xmlNodeSetBase(xmlNodePtr cur, xmlChar* uri) {
xmlNsPtr ns;
if (cur == NULL) return;
switch(cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_PI_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
return;
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
break;
case XML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
case XML_HTML_DOCUMENT_NODE: {
xmlDocPtr doc = (xmlDocPtr) cur;
if (doc->URL != NULL)
xmlFree((xmlChar *) doc->URL);
if (uri == NULL)
doc->URL = NULL;
else
doc->URL = xmlStrdup(uri);
return;
}
}
ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
if (ns == NULL)
return;
xmlSetNsProp(cur, ns, BAD_CAST "base", uri);
}
/**
* xmlNodeGetBase:
* @doc: the document the node pertains to
* @cur: the node being checked
*
* Searches for the BASE URL. The code should work on both XML
* and HTML document even if base mechanisms are completely different.
* It returns the base as defined in RFC 2396 sections
* 5.1.1. Base URI within Document Content
* and
* 5.1.2. Base URI from the Encapsulating Entity
* However it does not return the document base (5.1.3), use
* xmlDocumentGetBase() for this
*
* Returns a pointer to the base URL, or NULL if not found
* It's up to the caller to free the memory with xmlFree().
*/
xmlChar *
xmlNodeGetBase(xmlDocPtr doc, xmlNodePtr cur) {
xmlChar *oldbase = NULL;
xmlChar *base, *newbase;
if ((cur == NULL) && (doc == NULL))
return(NULL);
if (doc == NULL) doc = cur->doc;
if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
cur = doc->children;
while ((cur != NULL) && (cur->name != NULL)) {
if (cur->type != XML_ELEMENT_NODE) {
cur = cur->next;
continue;
}
if (!xmlStrcasecmp(cur->name, BAD_CAST "html")) {
cur = cur->children;
continue;
}
if (!xmlStrcasecmp(cur->name, BAD_CAST "head")) {
cur = cur->children;
continue;
}
if (!xmlStrcasecmp(cur->name, BAD_CAST "base")) {
return(xmlGetProp(cur, BAD_CAST "href"));
}
cur = cur->next;
}
return(NULL);
}
while (cur != NULL) {
if (cur->type == XML_ENTITY_DECL) {
xmlEntityPtr ent = (xmlEntityPtr) cur;
return(xmlStrdup(ent->URI));
}
if (cur->type == XML_ELEMENT_NODE) {
base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
if (base != NULL) {
if (oldbase != NULL) {
newbase = xmlBuildURI(oldbase, base);
if (newbase != NULL) {
xmlFree(oldbase);
xmlFree(base);
oldbase = newbase;
} else {
xmlFree(oldbase);
xmlFree(base);
return(NULL);
}
} else {
oldbase = base;
}
if ((!xmlStrncmp(oldbase, BAD_CAST "http://", 7)) ||
(!xmlStrncmp(oldbase, BAD_CAST "ftp://", 6)) ||
(!xmlStrncmp(oldbase, BAD_CAST "urn:", 4)))
return(oldbase);
}
}
cur = cur->parent;
}
if ((doc != NULL) && (doc->URL != NULL)) {
if (oldbase == NULL)
return(xmlStrdup(doc->URL));
newbase = xmlBuildURI(oldbase, doc->URL);
xmlFree(oldbase);
return(newbase);
}
return(oldbase);
}
/**
* xmlNodeGetContent:
* @cur: the node being read
*
* Read the value of a node, this can be either the text carried
* directly by this node if it's a TEXT node or the aggregate string
* of the values carried by this node child's (TEXT and ENTITY_REF).
* Entity references are substituted.
* Returns a new #xmlChar * or NULL if no content is available.
* It's up to the caller to free the memory with xmlFree().
*/
xmlChar *
xmlNodeGetContent(xmlNodePtr cur)
{
if (cur == NULL)
return (NULL);
switch (cur->type) {
case XML_DOCUMENT_FRAG_NODE:
case XML_ELEMENT_NODE:{
xmlNodePtr tmp = cur;
xmlBufferPtr buffer;
xmlChar *ret;
buffer = xmlBufferCreate();
if (buffer == NULL)
return (NULL);
while (tmp != NULL) {
switch (tmp->type) {
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
if (tmp->content != NULL)
xmlBufferCat(buffer, tmp->content);
break;
case XML_ENTITY_REF_NODE:{
/* recursive substitution of entity references */
xmlChar *cont = xmlNodeGetContent(tmp);
if (cont) {
xmlBufferCat(buffer,
(const xmlChar *) cont);
xmlFree(cont);
}
break;
}
default:
break;
}
/*
* Skip to next node
*/
if (tmp->children != NULL) {
if (tmp->children->type != XML_ENTITY_DECL) {
tmp = tmp->children;
continue;
}
}
if (tmp == cur)
break;
if (tmp->next != NULL) {
tmp = tmp->next;
continue;
}
do {
tmp = tmp->parent;
if (tmp == NULL)
break;
if (tmp == cur) {
tmp = NULL;
break;
}
if (tmp->next != NULL) {
tmp = tmp->next;
break;
}
} while (tmp != NULL);
}
ret = buffer->content;
buffer->content = NULL;
xmlBufferFree(buffer);
return (ret);
}
case XML_ATTRIBUTE_NODE:{
xmlAttrPtr attr = (xmlAttrPtr) cur;
if (attr->parent != NULL)
return (xmlNodeListGetString
(attr->parent->doc, attr->children, 1));
else
return (xmlNodeListGetString(NULL, attr->children, 1));
break;
}
case XML_COMMENT_NODE:
case XML_PI_NODE:
if (cur->content != NULL)
return (xmlStrdup(cur->content));
return (NULL);
case XML_ENTITY_REF_NODE:{
xmlEntityPtr ent;
xmlNodePtr tmp;
xmlBufferPtr buffer;
xmlChar *ret;
/* lookup entity declaration */
ent = xmlGetDocEntity(cur->doc, cur->name);
if (ent == NULL)
return (NULL);
buffer = xmlBufferCreate();
if (buffer == NULL)
return (NULL);
/* an entity content can be any "well balanced chunk",
* i.e. the result of the content [43] production:
* http://www.w3.org/TR/REC-xml#NT-content
* -> we iterate through child nodes and recursive call
* xmlNodeGetContent() which handles all possible node types */
tmp = ent->children;
while (tmp) {
xmlChar *cont = xmlNodeGetContent(tmp);
if (cont) {
xmlBufferCat(buffer, (const xmlChar *) cont);
xmlFree(cont);
}
tmp = tmp->next;
}
ret = buffer->content;
buffer->content = NULL;
xmlBufferFree(buffer);
return (ret);
}
case XML_ENTITY_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return (NULL);
case XML_NAMESPACE_DECL: {
xmlChar *tmp;
tmp = xmlStrdup(((xmlNsPtr) cur)->href);
return (tmp);
}
case XML_ELEMENT_DECL:
/* TODO !!! */
return (NULL);
case XML_ATTRIBUTE_DECL:
/* TODO !!! */
return (NULL);
case XML_ENTITY_DECL:
/* TODO !!! */
return (NULL);
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
if (cur->content != NULL)
return (xmlStrdup(cur->content));
return (NULL);
}
return (NULL);
}
/**
* xmlNodeSetContent:
* @cur: the node being modified
* @content: the new value of the content
*
* Replace the content of a node.
*/
void
xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeSetContent : node == NULL\n");
#endif
return;
}
switch (cur->type) {
case XML_DOCUMENT_FRAG_NODE:
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->children != NULL) xmlFreeNodeList(cur->children);
cur->children = xmlStringGetNodeList(cur->doc, content);
UPDATE_LAST_CHILD_AND_PARENT(cur)
break;
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
if (cur->content != NULL) {
xmlFree(cur->content);
}
if (cur->children != NULL) xmlFreeNodeList(cur->children);
cur->last = cur->children = NULL;
if (content != NULL) {
cur->content = xmlStrdup(content);
} else
cur->content = NULL;
break;
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
break;
case XML_NOTATION_NODE:
break;
case XML_DTD_NODE:
break;
case XML_NAMESPACE_DECL:
break;
case XML_ELEMENT_DECL:
/* TODO !!! */
break;
case XML_ATTRIBUTE_DECL:
/* TODO !!! */
break;
case XML_ENTITY_DECL:
/* TODO !!! */
break;
}
}
/**
* xmlNodeSetContentLen:
* @cur: the node being modified
* @content: the new value of the content
* @len: the size of @content
*
* Replace the content of a node.
*/
void
xmlNodeSetContentLen(xmlNodePtr cur, const xmlChar *content, int len) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeSetContentLen : node == NULL\n");
#endif
return;
}
switch (cur->type) {
case XML_DOCUMENT_FRAG_NODE:
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->children != NULL) xmlFreeNodeList(cur->children);
cur->children = xmlStringLenGetNodeList(cur->doc, content, len);
UPDATE_LAST_CHILD_AND_PARENT(cur)
break;
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_NOTATION_NODE:
if (cur->content != NULL) {
xmlFree(cur->content);
}
if (cur->children != NULL) xmlFreeNodeList(cur->children);
cur->children = cur->last = NULL;
if (content != NULL) {
cur->content = xmlStrndup(content, len);
} else
cur->content = NULL;
break;
case XML_DOCUMENT_NODE:
case XML_DTD_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
break;
case XML_ELEMENT_DECL:
/* TODO !!! */
break;
case XML_ATTRIBUTE_DECL:
/* TODO !!! */
break;
case XML_ENTITY_DECL:
/* TODO !!! */
break;
}
}
/**
* xmlNodeAddContentLen:
* @cur: the node being modified
* @content: extra content
* @len: the size of @content
*
* Append the extra substring to the node content.
*/
void
xmlNodeAddContentLen(xmlNodePtr cur, const xmlChar *content, int len) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeAddContentLen : node == NULL\n");
#endif
return;
}
if (len <= 0) return;
switch (cur->type) {
case XML_DOCUMENT_FRAG_NODE:
case XML_ELEMENT_NODE: {
xmlNodePtr last, newNode, tmp;
last = cur->last;
newNode = xmlNewTextLen(content, len);
if (newNode != NULL) {
tmp = xmlAddChild(cur, newNode);
if (tmp != newNode)
return;
if ((last != NULL) && (last->next == newNode)) {
xmlTextMerge(last, newNode);
}
}
break;
}
case XML_ATTRIBUTE_NODE:
break;
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_NOTATION_NODE:
if (content != NULL) {
cur->content = xmlStrncat(cur->content, content, len);
}
case XML_DOCUMENT_NODE:
case XML_DTD_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
break;
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
break;
}
}
/**
* xmlNodeAddContent:
* @cur: the node being modified
* @content: extra content
*
* Append the extra substring to the node content.
*/
void
xmlNodeAddContent(xmlNodePtr cur, const xmlChar *content) {
int len;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeAddContent : node == NULL\n");
#endif
return;
}
if (content == NULL) return;
len = xmlStrlen(content);
xmlNodeAddContentLen(cur, content, len);
}
/**
* xmlTextMerge:
* @first: the first text node
* @second: the second text node being merged
*
* Merge two text nodes into one
* Returns the first text node augmented
*/
xmlNodePtr
xmlTextMerge(xmlNodePtr first, xmlNodePtr second) {
if (first == NULL) return(second);
if (second == NULL) return(first);
if (first->type != XML_TEXT_NODE) return(first);
if (second->type != XML_TEXT_NODE) return(first);
if (second->name != first->name)
return(first);
xmlNodeAddContent(first, second->content);
xmlUnlinkNode(second);
xmlFreeNode(second);
return(first);
}
/**
* xmlGetNsList:
* @doc: the document
* @node: the current node
*
* Search all the namespace applying to a given element.
* Returns an NULL terminated array of all the #xmlNsPtr found
* that need to be freed by the caller or NULL if no
* namespace if defined
*/
xmlNsPtr *
xmlGetNsList(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node)
{
xmlNsPtr cur;
xmlNsPtr *ret = NULL;
int nbns = 0;
int maxns = 10;
int i;
while (node != NULL) {
if (node->type == XML_ELEMENT_NODE) {
cur = node->nsDef;
while (cur != NULL) {
if (ret == NULL) {
ret =
(xmlNsPtr *) xmlMalloc((maxns + 1) *
sizeof(xmlNsPtr));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlGetNsList : out of memory!\n");
return (NULL);
}
ret[nbns] = NULL;
}
for (i = 0; i < nbns; i++) {
if ((cur->prefix == ret[i]->prefix) ||
(xmlStrEqual(cur->prefix, ret[i]->prefix)))
break;
}
if (i >= nbns) {
if (nbns >= maxns) {
maxns *= 2;
ret = (xmlNsPtr *) xmlRealloc(ret,
(maxns +
1) *
sizeof(xmlNsPtr));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlGetNsList : realloc failed!\n");
return (NULL);
}
}
ret[nbns++] = cur;
ret[nbns] = NULL;
}
cur = cur->next;
}
}
node = node->parent;
}
return (ret);
}
/**
* xmlSearchNs:
* @doc: the document
* @node: the current node
* @nameSpace: the namespace prefix
*
* Search a Ns registered under a given name space for a document.
* recurse on the parents until it finds the defined namespace
* or return NULL otherwise.
* @nameSpace can be NULL, this is a search for the default namespace.
* We don't allow to cross entities boundaries. If you don't declare
* the namespace within those you will be in troubles !!! A warning
* is generated to cover this case.
*
* Returns the namespace pointer or NULL.
*/
xmlNsPtr
xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace) {
xmlNsPtr cur;
if (node == NULL) return(NULL);
if ((nameSpace != NULL) &&
(xmlStrEqual(nameSpace, (const xmlChar *)"xml"))) {
if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) {
/*
* The XML-1.0 namespace is normally held on the root
* element. In this case exceptionally create it on the
* node element.
*/
cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlSearchNs : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNs));
cur->type = XML_LOCAL_NAMESPACE;
cur->href = xmlStrdup(XML_XML_NAMESPACE);
cur->prefix = xmlStrdup((const xmlChar *)"xml");
cur->next = node->nsDef;
node->nsDef = cur;
return(cur);
}
if (doc->oldNs == NULL) {
/*
* Allocate a new Namespace and fill the fields.
*/
doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (doc->oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlSearchNs : malloc failed\n");
return(NULL);
}
memset(doc->oldNs, 0, sizeof(xmlNs));
doc->oldNs->type = XML_LOCAL_NAMESPACE;
doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE);
doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml");
}
return(doc->oldNs);
}
while (node != NULL) {
if ((node->type == XML_ENTITY_REF_NODE) ||
(node->type == XML_ENTITY_NODE) ||
(node->type == XML_ENTITY_DECL))
return(NULL);
if (node->type == XML_ELEMENT_NODE) {
cur = node->nsDef;
while (cur != NULL) {
if ((cur->prefix == NULL) && (nameSpace == NULL) &&
(cur->href != NULL))
return(cur);
if ((cur->prefix != NULL) && (nameSpace != NULL) &&
(cur->href != NULL) &&
(xmlStrEqual(cur->prefix, nameSpace)))
return(cur);
cur = cur->next;
}
}
node = node->parent;
}
return(NULL);
}
/**
* xmlSearchNsByHref:
* @doc: the document
* @node: the current node
* @href: the namespace value
*
* Search a Ns aliasing a given URI. Recurse on the parents until it finds
* the defined namespace or return NULL otherwise.
* Returns the namespace pointer or NULL.
*/
xmlNsPtr
xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar *href) {
xmlNsPtr cur;
xmlNodePtr orig = node;
if ((node == NULL) || (href == NULL)) return(NULL);
if (xmlStrEqual(href, XML_XML_NAMESPACE)) {
/*
* Only the document can hold the XML spec namespace.
*/
if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) {
/*
* The XML-1.0 namespace is normally held on the root
* element. In this case exceptionally create it on the
* node element.
*/
cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlSearchNs : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNs));
cur->type = XML_LOCAL_NAMESPACE;
cur->href = xmlStrdup(XML_XML_NAMESPACE);
cur->prefix = xmlStrdup((const xmlChar *)"xml");
cur->next = node->nsDef;
node->nsDef = cur;
return(cur);
}
if (doc->oldNs == NULL) {
/*
* Allocate a new Namespace and fill the fields.
*/
doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (doc->oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlSearchNsByHref : malloc failed\n");
return(NULL);
}
memset(doc->oldNs, 0, sizeof(xmlNs));
doc->oldNs->type = XML_LOCAL_NAMESPACE;
doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE);
doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml");
}
return(doc->oldNs);
}
while (node != NULL) {
cur = node->nsDef;
while (cur != NULL) {
if ((cur->href != NULL) && (href != NULL) &&
(xmlStrEqual(cur->href, href))) {
/*
* Check that the prefix is not shadowed between orig and node
*/
xmlNodePtr check = orig;
xmlNsPtr tst;
while (check != node) {
tst = check->nsDef;
while (tst != NULL) {
if ((tst->prefix == NULL) && (cur->prefix == NULL))
goto shadowed;
if ((tst->prefix != NULL) && (cur->prefix != NULL) &&
(xmlStrEqual(tst->prefix, cur->prefix)))
goto shadowed;
tst = tst->next;
}
check = check->parent;
}
return(cur);
}
shadowed:
cur = cur->next;
}
node = node->parent;
}
return(NULL);
}
/**
* xmlNewReconciliedNs
* @doc: the document
* @tree: a node expected to hold the new namespace
* @ns: the original namespace
*
* This function tries to locate a namespace definition in a tree
* ancestors, or create a new namespace definition node similar to
* @ns trying to reuse the same prefix. However if the given prefix is
* null (default namespace) or reused within the subtree defined by
* @tree or on one of its ancestors then a new prefix is generated.
* Returns the (new) namespace definition or NULL in case of error
*/
xmlNsPtr
xmlNewReconciliedNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) {
xmlNsPtr def;
xmlChar prefix[50];
int counter = 1;
if (tree == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewReconciliedNs : tree == NULL\n");
#endif
return(NULL);
}
if (ns == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewReconciliedNs : ns == NULL\n");
#endif
return(NULL);
}
/*
* Search an existing namespace definition inherited.
*/
def = xmlSearchNsByHref(doc, tree, ns->href);
if (def != NULL)
return(def);
/*
* Find a close prefix which is not already in use.
* Let's strip namespace prefixes longer than 20 chars !
*/
if (ns->prefix == NULL)
snprintf((char *) prefix, sizeof(prefix), "default");
else
snprintf((char *) prefix, sizeof(prefix), "%.20s", ns->prefix);
def = xmlSearchNs(doc, tree, prefix);
while (def != NULL) {
if (counter > 1000) return(NULL);
if (ns->prefix == NULL)
snprintf((char *) prefix, sizeof(prefix), "default%d", counter++);
else
snprintf((char *) prefix, sizeof(prefix), "%.20s%d", ns->prefix, counter++);
def = xmlSearchNs(doc, tree, prefix);
}
/*
* OK, now we are ready to create a new one.
*/
def = xmlNewNs(tree, ns->href, prefix);
return(def);
}
/**
* xmlReconciliateNs
* @doc: the document
* @tree: a node defining the subtree to reconciliate
*
* This function checks that all the namespaces declared within the given
* tree are properly declared. This is needed for example after Copy or Cut
* and then paste operations. The subtree may still hold pointers to
* namespace declarations outside the subtree or invalid/masked. As much
* as possible the function try to reuse the existing namespaces found in
* the new environment. If not possible the new namespaces are redeclared
* on @tree at the top of the given subtree.
* Returns the number of namespace declarations created or -1 in case of error.
*/
int
xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) {
xmlNsPtr *oldNs = NULL;
xmlNsPtr *newNs = NULL;
int sizeCache = 0;
int nbCache = 0;
xmlNsPtr n;
xmlNodePtr node = tree;
xmlAttrPtr attr;
int ret = 0, i;
while (node != NULL) {
/*
* Reconciliate the node namespace
*/
if (node->ns != NULL) {
/*
* initialize the cache if needed
*/
if (sizeCache == 0) {
sizeCache = 10;
oldNs = (xmlNsPtr *) xmlMalloc(sizeCache *
sizeof(xmlNsPtr));
if (oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
return(-1);
}
newNs = (xmlNsPtr *) xmlMalloc(sizeCache *
sizeof(xmlNsPtr));
if (newNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(oldNs);
return(-1);
}
}
for (i = 0;i < nbCache;i++) {
if (oldNs[i] == node->ns) {
node->ns = newNs[i];
break;
}
}
if (i == nbCache) {
/*
* OK we need to recreate a new namespace definition
*/
n = xmlNewReconciliedNs(doc, tree, node->ns);
if (n != NULL) { /* :-( what if else ??? */
/*
* check if we need to grow the cache buffers.
*/
if (sizeCache <= nbCache) {
sizeCache *= 2;
oldNs = (xmlNsPtr *) xmlRealloc(oldNs, sizeCache *
sizeof(xmlNsPtr));
if (oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(newNs);
return(-1);
}
newNs = (xmlNsPtr *) xmlRealloc(newNs, sizeCache *
sizeof(xmlNsPtr));
if (newNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(oldNs);
return(-1);
}
}
newNs[nbCache] = n;
oldNs[nbCache++] = node->ns;
node->ns = n;
}
}
}
/*
* now check for namespace hold by attributes on the node.
*/
attr = node->properties;
while (attr != NULL) {
if (attr->ns != NULL) {
/*
* initialize the cache if needed
*/
if (sizeCache == 0) {
sizeCache = 10;
oldNs = (xmlNsPtr *) xmlMalloc(sizeCache *
sizeof(xmlNsPtr));
if (oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
return(-1);
}
newNs = (xmlNsPtr *) xmlMalloc(sizeCache *
sizeof(xmlNsPtr));
if (newNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(oldNs);
return(-1);
}
}
for (i = 0;i < nbCache;i++) {
if (oldNs[i] == attr->ns) {
attr->ns = newNs[i];
break;
}
}
if (i == nbCache) {
/*
* OK we need to recreate a new namespace definition
*/
n = xmlNewReconciliedNs(doc, tree, attr->ns);
if (n != NULL) { /* :-( what if else ??? */
/*
* check if we need to grow the cache buffers.
*/
if (sizeCache <= nbCache) {
sizeCache *= 2;
oldNs = (xmlNsPtr *) xmlRealloc(oldNs, sizeCache *
sizeof(xmlNsPtr));
if (oldNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(newNs);
return(-1);
}
newNs = (xmlNsPtr *) xmlRealloc(newNs, sizeCache *
sizeof(xmlNsPtr));
if (newNs == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlReconciliateNs : memory pbm\n");
xmlFree(oldNs);
return(-1);
}
}
newNs[nbCache] = n;
oldNs[nbCache++] = attr->ns;
attr->ns = n;
}
}
}
attr = attr->next;
}
/*
* Browse the full subtree, deep first
*/
if (node->children != NULL) {
/* 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;
}
if (oldNs != NULL)
xmlFree(oldNs);
if (newNs != NULL)
xmlFree(newNs);
return(ret);
}
/**
* xmlHasProp:
* @node: the node
* @name: the attribute name
*
* Search an attribute associated to a node
* This function also looks in DTD attribute declaration for #FIXED or
* default declaration values unless DTD use has been turned off.
*
* Returns the attribute or the attribute declaration or NULL if
* neither was found.
*/
xmlAttrPtr
xmlHasProp(xmlNodePtr node, const xmlChar *name) {
xmlAttrPtr prop;
xmlDocPtr doc;
if ((node == NULL) || (name == NULL)) return(NULL);
/*
* Check on the properties attached to the node
*/
prop = node->properties;
while (prop != NULL) {
if (xmlStrEqual(prop->name, name)) {
return(prop);
}
prop = prop->next;
}
if (!xmlCheckDTD) return(NULL);
/*
* Check if there is a default declaration in the internal
* or external subsets
*/
doc = node->doc;
if (doc != NULL) {
xmlAttributePtr attrDecl;
if (doc->intSubset != NULL) {
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
if (attrDecl != NULL)
return((xmlAttrPtr) attrDecl);
}
}
return(NULL);
}
/**
* xmlHasNsProp:
* @node: the node
* @name: the attribute name
* @nameSpace: the URI of the namespace
*
* Search for an attribute associated to a node
* This attribute has to be anchored in the namespace specified.
* This does the entity substitution.
* This function looks in DTD attribute declaration for #FIXED or
* default declaration values unless DTD use has been turned off.
*
* Returns the attribute or the attribute declaration or NULL
* if neither was found.
*/
xmlAttrPtr
xmlHasNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
xmlAttrPtr prop;
xmlDocPtr doc;
if (node == NULL)
return(NULL);
prop = node->properties;
if (nameSpace == NULL)
return(xmlHasProp(node, name));
while (prop != NULL) {
/*
* One need to have
* - same attribute names
* - and the attribute carrying that namespace
*/
if ((xmlStrEqual(prop->name, name)) &&
((prop->ns != NULL) && (xmlStrEqual(prop->ns->href, nameSpace)))) {
return(prop);
}
prop = prop->next;
}
if (!xmlCheckDTD) return(NULL);
/*
* Check if there is a default declaration in the internal
* or external subsets
*/
doc = node->doc;
if (doc != NULL) {
if (doc->intSubset != NULL) {
xmlAttributePtr attrDecl = NULL;
xmlNsPtr *nsList, *cur;
xmlChar *ename;
nsList = xmlGetNsList(node->doc, node);
if (nsList == NULL)
return(NULL);
if ((node->ns != NULL) && (node->ns->prefix != NULL)) {
ename = xmlStrdup(node->ns->prefix);
ename = xmlStrcat(ename, BAD_CAST ":");
ename = xmlStrcat(ename, node->name);
} else {
ename = xmlStrdup(node->name);
}
if (ename == NULL) {
xmlFree(nsList);
return(NULL);
}
cur = nsList;
while (*cur != NULL) {
if (xmlStrEqual((*cur)->href, nameSpace)) {
attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, ename,
name, (*cur)->prefix);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, ename,
name, (*cur)->prefix);
}
cur++;
}
xmlFree(nsList);
xmlFree(ename);
return((xmlAttrPtr) attrDecl);
}
}
return(NULL);
}
/**
* xmlGetProp:
* @node: the node
* @name: the attribute name
*
* Search and get the value of an attribute associated to a node
* This does the entity substitution.
* This function looks in DTD attribute declaration for #FIXED or
* default declaration values unless DTD use has been turned off.
*
* Returns the attribute value or NULL if not found.
* It's up to the caller to free the memory with xmlFree().
*/
xmlChar *
xmlGetProp(xmlNodePtr node, const xmlChar *name) {
xmlAttrPtr prop;
xmlDocPtr doc;
if ((node == NULL) || (name == NULL)) return(NULL);
/*
* Check on the properties attached to the node
*/
prop = node->properties;
while (prop != NULL) {
if (xmlStrEqual(prop->name, name)) {
xmlChar *ret;
ret = xmlNodeListGetString(node->doc, prop->children, 1);
if (ret == NULL) return(xmlStrdup((xmlChar *)""));
return(ret);
}
prop = prop->next;
}
if (!xmlCheckDTD) return(NULL);
/*
* Check if there is a default declaration in the internal
* or external subsets
*/
doc = node->doc;
if (doc != NULL) {
xmlAttributePtr attrDecl;
if (doc->intSubset != NULL) {
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
if (attrDecl != NULL)
return(xmlStrdup(attrDecl->defaultValue));
}
}
return(NULL);
}
/**
* xmlGetNsProp:
* @node: the node
* @name: the attribute name
* @nameSpace: the URI of the namespace
*
* Search and get the value of an attribute associated to a node
* This attribute has to be anchored in the namespace specified.
* This does the entity substitution.
* This function looks in DTD attribute declaration for #FIXED or
* default declaration values unless DTD use has been turned off.
*
* Returns the attribute value or NULL if not found.
* It's up to the caller to free the memory with xmlFree().
*/
xmlChar *
xmlGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
xmlAttrPtr prop;
xmlDocPtr doc;
xmlNsPtr ns;
if (node == NULL)
return(NULL);
prop = node->properties;
if (nameSpace == NULL)
return(xmlGetProp(node, name));
while (prop != NULL) {
/*
* One need to have
* - same attribute names
* - and the attribute carrying that namespace
*/
if ((xmlStrEqual(prop->name, name)) &&
((prop->ns != NULL) &&
(xmlStrEqual(prop->ns->href, nameSpace)))) {
xmlChar *ret;
ret = xmlNodeListGetString(node->doc, prop->children, 1);
if (ret == NULL) return(xmlStrdup((xmlChar *)""));
return(ret);
}
prop = prop->next;
}
if (!xmlCheckDTD) return(NULL);
/*
* Check if there is a default declaration in the internal
* or external subsets
*/
doc = node->doc;
if (doc != NULL) {
if (doc->intSubset != NULL) {
xmlAttributePtr attrDecl;
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
/*
* The DTD declaration only allows a prefix search
*/
ns = xmlSearchNs(doc, node, attrDecl->prefix);
if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
return(xmlStrdup(attrDecl->defaultValue));
}
}
}
return(NULL);
}
/**
* xmlSetProp:
* @node: the node
* @name: the attribute name
* @value: the attribute value
*
* Set (or reset) an attribute carried by a node.
* Returns the attribute pointer.
*/
xmlAttrPtr
xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) {
xmlAttrPtr prop;
xmlDocPtr doc;
if ((node == NULL) || (name == NULL))
return(NULL);
doc = node->doc;
prop = node->properties;
while (prop != NULL) {
if ((xmlStrEqual(prop->name, name)) &&
(prop->ns == NULL)){
xmlNodePtr oldprop = prop->children;
prop->children = NULL;
prop->last = NULL;
if (value != NULL) {
xmlChar *buffer;
xmlNodePtr tmp;
buffer = xmlEncodeEntitiesReentrant(node->doc, value);
prop->children = xmlStringGetNodeList(node->doc, buffer);
prop->last = NULL;
prop->doc = doc;
tmp = prop->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) prop;
tmp->doc = doc;
if (tmp->next == NULL)
prop->last = tmp;
tmp = tmp->next;
}
xmlFree(buffer);
}
if (oldprop != NULL)
xmlFreeNodeList(oldprop);
return(prop);
}
prop = prop->next;
}
prop = xmlNewProp(node, name, value);
return(prop);
}
/**
* xmlUnsetProp:
* @node: the node
* @name: the attribute name
*
* Remove an attribute carried by a node.
* Returns 0 if successful, -1 if not found
*/
int
xmlUnsetProp(xmlNodePtr node, const xmlChar *name) {
xmlAttrPtr prop = node->properties, prev = NULL;;
if ((node == NULL) || (name == NULL))
return(-1);
while (prop != NULL) {
if ((xmlStrEqual(prop->name, name)) &&
(prop->ns == NULL)) {
if (prev == NULL)
node->properties = prop->next;
else
prev->next = prop->next;
xmlFreeProp(prop);
return(0);
}
prev = prop;
prop = prop->next;
}
return(-1);
}
/**
* xmlSetNsProp:
* @node: the node
* @ns: the namespace definition
* @name: the attribute name
* @value: the attribute value
*
* Set (or reset) an attribute carried by a node.
* The ns structure must be in scope, this is not checked.
*
* Returns the attribute pointer.
*/
xmlAttrPtr
xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name,
const xmlChar *value) {
xmlAttrPtr prop;
if ((node == NULL) || (name == NULL))
return(NULL);
if (ns == NULL)
return(xmlSetProp(node, name, value));
if (ns->href == NULL)
return(NULL);
prop = node->properties;
while (prop != NULL) {
/*
* One need to have
* - same attribute names
* - and the attribute carrying that namespace
*/
if ((xmlStrEqual(prop->name, name)) &&
(prop->ns != NULL) && (xmlStrEqual(prop->ns->href, ns->href))) {
if (prop->children != NULL)
xmlFreeNodeList(prop->children);
prop->children = NULL;
prop->last = NULL;
prop->ns = ns;
if (value != NULL) {
xmlChar *buffer;
xmlNodePtr tmp;
buffer = xmlEncodeEntitiesReentrant(node->doc, value);
prop->children = xmlStringGetNodeList(node->doc, buffer);
prop->last = NULL;
tmp = prop->children;
while (tmp != NULL) {
tmp->parent = (xmlNodePtr) prop;
if (tmp->next == NULL)
prop->last = tmp;
tmp = tmp->next;
}
xmlFree(buffer);
}
return(prop);
}
prop = prop->next;
}
prop = xmlNewNsProp(node, ns, name, value);
return(prop);
}
/**
* xmlUnsetNsProp:
* @node: the node
* @ns: the namespace definition
* @name: the attribute name
*
* Remove an attribute carried by a node.
* Returns 0 if successful, -1 if not found
*/
int
xmlUnsetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name) {
xmlAttrPtr prop = node->properties, prev = NULL;;
if ((node == NULL) || (name == NULL))
return(-1);
if (ns == NULL)
return(xmlUnsetProp(node, name));
if (ns->href == NULL)
return(-1);
while (prop != NULL) {
if ((xmlStrEqual(prop->name, name)) &&
(prop->ns != NULL) && (xmlStrEqual(prop->ns->href, ns->href))) {
if (prev == NULL)
node->properties = prop->next;
else
prev->next = prop->next;
xmlFreeProp(prop);
return(0);
}
prev = prop;
prop = prop->next;
}
return(-1);
}
/**
* xmlNodeIsText:
* @node: the node
*
* Is this node a Text node ?
* Returns 1 yes, 0 no
*/
int
xmlNodeIsText(xmlNodePtr node) {
if (node == NULL) return(0);
if (node->type == XML_TEXT_NODE) return(1);
return(0);
}
/**
* xmlIsBlankNode:
* @node: the node
*
* Checks whether this node is an empty or whitespace only
* (and possibly ignorable) text-node.
*
* Returns 1 yes, 0 no
*/
int
xmlIsBlankNode(xmlNodePtr node) {
const xmlChar *cur;
if (node == NULL) return(0);
if ((node->type != XML_TEXT_NODE) &&
(node->type != XML_CDATA_SECTION_NODE))
return(0);
if (node->content == NULL) return(1);
cur = node->content;
while (*cur != 0) {
if (!IS_BLANK(*cur)) return(0);
cur++;
}
return(1);
}
/**
* xmlTextConcat:
* @node: the node
* @content: the content
* @len: @content length
*
* Concat the given string at the end of the existing node content
*/
void
xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len) {
if (node == NULL) return;
if ((node->type != XML_TEXT_NODE) &&
(node->type != XML_CDATA_SECTION_NODE)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlTextConcat: node is not text nor CDATA\n");
#endif
return;
}
node->content = xmlStrncat(node->content, content, len);
}
/************************************************************************
* *
* Output : to a FILE or in memory *
* *
************************************************************************/
/**
* xmlBufferCreate:
*
* routine to create an XML buffer.
* returns the new structure.
*/
xmlBufferPtr
xmlBufferCreate(void) {
xmlBufferPtr ret;
ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCreate : out of memory!\n");
return(NULL);
}
ret->use = 0;
ret->size = xmlDefaultBufferSize;
ret->alloc = xmlBufferAllocScheme;
ret->content = (xmlChar *) xmlMalloc(ret->size * sizeof(xmlChar));
if (ret->content == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCreate : out of memory!\n");
xmlFree(ret);
return(NULL);
}
ret->content[0] = 0;
return(ret);
}
/**
* xmlBufferCreateSize:
* @size: initial size of buffer
*
* routine to create an XML buffer.
* returns the new structure.
*/
xmlBufferPtr
xmlBufferCreateSize(size_t size) {
xmlBufferPtr ret;
ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCreate : out of memory!\n");
return(NULL);
}
ret->use = 0;
ret->alloc = xmlBufferAllocScheme;
ret->size = (size ? size+2 : 0); /* +1 for ending null */
if (ret->size){
ret->content = (xmlChar *) xmlMalloc(ret->size * sizeof(xmlChar));
if (ret->content == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCreate : out of memory!\n");
xmlFree(ret);
return(NULL);
}
ret->content[0] = 0;
} else
ret->content = NULL;
return(ret);
}
/**
* xmlBufferSetAllocationScheme:
* @buf: the buffer to tune
* @scheme: allocation scheme to use
*
* Sets the allocation scheme for this buffer
*/
void
xmlBufferSetAllocationScheme(xmlBufferPtr buf,
xmlBufferAllocationScheme scheme) {
if (buf == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferSetAllocationScheme: buf == NULL\n");
#endif
return;
}
buf->alloc = scheme;
}
/**
* xmlBufferFree:
* @buf: the buffer to free
*
* Frees an XML buffer. It frees both the content and the structure which
* encapsulate it.
*/
void
xmlBufferFree(xmlBufferPtr buf) {
if (buf == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferFree: buf == NULL\n");
#endif
return;
}
if (buf->content != NULL) {
xmlFree(buf->content);
}
xmlFree(buf);
}
/**
* xmlBufferEmpty:
* @buf: the buffer
*
* empty a buffer.
*/
void
xmlBufferEmpty(xmlBufferPtr buf) {
if (buf->content == NULL) return;
buf->use = 0;
memset(buf->content, 0, buf->size);
}
/**
* xmlBufferShrink:
* @buf: the buffer to dump
* @len: the number of xmlChar to remove
*
* Remove the beginning of an XML buffer.
*
* Returns the number of #xmlChar removed, or -1 in case of failure.
*/
int
xmlBufferShrink(xmlBufferPtr buf, unsigned int len) {
if (len == 0) return(0);
if (len > buf->use) return(-1);
buf->use -= len;
memmove(buf->content, &buf->content[len], buf->use * sizeof(xmlChar));
buf->content[buf->use] = 0;
return(len);
}
/**
* xmlBufferGrow:
* @buf: the buffer
* @len: the minimum free size to allocate
*
* Grow the available space of an XML buffer.
*
* Returns the new available space or -1 in case of error
*/
int
xmlBufferGrow(xmlBufferPtr buf, unsigned int len) {
int size;
xmlChar *newbuf;
if (len + buf->use < buf->size) return(0);
size = buf->use + len + 100;
newbuf = (xmlChar *) xmlRealloc(buf->content, size);
if (newbuf == NULL) return(-1);
buf->content = newbuf;
buf->size = size;
return(buf->size - buf->use);
}
/**
* xmlBufferDump:
* @file: the file output
* @buf: the buffer to dump
*
* Dumps an XML buffer to a FILE *.
* Returns the number of #xmlChar written
*/
int
xmlBufferDump(FILE *file, xmlBufferPtr buf) {
int ret;
if (buf == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferDump: buf == NULL\n");
#endif
return(0);
}
if (buf->content == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferDump: buf->content == NULL\n");
#endif
return(0);
}
if (file == NULL)
file = stdout;
ret = fwrite(buf->content, sizeof(xmlChar), buf->use, file);
return(ret);
}
/**
* xmlBufferContent:
* @buf: the buffer
*
* Function to extract the content of a buffer
*
* Returns the internal content
*/
const xmlChar *
xmlBufferContent(const xmlBufferPtr buf)
{
if(!buf)
return NULL;
return buf->content;
}
/**
* xmlBufferLength:
* @buf: the buffer
*
* Function to get the length of a buffer
*
* Returns the length of data in the internal content
*/
int
xmlBufferLength(const xmlBufferPtr buf)
{
if(!buf)
return 0;
return buf->use;
}
/**
* xmlBufferResize:
* @buf: the buffer to resize
* @size: the desired size
*
* Resize a buffer to accommodate minimum size of @size.
*
* Returns 0 in case of problems, 1 otherwise
*/
int
xmlBufferResize(xmlBufferPtr buf, unsigned int size)
{
unsigned int newSize;
xmlChar* rebuf = NULL;
/*take care of empty case*/
newSize = (buf->size ? buf->size*2 : size);
/* Don't resize if we don't have to */
if (size < buf->size)
return 1;
/* figure out new size */
switch (buf->alloc){
case XML_BUFFER_ALLOC_DOUBLEIT:
while (size > newSize) newSize *= 2;
break;
case XML_BUFFER_ALLOC_EXACT:
newSize = size+10;
break;
default:
newSize = size+10;
break;
}
if (buf->content == NULL)
rebuf = (xmlChar *) xmlMalloc(newSize * sizeof(xmlChar));
else
rebuf = (xmlChar *) xmlRealloc(buf->content,
newSize * sizeof(xmlChar));
if (rebuf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlBufferResize : out of memory!\n");
return 0;
}
buf->content = rebuf;
buf->size = newSize;
return 1;
}
/**
* xmlBufferAdd:
* @buf: the buffer to dump
* @str: the #xmlChar string
* @len: the number of #xmlChar to add
*
* Add a string range to an XML buffer. if len == -1, the length of
* str is recomputed.
*/
void
xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len) {
unsigned int needSize;
if (str == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAdd: str == NULL\n");
#endif
return;
}
if (len < -1) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAdd: len < 0\n");
#endif
return;
}
if (len == 0) return;
if (len < 0)
len = xmlStrlen(str);
if (len <= 0) return;
needSize = buf->use + len + 2;
if (needSize > buf->size){
if (!xmlBufferResize(buf, needSize)){
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAdd : out of memory!\n");
return;
}
}
memmove(&buf->content[buf->use], str, len*sizeof(xmlChar));
buf->use += len;
buf->content[buf->use] = 0;
}
/**
* xmlBufferAddHead:
* @buf: the buffer
* @str: the #xmlChar string
* @len: the number of #xmlChar to add
*
* Add a string range to the beginning of an XML buffer.
* if len == -1, the length of @str is recomputed.
*/
void
xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len) {
unsigned int needSize;
if (str == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAddHead: str == NULL\n");
#endif
return;
}
if (len < -1) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAddHead: len < 0\n");
#endif
return;
}
if (len == 0) return;
if (len < 0)
len = xmlStrlen(str);
if (len <= 0) return;
needSize = buf->use + len + 2;
if (needSize > buf->size){
if (!xmlBufferResize(buf, needSize)){
xmlGenericError(xmlGenericErrorContext,
"xmlBufferAddHead : out of memory!\n");
return;
}
}
memmove(&buf->content[len], &buf->content[0], buf->use * sizeof(xmlChar));
memmove(&buf->content[0], str, len * sizeof(xmlChar));
buf->use += len;
buf->content[buf->use] = 0;
}
/**
* xmlBufferCat:
* @buf: the buffer to dump
* @str: the #xmlChar string
*
* Append a zero terminated string to an XML buffer.
*/
void
xmlBufferCat(xmlBufferPtr buf, const xmlChar *str) {
if (str != NULL)
xmlBufferAdd(buf, str, -1);
}
/**
* xmlBufferCCat:
* @buf: the buffer to dump
* @str: the C char string
*
* Append a zero terminated C string to an XML buffer.
*/
void
xmlBufferCCat(xmlBufferPtr buf, const char *str) {
const char *cur;
if (str == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCCat: str == NULL\n");
#endif
return;
}
for (cur = str;*cur != 0;cur++) {
if (buf->use + 10 >= buf->size) {
if (!xmlBufferResize(buf, buf->use+10)){
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCCat : out of memory!\n");
return;
}
}
buf->content[buf->use++] = *cur;
}
buf->content[buf->use] = 0;
}
/**
* xmlBufferWriteCHAR:
* @buf: the XML buffer
* @string: the string to add
*
* routine which manages and grows an output buffer. This one adds
* xmlChars at the end of the buffer.
*/
void
xmlBufferWriteCHAR
(xmlBufferPtr buf, const xmlChar *string) {
xmlBufferCat(buf, string);
}
/**
* xmlBufferWriteChar:
* @buf: the XML buffer output
* @string: the string to add
*
* routine which manage and grows an output buffer. This one add
* C chars at the end of the array.
*/
void
xmlBufferWriteChar(xmlBufferPtr buf, const char *string) {
xmlBufferCCat(buf, string);
}
/**
* xmlBufferWriteQuotedString:
* @buf: the XML buffer output
* @string: the string to add
*
* routine which manage and grows an output buffer. This one writes
* a quoted or double quoted #xmlChar string, checking first if it holds
* quote or double-quotes internally
*/
void
xmlBufferWriteQuotedString(xmlBufferPtr buf, const xmlChar *string) {
if (xmlStrchr(string, '"')) {
if (xmlStrchr(string, '\'')) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferWriteQuotedString: string contains quote and double-quotes !\n");
#endif
}
xmlBufferCCat(buf, "'");
xmlBufferCat(buf, string);
xmlBufferCCat(buf, "'");
} else {
xmlBufferCCat(buf, "\"");
xmlBufferCat(buf, string);
xmlBufferCCat(buf, "\"");
}
}
/************************************************************************
* *
* Dumping XML tree content to a simple buffer *
* *
************************************************************************/
static void
xmlNodeListDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
int format);
/**
* xmlNsDump:
* @buf: the XML buffer output
* @cur: a namespace
*
* Dump a local Namespace definition.
* Should be called in the context of attributes dumps.
*/
static void
xmlNsDump(xmlBufferPtr buf, xmlNsPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNsDump : Ns == NULL\n");
#endif
return;
}
if (cur->type == XML_LOCAL_NAMESPACE) {
if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
return;
/* Within the context of an element attributes */
if (cur->prefix != NULL) {
xmlBufferWriteChar(buf, " xmlns:");
xmlBufferWriteCHAR(buf, cur->prefix);
} else
xmlBufferWriteChar(buf, " xmlns");
xmlBufferWriteChar(buf, "=");
xmlBufferWriteQuotedString(buf, cur->href);
}
}
/**
* xmlNsListDump:
* @buf: the XML buffer output
* @cur: the first namespace
*
* Dump a list of local Namespace definitions.
* Should be called in the context of attributes dumps.
*/
static void
xmlNsListDump(xmlBufferPtr buf, xmlNsPtr cur) {
while (cur != NULL) {
xmlNsDump(buf, cur);
cur = cur->next;
}
}
/**
* xmlDtdDump:
* @buf: the XML buffer output
* @dtd: the DTD
*
* Dump the XML document DTD, if any.
*/
static void
xmlDtdDump(xmlBufferPtr buf, xmlDtdPtr dtd) {
if (dtd == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlDtdDump : no internal subset\n");
#endif
return;
}
xmlBufferWriteChar(buf, "<!DOCTYPE ");
xmlBufferWriteCHAR(buf, dtd->name);
if (dtd->ExternalID != NULL) {
xmlBufferWriteChar(buf, " PUBLIC ");
xmlBufferWriteQuotedString(buf, dtd->ExternalID);
xmlBufferWriteChar(buf, " ");
xmlBufferWriteQuotedString(buf, dtd->SystemID);
} else if (dtd->SystemID != NULL) {
xmlBufferWriteChar(buf, " SYSTEM ");
xmlBufferWriteQuotedString(buf, dtd->SystemID);
}
if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
(dtd->attributes == NULL) && (dtd->notations == NULL)) {
xmlBufferWriteChar(buf, ">");
return;
}
xmlBufferWriteChar(buf, " [\n");
xmlNodeListDump(buf, dtd->doc, dtd->children, -1, 0);
#if 0
if (dtd->entities != NULL)
xmlDumpEntitiesTable(buf, (xmlEntitiesTablePtr) dtd->entities);
if (dtd->notations != NULL)
xmlDumpNotationTable(buf, (xmlNotationTablePtr) dtd->notations);
if (dtd->elements != NULL)
xmlDumpElementTable(buf, (xmlElementTablePtr) dtd->elements);
if (dtd->attributes != NULL)
xmlDumpAttributeTable(buf, (xmlAttributeTablePtr) dtd->attributes);
#endif
xmlBufferWriteChar(buf, "]>");
}
/**
* xmlAttrSerializeContent:
* @buf: the XML buffer output
* @doc: the document
* @attr: the attribute pointer
*
* Serialize the attribute in the buffer
*/
static void
xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr) {
const xmlChar *cur, *base;
xmlNodePtr children;
children = attr->children;
while (children != NULL) {
switch (children->type) {
case XML_TEXT_NODE:
base = cur = children->content;
while (*cur != 0) {
if (*cur == '\n') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
cur++;
base = cur;
#if 0
} else if (*cur == '\'') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&apos;", 6);
cur++;
base = cur;
#endif
} else if (*cur == '"') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
cur++;
base = cur;
} else if (*cur == '<') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
cur++;
base = cur;
} else if (*cur == '>') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
cur++;
base = cur;
} else if (*cur == '&') {
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
cur++;
base = cur;
} else if ((*cur >= 0x80) && ((doc == NULL) ||
(doc->encoding == NULL))) {
/*
* We assume we have UTF-8 content.
*/
char tmp[10];
int val = 0, l = 1;
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
if (*cur < 0xC0) {
xmlGenericError(xmlGenericErrorContext,
"xmlAttrSerializeContent : input not UTF-8\n");
if (doc != NULL)
doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
tmp[sizeof(tmp) - 1] = 0;
xmlBufferAdd(buf, (xmlChar *) tmp, -1);
cur++;
base = cur;
continue;
} else if (*cur < 0xE0) {
val = (cur[0]) & 0x1F;
val <<= 6;
val |= (cur[1]) & 0x3F;
l = 2;
} else if (*cur < 0xF0) {
val = (cur[0]) & 0x0F;
val <<= 6;
val |= (cur[1]) & 0x3F;
val <<= 6;
val |= (cur[2]) & 0x3F;
l = 3;
} else if (*cur < 0xF8) {
val = (cur[0]) & 0x07;
val <<= 6;
val |= (cur[1]) & 0x3F;
val <<= 6;
val |= (cur[2]) & 0x3F;
val <<= 6;
val |= (cur[3]) & 0x3F;
l = 4;
}
if ((l == 1) || (!IS_CHAR(val))) {
xmlGenericError(xmlGenericErrorContext,
"xmlAttrSerializeContent : char out of range\n");
if (doc != NULL)
doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
tmp[sizeof(tmp) - 1] = 0;
xmlBufferAdd(buf, (xmlChar *) tmp, -1);
cur++;
base = cur;
continue;
}
/*
* We could do multiple things here. Just save
* as a char ref
*/
snprintf(tmp, sizeof(tmp), "&#x%X;", val);
tmp[sizeof(tmp) - 1] = 0;
xmlBufferAdd(buf, (xmlChar *) tmp, -1);
cur += l;
base = cur;
} else {
cur++;
}
}
if (base != cur)
xmlBufferAdd(buf, base, cur - base);
break;
case XML_ENTITY_REF_NODE:
xmlBufferAdd(buf, BAD_CAST "&", 1);
xmlBufferAdd(buf, children->name, xmlStrlen(children->name));
xmlBufferAdd(buf, BAD_CAST ";", 1);
break;
default:
/* should not happen unless we have a badly built tree */
break;
}
children = children->next;
}
}
/**
* xmlAttrDump:
* @buf: the XML buffer output
* @doc: the document
* @cur: the attribute pointer
*
* Dump an XML attribute
*/
static void
xmlAttrDump(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAttrDump : property == NULL\n");
#endif
return;
}
xmlBufferWriteChar(buf, " ");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlBufferWriteCHAR(buf, cur->ns->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, cur->name);
xmlBufferWriteChar(buf, "=\"");
xmlAttrSerializeContent(buf, doc, cur);
xmlBufferWriteChar(buf, "\"");
}
/**
* xmlAttrListDump:
* @buf: the XML buffer output
* @doc: the document
* @cur: the first attribute pointer
*
* Dump a list of XML attributes
*/
static void
xmlAttrListDump(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAttrListDump : property == NULL\n");
#endif
return;
}
while (cur != NULL) {
xmlAttrDump(buf, doc, cur);
cur = cur->next;
}
}
/**
* xmlNodeListDump:
* @buf: the XML buffer output
* @doc: the document
* @cur: the first node
* @level: the imbrication level for indenting
* @format: is formatting allowed
*
* Dump an XML node list, recursive behaviour,children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xmlNodeListDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
int format) {
int i;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeListDump : node == NULL\n");
#endif
return;
}
while (cur != NULL) {
if ((format) && (xmlIndentTreeOutput) &&
(cur->type == XML_ELEMENT_NODE))
for (i = 0;i < level;i++)
xmlBufferWriteChar(buf, xmlTreeIndentString);
xmlNodeDump(buf, doc, cur, level, format);
if (format) {
xmlBufferWriteChar(buf, "\n");
}
cur = cur->next;
}
}
/**
* xmlNodeDump:
* @buf: the XML buffer output
* @doc: the document
* @cur: the current node
* @level: the imbrication level for indenting
* @format: is formatting allowed
*
* Dump an XML node, recursive behaviour,children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
void
xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
int format) {
int i;
xmlNodePtr tmp;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeDump : node == NULL\n");
#endif
return;
}
if (cur->type == XML_XINCLUDE_START)
return;
if (cur->type == XML_XINCLUDE_END)
return;
if (cur->type == XML_DTD_NODE) {
xmlDtdDump(buf, (xmlDtdPtr) cur);
return;
}
if (cur->type == XML_ELEMENT_DECL) {
xmlDumpElementDecl(buf, (xmlElementPtr) cur);
return;
}
if (cur->type == XML_ATTRIBUTE_NODE){
xmlAttrDump(buf, doc, (xmlAttrPtr)cur);
return;
}
if (cur->type == XML_ATTRIBUTE_DECL) {
xmlDumpAttributeDecl(buf, (xmlAttributePtr) cur);
return;
}
if (cur->type == XML_ENTITY_DECL) {
xmlDumpEntityDecl(buf, (xmlEntityPtr) cur);
return;
}
if (cur->type == XML_TEXT_NODE) {
if (cur->content != NULL) {
if ((cur->name == xmlStringText) ||
(cur->name != xmlStringTextNoenc)) {
xmlChar *buffer;
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
if (buffer != NULL) {
xmlBufferWriteCHAR(buf, buffer);
xmlFree(buffer);
}
} else {
/*
* Disable escaping, needed for XSLT
*/
xmlBufferWriteCHAR(buf, cur->content);
}
}
return;
}
if (cur->type == XML_PI_NODE) {
xmlBufferWriteChar(buf, "<?");
xmlBufferWriteCHAR(buf, cur->name);
if (cur->content != NULL) {
xmlBufferWriteChar(buf, " ");
xmlBufferWriteCHAR(buf, cur->content);
}
xmlBufferWriteChar(buf, "?>");
return;
}
if (cur->type == XML_COMMENT_NODE) {
if (cur->content != NULL) {
xmlBufferWriteChar(buf, "<!--");
xmlBufferWriteCHAR(buf, cur->content);
xmlBufferWriteChar(buf, "-->");
}
return;
}
if (cur->type == XML_ENTITY_REF_NODE) {
xmlBufferWriteChar(buf, "&");
xmlBufferWriteCHAR(buf, cur->name);
xmlBufferWriteChar(buf, ";");
return;
}
if (cur->type == XML_CDATA_SECTION_NODE) {
xmlBufferWriteChar(buf, "<![CDATA[");
if (cur->content != NULL)
xmlBufferWriteCHAR(buf, cur->content);
xmlBufferWriteChar(buf, "]]>");
return;
}
if (format == 1) {
tmp = cur->children;
while (tmp != NULL) {
if ((tmp->type == XML_TEXT_NODE) ||
(tmp->type == XML_ENTITY_REF_NODE)) {
format = 0;
break;
}
tmp = tmp->next;
}
}
xmlBufferWriteChar(buf, "<");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlBufferWriteCHAR(buf, cur->ns->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, cur->name);
if (cur->nsDef)
xmlNsListDump(buf, cur->nsDef);
if (cur->properties != NULL)
xmlAttrListDump(buf, doc, cur->properties);
if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
(cur->children == NULL) &&
(!xmlSaveNoEmptyTags)) {
xmlBufferWriteChar(buf, "/>");
return;
}
xmlBufferWriteChar(buf, ">");
if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
xmlChar *buffer;
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
if (buffer != NULL) {
xmlBufferWriteCHAR(buf, buffer);
xmlFree(buffer);
}
}
if (cur->children != NULL) {
if (format) xmlBufferWriteChar(buf, "\n");
xmlNodeListDump(buf, doc, cur->children,
(level >= 0?level+1:-1), format);
if ((xmlIndentTreeOutput) && (format))
for (i = 0;i < level;i++)
xmlBufferWriteChar(buf, xmlTreeIndentString);
}
xmlBufferWriteChar(buf, "</");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlBufferWriteCHAR(buf, cur->ns->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, cur->name);
xmlBufferWriteChar(buf, ">");
}
/**
* xmlElemDump:
* @f: the FILE * for the output
* @doc: the document
* @cur: the current node
*
* Dump an XML/HTML node, recursive behaviour, children are printed too.
*/
void
xmlElemDump(FILE *f, xmlDocPtr doc, xmlNodePtr cur) {
xmlBufferPtr buf;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlElemDump : cur == NULL\n");
#endif
return;
}
#ifdef DEBUG_TREE
if (doc == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlElemDump : doc == NULL\n");
}
#endif
buf = xmlBufferCreate();
if (buf == NULL) return;
if ((doc != NULL) &&
(doc->type == XML_HTML_DOCUMENT_NODE)) {
#ifdef LIBXML_HTML_ENABLED
htmlNodeDump(buf, doc, cur);
#else
xmlGenericError(xmlGenericErrorContext,
"HTML support not compiled in\n");
#endif /* LIBXML_HTML_ENABLED */
} else
xmlNodeDump(buf, doc, cur, 0, 1);
xmlBufferDump(f, buf);
xmlBufferFree(buf);
}
/************************************************************************
* *
* Dumping XML tree content to an I/O output buffer *
* *
************************************************************************/
static void
xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
int level, int format, const char *encoding);
static void
xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
int level, int format, const char *encoding);
static void
xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlNodePtr cur, int level, int format, const char *encoding);
/**
* xmlNsDumpOutput:
* @buf: the XML buffer output
* @cur: a namespace
*
* Dump a local Namespace definition.
* Should be called in the context of attributes dumps.
*/
static void
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNsDumpOutput : Ns == NULL\n");
#endif
return;
}
if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
return;
/* Within the context of an element attributes */
if (cur->prefix != NULL) {
xmlOutputBufferWriteString(buf, " xmlns:");
xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
} else
xmlOutputBufferWriteString(buf, " xmlns");
xmlOutputBufferWriteString(buf, "=");
xmlBufferWriteQuotedString(buf->buffer, cur->href);
}
}
/**
* xmlNsListDumpOutput:
* @buf: the XML buffer output
* @cur: the first namespace
*
* Dump a list of local Namespace definitions.
* Should be called in the context of attributes dumps.
*/
static void
xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
while (cur != NULL) {
xmlNsDumpOutput(buf, cur);
cur = cur->next;
}
}
/**
* xmlDtdDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @encoding: an optional encoding string
*
* Dump the XML document DTD, if any.
*/
static void
xmlDtdDumpOutput(xmlOutputBufferPtr buf, xmlDtdPtr dtd, const char *encoding) {
if (dtd == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlDtdDumpOutput : no internal subset\n");
#endif
return;
}
xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
xmlOutputBufferWriteString(buf, (const char *)dtd->name);
if (dtd->ExternalID != NULL) {
xmlOutputBufferWriteString(buf, " PUBLIC ");
xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
xmlOutputBufferWriteString(buf, " ");
xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
} else if (dtd->SystemID != NULL) {
xmlOutputBufferWriteString(buf, " SYSTEM ");
xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
}
if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
(dtd->attributes == NULL) && (dtd->notations == NULL)) {
xmlOutputBufferWriteString(buf, ">");
return;
}
xmlOutputBufferWriteString(buf, " [\n");
xmlNodeListDumpOutput(buf, dtd->doc, dtd->children, -1, 0, encoding);
xmlOutputBufferWriteString(buf, "]>");
}
/**
* xmlAttrDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @cur: the attribute pointer
* @encoding: an optional encoding string
*
* Dump an XML attribute
*/
static void
xmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
const char *encoding ATTRIBUTE_UNUSED) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAttrDumpOutput : property == NULL\n");
#endif
return;
}
xmlOutputBufferWriteString(buf, " ");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, "=\"");
xmlAttrSerializeContent(buf->buffer, doc, cur);
xmlOutputBufferWriteString(buf, "\"");
}
/**
* xmlAttrListDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @cur: the first attribute pointer
* @encoding: an optional encoding string
*
* Dump a list of XML attributes
*/
static void
xmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlAttrPtr cur, const char *encoding) {
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAttrListDumpOutput : property == NULL\n");
#endif
return;
}
while (cur != NULL) {
xmlAttrDumpOutput(buf, doc, cur, encoding);
cur = cur->next;
}
}
/**
* xmlNodeListDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @cur: the first node
* @level: the imbrication level for indenting
* @format: is formatting allowed
* @encoding: an optional encoding string
*
* Dump an XML node list, recursive behaviour, children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlNodePtr cur, int level, int format, const char *encoding) {
int i;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeListDumpOutput : node == NULL\n");
#endif
return;
}
while (cur != NULL) {
if ((format) && (xmlIndentTreeOutput) &&
(cur->type == XML_ELEMENT_NODE))
for (i = 0;i < level;i++)
xmlOutputBufferWriteString(buf, xmlTreeIndentString);
xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
if (format) {
xmlOutputBufferWriteString(buf, "\n");
}
cur = cur->next;
}
}
/**
* xmlNodeDumpOutputInternal:
* @buf: the XML buffer output
* @doc: the document
* @cur: the current node
* @level: the imbrication level for indenting
* @format: is formatting allowed
* @encoding: an optional encoding string
*
* Dump an XML node, recursive behaviour, children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlNodePtr cur, int level, int format, const char *encoding) {
int i;
xmlNodePtr tmp;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeDumpOutput : node == NULL\n");
#endif
return;
}
if (cur->type == XML_XINCLUDE_START)
return;
if (cur->type == XML_XINCLUDE_END)
return;
if (cur->type == XML_DTD_NODE) {
xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
return;
}
if (cur->type == XML_ELEMENT_DECL) {
xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
return;
}
if (cur->type == XML_ATTRIBUTE_DECL) {
xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
return;
}
if (cur->type == XML_ENTITY_DECL) {
xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
return;
}
if (cur->type == XML_TEXT_NODE) {
if (cur->content != NULL) {
if ((cur->name == xmlStringText) ||
(cur->name != xmlStringTextNoenc)) {
xmlChar *buffer;
if (encoding == NULL)
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
else
buffer = xmlEncodeSpecialChars(doc, cur->content);
if (buffer != NULL) {
xmlOutputBufferWriteString(buf, (const char *)buffer);
xmlFree(buffer);
}
} else {
/*
* Disable escaping, needed for XSLT
*/
xmlOutputBufferWriteString(buf, (const char *) cur->content);
}
}
return;
}
if (cur->type == XML_PI_NODE) {
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, "<?");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, " ");
xmlOutputBufferWriteString(buf, (const char *)cur->content);
}
xmlOutputBufferWriteString(buf, "?>");
} else {
xmlOutputBufferWriteString(buf, "<?");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, "?>");
}
return;
}
if (cur->type == XML_COMMENT_NODE) {
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, "<!--");
xmlOutputBufferWriteString(buf, (const char *)cur->content);
xmlOutputBufferWriteString(buf, "-->");
}
return;
}
if (cur->type == XML_ENTITY_REF_NODE) {
xmlOutputBufferWriteString(buf, "&");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, ";");
return;
}
if (cur->type == XML_CDATA_SECTION_NODE) {
xmlOutputBufferWriteString(buf, "<![CDATA[");
if (cur->content != NULL)
xmlOutputBufferWriteString(buf, (const char *)cur->content);
xmlOutputBufferWriteString(buf, "]]>");
return;
}
if (format == 1) {
tmp = cur->children;
while (tmp != NULL) {
if ((tmp->type == XML_TEXT_NODE) ||
(tmp->type == XML_ENTITY_REF_NODE)) {
format = 0;
break;
}
tmp = tmp->next;
}
}
xmlOutputBufferWriteString(buf, "<");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
if (cur->nsDef)
xmlNsListDumpOutput(buf, cur->nsDef);
if (cur->properties != NULL)
xmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
(cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
xmlOutputBufferWriteString(buf, "/>");
return;
}
xmlOutputBufferWriteString(buf, ">");
if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
xmlChar *buffer;
if (encoding == NULL)
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
else
buffer = xmlEncodeSpecialChars(doc, cur->content);
if (buffer != NULL) {
xmlOutputBufferWriteString(buf, (const char *)buffer);
xmlFree(buffer);
}
}
if (cur->children != NULL) {
if (format) xmlOutputBufferWriteString(buf, "\n");
xmlNodeListDumpOutput(buf, doc, cur->children,
(level >= 0?level+1:-1), format, encoding);
if ((xmlIndentTreeOutput) && (format))
for (i = 0;i < level;i++)
xmlOutputBufferWriteString(buf, xmlTreeIndentString);
}
xmlOutputBufferWriteString(buf, "</");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, ">");
}
/**
* xmlNodeDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @cur: the current node
* @level: the imbrication level for indenting
* @format: is formatting allowed
* @encoding: an optional encoding string
*
* Dump an XML node, recursive behaviour, children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
void
xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
int level, int format, const char *encoding) {
#ifdef LIBXML_HTML_ENABLED
xmlDtdPtr dtd;
int is_xhtml = 0;
dtd = xmlGetIntSubset(doc);
if (dtd != NULL) {
is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
if (is_xhtml < 0) is_xhtml = 0;
if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
(cur->type == XML_ELEMENT_NODE) &&
(xmlStrEqual(cur->name, BAD_CAST "html"))) {
if (encoding != NULL)
htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
else
htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
}
}
if (is_xhtml)
xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
else
#endif
xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
}
/**
* xmlDocContentDumpOutput:
* @buf: the XML buffer output
* @cur: the document
* @encoding: an optional encoding string
* @format: should formatting spaces been added
*
* Dump an XML document.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
const char *encoding, int format) {
#ifdef LIBXML_HTML_ENABLED
xmlDtdPtr dtd;
int is_xhtml = 0;
#endif
xmlOutputBufferWriteString(buf, "<?xml version=");
if (cur->version != NULL)
xmlBufferWriteQuotedString(buf->buffer, cur->version);
else
xmlOutputBufferWriteString(buf, "\"1.0\"");
if (encoding == NULL) {
if (cur->encoding != NULL)
encoding = (const char *) cur->encoding;
else if (cur->charset != XML_CHAR_ENCODING_UTF8)
encoding = xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
}
if (encoding != NULL) {
xmlOutputBufferWriteString(buf, " encoding=");
xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
}
switch (cur->standalone) {
case 0:
xmlOutputBufferWriteString(buf, " standalone=\"no\"");
break;
case 1:
xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
break;
}
xmlOutputBufferWriteString(buf, "?>\n");
#ifdef LIBXML_HTML_ENABLED
dtd = xmlGetIntSubset(cur);
if (dtd != NULL) {
is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
if (is_xhtml < 0) is_xhtml = 0;
}
if (is_xhtml) {
if (encoding != NULL)
htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
else
htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
}
#endif
if (cur->children != NULL) {
xmlNodePtr child = cur->children;
while (child != NULL) {
#ifdef LIBXML_HTML_ENABLED
if (is_xhtml)
xhtmlNodeDumpOutput(buf, cur, child, 0, format, encoding);
else
#endif
xmlNodeDumpOutputInternal(buf, cur, child, 0, format, encoding);
xmlOutputBufferWriteString(buf, "\n");
child = child->next;
}
}
}
#ifdef LIBXML_HTML_ENABLED
/************************************************************************
* *
* Functions specific to XHTML serialization *
* *
************************************************************************/
#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
"-//W3C//DTD XHTML 1.0 Strict//EN"
#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
"-//W3C//DTD XHTML 1.0 Frameset//EN"
#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
"-//W3C//DTD XHTML 1.0 Transitional//EN"
#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
/**
* xmlIsXHTML:
* @systemID: the system identifier
* @publicID: the public identifier
*
* Try to find if the document correspond to an XHTML DTD
*
* Returns 1 if true, 0 if not and -1 in case of error
*/
int
xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
if ((systemID == NULL) && (publicID == NULL))
return(-1);
if (publicID != NULL) {
if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
}
if (systemID != NULL) {
if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
}
return(0);
}
/**
* xhtmlIsEmpty:
* @node: the node
*
* Check if a node is an empty xhtml node
*
* Returns 1 if the node is an empty node, 0 if not and -1 in case of error
*/
static int
xhtmlIsEmpty(xmlNodePtr node) {
if (node == NULL)
return(-1);
if (node->type != XML_ELEMENT_NODE)
return(0);
if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
return(0);
if (node->children != NULL)
return(0);
switch (node->name[0]) {
case 'a':
if (xmlStrEqual(node->name, BAD_CAST "area"))
return(1);
return(0);
case 'b':
if (xmlStrEqual(node->name, BAD_CAST "br"))
return(1);
if (xmlStrEqual(node->name, BAD_CAST "base"))
return(1);
if (xmlStrEqual(node->name, BAD_CAST "basefont"))
return(1);
return(0);
case 'c':
if (xmlStrEqual(node->name, BAD_CAST "col"))
return(1);
return(0);
case 'f':
if (xmlStrEqual(node->name, BAD_CAST "frame"))
return(1);
return(0);
case 'h':
if (xmlStrEqual(node->name, BAD_CAST "hr"))
return(1);
return(0);
case 'i':
if (xmlStrEqual(node->name, BAD_CAST "img"))
return(1);
if (xmlStrEqual(node->name, BAD_CAST "input"))
return(1);
if (xmlStrEqual(node->name, BAD_CAST "isindex"))
return(1);
return(0);
case 'l':
if (xmlStrEqual(node->name, BAD_CAST "link"))
return(1);
return(0);
case 'm':
if (xmlStrEqual(node->name, BAD_CAST "meta"))
return(1);
return(0);
case 'p':
if (xmlStrEqual(node->name, BAD_CAST "param"))
return(1);
return(0);
}
return(0);
}
/**
* xhtmlAttrListDumpOutput:
* @buf: the XML buffer output
* @doc: the document
* @cur: the first attribute pointer
* @encoding: an optional encoding string
*
* Dump a list of XML attributes
*/
static void
xhtmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlAttrPtr cur, const char *encoding) {
xmlAttrPtr xml_lang = NULL;
xmlAttrPtr lang = NULL;
xmlAttrPtr name = NULL;
xmlAttrPtr id = NULL;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAttrListDumpOutput : property == NULL\n");
#endif
return;
}
while (cur != NULL) {
if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
id = cur;
else
if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
name = cur;
else
if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
lang = cur;
else
if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
(xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
xml_lang = cur;
else if ((cur->ns == NULL) &&
((cur->children == NULL) ||
(cur->children->content == NULL) ||
(cur->children->content[0] == 0)) &&
(htmlIsBooleanAttr(cur->name))) {
if (cur->children != NULL)
xmlFreeNode(cur->children);
cur->children = xmlNewText(cur->name);
if (cur->children != NULL)
cur->children->parent = (xmlNodePtr) cur;
}
xmlAttrDumpOutput(buf, doc, cur, encoding);
cur = cur->next;
}
/*
* C.8
*/
if ((name != NULL) && (id == NULL)) {
xmlOutputBufferWriteString(buf, " id=\"");
xmlAttrSerializeContent(buf->buffer, doc, name);
xmlOutputBufferWriteString(buf, "\"");
}
/*
* C.7.
*/
if ((lang != NULL) && (xml_lang == NULL)) {
xmlOutputBufferWriteString(buf, " xml:lang=\"");
xmlAttrSerializeContent(buf->buffer, doc, lang);
xmlOutputBufferWriteString(buf, "\"");
} else
if ((xml_lang != NULL) && (lang == NULL)) {
xmlOutputBufferWriteString(buf, " lang=\"");
xmlAttrSerializeContent(buf->buffer, doc, xml_lang);
xmlOutputBufferWriteString(buf, "\"");
}
}
/**
* xhtmlNodeListDumpOutput:
* @buf: the XML buffer output
* @doc: the XHTML document
* @cur: the first node
* @level: the imbrication level for indenting
* @format: is formatting allowed
* @encoding: an optional encoding string
*
* Dump an XML node list, recursive behaviour, children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xhtmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
xmlNodePtr cur, int level, int format, const char *encoding) {
int i;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xhtmlNodeListDumpOutput : node == NULL\n");
#endif
return;
}
while (cur != NULL) {
if ((format) && (xmlIndentTreeOutput) &&
(cur->type == XML_ELEMENT_NODE))
for (i = 0;i < level;i++)
xmlOutputBufferWriteString(buf, xmlTreeIndentString);
xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
if (format) {
xmlOutputBufferWriteString(buf, "\n");
}
cur = cur->next;
}
}
/**
* xhtmlNodeDumpOutput:
* @buf: the XML buffer output
* @doc: the XHTML document
* @cur: the current node
* @level: the imbrication level for indenting
* @format: is formatting allowed
* @encoding: an optional encoding string
*
* Dump an XHTML node, recursive behaviour, children are printed too.
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
static void
xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
int level, int format, const char *encoding) {
int i;
xmlNodePtr tmp;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNodeDumpOutput : node == NULL\n");
#endif
return;
}
if (cur->type == XML_XINCLUDE_START)
return;
if (cur->type == XML_XINCLUDE_END)
return;
if (cur->type == XML_DTD_NODE) {
xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
return;
}
if (cur->type == XML_ELEMENT_DECL) {
xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
return;
}
if (cur->type == XML_ATTRIBUTE_DECL) {
xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
return;
}
if (cur->type == XML_ENTITY_DECL) {
xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
return;
}
if (cur->type == XML_TEXT_NODE) {
if (cur->content != NULL) {
if ((cur->name == xmlStringText) ||
(cur->name != xmlStringTextNoenc)) {
xmlChar *buffer;
if (encoding == NULL)
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
else
buffer = xmlEncodeSpecialChars(doc, cur->content);
if (buffer != NULL) {
xmlOutputBufferWriteString(buf, (const char *)buffer);
xmlFree(buffer);
}
} else {
/*
* Disable escaping, needed for XSLT
*/
xmlOutputBufferWriteString(buf, (const char *) cur->content);
}
}
return;
}
if (cur->type == XML_PI_NODE) {
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, "<?");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, " ");
xmlOutputBufferWriteString(buf, (const char *)cur->content);
}
xmlOutputBufferWriteString(buf, "?>");
} else {
xmlOutputBufferWriteString(buf, "<?");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, "?>");
}
return;
}
if (cur->type == XML_COMMENT_NODE) {
if (cur->content != NULL) {
xmlOutputBufferWriteString(buf, "<!--");
xmlOutputBufferWriteString(buf, (const char *)cur->content);
xmlOutputBufferWriteString(buf, "-->");
}
return;
}
if (cur->type == XML_ENTITY_REF_NODE) {
xmlOutputBufferWriteString(buf, "&");
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, ";");
return;
}
if (cur->type == XML_CDATA_SECTION_NODE) {
xmlOutputBufferWriteString(buf, "<![CDATA[");
if (cur->content != NULL)
xmlOutputBufferWriteString(buf, (const char *)cur->content);
xmlOutputBufferWriteString(buf, "]]>");
return;
}
if (format == 1) {
tmp = cur->children;
while (tmp != NULL) {
if ((tmp->type == XML_TEXT_NODE) ||
(tmp->type == XML_ENTITY_REF_NODE)) {
format = 0;
break;
}
tmp = tmp->next;
}
}
xmlOutputBufferWriteString(buf, "<");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
if (cur->nsDef)
xmlNsListDumpOutput(buf, cur->nsDef);
if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
(cur->ns == NULL) && (cur->nsDef == NULL))) {
/*
* 3.1.1. Strictly Conforming Documents A.3.1.1 3/
*/
xmlOutputBufferWriteString(buf,
" xmlns=\"http://www.w3.org/1999/xhtml\"");
}
if (cur->properties != NULL)
xhtmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
(xhtmlIsEmpty(cur) == 1)) {
/*
* C.2. Empty Elements
*/
xmlOutputBufferWriteString(buf, " />");
} else {
/*
* C.3. Element Minimization and Empty Element Content
*/
xmlOutputBufferWriteString(buf, "></");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, ">");
}
return;
}
xmlOutputBufferWriteString(buf, ">");
if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
xmlChar *buffer;
if (encoding == NULL)
buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
else
buffer = xmlEncodeSpecialChars(doc, cur->content);
if (buffer != NULL) {
xmlOutputBufferWriteString(buf, (const char *)buffer);
xmlFree(buffer);
}
}
/*
* 4.8. Script and Style elements
*/
if ((cur->type == XML_ELEMENT_NODE) &&
((xmlStrEqual(cur->name, BAD_CAST "script")) ||
(xmlStrEqual(cur->name, BAD_CAST "style"))) &&
((cur->ns == NULL) ||
(xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
xmlNodePtr child = cur->children;
while (child != NULL) {
if ((child->type == XML_TEXT_NODE) ||
(child->type == XML_CDATA_SECTION_NODE)) {
xmlOutputBufferWriteString(buf, "<![CDATA[");
if (child->content != NULL)
xmlOutputBufferWriteString(buf,
(const char *)child->content);
xmlOutputBufferWriteString(buf, "]]>");
} else {
xhtmlNodeDumpOutput(buf, doc, child, 0, 0, encoding);
}
child = child->next;
}
} else if (cur->children != NULL) {
if (format) xmlOutputBufferWriteString(buf, "\n");
xhtmlNodeListDumpOutput(buf, doc, cur->children,
(level >= 0?level+1:-1), format, encoding);
if ((xmlIndentTreeOutput) && (format))
for (i = 0;i < level;i++)
xmlOutputBufferWriteString(buf, xmlTreeIndentString);
}
xmlOutputBufferWriteString(buf, "</");
if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
xmlOutputBufferWriteString(buf, ":");
}
xmlOutputBufferWriteString(buf, (const char *)cur->name);
xmlOutputBufferWriteString(buf, ">");
}
#endif
/************************************************************************
* *
* Saving functions front-ends *
* *
************************************************************************/
/**
* xmlDocDumpFormatMemoryEnc:
* @out_doc: Document to generate XML text from
* @doc_txt_ptr: Memory pointer for allocated XML text
* @doc_txt_len: Length of the generated XML text
* @txt_encoding: Character encoding to use when generating XML text
* @format: should formatting spaces been added
*
* Dump the current DOM tree into memory using the character encoding specified
* by the caller. Note it is up to the caller of this function to free the
* allocated memory with xmlFree().
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
int * doc_txt_len, const char * txt_encoding,
int format) {
int dummy = 0;
xmlCharEncoding doc_charset;
xmlOutputBufferPtr out_buff = NULL;
xmlCharEncodingHandlerPtr conv_hdlr = NULL;
if (doc_txt_len == NULL) {
doc_txt_len = &dummy; /* Continue, caller just won't get length */
}
if (doc_txt_ptr == NULL) {
*doc_txt_len = 0;
xmlGenericError(xmlGenericErrorContext,
"xmlDocDumpFormatMemoryEnc: Null return buffer pointer.");
return;
}
*doc_txt_ptr = NULL;
*doc_txt_len = 0;
if (out_doc == NULL) {
/* No document, no output */
xmlGenericError(xmlGenericErrorContext,
"xmlDocDumpFormatMemoryEnc: Null DOM tree document pointer.\n");
return;
}
/*
* Validate the encoding value, if provided.
* This logic is copied from xmlSaveFileEnc.
*/
if (txt_encoding == NULL)
txt_encoding = (const char *) out_doc->encoding;
if (txt_encoding != NULL) {
doc_charset = xmlParseCharEncoding(txt_encoding);
if (out_doc->charset != XML_CHAR_ENCODING_UTF8) {
xmlGenericError(xmlGenericErrorContext,
"xmlDocDumpFormatMemoryEnc: Source document not in UTF8\n");
return;
} else if (doc_charset != XML_CHAR_ENCODING_UTF8) {
conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
if ( conv_hdlr == NULL ) {
xmlGenericError(xmlGenericErrorContext,
"%s: %s %s '%s'\n",
"xmlDocDumpFormatMemoryEnc",
"Failed to identify encoding handler for",
"character set",
txt_encoding);
return;
}
}
}
if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
xmlGenericError(xmlGenericErrorContext,
"xmlDocDumpFormatMemoryEnc: Failed to allocate output buffer.\n");
return;
}
xmlDocContentDumpOutput(out_buff, out_doc, txt_encoding, format);
xmlOutputBufferFlush(out_buff);
if (out_buff->conv != NULL) {
*doc_txt_len = out_buff->conv->use;
*doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
} else {
*doc_txt_len = out_buff->buffer->use;
*doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
}
(void)xmlOutputBufferClose(out_buff);
if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
*doc_txt_len = 0;
xmlGenericError(xmlGenericErrorContext,
"xmlDocDumpFormatMemoryEnc: %s\n",
"Failed to allocate memory for document text representation.");
}
return;
}
/**
* xmlDocDumpMemory:
* @cur: the document
* @mem: OUT: the memory pointer
* @size: OUT: the memory length
*
* Dump an XML document in memory and return the #xmlChar * and it's size.
* It's up to the caller to free the memory with xmlFree().
*/
void
xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
}
/**
* xmlDocDumpFormatMemory:
* @cur: the document
* @mem: OUT: the memory pointer
* @size: OUT: the memory length
* @format: should formatting spaces been added
*
*
* Dump an XML document in memory and return the #xmlChar * and it's size.
* It's up to the caller to free the memory with xmlFree().
* Note that format = 1 provide node indenting only if xmlIndentTreeOutput = 1
* or xmlKeepBlanksDefault(0) was called
*/
void
xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
}
/**
* xmlDocDumpMemoryEnc:
* @out_doc: Document to generate XML text from
* @doc_txt_ptr: Memory pointer for allocated XML text
* @doc_txt_len: Length of the generated XML text
* @txt_encoding: Character encoding to use when generating XML text
*
* Dump the current DOM tree into memory using the character encoding specified
* by the caller. Note it is up to the caller of this function to free the
* allocated memory with xmlFree().
*/
void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
int * doc_txt_len, const char * txt_encoding) {
xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
txt_encoding, 0);
}
/**
* xmlGetDocCompressMode:
* @doc: the document
*
* get the compression ratio for a document, ZLIB based
* Returns 0 (uncompressed) to 9 (max compression)
*/
int
xmlGetDocCompressMode (xmlDocPtr doc) {
if (doc == NULL) return(-1);
return(doc->compression);
}
/**
* xmlSetDocCompressMode:
* @doc: the document
* @mode: the compression ratio
*
* set the compression ratio for a document, ZLIB based
* Correct values: 0 (uncompressed) to 9 (max compression)
*/
void
xmlSetDocCompressMode (xmlDocPtr doc, int mode) {
if (doc == NULL) return;
if (mode < 0) doc->compression = 0;
else if (mode > 9) doc->compression = 9;
else doc->compression = mode;
}
/**
* xmlGetCompressMode:
*
* get the default compression mode used, ZLIB based.
* Returns 0 (uncompressed) to 9 (max compression)
*/
int
xmlGetCompressMode(void)
{
return (xmlCompressMode);
}
/**
* xmlSetCompressMode:
* @mode: the compression ratio
*
* set the default compression mode used, ZLIB based
* Correct values: 0 (uncompressed) to 9 (max compression)
*/
void
xmlSetCompressMode(int mode) {
if (mode < 0) xmlCompressMode = 0;
else if (mode > 9) xmlCompressMode = 9;
else xmlCompressMode = mode;
}
/**
* xmlDocFormatDump:
* @f: the FILE*
* @cur: the document
* @format: should formatting spaces been added
*
* Dump an XML document to an open FILE.
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
xmlOutputBufferPtr buf;
const char * encoding;
xmlCharEncodingHandlerPtr handler = NULL;
int ret;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlDocDump : document == NULL\n");
#endif
return(-1);
}
encoding = (const char *) cur->encoding;
if (encoding != NULL) {
xmlCharEncoding enc;
enc = xmlParseCharEncoding(encoding);
if (cur->charset != XML_CHAR_ENCODING_UTF8) {
xmlGenericError(xmlGenericErrorContext,
"xmlDocDump: document not in UTF8\n");
return(-1);
}
if (enc != XML_CHAR_ENCODING_UTF8) {
handler = xmlFindCharEncodingHandler(encoding);
if (handler == NULL) {
xmlFree((char *) cur->encoding);
cur->encoding = NULL;
}
}
}
buf = xmlOutputBufferCreateFile(f, handler);
if (buf == NULL) return(-1);
xmlDocContentDumpOutput(buf, cur, NULL, format);
ret = xmlOutputBufferClose(buf);
return(ret);
}
/**
* xmlDocDump:
* @f: the FILE*
* @cur: the document
*
* Dump an XML document to an open FILE.
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlDocDump(FILE *f, xmlDocPtr cur) {
return(xmlDocFormatDump (f, cur, 0));
}
/**
* xmlSaveFileTo:
* @buf: an output I/O buffer
* @cur: the document
* @encoding: the encoding if any assuming the I/O layer handles the trancoding
*
* Dump an XML document to an I/O buffer.
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
int ret;
if (buf == NULL) return(0);
xmlDocContentDumpOutput(buf, cur, encoding, 0);
ret = xmlOutputBufferClose(buf);
return(ret);
}
/**
* xmlSaveFormatFileTo:
* @buf: an output I/O buffer
* @cur: the document
* @encoding: the encoding if any assuming the I/O layer handles the trancoding
* @format: should formatting spaces been added
*
* Dump an XML document to an I/O buffer.
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding, int format) {
int ret;
if (buf == NULL) return(0);
xmlDocContentDumpOutput(buf, cur, encoding, format);
ret = xmlOutputBufferClose(buf);
return(ret);
}
/**
* xmlSaveFormatFileEnc
* @filename: the filename or URL to output
* @cur: the document being saved
* @encoding: the name of the encoding to use or NULL.
* @format: should formatting spaces be added.
*
* Returns the number of bytes written or -1 in case of error.
*/
int
xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
const char * encoding, int format ) {
xmlOutputBufferPtr buf;
xmlCharEncodingHandlerPtr handler = NULL;
xmlCharEncoding enc;
int ret;
if (encoding == NULL)
encoding = (const char *) cur->encoding;
if (encoding != NULL) {
enc = xmlParseCharEncoding(encoding);
if (cur->charset != XML_CHAR_ENCODING_UTF8) {
xmlGenericError(xmlGenericErrorContext,
"xmlSaveFormatFileEnc: document not in UTF8\n");
return(-1);
}
if (enc != XML_CHAR_ENCODING_UTF8) {
handler = xmlFindCharEncodingHandler(encoding);
if (handler == NULL)
return(-1);
}
}
#ifdef HAVE_ZLIB_H
if (cur->compression < 0) cur->compression = xmlCompressMode;
#endif
/*
* save the content to a temp buffer.
*/
buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
if (buf == NULL) return(-1);
xmlDocContentDumpOutput(buf, cur, encoding, format);
ret = xmlOutputBufferClose(buf);
return(ret);
}
/**
* xmlSaveFileEnc:
* @filename: the filename (or URL)
* @cur: the document
* @encoding: the name of an encoding (or NULL)
*
* Dump an XML document, converting it to the given encoding
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
}
/**
* xmlSaveFormatFile:
* @filename: the filename (or URL)
* @cur: the document
* @format: should formatting spaces been added
*
* Dump an XML document to a file. Will use compression if
* compiled in and enabled. If @filename is "-" the stdout file is
* used. If @format is set then the document will be indented on output.
*
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
}
/**
* xmlSaveFile:
* @filename: the filename (or URL)
* @cur: the document
*
* Dump an XML document to a file. Will use compression if
* compiled in and enabled. If @filename is "-" the stdout file is
* used.
* returns: the number of bytes written or -1 in case of failure.
*/
int
xmlSaveFile(const char *filename, xmlDocPtr cur) {
return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
}