source: trunk/third/libxml2/xinclude.c @ 20735

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