2 * xinclude.c : Code to implement XInclude processing
4 * World Wide Web Consortium W3C Last Call Working Draft 16 May 2001
5 * http://www.w3.org/TR/2001/WD-xinclude-20010516/
7 * See Copyright for the status of this software.
13 * TODO: compute XPointers nodesets
14 * TODO: add an node intermediate API and handle recursion at this level
21 #include <libxml/xmlmemory.h>
22 #include <libxml/tree.h>
23 #include <libxml/parser.h>
24 #include <libxml/uri.h>
25 #include <libxml/xpointer.h>
26 #include <libxml/parserInternals.h>
27 #include <libxml/xmlerror.h>
28 #include <libxml/encoding.h>
29 #include <libxml/globals.h>
31 #ifdef LIBXML_XINCLUDE_ENABLED
32 #include <libxml/xinclude.h>
34 #define XINCLUDE_NS (const xmlChar *) "http://www.w3.org/2001/XInclude"
35 #define XINCLUDE_NODE (const xmlChar *) "include"
36 #define XINCLUDE_FALLBACK (const xmlChar *) "fallback"
37 #define XINCLUDE_HREF (const xmlChar *) "href"
38 #define XINCLUDE_PARSE (const xmlChar *) "parse"
39 #define XINCLUDE_PARSE_XML (const xmlChar *) "xml"
40 #define XINCLUDE_PARSE_TEXT (const xmlChar *) "text"
41 #define XINCLUDE_PARSE_ENCODING (const xmlChar *) "encoding"
43 /* #define DEBUG_XINCLUDE */
45 #ifdef LIBXML_DEBUG_ENABLED
46 #include <libxml/debugXML.h>
50 /************************************************************************
52 * XInclude contexts handling *
54 ************************************************************************/
59 typedef xmlChar
*xmlURL
;
61 typedef struct _xmlXIncludeRef xmlXIncludeRef
;
62 typedef xmlXIncludeRef
*xmlXIncludeRefPtr
;
63 struct _xmlXIncludeRef
{
64 xmlChar
*URI
; /* the rully resolved resource URL */
65 xmlChar
*fragment
; /* the fragment in the URI */
66 xmlDocPtr doc
; /* the parsed document */
67 xmlNodePtr ref
; /* the node making the reference in the source */
68 xmlNodePtr inc
; /* the included copy */
69 int xml
; /* xml or txt */
70 int count
; /* how many refs use that specific doc */
73 typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt
;
74 typedef xmlXIncludeCtxt
*xmlXIncludeCtxtPtr
;
75 struct _xmlXIncludeCtxt
{
76 xmlDocPtr doc
; /* the source document */
77 int incBase
; /* the first include for this document */
78 int incNr
; /* number of includes */
79 int incMax
; /* size of includes tab */
80 xmlXIncludeRefPtr
*incTab
; /* array of included references */
82 int txtNr
; /* number of unparsed documents */
83 int txtMax
; /* size of unparsed documents tab */
84 xmlNodePtr
*txtTab
; /* array of unparsed text nodes */
85 xmlURL
*txturlTab
; /* array of unparsed txtuments URLs */
89 xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr doc
);
93 * @ref: the XInclude reference
95 * Free an XInclude reference
98 xmlXIncludeFreeRef(xmlXIncludeRefPtr ref
) {
101 #ifdef DEBUG_XINCLUDE
102 xmlGenericError(xmlGenericErrorContext
, "Freeing ref\n");
104 if (ref
->doc
!= NULL
) {
105 #ifdef DEBUG_XINCLUDE
106 xmlGenericError(xmlGenericErrorContext
, "Freeing doc %s\n", ref
->URI
);
108 xmlFreeDoc(ref
->doc
);
110 if (ref
->URI
!= NULL
)
112 if (ref
->fragment
!= NULL
)
113 xmlFree(ref
->fragment
);
119 * @ctxt: the XInclude context
120 * @URI: the resource URI
122 * Creates a new reference within an XInclude context
124 * Returns the new set
126 static xmlXIncludeRefPtr
127 xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt
, const xmlChar
*URI
,
129 xmlXIncludeRefPtr ret
;
131 #ifdef DEBUG_XINCLUDE
132 xmlGenericError(xmlGenericErrorContext
, "New ref %s\n", URI
);
134 ret
= (xmlXIncludeRefPtr
) xmlMalloc(sizeof(xmlXIncludeRef
));
137 memset(ret
, 0, sizeof(xmlXIncludeRef
));
141 ret
->URI
= xmlStrdup(URI
);
142 ret
->fragment
= NULL
;
148 if (ctxt
->incMax
== 0) {
150 ctxt
->incTab
= (xmlXIncludeRefPtr
*) xmlMalloc(ctxt
->incMax
*
151 sizeof(ctxt
->incTab
[0]));
152 if (ctxt
->incTab
== NULL
) {
153 xmlGenericError(xmlGenericErrorContext
,
154 "malloc failed !\n");
155 xmlXIncludeFreeRef(ret
);
159 if (ctxt
->incNr
>= ctxt
->incMax
) {
161 ctxt
->incTab
= (xmlXIncludeRefPtr
*) xmlRealloc(ctxt
->incTab
,
162 ctxt
->incMax
* sizeof(ctxt
->incTab
[0]));
163 if (ctxt
->incTab
== NULL
) {
164 xmlGenericError(xmlGenericErrorContext
,
165 "realloc failed !\n");
166 xmlXIncludeFreeRef(ret
);
170 ctxt
->incTab
[ctxt
->incNr
++] = ret
;
175 * xmlXIncludeNewContext:
176 * @doc: an XML Document
178 * Creates a new XInclude context
180 * Returns the new set
182 static xmlXIncludeCtxtPtr
183 xmlXIncludeNewContext(xmlDocPtr doc
) {
184 xmlXIncludeCtxtPtr ret
;
186 #ifdef DEBUG_XINCLUDE
187 xmlGenericError(xmlGenericErrorContext
, "New context\n");
191 ret
= (xmlXIncludeCtxtPtr
) xmlMalloc(sizeof(xmlXIncludeCtxt
));
194 memset(ret
, 0, sizeof(xmlXIncludeCtxt
));
204 * xmlXIncludeFreeContext:
205 * @ctxt: the XInclude context
207 * Free an XInclude context
210 xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt
) {
213 #ifdef DEBUG_XINCLUDE
214 xmlGenericError(xmlGenericErrorContext
, "Freeing context\n");
218 for (i
= 0;i
< ctxt
->incNr
;i
++) {
219 if (ctxt
->incTab
[i
] != NULL
)
220 xmlXIncludeFreeRef(ctxt
->incTab
[i
]);
222 for (i
= 0;i
< ctxt
->txtNr
;i
++) {
223 if (ctxt
->txturlTab
[i
] != NULL
)
224 xmlFree(ctxt
->txturlTab
[i
]);
226 if (ctxt
->incTab
!= NULL
)
227 xmlFree(ctxt
->incTab
);
228 if (ctxt
->txtTab
!= NULL
)
229 xmlFree(ctxt
->txtTab
);
230 if (ctxt
->txturlTab
!= NULL
)
231 xmlFree(ctxt
->txturlTab
);
236 * xmlXIncludeAddNode:
237 * @ctxt: the XInclude context
240 * Add a new node to process to an XInclude context
243 xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt
, xmlNodePtr cur
) {
244 xmlXIncludeRefPtr ref
;
247 xmlChar
*fragment
= NULL
;
252 int xml
= 1; /* default Issue 64 */
260 #ifdef DEBUG_XINCLUDE
261 xmlGenericError(xmlGenericErrorContext
, "Add node\n");
264 * read the attributes
266 href
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_HREF
);
268 href
= xmlGetProp(cur
, XINCLUDE_HREF
);
270 xmlGenericError(xmlGenericErrorContext
, "XInclude: no href\n");
274 parse
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_PARSE
);
276 parse
= xmlGetProp(cur
, XINCLUDE_PARSE
);
279 if (xmlStrEqual(parse
, XINCLUDE_PARSE_XML
))
281 else if (xmlStrEqual(parse
, XINCLUDE_PARSE_TEXT
))
284 xmlGenericError(xmlGenericErrorContext
,
285 "XInclude: invalid value %s for %s\n",
286 parse
, XINCLUDE_PARSE
);
298 base
= xmlNodeGetBase(ctxt
->doc
, cur
);
300 URI
= xmlBuildURI(href
, ctxt
->doc
->URL
);
302 URI
= xmlBuildURI(href
, base
);
308 * Some escaping may be needed
310 escbase
= xmlURIEscape(base
);
311 eschref
= xmlURIEscape(href
);
312 URI
= xmlBuildURI(eschref
, escbase
);
325 xmlGenericError(xmlGenericErrorContext
, "XInclude: failed build URL\n");
330 * Check the URL and remove any fragment identifier
332 uri
= xmlParseURI((const char *)URI
);
334 xmlGenericError(xmlGenericErrorContext
,
335 "XInclude: invalid value URI %s\n", URI
);
338 if (uri
->fragment
!= NULL
) {
339 fragment
= (xmlChar
*) uri
->fragment
;
340 uri
->fragment
= NULL
;
342 URL
= xmlSaveUri(uri
);
346 xmlGenericError(xmlGenericErrorContext
,
347 "XInclude: invalid value URI %s\n", URI
);
348 if (fragment
!= NULL
)
353 ref
= xmlXIncludeNewRef(ctxt
, URL
, cur
);
357 ref
->fragment
= fragment
;
366 * xmlXIncludeRecurseDoc:
367 * @ctxt: the XInclude context
368 * @doc: the new document
369 * @url: the associated URL
371 * The XInclude recursive nature is handled at this point.
374 xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr doc
,
375 const xmlURL url ATTRIBUTE_UNUSED
) {
376 xmlXIncludeCtxtPtr newctxt
;
379 #ifdef DEBUG_XINCLUDE
380 xmlGenericError(xmlGenericErrorContext
, "Recursing in doc %s\n", doc
->URL
);
383 * Handle recursion here.
386 newctxt
= xmlXIncludeNewContext(doc
);
387 if (newctxt
!= NULL
) {
389 * Copy the existing document set
391 newctxt
->incMax
= ctxt
->incMax
;
392 newctxt
->incNr
= ctxt
->incNr
;
393 newctxt
->incTab
= (xmlXIncludeRefPtr
*) xmlMalloc(newctxt
->incMax
*
394 sizeof(newctxt
->incTab
[0]));
395 if (newctxt
->incTab
== NULL
) {
396 xmlGenericError(xmlGenericErrorContext
,
397 "malloc failed !\n");
403 * Inherit the documents already in use by others includes
405 newctxt
->incBase
= ctxt
->incNr
;
406 for (i
= 0;i
< ctxt
->incNr
;i
++) {
407 newctxt
->incTab
[i
] = ctxt
->incTab
[i
];
408 newctxt
->incTab
[i
]->count
++; /* prevent the recursion from
411 xmlXIncludeDoProcess(newctxt
, doc
);
412 for (i
= 0;i
< ctxt
->incNr
;i
++) {
413 newctxt
->incTab
[i
]->count
--;
414 newctxt
->incTab
[i
] = NULL
;
416 xmlXIncludeFreeContext(newctxt
);
418 #ifdef DEBUG_XINCLUDE
419 xmlGenericError(xmlGenericErrorContext
, "Done recursing in doc %s\n", url
);
425 * @ctxt: the XInclude context
426 * @txt: the new text node
427 * @url: the associated URL
429 * Add a new txtument to the list
432 xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt
, xmlNodePtr txt
, const xmlURL url
) {
433 #ifdef DEBUG_XINCLUDE
434 xmlGenericError(xmlGenericErrorContext
, "Adding text %s\n", url
);
436 if (ctxt
->txtMax
== 0) {
438 ctxt
->txtTab
= (xmlNodePtr
*) xmlMalloc(ctxt
->txtMax
*
439 sizeof(ctxt
->txtTab
[0]));
440 if (ctxt
->txtTab
== NULL
) {
441 xmlGenericError(xmlGenericErrorContext
,
442 "malloc failed !\n");
445 ctxt
->txturlTab
= (xmlURL
*) xmlMalloc(ctxt
->txtMax
*
446 sizeof(ctxt
->txturlTab
[0]));
447 if (ctxt
->txturlTab
== NULL
) {
448 xmlGenericError(xmlGenericErrorContext
,
449 "malloc failed !\n");
453 if (ctxt
->txtNr
>= ctxt
->txtMax
) {
455 ctxt
->txtTab
= (xmlNodePtr
*) xmlRealloc(ctxt
->txtTab
,
456 ctxt
->txtMax
* sizeof(ctxt
->txtTab
[0]));
457 if (ctxt
->txtTab
== NULL
) {
458 xmlGenericError(xmlGenericErrorContext
,
459 "realloc failed !\n");
462 ctxt
->txturlTab
= (xmlURL
*) xmlRealloc(ctxt
->txturlTab
,
463 ctxt
->txtMax
* sizeof(ctxt
->txturlTab
[0]));
464 if (ctxt
->txturlTab
== NULL
) {
465 xmlGenericError(xmlGenericErrorContext
,
466 "realloc failed !\n");
470 ctxt
->txtTab
[ctxt
->txtNr
] = txt
;
471 ctxt
->txturlTab
[ctxt
->txtNr
] = xmlStrdup(url
);
475 /************************************************************************
477 * Node copy with specific semantic *
479 ************************************************************************/
482 * xmlXIncludeCopyNode:
483 * @ctxt: the XInclude context
484 * @target: the document target
485 * @source: the document source
488 * Make a copy of the node while preserving the XInclude semantic
489 * of the Infoset copy
492 xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr target
,
493 xmlDocPtr source
, xmlNodePtr elem
) {
494 xmlNodePtr result
= NULL
;
496 if ((ctxt
== NULL
) || (target
== NULL
) || (source
== NULL
) ||
499 if (elem
->type
== XML_DTD_NODE
)
501 result
= xmlDocCopyNode(elem
, target
, 1);
506 * xmlXIncludeCopyNodeList:
507 * @ctxt: the XInclude context
508 * @target: the document target
509 * @source: the document source
510 * @elem: the element list
512 * Make a copy of the node list while preserving the XInclude semantic
513 * of the Infoset copy
516 xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr target
,
517 xmlDocPtr source
, xmlNodePtr elem
) {
518 xmlNodePtr cur
, res
, result
= NULL
, last
= NULL
;
520 if ((ctxt
== NULL
) || (target
== NULL
) || (source
== NULL
) ||
524 while (cur
!= NULL
) {
525 res
= xmlXIncludeCopyNode(ctxt
, target
, source
, cur
);
527 if (result
== NULL
) {
541 * xmlXInclueGetNthChild:
543 * @no: the child number
545 * Returns the @no'th element child of @cur or NULL
548 xmlXIncludeGetNthChild(xmlNodePtr cur
, int no
) {
553 for (i
= 0;i
<= no
;cur
= cur
->next
) {
556 if ((cur
->type
== XML_ELEMENT_NODE
) ||
557 (cur
->type
== XML_DOCUMENT_NODE
) ||
558 (cur
->type
== XML_HTML_DOCUMENT_NODE
)) {
567 xmlNodePtr
xmlXPtrAdvanceNode(xmlNodePtr cur
);
570 * xmlXIncludeCopyRange:
571 * @ctxt: the XInclude context
572 * @target: the document target
573 * @source: the document source
574 * @obj: the XPointer result from the evaluation.
576 * Build a node list tree copy of the XPointer result.
578 * Returns an xmlNodePtr list or NULL.
579 * the caller has to free the node tree.
582 xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr target
,
583 xmlDocPtr source
, xmlXPathObjectPtr range
) {
584 /* pointers to generated nodes */
585 xmlNodePtr list
= NULL
, last
= NULL
, parent
= NULL
, tmp
;
586 /* pointers to traversal nodes */
587 xmlNodePtr start
, cur
, end
;
590 if ((ctxt
== NULL
) || (target
== NULL
) || (source
== NULL
) ||
593 if (range
->type
!= XPATH_RANGE
)
595 start
= (xmlNodePtr
) range
->user
;
601 return(xmlDocCopyNode(start
, target
, 1));
604 index1
= range
->index
;
605 index2
= range
->index2
;
606 while (cur
!= NULL
) {
608 if (cur
->type
== XML_TEXT_NODE
) {
609 const xmlChar
*content
= cur
->content
;
612 if (content
== NULL
) {
613 tmp
= xmlNewTextLen(NULL
, 0);
616 if ((cur
== start
) && (index1
> 1)) {
617 content
+= (index1
- 1);
623 tmp
= xmlNewTextLen(content
, len
);
625 /* single sub text node selection */
628 /* prune and return full set */
630 xmlAddNextSibling(last
, tmp
);
632 xmlAddChild(parent
, tmp
);
635 tmp
= xmlDocCopyNode(cur
, target
, 0);
640 xmlAddNextSibling(last
, tmp
);
642 xmlAddChild(parent
, tmp
);
648 end
= xmlXIncludeGetNthChild(cur
, index2
- 1);
651 if ((cur
== start
) && (index1
> 1)) {
652 cur
= xmlXIncludeGetNthChild(cur
, index1
- 1);
658 * Now gather the remaining nodes from cur to end
660 continue; /* while */
662 } else if ((cur
== start
) &&
663 (list
== NULL
) /* looks superfluous but ... */ ) {
664 if ((cur
->type
== XML_TEXT_NODE
) ||
665 (cur
->type
== XML_CDATA_SECTION_NODE
)) {
666 const xmlChar
*content
= cur
->content
;
668 if (content
== NULL
) {
669 tmp
= xmlNewTextLen(NULL
, 0);
672 content
+= (index1
- 1);
674 tmp
= xmlNewText(content
);
678 if ((cur
== start
) && (index1
> 1)) {
679 tmp
= xmlDocCopyNode(cur
, target
, 0);
683 cur
= xmlXIncludeGetNthChild(cur
, index1
- 1);
686 * Now gather the remaining nodes from cur to end
688 continue; /* while */
690 tmp
= xmlDocCopyNode(cur
, target
, 1);
699 case XML_ELEMENT_DECL
:
700 case XML_ATTRIBUTE_DECL
:
701 case XML_ENTITY_NODE
:
702 /* Do not copy DTD informations */
704 case XML_ENTITY_DECL
:
705 /* handle crossing entities -> stack needed */
707 case XML_XINCLUDE_START
:
708 case XML_XINCLUDE_END
:
709 /* don't consider it part of the tree content */
711 case XML_ATTRIBUTE_NODE
:
712 /* Humm, should not happen ! */
715 tmp
= xmlDocCopyNode(cur
, target
, 1);
719 if ((list
== NULL
) || ((last
== NULL
) && (parent
== NULL
))) {
723 xmlAddNextSibling(last
, tmp
);
725 xmlAddChild(parent
, tmp
);
731 * Skip to next node in document order
733 if ((list
== NULL
) || ((last
== NULL
) && (parent
== NULL
))) {
736 cur
= xmlXPtrAdvanceNode(cur
);
742 * xmlXIncludeBuildNodeList:
743 * @ctxt: the XInclude context
744 * @target: the document target
745 * @source: the document source
746 * @obj: the XPointer result from the evaluation.
748 * Build a node list tree copy of the XPointer result.
749 * This will drop Attributes and Namespace declarations.
751 * Returns an xmlNodePtr list or NULL.
752 * the caller has to free the node tree.
755 xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr target
,
756 xmlDocPtr source
, xmlXPathObjectPtr obj
) {
757 xmlNodePtr list
= NULL
, last
= NULL
;
760 if ((ctxt
== NULL
) || (target
== NULL
) || (source
== NULL
) ||
764 case XPATH_NODESET
: {
765 xmlNodeSetPtr set
= obj
->nodesetval
;
768 for (i
= 0;i
< set
->nodeNr
;i
++) {
769 if (set
->nodeTab
[i
] == NULL
)
771 switch (set
->nodeTab
[i
]->type
) {
773 case XML_CDATA_SECTION_NODE
:
774 case XML_ELEMENT_NODE
:
775 case XML_ENTITY_REF_NODE
:
776 case XML_ENTITY_NODE
:
778 case XML_COMMENT_NODE
:
779 case XML_DOCUMENT_NODE
:
780 case XML_HTML_DOCUMENT_NODE
:
781 #ifdef LIBXML_DOCB_ENABLED
782 case XML_DOCB_DOCUMENT_NODE
:
784 case XML_XINCLUDE_START
:
785 case XML_XINCLUDE_END
:
787 case XML_ATTRIBUTE_NODE
:
788 case XML_NAMESPACE_DECL
:
789 case XML_DOCUMENT_TYPE_NODE
:
790 case XML_DOCUMENT_FRAG_NODE
:
791 case XML_NOTATION_NODE
:
793 case XML_ELEMENT_DECL
:
794 case XML_ATTRIBUTE_DECL
:
795 case XML_ENTITY_DECL
:
799 list
= last
= xmlXIncludeCopyNode(ctxt
, target
, source
,
802 xmlAddNextSibling(last
,
803 xmlXIncludeCopyNode(ctxt
, target
, source
,
805 if (last
->next
!= NULL
)
811 case XPATH_LOCATIONSET
: {
812 xmlLocationSetPtr set
= (xmlLocationSetPtr
) obj
->user
;
815 for (i
= 0;i
< set
->locNr
;i
++) {
817 list
= last
= xmlXIncludeCopyXPointer(ctxt
, target
, source
,
820 xmlAddNextSibling(last
,
821 xmlXIncludeCopyXPointer(ctxt
, target
, source
,
824 while (last
->next
!= NULL
)
831 return(xmlXIncludeCopyRange(ctxt
, target
, source
, obj
));
833 /* points are ignored in XInclude */
840 /************************************************************************
842 * XInclude I/O handling *
844 ************************************************************************/
847 * xmlXIncludeLoadDoc:
848 * @ctxt: the XInclude context
849 * @url: the associated URL
850 * @nr: the xinclude node number
852 * Load the document, and store the result in the XInclude context
854 * Returns 0 in case of success, -1 in case of failure
857 xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt
, const xmlChar
*url
, int nr
) {
861 xmlChar
*fragment
= NULL
;
864 #ifdef DEBUG_XINCLUDE
865 xmlGenericError(xmlGenericErrorContext
, "Loading doc %s:%d\n", url
, nr
);
868 * Check the URL and remove any fragment identifier
870 uri
= xmlParseURI((const char *)url
);
872 xmlGenericError(xmlGenericErrorContext
,
873 "XInclude: invalid value URI %s\n", url
);
876 if (uri
->fragment
!= NULL
) {
877 fragment
= (xmlChar
*) uri
->fragment
;
878 uri
->fragment
= NULL
;
880 URL
= xmlSaveUri(uri
);
883 xmlGenericError(xmlGenericErrorContext
,
884 "XInclude: invalid value URI %s\n", url
);
885 if (fragment
!= NULL
)
891 * Handling of references to the local document are done
892 * directly through ctxt->doc.
894 if ((URL
[0] == 0) || (URL
[0] == '#')) {
900 * Prevent reloading twice the document.
902 for (i
= 0; i
< ctxt
->incNr
; i
++) {
903 if ((xmlStrEqual(URL
, ctxt
->incTab
[i
]->URI
)) &&
904 (ctxt
->incTab
[i
]->doc
!= NULL
)) {
905 doc
= ctxt
->incTab
[i
]->doc
;
906 #ifdef DEBUG_XINCLUDE
907 printf("Already loaded %s\n", URL
);
916 #ifdef DEBUG_XINCLUDE
917 printf("loading %s\n", URL
);
919 doc
= xmlParseFile((const char *)URL
);
922 if (fragment
!= NULL
)
926 ctxt
->incTab
[nr
]->doc
= doc
;
929 * TODO: Make sure we have all entities fixed up
933 * We don't need the DTD anymore, free up space
934 if (doc->intSubset != NULL) {
935 xmlUnlinkNode((xmlNodePtr) doc->intSubset);
936 xmlFreeNode((xmlNodePtr) doc->intSubset);
937 doc->intSubset = NULL;
939 if (doc->extSubset != NULL) {
940 xmlUnlinkNode((xmlNodePtr) doc->extSubset);
941 xmlFreeNode((xmlNodePtr) doc->extSubset);
942 doc->extSubset = NULL;
945 xmlXIncludeRecurseDoc(ctxt
, doc
, URL
);
948 if (fragment
== NULL
) {
950 * Add the top children list as the replacement copy.
954 /* Hopefully a DTD declaration won't be copied from
955 * the same document */
956 ctxt
->incTab
[nr
]->inc
= xmlCopyNodeList(ctxt
->doc
->children
);
958 ctxt
->incTab
[nr
]->inc
= xmlXIncludeCopyNodeList(ctxt
, ctxt
->doc
,
963 * Computes the XPointer expression and make a copy used
964 * as the replacement copy.
966 xmlXPathObjectPtr xptr
;
967 xmlXPathContextPtr xptrctxt
;
971 xptrctxt
= xmlXPtrNewContext(ctxt
->doc
, ctxt
->incTab
[nr
]->ref
,
974 xptrctxt
= xmlXPtrNewContext(doc
, NULL
, NULL
);
976 if (xptrctxt
== NULL
) {
977 xmlGenericError(xmlGenericErrorContext
,
978 "XInclude: could create XPointer context\n");
983 xptr
= xmlXPtrEval(fragment
, xptrctxt
);
985 xmlGenericError(xmlGenericErrorContext
,
986 "XInclude: XPointer evaluation failed: #%s\n",
988 xmlXPathFreeContext(xptrctxt
);
993 switch (xptr
->type
) {
994 case XPATH_UNDEFINED
:
1000 case XPATH_XSLT_TREE
:
1001 xmlGenericError(xmlGenericErrorContext
,
1002 "XInclude: XPointer is not a range: #%s\n",
1004 xmlXPathFreeContext(xptrctxt
);
1010 case XPATH_LOCATIONSET
:
1013 set
= xptr
->nodesetval
;
1015 for (i
= 0;i
< set
->nodeNr
;i
++) {
1016 if (set
->nodeTab
[i
] == NULL
)
1018 switch (set
->nodeTab
[i
]->type
) {
1020 case XML_CDATA_SECTION_NODE
:
1021 case XML_ELEMENT_NODE
:
1022 case XML_ENTITY_REF_NODE
:
1023 case XML_ENTITY_NODE
:
1025 case XML_COMMENT_NODE
:
1026 case XML_DOCUMENT_NODE
:
1027 case XML_HTML_DOCUMENT_NODE
:
1028 #ifdef LIBXML_DOCB_ENABLED
1029 case XML_DOCB_DOCUMENT_NODE
:
1032 case XML_ATTRIBUTE_NODE
:
1033 xmlGenericError(xmlGenericErrorContext
,
1034 "XInclude: XPointer selects an attribute: #%s\n",
1036 set
->nodeTab
[i
] = NULL
;
1038 case XML_NAMESPACE_DECL
:
1039 xmlGenericError(xmlGenericErrorContext
,
1040 "XInclude: XPointer selects a namespace: #%s\n",
1042 set
->nodeTab
[i
] = NULL
;
1044 case XML_DOCUMENT_TYPE_NODE
:
1045 case XML_DOCUMENT_FRAG_NODE
:
1046 case XML_NOTATION_NODE
:
1048 case XML_ELEMENT_DECL
:
1049 case XML_ATTRIBUTE_DECL
:
1050 case XML_ENTITY_DECL
:
1051 case XML_XINCLUDE_START
:
1052 case XML_XINCLUDE_END
:
1053 xmlGenericError(xmlGenericErrorContext
,
1054 "XInclude: XPointer selects unexpected nodes: #%s\n",
1056 set
->nodeTab
[i
] = NULL
;
1057 set
->nodeTab
[i
] = NULL
;
1062 ctxt
->incTab
[nr
]->inc
=
1063 xmlXIncludeCopyXPointer(ctxt
, ctxt
->doc
, doc
, xptr
);
1064 xmlXPathFreeObject(xptr
);
1065 xmlXPathFreeContext(xptrctxt
);
1070 * Do the xml:base fixup if needed
1072 if ((doc
!= NULL
) && (URL
!= NULL
) && (xmlStrchr(URL
, (xmlChar
) '/'))) {
1075 node
= ctxt
->incTab
[nr
]->inc
;
1076 while (node
!= NULL
) {
1077 if (node
->type
== XML_ELEMENT_NODE
)
1078 xmlNodeSetBase(node
, URL
);
1082 if ((nr
< ctxt
->incNr
) && (ctxt
->incTab
[nr
]->doc
!= NULL
) &&
1083 (ctxt
->incTab
[nr
]->count
<= 1)) {
1084 #ifdef DEBUG_XINCLUDE
1085 printf("freeing %s\n", ctxt
->incTab
[nr
]->doc
->URL
);
1087 xmlFreeDoc(ctxt
->incTab
[nr
]->doc
);
1088 ctxt
->incTab
[nr
]->doc
= NULL
;
1095 * xmlXIncludeLoadTxt:
1096 * @ctxt: the XInclude context
1097 * @url: the associated URL
1098 * @nr: the xinclude node number
1100 * Load the content, and store the result in the XInclude context
1102 * Returns 0 in case of success, -1 in case of failure
1105 xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt
, const xmlChar
*url
, int nr
) {
1106 xmlParserInputBufferPtr buf
;
1111 xmlChar
*encoding
= NULL
;
1112 xmlCharEncoding enc
= 0;
1115 * Check the URL and remove any fragment identifier
1117 uri
= xmlParseURI((const char *)url
);
1119 xmlGenericError(xmlGenericErrorContext
,
1120 "XInclude: invalid value URI %s\n", url
);
1123 if (uri
->fragment
!= NULL
) {
1124 xmlGenericError(xmlGenericErrorContext
,
1125 "XInclude: fragment identifier forbidden for text: %s\n",
1130 URL
= xmlSaveUri(uri
);
1133 xmlGenericError(xmlGenericErrorContext
,
1134 "XInclude: invalid value URI %s\n", url
);
1139 * Handling of references to the local document are done
1140 * directly through ctxt->doc.
1143 xmlGenericError(xmlGenericErrorContext
,
1144 "XInclude: text serialization of document not available\n");
1150 * Prevent reloading twice the document.
1152 for (i
= 0; i
< ctxt
->txtNr
; i
++) {
1153 if (xmlStrEqual(URL
, ctxt
->txturlTab
[i
])) {
1154 node
= xmlCopyNode(ctxt
->txtTab
[i
], 1);
1159 * Try to get the encoding if available
1161 if ((ctxt
->incTab
[nr
] != NULL
) && (ctxt
->incTab
[nr
]->ref
!= NULL
)) {
1162 encoding
= xmlGetProp(ctxt
->incTab
[nr
]->ref
, XINCLUDE_PARSE_ENCODING
);
1164 if (encoding
!= NULL
) {
1166 * TODO: we should not have to remap to the xmlCharEncoding
1167 * predefined set, a better interface than
1168 * xmlParserInputBufferCreateFilename should allow any
1169 * encoding supported by iconv
1171 enc
= xmlParseCharEncoding((const char *) encoding
);
1172 if (enc
== XML_CHAR_ENCODING_ERROR
) {
1173 xmlGenericError(xmlGenericErrorContext
,
1174 "XInclude: encoding %s not supported\n", encoding
);
1185 buf
= xmlParserInputBufferCreateFilename((const char *)URL
, enc
);
1190 node
= xmlNewText(NULL
);
1193 * Scan all chars from the resource and add the to the node
1195 while (xmlParserInputBufferRead(buf
, 128) > 0) {
1197 const xmlChar
*content
;
1199 content
= xmlBufferContent(buf
->buffer
);
1200 len
= xmlBufferLength(buf
->buffer
);
1201 for (i
= 0;i
< len
;) {
1205 cur
= xmlStringCurrentChar(NULL
, &content
[i
], &l
);
1206 if (!IS_CHAR(cur
)) {
1207 xmlGenericError(xmlGenericErrorContext
,
1208 "XInclude: %s contains invalid char %d\n", URL
, cur
);
1210 xmlNodeAddContentLen(node
, &content
[i
], l
);
1214 xmlBufferShrink(buf
->buffer
, len
);
1216 xmlFreeParserInputBuffer(buf
);
1217 xmlXIncludeAddTxt(ctxt
, node
, URL
);
1221 * Add the element as the replacement copy.
1223 ctxt
->incTab
[nr
]->inc
= node
;
1229 * xmlXIncludeLoadFallback:
1230 * @ctxt: the XInclude context
1231 * @fallback: the fallback node
1232 * @nr: the xinclude node number
1234 * Load the content of the fallback node, and store the result
1235 * in the XInclude context
1237 * Returns 0 in case of success, -1 in case of failure
1240 xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt
, xmlNodePtr fallback
, int nr
) {
1241 if ((fallback
== NULL
) || (ctxt
== NULL
))
1244 ctxt
->incTab
[nr
]->inc
= xmlCopyNode(fallback
->children
, 1);
1248 /************************************************************************
1250 * XInclude Processing *
1252 ************************************************************************/
1255 * xmlXIncludePreProcessNode:
1256 * @ctxt: an XInclude context
1257 * @node: an XInclude node
1259 * Implement the XInclude preprocessing, currently just adding the element
1260 * for further processing.
1262 * Returns the result list or NULL in case of error
1265 xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt
, xmlNodePtr node
) {
1266 xmlXIncludeAddNode(ctxt
, node
);
1272 * xmlXIncludePreloadNode:
1273 * @ctxt: an XInclude context
1274 * @nr: the node number
1276 * Do some precomputations and preload shared documents
1278 * Returns 0 if substitution succeeded, -1 if some processing failed
1281 xmlXIncludePreloadNode(xmlXIncludeCtxtPtr ctxt
, int nr
) {
1287 int xml
= 1; /* default Issue 64 */
1290 xmlChar
*fragment
= NULL
;
1296 if ((nr
< 0) || (nr
>= ctxt
->incNr
))
1298 cur
= ctxt
->incTab
[nr
]->ref
;
1303 * read the attributes
1305 href
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_HREF
);
1307 href
= xmlGetProp(cur
, XINCLUDE_HREF
);
1309 xmlGenericError(xmlGenericErrorContext
, "XInclude: no href\n");
1313 parse
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_PARSE
);
1314 if (parse
== NULL
) {
1315 parse
= xmlGetProp(cur
, XINCLUDE_PARSE
);
1317 if (parse
!= NULL
) {
1318 if (xmlStrEqual(parse
, XINCLUDE_PARSE_XML
))
1320 else if (xmlStrEqual(parse
, XINCLUDE_PARSE_TEXT
))
1323 xmlGenericError(xmlGenericErrorContext
,
1324 "XInclude: invalid value %s for %s\n",
1325 parse
, XINCLUDE_PARSE
);
1337 base
= xmlNodeGetBase(ctxt
->doc
, cur
);
1339 URI
= xmlBuildURI(href
, ctxt
->doc
->URL
);
1341 URI
= xmlBuildURI(href
, base
);
1347 * Some escaping may be needed
1349 escbase
= xmlURIEscape(base
);
1350 eschref
= xmlURIEscape(href
);
1351 URI
= xmlBuildURI(eschref
, escbase
);
1352 if (escbase
!= NULL
)
1354 if (eschref
!= NULL
)
1364 xmlGenericError(xmlGenericErrorContext
, "XInclude: failed build URL\n");
1369 * Check the URL and remove any fragment identifier
1371 uri
= xmlParseURI((const char *)URI
);
1373 xmlGenericError(xmlGenericErrorContext
,
1374 "XInclude: invalid value URI %s\n", URI
);
1378 if (uri
->fragment
!= NULL
) {
1379 fragment
= (xmlChar
*) uri
->fragment
;
1380 uri
->fragment
= NULL
;
1382 URL
= xmlSaveUri(uri
);
1385 xmlGenericError(xmlGenericErrorContext
,
1386 "XInclude: invalid value URI %s\n", URI
);
1387 if (fragment
!= NULL
)
1393 if (fragment
!= NULL
)
1396 for (i
= 0; i
< nr
; i
++) {
1397 if (xmlStrEqual(URL
, ctxt
->incTab
[i
]->URI
)) {
1398 #ifdef DEBUG_XINCLUDE
1399 printf("Incrementing count for %d : %s\n", i
, ctxt
->incTab
[i
]->URI
);
1401 ctxt
->incTab
[i
]->count
++;
1411 * xmlXIncludeLoadNode:
1412 * @ctxt: an XInclude context
1413 * @nr: the node number
1415 * Find and load the infoset replacement for the given node.
1417 * Returns 0 if substitution succeeded, -1 if some processing failed
1420 xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt
, int nr
) {
1426 int xml
= 1; /* default Issue 64 */
1431 if ((nr
< 0) || (nr
>= ctxt
->incNr
))
1433 cur
= ctxt
->incTab
[nr
]->ref
;
1438 * read the attributes
1440 href
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_HREF
);
1442 href
= xmlGetProp(cur
, XINCLUDE_HREF
);
1444 xmlGenericError(xmlGenericErrorContext
, "XInclude: no href\n");
1448 parse
= xmlGetNsProp(cur
, XINCLUDE_NS
, XINCLUDE_PARSE
);
1449 if (parse
== NULL
) {
1450 parse
= xmlGetProp(cur
, XINCLUDE_PARSE
);
1452 if (parse
!= NULL
) {
1453 if (xmlStrEqual(parse
, XINCLUDE_PARSE_XML
))
1455 else if (xmlStrEqual(parse
, XINCLUDE_PARSE_TEXT
))
1458 xmlGenericError(xmlGenericErrorContext
,
1459 "XInclude: invalid value %s for %s\n",
1460 parse
, XINCLUDE_PARSE
);
1472 base
= xmlNodeGetBase(ctxt
->doc
, cur
);
1474 URI
= xmlBuildURI(href
, ctxt
->doc
->URL
);
1476 URI
= xmlBuildURI(href
, base
);
1482 * Some escaping may be needed
1484 escbase
= xmlURIEscape(base
);
1485 eschref
= xmlURIEscape(href
);
1486 URI
= xmlBuildURI(eschref
, escbase
);
1487 if (escbase
!= NULL
)
1489 if (eschref
!= NULL
)
1493 xmlGenericError(xmlGenericErrorContext
, "XInclude: failed build URL\n");
1502 #ifdef DEBUG_XINCLUDE
1503 xmlGenericError(xmlGenericErrorContext
, "parse: %s\n",
1504 xml
? "xml": "text");
1505 xmlGenericError(xmlGenericErrorContext
, "URI: %s\n", URI
);
1512 ret
= xmlXIncludeLoadDoc(ctxt
, URI
, nr
);
1513 /* xmlXIncludeGetFragment(ctxt, cur, URI); */
1515 ret
= xmlXIncludeLoadTxt(ctxt
, URI
, nr
);
1518 xmlNodePtr children
;
1521 * Time to try a fallback if availble
1523 #ifdef DEBUG_XINCLUDE
1524 xmlGenericError(xmlGenericErrorContext
, "error looking for fallback\n");
1526 children
= cur
->children
;
1527 while (children
!= NULL
) {
1528 if ((children
->type
== XML_ELEMENT_NODE
) &&
1529 (children
->ns
!= NULL
) &&
1530 (xmlStrEqual(children
->name
, XINCLUDE_FALLBACK
)) &&
1531 (xmlStrEqual(children
->ns
->href
, XINCLUDE_NS
))) {
1532 ret
= xmlXIncludeLoadFallback(ctxt
, children
, nr
);
1536 children
= children
->next
;
1540 xmlGenericError(xmlGenericErrorContext
,
1541 "XInclude: could not load %s, and no fallback was found\n",
1560 * xmlXIncludeIncludeNode:
1561 * @ctxt: an XInclude context
1562 * @nr: the node number
1564 * Inplement the infoset replacement for the given node
1566 * Returns 0 if substitution succeeded, -1 if some processing failed
1569 xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt
, int nr
) {
1570 xmlNodePtr cur
, end
, list
;
1574 if ((nr
< 0) || (nr
>= ctxt
->incNr
))
1576 cur
= ctxt
->incTab
[nr
]->ref
;
1581 * Change the current node as an XInclude start one, and add an
1584 cur
->type
= XML_XINCLUDE_START
;
1585 end
= xmlNewNode(cur
->ns
, cur
->name
);
1587 xmlGenericError(xmlGenericErrorContext
,
1588 "XInclude: failed to build node\n");
1591 end
->type
= XML_XINCLUDE_END
;
1592 xmlAddNextSibling(cur
, end
);
1595 * Add the list of nodes
1597 list
= ctxt
->incTab
[nr
]->inc
;
1598 ctxt
->incTab
[nr
]->inc
= NULL
;
1599 while (list
!= NULL
) {
1603 xmlAddPrevSibling(end
, cur
);
1611 * xmlXIncludeTestNode:
1612 * @node: an XInclude node
1614 * test if the node is an XInclude node
1616 * Returns 1 true, 0 otherwise
1619 xmlXIncludeTestNode(xmlNodePtr node
) {
1622 if (node
->ns
== NULL
)
1624 if ((xmlStrEqual(node
->name
, XINCLUDE_NODE
)) &&
1625 (xmlStrEqual(node
->ns
->href
, XINCLUDE_NS
))) return(1);
1630 * xmlXIncludeDoProcess:
1632 * @doc: an XML document
1634 * Implement the XInclude substitution on the XML document @doc
1636 * Returns 0 if no substitution were done, -1 if some processing failed
1637 * or the number of substitutions done.
1640 xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt
, xmlDocPtr doc
) {
1651 * First phase: lookup the elements in the document
1653 cur
= xmlDocGetRootElement(doc
);
1654 if (xmlXIncludeTestNode(cur
))
1655 xmlXIncludePreProcessNode(ctxt
, cur
);
1656 while (cur
!= NULL
) {
1657 /* TODO: need to work on entities -> stack */
1658 if ((cur
->children
!= NULL
) &&
1659 (cur
->children
->type
!= XML_ENTITY_DECL
)) {
1660 cur
= cur
->children
;
1661 if (xmlXIncludeTestNode(cur
))
1662 xmlXIncludePreProcessNode(ctxt
, cur
);
1663 } else if (cur
->next
!= NULL
) {
1665 if (xmlXIncludeTestNode(cur
))
1666 xmlXIncludePreProcessNode(ctxt
, cur
);
1670 if (cur
== NULL
) break; /* do */
1671 if (cur
->next
!= NULL
) {
1673 if (xmlXIncludeTestNode(cur
))
1674 xmlXIncludePreProcessNode(ctxt
, cur
);
1677 } while (cur
!= NULL
);
1682 * Second Phase : collect the infosets fragments
1685 for (i = ctxt->incBase;i < ctxt->incNr; i++) {
1686 xmlXIncludePreloadNode(ctxt, i);
1689 for (i
= ctxt
->incBase
;i
< ctxt
->incNr
; i
++) {
1690 xmlXIncludeLoadNode(ctxt
, i
);
1694 * Third phase: extend the original document infoset.
1696 for (i
= ctxt
->incBase
;i
< ctxt
->incNr
; i
++) {
1697 xmlXIncludeIncludeNode(ctxt
, i
);
1704 * xmlXIncludeProcess:
1705 * @doc: an XML document
1707 * Implement the XInclude substitution on the XML document @doc
1709 * Returns 0 if no substitution were done, -1 if some processing failed
1710 * or the number of substitutions done.
1713 xmlXIncludeProcess(xmlDocPtr doc
) {
1714 xmlXIncludeCtxtPtr ctxt
;
1719 ctxt
= xmlXIncludeNewContext(doc
);
1722 ret
= xmlXIncludeDoProcess(ctxt
, doc
);
1724 xmlXIncludeFreeContext(ctxt
);
1728 #else /* !LIBXML_XINCLUDE_ENABLED */