root/third_party/libxml/src/xinclude.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xmlXIncludeErrMemory
  2. xmlXIncludeErr
  3. xmlXIncludeWarn
  4. xmlXIncludeGetProp
  5. xmlXIncludeFreeRef
  6. xmlXIncludeNewRef
  7. xmlXIncludeNewContext
  8. xmlXIncludeURLPush
  9. xmlXIncludeURLPop
  10. xmlXIncludeFreeContext
  11. xmlXIncludeParseFile
  12. xmlXIncludeAddNode
  13. xmlXIncludeRecurseDoc
  14. xmlXIncludeAddTxt
  15. xmlXIncludeCopyNode
  16. xmlXIncludeCopyNodeList
  17. xmlXIncludeGetNthChild
  18. xmlXIncludeCopyRange
  19. xmlXIncludeCopyXPointer
  20. xmlXIncludeMergeEntity
  21. xmlXIncludeMergeEntities
  22. xmlXIncludeLoadDoc
  23. xmlXIncludeLoadTxt
  24. xmlXIncludeLoadFallback
  25. xmlXIncludePreProcessNode
  26. xmlXIncludeLoadNode
  27. xmlXIncludeIncludeNode
  28. xmlXIncludeTestNode
  29. xmlXIncludeDoProcess
  30. xmlXIncludeSetFlags
  31. xmlXIncludeProcessTreeFlagsData
  32. xmlXIncludeProcessFlagsData
  33. xmlXIncludeProcessFlags
  34. xmlXIncludeProcess
  35. xmlXIncludeProcessTreeFlags
  36. xmlXIncludeProcessTree
  37. xmlXIncludeProcessNode

/*
 * xinclude.c : Code to implement XInclude processing
 *
 * World Wide Web Consortium W3C Last Call Working Draft 10 November 2003
 * http://www.w3.org/TR/2003/WD-xinclude-20031110
 *
 * See Copyright for the status of this software.
 *
 * daniel@veillard.com
 */

#define IN_LIBXML
#include "libxml.h"

#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/uri.h>
#include <libxml/xpointer.h>
#include <libxml/parserInternals.h>
#include <libxml/xmlerror.h>
#include <libxml/encoding.h>
#include <libxml/globals.h>

#ifdef LIBXML_XINCLUDE_ENABLED
#include <libxml/xinclude.h>


#define XINCLUDE_MAX_DEPTH 40

/* #define DEBUG_XINCLUDE */
#ifdef DEBUG_XINCLUDE
#ifdef LIBXML_DEBUG_ENABLED
#include <libxml/debugXML.h>
#endif
#endif

/************************************************************************
 *                                                                      *
 *                      XInclude context handling                       *
 *                                                                      *
 ************************************************************************/

/*
 * An XInclude context
 */
typedef xmlChar *xmlURL;

typedef struct _xmlXIncludeRef xmlXIncludeRef;
typedef xmlXIncludeRef *xmlXIncludeRefPtr;
struct _xmlXIncludeRef {
    xmlChar              *URI; /* the fully resolved resource URL */
    xmlChar         *fragment; /* the fragment in the URI */
    xmlDocPtr             doc; /* the parsed document */
    xmlNodePtr            ref; /* the node making the reference in the source */
    xmlNodePtr            inc; /* the included copy */
    int                   xml; /* xml or txt */
    int                 count; /* how many refs use that specific doc */
    xmlXPathObjectPtr    xptr; /* the xpointer if needed */
    int               emptyFb; /* flag to show fallback empty */
};

struct _xmlXIncludeCtxt {
    xmlDocPtr             doc; /* the source document */
    int               incBase; /* the first include for this document */
    int                 incNr; /* number of includes */
    int                incMax; /* size of includes tab */
    xmlXIncludeRefPtr *incTab; /* array of included references */

    int                 txtNr; /* number of unparsed documents */
    int                txtMax; /* size of unparsed documents tab */
    xmlNodePtr        *txtTab; /* array of unparsed text nodes */
    xmlURL         *txturlTab; /* array of unparsed text URLs */

    xmlChar *             url; /* the current URL processed */
    int                 urlNr; /* number of URLs stacked */
    int                urlMax; /* size of URL stack */
    xmlChar *         *urlTab; /* URL stack */

    int              nbErrors; /* the number of errors detected */
    int                legacy; /* using XINCLUDE_OLD_NS */
    int            parseFlags; /* the flags used for parsing XML documents */
    xmlChar *            base; /* the current xml:base */

    void            *_private; /* application data */
};

static int
xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree);


/************************************************************************
 *                                                                      *
 *                      XInclude error handler                          *
 *                                                                      *
 ************************************************************************/

/**
 * xmlXIncludeErrMemory:
 * @extra:  extra information
 *
 * Handle an out of memory condition
 */
static void
xmlXIncludeErrMemory(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node,
                     const char *extra)
{
    if (ctxt != NULL)
        ctxt->nbErrors++;
    __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
                    extra, NULL, NULL, 0, 0,
                    "Memory allocation failed : %s\n", extra);
}

/**
 * xmlXIncludeErr:
 * @ctxt: the XInclude context
 * @node: the context node
 * @msg:  the error message
 * @extra:  extra information
 *
 * Handle an XInclude error
 */
static void
xmlXIncludeErr(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error,
               const char *msg, const xmlChar *extra)
{
    if (ctxt != NULL)
        ctxt->nbErrors++;
    __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
                    error, XML_ERR_ERROR, NULL, 0,
                    (const char *) extra, NULL, NULL, 0, 0,
                    msg, (const char *) extra);
}

#if 0
/**
 * xmlXIncludeWarn:
 * @ctxt: the XInclude context
 * @node: the context node
 * @msg:  the error message
 * @extra:  extra information
 *
 * Emit an XInclude warning.
 */
static void
xmlXIncludeWarn(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error,
               const char *msg, const xmlChar *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
                    error, XML_ERR_WARNING, NULL, 0,
                    (const char *) extra, NULL, NULL, 0, 0,
                    msg, (const char *) extra);
}
#endif

/**
 * xmlXIncludeGetProp:
 * @ctxt:  the XInclude context
 * @cur:  the node
 * @name:  the attribute name
 *
 * Get an XInclude attribute
 *
 * Returns the value (to be freed) or NULL if not found
 */
static xmlChar *
xmlXIncludeGetProp(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur,
                   const xmlChar *name) {
    xmlChar *ret;

    ret = xmlGetNsProp(cur, XINCLUDE_NS, name);
    if (ret != NULL)
        return(ret);
    if (ctxt->legacy != 0) {
        ret = xmlGetNsProp(cur, XINCLUDE_OLD_NS, name);
        if (ret != NULL)
            return(ret);
    }
    ret = xmlGetProp(cur, name);
    return(ret);
}
/**
 * xmlXIncludeFreeRef:
 * @ref: the XInclude reference
 *
 * Free an XInclude reference
 */
static void
xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) {
    if (ref == NULL)
        return;
#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Freeing ref\n");
#endif
    if (ref->doc != NULL) {
#ifdef DEBUG_XINCLUDE
        xmlGenericError(xmlGenericErrorContext, "Freeing doc %s\n", ref->URI);
#endif
        xmlFreeDoc(ref->doc);
    }
    if (ref->URI != NULL)
        xmlFree(ref->URI);
    if (ref->fragment != NULL)
        xmlFree(ref->fragment);
    if (ref->xptr != NULL)
        xmlXPathFreeObject(ref->xptr);
    xmlFree(ref);
}

/**
 * xmlXIncludeNewRef:
 * @ctxt: the XInclude context
 * @URI:  the resource URI
 *
 * Creates a new reference within an XInclude context
 *
 * Returns the new set
 */
static xmlXIncludeRefPtr
xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI,
                  xmlNodePtr ref) {
    xmlXIncludeRefPtr ret;

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "New ref %s\n", URI);
#endif
    ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef));
    if (ret == NULL) {
        xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
        return(NULL);
    }
    memset(ret, 0, sizeof(xmlXIncludeRef));
    if (URI == NULL)
        ret->URI = NULL;
    else
        ret->URI = xmlStrdup(URI);
    ret->fragment = NULL;
    ret->ref = ref;
    ret->doc = NULL;
    ret->count = 0;
    ret->xml = 0;
    ret->inc = NULL;
    if (ctxt->incMax == 0) {
        ctxt->incMax = 4;
        ctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(ctxt->incMax *
                                              sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
            xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
            xmlXIncludeFreeRef(ret);
            return(NULL);
        }
    }
    if (ctxt->incNr >= ctxt->incMax) {
        ctxt->incMax *= 2;
        ctxt->incTab = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab,
                     ctxt->incMax * sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
            xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
            xmlXIncludeFreeRef(ret);
            return(NULL);
        }
    }
    ctxt->incTab[ctxt->incNr++] = ret;
    return(ret);
}

/**
 * xmlXIncludeNewContext:
 * @doc:  an XML Document
 *
 * Creates a new XInclude context
 *
 * Returns the new set
 */
xmlXIncludeCtxtPtr
xmlXIncludeNewContext(xmlDocPtr doc) {
    xmlXIncludeCtxtPtr ret;

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "New context\n");
#endif
    if (doc == NULL)
        return(NULL);
    ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt));
    if (ret == NULL) {
        xmlXIncludeErrMemory(NULL, (xmlNodePtr) doc,
                             "creating XInclude context");
        return(NULL);
    }
    memset(ret, 0, sizeof(xmlXIncludeCtxt));
    ret->doc = doc;
    ret->incNr = 0;
    ret->incBase = 0;
    ret->incMax = 0;
    ret->incTab = NULL;
    ret->nbErrors = 0;
    return(ret);
}

