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