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