/**
 * xmlXIncludeURLPush:
 * @ctxt:  the parser context
 * @value:  the url
 *
 * Pushes a new url on top of the url stack
 *
 * Returns -1 in case of error, the index in the stack otherwise
 */
static int
xmlXIncludeURLPush(xmlXIncludeCtxtPtr ctxt,
                   const xmlChar *value)
{
    if (ctxt->urlNr > XINCLUDE_MAX_DEPTH) {
        xmlXIncludeErr(ctxt, NULL, XML_XINCLUDE_RECURSION,
                       "detected a recursion in %s\n", value);
        return(-1);
    }
    if (ctxt->urlTab == NULL) {
        ctxt->urlMax = 4;
        ctxt->urlNr = 0;
        ctxt->urlTab = (xmlChar * *) xmlMalloc(
                        ctxt->urlMax * sizeof(ctxt->urlTab[0]));
        if (ctxt->urlTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "adding URL");
            return (-1);
        }
    }
    if (ctxt->urlNr >= ctxt->urlMax) {
        ctxt->urlMax *= 2;
        ctxt->urlTab =
            (xmlChar * *) xmlRealloc(ctxt->urlTab,
                                      ctxt->urlMax *
                                      sizeof(ctxt->urlTab[0]));
        if (ctxt->urlTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "adding URL");
            return (-1);
        }
    }
    ctxt->url = ctxt->urlTab[ctxt->urlNr] = xmlStrdup(value);
    return (ctxt->urlNr++);
}

/**
 * xmlXIncludeURLPop:
 * @ctxt: the parser context
 *
 * Pops the top URL from the URL stack
 */
static void
xmlXIncludeURLPop(xmlXIncludeCtxtPtr ctxt)
{
    xmlChar * ret;

    if (ctxt->urlNr <= 0)
        return;
    ctxt->urlNr--;
    if (ctxt->urlNr > 0)
        ctxt->url = ctxt->urlTab[ctxt->urlNr - 1];
    else
        ctxt->url = NULL;
    ret = ctxt->urlTab[ctxt->urlNr];
    ctxt->urlTab[ctxt->urlNr] = NULL;
    if (ret != NULL)
        xmlFree(ret);
}

/**
 * xmlXIncludeFreeContext:
 * @ctxt: the XInclude context
 *
 * Free an XInclude context
 */
void
xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) {
    int i;

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Freeing context\n");
#endif
    if (ctxt == NULL)
        return;
    while (ctxt->urlNr > 0)
        xmlXIncludeURLPop(ctxt);
    if (ctxt->urlTab != NULL)
        xmlFree(ctxt->urlTab);
    for (i = 0;i < ctxt->incNr;i++) {
        if (ctxt->incTab[i] != NULL)
            xmlXIncludeFreeRef(ctxt->incTab[i]);
    }
    if (ctxt->txturlTab != NULL) {
        for (i = 0;i < ctxt->txtNr;i++) {
            if (ctxt->txturlTab[i] != NULL)
                xmlFree(ctxt->txturlTab[i]);
        }
    }
    if (ctxt->incTab != NULL)
        xmlFree(ctxt->incTab);
    if (ctxt->txtTab != NULL)
        xmlFree(ctxt->txtTab);
    if (ctxt->txturlTab != NULL)
        xmlFree(ctxt->txturlTab);
    if (ctxt->base != NULL) {
        xmlFree(ctxt->base);
    }
    xmlFree(ctxt);
}

/**
 * xmlXIncludeParseFile:
 * @ctxt:  the XInclude context
 * @URL:  the URL or file path
 * 
 * parse a document for XInclude
 */
static xmlDocPtr
xmlXIncludeParseFile(xmlXIncludeCtxtPtr ctxt, const char *URL) {
    xmlDocPtr ret;
    xmlParserCtxtPtr pctxt;
    xmlParserInputPtr inputStream;

    xmlInitParser();

    pctxt = xmlNewParserCtxt();
    if (pctxt == NULL) {
        xmlXIncludeErrMemory(ctxt, NULL, "cannot allocate parser context");
        return(NULL);
    }

    /*
     * pass in the application data to the parser context.
     */
    pctxt->_private = ctxt->_private;
    
    /*
     * try to ensure that new documents included are actually
     * built with the same dictionary as the including document.
     */
    if ((ctxt->doc != NULL) && (ctxt->doc->dict != NULL)) {
       if (pctxt->dict != NULL)
            xmlDictFree(pctxt->dict);
        pctxt->dict = ctxt->doc->dict;
        xmlDictReference(pctxt->dict);
    }

    xmlCtxtUseOptions(pctxt, ctxt->parseFlags | XML_PARSE_DTDLOAD);
    
    inputStream = xmlLoadExternalEntity(URL, NULL, pctxt);
    if (inputStream == NULL) {
        xmlFreeParserCtxt(pctxt);
        return(NULL);
    }

    inputPush(pctxt, inputStream);

    if (pctxt->directory == NULL)
        pctxt->directory = xmlParserGetDirectory(URL);

    pctxt->loadsubset |= XML_DETECT_IDS;

    xmlParseDocument(pctxt);

    if (pctxt->wellFormed) {
        ret = pctxt->myDoc;
    }
    else {
        ret = NULL;
        if (pctxt->myDoc != NULL)
            xmlFreeDoc(pctxt->myDoc);
        pctxt->myDoc = NULL;
    }
    xmlFreeParserCtxt(pctxt);
    
    return(ret);
}

/**
 * xmlXIncludeAddNode:
 * @ctxt:  the XInclude context
 * @cur:  the new node
 * 
 * Add a new node to process to an XInclude context
 */
static int
xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
    xmlXIncludeRefPtr ref;
    xmlURIPtr uri;
    xmlChar *URL;
    xmlChar *fragment = NULL;
    xmlChar *href;
    xmlChar *parse;
    xmlChar *base;
    xmlChar *URI;
    int xml = 1, i; /* default Issue 64 */
    int local = 0;


    if (ctxt == NULL)
        return(-1);
    if (cur == NULL)
        return(-1);

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Add node\n");
#endif
    /*
     * read the attributes
     */
    href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF);
    if (href == NULL) {
        href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */
        if (href == NULL) 
            return(-1);
    }
    if ((href[0] == '#') || (href[0] == 0))
        local = 1;
    parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE);
    if (parse != NULL) {
        if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
            xml = 1;
        else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
            xml = 0;
        else {
            xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE,
                           "invalid value %s for 'parse'\n", parse);
            if (href != NULL)
                xmlFree(href);
            if (parse != NULL)
                xmlFree(parse);
            return(-1);
        }
    }

    /*
     * compute the URI
     */
    base = xmlNodeGetBase(ctxt->doc, cur);
    if (base == NULL) {
        URI = xmlBuildURI(href, ctxt->doc->URL);
    } else {
        URI = xmlBuildURI(href, base);
    }
    if (URI == NULL) {
        xmlChar *escbase;
        xmlChar *eschref;
        /*
         * Some escaping may be needed
         */
        escbase = xmlURIEscape(base);
        eschref = xmlURIEscape(href);
        URI = xmlBuildURI(eschref, escbase);
        if (escbase != NULL)
            xmlFree(escbase);
        if (eschref != NULL)
            xmlFree(eschref);
    }
    if (parse != NULL)
        xmlFree(parse);
    if (href != NULL)
        xmlFree(href);
    if (base != NULL)
        xmlFree(base);
    if (URI == NULL) {
        xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
                       "failed build URL\n", NULL);
        return(-1);
    }
    fragment = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE_XPOINTER);

    /*
     * Check the URL and remove any fragment identifier
     */
    uri = xmlParseURI((const char *)URI);
    if (uri == NULL) {
        xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
                       "invalid value URI %s\n", URI);
        if (fragment != NULL)
            xmlFree(fragment);
        xmlFree(URI);
        return(-1);
    }

    if (uri->fragment != NULL) {
        if (ctxt->legacy != 0) {
            if (fragment == NULL) {
                fragment = (xmlChar *) uri->fragment;
            } else {
                xmlFree(uri->fragment);
            }
        } else {
            xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_FRAGMENT_ID,
       "Invalid fragment identifier in URI %s use the xpointer attribute\n",
                           URI);
            if (fragment != NULL)
                xmlFree(fragment);
            xmlFreeURI(uri);
            xmlFree(URI);
            return(-1);
        }
        uri->fragment = NULL;
    }
    URL = xmlSaveUri(uri);
    xmlFreeURI(uri);
    xmlFree(URI);
    if (URL == NULL) {
        xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
                       "invalid value URI %s\n", URI);
        if (fragment != NULL)
            xmlFree(fragment);
        return(-1);
    }

    /*
     * If local and xml then we need a fragment
     */
    if ((local == 1) && (xml == 1) &&
        ((fragment == NULL) || (fragment[0] == 0))) {
        xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION,
                       "detected a local recursion with no xpointer in %s\n",
                       URL);
        if (fragment != NULL)
            xmlFree(fragment);
        return(-1);
    }

    /*
     * Check the URL against the stack for recursions
     */
    if ((!local) && (xml == 1)) {
        for (i = 0;i < ctxt->urlNr;i++) {
            if (xmlStrEqual(URL, ctxt->urlTab[i])) {
                xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION,
                               "detected a recursion in %s\n", URL);
                return(-1);
            }
        }
    }

    ref = xmlXIncludeNewRef(ctxt, URL, cur);
    if (ref == NULL) {
        return(-1);
    }
    ref->fragment = fragment;
    ref->doc = NULL;
    ref->xml = xml;
    ref->count = 1;
    xmlFree(URL);
    return(0);
}

