blob: 620e42615dc87490944f78cc49b05e23eb327c0d [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xinclude.c : Code to implement XInclude processing
3 *
Daniel Veillardbbd22452001-05-23 12:02:27 +00004 * World Wide Web Consortium W3C Last Call Working Draft 16 May 2001
5 * http://www.w3.org/TR/2001/WD-xinclude-20010516/
Owen Taylor3473f882001-02-23 17:55:21 +00006 *
7 * See Copyright for the status of this software.
8 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00009 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +000010 */
11
12/*
13 * TODO: compute XPointers nodesets
Daniel Veillardd16df9f2001-05-23 13:44:21 +000014 * TODO: add an node intermediate API and handle recursion at this level
Owen Taylor3473f882001-02-23 17:55:21 +000015 */
16
Bjorn Reese70a9da52001-04-21 16:57:29 +000017#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000018
Owen Taylor3473f882001-02-23 17:55:21 +000019#include <string.h>
20#include <libxml/xmlmemory.h>
21#include <libxml/tree.h>
22#include <libxml/parser.h>
23#include <libxml/uri.h>
24#include <libxml/xpointer.h>
25#include <libxml/parserInternals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000026#include <libxml/xmlerror.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000027#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000028
29#ifdef LIBXML_XINCLUDE_ENABLED
30#include <libxml/xinclude.h>
31
Daniel Veillardbbd22452001-05-23 12:02:27 +000032#define XINCLUDE_NS (const xmlChar *) "http://www.w3.org/2001/XInclude"
Owen Taylor3473f882001-02-23 17:55:21 +000033#define XINCLUDE_NODE (const xmlChar *) "include"
34#define XINCLUDE_HREF (const xmlChar *) "href"
35#define XINCLUDE_PARSE (const xmlChar *) "parse"
36#define XINCLUDE_PARSE_XML (const xmlChar *) "xml"
37#define XINCLUDE_PARSE_TEXT (const xmlChar *) "text"
38
39/* #define DEBUG_XINCLUDE */
Daniel Veillard017b1082001-06-21 11:20:21 +000040#ifdef DEBUG_XINCLUDE
41#ifdef LIBXML_DEBUG_ENABLED
42#include <libxml/debugXML.h>
43#endif
44#endif
Owen Taylor3473f882001-02-23 17:55:21 +000045
46/************************************************************************
47 * *
48 * XInclude contexts handling *
49 * *
50 ************************************************************************/
51
52/*
53 * An XInclude context
54 */
Daniel Veillardedac3c92001-02-26 01:36:19 +000055typedef xmlChar *xmlURL;
Owen Taylor3473f882001-02-23 17:55:21 +000056typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt;
57typedef xmlXIncludeCtxt *xmlXIncludeCtxtPtr;
58struct _xmlXIncludeCtxt {
59 xmlDocPtr doc; /* the source document */
60 int incNr; /* number of includes */
61 int incMax; /* size of includes tab */
62 xmlNodePtr *incTab; /* array of include nodes */
63 xmlNodePtr *repTab; /* array of replacement node lists */
64 int docNr; /* number of parsed documents */
65 int docMax; /* size of parsed documents tab */
66 xmlDocPtr *docTab; /* array of parsed documents */
Daniel Veillardedac3c92001-02-26 01:36:19 +000067 xmlURL *urlTab; /* array of parsed documents URLs */
Owen Taylor3473f882001-02-23 17:55:21 +000068 int txtNr; /* number of unparsed documents */
69 int txtMax; /* size of unparsed documents tab */
70 xmlNodePtr *txtTab; /* array of unparsed text nodes */
Daniel Veillardedac3c92001-02-26 01:36:19 +000071 xmlURL *txturlTab; /* array of unparsed txtuments URLs */
Owen Taylor3473f882001-02-23 17:55:21 +000072};
73
Daniel Veillardd16df9f2001-05-23 13:44:21 +000074static int
75xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc);
Owen Taylor3473f882001-02-23 17:55:21 +000076
77/**
78 * xmlXIncludeNewContext:
79 * @doc: an XML Document
80 *
81 * Creates a new XInclude context
82 *
83 * Returns the new set
84 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +000085static xmlXIncludeCtxtPtr
Owen Taylor3473f882001-02-23 17:55:21 +000086xmlXIncludeNewContext(xmlDocPtr doc) {
87 xmlXIncludeCtxtPtr ret;
88
89 if (doc == NULL)
90 return(NULL);
91 ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt));
92 if (ret == NULL)
93 return(NULL);
94 memset(ret, 0, sizeof(xmlXIncludeCtxt));
95 ret->doc = doc;
96 ret->incNr = 0;
97 ret->incMax = 0;
98 ret->incTab = NULL;
99 ret->repTab = NULL;
100 ret->docNr = 0;
101 ret->docMax = 0;
102 ret->docTab = NULL;
103 ret->urlTab = NULL;
104 return(ret);
105}
106
107/**
108 * xmlXIncludeFreeContext:
109 * @ctxt: the XInclude context
110 *
111 * Free an XInclude context
112 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000113static void
Owen Taylor3473f882001-02-23 17:55:21 +0000114xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) {
115 int i;
116
117 if (ctxt == NULL)
118 return;
119 for (i = 0;i < ctxt->docNr;i++) {
120 xmlFreeDoc(ctxt->docTab[i]);
121 if (ctxt->urlTab[i] != NULL)
122 xmlFree(ctxt->urlTab[i]);
123 }
124 for (i = 0;i < ctxt->txtNr;i++) {
125 if (ctxt->txturlTab[i] != NULL)
126 xmlFree(ctxt->txturlTab[i]);
127 }
128 if (ctxt->incTab != NULL)
129 xmlFree(ctxt->incTab);
130 if (ctxt->repTab != NULL)
131 xmlFree(ctxt->repTab);
132 if (ctxt->urlTab != NULL)
133 xmlFree(ctxt->urlTab);
134 if (ctxt->docTab != NULL)
135 xmlFree(ctxt->docTab);
136 if (ctxt->txtTab != NULL)
137 xmlFree(ctxt->txtTab);
138 if (ctxt->txturlTab != NULL)
139 xmlFree(ctxt->txturlTab);
Owen Taylor3473f882001-02-23 17:55:21 +0000140 xmlFree(ctxt);
141}
142
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000143/**
144 * xmlXIncludeAddNode:
145 * @ctxt: the XInclude context
146 * @node: the new node
147 *
148 * Add a new node to process to an XInclude context
149 */
150static void
151xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
152 if (ctxt->incMax == 0) {
153 ctxt->incMax = 4;
154 ctxt->incTab = (xmlNodePtr *) xmlMalloc(ctxt->incMax *
155 sizeof(ctxt->incTab[0]));
156 if (ctxt->incTab == NULL) {
157 xmlGenericError(xmlGenericErrorContext,
158 "malloc failed !\n");
159 return;
160 }
161 ctxt->repTab = (xmlNodePtr *) xmlMalloc(ctxt->incMax *
162 sizeof(ctxt->repTab[0]));
163 if (ctxt->repTab == NULL) {
164 xmlGenericError(xmlGenericErrorContext,
165 "malloc failed !\n");
166 return;
167 }
168 }
169 if (ctxt->incNr >= ctxt->incMax) {
170 ctxt->incMax *= 2;
171 ctxt->incTab = (xmlNodePtr *) xmlRealloc(ctxt->incTab,
172 ctxt->incMax * sizeof(ctxt->incTab[0]));
173 if (ctxt->incTab == NULL) {
174 xmlGenericError(xmlGenericErrorContext,
175 "realloc failed !\n");
176 return;
177 }
178 ctxt->repTab = (xmlNodePtr *) xmlRealloc(ctxt->repTab,
179 ctxt->incMax * sizeof(ctxt->repTab[0]));
180 if (ctxt->repTab == NULL) {
181 xmlGenericError(xmlGenericErrorContext,
182 "realloc failed !\n");
183 return;
184 }
185 }
186 ctxt->incTab[ctxt->incNr] = node;
187 ctxt->repTab[ctxt->incNr] = NULL;
188 ctxt->incNr++;
189}
190
191/**
192 * xmlXIncludeAddDoc:
193 * @ctxt: the XInclude context
194 * @doc: the new document
195 * @url: the associated URL
196 *
197 * Add a new document to the list. The XInclude recursive nature is handled
198 * at this point.
199 */
200static void
201xmlXIncludeAddDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, const xmlURL url) {
202 xmlXIncludeCtxtPtr newctxt;
203 int i;
204
205 if (ctxt->docMax == 0) {
206 ctxt->docMax = 4;
207 ctxt->docTab = (xmlDocPtr *) xmlMalloc(ctxt->docMax *
208 sizeof(ctxt->docTab[0]));
209 if (ctxt->docTab == NULL) {
210 xmlGenericError(xmlGenericErrorContext,
211 "malloc failed !\n");
212 return;
213 }
214 ctxt->urlTab = (xmlURL *) xmlMalloc(ctxt->docMax *
215 sizeof(ctxt->urlTab[0]));
216 if (ctxt->urlTab == NULL) {
217 xmlGenericError(xmlGenericErrorContext,
218 "malloc failed !\n");
219 return;
220 }
221 }
222 if (ctxt->docNr >= ctxt->docMax) {
223 ctxt->docMax *= 2;
224 ctxt->docTab = (xmlDocPtr *) xmlRealloc(ctxt->docTab,
225 ctxt->docMax * sizeof(ctxt->docTab[0]));
226 if (ctxt->docTab == NULL) {
227 xmlGenericError(xmlGenericErrorContext,
228 "realloc failed !\n");
229 return;
230 }
231 ctxt->urlTab = (xmlURL *) xmlRealloc(ctxt->urlTab,
232 ctxt->docMax * sizeof(ctxt->urlTab[0]));
233 if (ctxt->urlTab == NULL) {
234 xmlGenericError(xmlGenericErrorContext,
235 "realloc failed !\n");
236 return;
237 }
238 }
239 ctxt->docTab[ctxt->docNr] = doc;
240 ctxt->urlTab[ctxt->docNr] = xmlStrdup(url);
241 ctxt->docNr++;
242
243 /*
244 * Handle recursion here.
245 */
246
247 newctxt = xmlXIncludeNewContext(doc);
248 if (newctxt != NULL) {
249 /*
250 * Copy the existing document set
251 */
252 newctxt->docMax = ctxt->docMax;
253 newctxt->docNr = ctxt->docNr;
254 newctxt->docTab = (xmlDocPtr *) xmlMalloc(newctxt->docMax *
255 sizeof(newctxt->docTab[0]));
256 if (newctxt->docTab == NULL) {
257 xmlGenericError(xmlGenericErrorContext,
258 "malloc failed !\n");
259 xmlFree(newctxt);
260 return;
261 }
262 newctxt->urlTab = (xmlURL *) xmlMalloc(newctxt->docMax *
263 sizeof(newctxt->urlTab[0]));
264 if (ctxt->urlTab == NULL) {
265 xmlGenericError(xmlGenericErrorContext,
266 "malloc failed !\n");
267 xmlFree(newctxt);
268 return;
269 }
270
271 for (i = 0;i < ctxt->docNr;i++) {
272 newctxt->docTab[i] = ctxt->docTab[i];
273 newctxt->urlTab[i] = ctxt->urlTab[i];
274 }
275 xmlXIncludeDoProcess(newctxt, doc);
276 for (i = 0;i < ctxt->docNr;i++) {
277 newctxt->docTab[i] = NULL;
278 newctxt->urlTab[i] = NULL;
279 }
280 xmlXIncludeFreeContext(newctxt);
281 }
282}
283
284/**
285 * xmlXIncludeAddTxt:
286 * @ctxt: the XInclude context
287 * @txt: the new text node
288 * @url: the associated URL
289 *
290 * Add a new txtument to the list
291 */
292static void
293xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL url) {
294 if (ctxt->txtMax == 0) {
295 ctxt->txtMax = 4;
296 ctxt->txtTab = (xmlNodePtr *) xmlMalloc(ctxt->txtMax *
297 sizeof(ctxt->txtTab[0]));
298 if (ctxt->txtTab == NULL) {
299 xmlGenericError(xmlGenericErrorContext,
300 "malloc failed !\n");
301 return;
302 }
303 ctxt->txturlTab = (xmlURL *) xmlMalloc(ctxt->txtMax *
304 sizeof(ctxt->txturlTab[0]));
305 if (ctxt->txturlTab == NULL) {
306 xmlGenericError(xmlGenericErrorContext,
307 "malloc failed !\n");
308 return;
309 }
310 }
311 if (ctxt->txtNr >= ctxt->txtMax) {
312 ctxt->txtMax *= 2;
313 ctxt->txtTab = (xmlNodePtr *) xmlRealloc(ctxt->txtTab,
314 ctxt->txtMax * sizeof(ctxt->txtTab[0]));
315 if (ctxt->txtTab == NULL) {
316 xmlGenericError(xmlGenericErrorContext,
317 "realloc failed !\n");
318 return;
319 }
320 ctxt->txturlTab = (xmlURL *) xmlRealloc(ctxt->txturlTab,
321 ctxt->txtMax * sizeof(ctxt->urlTab[0]));
322 if (ctxt->txturlTab == NULL) {
323 xmlGenericError(xmlGenericErrorContext,
324 "realloc failed !\n");
325 return;
326 }
327 }
328 ctxt->txtTab[ctxt->txtNr] = txt;
329 ctxt->txturlTab[ctxt->txtNr] = xmlStrdup(url);
330 ctxt->txtNr++;
331}
332
Owen Taylor3473f882001-02-23 17:55:21 +0000333/************************************************************************
334 * *
335 * XInclude I/O handling *
336 * *
337 ************************************************************************/
338
339/**
340 * xmlXIncludeLoadDoc:
341 * @ctxt: the XInclude context
342 * @url: the associated URL
343 * @nr: the xinclude node number
344 *
345 * Load the document, and store the result in the XInclude context
346 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000347static void
Owen Taylor3473f882001-02-23 17:55:21 +0000348xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
349 xmlDocPtr doc;
350 xmlURIPtr uri;
351 xmlChar *URL;
352 xmlChar *fragment = NULL;
353 int i;
354 /*
355 * Check the URL and remove any fragment identifier
356 */
357 uri = xmlParseURI((const char *)url);
358 if (uri == NULL) {
359 xmlGenericError(xmlGenericErrorContext,
360 "XInclude: invalid value URI %s\n", url);
361 return;
362 }
363 if (uri->fragment != NULL) {
364 fragment = (xmlChar *) uri->fragment;
365 uri->fragment = NULL;
366 }
367 URL = xmlSaveUri(uri);
368 xmlFreeURI(uri);
369 if (URL == NULL) {
370 xmlGenericError(xmlGenericErrorContext,
371 "XInclude: invalid value URI %s\n", url);
372 if (fragment != NULL)
373 xmlFree(fragment);
374 return;
375 }
376
377 /*
378 * Handling of references to the local document are done
379 * directly through ctxt->doc.
380 */
381 if ((URL[0] == 0) || (URL[0] == '#')) {
382 doc = NULL;
383 goto loaded;
384 }
385
386 /*
387 * Prevent reloading twice the document.
388 */
389 for (i = 0; i < ctxt->docNr; i++) {
390 if (xmlStrEqual(URL, ctxt->urlTab[i])) {
391 doc = ctxt->docTab[i];
392 goto loaded;
393 }
394 }
395 /*
396 * Load it.
397 */
398 doc = xmlParseFile((const char *)URL);
399 if (doc == NULL) {
400 xmlGenericError(xmlGenericErrorContext,
401 "XInclude: could not load %s\n", URL);
402 xmlFree(URL);
403 if (fragment != NULL)
404 xmlFree(fragment);
405 return;
406 }
407 xmlXIncludeAddDoc(ctxt, doc, URL);
408
409loaded:
410 if (fragment == NULL) {
411 /*
412 * Add the top children list as the replacement copy.
Owen Taylor3473f882001-02-23 17:55:21 +0000413 */
414 if (doc == NULL)
Daniel Veillard4497e692001-06-09 14:19:02 +0000415 {
416 /* Hopefully a DTD declaration won't be copied from
417 * the same document */
Owen Taylor3473f882001-02-23 17:55:21 +0000418 ctxt->repTab[nr] = xmlCopyNodeList(ctxt->doc->children);
Daniel Veillard4497e692001-06-09 14:19:02 +0000419 } else {
420 /* DTD declarations can't be copied from included files */
421 xmlNodePtr node = doc->children;
422 while (node != NULL)
423 {
424 if (node->type == XML_DTD_NODE)
425 {
426 xmlUnlinkNode(node);
427 xmlFreeNode(node);
428 }
429 node = node->next;
430 }
Owen Taylor3473f882001-02-23 17:55:21 +0000431 ctxt->repTab[nr] = xmlCopyNodeList(doc->children);
Daniel Veillard4497e692001-06-09 14:19:02 +0000432 }
Owen Taylor3473f882001-02-23 17:55:21 +0000433 } else {
434 /*
435 * Computes the XPointer expression and make a copy used
436 * as the replacement copy.
437 */
438 xmlXPathObjectPtr xptr;
439 xmlXPathContextPtr xptrctxt;
Daniel Veillard39196eb2001-06-19 18:09:42 +0000440 xmlNodeSetPtr set;
Owen Taylor3473f882001-02-23 17:55:21 +0000441
442 if (doc == NULL) {
443 xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr], NULL);
444 } else {
445 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
446 }
447 if (xptrctxt == NULL) {
448 xmlGenericError(xmlGenericErrorContext,
449 "XInclude: could create XPointer context\n");
450 xmlFree(URL);
451 xmlFree(fragment);
452 return;
453 }
454 xptr = xmlXPtrEval(fragment, xptrctxt);
455 if (xptr == NULL) {
456 xmlGenericError(xmlGenericErrorContext,
457 "XInclude: XPointer evaluation failed: #%s\n",
458 fragment);
459 xmlXPathFreeContext(xptrctxt);
460 xmlFree(URL);
461 xmlFree(fragment);
462 return;
463 }
Daniel Veillard39196eb2001-06-19 18:09:42 +0000464 switch (xptr->type) {
465 case XPATH_UNDEFINED:
466 case XPATH_BOOLEAN:
467 case XPATH_NUMBER:
468 case XPATH_STRING:
469 case XPATH_POINT:
470 case XPATH_USERS:
471 case XPATH_XSLT_TREE:
472 xmlGenericError(xmlGenericErrorContext,
473 "XInclude: XPointer is not a range: #%s\n",
474 fragment);
475 xmlXPathFreeContext(xptrctxt);
476 xmlFree(URL);
477 xmlFree(fragment);
478 return;
479 case XPATH_NODESET:
480 case XPATH_RANGE:
481 case XPATH_LOCATIONSET:
482 break;
483 }
484 set = xptr->nodesetval;
485 if (set != NULL) {
486 for (i = 0;i < set->nodeNr;i++) {
487 if (set->nodeTab[i] == NULL)
488 continue;
489 switch (set->nodeTab[i]->type) {
490 case XML_TEXT_NODE:
491 case XML_CDATA_SECTION_NODE:
492 case XML_ELEMENT_NODE:
493 case XML_ENTITY_REF_NODE:
494 case XML_ENTITY_NODE:
495 case XML_PI_NODE:
496 case XML_COMMENT_NODE:
497 case XML_DOCUMENT_NODE:
498 case XML_HTML_DOCUMENT_NODE:
499#ifdef LIBXML_DOCB_ENABLED
500 case XML_DOCB_DOCUMENT_NODE:
501#endif
502 continue;
503 case XML_ATTRIBUTE_NODE:
504 xmlGenericError(xmlGenericErrorContext,
505 "XInclude: XPointer selects an attribute: #%s\n",
506 fragment);
507 set->nodeTab[i] = NULL;
508 continue;
509 case XML_NAMESPACE_DECL:
510 xmlGenericError(xmlGenericErrorContext,
511 "XInclude: XPointer selects a namespace: #%s\n",
512 fragment);
513 set->nodeTab[i] = NULL;
514 continue;
515 case XML_DOCUMENT_TYPE_NODE:
516 case XML_DOCUMENT_FRAG_NODE:
517 case XML_NOTATION_NODE:
518 case XML_DTD_NODE:
519 case XML_ELEMENT_DECL:
520 case XML_ATTRIBUTE_DECL:
521 case XML_ENTITY_DECL:
522 case XML_XINCLUDE_START:
523 case XML_XINCLUDE_END:
524 xmlGenericError(xmlGenericErrorContext,
525 "XInclude: XPointer selects unexpected nodes: #%s\n",
526 fragment);
527 set->nodeTab[i] = NULL;
528 set->nodeTab[i] = NULL;
529 continue; /* for */
530 }
531 }
532 }
Owen Taylor3473f882001-02-23 17:55:21 +0000533 ctxt->repTab[nr] = xmlXPtrBuildNodeList(xptr);
534 xmlXPathFreeObject(xptr);
535 xmlXPathFreeContext(xptrctxt);
536 xmlFree(fragment);
537 }
538 xmlFree(URL);
539}
540
541/**
542 * xmlXIncludeLoadTxt:
543 * @ctxt: the XInclude context
544 * @url: the associated URL
545 * @nr: the xinclude node number
546 *
547 * Load the content, and store the result in the XInclude context
548 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000549static void
Owen Taylor3473f882001-02-23 17:55:21 +0000550xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
551 xmlParserInputBufferPtr buf;
552 xmlNodePtr node;
553 xmlURIPtr uri;
554 xmlChar *URL;
555 int i;
556 /*
557 * Check the URL and remove any fragment identifier
558 */
559 uri = xmlParseURI((const char *)url);
560 if (uri == NULL) {
561 xmlGenericError(xmlGenericErrorContext,
562 "XInclude: invalid value URI %s\n", url);
563 return;
564 }
565 if (uri->fragment != NULL) {
566 xmlGenericError(xmlGenericErrorContext,
567 "XInclude: fragment identifier forbidden for text: %s\n",
568 uri->fragment);
569 xmlFreeURI(uri);
570 return;
571 }
572 URL = xmlSaveUri(uri);
573 xmlFreeURI(uri);
574 if (URL == NULL) {
575 xmlGenericError(xmlGenericErrorContext,
576 "XInclude: invalid value URI %s\n", url);
577 return;
578 }
579
580 /*
581 * Handling of references to the local document are done
582 * directly through ctxt->doc.
583 */
584 if (URL[0] == 0) {
585 xmlGenericError(xmlGenericErrorContext,
586 "XInclude: text serialization of document not available\n");
587 xmlFree(URL);
588 return;
589 }
590
591 /*
592 * Prevent reloading twice the document.
593 */
594 for (i = 0; i < ctxt->txtNr; i++) {
595 if (xmlStrEqual(URL, ctxt->txturlTab[i])) {
596 node = xmlCopyNode(ctxt->txtTab[i], 1);
597 goto loaded;
598 }
599 }
600 /*
601 * Load it.
602 * Issue 62: how to detect the encoding
603 */
604 buf = xmlParserInputBufferCreateFilename((const char *)URL, 0);
605 if (buf == NULL) {
606 xmlGenericError(xmlGenericErrorContext,
607 "XInclude: could not load %s\n", URL);
608 xmlFree(URL);
609 return;
610 }
611 node = xmlNewText(NULL);
612
613 /*
614 * Scan all chars from the resource and add the to the node
615 */
616 while (xmlParserInputBufferRead(buf, 128) > 0) {
617 int len;
618 const xmlChar *content;
619
620 content = xmlBufferContent(buf->buffer);
621 len = xmlBufferLength(buf->buffer);
622 for (i = 0;i < len; i++) {
623 /*
624 * TODO: if the encoding issue is solved, scan UTF8 chars instead
625 */
626 if (!IS_CHAR(content[i])) {
627 xmlGenericError(xmlGenericErrorContext,
628 "XInclude: %s contains invalid char %d\n", URL, content[i]);
629 } else {
630 xmlNodeAddContentLen(node, &content[i], 1);
631 }
632 }
633 xmlBufferShrink(buf->buffer, len);
634 }
635 xmlFreeParserInputBuffer(buf);
636 xmlXIncludeAddTxt(ctxt, node, URL);
637
638loaded:
639 /*
640 * Add the element as the replacement copy.
641 */
642 ctxt->repTab[nr] = node;
643 xmlFree(URL);
644}
645
646/************************************************************************
647 * *
648 * XInclude Processing *
649 * *
650 ************************************************************************/
651
652/**
653 * xmlXIncludePreProcessNode:
654 * @ctxt: an XInclude context
655 * @node: an XInclude node
656 *
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000657 * Implement the XInclude preprocessing, currently just adding the element
658 * for further processing.
Owen Taylor3473f882001-02-23 17:55:21 +0000659 *
660 * Returns the result list or NULL in case of error
661 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000662static xmlNodePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000663xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
664 xmlXIncludeAddNode(ctxt, node);
665 return(0);
666}
667
668/**
669 * xmlXIncludeLoadNode:
670 * @ctxt: an XInclude context
671 * @nr: the node number
672 *
673 * Find and load the infoset replacement for the given node.
674 *
675 * Returns 0 if substition succeeded, -1 if some processing failed
676 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000677static int
Owen Taylor3473f882001-02-23 17:55:21 +0000678xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) {
679 xmlNodePtr cur;
680 xmlChar *href;
681 xmlChar *parse;
682 xmlChar *base;
683 xmlChar *URI;
684 int xml = 1; /* default Issue 64 */
685
686 if (ctxt == NULL)
687 return(-1);
688 if ((nr < 0) || (nr >= ctxt->incNr))
689 return(-1);
690 cur = ctxt->incTab[nr];
691 if (cur == NULL)
692 return(-1);
693
694#ifdef DEBUG_XINCLUDE
695 xmlDebugDumpNode(stdout, cur, 0);
696#endif
697 /*
698 * read the attributes
699 */
700 href = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_HREF);
701 if (href == NULL) {
702 href = xmlGetProp(cur, XINCLUDE_HREF);
703 if (href == NULL) {
704 xmlGenericError(xmlGenericErrorContext, "XInclude: no href\n");
705 return(-1);
706 }
707 }
708 parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE);
709 if (parse == NULL) {
710 parse = xmlGetProp(cur, XINCLUDE_PARSE);
711 }
712 if (parse != NULL) {
713 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
714 xml = 1;
715 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
716 xml = 0;
717 else {
718 xmlGenericError(xmlGenericErrorContext,
719 "XInclude: invalid value %s for %s\n",
720 parse, XINCLUDE_PARSE);
721 if (href != NULL)
722 xmlFree(href);
723 if (parse != NULL)
724 xmlFree(parse);
725 return(-1);
726 }
727 }
728
729 /*
730 * compute the URI
731 */
732 base = xmlNodeGetBase(ctxt->doc, cur);
733 if (base == NULL) {
734 URI = xmlBuildURI(href, ctxt->doc->URL);
735 } else {
736 URI = xmlBuildURI(href, base);
737 }
738 if (URI == NULL) {
739 xmlChar *escbase;
740 xmlChar *eschref;
741 /*
742 * Some escapeing may be needed
743 */
744 escbase = xmlURIEscape(base);
745 eschref = xmlURIEscape(href);
746 URI = xmlBuildURI(eschref, escbase);
747 if (escbase != NULL)
748 xmlFree(escbase);
749 if (eschref != NULL)
750 xmlFree(eschref);
751 }
752 if (URI == NULL) {
753 xmlGenericError(xmlGenericErrorContext, "XInclude: failed build URL\n");
754 if (parse != NULL)
755 xmlFree(parse);
756 if (href != NULL)
757 xmlFree(href);
758 if (base != NULL)
759 xmlFree(base);
760 return(-1);
761 }
762#ifdef DEBUG_XINCLUDE
763 xmlGenericError(xmlGenericErrorContext, "parse: %s\n",
764 xml ? "xml": "text");
765 xmlGenericError(xmlGenericErrorContext, "URI: %s\n", URI);
766#endif
767
768 /*
769 * Cleanup
770 */
771 if (xml) {
772 xmlXIncludeLoadDoc(ctxt, URI, nr);
773 /* xmlXIncludeGetFragment(ctxt, cur, URI); */
774 } else {
775 xmlXIncludeLoadTxt(ctxt, URI, nr);
776 }
777
778 /*
779 * Cleanup
780 */
781 if (URI != NULL)
782 xmlFree(URI);
783 if (parse != NULL)
784 xmlFree(parse);
785 if (href != NULL)
786 xmlFree(href);
787 if (base != NULL)
788 xmlFree(base);
789 return(0);
790}
791
792/**
793 * xmlXIncludeIncludeNode:
794 * @ctxt: an XInclude context
795 * @nr: the node number
796 *
797 * Inplement the infoset replacement for the given node
798 *
799 * Returns 0 if substition succeeded, -1 if some processing failed
800 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000801static int
Owen Taylor3473f882001-02-23 17:55:21 +0000802xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
803 xmlNodePtr cur, end, list;
804
805 if (ctxt == NULL)
806 return(-1);
807 if ((nr < 0) || (nr >= ctxt->incNr))
808 return(-1);
809 cur = ctxt->incTab[nr];
810 if (cur == NULL)
811 return(-1);
812
813 /*
814 * Change the current node as an XInclude start one, and add an
815 * entity end one
816 */
817 cur->type = XML_XINCLUDE_START;
818 end = xmlNewNode(cur->ns, cur->name);
819 if (end == NULL) {
820 xmlGenericError(xmlGenericErrorContext,
821 "XInclude: failed to build node\n");
822 return(-1);
823 }
824 end->type = XML_XINCLUDE_END;
825 xmlAddNextSibling(cur, end);
826
827 /*
828 * Add the list of nodes
829 */
830 list = ctxt->repTab[nr];
831 ctxt->repTab[nr] = NULL;
832 while (list != NULL) {
833 cur = list;
834 list = list->next;
835
836 xmlAddPrevSibling(end, cur);
837 }
838 return(0);
839}
840
841/**
842 * xmlXIncludeTestNode:
Owen Taylor3473f882001-02-23 17:55:21 +0000843 * @node: an XInclude node
844 *
845 * test if the node is an XInclude node
846 *
847 * Returns 1 true, 0 otherwise
848 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000849static int
850xmlXIncludeTestNode(xmlNodePtr node) {
Owen Taylor3473f882001-02-23 17:55:21 +0000851 if (node == NULL)
852 return(0);
853 if (node->ns == NULL)
854 return(0);
855 if ((xmlStrEqual(node->name, XINCLUDE_NODE)) &&
856 (xmlStrEqual(node->ns->href, XINCLUDE_NS))) return(1);
857 return(0);
858}
859
860/**
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000861 * xmlXIncludeDoProcess:
862 * @ctxt:
Owen Taylor3473f882001-02-23 17:55:21 +0000863 * @doc: an XML document
864 *
865 * Implement the XInclude substitution on the XML document @doc
866 *
867 * Returns 0 if no substition were done, -1 if some processing failed
868 * or the number of substitutions done.
869 */
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000870static int
871xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +0000872 xmlNodePtr cur;
873 int ret = 0;
874 int i;
875
876 if (doc == NULL)
877 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000878 if (ctxt == NULL)
879 return(-1);
880
881 /*
882 * First phase: lookup the elements in the document
883 */
884 cur = xmlDocGetRootElement(doc);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000885 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +0000886 xmlXIncludePreProcessNode(ctxt, cur);
887 while (cur != NULL) {
888 /* TODO: need to work on entities -> stack */
889 if ((cur->children != NULL) &&
890 (cur->children->type != XML_ENTITY_DECL)) {
891 cur = cur->children;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000892 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +0000893 xmlXIncludePreProcessNode(ctxt, cur);
894 } else if (cur->next != NULL) {
895 cur = cur->next;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000896 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +0000897 xmlXIncludePreProcessNode(ctxt, cur);
898 } else {
899 do {
900 cur = cur->parent;
901 if (cur == NULL) break; /* do */
902 if (cur->next != NULL) {
903 cur = cur->next;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000904 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +0000905 xmlXIncludePreProcessNode(ctxt, cur);
906 break; /* do */
907 }
908 } while (cur != NULL);
909 }
910 }
911
912 /*
913 * Second Phase : collect the infosets fragments
914 */
915 for (i = 0;i < ctxt->incNr; i++) {
916 xmlXIncludeLoadNode(ctxt, i);
917 }
918
919 /*
920 * Third phase: extend the original document infoset.
921 */
922 for (i = 0;i < ctxt->incNr; i++) {
923 xmlXIncludeIncludeNode(ctxt, i);
924 }
925
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000926 return(ret);
927}
928
929/**
930 * xmlXIncludeProcess:
931 * @doc: an XML document
932 *
933 * Implement the XInclude substitution on the XML document @doc
934 *
935 * Returns 0 if no substition were done, -1 if some processing failed
936 * or the number of substitutions done.
937 */
938int
939xmlXIncludeProcess(xmlDocPtr doc) {
940 xmlXIncludeCtxtPtr ctxt;
941 int ret = 0;
942
943 if (doc == NULL)
944 return(-1);
945 ctxt = xmlXIncludeNewContext(doc);
946 if (ctxt == NULL)
947 return(-1);
948 ret = xmlXIncludeDoProcess(ctxt, doc);
949
Owen Taylor3473f882001-02-23 17:55:21 +0000950 xmlXIncludeFreeContext(ctxt);
951 return(ret);
952}
953
954#else /* !LIBXML_XINCLUDE_ENABLED */
955#endif