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