/**
 * xmlXIncludeRecurseDoc:
 * @ctxt:  the XInclude context
 * @doc:  the new document
 * @url:  the associated URL
 * 
 * The XInclude recursive nature is handled at this point.
 */
static void
xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
                      const xmlURL url ATTRIBUTE_UNUSED) {
    xmlXIncludeCtxtPtr newctxt;
    int i;

    /*
     * Avoid recursion in already substitued resources
    for (i = 0;i < ctxt->urlNr;i++) {
        if (xmlStrEqual(doc->URL, ctxt->urlTab[i]))
            return;
    }
     */

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Recursing in doc %s\n", doc->URL);
#endif
    /*
     * Handle recursion here.
     */

    newctxt = xmlXIncludeNewContext(doc);
    if (newctxt != NULL) {
        /*
         * Copy the private user data
         */
        newctxt->_private = ctxt->_private;     
        /*
         * Copy the existing document set
         */
        newctxt->incMax = ctxt->incMax;
        newctxt->incNr = ctxt->incNr;
        newctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(newctxt->incMax *
                                          sizeof(newctxt->incTab[0]));
        if (newctxt->incTab == NULL) {
            xmlXIncludeErrMemory(ctxt, (xmlNodePtr) doc, "processing doc");
            xmlFree(newctxt);
            return;
        }
        /*
         * copy the urlTab
         */
        newctxt->urlMax = ctxt->urlMax;
        newctxt->urlNr = ctxt->urlNr;
        newctxt->urlTab = ctxt->urlTab;

        /*
         * Inherit the existing base
         */
        newctxt->base = xmlStrdup(ctxt->base);

        /*
         * Inherit the documents already in use by other includes
         */
        newctxt->incBase = ctxt->incNr;
        for (i = 0;i < ctxt->incNr;i++) {
            newctxt->incTab[i] = ctxt->incTab[i];
            newctxt->incTab[i]->count++; /* prevent the recursion from
                                            freeing it */
        }
        /*
         * The new context should also inherit the Parse Flags
         * (bug 132597)
         */
        newctxt->parseFlags = ctxt->parseFlags;
        xmlXIncludeDoProcess(newctxt, doc, xmlDocGetRootElement(doc));
        for (i = 0;i < ctxt->incNr;i++) {
            newctxt->incTab[i]->count--;
            newctxt->incTab[i] = NULL;
        }

        /* urlTab may have been reallocated */
        ctxt->urlTab = newctxt->urlTab;
        ctxt->urlMax = newctxt->urlMax;

        newctxt->urlMax = 0;
        newctxt->urlNr = 0;
        newctxt->urlTab = NULL;

        xmlXIncludeFreeContext(newctxt);
    }
#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Done recursing in doc %s\n", url);
#endif
}

/**
 * xmlXIncludeAddTxt:
 * @ctxt:  the XInclude context
 * @txt:  the new text node
 * @url:  the associated URL
 * 
 * Add a new txtument to the list
 */
static void
xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL url) {
#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Adding text %s\n", url);
#endif
    if (ctxt->txtMax == 0) {
        ctxt->txtMax = 4;
        ctxt->txtTab = (xmlNodePtr *) xmlMalloc(ctxt->txtMax *
                                          sizeof(ctxt->txtTab[0]));
        if (ctxt->txtTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "processing text");
            return;
        }
        ctxt->txturlTab = (xmlURL *) xmlMalloc(ctxt->txtMax *
                                          sizeof(ctxt->txturlTab[0]));
        if (ctxt->txturlTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "processing text");
            return;
        }
    }
    if (ctxt->txtNr >= ctxt->txtMax) {
        ctxt->txtMax *= 2;
        ctxt->txtTab = (xmlNodePtr *) xmlRealloc(ctxt->txtTab,
                     ctxt->txtMax * sizeof(ctxt->txtTab[0]));
        if (ctxt->txtTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "processing text");
            return;
        }
        ctxt->txturlTab = (xmlURL *) xmlRealloc(ctxt->txturlTab,
                     ctxt->txtMax * sizeof(ctxt->txturlTab[0]));
        if (ctxt->txturlTab == NULL) {
            xmlXIncludeErrMemory(ctxt, NULL, "processing text");
            return;
        }
    }
    ctxt->txtTab[ctxt->txtNr] = txt;
    ctxt->txturlTab[ctxt->txtNr] = xmlStrdup(url);
    ctxt->txtNr++;
}

/************************************************************************
 *                                                                      *
 *                      Node copy with specific semantic                *
 *                                                                      *
 ************************************************************************/

static xmlNodePtr
xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
                        xmlDocPtr source, xmlNodePtr elem);

/**
 * xmlXIncludeCopyNode:
 * @ctxt:  the XInclude context
 * @target:  the document target
 * @source:  the document source
 * @elem:  the element
 * 
 * Make a copy of the node while preserving the XInclude semantic
 * of the Infoset copy
 */
static xmlNodePtr
xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
                    xmlDocPtr source, xmlNodePtr elem) {
    xmlNodePtr result = NULL;

    if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
        (elem == NULL))
        return(NULL);
    if (elem->type == XML_DTD_NODE)
        return(NULL);
    if (elem->type == XML_DOCUMENT_NODE)
        result = xmlXIncludeCopyNodeList(ctxt, target, source, elem->children);
    else
        result = xmlDocCopyNode(elem, target, 1);
    return(result);
}

/**
 * xmlXIncludeCopyNodeList:
 * @ctxt:  the XInclude context
 * @target:  the document target
 * @source:  the document source
 * @elem:  the element list
 * 
 * Make a copy of the node list while preserving the XInclude semantic
 * of the Infoset copy
 */
static xmlNodePtr
xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
                        xmlDocPtr source, xmlNodePtr elem) {
    xmlNodePtr cur, res, result = NULL, last = NULL;

    if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
        (elem == NULL))
        return(NULL);
    cur = elem;
    while (cur != NULL) {
        res = xmlXIncludeCopyNode(ctxt, target, source, cur);
        if (res != NULL) {
            if (result == NULL) {
                result = last = res;
            } else {
                last->next = res;
                res->prev = last;
                last = res;
            }
        }
        cur = cur->next;
    }
    return(result);
}

/**
 * xmlXIncludeGetNthChild:
 * @cur:  the node
 * @no:  the child number
 *
 * Returns the @n'th element child of @cur or NULL
 */
static xmlNodePtr
xmlXIncludeGetNthChild(xmlNodePtr cur, int no) {
    int i;
    if (cur == NULL) 
        return(cur);
    cur = cur->children;
    for (i = 0;i <= no;cur = cur->next) {
        if (cur == NULL) 
            return(cur);
        if ((cur->type == XML_ELEMENT_NODE) ||
            (cur->type == XML_DOCUMENT_NODE) ||
            (cur->type == XML_HTML_DOCUMENT_NODE)) {
            i++;
            if (i == no)
                break;
        }
    }
    return(cur);
}

xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); /* in xpointer.c */
/**
 * xmlXIncludeCopyRange:
 * @ctxt:  the XInclude context
 * @target:  the document target
 * @source:  the document source
 * @obj:  the XPointer result from the evaluation.
 *
 * Build a node list tree copy of the XPointer result.
 *
 * Returns an xmlNodePtr list or NULL.
 *         The caller has to free the node tree.
 */
