(root)/
libxml2-2.12.3/
xinclude.c
       1  /*
       2   * xinclude.c : Code to implement XInclude processing
       3   *
       4   * World Wide Web Consortium W3C Last Call Working Draft 10 November 2003
       5   * http://www.w3.org/TR/2003/WD-xinclude-20031110
       6   *
       7   * See Copyright for the status of this software.
       8   *
       9   * daniel@veillard.com
      10   */
      11  
      12  #define IN_LIBXML
      13  #include "libxml.h"
      14  
      15  #include <string.h>
      16  #include <libxml/xmlmemory.h>
      17  #include <libxml/tree.h>
      18  #include <libxml/parser.h>
      19  #include <libxml/uri.h>
      20  #include <libxml/xpath.h>
      21  #include <libxml/xpointer.h>
      22  #include <libxml/parserInternals.h>
      23  #include <libxml/xmlerror.h>
      24  #include <libxml/encoding.h>
      25  
      26  #ifdef LIBXML_XINCLUDE_ENABLED
      27  #include <libxml/xinclude.h>
      28  
      29  #include "private/buf.h"
      30  #include "private/error.h"
      31  #include "private/tree.h"
      32  #include "private/xinclude.h"
      33  
      34  #define XINCLUDE_MAX_DEPTH 40
      35  
      36  /************************************************************************
      37   *									*
      38   *			XInclude context handling			*
      39   *									*
      40   ************************************************************************/
      41  
      42  /*
      43   * An XInclude context
      44   */
      45  typedef xmlChar *xmlURL;
      46  
      47  typedef struct _xmlXIncludeRef xmlXIncludeRef;
      48  typedef xmlXIncludeRef *xmlXIncludeRefPtr;
      49  struct _xmlXIncludeRef {
      50      xmlChar              *URI; /* the fully resolved resource URL */
      51      xmlChar         *fragment; /* the fragment in the URI */
      52      xmlNodePtr           elem; /* the xi:include element */
      53      xmlNodePtr            inc; /* the included copy */
      54      int                   xml; /* xml or txt */
      55      int	             fallback; /* fallback was loaded */
      56      int		      emptyFb; /* flag to show fallback empty */
      57      int		    expanding; /* flag to detect inclusion loops */
      58      int		      replace; /* should the node be replaced? */
      59  };
      60  
      61  typedef struct _xmlXIncludeDoc xmlXIncludeDoc;
      62  typedef xmlXIncludeDoc *xmlXIncludeDocPtr;
      63  struct _xmlXIncludeDoc {
      64      xmlDocPtr             doc; /* the parsed document */
      65      xmlChar              *url; /* the URL */
      66      int             expanding; /* flag to detect inclusion loops */
      67  };
      68  
      69  typedef struct _xmlXIncludeTxt xmlXIncludeTxt;
      70  typedef xmlXIncludeTxt *xmlXIncludeTxtPtr;
      71  struct _xmlXIncludeTxt {
      72      xmlChar		*text; /* text string */
      73      xmlChar              *url; /* the URL */
      74  };
      75  
      76  struct _xmlXIncludeCtxt {
      77      xmlDocPtr             doc; /* the source document */
      78      int                 incNr; /* number of includes */
      79      int                incMax; /* size of includes tab */
      80      xmlXIncludeRefPtr *incTab; /* array of included references */
      81  
      82      int                 txtNr; /* number of unparsed documents */
      83      int                txtMax; /* size of unparsed documents tab */
      84      xmlXIncludeTxt    *txtTab; /* array of unparsed documents */
      85  
      86      int                 urlNr; /* number of documents stacked */
      87      int                urlMax; /* size of document stack */
      88      xmlXIncludeDoc    *urlTab; /* document stack */
      89  
      90      int              nbErrors; /* the number of errors detected */
      91      int              fatalErr; /* abort processing */
      92      int                legacy; /* using XINCLUDE_OLD_NS */
      93      int            parseFlags; /* the flags used for parsing XML documents */
      94      xmlChar *		 base; /* the current xml:base */
      95  
      96      void            *_private; /* application data */
      97  
      98  #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
      99      unsigned long    incTotal; /* total number of processed inclusions */
     100  #endif
     101      int			depth; /* recursion depth */
     102      int		     isStream; /* streaming mode */
     103  };
     104  
     105  static xmlXIncludeRefPtr
     106  xmlXIncludeExpandNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node);
     107  
     108  static int
     109  xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref);
     110  
     111  static int
     112  xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlNodePtr tree);
     113  
     114  
     115  /************************************************************************
     116   *									*
     117   *			XInclude error handler				*
     118   *									*
     119   ************************************************************************/
     120  
     121  /**
     122   * xmlXIncludeErrMemory:
     123   * @extra:  extra information
     124   *
     125   * Handle an out of memory condition
     126   */
     127  static void
     128  xmlXIncludeErrMemory(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node,
     129                       const char *extra)
     130  {
     131      if (ctxt != NULL)
     132  	ctxt->nbErrors++;
     133      __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
     134                      XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
     135  		    extra, NULL, NULL, 0, 0,
     136  		    "Memory allocation failed : %s\n", extra);
     137  }
     138  
     139  /**
     140   * xmlXIncludeErr:
     141   * @ctxt: the XInclude context
     142   * @node: the context node
     143   * @msg:  the error message
     144   * @extra:  extra information
     145   *
     146   * Handle an XInclude error
     147   */
     148  static void LIBXML_ATTR_FORMAT(4,0)
     149  xmlXIncludeErr(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error,
     150                 const char *msg, const xmlChar *extra)
     151  {
     152      if (ctxt != NULL)
     153  	ctxt->nbErrors++;
     154      __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
     155                      error, XML_ERR_ERROR, NULL, 0,
     156  		    (const char *) extra, NULL, NULL, 0, 0,
     157  		    msg, (const char *) extra);
     158  }
     159  
     160  #if 0
     161  /**
     162   * xmlXIncludeWarn:
     163   * @ctxt: the XInclude context
     164   * @node: the context node
     165   * @msg:  the error message
     166   * @extra:  extra information
     167   *
     168   * Emit an XInclude warning.
     169   */
     170  static void LIBXML_ATTR_FORMAT(4,0)
     171  xmlXIncludeWarn(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error,
     172                 const char *msg, const xmlChar *extra)
     173  {
     174      __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE,
     175                      error, XML_ERR_WARNING, NULL, 0,
     176  		    (const char *) extra, NULL, NULL, 0, 0,
     177  		    msg, (const char *) extra);
     178  }
     179  #endif
     180  
     181  /**
     182   * xmlXIncludeGetProp:
     183   * @ctxt:  the XInclude context
     184   * @cur:  the node
     185   * @name:  the attribute name
     186   *
     187   * Get an XInclude attribute
     188   *
     189   * Returns the value (to be freed) or NULL if not found
     190   */
     191  static xmlChar *
     192  xmlXIncludeGetProp(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur,
     193                     const xmlChar *name) {
     194      xmlChar *ret;
     195  
     196      ret = xmlGetNsProp(cur, XINCLUDE_NS, name);
     197      if (ret != NULL)
     198          return(ret);
     199      if (ctxt->legacy != 0) {
     200  	ret = xmlGetNsProp(cur, XINCLUDE_OLD_NS, name);
     201  	if (ret != NULL)
     202  	    return(ret);
     203      }
     204      ret = xmlGetProp(cur, name);
     205      return(ret);
     206  }
     207  /**
     208   * xmlXIncludeFreeRef:
     209   * @ref: the XInclude reference
     210   *
     211   * Free an XInclude reference
     212   */
     213  static void
     214  xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) {
     215      if (ref == NULL)
     216  	return;
     217      if (ref->URI != NULL)
     218  	xmlFree(ref->URI);
     219      if (ref->fragment != NULL)
     220  	xmlFree(ref->fragment);
     221      xmlFree(ref);
     222  }
     223  
     224  /**
     225   * xmlXIncludeNewRef:
     226   * @ctxt: the XInclude context
     227   * @URI:  the resource URI
     228   * @elem:  the xi:include element
     229   *
     230   * Creates a new reference within an XInclude context
     231   *
     232   * Returns the new set
     233   */
     234  static xmlXIncludeRefPtr
     235  xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI,
     236  	          xmlNodePtr elem) {
     237      xmlXIncludeRefPtr ret;
     238  
     239      ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef));
     240      if (ret == NULL) {
     241          xmlXIncludeErrMemory(ctxt, elem, "growing XInclude context");
     242  	return(NULL);
     243      }
     244      memset(ret, 0, sizeof(xmlXIncludeRef));
     245      if (URI == NULL)
     246  	ret->URI = NULL;
     247      else
     248  	ret->URI = xmlStrdup(URI);
     249      ret->fragment = NULL;
     250      ret->elem = elem;
     251      ret->xml = 0;
     252      ret->inc = NULL;
     253      if (ctxt->incNr >= ctxt->incMax) {
     254          xmlXIncludeRefPtr *tmp;
     255  #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     256          size_t newSize = ctxt->incMax ? ctxt->incMax * 2 : 1;
     257  #else
     258          size_t newSize = ctxt->incMax ? ctxt->incMax * 2 : 4;
     259  #endif
     260  
     261          tmp = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab,
     262  	             newSize * sizeof(ctxt->incTab[0]));
     263          if (tmp == NULL) {
     264  	    xmlXIncludeErrMemory(ctxt, elem, "growing XInclude context");
     265  	    xmlXIncludeFreeRef(ret);
     266  	    return(NULL);
     267  	}
     268          ctxt->incTab = tmp;
     269          ctxt->incMax = newSize;
     270      }
     271      ctxt->incTab[ctxt->incNr++] = ret;
     272      return(ret);
     273  }
     274  
     275  /**
     276   * xmlXIncludeNewContext:
     277   * @doc:  an XML Document
     278   *
     279   * Creates a new XInclude context
     280   *
     281   * Returns the new set
     282   */
     283  xmlXIncludeCtxtPtr
     284  xmlXIncludeNewContext(xmlDocPtr doc) {
     285      xmlXIncludeCtxtPtr ret;
     286  
     287      if (doc == NULL)
     288  	return(NULL);
     289      ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt));
     290      if (ret == NULL) {
     291  	xmlXIncludeErrMemory(NULL, (xmlNodePtr) doc,
     292  	                     "creating XInclude context");
     293  	return(NULL);
     294      }
     295      memset(ret, 0, sizeof(xmlXIncludeCtxt));
     296      ret->doc = doc;
     297      ret->incNr = 0;
     298      ret->incMax = 0;
     299      ret->incTab = NULL;
     300      ret->nbErrors = 0;
     301      return(ret);
     302  }
     303  
     304  /**
     305   * xmlXIncludeFreeContext:
     306   * @ctxt: the XInclude context
     307   *
     308   * Free an XInclude context
     309   */
     310  void
     311  xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) {
     312      int i;
     313  
     314      if (ctxt == NULL)
     315  	return;
     316      if (ctxt->urlTab != NULL) {
     317  	for (i = 0; i < ctxt->urlNr; i++) {
     318  	    xmlFreeDoc(ctxt->urlTab[i].doc);
     319  	    xmlFree(ctxt->urlTab[i].url);
     320  	}
     321  	xmlFree(ctxt->urlTab);
     322      }
     323      for (i = 0;i < ctxt->incNr;i++) {
     324  	if (ctxt->incTab[i] != NULL)
     325  	    xmlXIncludeFreeRef(ctxt->incTab[i]);
     326      }
     327      if (ctxt->incTab != NULL)
     328  	xmlFree(ctxt->incTab);
     329      if (ctxt->txtTab != NULL) {
     330  	for (i = 0;i < ctxt->txtNr;i++) {
     331  	    xmlFree(ctxt->txtTab[i].text);
     332  	    xmlFree(ctxt->txtTab[i].url);
     333  	}
     334  	xmlFree(ctxt->txtTab);
     335      }
     336      if (ctxt->base != NULL) {
     337          xmlFree(ctxt->base);
     338      }
     339      xmlFree(ctxt);
     340  }
     341  
     342  /**
     343   * xmlXIncludeParseFile:
     344   * @ctxt:  the XInclude context
     345   * @URL:  the URL or file path
     346   *
     347   * parse a document for XInclude
     348   */
     349  static xmlDocPtr
     350  xmlXIncludeParseFile(xmlXIncludeCtxtPtr ctxt, const char *URL) {
     351      xmlDocPtr ret;
     352      xmlParserCtxtPtr pctxt;
     353      xmlParserInputPtr inputStream;
     354  
     355      xmlInitParser();
     356  
     357      pctxt = xmlNewParserCtxt();
     358      if (pctxt == NULL) {
     359  	xmlXIncludeErrMemory(ctxt, NULL, "cannot allocate parser context");
     360  	return(NULL);
     361      }
     362  
     363      /*
     364       * pass in the application data to the parser context.
     365       */
     366      pctxt->_private = ctxt->_private;
     367  
     368      /*
     369       * try to ensure that new documents included are actually
     370       * built with the same dictionary as the including document.
     371       */
     372      if ((ctxt->doc != NULL) && (ctxt->doc->dict != NULL)) {
     373         if (pctxt->dict != NULL)
     374              xmlDictFree(pctxt->dict);
     375  	pctxt->dict = ctxt->doc->dict;
     376  	xmlDictReference(pctxt->dict);
     377      }
     378  
     379      xmlCtxtUseOptions(pctxt, ctxt->parseFlags | XML_PARSE_DTDLOAD);
     380  
     381      /* Don't read from stdin. */
     382      if ((URL != NULL) && (strcmp(URL, "-") == 0))
     383          URL = "./-";
     384  
     385      inputStream = xmlLoadExternalEntity(URL, NULL, pctxt);
     386      if (inputStream == NULL) {
     387  	xmlFreeParserCtxt(pctxt);
     388  	return(NULL);
     389      }
     390  
     391      inputPush(pctxt, inputStream);
     392  
     393      if (pctxt->directory == NULL)
     394          pctxt->directory = xmlParserGetDirectory(URL);
     395  
     396      pctxt->loadsubset |= XML_DETECT_IDS;
     397  
     398      xmlParseDocument(pctxt);
     399  
     400      if (pctxt->wellFormed) {
     401          ret = pctxt->myDoc;
     402      }
     403      else {
     404          ret = NULL;
     405  	if (pctxt->myDoc != NULL)
     406  	    xmlFreeDoc(pctxt->myDoc);
     407          pctxt->myDoc = NULL;
     408      }
     409      xmlFreeParserCtxt(pctxt);
     410  
     411      return(ret);
     412  }
     413  
     414  /**
     415   * xmlXIncludeAddNode:
     416   * @ctxt:  the XInclude context
     417   * @cur:  the new node
     418   *
     419   * Add a new node to process to an XInclude context
     420   */
     421  static xmlXIncludeRefPtr
     422  xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
     423      xmlXIncludeRefPtr ref;
     424      xmlURIPtr uri;
     425      xmlChar *URL;
     426      xmlChar *fragment = NULL;
     427      xmlChar *href;
     428      xmlChar *parse;
     429      xmlChar *base;
     430      xmlChar *URI;
     431      int xml = 1;
     432      int local = 0;
     433  
     434  
     435      if (ctxt == NULL)
     436  	return(NULL);
     437      if (cur == NULL)
     438  	return(NULL);
     439  
     440      /*
     441       * read the attributes
     442       */
     443      href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF);
     444      if (href == NULL) {
     445  	href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */
     446  	if (href == NULL)
     447  	    return(NULL);
     448      }
     449      parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE);
     450      if (parse != NULL) {
     451  	if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
     452  	    xml = 1;
     453  	else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
     454  	    xml = 0;
     455  	else {
     456  	    xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE,
     457  	                   "invalid value %s for 'parse'\n", parse);
     458  	    if (href != NULL)
     459  		xmlFree(href);
     460  	    if (parse != NULL)
     461  		xmlFree(parse);
     462  	    return(NULL);
     463  	}
     464      }
     465  
     466      /*
     467       * compute the URI
     468       */
     469      base = xmlNodeGetBase(ctxt->doc, cur);
     470      if (base == NULL) {
     471  	URI = xmlBuildURI(href, ctxt->doc->URL);
     472      } else {
     473  	URI = xmlBuildURI(href, base);
     474      }
     475      if (URI == NULL) {
     476  	xmlChar *escbase;
     477  	xmlChar *eschref;
     478  	/*
     479  	 * Some escaping may be needed
     480  	 */
     481  	escbase = xmlURIEscape(base);
     482  	eschref = xmlURIEscape(href);
     483  	URI = xmlBuildURI(eschref, escbase);
     484  	if (escbase != NULL)
     485  	    xmlFree(escbase);
     486  	if (eschref != NULL)
     487  	    xmlFree(eschref);
     488      }
     489      if (parse != NULL)
     490  	xmlFree(parse);
     491      if (href != NULL)
     492  	xmlFree(href);
     493      if (base != NULL)
     494  	xmlFree(base);
     495      if (URI == NULL) {
     496  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
     497  	               "failed build URL\n", NULL);
     498  	return(NULL);
     499      }
     500      fragment = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE_XPOINTER);
     501  
     502      /*
     503       * Check the URL and remove any fragment identifier
     504       */
     505      uri = xmlParseURI((const char *)URI);
     506      if (uri == NULL) {
     507  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
     508  	               "invalid value URI %s\n", URI);
     509  	if (fragment != NULL)
     510  	    xmlFree(fragment);
     511  	xmlFree(URI);
     512  	return(NULL);
     513      }
     514  
     515      if (uri->fragment != NULL) {
     516          if (ctxt->legacy != 0) {
     517  	    if (fragment == NULL) {
     518  		fragment = (xmlChar *) uri->fragment;
     519  	    } else {
     520  		xmlFree(uri->fragment);
     521  	    }
     522  	} else {
     523  	    xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_FRAGMENT_ID,
     524         "Invalid fragment identifier in URI %s use the xpointer attribute\n",
     525                             URI);
     526  	    if (fragment != NULL)
     527  	        xmlFree(fragment);
     528  	    xmlFreeURI(uri);
     529  	    xmlFree(URI);
     530  	    return(NULL);
     531  	}
     532  	uri->fragment = NULL;
     533      }
     534      URL = xmlSaveUri(uri);
     535      xmlFreeURI(uri);
     536      if (URL == NULL) {
     537  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
     538  	               "invalid value URI %s\n", URI);
     539  	if (fragment != NULL)
     540  	    xmlFree(fragment);
     541          xmlFree(URI);
     542  	return(NULL);
     543      }
     544      xmlFree(URI);
     545  
     546      if (xmlStrEqual(URL, ctxt->doc->URL))
     547  	local = 1;
     548  
     549      /*
     550       * If local and xml then we need a fragment
     551       */
     552      if ((local == 1) && (xml == 1) &&
     553          ((fragment == NULL) || (fragment[0] == 0))) {
     554  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION,
     555  	               "detected a local recursion with no xpointer in %s\n",
     556  		       URL);
     557          xmlFree(URL);
     558          xmlFree(fragment);
     559  	return(NULL);
     560      }
     561  
     562      ref = xmlXIncludeNewRef(ctxt, URL, cur);
     563      xmlFree(URL);
     564      if (ref == NULL) {
     565          xmlFree(fragment);
     566  	return(NULL);
     567      }
     568      ref->fragment = fragment;
     569      ref->xml = xml;
     570      return(ref);
     571  }
     572  
     573  /**
     574   * xmlXIncludeRecurseDoc:
     575   * @ctxt:  the XInclude context
     576   * @doc:  the new document
     577   * @url:  the associated URL
     578   *
     579   * The XInclude recursive nature is handled at this point.
     580   */
     581  static void
     582  xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
     583  	              const xmlURL url ATTRIBUTE_UNUSED) {
     584      xmlDocPtr oldDoc;
     585      xmlXIncludeRefPtr *oldIncTab;
     586      int oldIncMax, oldIncNr, oldIsStream;
     587      int i;
     588  
     589      oldDoc = ctxt->doc;
     590      oldIncMax = ctxt->incMax;
     591      oldIncNr = ctxt->incNr;
     592      oldIncTab = ctxt->incTab;
     593      oldIsStream = ctxt->isStream;
     594      ctxt->doc = doc;
     595      ctxt->incMax = 0;
     596      ctxt->incNr = 0;
     597      ctxt->incTab = NULL;
     598      ctxt->isStream = 0;
     599  
     600      xmlXIncludeDoProcess(ctxt, xmlDocGetRootElement(doc));
     601  
     602      if (ctxt->incTab != NULL) {
     603          for (i = 0; i < ctxt->incNr; i++)
     604              xmlXIncludeFreeRef(ctxt->incTab[i]);
     605          xmlFree(ctxt->incTab);
     606      }
     607  
     608      ctxt->doc = oldDoc;
     609      ctxt->incMax = oldIncMax;
     610      ctxt->incNr = oldIncNr;
     611      ctxt->incTab = oldIncTab;
     612      ctxt->isStream = oldIsStream;
     613  }
     614  
     615  /************************************************************************
     616   *									*
     617   *			Node copy with specific semantic		*
     618   *									*
     619   ************************************************************************/
     620  
     621  /**
     622   * xmlXIncludeCopyNode:
     623   * @ctxt:  the XInclude context
     624   * @elem:  the element
     625   * @copyChildren:  copy children instead of node if true
     626   *
     627   * Make a copy of the node while expanding nested XIncludes.
     628   *
     629   * Returns a node list, not a single node.
     630   */
     631  static xmlNodePtr
     632  xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr elem,
     633                      int copyChildren) {
     634      xmlNodePtr result = NULL;
     635      xmlNodePtr insertParent = NULL;
     636      xmlNodePtr insertLast = NULL;
     637      xmlNodePtr cur;
     638  
     639      if (copyChildren) {
     640          cur = elem->children;
     641          if (cur == NULL)
     642              return(NULL);
     643      } else {
     644          cur = elem;
     645      }
     646  
     647      while (1) {
     648          xmlNodePtr copy = NULL;
     649          int recurse = 0;
     650  
     651          if ((cur->type == XML_DOCUMENT_NODE) ||
     652              (cur->type == XML_DTD_NODE)) {
     653              ;
     654          } else if ((cur->type == XML_ELEMENT_NODE) &&
     655                     (cur->ns != NULL) &&
     656                     (xmlStrEqual(cur->name, XINCLUDE_NODE)) &&
     657                     ((xmlStrEqual(cur->ns->href, XINCLUDE_NS)) ||
     658                      (xmlStrEqual(cur->ns->href, XINCLUDE_OLD_NS)))) {
     659              xmlXIncludeRefPtr ref = xmlXIncludeExpandNode(ctxt, cur);
     660  
     661              if (ref == NULL)
     662                  goto error;
     663              /*
     664               * TODO: Insert XML_XINCLUDE_START and XML_XINCLUDE_END nodes
     665               */
     666              if (ref->inc != NULL) {
     667                  copy = xmlStaticCopyNodeList(ref->inc, ctxt->doc,
     668                                               insertParent);
     669                  if (copy == NULL)
     670                      goto error;
     671              }
     672          } else {
     673              copy = xmlStaticCopyNode(cur, ctxt->doc, insertParent, 2);
     674              if (copy == NULL)
     675                  goto error;
     676  
     677              recurse = (cur->type != XML_ENTITY_REF_NODE) &&
     678                        (cur->children != NULL);
     679          }
     680  
     681          if (copy != NULL) {
     682              if (result == NULL)
     683                  result = copy;
     684              if (insertLast != NULL) {
     685                  insertLast->next = copy;
     686                  copy->prev = insertLast;
     687              } else if (insertParent != NULL) {
     688                  insertParent->children = copy;
     689              }
     690              insertLast = copy;
     691              while (insertLast->next != NULL) {
     692                  insertLast = insertLast->next;
     693              }
     694          }
     695  
     696          if (recurse) {
     697              cur = cur->children;
     698              insertParent = insertLast;
     699              insertLast = NULL;
     700              continue;
     701          }
     702  
     703          if (cur == elem)
     704              return(result);
     705  
     706          while (cur->next == NULL) {
     707              if (insertParent != NULL)
     708                  insertParent->last = insertLast;
     709              cur = cur->parent;
     710              if (cur == elem)
     711                  return(result);
     712              insertLast = insertParent;
     713              insertParent = insertParent->parent;
     714          }
     715  
     716          cur = cur->next;
     717      }
     718  
     719  error:
     720      xmlFreeNodeList(result);
     721      return(NULL);
     722  }
     723  
     724  #ifdef LIBXML_XPTR_LOCS_ENABLED
     725  /**
     726   * xmlXIncludeGetNthChild:
     727   * @cur:  the node
     728   * @no:  the child number
     729   *
     730   * Returns the @n'th element child of @cur or NULL
     731   */
     732  static xmlNodePtr
     733  xmlXIncludeGetNthChild(xmlNodePtr cur, int no) {
     734      int i;
     735      if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
     736          return(NULL);
     737      cur = cur->children;
     738      for (i = 0;i <= no;cur = cur->next) {
     739  	if (cur == NULL)
     740  	    return(cur);
     741  	if ((cur->type == XML_ELEMENT_NODE) ||
     742  	    (cur->type == XML_DOCUMENT_NODE) ||
     743  	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
     744  	    i++;
     745  	    if (i == no)
     746  		break;
     747  	}
     748      }
     749      return(cur);
     750  }
     751  
     752  xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); /* in xpointer.c */
     753  /**
     754   * xmlXIncludeCopyRange:
     755   * @ctxt:  the XInclude context
     756   * @obj:  the XPointer result from the evaluation.
     757   *
     758   * Build a node list tree copy of the XPointer result.
     759   *
     760   * Returns an xmlNodePtr list or NULL.
     761   *         The caller has to free the node tree.
     762   */
     763  static xmlNodePtr
     764  xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlXPathObjectPtr range) {
     765      /* pointers to generated nodes */
     766      xmlNodePtr list = NULL, last = NULL, listParent = NULL;
     767      xmlNodePtr tmp, tmp2;
     768      /* pointers to traversal nodes */
     769      xmlNodePtr start, cur, end;
     770      int index1, index2;
     771      int level = 0, lastLevel = 0, endLevel = 0, endFlag = 0;
     772  
     773      if ((ctxt == NULL) || (range == NULL))
     774  	return(NULL);
     775      if (range->type != XPATH_RANGE)
     776  	return(NULL);
     777      start = (xmlNodePtr) range->user;
     778  
     779      if ((start == NULL) || (start->type == XML_NAMESPACE_DECL))
     780  	return(NULL);
     781      end = range->user2;
     782      if (end == NULL)
     783  	return(xmlDocCopyNode(start, ctxt->doc, 1));
     784      if (end->type == XML_NAMESPACE_DECL)
     785          return(NULL);
     786  
     787      cur = start;
     788      index1 = range->index;
     789      index2 = range->index2;
     790      /*
     791       * level is depth of the current node under consideration
     792       * list is the pointer to the root of the output tree
     793       * listParent is a pointer to the parent of output tree (within
     794         the included file) in case we need to add another level
     795       * last is a pointer to the last node added to the output tree
     796       * lastLevel is the depth of last (relative to the root)
     797       */
     798      while (cur != NULL) {
     799  	/*
     800  	 * Check if our output tree needs a parent
     801  	 */
     802  	if (level < 0) {
     803  	    while (level < 0) {
     804  	        /* copy must include namespaces and properties */
     805  	        tmp2 = xmlDocCopyNode(listParent, ctxt->doc, 2);
     806  	        xmlAddChild(tmp2, list);
     807  	        list = tmp2;
     808  	        listParent = listParent->parent;
     809  	        level++;
     810  	    }
     811  	    last = list;
     812  	    lastLevel = 0;
     813  	}
     814  	/*
     815  	 * Check whether we need to change our insertion point
     816  	 */
     817  	while (level < lastLevel) {
     818  	    last = last->parent;
     819  	    lastLevel --;
     820  	}
     821  	if (cur == end) {	/* Are we at the end of the range? */
     822  	    if (cur->type == XML_TEXT_NODE) {
     823  		const xmlChar *content = cur->content;
     824  		int len;
     825  
     826  		if (content == NULL) {
     827  		    tmp = xmlNewDocTextLen(ctxt->doc, NULL, 0);
     828  		} else {
     829  		    len = index2;
     830  		    if ((cur == start) && (index1 > 1)) {
     831  			content += (index1 - 1);
     832  			len -= (index1 - 1);
     833  		    } else {
     834  			len = index2;
     835  		    }
     836  		    tmp = xmlNewDocTextLen(ctxt->doc, content, len);
     837  		}
     838  		/* single sub text node selection */
     839  		if (list == NULL)
     840  		    return(tmp);
     841  		/* prune and return full set */
     842  		if (level == lastLevel)
     843  		    xmlAddNextSibling(last, tmp);
     844  		else
     845  		    xmlAddChild(last, tmp);
     846  		return(list);
     847  	    } else {	/* ending node not a text node */
     848  	        endLevel = level;	/* remember the level of the end node */
     849  		endFlag = 1;
     850  		/* last node - need to take care of properties + namespaces */
     851  		tmp = xmlDocCopyNode(cur, ctxt->doc, 2);
     852  		if (list == NULL) {
     853  		    list = tmp;
     854  		    listParent = cur->parent;
     855  		    last = tmp;
     856  		} else {
     857  		    if (level == lastLevel)
     858  			last = xmlAddNextSibling(last, tmp);
     859  		    else {
     860  			last = xmlAddChild(last, tmp);
     861  			lastLevel = level;
     862  		    }
     863  		}
     864  
     865  		if (index2 > 1) {
     866  		    end = xmlXIncludeGetNthChild(cur, index2 - 1);
     867  		    index2 = 0;
     868  		}
     869  		if ((cur == start) && (index1 > 1)) {
     870  		    cur = xmlXIncludeGetNthChild(cur, index1 - 1);
     871  		    index1 = 0;
     872  		}  else {
     873  		    cur = cur->children;
     874  		}
     875  		level++;	/* increment level to show change */
     876  		/*
     877  		 * Now gather the remaining nodes from cur to end
     878  		 */
     879  		continue;	/* while */
     880  	    }
     881  	} else if (cur == start) {	/* Not at the end, are we at start? */
     882  	    if ((cur->type == XML_TEXT_NODE) ||
     883  		(cur->type == XML_CDATA_SECTION_NODE)) {
     884  		const xmlChar *content = cur->content;
     885  
     886  		if (content == NULL) {
     887  		    tmp = xmlNewDocTextLen(ctxt->doc, NULL, 0);
     888  		} else {
     889  		    if (index1 > 1) {
     890  			content += (index1 - 1);
     891  			index1 = 0;
     892  		    }
     893  		    tmp = xmlNewDocText(ctxt->doc, content);
     894  		}
     895  		last = list = tmp;
     896  		listParent = cur->parent;
     897  	    } else {		/* Not text node */
     898  	        /*
     899  		 * start of the range - need to take care of
     900  		 * properties and namespaces
     901  		 */
     902  		tmp = xmlDocCopyNode(cur, ctxt->doc, 2);
     903  		list = last = tmp;
     904  		listParent = cur->parent;
     905  		if (index1 > 1) {	/* Do we need to position? */
     906  		    cur = xmlXIncludeGetNthChild(cur, index1 - 1);
     907  		    level = lastLevel = 1;
     908  		    index1 = 0;
     909  		    /*
     910  		     * Now gather the remaining nodes from cur to end
     911  		     */
     912  		    continue; /* while */
     913  		}
     914  	    }
     915  	} else {
     916  	    tmp = NULL;
     917  	    switch (cur->type) {
     918  		case XML_DTD_NODE:
     919  		case XML_ELEMENT_DECL:
     920  		case XML_ATTRIBUTE_DECL:
     921  		case XML_ENTITY_NODE:
     922  		    /* Do not copy DTD information */
     923  		    break;
     924  		case XML_ENTITY_DECL:
     925  		    /* handle crossing entities -> stack needed */
     926  		    break;
     927  		case XML_XINCLUDE_START:
     928  		case XML_XINCLUDE_END:
     929  		    /* don't consider it part of the tree content */
     930  		    break;
     931  		case XML_ATTRIBUTE_NODE:
     932  		    /* Humm, should not happen ! */
     933  		    break;
     934  		default:
     935  		    /*
     936  		     * Middle of the range - need to take care of
     937  		     * properties and namespaces
     938  		     */
     939  		    tmp = xmlDocCopyNode(cur, ctxt->doc, 2);
     940  		    break;
     941  	    }
     942  	    if (tmp != NULL) {
     943  		if (level == lastLevel)
     944  		    last = xmlAddNextSibling(last, tmp);
     945  		else {
     946  		    last = xmlAddChild(last, tmp);
     947  		    lastLevel = level;
     948  		}
     949  	    }
     950  	}
     951  	/*
     952  	 * Skip to next node in document order
     953  	 */
     954  	cur = xmlXPtrAdvanceNode(cur, &level);
     955  	if (endFlag && (level >= endLevel))
     956  	    break;
     957      }
     958      return(list);
     959  }
     960  #endif /* LIBXML_XPTR_LOCS_ENABLED */
     961  
     962  /**
     963   * xmlXIncludeCopyXPointer:
     964   * @ctxt:  the XInclude context
     965   * @obj:  the XPointer result from the evaluation.
     966   *
     967   * Build a node list tree copy of the XPointer result.
     968   * This will drop Attributes and Namespace declarations.
     969   *
     970   * Returns an xmlNodePtr list or NULL.
     971   *         the caller has to free the node tree.
     972   */
     973  static xmlNodePtr
     974  xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlXPathObjectPtr obj) {
     975      xmlNodePtr list = NULL, last = NULL, copy;
     976      int i;
     977  
     978      if ((ctxt == NULL) || (obj == NULL))
     979  	return(NULL);
     980      switch (obj->type) {
     981          case XPATH_NODESET: {
     982  	    xmlNodeSetPtr set = obj->nodesetval;
     983  	    if (set == NULL)
     984  		return(NULL);
     985  	    for (i = 0;i < set->nodeNr;i++) {
     986                  xmlNodePtr node;
     987  
     988  		if (set->nodeTab[i] == NULL)
     989  		    continue;
     990  		switch (set->nodeTab[i]->type) {
     991  		    case XML_DOCUMENT_NODE:
     992  		    case XML_HTML_DOCUMENT_NODE:
     993                          node = xmlDocGetRootElement(
     994                                  (xmlDocPtr) set->nodeTab[i]);
     995                          if (node == NULL) {
     996                              xmlXIncludeErr(ctxt, set->nodeTab[i],
     997                                             XML_ERR_INTERNAL_ERROR,
     998                                             "document without root\n", NULL);
     999                              continue;
    1000                          }
    1001                          break;
    1002  		    case XML_TEXT_NODE:
    1003  		    case XML_CDATA_SECTION_NODE:
    1004  		    case XML_ELEMENT_NODE:
    1005  		    case XML_PI_NODE:
    1006  		    case XML_COMMENT_NODE:
    1007                          node = set->nodeTab[i];
    1008  			break;
    1009                      default:
    1010                          xmlXIncludeErr(ctxt, set->nodeTab[i],
    1011                                         XML_XINCLUDE_XPTR_RESULT,
    1012                                         "invalid node type in XPtr result\n",
    1013                                         NULL);
    1014  			continue; /* for */
    1015  		}
    1016                  /*
    1017                   * OPTIMIZE TODO: External documents should already be
    1018                   * expanded, so xmlDocCopyNode should work as well.
    1019                   * xmlXIncludeCopyNode is only required for the initial
    1020                   * document.
    1021                   */
    1022  		copy = xmlXIncludeCopyNode(ctxt, node, 0);
    1023                  if (copy == NULL) {
    1024                      xmlFreeNodeList(list);
    1025                      return(NULL);
    1026                  }
    1027  		if (last == NULL) {
    1028                      list = copy;
    1029                  } else {
    1030                      while (last->next != NULL)
    1031                          last = last->next;
    1032                      copy->prev = last;
    1033                      last->next = copy;
    1034  		}
    1035                  last = copy;
    1036  	    }
    1037  	    break;
    1038  	}
    1039  #ifdef LIBXML_XPTR_LOCS_ENABLED
    1040  	case XPATH_LOCATIONSET: {
    1041  	    xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user;
    1042  	    if (set == NULL)
    1043  		return(NULL);
    1044  	    for (i = 0;i < set->locNr;i++) {
    1045  		if (last == NULL)
    1046  		    list = last = xmlXIncludeCopyXPointer(ctxt,
    1047  			                                  set->locTab[i]);
    1048  		else
    1049  		    xmlAddNextSibling(last,
    1050  			    xmlXIncludeCopyXPointer(ctxt, set->locTab[i]));
    1051  		if (last != NULL) {
    1052  		    while (last->next != NULL)
    1053  			last = last->next;
    1054  		}
    1055  	    }
    1056  	    break;
    1057  	}
    1058  	case XPATH_RANGE:
    1059  	    return(xmlXIncludeCopyRange(ctxt, obj));
    1060  	case XPATH_POINT:
    1061  	    /* points are ignored in XInclude */
    1062  	    break;
    1063  #endif
    1064  	default:
    1065  	    break;
    1066      }
    1067      return(list);
    1068  }
    1069  /************************************************************************
    1070   *									*
    1071   *			XInclude I/O handling				*
    1072   *									*
    1073   ************************************************************************/
    1074  
    1075  typedef struct _xmlXIncludeMergeData xmlXIncludeMergeData;
    1076  typedef xmlXIncludeMergeData *xmlXIncludeMergeDataPtr;
    1077  struct _xmlXIncludeMergeData {
    1078      xmlDocPtr doc;
    1079      xmlXIncludeCtxtPtr ctxt;
    1080  };
    1081  
    1082  /**
    1083   * xmlXIncludeMergeOneEntity:
    1084   * @ent: the entity
    1085   * @doc:  the including doc
    1086   * @name: the entity name
    1087   *
    1088   * Implements the merge of one entity
    1089   */
    1090  static void
    1091  xmlXIncludeMergeEntity(void *payload, void *vdata,
    1092  	               const xmlChar *name ATTRIBUTE_UNUSED) {
    1093      xmlEntityPtr ent = (xmlEntityPtr) payload;
    1094      xmlXIncludeMergeDataPtr data = (xmlXIncludeMergeDataPtr) vdata;
    1095      xmlEntityPtr ret, prev;
    1096      xmlDocPtr doc;
    1097      xmlXIncludeCtxtPtr ctxt;
    1098  
    1099      if ((ent == NULL) || (data == NULL))
    1100  	return;
    1101      ctxt = data->ctxt;
    1102      doc = data->doc;
    1103      if ((ctxt == NULL) || (doc == NULL))
    1104  	return;
    1105      switch (ent->etype) {
    1106          case XML_INTERNAL_PARAMETER_ENTITY:
    1107          case XML_EXTERNAL_PARAMETER_ENTITY:
    1108          case XML_INTERNAL_PREDEFINED_ENTITY:
    1109  	    return;
    1110          case XML_INTERNAL_GENERAL_ENTITY:
    1111          case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
    1112          case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
    1113  	    break;
    1114      }
    1115      ret = xmlAddDocEntity(doc, ent->name, ent->etype, ent->ExternalID,
    1116  			  ent->SystemID, ent->content);
    1117      if (ret != NULL) {
    1118  	if (ent->URI != NULL)
    1119  	    ret->URI = xmlStrdup(ent->URI);
    1120      } else {
    1121  	prev = xmlGetDocEntity(doc, ent->name);
    1122  	if (prev != NULL) {
    1123  	    if (ent->etype != prev->etype)
    1124  		goto error;
    1125  
    1126  	    if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) {
    1127  		if (!xmlStrEqual(ent->SystemID, prev->SystemID))
    1128  		    goto error;
    1129  	    } else if ((ent->ExternalID != NULL) &&
    1130  		       (prev->ExternalID != NULL)) {
    1131  		if (!xmlStrEqual(ent->ExternalID, prev->ExternalID))
    1132  		    goto error;
    1133  	    } else if ((ent->content != NULL) && (prev->content != NULL)) {
    1134  		if (!xmlStrEqual(ent->content, prev->content))
    1135  		    goto error;
    1136  	    } else {
    1137  		goto error;
    1138  	    }
    1139  
    1140  	}
    1141      }
    1142      return;
    1143  error:
    1144      switch (ent->etype) {
    1145          case XML_INTERNAL_PARAMETER_ENTITY:
    1146          case XML_EXTERNAL_PARAMETER_ENTITY:
    1147          case XML_INTERNAL_PREDEFINED_ENTITY:
    1148          case XML_INTERNAL_GENERAL_ENTITY:
    1149          case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
    1150  	    return;
    1151          case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
    1152  	    break;
    1153      }
    1154      xmlXIncludeErr(ctxt, (xmlNodePtr) ent, XML_XINCLUDE_ENTITY_DEF_MISMATCH,
    1155                     "mismatch in redefinition of entity %s\n",
    1156  		   ent->name);
    1157  }
    1158  
    1159  /**
    1160   * xmlXIncludeMergeEntities:
    1161   * @ctxt: an XInclude context
    1162   * @doc:  the including doc
    1163   * @from:  the included doc
    1164   *
    1165   * Implements the entity merge
    1166   *
    1167   * Returns 0 if merge succeeded, -1 if some processing failed
    1168   */
    1169  static int
    1170  xmlXIncludeMergeEntities(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
    1171  	                 xmlDocPtr from) {
    1172      xmlNodePtr cur;
    1173      xmlDtdPtr target, source;
    1174  
    1175      if (ctxt == NULL)
    1176  	return(-1);
    1177  
    1178      if ((from == NULL) || (from->intSubset == NULL))
    1179  	return(0);
    1180  
    1181      target = doc->intSubset;
    1182      if (target == NULL) {
    1183  	cur = xmlDocGetRootElement(doc);
    1184  	if (cur == NULL)
    1185  	    return(-1);
    1186          target = xmlCreateIntSubset(doc, cur->name, NULL, NULL);
    1187  	if (target == NULL)
    1188  	    return(-1);
    1189      }
    1190  
    1191      source = from->intSubset;
    1192      if ((source != NULL) && (source->entities != NULL)) {
    1193  	xmlXIncludeMergeData data;
    1194  
    1195  	data.ctxt = ctxt;
    1196  	data.doc = doc;
    1197  
    1198  	xmlHashScan((xmlHashTablePtr) source->entities,
    1199  		    xmlXIncludeMergeEntity, &data);
    1200      }
    1201      source = from->extSubset;
    1202      if ((source != NULL) && (source->entities != NULL)) {
    1203  	xmlXIncludeMergeData data;
    1204  
    1205  	data.ctxt = ctxt;
    1206  	data.doc = doc;
    1207  
    1208  	/*
    1209  	 * don't duplicate existing stuff when external subsets are the same
    1210  	 */
    1211  	if ((!xmlStrEqual(target->ExternalID, source->ExternalID)) &&
    1212  	    (!xmlStrEqual(target->SystemID, source->SystemID))) {
    1213  	    xmlHashScan((xmlHashTablePtr) source->entities,
    1214  			xmlXIncludeMergeEntity, &data);
    1215  	}
    1216      }
    1217      return(0);
    1218  }
    1219  
    1220  /**
    1221   * xmlXIncludeLoadDoc:
    1222   * @ctxt:  the XInclude context
    1223   * @url:  the associated URL
    1224   * @ref:  an XMLXincludeRefPtr
    1225   *
    1226   * Load the document, and store the result in the XInclude context
    1227   *
    1228   * Returns 0 in case of success, -1 in case of failure
    1229   */
    1230  static int
    1231  xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url,
    1232                     xmlXIncludeRefPtr ref) {
    1233      xmlXIncludeDocPtr cache;
    1234      xmlDocPtr doc;
    1235      xmlURIPtr uri;
    1236      xmlChar *URL = NULL;
    1237      xmlChar *fragment = NULL;
    1238      int i = 0;
    1239      int ret = -1;
    1240      int cacheNr;
    1241  #ifdef LIBXML_XPTR_ENABLED
    1242      int saveFlags;
    1243  #endif
    1244  
    1245      /*
    1246       * Check the URL and remove any fragment identifier
    1247       */
    1248      uri = xmlParseURI((const char *)url);
    1249      if (uri == NULL) {
    1250  	xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI,
    1251  		       "invalid value URI %s\n", url);
    1252          goto error;
    1253      }
    1254      if (uri->fragment != NULL) {
    1255  	fragment = (xmlChar *) uri->fragment;
    1256  	uri->fragment = NULL;
    1257      }
    1258      if (ref->fragment != NULL) {
    1259  	if (fragment != NULL) xmlFree(fragment);
    1260  	fragment = xmlStrdup(ref->fragment);
    1261      }
    1262      URL = xmlSaveUri(uri);
    1263      xmlFreeURI(uri);
    1264      if (URL == NULL) {
    1265          xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI,
    1266                         "invalid value URI %s\n", url);
    1267          goto error;
    1268      }
    1269  
    1270      /*
    1271       * Handling of references to the local document are done
    1272       * directly through ctxt->doc.
    1273       */
    1274      if ((URL[0] == 0) || (URL[0] == '#') ||
    1275  	((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) {
    1276  	doc = ctxt->doc;
    1277          goto loaded;
    1278      }
    1279  
    1280      /*
    1281       * Prevent reloading the document twice.
    1282       */
    1283      for (i = 0; i < ctxt->urlNr; i++) {
    1284  	if (xmlStrEqual(URL, ctxt->urlTab[i].url)) {
    1285              if (ctxt->urlTab[i].expanding) {
    1286                  xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_RECURSION,
    1287                                 "inclusion loop detected\n", NULL);
    1288                  goto error;
    1289              }
    1290  	    doc = ctxt->urlTab[i].doc;
    1291              if (doc == NULL)
    1292                  goto error;
    1293  	    goto loaded;
    1294  	}
    1295      }
    1296  
    1297      /*
    1298       * Load it.
    1299       */
    1300  #ifdef LIBXML_XPTR_ENABLED
    1301      /*
    1302       * If this is an XPointer evaluation, we want to assure that
    1303       * all entities have been resolved prior to processing the
    1304       * referenced document
    1305       */
    1306      saveFlags = ctxt->parseFlags;
    1307      if (fragment != NULL) {	/* if this is an XPointer eval */
    1308  	ctxt->parseFlags |= XML_PARSE_NOENT;
    1309      }
    1310  #endif
    1311  
    1312      doc = xmlXIncludeParseFile(ctxt, (const char *)URL);
    1313  #ifdef LIBXML_XPTR_ENABLED
    1314      ctxt->parseFlags = saveFlags;
    1315  #endif
    1316  
    1317      /* Also cache NULL docs */
    1318      if (ctxt->urlNr >= ctxt->urlMax) {
    1319          xmlXIncludeDoc *tmp;
    1320  #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1321          size_t newSize = ctxt->urlMax ? ctxt->urlMax * 2 : 1;
    1322  #else
    1323          size_t newSize = ctxt->urlMax ? ctxt->urlMax * 2 : 8;
    1324  #endif
    1325  
    1326          tmp = xmlRealloc(ctxt->urlTab, sizeof(xmlXIncludeDoc) * newSize);
    1327          if (tmp == NULL) {
    1328              xmlXIncludeErrMemory(ctxt, ref->elem,
    1329                                   "growing XInclude URL table");
    1330              xmlFreeDoc(doc);
    1331              goto error;
    1332          }
    1333          ctxt->urlMax = newSize;
    1334          ctxt->urlTab = tmp;
    1335      }
    1336      cacheNr = ctxt->urlNr++;
    1337      cache = &ctxt->urlTab[cacheNr];
    1338      cache->doc = doc;
    1339      cache->url = xmlStrdup(URL);
    1340      cache->expanding = 0;
    1341  
    1342      if (doc == NULL)
    1343          goto error;
    1344      /*
    1345       * It's possible that the requested URL has been mapped to a
    1346       * completely different location (e.g. through a catalog entry).
    1347       * To check for this, we compare the URL with that of the doc
    1348       * and change it if they disagree (bug 146988).
    1349       */
    1350     if (!xmlStrEqual(URL, doc->URL)) {
    1351         xmlFree(URL);
    1352         URL = xmlStrdup(doc->URL);
    1353     }
    1354  
    1355      /*
    1356       * Make sure we have all entities fixed up
    1357       */
    1358      xmlXIncludeMergeEntities(ctxt, ctxt->doc, doc);
    1359  
    1360      /*
    1361       * We don't need the DTD anymore, free up space
    1362      if (doc->intSubset != NULL) {
    1363  	xmlUnlinkNode((xmlNodePtr) doc->intSubset);
    1364  	xmlFreeNode((xmlNodePtr) doc->intSubset);
    1365  	doc->intSubset = NULL;
    1366      }
    1367      if (doc->extSubset != NULL) {
    1368  	xmlUnlinkNode((xmlNodePtr) doc->extSubset);
    1369  	xmlFreeNode((xmlNodePtr) doc->extSubset);
    1370  	doc->extSubset = NULL;
    1371      }
    1372       */
    1373      cache->expanding = 1;
    1374      xmlXIncludeRecurseDoc(ctxt, doc, URL);
    1375      /* urlTab might be reallocated. */
    1376      cache = &ctxt->urlTab[cacheNr];
    1377      cache->expanding = 0;
    1378  
    1379  loaded:
    1380      if (fragment == NULL) {
    1381  	/*
    1382  	 * Add the top children list as the replacement copy.
    1383  	 */
    1384          ref->inc = xmlDocCopyNode(xmlDocGetRootElement(doc), ctxt->doc, 1);
    1385      }
    1386  #ifdef LIBXML_XPTR_ENABLED
    1387      else {
    1388  	/*
    1389  	 * Computes the XPointer expression and make a copy used
    1390  	 * as the replacement copy.
    1391  	 */
    1392  	xmlXPathObjectPtr xptr;
    1393  	xmlXPathContextPtr xptrctxt;
    1394  	xmlNodeSetPtr set;
    1395  
    1396          if (ctxt->isStream && doc == ctxt->doc) {
    1397  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED,
    1398  			   "XPointer expressions not allowed in streaming"
    1399                             " mode\n", NULL);
    1400              goto error;
    1401          }
    1402  
    1403  	xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
    1404  	if (xptrctxt == NULL) {
    1405  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED,
    1406  			   "could not create XPointer context\n", NULL);
    1407              goto error;
    1408  	}
    1409  	xptr = xmlXPtrEval(fragment, xptrctxt);
    1410  	if (xptr == NULL) {
    1411  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED,
    1412  			   "XPointer evaluation failed: #%s\n",
    1413  			   fragment);
    1414  	    xmlXPathFreeContext(xptrctxt);
    1415              goto error;
    1416  	}
    1417  	switch (xptr->type) {
    1418  	    case XPATH_UNDEFINED:
    1419  	    case XPATH_BOOLEAN:
    1420  	    case XPATH_NUMBER:
    1421  	    case XPATH_STRING:
    1422  #ifdef LIBXML_XPTR_LOCS_ENABLED
    1423  	    case XPATH_POINT:
    1424  #endif
    1425  	    case XPATH_USERS:
    1426  	    case XPATH_XSLT_TREE:
    1427  		xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_RESULT,
    1428  			       "XPointer is not a range: #%s\n",
    1429  			       fragment);
    1430                  xmlXPathFreeObject(xptr);
    1431  		xmlXPathFreeContext(xptrctxt);
    1432                  goto error;
    1433  	    case XPATH_NODESET:
    1434  	        if ((xptr->nodesetval == NULL) ||
    1435  		    (xptr->nodesetval->nodeNr <= 0)) {
    1436                      xmlXPathFreeObject(xptr);
    1437  		    xmlXPathFreeContext(xptrctxt);
    1438                      goto error;
    1439  		}
    1440  
    1441  #ifdef LIBXML_XPTR_LOCS_ENABLED
    1442  	    case XPATH_RANGE:
    1443  	    case XPATH_LOCATIONSET:
    1444  		break;
    1445  #endif
    1446  	}
    1447  	set = xptr->nodesetval;
    1448  	if (set != NULL) {
    1449  	    for (i = 0;i < set->nodeNr;i++) {
    1450  		if (set->nodeTab[i] == NULL)
    1451  		    continue;
    1452  		switch (set->nodeTab[i]->type) {
    1453  		    case XML_ELEMENT_NODE:
    1454  		    case XML_TEXT_NODE:
    1455  		    case XML_CDATA_SECTION_NODE:
    1456  		    case XML_ENTITY_REF_NODE:
    1457  		    case XML_ENTITY_NODE:
    1458  		    case XML_PI_NODE:
    1459  		    case XML_COMMENT_NODE:
    1460  		    case XML_DOCUMENT_NODE:
    1461  		    case XML_HTML_DOCUMENT_NODE:
    1462  			continue;
    1463  
    1464  		    case XML_ATTRIBUTE_NODE:
    1465  			xmlXIncludeErr(ctxt, ref->elem,
    1466  			               XML_XINCLUDE_XPTR_RESULT,
    1467  				       "XPointer selects an attribute: #%s\n",
    1468  				       fragment);
    1469  			set->nodeTab[i] = NULL;
    1470  			continue;
    1471  		    case XML_NAMESPACE_DECL:
    1472  			xmlXIncludeErr(ctxt, ref->elem,
    1473  			               XML_XINCLUDE_XPTR_RESULT,
    1474  				       "XPointer selects a namespace: #%s\n",
    1475  				       fragment);
    1476  			set->nodeTab[i] = NULL;
    1477  			continue;
    1478  		    case XML_DOCUMENT_TYPE_NODE:
    1479  		    case XML_DOCUMENT_FRAG_NODE:
    1480  		    case XML_NOTATION_NODE:
    1481  		    case XML_DTD_NODE:
    1482  		    case XML_ELEMENT_DECL:
    1483  		    case XML_ATTRIBUTE_DECL:
    1484  		    case XML_ENTITY_DECL:
    1485  		    case XML_XINCLUDE_START:
    1486  		    case XML_XINCLUDE_END:
    1487  			xmlXIncludeErr(ctxt, ref->elem,
    1488  			               XML_XINCLUDE_XPTR_RESULT,
    1489  				   "XPointer selects unexpected nodes: #%s\n",
    1490  				       fragment);
    1491  			set->nodeTab[i] = NULL;
    1492  			set->nodeTab[i] = NULL;
    1493  			continue; /* for */
    1494  		}
    1495  	    }
    1496  	}
    1497          ref->inc = xmlXIncludeCopyXPointer(ctxt, xptr);
    1498          xmlXPathFreeObject(xptr);
    1499  	xmlXPathFreeContext(xptrctxt);
    1500      }
    1501  #endif
    1502  
    1503      /*
    1504       * Do the xml:base fixup if needed
    1505       */
    1506      if ((doc != NULL) && (URL != NULL) &&
    1507          (!(ctxt->parseFlags & XML_PARSE_NOBASEFIX)) &&
    1508  	(!(doc->parseFlags & XML_PARSE_NOBASEFIX))) {
    1509  	xmlNodePtr node;
    1510  	xmlChar *base;
    1511  	xmlChar *curBase;
    1512  
    1513  	/*
    1514  	 * The base is only adjusted if "necessary", i.e. if the xinclude node
    1515  	 * has a base specified, or the URL is relative
    1516  	 */
    1517  	base = xmlGetNsProp(ref->elem, BAD_CAST "base", XML_XML_NAMESPACE);
    1518  	if (base == NULL) {
    1519  	    /*
    1520  	     * No xml:base on the xinclude node, so we check whether the
    1521  	     * URI base is different than (relative to) the context base
    1522  	     */
    1523  	    curBase = xmlBuildRelativeURI(URL, ctxt->base);
    1524  	    if (curBase == NULL) {	/* Error return */
    1525  	        xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI,
    1526  		       "trying to build relative URI from %s\n", URL);
    1527  	    } else {
    1528  		/* If the URI doesn't contain a slash, it's not relative */
    1529  	        if (!xmlStrchr(curBase, '/'))
    1530  		    xmlFree(curBase);
    1531  		else
    1532  		    base = curBase;
    1533  	    }
    1534  	}
    1535  	if (base != NULL) {	/* Adjustment may be needed */
    1536  	    node = ref->inc;
    1537  	    while (node != NULL) {
    1538  		/* Only work on element nodes */
    1539  		if (node->type == XML_ELEMENT_NODE) {
    1540  		    curBase = xmlNodeGetBase(node->doc, node);
    1541  		    /* If no current base, set it */
    1542  		    if (curBase == NULL) {
    1543  			xmlNodeSetBase(node, base);
    1544  		    } else {
    1545  			/*
    1546  			 * If the current base is the same as the
    1547  			 * URL of the document, then reset it to be
    1548  			 * the specified xml:base or the relative URI
    1549  			 */
    1550  			if (xmlStrEqual(curBase, node->doc->URL)) {
    1551  			    xmlNodeSetBase(node, base);
    1552  			} else {
    1553  			    /*
    1554  			     * If the element already has an xml:base
    1555  			     * set, then relativise it if necessary
    1556  			     */
    1557  			    xmlChar *xmlBase;
    1558  			    xmlBase = xmlGetNsProp(node,
    1559  					    BAD_CAST "base",
    1560  					    XML_XML_NAMESPACE);
    1561  			    if (xmlBase != NULL) {
    1562  				xmlChar *relBase;
    1563  				relBase = xmlBuildURI(xmlBase, base);
    1564  				if (relBase == NULL) { /* error */
    1565  				    xmlXIncludeErr(ctxt,
    1566  						ref->elem,
    1567  						XML_XINCLUDE_HREF_URI,
    1568  					"trying to rebuild base from %s\n",
    1569  						xmlBase);
    1570  				} else {
    1571  				    xmlNodeSetBase(node, relBase);
    1572  				    xmlFree(relBase);
    1573  				}
    1574  				xmlFree(xmlBase);
    1575  			    }
    1576  			}
    1577  			xmlFree(curBase);
    1578  		    }
    1579  		}
    1580  	        node = node->next;
    1581  	    }
    1582  	    xmlFree(base);
    1583  	}
    1584      }
    1585      ret = 0;
    1586  
    1587  error:
    1588      xmlFree(URL);
    1589      xmlFree(fragment);
    1590      return(ret);
    1591  }
    1592  
    1593  /**
    1594   * xmlXIncludeLoadTxt:
    1595   * @ctxt:  the XInclude context
    1596   * @url:  the associated URL
    1597   * @ref:  an XMLXincludeRefPtr
    1598   *
    1599   * Load the content, and store the result in the XInclude context
    1600   *
    1601   * Returns 0 in case of success, -1 in case of failure
    1602   */
    1603  static int
    1604  xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url,
    1605                     xmlXIncludeRefPtr ref) {
    1606      xmlParserInputBufferPtr buf;
    1607      xmlNodePtr node = NULL;
    1608      xmlURIPtr uri = NULL;
    1609      xmlChar *URL = NULL;
    1610      int i;
    1611      int ret = -1;
    1612      xmlChar *encoding = NULL;
    1613      xmlCharEncoding enc = (xmlCharEncoding) 0;
    1614      xmlParserCtxtPtr pctxt = NULL;
    1615      xmlParserInputPtr inputStream = NULL;
    1616      int len;
    1617      const xmlChar *content;
    1618  
    1619  
    1620      /* Don't read from stdin. */
    1621      if (xmlStrcmp(url, BAD_CAST "-") == 0)
    1622          url = BAD_CAST "./-";
    1623  
    1624      /*
    1625       * Check the URL and remove any fragment identifier
    1626       */
    1627      uri = xmlParseURI((const char *)url);
    1628      if (uri == NULL) {
    1629  	xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI,
    1630  	               "invalid value URI %s\n", url);
    1631  	goto error;
    1632      }
    1633      if (uri->fragment != NULL) {
    1634  	xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_TEXT_FRAGMENT,
    1635  	               "fragment identifier forbidden for text: %s\n",
    1636  		       (const xmlChar *) uri->fragment);
    1637  	goto error;
    1638      }
    1639      URL = xmlSaveUri(uri);
    1640      if (URL == NULL) {
    1641  	xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI,
    1642  	               "invalid value URI %s\n", url);
    1643  	goto error;
    1644      }
    1645  
    1646      /*
    1647       * Handling of references to the local document are done
    1648       * directly through ctxt->doc.
    1649       */
    1650      if (URL[0] == 0) {
    1651  	xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_TEXT_DOCUMENT,
    1652  		       "text serialization of document not available\n", NULL);
    1653  	goto error;
    1654      }
    1655  
    1656      /*
    1657       * Prevent reloading the document twice.
    1658       */
    1659      for (i = 0; i < ctxt->txtNr; i++) {
    1660  	if (xmlStrEqual(URL, ctxt->txtTab[i].url)) {
    1661              node = xmlNewDocText(ctxt->doc, ctxt->txtTab[i].text);
    1662  	    goto loaded;
    1663  	}
    1664      }
    1665  
    1666      /*
    1667       * Try to get the encoding if available
    1668       */
    1669      if (ref->elem != NULL) {
    1670  	encoding = xmlGetProp(ref->elem, XINCLUDE_PARSE_ENCODING);
    1671      }
    1672      if (encoding != NULL) {
    1673  	/*
    1674  	 * TODO: we should not have to remap to the xmlCharEncoding
    1675  	 *       predefined set, a better interface than
    1676  	 *       xmlParserInputBufferCreateFilename should allow any
    1677  	 *       encoding supported by iconv
    1678  	 */
    1679          enc = xmlParseCharEncoding((const char *) encoding);
    1680  	if (enc == XML_CHAR_ENCODING_ERROR) {
    1681  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_UNKNOWN_ENCODING,
    1682  			   "encoding %s not supported\n", encoding);
    1683  	    goto error;
    1684  	}
    1685      }
    1686  
    1687      /*
    1688       * Load it.
    1689       */
    1690      pctxt = xmlNewParserCtxt();
    1691      inputStream = xmlLoadExternalEntity((const char*)URL, NULL, pctxt);
    1692      if(inputStream == NULL)
    1693  	goto error;
    1694      buf = inputStream->buf;
    1695      if (buf == NULL)
    1696  	goto error;
    1697      if (buf->encoder)
    1698  	xmlCharEncCloseFunc(buf->encoder);
    1699      buf->encoder = xmlGetCharEncodingHandler(enc);
    1700      node = xmlNewDocText(ctxt->doc, NULL);
    1701      if (node == NULL) {
    1702          xmlXIncludeErrMemory(ctxt, ref->elem, NULL);
    1703  	goto error;
    1704      }
    1705  
    1706      /*
    1707       * Scan all chars from the resource and add the to the node
    1708       */
    1709      while (xmlParserInputBufferRead(buf, 4096) > 0)
    1710          ;
    1711  
    1712      content = xmlBufContent(buf->buffer);
    1713      len = xmlBufLength(buf->buffer);
    1714      for (i = 0; i < len;) {
    1715          int cur;
    1716          int l;
    1717  
    1718          l = len - i;
    1719          cur = xmlGetUTF8Char(&content[i], &l);
    1720          if ((cur < 0) || (!IS_CHAR(cur))) {
    1721              xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_INVALID_CHAR,
    1722                             "%s contains invalid char\n", URL);
    1723              goto error;
    1724          }
    1725  
    1726          i += l;
    1727      }
    1728  
    1729      xmlNodeAddContentLen(node, content, len);
    1730  
    1731      if (ctxt->txtNr >= ctxt->txtMax) {
    1732          xmlXIncludeTxt *tmp;
    1733  #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1734          size_t newSize = ctxt->txtMax ? ctxt->txtMax * 2 : 1;
    1735  #else
    1736          size_t newSize = ctxt->txtMax ? ctxt->txtMax * 2 : 8;
    1737  #endif
    1738  
    1739          tmp = xmlRealloc(ctxt->txtTab, sizeof(xmlXIncludeTxt) * newSize);
    1740          if (tmp == NULL) {
    1741              xmlXIncludeErrMemory(ctxt, ref->elem,
    1742                                   "growing XInclude text table");
    1743  	    goto error;
    1744          }
    1745          ctxt->txtMax = newSize;
    1746          ctxt->txtTab = tmp;
    1747      }
    1748      ctxt->txtTab[ctxt->txtNr].text = xmlStrdup(node->content);
    1749      ctxt->txtTab[ctxt->txtNr].url = xmlStrdup(URL);
    1750      ctxt->txtNr++;
    1751  
    1752  loaded:
    1753      /*
    1754       * Add the element as the replacement copy.
    1755       */
    1756      ref->inc = node;
    1757      node = NULL;
    1758      ret = 0;
    1759  
    1760  error:
    1761      xmlFreeNode(node);
    1762      xmlFreeInputStream(inputStream);
    1763      xmlFreeParserCtxt(pctxt);
    1764      xmlFree(encoding);
    1765      xmlFreeURI(uri);
    1766      xmlFree(URL);
    1767      return(ret);
    1768  }
    1769  
    1770  /**
    1771   * xmlXIncludeLoadFallback:
    1772   * @ctxt:  the XInclude context
    1773   * @fallback:  the fallback node
    1774   * @ref:  an XMLXincludeRefPtr
    1775   *
    1776   * Load the content of the fallback node, and store the result
    1777   * in the XInclude context
    1778   *
    1779   * Returns 0 in case of success, -1 in case of failure
    1780   */
    1781  static int
    1782  xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback,
    1783                          xmlXIncludeRefPtr ref) {
    1784      int ret = 0;
    1785      int oldNbErrors;
    1786  
    1787      if ((fallback == NULL) || (fallback->type == XML_NAMESPACE_DECL) ||
    1788          (ctxt == NULL))
    1789  	return(-1);
    1790      if (fallback->children != NULL) {
    1791  	/*
    1792  	 * It's possible that the fallback also has 'includes'
    1793  	 * (Bug 129969), so we re-process the fallback just in case
    1794  	 */
    1795          oldNbErrors = ctxt->nbErrors;
    1796  	ref->inc = xmlXIncludeCopyNode(ctxt, fallback, 1);
    1797  	if (ctxt->nbErrors > oldNbErrors)
    1798  	    ret = -1;
    1799          else if (ref->inc == NULL)
    1800              ref->emptyFb = 1;
    1801      } else {
    1802          ref->inc = NULL;
    1803  	ref->emptyFb = 1;	/* flag empty callback */
    1804      }
    1805      ref->fallback = 1;
    1806      return(ret);
    1807  }
    1808  
    1809  /************************************************************************
    1810   *									*
    1811   *			XInclude Processing				*
    1812   *									*
    1813   ************************************************************************/
    1814  
    1815  /**
    1816   * xmlXIncludeExpandNode:
    1817   * @ctxt: an XInclude context
    1818   * @node: an XInclude node
    1819   *
    1820   * If the XInclude node wasn't processed yet, create a new RefPtr,
    1821   * add it to ctxt->incTab and load the included items.
    1822   *
    1823   * Returns the new or existing xmlXIncludeRefPtr, or NULL in case of error.
    1824   */
    1825  static xmlXIncludeRefPtr
    1826  xmlXIncludeExpandNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    1827      xmlXIncludeRefPtr ref;
    1828      int i;
    1829  
    1830      if (ctxt->fatalErr)
    1831          return(NULL);
    1832      if (ctxt->depth >= XINCLUDE_MAX_DEPTH) {
    1833          xmlXIncludeErr(ctxt, node, XML_XINCLUDE_RECURSION,
    1834                         "maximum recursion depth exceeded\n", NULL);
    1835          ctxt->fatalErr = 1;
    1836          return(NULL);
    1837      }
    1838  
    1839  #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1840      /*
    1841       * The XInclude engine offers no protection against exponential
    1842       * expansion attacks similar to "billion laughs". Avoid timeouts by
    1843       * limiting the total number of replacements when fuzzing.
    1844       *
    1845       * Unfortuately, a single XInclude can already result in quadratic
    1846       * behavior:
    1847       *
    1848       *     <doc xmlns:xi="http://www.w3.org/2001/XInclude">
    1849       *       <xi:include xpointer="xpointer(//e)"/>
    1850       *       <e>
    1851       *         <e>
    1852       *           <e>
    1853       *             <!-- more nested elements -->
    1854       *           </e>
    1855       *         </e>
    1856       *       </e>
    1857       *     </doc>
    1858       */
    1859      if (ctxt->incTotal >= 20)
    1860          return(NULL);
    1861      ctxt->incTotal++;
    1862  #endif
    1863  
    1864      for (i = 0; i < ctxt->incNr; i++) {
    1865          if (ctxt->incTab[i]->elem == node) {
    1866              if (ctxt->incTab[i]->expanding) {
    1867                  xmlXIncludeErr(ctxt, node, XML_XINCLUDE_RECURSION,
    1868                                 "inclusion loop detected\n", NULL);
    1869                  return(NULL);
    1870              }
    1871              return(ctxt->incTab[i]);
    1872          }
    1873      }
    1874  
    1875      ref = xmlXIncludeAddNode(ctxt, node);
    1876      if (ref == NULL)
    1877          return(NULL);
    1878      ref->expanding = 1;
    1879      ctxt->depth++;
    1880      xmlXIncludeLoadNode(ctxt, ref);
    1881      ctxt->depth--;
    1882      ref->expanding = 0;
    1883  
    1884      return(ref);
    1885  }
    1886  
    1887  /**
    1888   * xmlXIncludeLoadNode:
    1889   * @ctxt: an XInclude context
    1890   * @ref: an xmlXIncludeRefPtr
    1891   *
    1892   * Find and load the infoset replacement for the given node.
    1893   *
    1894   * Returns 0 if substitution succeeded, -1 if some processing failed
    1895   */
    1896  static int
    1897  xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref) {
    1898      xmlNodePtr cur;
    1899      xmlChar *href;
    1900      xmlChar *parse;
    1901      xmlChar *base;
    1902      xmlChar *oldBase;
    1903      xmlChar *URI;
    1904      int xml = 1; /* default Issue 64 */
    1905      int ret;
    1906  
    1907      if ((ctxt == NULL) || (ref == NULL))
    1908  	return(-1);
    1909      cur = ref->elem;
    1910      if (cur == NULL)
    1911  	return(-1);
    1912  
    1913      /*
    1914       * read the attributes
    1915       */
    1916      href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF);
    1917      if (href == NULL) {
    1918  	href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */
    1919  	if (href == NULL)
    1920  	    return(-1);
    1921      }
    1922      parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE);
    1923      if (parse != NULL) {
    1924  	if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
    1925  	    xml = 1;
    1926  	else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
    1927  	    xml = 0;
    1928  	else {
    1929  	    xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE,
    1930  			   "invalid value %s for 'parse'\n", parse);
    1931  	    if (href != NULL)
    1932  		xmlFree(href);
    1933  	    if (parse != NULL)
    1934  		xmlFree(parse);
    1935  	    return(-1);
    1936  	}
    1937      }
    1938  
    1939      /*
    1940       * compute the URI
    1941       */
    1942      base = xmlNodeGetBase(ctxt->doc, cur);
    1943      if (base == NULL) {
    1944  	URI = xmlBuildURI(href, ctxt->doc->URL);
    1945      } else {
    1946  	URI = xmlBuildURI(href, base);
    1947      }
    1948      if (URI == NULL) {
    1949  	xmlChar *escbase;
    1950  	xmlChar *eschref;
    1951  	/*
    1952  	 * Some escaping may be needed
    1953  	 */
    1954  	escbase = xmlURIEscape(base);
    1955  	eschref = xmlURIEscape(href);
    1956  	URI = xmlBuildURI(eschref, escbase);
    1957  	if (escbase != NULL)
    1958  	    xmlFree(escbase);
    1959  	if (eschref != NULL)
    1960  	    xmlFree(eschref);
    1961      }
    1962      if (URI == NULL) {
    1963  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI,
    1964                         "failed build URL\n", NULL);
    1965  	if (parse != NULL)
    1966  	    xmlFree(parse);
    1967  	if (href != NULL)
    1968  	    xmlFree(href);
    1969  	if (base != NULL)
    1970  	    xmlFree(base);
    1971  	return(-1);
    1972      }
    1973  
    1974      /*
    1975       * Save the base for this include (saving the current one)
    1976       */
    1977      oldBase = ctxt->base;
    1978      ctxt->base = base;
    1979  
    1980      if (xml) {
    1981  	ret = xmlXIncludeLoadDoc(ctxt, URI, ref);
    1982  	/* xmlXIncludeGetFragment(ctxt, cur, URI); */
    1983      } else {
    1984  	ret = xmlXIncludeLoadTxt(ctxt, URI, ref);
    1985      }
    1986  
    1987      /*
    1988       * Restore the original base before checking for fallback
    1989       */
    1990      ctxt->base = oldBase;
    1991  
    1992      if (ret < 0) {
    1993  	xmlNodePtr children;
    1994  
    1995  	/*
    1996  	 * Time to try a fallback if available
    1997  	 */
    1998  	children = cur->children;
    1999  	while (children != NULL) {
    2000  	    if ((children->type == XML_ELEMENT_NODE) &&
    2001  		(children->ns != NULL) &&
    2002  		(xmlStrEqual(children->name, XINCLUDE_FALLBACK)) &&
    2003  		((xmlStrEqual(children->ns->href, XINCLUDE_NS)) ||
    2004  		 (xmlStrEqual(children->ns->href, XINCLUDE_OLD_NS)))) {
    2005  		ret = xmlXIncludeLoadFallback(ctxt, children, ref);
    2006  		break;
    2007  	    }
    2008  	    children = children->next;
    2009  	}
    2010      }
    2011      if (ret < 0) {
    2012  	xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_NO_FALLBACK,
    2013  		       "could not load %s, and no fallback was found\n",
    2014  		       URI);
    2015      }
    2016  
    2017      /*
    2018       * Cleanup
    2019       */
    2020      if (URI != NULL)
    2021  	xmlFree(URI);
    2022      if (parse != NULL)
    2023  	xmlFree(parse);
    2024      if (href != NULL)
    2025  	xmlFree(href);
    2026      if (base != NULL)
    2027  	xmlFree(base);
    2028      return(0);
    2029  }
    2030  
    2031  /**
    2032   * xmlXIncludeIncludeNode:
    2033   * @ctxt: an XInclude context
    2034   * @ref: an xmlXIncludeRefPtr
    2035   *
    2036   * Implement the infoset replacement for the given node
    2037   *
    2038   * Returns 0 if substitution succeeded, -1 if some processing failed
    2039   */
    2040  static int
    2041  xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref) {
    2042      xmlNodePtr cur, end, list, tmp;
    2043  
    2044      if ((ctxt == NULL) || (ref == NULL))
    2045  	return(-1);
    2046      cur = ref->elem;
    2047      if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
    2048  	return(-1);
    2049  
    2050      list = ref->inc;
    2051      ref->inc = NULL;
    2052      ref->emptyFb = 0;
    2053  
    2054      /*
    2055       * Check against the risk of generating a multi-rooted document
    2056       */
    2057      if ((cur->parent != NULL) &&
    2058  	(cur->parent->type != XML_ELEMENT_NODE)) {
    2059  	int nb_elem = 0;
    2060  
    2061  	tmp = list;
    2062  	while (tmp != NULL) {
    2063  	    if (tmp->type == XML_ELEMENT_NODE)
    2064  		nb_elem++;
    2065  	    tmp = tmp->next;
    2066  	}
    2067  	if (nb_elem > 1) {
    2068  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_MULTIPLE_ROOT,
    2069  		       "XInclude error: would result in multiple root nodes\n",
    2070  			   NULL);
    2071              xmlFreeNodeList(list);
    2072  	    return(-1);
    2073  	}
    2074      }
    2075  
    2076      if (ctxt->parseFlags & XML_PARSE_NOXINCNODE) {
    2077  	/*
    2078  	 * Add the list of nodes
    2079  	 */
    2080  	while (list != NULL) {
    2081  	    end = list;
    2082  	    list = list->next;
    2083  
    2084  	    xmlAddPrevSibling(cur, end);
    2085  	}
    2086          /*
    2087           * FIXME: xmlUnlinkNode doesn't coalesce text nodes.
    2088           */
    2089  	xmlUnlinkNode(cur);
    2090  	xmlFreeNode(cur);
    2091      } else {
    2092          xmlNodePtr child, next;
    2093  
    2094  	/*
    2095  	 * Change the current node as an XInclude start one, and add an
    2096  	 * XInclude end one
    2097  	 */
    2098          if (ref->fallback)
    2099              xmlUnsetProp(cur, BAD_CAST "href");
    2100  	cur->type = XML_XINCLUDE_START;
    2101          /* Remove fallback children */
    2102          for (child = cur->children; child != NULL; child = next) {
    2103              next = child->next;
    2104              xmlUnlinkNode(child);
    2105              xmlFreeNode(child);
    2106          }
    2107  	end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL);
    2108  	if (end == NULL) {
    2109  	    xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_BUILD_FAILED,
    2110  			   "failed to build node\n", NULL);
    2111              xmlFreeNodeList(list);
    2112  	    return(-1);
    2113  	}
    2114  	end->type = XML_XINCLUDE_END;
    2115  	xmlAddNextSibling(cur, end);
    2116  
    2117  	/*
    2118  	 * Add the list of nodes
    2119  	 */
    2120  	while (list != NULL) {
    2121  	    cur = list;
    2122  	    list = list->next;
    2123  
    2124  	    xmlAddPrevSibling(end, cur);
    2125  	}
    2126      }
    2127  
    2128  
    2129      return(0);
    2130  }
    2131  
    2132  /**
    2133   * xmlXIncludeTestNode:
    2134   * @ctxt: the XInclude processing context
    2135   * @node: an XInclude node
    2136   *
    2137   * test if the node is an XInclude node
    2138   *
    2139   * Returns 1 true, 0 otherwise
    2140   */
    2141  static int
    2142  xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    2143      if (node == NULL)
    2144  	return(0);
    2145      if (node->type != XML_ELEMENT_NODE)
    2146  	return(0);
    2147      if (node->ns == NULL)
    2148  	return(0);
    2149      if ((xmlStrEqual(node->ns->href, XINCLUDE_NS)) ||
    2150          (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS))) {
    2151  	if (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS)) {
    2152  	    if (ctxt->legacy == 0) {
    2153  #if 0 /* wait for the XML Core Working Group to get something stable ! */
    2154  		xmlXIncludeWarn(ctxt, node, XML_XINCLUDE_DEPRECATED_NS,
    2155  	               "Deprecated XInclude namespace found, use %s",
    2156  		                XINCLUDE_NS);
    2157  #endif
    2158  	        ctxt->legacy = 1;
    2159  	    }
    2160  	}
    2161  	if (xmlStrEqual(node->name, XINCLUDE_NODE)) {
    2162  	    xmlNodePtr child = node->children;
    2163  	    int nb_fallback = 0;
    2164  
    2165  	    while (child != NULL) {
    2166  		if ((child->type == XML_ELEMENT_NODE) &&
    2167  		    (child->ns != NULL) &&
    2168  		    ((xmlStrEqual(child->ns->href, XINCLUDE_NS)) ||
    2169  		     (xmlStrEqual(child->ns->href, XINCLUDE_OLD_NS)))) {
    2170  		    if (xmlStrEqual(child->name, XINCLUDE_NODE)) {
    2171  			xmlXIncludeErr(ctxt, node,
    2172  			               XML_XINCLUDE_INCLUDE_IN_INCLUDE,
    2173  				       "%s has an 'include' child\n",
    2174  				       XINCLUDE_NODE);
    2175  			return(0);
    2176  		    }
    2177  		    if (xmlStrEqual(child->name, XINCLUDE_FALLBACK)) {
    2178  			nb_fallback++;
    2179  		    }
    2180  		}
    2181  		child = child->next;
    2182  	    }
    2183  	    if (nb_fallback > 1) {
    2184  		xmlXIncludeErr(ctxt, node, XML_XINCLUDE_FALLBACKS_IN_INCLUDE,
    2185  			       "%s has multiple fallback children\n",
    2186  		               XINCLUDE_NODE);
    2187  		return(0);
    2188  	    }
    2189  	    return(1);
    2190  	}
    2191  	if (xmlStrEqual(node->name, XINCLUDE_FALLBACK)) {
    2192  	    if ((node->parent == NULL) ||
    2193  		(node->parent->type != XML_ELEMENT_NODE) ||
    2194  		(node->parent->ns == NULL) ||
    2195  		((!xmlStrEqual(node->parent->ns->href, XINCLUDE_NS)) &&
    2196  		 (!xmlStrEqual(node->parent->ns->href, XINCLUDE_OLD_NS))) ||
    2197  		(!xmlStrEqual(node->parent->name, XINCLUDE_NODE))) {
    2198  		xmlXIncludeErr(ctxt, node,
    2199  		               XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE,
    2200  			       "%s is not the child of an 'include'\n",
    2201  			       XINCLUDE_FALLBACK);
    2202  	    }
    2203  	}
    2204      }
    2205      return(0);
    2206  }
    2207  
    2208  /**
    2209   * xmlXIncludeDoProcess:
    2210   * @ctxt: the XInclude processing context
    2211   * @tree: the top of the tree to process
    2212   *
    2213   * Implement the XInclude substitution on the XML document @doc
    2214   *
    2215   * Returns 0 if no substitution were done, -1 if some processing failed
    2216   *    or the number of substitutions done.
    2217   */
    2218  static int
    2219  xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlNodePtr tree) {
    2220      xmlXIncludeRefPtr ref;
    2221      xmlNodePtr cur;
    2222      int ret = 0;
    2223      int i, start;
    2224  
    2225      if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL))
    2226  	return(-1);
    2227      if (ctxt == NULL)
    2228  	return(-1);
    2229  
    2230      /*
    2231       * First phase: lookup the elements in the document
    2232       */
    2233      start = ctxt->incNr;
    2234      cur = tree;
    2235      do {
    2236  	/* TODO: need to work on entities -> stack */
    2237          if (xmlXIncludeTestNode(ctxt, cur) == 1) {
    2238              ref = xmlXIncludeExpandNode(ctxt, cur);
    2239              /*
    2240               * Mark direct includes.
    2241               */
    2242              if (ref != NULL)
    2243                  ref->replace = 1;
    2244          } else if ((cur->children != NULL) &&
    2245                     ((cur->type == XML_DOCUMENT_NODE) ||
    2246                      (cur->type == XML_ELEMENT_NODE))) {
    2247              cur = cur->children;
    2248              continue;
    2249          }
    2250          do {
    2251              if (cur == tree)
    2252                  break;
    2253              if (cur->next != NULL) {
    2254                  cur = cur->next;
    2255                  break;
    2256              }
    2257              cur = cur->parent;
    2258          } while (cur != NULL);
    2259      } while ((cur != NULL) && (cur != tree));
    2260  
    2261      /*
    2262       * Second phase: extend the original document infoset.
    2263       */
    2264      for (i = start; i < ctxt->incNr; i++) {
    2265  	if (ctxt->incTab[i]->replace != 0) {
    2266              if ((ctxt->incTab[i]->inc != NULL) ||
    2267                  (ctxt->incTab[i]->emptyFb != 0)) {	/* (empty fallback) */
    2268                  xmlXIncludeIncludeNode(ctxt, ctxt->incTab[i]);
    2269              }
    2270              ctxt->incTab[i]->replace = 0;
    2271          } else {
    2272              /*
    2273               * Ignore includes which were added indirectly, for example
    2274               * inside xi:fallback elements.
    2275               */
    2276              if (ctxt->incTab[i]->inc != NULL) {
    2277                  xmlFreeNodeList(ctxt->incTab[i]->inc);
    2278                  ctxt->incTab[i]->inc = NULL;
    2279              }
    2280          }
    2281  	ret++;
    2282      }
    2283  
    2284      if (ctxt->isStream) {
    2285          /*
    2286           * incTab references nodes which will eventually be deleted in
    2287           * streaming mode. The table is only required for XPointer
    2288           * expressions which aren't allowed in streaming mode.
    2289           */
    2290          for (i = 0;i < ctxt->incNr;i++) {
    2291              xmlXIncludeFreeRef(ctxt->incTab[i]);
    2292          }
    2293          ctxt->incNr = 0;
    2294      }
    2295  
    2296      return(ret);
    2297  }
    2298  
    2299  /**
    2300   * xmlXIncludeSetFlags:
    2301   * @ctxt:  an XInclude processing context
    2302   * @flags: a set of xmlParserOption used for parsing XML includes
    2303   *
    2304   * Set the flags used for further processing of XML resources.
    2305   *
    2306   * Returns 0 in case of success and -1 in case of error.
    2307   */
    2308  int
    2309  xmlXIncludeSetFlags(xmlXIncludeCtxtPtr ctxt, int flags) {
    2310      if (ctxt == NULL)
    2311          return(-1);
    2312      ctxt->parseFlags = flags;
    2313      return(0);
    2314  }
    2315  
    2316  /**
    2317   * xmlXIncludeSetStreamingMode:
    2318   * @ctxt:  an XInclude processing context
    2319   * @mode:  whether streaming mode should be enabled
    2320   *
    2321   * In streaming mode, XPointer expressions aren't allowed.
    2322   *
    2323   * Returns 0 in case of success and -1 in case of error.
    2324   */
    2325  int
    2326  xmlXIncludeSetStreamingMode(xmlXIncludeCtxtPtr ctxt, int mode) {
    2327      if (ctxt == NULL)
    2328          return(-1);
    2329      ctxt->isStream = !!mode;
    2330      return(0);
    2331  }
    2332  
    2333  /**
    2334   * xmlXIncludeProcessTreeFlagsData:
    2335   * @tree: an XML node
    2336   * @flags: a set of xmlParserOption used for parsing XML includes
    2337   * @data: application data that will be passed to the parser context
    2338   *        in the _private field of the parser context(s)
    2339   *
    2340   * Implement the XInclude substitution on the XML node @tree
    2341   *
    2342   * Returns 0 if no substitution were done, -1 if some processing failed
    2343   *    or the number of substitutions done.
    2344   */
    2345  
    2346  int
    2347  xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree, int flags, void *data) {
    2348      xmlXIncludeCtxtPtr ctxt;
    2349      int ret = 0;
    2350  
    2351      if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) ||
    2352          (tree->doc == NULL))
    2353          return(-1);
    2354  
    2355      ctxt = xmlXIncludeNewContext(tree->doc);
    2356      if (ctxt == NULL)
    2357          return(-1);
    2358      ctxt->_private = data;
    2359      ctxt->base = xmlStrdup((xmlChar *)tree->doc->URL);
    2360      xmlXIncludeSetFlags(ctxt, flags);
    2361      ret = xmlXIncludeDoProcess(ctxt, tree);
    2362      if ((ret >= 0) && (ctxt->nbErrors > 0))
    2363          ret = -1;
    2364  
    2365      xmlXIncludeFreeContext(ctxt);
    2366      return(ret);
    2367  }
    2368  
    2369  /**
    2370   * xmlXIncludeProcessFlagsData:
    2371   * @doc: an XML document
    2372   * @flags: a set of xmlParserOption used for parsing XML includes
    2373   * @data: application data that will be passed to the parser context
    2374   *        in the _private field of the parser context(s)
    2375   *
    2376   * Implement the XInclude substitution on the XML document @doc
    2377   *
    2378   * Returns 0 if no substitution were done, -1 if some processing failed
    2379   *    or the number of substitutions done.
    2380   */
    2381  int
    2382  xmlXIncludeProcessFlagsData(xmlDocPtr doc, int flags, void *data) {
    2383      xmlNodePtr tree;
    2384  
    2385      if (doc == NULL)
    2386  	return(-1);
    2387      tree = xmlDocGetRootElement(doc);
    2388      if (tree == NULL)
    2389  	return(-1);
    2390      return(xmlXIncludeProcessTreeFlagsData(tree, flags, data));
    2391  }
    2392  
    2393  /**
    2394   * xmlXIncludeProcessFlags:
    2395   * @doc: an XML document
    2396   * @flags: a set of xmlParserOption used for parsing XML includes
    2397   *
    2398   * Implement the XInclude substitution on the XML document @doc
    2399   *
    2400   * Returns 0 if no substitution were done, -1 if some processing failed
    2401   *    or the number of substitutions done.
    2402   */
    2403  int
    2404  xmlXIncludeProcessFlags(xmlDocPtr doc, int flags) {
    2405      return xmlXIncludeProcessFlagsData(doc, flags, NULL);
    2406  }
    2407  
    2408  /**
    2409   * xmlXIncludeProcess:
    2410   * @doc: an XML document
    2411   *
    2412   * Implement the XInclude substitution on the XML document @doc
    2413   *
    2414   * Returns 0 if no substitution were done, -1 if some processing failed
    2415   *    or the number of substitutions done.
    2416   */
    2417  int
    2418  xmlXIncludeProcess(xmlDocPtr doc) {
    2419      return(xmlXIncludeProcessFlags(doc, 0));
    2420  }
    2421  
    2422  /**
    2423   * xmlXIncludeProcessTreeFlags:
    2424   * @tree: a node in an XML document
    2425   * @flags: a set of xmlParserOption used for parsing XML includes
    2426   *
    2427   * Implement the XInclude substitution for the given subtree
    2428   *
    2429   * Returns 0 if no substitution were done, -1 if some processing failed
    2430   *    or the number of substitutions done.
    2431   */
    2432  int
    2433  xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) {
    2434      xmlXIncludeCtxtPtr ctxt;
    2435      int ret = 0;
    2436  
    2437      if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) ||
    2438          (tree->doc == NULL))
    2439  	return(-1);
    2440      ctxt = xmlXIncludeNewContext(tree->doc);
    2441      if (ctxt == NULL)
    2442  	return(-1);
    2443      ctxt->base = xmlNodeGetBase(tree->doc, tree);
    2444      xmlXIncludeSetFlags(ctxt, flags);
    2445      ret = xmlXIncludeDoProcess(ctxt, tree);
    2446      if ((ret >= 0) && (ctxt->nbErrors > 0))
    2447  	ret = -1;
    2448  
    2449      xmlXIncludeFreeContext(ctxt);
    2450      return(ret);
    2451  }
    2452  
    2453  /**
    2454   * xmlXIncludeProcessTree:
    2455   * @tree: a node in an XML document
    2456   *
    2457   * Implement the XInclude substitution for the given subtree
    2458   *
    2459   * Returns 0 if no substitution were done, -1 if some processing failed
    2460   *    or the number of substitutions done.
    2461   */
    2462  int
    2463  xmlXIncludeProcessTree(xmlNodePtr tree) {
    2464      return(xmlXIncludeProcessTreeFlags(tree, 0));
    2465  }
    2466  
    2467  /**
    2468   * xmlXIncludeProcessNode:
    2469   * @ctxt: an existing XInclude context
    2470   * @node: a node in an XML document
    2471   *
    2472   * Implement the XInclude substitution for the given subtree reusing
    2473   * the information and data coming from the given context.
    2474   *
    2475   * Returns 0 if no substitution were done, -1 if some processing failed
    2476   *    or the number of substitutions done.
    2477   */
    2478  int
    2479  xmlXIncludeProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
    2480      int ret = 0;
    2481  
    2482      if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) ||
    2483          (node->doc == NULL) || (ctxt == NULL))
    2484  	return(-1);
    2485      ret = xmlXIncludeDoProcess(ctxt, node);
    2486      if ((ret >= 0) && (ctxt->nbErrors > 0))
    2487  	ret = -1;
    2488      return(ret);
    2489  }
    2490  
    2491  #else /* !LIBXML_XINCLUDE_ENABLED */
    2492  #endif