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