static xmlNodePtr
xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
                        xmlDocPtr source, xmlXPathObjectPtr range) {
    /* pointers to generated nodes */
    xmlNodePtr list = NULL, last = NULL, listParent = NULL;
    xmlNodePtr tmp, tmp2;
    /* pointers to traversal nodes */
    xmlNodePtr start, cur, end;
    int index1, index2;
    int level = 0, lastLevel = 0, endLevel = 0, endFlag = 0;

    if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
        (range == NULL))
        return(NULL);
    if (range->type != XPATH_RANGE)
        return(NULL);
    start = (xmlNodePtr) range->user;

    if (start == NULL)
        return(NULL);
    end = range->user2;
    if (end == NULL)
        return(xmlDocCopyNode(start, target, 1));

    cur = start;
    index1 = range->index;
    index2 = range->index2;
    /*
     * level is depth of the current node under consideration
     * list is the pointer to the root of the output tree
     * listParent is a pointer to the parent of output tree (within
       the included file) in case we need to add another level
     * last is a pointer to the last node added to the output tree
     * lastLevel is the depth of last (relative to the root)
     */
    while (cur != NULL) {
        /*
         * Check if our output tree needs a parent
         */
        if (level < 0) {
            while (level < 0) {
                /* copy must include namespaces and properties */
                tmp2 = xmlDocCopyNode(listParent, target, 2);
                xmlAddChild(tmp2, list);
                list = tmp2;
                listParent = listParent->parent;
                level++;
            }
            last = list;
            lastLevel = 0;
        }
        /*
         * Check whether we need to change our insertion point
         */
        while (level < lastLevel) {
            last = last->parent;
            lastLevel --;
        }
        if (cur == end) {       /* Are we at the end of the range? */
            if (cur->type == XML_TEXT_NODE) {
                const xmlChar *content = cur->content;
                int len;

                if (content == NULL) {
                    tmp = xmlNewTextLen(NULL, 0);
                } else {
                    len = index2;
                    if ((cur == start) && (index1 > 1)) {
                        content += (index1 - 1);
                        len -= (index1 - 1);
                    } else {
                        len = index2;
                    }
                    tmp = xmlNewTextLen(content, len);
                }
                /* single sub text node selection */
                if (list == NULL)
                    return(tmp);
                /* prune and return full set */
                if (level == lastLevel)
                    xmlAddNextSibling(last, tmp);
                else 
                    xmlAddChild(last, tmp);
                return(list);
            } else {    /* ending node not a text node */
                endLevel = level;       /* remember the level of the end node */
                endFlag = 1;
                /* last node - need to take care of properties + namespaces */
                tmp = xmlDocCopyNode(cur, target, 2);
                if (list == NULL) {
                    list = tmp;
                    listParent = cur->parent;
                } else {
                    if (level == lastLevel)
                        xmlAddNextSibling(last, tmp);
                    else {
                        xmlAddChild(last, tmp);
                        lastLevel = level;
                    }
                }
                last = tmp;

                if (index2 > 1) {
                    end = xmlXIncludeGetNthChild(cur, index2 - 1);
                    index2 = 0;
                }
                if ((cur == start) && (index1 > 1)) {
                    cur = xmlXIncludeGetNthChild(cur, index1 - 1);
                    index1 = 0;
                }  else {
                    cur = cur->children;
                }
                level++;        /* increment level to show change */
                /*
                 * Now gather the remaining nodes from cur to end
                 */
                continue;       /* while */
            }
        } else if (cur == start) {      /* Not at the end, are we at start? */
            if ((cur->type == XML_TEXT_NODE) ||
                (cur->type == XML_CDATA_SECTION_NODE)) {
                const xmlChar *content = cur->content;

                if (content == NULL) {
                    tmp = xmlNewTextLen(NULL, 0);
                } else {
                    if (index1 > 1) {
                        content += (index1 - 1);
                        index1 = 0;
                    }
                    tmp = xmlNewText(content);
                }
                last = list = tmp;
                listParent = cur->parent;
            } else {            /* Not text node */
                /*
                 * start of the range - need to take care of
                 * properties and namespaces
                 */
                tmp = xmlDocCopyNode(cur, target, 2);
                list = last = tmp;
                listParent = cur->parent;
                if (index1 > 1) {       /* Do we need to position? */
                    cur = xmlXIncludeGetNthChild(cur, index1 - 1);
                    level = lastLevel = 1;
                    index1 = 0;
                    /*
                     * Now gather the remaining nodes from cur to end
                     */
                    continue; /* while */
                }
            }
        } else {
            tmp = NULL;
            switch (cur->type) {
                case XML_DTD_NODE:
                case XML_ELEMENT_DECL:
                case XML_ATTRIBUTE_DECL:
                case XML_ENTITY_NODE:
                    /* Do not copy DTD informations */
                    break;
                case XML_ENTITY_DECL:
                    /* handle crossing entities -> stack needed */
                    break;
                case XML_XINCLUDE_START:
                case XML_XINCLUDE_END:
                    /* don't consider it part of the tree content */
                    break;
                case XML_ATTRIBUTE_NODE:
                    /* Humm, should not happen ! */
                    break;
                default:
                    /*
                     * Middle of the range - need to take care of
                     * properties and namespaces
                     */
                    tmp = xmlDocCopyNode(cur, target, 2);
                    break;
            }
            if (tmp != NULL) {
                if (level == lastLevel)
                    xmlAddNextSibling(last, tmp);
                else {
                    xmlAddChild(last, tmp);
                    lastLevel = level;
                }
                last = tmp;
            }
        }
        /*
         * Skip to next node in document order
         */
        cur = xmlXPtrAdvanceNode(cur, &level);
        if (endFlag && (level >= endLevel))
            break;
    }
    return(list);
}

/**
 * xmlXIncludeBuildNodeList:
 * @ctxt:  the XInclude context
 * @target:  the document target
 * @source:  the document source
 * @obj:  the XPointer result from the evaluation.
 *
 * Build a node list tree copy of the XPointer result.
 * This will drop Attributes and Namespace declarations.
 *
 * Returns an xmlNodePtr list or NULL.
 *         the caller has to free the node tree.
 */
static xmlNodePtr
xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
                        xmlDocPtr source, xmlXPathObjectPtr obj) {
    xmlNodePtr list = NULL, last = NULL;
    int i;

    if (source == NULL)
        source = ctxt->doc;
    if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
        (obj == NULL))
        return(NULL);
    switch (obj->type) {
        case XPATH_NODESET: {
            xmlNodeSetPtr set = obj->nodesetval;
            if (set == NULL)
                return(NULL);
            for (i = 0;i < set->nodeNr;i++) {
                if (set->nodeTab[i] == NULL)
                    continue;
                switch (set->nodeTab[i]->type) {
                    case XML_TEXT_NODE:
                    case XML_CDATA_SECTION_NODE:
                    case XML_ELEMENT_NODE:
                    case XML_ENTITY_REF_NODE:
                    case XML_ENTITY_NODE:
                    case XML_PI_NODE:
                    case XML_COMMENT_NODE:
                    case XML_DOCUMENT_NODE:
                    case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
                    case XML_DOCB_DOCUMENT_NODE:
#endif
                    case XML_XINCLUDE_END:
                        break;
                    case XML_XINCLUDE_START: {
                        xmlNodePtr tmp, cur = set->nodeTab[i];

                        cur = cur->next;
                        while (cur != NULL) {
                            switch(cur->type) {
                                case XML_TEXT_NODE:
                                case XML_CDATA_SECTION_NODE:
                                case XML_ELEMENT_NODE:
                                case XML_ENTITY_REF_NODE:
                                case XML_ENTITY_NODE:
                                case XML_PI_NODE:
                                case XML_COMMENT_NODE:
                                    tmp = xmlXIncludeCopyNode(ctxt, target,
                                                              source, cur);
                                    if (last == NULL) {
                                        list = last = tmp;
                                    } else {
                                        xmlAddNextSibling(last, tmp);
                                        last = tmp;
                                    }
                                    cur = cur->next;
                                    continue;
                                default:
                                    break;
                            }
                            break;
                        }
                        continue;
                    }
                    case XML_ATTRIBUTE_NODE:
                    case XML_NAMESPACE_DECL:
                    case XML_DOCUMENT_TYPE_NODE:
                    case XML_DOCUMENT_FRAG_NODE:
                    case XML_NOTATION_NODE:
                    case XML_DTD_NODE:
                    case XML_ELEMENT_DECL:
                    case XML_ATTRIBUTE_DECL:
                    case XML_ENTITY_DECL:
                        continue; /* for */
                }
                if (last == NULL)
                    list = last = xmlXIncludeCopyNode(ctxt, target, source,
                                                      set->nodeTab[i]);
                else {
                    xmlAddNextSibling(last,
                            xmlXIncludeCopyNode(ctxt, target, source,
                                                set->nodeTab[i]));
                    if (last->next != NULL)
                        last = last->next;
                }
            }
            break;
        }
        case XPATH_LOCATIONSET: {
            xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user;
            if (set == NULL)
                return(NULL);
            for (i = 0;i < set->locNr;i++) {
                if (last == NULL)
                    list = last = xmlXIncludeCopyXPointer(ctxt, target, source,
                                                          set->locTab[i]);
                else
                    xmlAddNextSibling(last,
                            xmlXIncludeCopyXPointer(ctxt, target, source,
                                                    set->locTab[i]));
                if (last != NULL) {
                    while (last->next != NULL)
                        last = last->next;
                }
            }
            break;
        }
#ifdef LIBXML_XPTR_ENABLED
        case XPATH_RANGE:
            return(xmlXIncludeCopyRange(ctxt, target, source, obj));
#endif
        case XPATH_POINT:
            /* points are ignored in XInclude */
            break;
        default:
            break;
    }
    return(list);
}
/************************************************************************
 *                                                                      *
 *                      XInclude I/O handling                           *
 *                                                                      *
 ************************************************************************/

typedef struct _xmlXIncludeMergeData xmlXIncludeMergeData;
typedef xmlXIncludeMergeData *xmlXIncludeMergeDataPtr;
struct _xmlXIncludeMergeData {
    xmlDocPtr doc;
    xmlXIncludeCtxtPtr ctxt;
};

/**
 * xmlXIncludeMergeOneEntity:
 * @ent: the entity
 * @doc:  the including doc
 * @nr: the entity name
 *
 * Inplements the merge of one entity
 */
static void
xmlXIncludeMergeEntity(xmlEntityPtr ent, xmlXIncludeMergeDataPtr data,
                       xmlChar *name ATTRIBUTE_UNUSED) {
    xmlEntityPtr ret, prev;
    xmlDocPtr doc;
    xmlXIncludeCtxtPtr ctxt;

    if ((ent == NULL) || (data == NULL))
        return;
    ctxt = data->ctxt;
    doc = data->doc;
    if ((ctxt == NULL) || (doc == NULL))
        return;
    switch (ent->etype) {
        case XML_INTERNAL_PARAMETER_ENTITY:
        case XML_EXTERNAL_PARAMETER_ENTITY:
        case XML_INTERNAL_PREDEFINED_ENTITY:
            return;
        case XML_INTERNAL_GENERAL_ENTITY:
        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
            break;
    }
    ret = xmlAddDocEntity(doc, ent->name, ent->etype, ent->ExternalID,
                          ent->SystemID, ent->content);
    if (ret != NULL) {
        if (ent->URI != NULL)
            ret->URI = xmlStrdup(ent->URI);
    } else {
        prev = xmlGetDocEntity(doc, ent->name);
        if (prev != NULL) {
            if (ent->etype != prev->etype)
                goto error;
        
            if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) {
                if (!xmlStrEqual(ent->SystemID, prev->SystemID))
                    goto error;
            } else if ((ent->ExternalID != NULL) &&
                       (prev->ExternalID != NULL)) {
                if (!xmlStrEqual(ent->ExternalID, prev->ExternalID))
                    goto error;
            } else if ((ent->content != NULL) && (prev->content != NULL)) {
                if (!xmlStrEqual(ent->content, prev->content))
                    goto error;
            } else {
                goto error;
            }

        }
    }
    return;
