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