blob: c707f598e8d5b33de934757fada92ff1c6c57000 [file] [log] [blame]
/*
* 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
*
*/
/* To avoid EBCDIC trouble when parsing on zOS */
#if defined(__MVS__)
#pragma convert("ISO8859-1")
#endif
#define IN_LIBXML
#include "libxml.h"
#include <string.h> /* for memset() only ! */
#include <stddef.h>
#include <limits.h>
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef LIBXML_ZLIB_ENABLED
#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
#ifdef LIBXML_DEBUG_ENABLED
#include <libxml/debugXML.h>
#endif
#include "buf.h"
#include "save.h"
int __xmlRegisterCallbacks = 0;
/************************************************************************
* *
* Forward declarations *
* *
************************************************************************/
static xmlNsPtr
xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns);
static xmlChar* xmlGetPropNodeValueInternal(const xmlAttr *prop);
/************************************************************************
* *
* Tree memory error handler *
* *
************************************************************************/
/**
* xmlTreeErrMemory:
* @extra: extra information
*
* Handle an out of memory condition
*/
static void
xmlTreeErrMemory(const char *extra)
{
__xmlSimpleError(XML_FROM_TREE, XML_ERR_NO_MEMORY, NULL, NULL, extra);
}
/**
* xmlTreeErr:
* @code: the error number
* @extra: extra information
*
* Handle an out of memory condition
*/
static void
xmlTreeErr(int code, xmlNodePtr node, const char *extra)
{
const char *msg = NULL;
switch(code) {
case XML_TREE_INVALID_HEX:
msg = "invalid hexadecimal character value\n";
break;
case XML_TREE_INVALID_DEC:
msg = "invalid decimal character value\n";
break;
case XML_TREE_UNTERMINATED_ENTITY:
msg = "unterminated entity reference %15s\n";
break;
case XML_TREE_NOT_UTF8:
msg = "string is not in UTF-8\n";
break;
default:
msg = "unexpected error number\n";
}
__xmlSimpleError(XML_FROM_TREE, code, node, msg, extra);
}
/************************************************************************
* *
* 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 IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \
(str[1] == 'm') && (str[2] == 'l') && (str[3] == 0))
/* #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>
#ifdef LIBXML_TREE_ENABLED
/**
* 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(const xmlDtd *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 parameter 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(const xmlDtd *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);
}
#endif /* LIBXML_TREE_ENABLED */
/************************************************************************
* *
* QName handling helper *
* *
************************************************************************/
/**
* xmlBuildQName:
* @ncname: the Name
* @prefix: the prefix
* @memory: preallocated memory
* @len: preallocated memory length
*
* Builds the QName @prefix:@ncname in @memory if there is enough space
* and prefix is not NULL nor empty, otherwise allocate a new string.
* If prefix is NULL or empty it returns ncname.
*
* Returns the new string which must be freed by the caller if different from
* @memory and @ncname or NULL in case of error
*/
xmlChar *
xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix,
xmlChar *memory, int len) {
int lenn, lenp;
xmlChar *ret;
if (ncname == NULL) return(NULL);
if (prefix == NULL) return((xmlChar *) ncname);
lenn = strlen((char *) ncname);
lenp = strlen((char *) prefix);
if ((memory == NULL) || (len < lenn + lenp + 2)) {
ret = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2);
if (ret == NULL) {
xmlTreeErrMemory("building QName");
return(NULL);
}
} else {
ret = memory;
}
memcpy(&ret[0], prefix, lenp);
ret[lenp] = ':';
memcpy(&ret[lenp + 1], ncname, lenn);
ret[lenn + lenp + 1] = 0;
return(ret);
}
/**
* xmlSplitQName2:
* @name: the full QName
* @prefix: a xmlChar **
*
* parse an XML qualified name string
*
* [NS 5] QName ::= (Prefix ':')? LocalPart
*
* [NS 6] Prefix ::= NCName
*
* [NS 7] LocalPart ::= NCName
*
* Returns NULL if the name doesn't have a prefix. Otherwise, returns the
* local part, and prefix is updated to get the Prefix. Both the return value
* and the prefix must be freed by the caller.
*/
xmlChar *
xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
int len = 0;
xmlChar *ret = NULL;
if (prefix == NULL) return(NULL);
*prefix = NULL;
if (name == NULL) return(NULL);
#ifndef XML_XML_NAMESPACE
/* xml: prefix is not really a namespace */
if ((name[0] == 'x') && (name[1] == 'm') &&
(name[2] == 'l') && (name[3] == ':'))
return(NULL);
#endif
/* nasty but valid */
if (name[0] == ':')
return(NULL);
/*
* we are not trying to validate but just to cut, and yes it will
* work even if this is as set of UTF-8 encoded chars
*/
while ((name[len] != 0) && (name[len] != ':'))
len++;
if (name[len] == 0)
return(NULL);
*prefix = xmlStrndup(name, len);
if (*prefix == NULL) {
xmlTreeErrMemory("QName split");
return(NULL);
}
ret = xmlStrdup(&name[len + 1]);
if (ret == NULL) {
xmlTreeErrMemory("QName split");
if (*prefix != NULL) {
xmlFree(*prefix);
*prefix = NULL;
}
return(NULL);
}
return(ret);
}
/**
* xmlSplitQName3:
* @name: the full QName
* @len: an int *
*
* parse an XML qualified name string,i
*
* returns NULL if it is not a Qualified Name, otherwise, update len
* with the length in byte of the prefix and return a pointer
* to the start of the name without the prefix
*/
const xmlChar *
xmlSplitQName3(const xmlChar *name, int *len) {
int l = 0;
if (name == NULL) return(NULL);
if (len == NULL) return(NULL);
/* nasty but valid */
if (name[0] == ':')
return(NULL);
/*
* we are not trying to validate but just to cut, and yes it will
* work even if this is as set of UTF-8 encoded chars
*/
while ((name[l] != 0) && (name[l] != ':'))
l++;
if (name[l] == 0)
return(NULL);
*len = l;
return(&name[l+1]);
}
/************************************************************************
* *
* Check Name, NCName and QName strings *
* *
************************************************************************/
#define CUR_SCHAR(s, l) xmlStringCurrentChar(NULL, s, &l)
#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_DEBUG_ENABLED) || defined (LIBXML_HTML_ENABLED) || defined(LIBXML_SAX1_ENABLED) || defined(LIBXML_HTML_ENABLED) || defined(LIBXML_WRITER_ENABLED) || defined(LIBXML_DOCB_ENABLED) || defined(LIBXML_LEGACY_ENABLED)
/**
* xmlValidateNCName:
* @value: the value to check
* @space: allow spaces in front and end of the string
*
* Check that a value conforms to the lexical space of NCName
*
* Returns 0 if this validates, a positive error code number otherwise
* and -1 in case of internal or API error.
*/
int
xmlValidateNCName(const xmlChar *value, int space) {
const xmlChar *cur = value;
int c,l;
if (value == NULL)
return(-1);
/*
* First quick algorithm for ASCII range
*/
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (((*cur >= 'a') && (*cur <= 'z')) || ((*cur >= 'A') && (*cur <= 'Z')) ||
(*cur == '_'))
cur++;
else
goto try_complex;
while (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.'))
cur++;
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (*cur == 0)
return(0);
try_complex:
/*
* Second check for chars outside the ASCII range
*/
cur = value;
c = CUR_SCHAR(cur, l);
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if ((!IS_LETTER(c)) && (c != '_'))
return(1);
cur += l;
c = CUR_SCHAR(cur, l);
while (IS_LETTER(c) || IS_DIGIT(c) || (c == '.') ||
(c == '-') || (c == '_') || IS_COMBINING(c) ||
IS_EXTENDER(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (c != 0)
return(1);
return(0);
}
#endif
#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
/**
* xmlValidateQName:
* @value: the value to check
* @space: allow spaces in front and end of the string
*
* Check that a value conforms to the lexical space of QName
*
* Returns 0 if this validates, a positive error code number otherwise
* and -1 in case of internal or API error.
*/
int
xmlValidateQName(const xmlChar *value, int space) {
const xmlChar *cur = value;
int c,l;
if (value == NULL)
return(-1);
/*
* First quick algorithm for ASCII range
*/
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (((*cur >= 'a') && (*cur <= 'z')) || ((*cur >= 'A') && (*cur <= 'Z')) ||
(*cur == '_'))
cur++;
else
goto try_complex;
while (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.'))
cur++;
if (*cur == ':') {
cur++;
if (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
(*cur == '_'))
cur++;
else
goto try_complex;
while (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.'))
cur++;
}
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (*cur == 0)
return(0);
try_complex:
/*
* Second check for chars outside the ASCII range
*/
cur = value;
c = CUR_SCHAR(cur, l);
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if ((!IS_LETTER(c)) && (c != '_'))
return(1);
cur += l;
c = CUR_SCHAR(cur, l);
while (IS_LETTER(c) || IS_DIGIT(c) || (c == '.') ||
(c == '-') || (c == '_') || IS_COMBINING(c) ||
IS_EXTENDER(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
if (c == ':') {
cur += l;
c = CUR_SCHAR(cur, l);
if ((!IS_LETTER(c)) && (c != '_'))
return(1);
cur += l;
c = CUR_SCHAR(cur, l);
while (IS_LETTER(c) || IS_DIGIT(c) || (c == '.') ||
(c == '-') || (c == '_') || IS_COMBINING(c) ||
IS_EXTENDER(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (c != 0)
return(1);
return(0);
}
/**
* xmlValidateName:
* @value: the value to check
* @space: allow spaces in front and end of the string
*
* Check that a value conforms to the lexical space of Name
*
* Returns 0 if this validates, a positive error code number otherwise
* and -1 in case of internal or API error.
*/
int
xmlValidateName(const xmlChar *value, int space) {
const xmlChar *cur = value;
int c,l;
if (value == NULL)
return(-1);
/*
* First quick algorithm for ASCII range
*/
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (((*cur >= 'a') && (*cur <= 'z')) || ((*cur >= 'A') && (*cur <= 'Z')) ||
(*cur == '_') || (*cur == ':'))
cur++;
else
goto try_complex;
while (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.') || (*cur == ':'))
cur++;
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (*cur == 0)
return(0);
try_complex:
/*
* Second check for chars outside the ASCII range
*/
cur = value;
c = CUR_SCHAR(cur, l);
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if ((!IS_LETTER(c)) && (c != '_') && (c != ':'))
return(1);
cur += l;
c = CUR_SCHAR(cur, l);
while (IS_LETTER(c) || IS_DIGIT(c) || (c == '.') || (c == ':') ||
(c == '-') || (c == '_') || IS_COMBINING(c) || IS_EXTENDER(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (c != 0)
return(1);
return(0);
}
/**
* xmlValidateNMToken:
* @value: the value to check
* @space: allow spaces in front and end of the string
*
* Check that a value conforms to the lexical space of NMToken
*
* Returns 0 if this validates, a positive error code number otherwise
* and -1 in case of internal or API error.
*/
int
xmlValidateNMToken(const xmlChar *value, int space) {
const xmlChar *cur = value;
int c,l;
if (value == NULL)
return(-1);
/*
* First quick algorithm for ASCII range
*/
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.') || (*cur == ':'))
cur++;
else
goto try_complex;
while (((*cur >= 'a') && (*cur <= 'z')) ||
((*cur >= 'A') && (*cur <= 'Z')) ||
((*cur >= '0') && (*cur <= '9')) ||
(*cur == '_') || (*cur == '-') || (*cur == '.') || (*cur == ':'))
cur++;
if (space)
while (IS_BLANK_CH(*cur)) cur++;
if (*cur == 0)
return(0);
try_complex:
/*
* Second check for chars outside the ASCII range
*/
cur = value;
c = CUR_SCHAR(cur, l);
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (!(IS_LETTER(c) || IS_DIGIT(c) || (c == '.') || (c == ':') ||
(c == '-') || (c == '_') || IS_COMBINING(c) || IS_EXTENDER(c)))
return(1);
cur += l;
c = CUR_SCHAR(cur, l);
while (IS_LETTER(c) || IS_DIGIT(c) || (c == '.') || (c == ':') ||
(c == '-') || (c == '_') || IS_COMBINING(c) || IS_EXTENDER(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
if (space) {
while (IS_BLANK(c)) {
cur += l;
c = CUR_SCHAR(cur, l);
}
}
if (c != 0)
return(1);
return(0);
}
#endif /* LIBXML_TREE_ENABLED */
/************************************************************************
* *
* 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) {
if ((scheme == XML_BUFFER_ALLOC_EXACT) ||
(scheme == XML_BUFFER_ALLOC_DOUBLEIT) ||
(scheme == XML_BUFFER_ALLOC_HYBRID))
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
* XML_BUFFER_ALLOC_HYBRID - use exact sizes on small strings to keep memory usage tight
* in normal usage, and doubleit on large strings to avoid
* pathological 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.
* Note that for a default namespace, @prefix should be NULL.
*
* 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"))) {
/* xml namespace is predefined, no need to add it */
if (xmlStrEqual(href, XML_XML_NAMESPACE))
return(NULL);
/*
* Problem, this is an attempt to bind xml prefix to a wrong
* namespace, which breaks
* Namespace constraint: Reserved Prefixes and Namespace Names
* from XML namespace. But documents authors may not care in
* their context so let's proceed.
*/
}
/*
* Allocate a new Namespace and fill the fields.
*/
cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (cur == NULL) {
xmlTreeErrMemory("building namespace");
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;
}
if ((node->type == XML_ELEMENT_NODE) ||
(node->type == XML_ATTRIBUTE_NODE))
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) {
xmlTreeErrMemory("building DTD");
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;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
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(const xmlDoc *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) {
xmlTreeErrMemory("building internal subset");
return(NULL);
}
memset(cur, 0, sizeof(xmlDtd));
cur->type = XML_DTD_NODE;
if (name != NULL) {
cur->name = xmlStrdup(name);
if (cur->name == NULL) {
xmlTreeErrMemory("building internal subset");
xmlFree(cur);
return(NULL);
}
}
if (ExternalID != NULL) {
cur->ExternalID = xmlStrdup(ExternalID);
if (cur->ExternalID == NULL) {
xmlTreeErrMemory("building internal subset");
if (cur->name != NULL)
xmlFree((char *)cur->name);
xmlFree(cur);
return(NULL);
}
}
if (SystemID != NULL) {
cur->SystemID = xmlStrdup(SystemID);
if (cur->SystemID == NULL) {
xmlTreeErrMemory("building internal subset");
if (cur->name != NULL)
xmlFree((char *)cur->name);
if (cur->ExternalID != NULL)
xmlFree((char *)cur->ExternalID);
xmlFree(cur);
return(NULL);
}
}
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;
}
}
}
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
return(cur);
}
/**
* DICT_FREE:
* @str: a string
*
* Free a string if it is not owned by the "dict" dictionary in the
* current scope
*/
#define DICT_FREE(str) \
if ((str) && ((!dict) || \
(xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
xmlFree((char *)(str));
/**
* DICT_COPY:
* @str: a string
*
* Copy a string using a "dict" dictionary in the current scope,
* if available.
*/
#define DICT_COPY(str, cpy) \
if (str) { \
if (dict) { \
if (xmlDictOwns(dict, (const xmlChar *)(str))) \
cpy = (xmlChar *) (str); \
else \
cpy = (xmlChar *) xmlDictLookup((dict), (const xmlChar *)(str), -1); \
} else \
cpy = xmlStrdup((const xmlChar *)(str)); }
/**
* DICT_CONST_COPY:
* @str: a string
*
* Copy a string using a "dict" dictionary in the current scope,
* if available.
*/
#define DICT_CONST_COPY(str, cpy) \
if (str) { \
if (dict) { \
if (xmlDictOwns(dict, (const xmlChar *)(str))) \
cpy = (const xmlChar *) (str); \
else \
cpy = xmlDictLookup((dict), (const xmlChar *)(str), -1); \
} else \
cpy = (const xmlChar *) xmlStrdup((const xmlChar *)(str)); }
/**
* xmlFreeDtd:
* @cur: the DTD structure to free up
*
* Free a DTD structure.
*/
void
xmlFreeDtd(xmlDtdPtr cur) {
xmlDictPtr dict = NULL;
if (cur == NULL) {
return;
}
if (cur->doc != NULL) dict = cur->doc->dict;
if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue))
xmlDeregisterNodeDefaultValue((xmlNodePtr)cur);
if (cur->children != NULL) {
xmlNodePtr next, c = cur->children;
/*
* Cleanup all nodes which are not part of the specific lists
* of notations, elements, attributes and entities.
*/
while (c != NULL) {
next = c->next;
if ((c->type != XML_NOTATION_NODE) &&
(c->type != XML_ELEMENT_DECL) &&
(c->type != XML_ATTRIBUTE_DECL) &&
(c->type != XML_ENTITY_DECL)) {
xmlUnlinkNode(c);
xmlFreeNode(c);
}
c = next;
}
}
DICT_FREE(cur->name)
DICT_FREE(cur->SystemID)
DICT_FREE(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) {
xmlTreeErrMemory("building doc");
return(NULL);
}
memset(cur, 0, sizeof(xmlDoc));
cur->type = XML_DOCUMENT_NODE;
cur->version = xmlStrdup(version);
if (cur->version == NULL) {
xmlTreeErrMemory("building doc");
xmlFree(cur);
return(NULL);
}
cur->standalone = -1;
cur->compression = -1; /* not initialized */
cur->doc = cur;
cur->parseFlags = 0;
cur->properties = XML_DOC_USERBUILT;
/*
* The in memory encoding is always UTF8
* This field will never change and would
* be obsolete if not for binary compatibility.
*/
cur->charset = XML_CHAR_ENCODING_UTF8;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
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;
xmlDictPtr dict = NULL;
if (cur == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlFreeDoc : document == NULL\n");
#endif
return;
}
#ifdef LIBXML_DEBUG_RUNTIME
#ifdef LIBXML_DEBUG_ENABLED
xmlDebugCheckDocument(stderr, cur);
#endif
#endif
if (cur != NULL) dict = cur->dict;
if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue))
xmlDeregisterNodeDefaultValue((xmlNodePtr)cur);
/*
* 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->oldNs != NULL) xmlFreeNsList(cur->oldNs);
DICT_FREE(cur->version)
DICT_FREE(cur->name)
DICT_FREE(cur->encoding)
DICT_FREE(cur->URL)
xmlFree(cur);
if (dict) xmlDictFree(dict);
}
/**
* 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(const xmlDoc *doc, const xmlChar *value, int len) {
xmlNodePtr ret = NULL, last = NULL;
xmlNodePtr node;
xmlChar *val;
const xmlChar *cur, *end;
const xmlChar *q;
xmlEntityPtr ent;
xmlBufPtr buf;
if (value == NULL) return(NULL);
cur = value;
end = cur + len;
buf = xmlBufCreateSize(0);
if (buf == NULL) return(NULL);
xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_HYBRID);
q = cur;
while ((cur < end) && (*cur != 0)) {
if (cur[0] == '&') {
int charval = 0;
xmlChar tmp;
/*
* Save the current text.
*/
if (cur != q) {
if (xmlBufAdd(buf, q, cur - q))
goto out;
}
q = cur;
if ((cur + 2 < end) && (cur[1] == '#') && (cur[2] == 'x')) {
cur += 3;
if (cur < end)
tmp = *cur;
else
tmp = 0;
while (tmp != ';') { /* Non input consuming loop */
/*
* If you find an integer overflow here when fuzzing,
* the bug is probably elsewhere. This function should
* only receive entities that were already validated by
* the parser, typically by xmlParseAttValueComplex
* calling xmlStringDecodeEntities.
*
* So it's better *not* to check for overflow to
* potentially discover new bugs.
*/
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 {
xmlTreeErr(XML_TREE_INVALID_HEX, (xmlNodePtr) doc,
NULL);
charval = 0;
break;
}
cur++;
if (cur < end)
tmp = *cur;
else
tmp = 0;
}
if (tmp == ';')
cur++;
q = cur;
} else if ((cur + 1 < end) && (cur[1] == '#')) {
cur += 2;
if (cur < end)
tmp = *cur;
else
tmp = 0;
while (tmp != ';') { /* Non input consuming loops */
/* Don't check for integer overflow, see above. */
if ((tmp >= '0') && (tmp <= '9'))
charval = charval * 10 + (tmp - '0');
else {
xmlTreeErr(XML_TREE_INVALID_DEC, (xmlNodePtr) doc,
NULL);
charval = 0;
break;
}
cur++;
if (cur < end)
tmp = *cur;
else
tmp = 0;
}
if (tmp == ';')
cur++;
q = cur;
} else {
/*
* Read the entity string
*/
cur++;
q = cur;
while ((cur < end) && (*cur != 0) && (*cur != ';')) cur++;
if ((cur >= end) || (*cur == 0)) {
xmlTreeErr(XML_TREE_UNTERMINATED_ENTITY, (xmlNodePtr) doc,
(const char *) q);
goto out;
}
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 (xmlBufCat(buf, ent->content))
goto out;
} else {
/*
* Flush buffer so far
*/
if (!xmlBufIsEmpty(buf)) {
node = xmlNewDocText(doc, NULL);
if (node == NULL) {
if (val != NULL) xmlFree(val);
goto out;
}
node->content = xmlBufDetach(buf);
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
/*
* Create a new REFERENCE_REF node
*/
node = xmlNewReference(doc, val);
if (node == NULL) {
if (val != NULL) xmlFree(val);
goto out;
}
else if ((ent != NULL) && (ent->children == NULL)) {
xmlNodePtr temp;
/* Set to non-NULL value to avoid recursion. */
ent->children = (xmlNodePtr) -1;
ent->children = xmlStringGetNodeList(doc,
(const xmlChar*)node->content);
ent->owner = 1;
temp = ent->children;
while (temp) {
temp->parent = (xmlNodePtr)ent;
ent->last = temp;
temp = temp->next;
}
}
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
xmlFree(val);
}
cur++;
q = cur;
}
if (charval != 0) {
xmlChar buffer[10];
int l;
l = xmlCopyCharMultiByte(buffer, charval);
buffer[l] = 0;
if (xmlBufCat(buf, buffer))
goto out;
charval = 0;
}
} else
cur++;
}
if (cur != q) {
/*
* Handle the last piece of text.
*/
if (xmlBufAdd(buf, q, cur - q))
goto out;
}
if (!xmlBufIsEmpty(buf)) {
node = xmlNewDocText(doc, NULL);
if (node == NULL) goto out;
node->content = xmlBufDetach(buf);
if (last == NULL) {
ret = node;
} else {
xmlAddNextSibling(last, node);
}
} else if (ret == NULL) {
ret = xmlNewDocText(doc, BAD_CAST "");
}
out:
xmlBufFree(buf);
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(const xmlDoc *doc, const xmlChar *value) {
xmlNodePtr ret = NULL, last = NULL;
xmlNodePtr node;
xmlChar *val;
const xmlChar *cur = value;
const xmlChar *q;
xmlEntityPtr ent;
xmlBufPtr buf;
if (value == NULL) return(NULL);
buf = xmlBufCreateSize(0);
if (buf == NULL) return(NULL);
xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_HYBRID);
q = cur;
while (*cur != 0) {
if (cur[0] == '&') {
int charval = 0;
xmlChar tmp;
/*
* Save the current text.
*/
if (cur != q) {
if (xmlBufAdd(buf, q, cur - q))
goto out;
}
q = cur;
if ((cur[1] == '#') && (cur[2] == 'x')) {
cur += 3;
tmp = *cur;
while (tmp != ';') { /* Non input consuming loop */
/* Don't check for integer overflow, see above. */
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 {
xmlTreeErr(XML_TREE_INVALID_HEX, (xmlNodePtr) doc,
NULL);
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 */
/* Don't check for integer overflow, see above. */
if ((tmp >= '0') && (tmp <= '9'))
charval = charval * 10 + (tmp - '0');
else {
xmlTreeErr(XML_TREE_INVALID_DEC, (xmlNodePtr) doc,
NULL);
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) {
xmlTreeErr(XML_TREE_UNTERMINATED_ENTITY,
(xmlNodePtr) doc, (const char *) q);
goto out;
}
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 (xmlBufCat(buf, ent->content))
goto out;
} else {
/*
* Flush buffer so far
*/
if (!xmlBufIsEmpty(buf)) {
node = xmlNewDocText(doc, NULL);
node->content = xmlBufDetach(buf);
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
/*
* Create a new REFERENCE_REF node
*/
node = xmlNewReference(doc, val);
if (node == NULL) {
if (val != NULL) xmlFree(val);
goto out;
}
else if ((ent != NULL) && (ent->children == NULL)) {
xmlNodePtr temp;
/* Set to non-NULL value to avoid recursion. */
ent->children = (xmlNodePtr) -1;
ent->children = xmlStringGetNodeList(doc,
(const xmlChar*)node->content);
ent->owner = 1;
temp = ent->children;
while (temp) {
temp->parent = (xmlNodePtr)ent;
ent->last = temp;
temp = temp->next;
}
}
if (last == NULL) {
last = ret = node;
} else {
last = xmlAddNextSibling(last, node);
}
}
xmlFree(val);
}
cur++;
q = cur;
}
if (charval != 0) {
xmlChar buffer[10];
int len;
len = xmlCopyCharMultiByte(buffer, charval);
buffer[len] = 0;
if (xmlBufCat(buf, buffer))
goto out;
charval = 0;
}
} else
cur++;
}
if ((cur != q) || (ret == NULL)) {
/*
* Handle the last piece of text.
*/
xmlBufAdd(buf, q, cur - q);
}
if (!xmlBufIsEmpty(buf)) {
node = xmlNewDocText(doc, NULL);
if (node == NULL) {
xmlBufFree(buf);
return(NULL);
}
node->content = xmlBufDetach(buf);
if (last == NULL) {
ret = node;
} else {
xmlAddNextSibling(last, node);
}
}
out:
xmlBufFree(buf);
return(ret);
}
/**
* xmlNodeListGetString:
* @doc: the document
* @list: a Node list
* @inLine: should we replace entity contents or show their external form
*
* Build 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, const xmlNode *list, int inLine)
{
const xmlNode *node = list;
xmlChar *ret = NULL;
xmlEntityPtr ent;
int attr;
if (list == NULL)
return (NULL);
if ((list->parent != NULL) && (list->parent->type == XML_ATTRIBUTE_NODE))
attr = 1;
else
attr = 0;
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;
if (attr)
buffer = xmlEncodeAttributeEntities(doc, node->content);
else
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);
}
#ifdef LIBXML_TREE_ENABLED
/**
* xmlNodeListGetRawString:
* @doc: the document
* @list: a Node list
* @inLine: should we replace entity contents or show their external form
*
* Builds 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(const xmlDoc *doc, const xmlNode *list, int inLine)
{
const xmlNode *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);
}
#endif /* LIBXML_TREE_ENABLED */
static xmlAttrPtr
xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns,
const xmlChar * name, const xmlChar * value,
int eatname)
{
xmlAttrPtr cur;
xmlDocPtr doc = NULL;
if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) {
if ((eatname == 1) &&
((node->doc == NULL) ||
(!(xmlDictOwns(node->doc->dict, name)))))
xmlFree((xmlChar *) name);
return (NULL);
}
/*
* Allocate a new property and fill the fields.
*/
cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
if (cur == NULL) {
if ((eatname == 1) &&
((node == NULL) || (node->doc == NULL) ||
(!(xmlDictOwns(node->doc->dict, name)))))
xmlFree((xmlChar *) name);
xmlTreeErrMemory("building attribute");
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;
if (eatname == 0) {
if ((doc != NULL) && (doc->dict != NULL))
cur->name = (xmlChar *) xmlDictLookup(doc->dict, name, -1);
else
cur->name = xmlStrdup(name);
} else
cur->name = name;
if (value != NULL) {
xmlNodePtr tmp;
cur->children = xmlNewDocText(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;
}
}
/*
* 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;
}
}
if ((value != NULL) && (node != NULL) &&
(xmlIsID(node->doc, node, cur) == 1))
xmlAddID(NULL, node->doc, value, cur);
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr) cur);
return (cur);
}
#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
defined(LIBXML_SCHEMAS_ENABLED)
/**
* 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) {
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewProp : name == NULL\n");
#endif
return(NULL);
}
return xmlNewPropInternal(node, NULL, name, value, 0);
}
#endif /* LIBXML_TREE_ENABLED */
/**
* 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) {
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsProp : name == NULL\n");
#endif
return(NULL);
}
return xmlNewPropInternal(node, ns, name, value, 0);
}
/**
* 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) {
if (name == NULL) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsPropEatName : name == NULL\n");
#endif
return(NULL);
}
return xmlNewPropInternal(node, ns, name, value, 1);
}
/**
* xmlNewDocProp:
* @doc: the document
* @name: the name of the attribute
* @value: the value of the attribute
*
* Create a new property carried by a document.
* NOTE: @value is supposed to be a piece of XML CDATA, so it allows entity
* references, but XML special chars need to be escaped first by using
* xmlEncodeEntitiesReentrant(). Use xmlNewProp() if you don't need
* entities support.
*
* 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) {
xmlTreeErrMemory("building attribute");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
cur->type = XML_ATTRIBUTE_NODE;
if ((doc != NULL) && (doc->dict != NULL))
cur->name = xmlDictLookup(doc->dict, name, -1);
else
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;
}
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
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) 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) {
xmlDictPtr dict = NULL;
if (cur == NULL) return;
if (cur->doc != NULL) dict = cur->doc->dict;
if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue))
xmlDeregisterNodeDefaultValue((xmlNodePtr)cur);
/* Check for ID removal -> leading to invalid references ! */
if ((cur->doc != NULL) && (cur->atype == XML_ATTRIBUTE_ID)) {
xmlRemoveID(cur->doc, cur);
}
if (cur->children != NULL) xmlFreeNodeList(cur->children);
DICT_FREE(cur->name)
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;
if (cur->next != NULL)
cur->next->prev = NULL;
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);
}
/**
* xmlNewDocPI:
* @doc: the target document
* @name: the processing instruction name
* @content: the PI content
*
* Creation of a processing instruction element.
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewDocPI(xmlDocPtr doc, 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) {
xmlTreeErrMemory("building PI");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_PI_NODE;
if ((doc != NULL) && (doc->dict != NULL))
cur->name = xmlDictLookup(doc->dict, name, -1);
else
cur->name = xmlStrdup(name);
if (content != NULL) {
cur->content = xmlStrdup(content);
}
cur->doc = doc;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
return(cur);
}
/**
* xmlNewPI:
* @name: the processing instruction name
* @content: the PI content
*
* Creation of a processing instruction element.
* Use xmlDocNewPI preferably to get string interning
*
* Returns a pointer to the new node object.
*/
xmlNodePtr
xmlNewPI(const xmlChar *name, const xmlChar *content) {
return(xmlNewDocPI(NULL, name, content));
}
/**
* 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. Uses xmlStrdup() to make
* copy of @name.
*/
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) {
xmlTreeErrMemory("building node");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ELEMENT_NODE;
cur->name = xmlStrdup(name);
cur->ns = ns;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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, with pointer @name as
* new node's name. Use xmlNewNode() if a copy of @name string is
* is needed as new node's name.
*/
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) {
xmlTreeErrMemory("building node");
/* we can't check here that name comes from the doc dictionary */
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ELEMENT_NODE;
cur->name = name;
cur->ns = ns;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
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;
if ((doc != NULL) && (doc->dict != NULL))
cur = xmlNewNodeEatName(ns, (xmlChar *)
xmlDictLookup(doc->dict, name, -1));
else
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)
}
} else {
/* if name don't come from the doc dictionary free it here */
if ((name != NULL) && (doc != NULL) &&
(!(xmlDictOwns(doc->dict, name))))
xmlFree(name);
}
return(cur);
}
#ifdef LIBXML_TREE_ENABLED
/**
* 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 = xmlNewDocNode(doc, ns, name, NULL);
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) {
xmlTreeErrMemory("building fragment");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_DOCUMENT_FRAG_NODE;
cur->doc = doc;
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
return(cur);
}
#endif /* LIBXML_TREE_ENABLED */
/**
* 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) {
xmlTreeErrMemory("building text");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_TEXT_NODE;
cur->name = xmlStringText;
if (content != NULL) {
cur->content = xmlStrdup(content);
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
return(cur);
}
#ifdef LIBXML_TREE_ENABLED
/**
* 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 @ns is NULL, the newly
* created element inherits the namespace of @parent. If @content is non NULL,
* a child TEXT node will be created containing the string @content.
* NOTE: Use xmlNewChild() if @content will contain entities that need to be
* preserved. Use this function, xmlNewTextChild(), if you need to ensure that
* reserved XML chars that might appear in @content, such as the ampersand,
* greater-than or less-than signs, are automatically replaced by their XML
* escaped entity representations.
*
* 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 (parent->type == XML_ELEMENT_NODE) {
if (ns == NULL)
cur = xmlNewDocRawNode(parent->doc, parent->ns, name, content);
else
cur = xmlNewDocRawNode(parent->doc, ns, name, content);
} else if ((parent->type == XML_DOCUMENT_NODE) ||
(parent->type == XML_HTML_DOCUMENT_NODE)) {
if (ns == NULL)
cur = xmlNewDocRawNode((xmlDocPtr) parent, NULL, name, content);
else
cur = xmlNewDocRawNode((xmlDocPtr) parent, ns, name, content);
} else if (parent->type == XML_DOCUMENT_FRAG_NODE) {
cur = xmlNewDocRawNode( 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);
}
#endif /* LIBXML_TREE_ENABLED */
/**
* 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;
if (name == NULL)
return(NULL);
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlTreeErrMemory("building character reference");
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);
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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(const xmlDoc *doc, const xmlChar *name) {
xmlNodePtr cur;
xmlEntityPtr ent;
if (name == NULL)
return(NULL);
/*
* Allocate a new node and fill the fields.
*/
cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
if (cur == NULL) {
xmlTreeErrMemory("building reference");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_ENTITY_REF_NODE;
cur->doc = (xmlDoc *)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;
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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(const xmlDoc *doc, const xmlChar *content) {
xmlNodePtr cur;
cur = xmlNewText(content);
if (cur != NULL) cur->doc = (xmlDoc *)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) {
xmlTreeErrMemory("building text");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_TEXT_NODE;
cur->name = xmlStringText;
if (content != NULL) {
cur->content = xmlStrndup(content, len);
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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) {
xmlTreeErrMemory("building comment");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_COMMENT_NODE;
cur->name = xmlStringComment;
if (content != NULL) {
cur->content = xmlStrdup(content);
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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) {
xmlTreeErrMemory("building CDATA");
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
cur->type = XML_CDATA_SECTION_NODE;
cur->doc = doc;
if (content != NULL) {
cur->content = xmlStrndup(content, len);
}
if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
xmlRegisterNodeDefaultValue(cur);
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) || (tree->type == XML_NAMESPACE_DECL))
return;
if (tree->doc != doc) {
if(tree->type == XML_ELEMENT_NODE) {
prop = tree->properties;
while (prop != NULL) {
if (prop->atype == XML_ATTRIBUTE_ID) {
xmlRemoveID(tree->doc, prop);
}
prop->doc = doc;
xmlSetListDoc(prop->children, doc);
/*
* TODO: ID attributes should be also added to the new
* document, but this breaks things like xmlReplaceNode.
* The underlying problem is that xmlRemoveID is only called
* if a node is destroyed, not if it's unlinked.
*/
#if 0
if (xmlIsID(doc, tree, prop)) {
xmlChar *idVal = xmlNodeListGetString(doc, prop->children,
1);
xmlAddID(NULL, doc, idVal, prop);
}
#endif
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) || (list->type == XML_NAMESPACE_DECL))
return;
cur = list;
while (cur != NULL) {
if (cur->doc != doc)
xmlSetTreeDoc(cur, doc);
cur = cur->next;
}
}
#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
/**
* 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 @ns is NULL, the newly
* created element inherits the namespace of @parent. 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 allows entity
* references. XML special chars must be escaped first by using
* xmlEncodeEntitiesReentrant(), or xmlNewTextChild() should be used.
*
* 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);
}
#endif /* LIBXML_TREE_ENABLED */
/**
* xmlAddPropSibling:
* @prev: the attribute to which @prop is added after
* @cur: the base attribute passed to calling function
* @prop: the new attribute
*
* Add a new attribute after @prev using @cur as base attribute.
* When inserting before @cur, @prev is passed as @cur->prev.
* When inserting after @cur, @prev is passed as @cur.
* If an existing attribute is found it is destroyed prior to adding @prop.
*
* Returns the attribute being inserted or NULL in case of error.
*/
static xmlNodePtr
xmlAddPropSibling(xmlNodePtr prev, xmlNodePtr cur, xmlNodePtr prop) {
xmlAttrPtr attr;
if ((cur == NULL) || (cur->type != XML_ATTRIBUTE_NODE) ||
(prop == NULL) || (prop->type != XML_ATTRIBUTE_NODE) ||
((prev != NULL) && (prev->type != XML_ATTRIBUTE_NODE)))
return(NULL);
/* check if an attribute with the same name exists */
if (prop->ns == NULL)
attr = xmlHasNsProp(cur->parent, prop->name, NULL);
else
attr = xmlHasNsProp(cur->parent, prop->name, prop->ns->href);
if (prop->doc != cur->doc) {
xmlSetTreeDoc(prop, cur->doc);
}
prop->parent = cur->parent;
prop->prev = prev;
if (prev != NULL) {
prop->next = prev->next;
prev->next = prop;
if (prop->next)
prop->next->prev = prop;
} else {
prop->next = cur;
cur->prev = prop;
}
if (prop->prev == NULL && prop->parent != NULL)
prop->parent->properties = (xmlAttrPtr) prop;
if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) {
/* different instance, destroy it (attributes must be unique) */
xmlRemoveProp((xmlAttrPtr) attr);
}
return prop;
}
/**
* 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) || (cur->type == XML_NAMESPACE_DECL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddNextSibling : cur == NULL\n");
#endif
return(NULL);
}
if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddNextSibling : elem == NULL\n");
#endif
return(NULL);
}
if (cur == elem) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddNextSibling : cur == elem\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) {
return xmlAddPropSibling(cur, cur, elem);
}
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->parent->last = elem;
return(elem);
}
#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED)
/**
* 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) || (cur->type == XML_NAMESPACE_DECL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddPrevSibling : cur == NULL\n");
#endif
return(NULL);
}
if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddPrevSibling : elem == NULL\n");
#endif
return(NULL);
}
if (cur == elem) {
#ifdef DEBUG_TREE
xmlGenericError(xmlGenericErrorContext,
"xmlAddPrevSibling : cur == elem\n");
#endif
return(NULL);
}
xmlUnlinkNode(elem);
if (elem->type == XML_TEXT_NODE) {
if (cur->type == XML_TEXT_NODE) {
xmlChar *tmp;