error:
    switch (ent->etype) {
        case XML_INTERNAL_PARAMETER_ENTITY:
        case XML_EXTERNAL_PARAMETER_ENTITY:
        case XML_INTERNAL_PREDEFINED_ENTITY:
        case XML_INTERNAL_GENERAL_ENTITY:
        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
            return;
        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
            break;
    }
    xmlXIncludeErr(ctxt, (xmlNodePtr) ent, XML_XINCLUDE_ENTITY_DEF_MISMATCH,
                   "mismatch in redefinition of entity %s\n",
                   ent->name);
}

/**
 * xmlXIncludeMergeEntities:
 * @ctxt: an XInclude context
 * @doc:  the including doc
 * @from:  the included doc
 *
 * Inplements the entity merge
 *
 * Returns 0 if merge succeeded, -1 if some processing failed
 */
static int
xmlXIncludeMergeEntities(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
                         xmlDocPtr from) {
    xmlNodePtr cur;
    xmlDtdPtr target, source;

    if (ctxt == NULL)
        return(-1);

    if ((from == NULL) || (from->intSubset == NULL))
        return(0);

    target = doc->intSubset;
    if (target == NULL) {
        cur = xmlDocGetRootElement(doc);
        if (cur == NULL)
            return(-1);
        target = xmlCreateIntSubset(doc, cur->name, NULL, NULL);
        if (target == NULL)
            return(-1);
    }

    source = from->intSubset;
    if ((source != NULL) && (source->entities != NULL)) {
        xmlXIncludeMergeData data;

        data.ctxt = ctxt;
        data.doc = doc;

        xmlHashScan((xmlHashTablePtr) source->entities,
                    (xmlHashScanner) xmlXIncludeMergeEntity, &data);
    }
    source = from->extSubset;
    if ((source != NULL) && (source->entities != NULL)) {
        xmlXIncludeMergeData data;

        data.ctxt = ctxt;
        data.doc = doc;

        /*
         * don't duplicate existing stuff when external subsets are the same
         */
        if ((!xmlStrEqual(target->ExternalID, source->ExternalID)) &&
            (!xmlStrEqual(target->SystemID, source->SystemID))) {
            xmlHashScan((xmlHashTablePtr) source->entities,
                        (xmlHashScanner) xmlXIncludeMergeEntity, &data);
        }
    }
    return(0);
}

/**
 * xmlXIncludeLoadDoc:
 * @ctxt:  the XInclude context
 * @url:  the associated URL
 * @nr:  the xinclude node number
 * 
 * Load the document, and store the result in the XInclude context
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
    xmlDocPtr doc;
    xmlURIPtr uri;
    xmlChar *URL;
    xmlChar *fragment = NULL;
    int i = 0;
#ifdef LIBXML_XPTR_ENABLED
    int saveFlags;
#endif

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Loading doc %s:%d\n", url, nr);
#endif
    /*
     * Check the URL and remove any fragment identifier
     */
    uri = xmlParseURI((const char *)url);
    if (uri == NULL) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                       XML_XINCLUDE_HREF_URI,
                       "invalid value URI %s\n", url);
        return(-1);
    }
    if (uri->fragment != NULL) {
        fragment = (xmlChar *) uri->fragment;
        uri->fragment = NULL;
    }
    if ((ctxt->incTab != NULL) && (ctxt->incTab[nr] != NULL) &&
        (ctxt->incTab[nr]->fragment != NULL)) {
        if (fragment != NULL) xmlFree(fragment);
        fragment = xmlStrdup(ctxt->incTab[nr]->fragment);
    }
    URL = xmlSaveUri(uri);
    xmlFreeURI(uri);
    if (URL == NULL) {
        if (ctxt->incTab != NULL)
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                           XML_XINCLUDE_HREF_URI,
                           "invalid value URI %s\n", url);
        else
            xmlXIncludeErr(ctxt, NULL,
                           XML_XINCLUDE_HREF_URI,
                           "invalid value URI %s\n", url);
        if (fragment != NULL)
            xmlFree(fragment);
        return(-1);
    }

    /*
     * Handling of references to the local document are done
     * directly through ctxt->doc.
     */
    if ((URL[0] == 0) || (URL[0] == '#') ||
        ((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) {
        doc = NULL;
        goto loaded;
    }

    /*
     * Prevent reloading twice the document.
     */
    for (i = 0; i < ctxt->incNr; i++) {
        if ((xmlStrEqual(URL, ctxt->incTab[i]->URI)) &&
            (ctxt->incTab[i]->doc != NULL)) {
            doc = ctxt->incTab[i]->doc;
#ifdef DEBUG_XINCLUDE
            printf("Already loaded %s\n", URL);
#endif
            goto loaded;
        }
    }

    /*
     * Load it.
     */
#ifdef DEBUG_XINCLUDE
    printf("loading %s\n", URL);
#endif
#ifdef LIBXML_XPTR_ENABLED
    /*
     * If this is an XPointer evaluation, we want to assure that
     * all entities have been resolved prior to processing the
     * referenced document
     */
    saveFlags = ctxt->parseFlags;
    if (fragment != NULL) {     /* if this is an XPointer eval */
        ctxt->parseFlags |= XML_PARSE_NOENT;
    }
#endif

    doc = xmlXIncludeParseFile(ctxt, (const char *)URL);
#ifdef LIBXML_XPTR_ENABLED
    ctxt->parseFlags = saveFlags;
#endif
    if (doc == NULL) {
        xmlFree(URL);
        if (fragment != NULL)
            xmlFree(fragment);
        return(-1);
    }
    ctxt->incTab[nr]->doc = doc;
    /*
     * It's possible that the requested URL has been mapped to a
     * completely different location (e.g. through a catalog entry).
     * To check for this, we compare the URL with that of the doc
     * and change it if they disagree (bug 146988).
     */
   if (!xmlStrEqual(URL, doc->URL)) {
       xmlFree(URL);
       URL = xmlStrdup(doc->URL);
   }
    for (i = nr + 1; i < ctxt->incNr; i++) {
        if (xmlStrEqual(URL, ctxt->incTab[i]->URI)) {
            ctxt->incTab[nr]->count++;
#ifdef DEBUG_XINCLUDE
            printf("Increasing %s count since reused\n", URL);
#endif
            break;
        }
    }

    /*
     * Make sure we have all entities fixed up
     */
    xmlXIncludeMergeEntities(ctxt, ctxt->doc, doc);

    /*
     * We don't need the DTD anymore, free up space
    if (doc->intSubset != NULL) {
        xmlUnlinkNode((xmlNodePtr) doc->intSubset);
        xmlFreeNode((xmlNodePtr) doc->intSubset);
        doc->intSubset = NULL;
    }
    if (doc->extSubset != NULL) {
        xmlUnlinkNode((xmlNodePtr) doc->extSubset);
        xmlFreeNode((xmlNodePtr) doc->extSubset);
        doc->extSubset = NULL;
    }
     */
    xmlXIncludeRecurseDoc(ctxt, doc, URL);

loaded:
    if (fragment == NULL) {
        /*
         * Add the top children list as the replacement copy.
         */
        if (doc == NULL)
        {
            /* Hopefully a DTD declaration won't be copied from
             * the same document */
            ctxt->incTab[nr]->inc = xmlCopyNodeList(ctxt->doc->children);
        } else {
            ctxt->incTab[nr]->inc = xmlXIncludeCopyNodeList(ctxt, ctxt->doc,
                                                       doc, doc->children);
        }
    } 
#ifdef LIBXML_XPTR_ENABLED
    else {
        /*
         * Computes the XPointer expression and make a copy used
         * as the replacement copy.
         */
        xmlXPathObjectPtr xptr;
        xmlXPathContextPtr xptrctxt;
        xmlNodeSetPtr set;

        if (doc == NULL) {
            xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr]->ref,
                                         NULL);
        } else {
            xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
        }
        if (xptrctxt == NULL) {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                           XML_XINCLUDE_XPTR_FAILED,
                           "could not create XPointer context\n", NULL);
            xmlFree(URL);
            xmlFree(fragment);
            return(-1);
        }
        xptr = xmlXPtrEval(fragment, xptrctxt);
        if (xptr == NULL) {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
                           XML_XINCLUDE_XPTR_FAILED,
                           "XPointer evaluation failed: #%s\n",
                           fragment);
            xmlXPathFreeContext(xptrctxt);
            xmlFree(URL);
            xmlFree(fragment);
            return(-1);
        }
        switch (xptr->type) {
            case XPATH_UNDEFINED:
            case XPATH_BOOLEAN:
            case XPATH_NUMBER:
            case XPATH_STRING:
            case XPATH_POINT:
            case XPATH_USERS:
            case XPATH_XSLT_TREE:
                xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                               XML_XINCLUDE_XPTR_RESULT,
                               "XPointer is not a range: #%s\n",
                               fragment);
                xmlXPathFreeContext(xptrctxt);
                xmlFree(URL);
                xmlFree(fragment);
                return(-1);
            case XPATH_NODESET:
                if ((xptr->nodesetval == NULL) ||
                    (xptr->nodesetval->nodeNr <= 0)) {
                    xmlXPathFreeContext(xptrctxt);
                    xmlFree(URL);
                    xmlFree(fragment);
                    return(-1);
                }

            case XPATH_RANGE:
            case XPATH_LOCATIONSET:
                break;
        }
        set = xptr->nodesetval;
        if (set != NULL) {
            for (i = 0;i < set->nodeNr;i++) {
                if (set->nodeTab[i] == NULL)
                    continue;
                switch (set->nodeTab[i]->type) {
                    case XML_ELEMENT_NODE:
                    case XML_TEXT_NODE:
                    case XML_CDATA_SECTION_NODE:
                    case XML_ENTITY_REF_NODE:
                    case XML_ENTITY_NODE:
                    case XML_PI_NODE:
                    case XML_COMMENT_NODE:
                    case XML_DOCUMENT_NODE:
                    case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
                    case XML_DOCB_DOCUMENT_NODE:
#endif
                        continue;

                    case XML_ATTRIBUTE_NODE:
                        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                                       XML_XINCLUDE_XPTR_RESULT,
                                       "XPointer selects an attribute: #%s\n",
                                       fragment);
                        set->nodeTab[i] = NULL;
                        continue;
                    case XML_NAMESPACE_DECL:
                        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                                       XML_XINCLUDE_XPTR_RESULT,
                                       "XPointer selects a namespace: #%s\n",
                                       fragment);
                        set->nodeTab[i] = NULL;
                        continue;
                    case XML_DOCUMENT_TYPE_NODE:
                    case XML_DOCUMENT_FRAG_NODE:
                    case XML_NOTATION_NODE:
                    case XML_DTD_NODE:
                    case XML_ELEMENT_DECL:
                    case XML_ATTRIBUTE_DECL:
                    case XML_ENTITY_DECL:
                    case XML_XINCLUDE_START:
                    case XML_XINCLUDE_END:
                        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                                       XML_XINCLUDE_XPTR_RESULT,
                                   "XPointer selects unexpected nodes: #%s\n",
                                       fragment);
                        set->nodeTab[i] = NULL;
                        set->nodeTab[i] = NULL;
                        continue; /* for */
                }
            }
        }
        if (doc == NULL) {
            ctxt->incTab[nr]->xptr = xptr;
            ctxt->incTab[nr]->inc = NULL;
        } else {
            ctxt->incTab[nr]->inc =
                xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
            xmlXPathFreeObject(xptr);
        }
        xmlXPathFreeContext(xptrctxt);
        xmlFree(fragment);
    }
