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