Skip to content
Snippets Groups Projects
tree.c 261 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * 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
    
    
    Bjorn Reese's avatar
    Bjorn Reese committed
    #include "libxml.h"
    
    
    #include <string.h> /* for memset() only ! */
    
    #include <stddef.h>
    
    #include <ctype.h>
    #include <stdlib.h>
    
    #ifdef LIBXML_ZLIB_ENABLED
    
    #include <zlib.h>
    #endif
    
    #include <libxml/xmlmemory.h>
    #include <libxml/tree.h>
    #include <libxml/parser.h>
    
    #include <libxml/entities.h>
    #include <libxml/valid.h>
    #include <libxml/xmlerror.h>
    
    #include <libxml/parserInternals.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"
    
    /************************************************************************
     *									*
    
     *									*
     ************************************************************************/
    
    
    xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns);
    
    Kurt Roeckx's avatar
    Kurt Roeckx committed
    static xmlChar* xmlGetPropNodeValueInternal(const xmlAttr *prop);
    
    /************************************************************************
     *									*
    
     *									*
     ************************************************************************/
    /**
     * xmlTreeErrMemory:
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
     * @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
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
     * @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";
    
    	    msg = "invalid decimal character value\n";
    
    	    break;
    	case XML_TREE_UNTERMINATED_ENTITY:
    
    	    msg = "unterminated entity reference %15s\n";
    
    	case XML_TREE_NOT_UTF8:
    	    msg = "string is not in UTF-8\n";
    	    break;
    
        }
        __xmlSimpleError(XML_FROM_TREE, code, node, msg, extra);
    }
    
    /************************************************************************
     *									*
    
     *		A few static variables and macros			*
    
     *									*
     ************************************************************************/
    
    const xmlChar xmlStringText[] = { 't', 'e', 'x', 't', 0 };
    
    const xmlChar xmlStringTextNoenc[] =
    
                  { 't', 'e', 'x', 't', 'n', 'o', 'e', 'n', 'c', 0 };
    
    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 = 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>
    
    /**
     * 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
    
    Kurt Roeckx's avatar
    Kurt Roeckx committed
    xmlGetEntityFromDtd(const xmlDtd *dtd, const xmlChar *name) {
    
        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
    
    Kurt Roeckx's avatar
    Kurt Roeckx committed
    xmlGetParameterEntityFromDtd(const xmlDtd *dtd, const xmlChar *name) {
    
        if ((dtd != NULL) && (dtd->pentities != NULL)) {
    	table = (xmlEntitiesTablePtr) dtd->pentities;
    	return((xmlEntityPtr) xmlHashLookup(table, name));
    	/* return(xmlGetEntityFromTable(table, name)); */
        }
        return(NULL);
    }
    
    
    /************************************************************************
     *									*
    
     *			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
    
     *
     * parse an XML qualified name string
     *
     * [NS 5] QName ::= (Prefix ':')? LocalPart
     *
     * [NS 6] Prefix ::= NCName
     *
     * [NS 7] LocalPart ::= NCName
     *
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
     * 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;
    
    
    
    #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] != ':'))
    
        if (name[len] == 0)
    	return(NULL);
    
        *prefix = xmlStrndup(name, len);
    
    	if (*prefix != NULL) {
    	    xmlFree(*prefix);
    	    *prefix = NULL;
    	}
    	return(NULL);
        }
    
    /**
     * 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
    
    Michael Wood's avatar
    Michael Wood committed
     *         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] != ':'))
    
    /************************************************************************
     *									*
    
     *		Check Name, NCName and QName strings			*
     *									*
     ************************************************************************/
    
    #define CUR_SCHAR(s, l) xmlStringCurrentChar(NULL, s, &l)
    
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
    #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_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;
    
    
        /*
         * 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);
    	}
        }
    
    	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;
    
    
        /*
         * 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);
    	}
        }
    
    	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);
    
    	    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;
    
    
        /*
         * 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;
    
    
        /*
         * 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)))
    
        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);
    }
    
    /************************************************************************
     *									*
    
     *		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))
    
    }
    
    /**
     * 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)
    
        if (prefix != NULL)
    
    
        /*
         * 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) {
    
    	return(NULL);
        }
        memset(cur, 0 , sizeof(xmlDtd));
        cur->type = XML_DTD_NODE;
    
        if (name != NULL)
    
        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
    
    Kurt Roeckx's avatar
    Kurt Roeckx committed
    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);
    	}