#endif

    /*
     * Do the xml:base fixup if needed
     */
    if ((doc != NULL) && (URL != NULL) && (xmlStrchr(URL, (xmlChar) '/')) &&
        (!(ctxt->parseFlags & XML_PARSE_NOBASEFIX)) &&
        (!(doc->parseFlags & XML_PARSE_NOBASEFIX))) {
        xmlNodePtr node;
        xmlChar *base;
        xmlChar *curBase;

        /*
         * The base is only adjusted if "necessary", i.e. if the xinclude node
         * has a base specified, or the URL is relative
         */
        base = xmlGetNsProp(ctxt->incTab[nr]->ref, BAD_CAST "base",
                        XML_XML_NAMESPACE);
        if (base == NULL) {
            /*
             * No xml:base on the xinclude node, so we check whether the
             * URI base is different than (relative to) the context base
             */
            curBase = xmlBuildRelativeURI(URL, ctxt->base);
            if (curBase == NULL) {      /* Error return */
                xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                       XML_XINCLUDE_HREF_URI,
                       "trying to build relative URI from %s\n", URL);
            } else {
                /* If the URI doesn't contain a slash, it's not relative */
                if (!xmlStrchr(curBase, (xmlChar) '/'))
                    xmlFree(curBase);
                else
                    base = curBase;
            }
        }
        if (base != NULL) {     /* Adjustment may be needed */
            node = ctxt->incTab[nr]->inc;
            while (node != NULL) {
                /* Only work on element nodes */
                if (node->type == XML_ELEMENT_NODE) {
                    curBase = xmlNodeGetBase(node->doc, node);
                    /* If no current base, set it */
                    if (curBase == NULL) {
                        xmlNodeSetBase(node, base);
                    } else {
                        /*
                         * If the current base is the same as the
                         * URL of the document, then reset it to be
                         * the specified xml:base or the relative URI
                         */
                        if (xmlStrEqual(curBase, node->doc->URL)) {
                            xmlNodeSetBase(node, base);
                        } else {
                            /*
                             * If the element already has an xml:base
                             * set, then relativise it if necessary
                             */
                            xmlChar *xmlBase;
                            xmlBase = xmlGetNsProp(node,
                                            BAD_CAST "base",
                                            XML_XML_NAMESPACE);
                            if (xmlBase != NULL) {
                                xmlChar *relBase;
                                relBase = xmlBuildURI(xmlBase, base);
                                if (relBase == NULL) { /* error */
                                    xmlXIncludeErr(ctxt, 
                                                ctxt->incTab[nr]->ref,
                                                XML_XINCLUDE_HREF_URI,
                                        "trying to rebuild base from %s\n",
                                                xmlBase);
                                } else {
                                    xmlNodeSetBase(node, relBase);
                                    xmlFree(relBase);
                                }
                                xmlFree(xmlBase);
                            }
                        }
                        xmlFree(curBase);
                    }
                }
                node = node->next;
            }
            xmlFree(base);
        }
    }
    if ((nr < ctxt->incNr) && (ctxt->incTab[nr]->doc != NULL) &&
        (ctxt->incTab[nr]->count <= 1)) {
#ifdef DEBUG_XINCLUDE
        printf("freeing %s\n", ctxt->incTab[nr]->doc->URL);
#endif
        xmlFreeDoc(ctxt->incTab[nr]->doc);
        ctxt->incTab[nr]->doc = NULL;
    }
    xmlFree(URL);
    return(0);
}

/**
 * xmlXIncludeLoadTxt:
 * @ctxt:  the XInclude context
 * @url:  the associated URL
 * @nr:  the xinclude node number
 * 
 * Load the content, and store the result in the XInclude context
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
    xmlParserInputBufferPtr buf;
    xmlNodePtr node;
    xmlURIPtr uri;
    xmlChar *URL;
    int i;
    xmlChar *encoding = NULL;
    xmlCharEncoding enc = (xmlCharEncoding) 0;

    /*
     * Check the URL and remove any fragment identifier
     */
    uri = xmlParseURI((const char *)url);
    if (uri == NULL) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_HREF_URI,
                       "invalid value URI %s\n", url);
        return(-1);
    }
    if (uri->fragment != NULL) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_TEXT_FRAGMENT,
                       "fragment identifier forbidden for text: %s\n",
                       (const xmlChar *) uri->fragment);
        xmlFreeURI(uri);
        return(-1);
    }
    URL = xmlSaveUri(uri);
    xmlFreeURI(uri);
    if (URL == NULL) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_HREF_URI,
                       "invalid value URI %s\n", url);
        return(-1);
    }

    /*
     * Handling of references to the local document are done
     * directly through ctxt->doc.
     */
    if (URL[0] == 0) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                       XML_XINCLUDE_TEXT_DOCUMENT,
                       "text serialization of document not available\n", NULL);
        xmlFree(URL);
        return(-1);
    }

    /*
     * Prevent reloading twice the document.
     */
    for (i = 0; i < ctxt->txtNr; i++) {
        if (xmlStrEqual(URL, ctxt->txturlTab[i])) {
            node = xmlCopyNode(ctxt->txtTab[i], 1);
            goto loaded;
        }
    }
    /*
     * Try to get the encoding if available
     */
    if ((ctxt->incTab[nr] != NULL) && (ctxt->incTab[nr]->ref != NULL)) {
        encoding = xmlGetProp(ctxt->incTab[nr]->ref, XINCLUDE_PARSE_ENCODING);
    }
    if (encoding != NULL) {
        /*
         * TODO: we should not have to remap to the xmlCharEncoding
         *       predefined set, a better interface than
         *       xmlParserInputBufferCreateFilename should allow any
         *       encoding supported by iconv
         */
        enc = xmlParseCharEncoding((const char *) encoding);
        if (enc == XML_CHAR_ENCODING_ERROR) {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
                           XML_XINCLUDE_UNKNOWN_ENCODING,
                           "encoding %s not supported\n", encoding);
            xmlFree(encoding);
            xmlFree(URL);
            return(-1);
        }
        xmlFree(encoding);
    }

    /*
     * Load it.
     */
    buf = xmlParserInputBufferCreateFilename((const char *)URL, enc);
    if (buf == NULL) {
        xmlFree(URL);
        return(-1);
    }
    node = xmlNewText(NULL);

    /*
     * Scan all chars from the resource and add the to the node
     */
    while (xmlParserInputBufferRead(buf, 128) > 0) {
        int len;
        const xmlChar *content;

        content = xmlBufferContent(buf->buffer);
        len = xmlBufferLength(buf->buffer);
        for (i = 0;i < len;) {
            int cur;
            int l;

            cur = xmlStringCurrentChar(NULL, &content[i], &l);
            if (!IS_CHAR(cur)) {
                xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
                               XML_XINCLUDE_INVALID_CHAR,
                               "%s contains invalid char\n", URL);
                xmlFreeParserInputBuffer(buf);
                xmlFree(URL);
                return(-1);
            } else {
                xmlNodeAddContentLen(node, &content[i], l);
            }
            i += l;
        }
        xmlBufferShrink(buf->buffer, len);
    }
    xmlFreeParserInputBuffer(buf);
    xmlXIncludeAddTxt(ctxt, node, URL);

loaded:
    /*
     * Add the element as the replacement copy.
     */
    ctxt->incTab[nr]->inc = node;
    xmlFree(URL);
    return(0);
}

/**
 * xmlXIncludeLoadFallback:
 * @ctxt:  the XInclude context
 * @fallback:  the fallback node
 * @nr:  the xinclude node number
 * 
 * Load the content of the fallback node, and store the result
 * in the XInclude context
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, int nr) {
    xmlXIncludeCtxtPtr newctxt;
    int ret = 0;
    
    if ((fallback == NULL) || (ctxt == NULL))
        return(-1);
    if (fallback->children != NULL) {
        /*
         * It's possible that the fallback also has 'includes'
         * (Bug 129969), so we re-process the fallback just in case
         */
        newctxt = xmlXIncludeNewContext(ctxt->doc);
        if (newctxt == NULL)
            return (-1);
        newctxt->_private = ctxt->_private;
        newctxt->base = xmlStrdup(ctxt->base);  /* Inherit the base from the existing context */
        xmlXIncludeSetFlags(newctxt, ctxt->parseFlags);
        ret = xmlXIncludeDoProcess(newctxt, ctxt->doc, fallback->children);
        if (ctxt->nbErrors > 0)
            ret = -1;
        else if (ret > 0)
            ret = 0;    /* xmlXIncludeDoProcess can return +ve number */
        xmlXIncludeFreeContext(newctxt);

        ctxt->incTab[nr]->inc = xmlDocCopyNodeList(ctxt->doc,
                                                   fallback->children);
    } else {
        ctxt->incTab[nr]->inc = NULL;
        ctxt->incTab[nr]->emptyFb = 1;  /* flag empty callback */
    }
    return(ret);
}

/************************************************************************
 *                                                                      *
 *                      XInclude Processing                             *
 *                                                                      *
 ************************************************************************/

/**
 * xmlXIncludePreProcessNode:
 * @ctxt: an XInclude context
 * @node: an XInclude node
 *
 * Implement the XInclude preprocessing, currently just adding the element
 * for further processing.
 *
 * Returns the result list or NULL in case of error
 */
static xmlNodePtr
xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    xmlXIncludeAddNode(ctxt, node);
    return(NULL);
}

/**
 * xmlXIncludeLoadNode:
 * @ctxt: an XInclude context
 * @nr: the node number
 *
 * Find and load the infoset replacement for the given node.
 *
 * Returns 0 if substitution succeeded, -1 if some processing failed
 */
static int
xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) {
    xmlNodePtr cur;
    xmlChar *href;
    xmlChar *parse;
    xmlChar *base;
    xmlChar *oldBase;
    xmlChar *URI;
    int xml = 1; /* default Issue 64 */
    int ret;

    if (ctxt == NULL)
        return(-1);
    if ((nr < 0) || (nr >= ctxt->incNr))
        return(-1);
    cur = ctxt->incTab[nr]->ref;
    if (cur == NULL)
        return(-1);

    /*
     * read the attributes
     */
    href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF);
    if (href == NULL) {
        href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */
        if (href == NULL) 
            return(-1);
    }
    parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE);
    if (parse != NULL) {
        if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
            xml = 1;
        else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
            xml = 0;
        else {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
                           XML_XINCLUDE_PARSE_VALUE,
                           "invalid value %s for 'parse'\n", parse);
            if (href != NULL)
                xmlFree(href);
            if (parse != NULL)
                xmlFree(parse);
            return(-1);
        }
    }

    /*
     * compute the URI
     */
    base = xmlNodeGetBase(ctxt->doc, cur);
    if (base == NULL) {
        URI = xmlBuildURI(href, ctxt->doc->URL);
    } else {
        URI = xmlBuildURI(href, base);
    }
    if (URI == NULL) {
        xmlChar *escbase;
        xmlChar *eschref;
        /*
         * Some escaping may be needed
         */
        escbase = xmlURIEscape(base);
        eschref = xmlURIEscape(href);
        URI = xmlBuildURI(eschref, escbase);
        if (escbase != NULL)
            xmlFree(escbase);
        if (eschref != NULL)
            xmlFree(eschref);
    }
    if (URI == NULL) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                       XML_XINCLUDE_HREF_URI, "failed build URL\n", NULL);
        if (parse != NULL)
            xmlFree(parse);
        if (href != NULL)
            xmlFree(href);
        if (base != NULL)
            xmlFree(base);
        return(-1);
    }
#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "parse: %s\n",
            xml ? "xml": "text");
    xmlGenericError(xmlGenericErrorContext, "URI: %s\n", URI);
#endif

    /*
     * Save the base for this include (saving the current one)
     */
    oldBase = ctxt->base;
    ctxt->base = base;

    if (xml) {
        ret = xmlXIncludeLoadDoc(ctxt, URI, nr);
        /* xmlXIncludeGetFragment(ctxt, cur, URI); */
    } else {
        ret = xmlXIncludeLoadTxt(ctxt, URI, nr);
    }

    /*
     * Restore the original base before checking for fallback
     */
    ctxt->base = oldBase;
    
    if (ret < 0) {
        xmlNodePtr children;

        /*
         * Time to try a fallback if availble
         */
#ifdef DEBUG_XINCLUDE
        xmlGenericError(xmlGenericErrorContext, "error looking for fallback\n");
#endif
        children = cur->children;
        while (children != NULL) {
            if ((children->type == XML_ELEMENT_NODE) &&
                (children->ns != NULL) &&
                (xmlStrEqual(children->name, XINCLUDE_FALLBACK)) &&
                ((xmlStrEqual(children->ns->href, XINCLUDE_NS)) ||
                 (xmlStrEqual(children->ns->href, XINCLUDE_OLD_NS)))) {
                ret = xmlXIncludeLoadFallback(ctxt, children, nr);
                if (ret == 0) 
                    break;
            }
            children = children->next;
        }
    }
    if (ret < 0) {
        xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                       XML_XINCLUDE_NO_FALLBACK,
                       "could not load %s, and no fallback was found\n",
                       URI);
    }

    /*
     * Cleanup
     */
    if (URI != NULL)
        xmlFree(URI);
    if (parse != NULL)
        xmlFree(parse);
    if (href != NULL)
        xmlFree(href);
    if (base != NULL)
        xmlFree(base);
    return(0);
}

/**
 * xmlXIncludeIncludeNode:
 * @ctxt: an XInclude context
 * @nr: the node number
 *
 * Inplement the infoset replacement for the given node
 *
 * Returns 0 if substitution succeeded, -1 if some processing failed
 */
static int
xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
    xmlNodePtr cur, end, list, tmp;

    if (ctxt == NULL)
        return(-1);
    if ((nr < 0) || (nr >= ctxt->incNr))
        return(-1);
    cur = ctxt->incTab[nr]->ref;
    if (cur == NULL)
        return(-1);

    /*
     * If we stored an XPointer a late computation may be needed
     */
    if ((ctxt->incTab[nr]->inc == NULL) &&
        (ctxt->incTab[nr]->xptr != NULL)) {
        ctxt->incTab[nr]->inc =
            xmlXIncludeCopyXPointer(ctxt, ctxt->doc, ctxt->doc,
                                    ctxt->incTab[nr]->xptr);
        xmlXPathFreeObject(ctxt->incTab[nr]->xptr);
        ctxt->incTab[nr]->xptr = NULL;
    }
    list = ctxt->incTab[nr]->inc;
    ctxt->incTab[nr]->inc = NULL;

    /*
     * Check against the risk of generating a multi-rooted document
     */
    if ((cur->parent != NULL) &&
        (cur->parent->type != XML_ELEMENT_NODE)) {
        int nb_elem = 0;

        tmp = list;
        while (tmp != NULL) {
            if (tmp->type == XML_ELEMENT_NODE)
                nb_elem++;
            tmp = tmp->next;
        }
        if (nb_elem > 1) {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, 
                           XML_XINCLUDE_MULTIPLE_ROOT,
                       "XInclude error: would result in multiple root nodes\n",
                           NULL);
            return(-1);
        }
    }

    if (ctxt->parseFlags & XML_PARSE_NOXINCNODE) {
        /*
         * Add the list of nodes
         */
        while (list != NULL) {
            end = list;
            list = list->next;

            xmlAddPrevSibling(cur, end);
        }
        xmlUnlinkNode(cur);
        xmlFreeNode(cur);
    } else {
        /*
         * Change the current node as an XInclude start one, and add an
         * XInclude end one
         */
        cur->type = XML_XINCLUDE_START;
        end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL);
        if (end == NULL) {
            xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
                           XML_XINCLUDE_BUILD_FAILED,
                           "failed to build node\n", NULL);
            return(-1);
        }
        end->type = XML_XINCLUDE_END;
        xmlAddNextSibling(cur, end);

        /*
         * Add the list of nodes
         */
        while (list != NULL) {
            cur = list;
            list = list->next;

            xmlAddPrevSibling(end, cur);
        }
    }

    
    return(0);
}

/**
 * xmlXIncludeTestNode:
 * @ctxt: the XInclude processing context
 * @node: an XInclude node
 *
 * test if the node is an XInclude node
 *
 * Returns 1 true, 0 otherwise
 */
static int
xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    if (node == NULL)
        return(0);
    if (node->type != XML_ELEMENT_NODE)
        return(0);
    if (node->ns == NULL)
        return(0);
    if ((xmlStrEqual(node->ns->href, XINCLUDE_NS)) ||
        (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS))) {
        if (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS)) {
            if (ctxt->legacy == 0) {
#if 0 /* wait for the XML Core Working Group to get something stable ! */
                xmlXIncludeWarn(ctxt, node, XML_XINCLUDE_DEPRECATED_NS,
                       "Deprecated XInclude namespace found, use %s",
                                XINCLUDE_NS);
#endif
                ctxt->legacy = 1;
            }
        }
        if (xmlStrEqual(node->name, XINCLUDE_NODE)) {
            xmlNodePtr child = node->children;
            int nb_fallback = 0;

            while (child != NULL) {
                if ((child->type == XML_ELEMENT_NODE) &&
                    (child->ns != NULL) &&
                    ((xmlStrEqual(child->ns->href, XINCLUDE_NS)) ||
                     (xmlStrEqual(child->ns->href, XINCLUDE_OLD_NS)))) {
                    if (xmlStrEqual(child->name, XINCLUDE_NODE)) {
                        xmlXIncludeErr(ctxt, node,
                                       XML_XINCLUDE_INCLUDE_IN_INCLUDE,
                                       "%s has an 'include' child\n",
                                       XINCLUDE_NODE);
                        return(0);
                    }
                    if (xmlStrEqual(child->name, XINCLUDE_FALLBACK)) {
                        nb_fallback++;
                    }
                }
                child = child->next;
            }
            if (nb_fallback > 1) {
                xmlXIncludeErr(ctxt, node, XML_XINCLUDE_FALLBACKS_IN_INCLUDE,
                               "%s has multiple fallback children\n",
                               XINCLUDE_NODE);
                return(0);
            }
            return(1);
        }
        if (xmlStrEqual(node->name, XINCLUDE_FALLBACK)) {
            if ((node->parent == NULL) ||
                (node->parent->type != XML_ELEMENT_NODE) ||
                (node->parent->ns == NULL) ||
                ((!xmlStrEqual(node->parent->ns->href, XINCLUDE_NS)) &&
                 (!xmlStrEqual(node->parent->ns->href, XINCLUDE_OLD_NS))) ||
                (!xmlStrEqual(node->parent->name, XINCLUDE_NODE))) {
                xmlXIncludeErr(ctxt, node,
                               XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE,
                               "%s is not the child of an 'include'\n",
                               XINCLUDE_FALLBACK);
            }
        }
    }
    return(0);
}

/**
 * xmlXIncludeDoProcess:
 * @ctxt: the XInclude processing context
 * @doc: an XML document
 * @tree: the top of the tree to process
 *
 * Implement the XInclude substitution on the XML document @doc
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
static int
xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree) {
    xmlNodePtr cur;
    int ret = 0;
    int i, start;

    if ((doc == NULL) || (tree == NULL))
        return(-1);
    if (ctxt == NULL)
        return(-1);

    if (doc->URL != NULL) {
        ret = xmlXIncludeURLPush(ctxt, doc->URL);
        if (ret < 0)
            return(-1);
    }
    start = ctxt->incNr;

    /*
     * First phase: lookup the elements in the document
     */
    cur = tree;
    if (xmlXIncludeTestNode(ctxt, cur) == 1)
        xmlXIncludePreProcessNode(ctxt, cur);
    while ((cur != NULL) && (cur != tree->parent)) {
        /* TODO: need to work on entities -> stack */
        if ((cur->children != NULL) &&
            (cur->children->type != XML_ENTITY_DECL) &&
            (cur->children->type != XML_XINCLUDE_START) &&
            (cur->children->type != XML_XINCLUDE_END)) {
            cur = cur->children;
            if (xmlXIncludeTestNode(ctxt, cur))
                xmlXIncludePreProcessNode(ctxt, cur);
        } else if (cur->next != NULL) {
            cur = cur->next;
            if (xmlXIncludeTestNode(ctxt, cur))
                xmlXIncludePreProcessNode(ctxt, cur);
        } else {
            if (cur == tree)
                break;
            do {
                cur = cur->parent;
                if ((cur == NULL) || (cur == tree->parent))
                    break; /* do */
                if (cur->next != NULL) {
                    cur = cur->next;
                    if (xmlXIncludeTestNode(ctxt, cur))
                        xmlXIncludePreProcessNode(ctxt, cur);
                    break; /* do */
                }
            } while (cur != NULL);
        }
    }

    /*
     * Second Phase : collect the infosets fragments
     */
    for (i = start;i < ctxt->incNr; i++) {
        xmlXIncludeLoadNode(ctxt, i);
        ret++;
    }

    /*
     * Third phase: extend the original document infoset.
     *
     * Originally we bypassed the inclusion if there were any errors
     * encountered on any of the XIncludes.  A bug was raised (bug
     * 132588) requesting that we output the XIncludes without error,
     * so the check for inc!=NULL || xptr!=NULL was put in.  This may
     * give some other problems in the future, but for now it seems to
     * work ok.
     *
     */
    for (i = ctxt->incBase;i < ctxt->incNr; i++) {
        if ((ctxt->incTab[i]->inc != NULL) ||
                (ctxt->incTab[i]->xptr != NULL) ||
                (ctxt->incTab[i]->emptyFb != 0))        /* (empty fallback) */
            xmlXIncludeIncludeNode(ctxt, i);
    }

    if (doc->URL != NULL)
        xmlXIncludeURLPop(ctxt);
    return(ret);
}

/**
 * xmlXIncludeSetFlags:
 * @ctxt:  an XInclude processing context
 * @flags: a set of xmlParserOption used for parsing XML includes
 *
 * Set the flags used for further processing of XML resources.
 *
 * Returns 0 in case of success and -1 in case of error.
 */
int
xmlXIncludeSetFlags(xmlXIncludeCtxtPtr ctxt, int flags) {
    if (ctxt == NULL)
        return(-1);
    ctxt->parseFlags = flags;
    return(0);
}

/**
 * xmlXIncludeProcessTreeFlagsData:
 * @tree: an XML node
 * @flags: a set of xmlParserOption used for parsing XML includes
 * @data: application data that will be passed to the parser context
 *        in the _private field of the parser context(s)
 *
 * Implement the XInclude substitution on the XML node @tree
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */

int
xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree, int flags, void *data) {
    xmlXIncludeCtxtPtr ctxt;
    int ret = 0;

    if ((tree == NULL) || (tree->doc == NULL))
        return(-1);

    ctxt = xmlXIncludeNewContext(tree->doc);
    if (ctxt == NULL)
        return(-1);
    ctxt->_private = data;
    ctxt->base = xmlStrdup((xmlChar *)tree->doc->URL);
    xmlXIncludeSetFlags(ctxt, flags);
    ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree);
    if ((ret >= 0) && (ctxt->nbErrors > 0))
        ret = -1;

    xmlXIncludeFreeContext(ctxt);
    return(ret);
}

/**
 * xmlXIncludeProcessFlagsData:
 * @doc: an XML document
 * @flags: a set of xmlParserOption used for parsing XML includes
 * @data: application data that will be passed to the parser context
 *        in the _private field of the parser context(s)
 *
 * Implement the XInclude substitution on the XML document @doc
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcessFlagsData(xmlDocPtr doc, int flags, void *data) {
    xmlNodePtr tree;

    if (doc == NULL)
        return(-1);
    tree = xmlDocGetRootElement(doc);
    if (tree == NULL)
        return(-1);
    return(xmlXIncludeProcessTreeFlagsData(tree, flags, data));
}

/**
 * xmlXIncludeProcessFlags:
 * @doc: an XML document
 * @flags: a set of xmlParserOption used for parsing XML includes
 *
 * Implement the XInclude substitution on the XML document @doc
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcessFlags(xmlDocPtr doc, int flags) {
    return xmlXIncludeProcessFlagsData(doc, flags, NULL);
}

/**
 * xmlXIncludeProcess:
 * @doc: an XML document
 *
 * Implement the XInclude substitution on the XML document @doc
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcess(xmlDocPtr doc) {
    return(xmlXIncludeProcessFlags(doc, 0));
}

/**
 * xmlXIncludeProcessTreeFlags:
 * @tree: a node in an XML document
 * @flags: a set of xmlParserOption used for parsing XML includes
 *
 * Implement the XInclude substitution for the given subtree
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) {
    xmlXIncludeCtxtPtr ctxt;
    int ret = 0;

    if ((tree == NULL) || (tree->doc == NULL))
        return(-1);
    ctxt = xmlXIncludeNewContext(tree->doc);
    if (ctxt == NULL)
        return(-1);
    ctxt->base = xmlNodeGetBase(tree->doc, tree);
    xmlXIncludeSetFlags(ctxt, flags);
    ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree);
    if ((ret >= 0) && (ctxt->nbErrors > 0))
        ret = -1;

    xmlXIncludeFreeContext(ctxt);
    return(ret);
}

/**
 * xmlXIncludeProcessTree:
 * @tree: a node in an XML document
 *
 * Implement the XInclude substitution for the given subtree
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcessTree(xmlNodePtr tree) {
    return(xmlXIncludeProcessTreeFlags(tree, 0));
}

/**
 * xmlXIncludeProcessNode:
 * @ctxt: an existing XInclude context
 * @node: a node in an XML document
 *
 * Implement the XInclude substitution for the given subtree reusing
 * the informations and data coming from the given context.
 *
 * Returns 0 if no substitution were done, -1 if some processing failed
 *    or the number of substitutions done.
 */
int
xmlXIncludeProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    int ret = 0;

    if ((node == NULL) || (node->doc == NULL) || (ctxt == NULL))
        return(-1);
    ret = xmlXIncludeDoProcess(ctxt, node->doc, node);
    if ((ret >= 0) && (ctxt->nbErrors > 0))
        ret = -1;
    return(ret);
}

#else /* !LIBXML_XINCLUDE_ENABLED */
#endif
#define bottom_xinclude
#include "elfgcchack.h"

/* [<][>][^][v][top][bottom][index][help] */