blob: 7650952f5b2b656743f4eb823daf1053e839a20b [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
Daniel Veillard34ce8be2002-03-18 19:37:11 +000017#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000018#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000019
Owen Taylor3473f882001-02-23 17:55:21 +000020#include <string.h>
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>
Owen Taylor3473f882001-02-23 17:55:21 +000027#include <libxml/xmlerror.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000028#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000029
30#ifdef LIBXML_XINCLUDE_ENABLED
31#include <libxml/xinclude.h>
32
Daniel Veillardbbd22452001-05-23 12:02:27 +000033#define XINCLUDE_NS (const xmlChar *) "http://www.w3.org/2001/XInclude"
Owen Taylor3473f882001-02-23 17:55:21 +000034#define XINCLUDE_NODE (const xmlChar *) "include"
Daniel Veillard58e44c92002-08-02 22:19:49 +000035#define XINCLUDE_FALLBACK (const xmlChar *) "fallback"
Owen Taylor3473f882001-02-23 17:55:21 +000036#define XINCLUDE_HREF (const xmlChar *) "href"
37#define XINCLUDE_PARSE (const xmlChar *) "parse"
38#define XINCLUDE_PARSE_XML (const xmlChar *) "xml"
39#define XINCLUDE_PARSE_TEXT (const xmlChar *) "text"
40
41/* #define DEBUG_XINCLUDE */
Daniel Veillard017b1082001-06-21 11:20:21 +000042#ifdef DEBUG_XINCLUDE
43#ifdef LIBXML_DEBUG_ENABLED
44#include <libxml/debugXML.h>
45#endif
46#endif
Owen Taylor3473f882001-02-23 17:55:21 +000047
48/************************************************************************
49 * *
50 * XInclude contexts handling *
51 * *
52 ************************************************************************/
53
54/*
55 * An XInclude context
56 */
Daniel Veillardedac3c92001-02-26 01:36:19 +000057typedef xmlChar *xmlURL;
Daniel Veillardbbc72c32002-09-05 10:52:10 +000058
59typedef struct _xmlXIncludeRef xmlXIncludeRef;
60typedef xmlXIncludeRef *xmlXIncludeRefPtr;
61struct _xmlXIncludeRef {
62 xmlChar *URI; /* the rully resolved resource URL */
63 xmlChar *fragment; /* the fragment in the URI */
64 xmlDocPtr doc; /* the parsed document */
65 xmlNodePtr ref; /* the node making the reference in the source */
66 xmlNodePtr inc; /* the included copy */
67 int xml; /* xml or txt */
68 int count; /* how many refs use that specific doc */
69};
70
Owen Taylor3473f882001-02-23 17:55:21 +000071typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt;
72typedef xmlXIncludeCtxt *xmlXIncludeCtxtPtr;
73struct _xmlXIncludeCtxt {
74 xmlDocPtr doc; /* the source document */
Daniel Veillardbbc72c32002-09-05 10:52:10 +000075 int incBase; /* the first include for this document */
Owen Taylor3473f882001-02-23 17:55:21 +000076 int incNr; /* number of includes */
77 int incMax; /* size of includes tab */
Daniel Veillardbbc72c32002-09-05 10:52:10 +000078 xmlXIncludeRefPtr *incTab; /* array of included references */
79
Owen Taylor3473f882001-02-23 17:55:21 +000080 int txtNr; /* number of unparsed documents */
81 int txtMax; /* size of unparsed documents tab */
82 xmlNodePtr *txtTab; /* array of unparsed text nodes */
Daniel Veillardedac3c92001-02-26 01:36:19 +000083 xmlURL *txturlTab; /* array of unparsed txtuments URLs */
Owen Taylor3473f882001-02-23 17:55:21 +000084};
85
Daniel Veillardd16df9f2001-05-23 13:44:21 +000086static int
87xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc);
Owen Taylor3473f882001-02-23 17:55:21 +000088
89/**
Daniel Veillardbbc72c32002-09-05 10:52:10 +000090 * xmlXIncludeFreeRef:
91 * @ref: the XInclude reference
92 *
93 * Free an XInclude reference
94 */
95static void
96xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) {
97 if (ref == NULL)
98 return;
99#ifdef DEBUG_XINCLUDE
100 xmlGenericError(xmlGenericErrorContext, "Freeing ref\n");
101#endif
102 if (ref->doc != NULL) {
103#ifdef DEBUG_XINCLUDE
104 xmlGenericError(xmlGenericErrorContext, "Freeing doc %s\n", ref->URI);
105#endif
106 xmlFreeDoc(ref->doc);
107 }
108 if (ref->URI != NULL)
109 xmlFree(ref->URI);
110 if (ref->fragment != NULL)
111 xmlFree(ref->fragment);
112 xmlFree(ref);
113}
114
115/**
116 * xmlXIncludeNewRef:
117 * @ctxt: the XInclude context
118 * @URI: the resource URI
119 *
120 * Creates a new reference within an XInclude context
121 *
122 * Returns the new set
123 */
124static xmlXIncludeRefPtr
125xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI,
126 xmlNodePtr ref) {
127 xmlXIncludeRefPtr ret;
128
129#ifdef DEBUG_XINCLUDE
130 xmlGenericError(xmlGenericErrorContext, "New ref %s\n", URI);
131#endif
132 ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef));
133 if (ret == NULL)
134 return(NULL);
135 memset(ret, 0, sizeof(xmlXIncludeRef));
136 if (URI == NULL)
137 ret->URI = NULL;
138 else
139 ret->URI = xmlStrdup(URI);
140 ret->fragment = NULL;
141 ret->ref = ref;
142 ret->doc = 0;
143 ret->count = 0;
144 ret->xml = 0;
145 ret->inc = NULL;
146 if (ctxt->incMax == 0) {
147 ctxt->incMax = 4;
148 ctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(ctxt->incMax *
149 sizeof(ctxt->incTab[0]));
150 if (ctxt->incTab == NULL) {
151 xmlGenericError(xmlGenericErrorContext,
152 "malloc failed !\n");
153 xmlXIncludeFreeRef(ret);
154 return(NULL);
155 }
156 }
157 if (ctxt->incNr >= ctxt->incMax) {
158 ctxt->incMax *= 2;
159 ctxt->incTab = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab,
160 ctxt->incMax * sizeof(ctxt->incTab[0]));
161 if (ctxt->incTab == NULL) {
162 xmlGenericError(xmlGenericErrorContext,
163 "realloc failed !\n");
164 xmlXIncludeFreeRef(ret);
165 return(NULL);
166 }
167 }
168 ctxt->incTab[ctxt->incNr++] = ret;
169 return(ret);
170}
171
172/**
Owen Taylor3473f882001-02-23 17:55:21 +0000173 * xmlXIncludeNewContext:
174 * @doc: an XML Document
175 *
176 * Creates a new XInclude context
177 *
178 * Returns the new set
179 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000180static xmlXIncludeCtxtPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000181xmlXIncludeNewContext(xmlDocPtr doc) {
182 xmlXIncludeCtxtPtr ret;
183
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000184#ifdef DEBUG_XINCLUDE
185 xmlGenericError(xmlGenericErrorContext, "New context\n");
186#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000187 if (doc == NULL)
188 return(NULL);
189 ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt));
190 if (ret == NULL)
191 return(NULL);
192 memset(ret, 0, sizeof(xmlXIncludeCtxt));
193 ret->doc = doc;
194 ret->incNr = 0;
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000195 ret->incBase = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000196 ret->incMax = 0;
197 ret->incTab = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000198 return(ret);
199}
200
201/**
202 * xmlXIncludeFreeContext:
203 * @ctxt: the XInclude context
204 *
205 * Free an XInclude context
206 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000207static void
Owen Taylor3473f882001-02-23 17:55:21 +0000208xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) {
209 int i;
210
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000211#ifdef DEBUG_XINCLUDE
212 xmlGenericError(xmlGenericErrorContext, "Freeing context\n");
213#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000214 if (ctxt == NULL)
215 return;
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000216 for (i = 0;i < ctxt->incNr;i++) {
217 if (ctxt->incTab[i] != NULL)
218 xmlXIncludeFreeRef(ctxt->incTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +0000219 }
220 for (i = 0;i < ctxt->txtNr;i++) {
221 if (ctxt->txturlTab[i] != NULL)
222 xmlFree(ctxt->txturlTab[i]);
223 }
224 if (ctxt->incTab != NULL)
225 xmlFree(ctxt->incTab);
Owen Taylor3473f882001-02-23 17:55:21 +0000226 if (ctxt->txtTab != NULL)
227 xmlFree(ctxt->txtTab);
228 if (ctxt->txturlTab != NULL)
229 xmlFree(ctxt->txturlTab);
Owen Taylor3473f882001-02-23 17:55:21 +0000230 xmlFree(ctxt);
231}
232
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000233/**
234 * xmlXIncludeAddNode:
235 * @ctxt: the XInclude context
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000236 * @cur: the new node
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000237 *
238 * Add a new node to process to an XInclude context
239 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000240static int
241xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
242 xmlXIncludeRefPtr ref;
243 xmlURIPtr uri;
244 xmlChar *URL;
245 xmlChar *fragment = NULL;
246 xmlChar *href;
247 xmlChar *parse;
248 xmlChar *base;
249 xmlChar *URI;
250 int xml = 1; /* default Issue 64 */
251
252
253 if (ctxt == NULL)
254 return(-1);
255 if (cur == NULL)
256 return(-1);
257
258#ifdef DEBUG_XINCLUDE
259 xmlGenericError(xmlGenericErrorContext, "Add node\n");
260#endif
261 /*
262 * read the attributes
263 */
264 href = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_HREF);
265 if (href == NULL) {
266 href = xmlGetProp(cur, XINCLUDE_HREF);
267 if (href == NULL) {
268 xmlGenericError(xmlGenericErrorContext, "XInclude: no href\n");
269 return(-1);
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000270 }
271 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000272 parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE);
273 if (parse == NULL) {
274 parse = xmlGetProp(cur, XINCLUDE_PARSE);
275 }
276 if (parse != NULL) {
277 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
278 xml = 1;
279 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
280 xml = 0;
281 else {
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000282 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000283 "XInclude: invalid value %s for %s\n",
284 parse, XINCLUDE_PARSE);
285 if (href != NULL)
286 xmlFree(href);
287 if (parse != NULL)
288 xmlFree(parse);
289 return(-1);
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000290 }
291 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000292
293 /*
294 * compute the URI
295 */
296 base = xmlNodeGetBase(ctxt->doc, cur);
297 if (base == NULL) {
298 URI = xmlBuildURI(href, ctxt->doc->URL);
299 } else {
300 URI = xmlBuildURI(href, base);
301 }
302 if (URI == NULL) {
303 xmlChar *escbase;
304 xmlChar *eschref;
305 /*
306 * Some escaping may be needed
307 */
308 escbase = xmlURIEscape(base);
309 eschref = xmlURIEscape(href);
310 URI = xmlBuildURI(eschref, escbase);
311 if (escbase != NULL)
312 xmlFree(escbase);
313 if (eschref != NULL)
314 xmlFree(eschref);
315 }
316 if (parse != NULL)
317 xmlFree(parse);
318 if (href != NULL)
319 xmlFree(href);
320 if (base != NULL)
321 xmlFree(base);
322 if (URI == NULL) {
323 xmlGenericError(xmlGenericErrorContext, "XInclude: failed build URL\n");
324 return(-1);
325 }
326
327 /*
328 * Check the URL and remove any fragment identifier
329 */
330 uri = xmlParseURI((const char *)URI);
331 if (uri == NULL) {
332 xmlGenericError(xmlGenericErrorContext,
333 "XInclude: invalid value URI %s\n", URI);
334 return(-1);
335 }
336 if (uri->fragment != NULL) {
337 fragment = (xmlChar *) uri->fragment;
338 uri->fragment = NULL;
339 }
340 URL = xmlSaveUri(uri);
341 xmlFreeURI(uri);
342 xmlFree(URI);
343 if (URL == NULL) {
344 xmlGenericError(xmlGenericErrorContext,
345 "XInclude: invalid value URI %s\n", URI);
346 if (fragment != NULL)
347 xmlFree(fragment);
348 return(-1);
349 }
350
351 ref = xmlXIncludeNewRef(ctxt, URL, cur);
352 if (ref == NULL) {
353 return(-1);
354 }
355 ref->fragment = fragment;
356 ref->doc = NULL;
357 ref->xml = xml;
358 ref->count = 1;
359 xmlFree(URL);
360 return(0);
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000361}
362
363/**
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000364 * xmlXIncludeRecurseDoc:
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000365 * @ctxt: the XInclude context
366 * @doc: the new document
367 * @url: the associated URL
368 *
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000369 * The XInclude recursive nature is handled at this point.
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000370 */
371static void
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000372xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, const xmlURL url) {
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000373 xmlXIncludeCtxtPtr newctxt;
374 int i;
375
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000376#ifdef DEBUG_XINCLUDE
377 xmlGenericError(xmlGenericErrorContext, "Recursing in doc %s\n", doc->URL);
378#endif
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000379 /*
380 * Handle recursion here.
381 */
382
383 newctxt = xmlXIncludeNewContext(doc);
384 if (newctxt != NULL) {
385 /*
386 * Copy the existing document set
387 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000388 newctxt->incMax = ctxt->incMax;
389 newctxt->incNr = ctxt->incNr;
390 newctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(newctxt->incMax *
391 sizeof(newctxt->incTab[0]));
392 if (newctxt->incTab == NULL) {
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000393 xmlGenericError(xmlGenericErrorContext,
394 "malloc failed !\n");
395 xmlFree(newctxt);
396 return;
397 }
398
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000399 /*
400 * Inherit the documents already in use by others includes
401 */
402 newctxt->incBase = ctxt->incNr;
403 for (i = 0;i < ctxt->incNr;i++) {
404 newctxt->incTab[i] = ctxt->incTab[i];
405 newctxt->incTab[i]->count++; /* prevent the recursion from
406 freeing it */
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000407 }
408 xmlXIncludeDoProcess(newctxt, doc);
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000409 for (i = 0;i < ctxt->incNr;i++) {
410 newctxt->incTab[i]->count--;
411 newctxt->incTab[i] = NULL;
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000412 }
413 xmlXIncludeFreeContext(newctxt);
414 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000415#ifdef DEBUG_XINCLUDE
416 xmlGenericError(xmlGenericErrorContext, "Done recursing in doc %s\n", url);
417#endif
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000418}
419
420/**
421 * xmlXIncludeAddTxt:
422 * @ctxt: the XInclude context
423 * @txt: the new text node
424 * @url: the associated URL
425 *
426 * Add a new txtument to the list
427 */
428static void
429xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL url) {
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000430#ifdef DEBUG_XINCLUDE
431 xmlGenericError(xmlGenericErrorContext, "Adding text %s\n", url);
432#endif
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000433 if (ctxt->txtMax == 0) {
434 ctxt->txtMax = 4;
435 ctxt->txtTab = (xmlNodePtr *) xmlMalloc(ctxt->txtMax *
436 sizeof(ctxt->txtTab[0]));
437 if (ctxt->txtTab == NULL) {
438 xmlGenericError(xmlGenericErrorContext,
439 "malloc failed !\n");
440 return;
441 }
442 ctxt->txturlTab = (xmlURL *) xmlMalloc(ctxt->txtMax *
443 sizeof(ctxt->txturlTab[0]));
444 if (ctxt->txturlTab == NULL) {
445 xmlGenericError(xmlGenericErrorContext,
446 "malloc failed !\n");
447 return;
448 }
449 }
450 if (ctxt->txtNr >= ctxt->txtMax) {
451 ctxt->txtMax *= 2;
452 ctxt->txtTab = (xmlNodePtr *) xmlRealloc(ctxt->txtTab,
453 ctxt->txtMax * sizeof(ctxt->txtTab[0]));
454 if (ctxt->txtTab == NULL) {
455 xmlGenericError(xmlGenericErrorContext,
456 "realloc failed !\n");
457 return;
458 }
459 ctxt->txturlTab = (xmlURL *) xmlRealloc(ctxt->txturlTab,
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000460 ctxt->txtMax * sizeof(ctxt->txturlTab[0]));
Daniel Veillardd16df9f2001-05-23 13:44:21 +0000461 if (ctxt->txturlTab == NULL) {
462 xmlGenericError(xmlGenericErrorContext,
463 "realloc failed !\n");
464 return;
465 }
466 }
467 ctxt->txtTab[ctxt->txtNr] = txt;
468 ctxt->txturlTab[ctxt->txtNr] = xmlStrdup(url);
469 ctxt->txtNr++;
470}
471
Owen Taylor3473f882001-02-23 17:55:21 +0000472/************************************************************************
473 * *
Daniel Veillardc5f05ad2002-02-10 11:57:22 +0000474 * Node copy with specific semantic *
475 * *
476 ************************************************************************/
477
478/**
479 * xmlXIncludeCopyNode:
480 * @ctxt: the XInclude context
481 * @target: the document target
482 * @source: the document source
483 * @elem: the element
484 *
485 * Make a copy of the node while preserving the XInclude semantic
486 * of the Infoset copy
487 */
488static xmlNodePtr
489xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
490 xmlDocPtr source, xmlNodePtr elem) {
491 xmlNodePtr result = NULL;
492
493 if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
494 (elem == NULL))
495 return(NULL);
496 if (elem->type == XML_DTD_NODE)
497 return(NULL);
498 result = xmlDocCopyNode(elem, target, 1);
499 return(result);
500}
501
502/**
503 * xmlXIncludeCopyNodeList:
504 * @ctxt: the XInclude context
505 * @target: the document target
506 * @source: the document source
507 * @elem: the element list
508 *
509 * Make a copy of the node list while preserving the XInclude semantic
510 * of the Infoset copy
511 */
512static xmlNodePtr
513xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
514 xmlDocPtr source, xmlNodePtr elem) {
515 xmlNodePtr cur, res, result = NULL, last = NULL;
516
517 if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
518 (elem == NULL))
519 return(NULL);
520 cur = elem;
521 while (cur != NULL) {
522 res = xmlXIncludeCopyNode(ctxt, target, source, cur);
523 if (res != NULL) {
524 if (result == NULL) {
525 result = last = res;
526 } else {
527 last->next = res;
528 res->prev = last;
529 last = res;
530 }
531 }
532 cur = cur->next;
533 }
534 return(result);
535}
536
537/**
538 * xmlXInclueGetNthChild:
539 * @cur: the node
540 * @no: the child number
541 *
542 * Returns the @no'th element child of @cur or NULL
543 */
544static xmlNodePtr
545xmlXIncludeGetNthChild(xmlNodePtr cur, int no) {
546 int i;
547 if (cur == NULL)
548 return(cur);
549 cur = cur->children;
550 for (i = 0;i <= no;cur = cur->next) {
551 if (cur == NULL)
552 return(cur);
553 if ((cur->type == XML_ELEMENT_NODE) ||
554 (cur->type == XML_DOCUMENT_NODE) ||
555 (cur->type == XML_HTML_DOCUMENT_NODE)) {
556 i++;
557 if (i == no)
558 break;
559 }
560 }
561 return(cur);
562}
563
564xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur);
565
566/**
567 * xmlXIncludeCopyRange:
568 * @ctxt: the XInclude context
569 * @target: the document target
570 * @source: the document source
571 * @obj: the XPointer result from the evaluation.
572 *
573 * Build a node list tree copy of the XPointer result.
574 *
575 * Returns an xmlNodePtr list or NULL.
576 * the caller has to free the node tree.
577 */
578static xmlNodePtr
579xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
580 xmlDocPtr source, xmlXPathObjectPtr range) {
581 /* pointers to generated nodes */
582 xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp;
583 /* pointers to traversal nodes */
584 xmlNodePtr start, cur, end;
585 int index1, index2;
586
587 if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
588 (range == NULL))
589 return(NULL);
590 if (range->type != XPATH_RANGE)
591 return(NULL);
592 start = (xmlNodePtr) range->user;
593
594 if (start == NULL)
595 return(NULL);
596 end = range->user2;
597 if (end == NULL)
598 return(xmlDocCopyNode(start, target, 1));
599
600 cur = start;
601 index1 = range->index;
602 index2 = range->index2;
603 while (cur != NULL) {
604 if (cur == end) {
605 if (cur->type == XML_TEXT_NODE) {
606 const xmlChar *content = cur->content;
607 int len;
608
609 if (content == NULL) {
610 tmp = xmlNewTextLen(NULL, 0);
611 } else {
612 len = index2;
613 if ((cur == start) && (index1 > 1)) {
614 content += (index1 - 1);
615 len -= (index1 - 1);
616 index1 = 0;
617 } else {
618 len = index2;
619 }
620 tmp = xmlNewTextLen(content, len);
621 }
622 /* single sub text node selection */
623 if (list == NULL)
624 return(tmp);
625 /* prune and return full set */
626 if (last != NULL)
627 xmlAddNextSibling(last, tmp);
628 else
629 xmlAddChild(parent, tmp);
630 return(list);
631 } else {
632 tmp = xmlDocCopyNode(cur, target, 0);
633 if (list == NULL)
634 list = tmp;
635 else {
636 if (last != NULL)
637 xmlAddNextSibling(last, tmp);
638 else
639 xmlAddChild(parent, tmp);
640 }
641 last = NULL;
642 parent = tmp;
643
644 if (index2 > 1) {
645 end = xmlXIncludeGetNthChild(cur, index2 - 1);
646 index2 = 0;
647 }
648 if ((cur == start) && (index1 > 1)) {
649 cur = xmlXIncludeGetNthChild(cur, index1 - 1);
650 index1 = 0;
651 } else {
652 cur = cur->children;
653 }
654 /*
655 * Now gather the remaining nodes from cur to end
656 */
657 continue; /* while */
658 }
659 } else if ((cur == start) &&
660 (list == NULL) /* looks superfluous but ... */ ) {
661 if ((cur->type == XML_TEXT_NODE) ||
662 (cur->type == XML_CDATA_SECTION_NODE)) {
663 const xmlChar *content = cur->content;
664
665 if (content == NULL) {
666 tmp = xmlNewTextLen(NULL, 0);
667 } else {
668 if (index1 > 1) {
669 content += (index1 - 1);
670 }
671 tmp = xmlNewText(content);
672 }
673 last = list = tmp;
674 } else {
675 if ((cur == start) && (index1 > 1)) {
676 tmp = xmlDocCopyNode(cur, target, 0);
677 list = tmp;
678 parent = tmp;
679 last = NULL;
680 cur = xmlXIncludeGetNthChild(cur, index1 - 1);
681 index1 = 0;
682 /*
683 * Now gather the remaining nodes from cur to end
684 */
685 continue; /* while */
686 }
687 tmp = xmlDocCopyNode(cur, target, 1);
688 list = tmp;
689 parent = NULL;
690 last = tmp;
691 }
692 } else {
693 tmp = NULL;
694 switch (cur->type) {
695 case XML_DTD_NODE:
696 case XML_ELEMENT_DECL:
697 case XML_ATTRIBUTE_DECL:
698 case XML_ENTITY_NODE:
699 /* Do not copy DTD informations */
700 break;
701 case XML_ENTITY_DECL:
702 /* handle crossing entities -> stack needed */
703 break;
704 case XML_XINCLUDE_START:
705 case XML_XINCLUDE_END:
706 /* don't consider it part of the tree content */
707 break;
708 case XML_ATTRIBUTE_NODE:
709 /* Humm, should not happen ! */
710 break;
711 default:
712 tmp = xmlDocCopyNode(cur, target, 1);
713 break;
714 }
715 if (tmp != NULL) {
716 if ((list == NULL) || ((last == NULL) && (parent == NULL))) {
717 return(NULL);
718 }
719 if (last != NULL)
720 xmlAddNextSibling(last, tmp);
721 else {
722 xmlAddChild(parent, tmp);
723 last = tmp;
724 }
725 }
726 }
727 /*
728 * Skip to next node in document order
729 */
730 if ((list == NULL) || ((last == NULL) && (parent == NULL))) {
731 return(NULL);
732 }
733 cur = xmlXPtrAdvanceNode(cur);
734 }
735 return(list);
736}
737
738/**
739 * xmlXIncludeBuildNodeList:
740 * @ctxt: the XInclude context
741 * @target: the document target
742 * @source: the document source
743 * @obj: the XPointer result from the evaluation.
744 *
745 * Build a node list tree copy of the XPointer result.
746 * This will drop Attributes and Namespace declarations.
747 *
748 * Returns an xmlNodePtr list or NULL.
749 * the caller has to free the node tree.
750 */
751static xmlNodePtr
752xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target,
753 xmlDocPtr source, xmlXPathObjectPtr obj) {
754 xmlNodePtr list = NULL, last = NULL;
755 int i;
756
757 if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
758 (obj == NULL))
759 return(NULL);
760 switch (obj->type) {
761 case XPATH_NODESET: {
762 xmlNodeSetPtr set = obj->nodesetval;
763 if (set == NULL)
764 return(NULL);
765 for (i = 0;i < set->nodeNr;i++) {
766 if (set->nodeTab[i] == NULL)
767 continue;
768 switch (set->nodeTab[i]->type) {
769 case XML_TEXT_NODE:
770 case XML_CDATA_SECTION_NODE:
771 case XML_ELEMENT_NODE:
772 case XML_ENTITY_REF_NODE:
773 case XML_ENTITY_NODE:
774 case XML_PI_NODE:
775 case XML_COMMENT_NODE:
776 case XML_DOCUMENT_NODE:
777 case XML_HTML_DOCUMENT_NODE:
778#ifdef LIBXML_DOCB_ENABLED
779 case XML_DOCB_DOCUMENT_NODE:
780#endif
781 case XML_XINCLUDE_START:
782 case XML_XINCLUDE_END:
783 break;
784 case XML_ATTRIBUTE_NODE:
785 case XML_NAMESPACE_DECL:
786 case XML_DOCUMENT_TYPE_NODE:
787 case XML_DOCUMENT_FRAG_NODE:
788 case XML_NOTATION_NODE:
789 case XML_DTD_NODE:
790 case XML_ELEMENT_DECL:
791 case XML_ATTRIBUTE_DECL:
792 case XML_ENTITY_DECL:
793 continue; /* for */
794 }
795 if (last == NULL)
796 list = last = xmlXIncludeCopyNode(ctxt, target, source,
797 set->nodeTab[i]);
798 else {
799 xmlAddNextSibling(last,
800 xmlXIncludeCopyNode(ctxt, target, source,
801 set->nodeTab[i]));
802 if (last->next != NULL)
803 last = last->next;
804 }
805 }
806 break;
807 }
808 case XPATH_LOCATIONSET: {
809 xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user;
810 if (set == NULL)
811 return(NULL);
812 for (i = 0;i < set->locNr;i++) {
813 if (last == NULL)
814 list = last = xmlXIncludeCopyXPointer(ctxt, target, source,
815 set->locTab[i]);
816 else
817 xmlAddNextSibling(last,
818 xmlXIncludeCopyXPointer(ctxt, target, source,
819 set->locTab[i]));
820 if (last != NULL) {
821 while (last->next != NULL)
822 last = last->next;
823 }
824 }
825 break;
826 }
827 case XPATH_RANGE:
828 return(xmlXIncludeCopyRange(ctxt, target, source, obj));
829 case XPATH_POINT:
830 /* points are ignored in XInclude */
831 break;
832 default:
833 break;
834 }
835 return(list);
836}
837/************************************************************************
838 * *
Owen Taylor3473f882001-02-23 17:55:21 +0000839 * XInclude I/O handling *
840 * *
841 ************************************************************************/
842
843/**
844 * xmlXIncludeLoadDoc:
845 * @ctxt: the XInclude context
846 * @url: the associated URL
847 * @nr: the xinclude node number
848 *
849 * Load the document, and store the result in the XInclude context
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000850 *
851 * Returns 0 in case of success, -1 in case of failure
Owen Taylor3473f882001-02-23 17:55:21 +0000852 */
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000853static int
Owen Taylor3473f882001-02-23 17:55:21 +0000854xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
855 xmlDocPtr doc;
856 xmlURIPtr uri;
857 xmlChar *URL;
858 xmlChar *fragment = NULL;
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000859 int i = 0;
860
861#ifdef DEBUG_XINCLUDE
862 xmlGenericError(xmlGenericErrorContext, "Loading doc %s:%d\n", url, nr);
863#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000864 /*
865 * Check the URL and remove any fragment identifier
866 */
867 uri = xmlParseURI((const char *)url);
868 if (uri == NULL) {
869 xmlGenericError(xmlGenericErrorContext,
870 "XInclude: invalid value URI %s\n", url);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000871 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000872 }
873 if (uri->fragment != NULL) {
874 fragment = (xmlChar *) uri->fragment;
875 uri->fragment = NULL;
876 }
877 URL = xmlSaveUri(uri);
878 xmlFreeURI(uri);
879 if (URL == NULL) {
880 xmlGenericError(xmlGenericErrorContext,
881 "XInclude: invalid value URI %s\n", url);
882 if (fragment != NULL)
883 xmlFree(fragment);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000884 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000885 }
886
887 /*
888 * Handling of references to the local document are done
889 * directly through ctxt->doc.
890 */
891 if ((URL[0] == 0) || (URL[0] == '#')) {
892 doc = NULL;
893 goto loaded;
894 }
895
896 /*
897 * Prevent reloading twice the document.
898 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000899 for (i = 0; i < ctxt->incNr; i++) {
900 if ((xmlStrEqual(URL, ctxt->incTab[i]->URI)) &&
901 (ctxt->incTab[i]->doc != NULL)) {
902 doc = ctxt->incTab[i]->doc;
903#ifdef DEBUG_XINCLUDE
904 printf("Already loaded %s\n", URL);
905#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000906 goto loaded;
907 }
908 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000909
Owen Taylor3473f882001-02-23 17:55:21 +0000910 /*
911 * Load it.
912 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000913#ifdef DEBUG_XINCLUDE
914 printf("loading %s\n", URL);
915#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000916 doc = xmlParseFile((const char *)URL);
917 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +0000918 xmlFree(URL);
919 if (fragment != NULL)
920 xmlFree(fragment);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000921 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000922 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000923 ctxt->incTab[nr]->doc = doc;
924
925 /*
926 * TODO: Make sure we have all entities fixed up
927 */
928
929 /*
930 * We don't need the DTD anymore, free up space
931 if (doc->intSubset != NULL) {
932 xmlUnlinkNode((xmlNodePtr) doc->intSubset);
933 xmlFreeNode((xmlNodePtr) doc->intSubset);
934 doc->intSubset = NULL;
935 }
936 if (doc->extSubset != NULL) {
937 xmlUnlinkNode((xmlNodePtr) doc->extSubset);
938 xmlFreeNode((xmlNodePtr) doc->extSubset);
939 doc->extSubset = NULL;
940 }
941 */
942 xmlXIncludeRecurseDoc(ctxt, doc, URL);
Owen Taylor3473f882001-02-23 17:55:21 +0000943
944loaded:
945 if (fragment == NULL) {
946 /*
947 * Add the top children list as the replacement copy.
Owen Taylor3473f882001-02-23 17:55:21 +0000948 */
949 if (doc == NULL)
Daniel Veillard4497e692001-06-09 14:19:02 +0000950 {
951 /* Hopefully a DTD declaration won't be copied from
952 * the same document */
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000953 ctxt->incTab[nr]->inc = xmlCopyNodeList(ctxt->doc->children);
Daniel Veillard4497e692001-06-09 14:19:02 +0000954 } else {
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000955 ctxt->incTab[nr]->inc = xmlXIncludeCopyNodeList(ctxt, ctxt->doc,
Daniel Veillardc5f05ad2002-02-10 11:57:22 +0000956 doc, doc->children);
Daniel Veillard4497e692001-06-09 14:19:02 +0000957 }
Owen Taylor3473f882001-02-23 17:55:21 +0000958 } else {
959 /*
960 * Computes the XPointer expression and make a copy used
961 * as the replacement copy.
962 */
963 xmlXPathObjectPtr xptr;
964 xmlXPathContextPtr xptrctxt;
Daniel Veillard39196eb2001-06-19 18:09:42 +0000965 xmlNodeSetPtr set;
Owen Taylor3473f882001-02-23 17:55:21 +0000966
967 if (doc == NULL) {
Daniel Veillardbbc72c32002-09-05 10:52:10 +0000968 xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr]->ref,
969 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000970 } else {
971 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
972 }
973 if (xptrctxt == NULL) {
974 xmlGenericError(xmlGenericErrorContext,
975 "XInclude: could create XPointer context\n");
976 xmlFree(URL);
977 xmlFree(fragment);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000978 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000979 }
980 xptr = xmlXPtrEval(fragment, xptrctxt);
981 if (xptr == NULL) {
982 xmlGenericError(xmlGenericErrorContext,
983 "XInclude: XPointer evaluation failed: #%s\n",
984 fragment);
985 xmlXPathFreeContext(xptrctxt);
986 xmlFree(URL);
987 xmlFree(fragment);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +0000988 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000989 }
Daniel Veillard39196eb2001-06-19 18:09:42 +0000990 switch (xptr->type) {
991 case XPATH_UNDEFINED:
992 case XPATH_BOOLEAN:
993 case XPATH_NUMBER:
994 case XPATH_STRING:
995 case XPATH_POINT:
996 case XPATH_USERS:
997 case XPATH_XSLT_TREE:
998 xmlGenericError(xmlGenericErrorContext,
999 "XInclude: XPointer is not a range: #%s\n",
1000 fragment);
1001 xmlXPathFreeContext(xptrctxt);
1002 xmlFree(URL);
1003 xmlFree(fragment);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001004 return(-1);
Daniel Veillard39196eb2001-06-19 18:09:42 +00001005 case XPATH_NODESET:
1006 case XPATH_RANGE:
1007 case XPATH_LOCATIONSET:
1008 break;
1009 }
1010 set = xptr->nodesetval;
1011 if (set != NULL) {
1012 for (i = 0;i < set->nodeNr;i++) {
1013 if (set->nodeTab[i] == NULL)
1014 continue;
1015 switch (set->nodeTab[i]->type) {
1016 case XML_TEXT_NODE:
1017 case XML_CDATA_SECTION_NODE:
1018 case XML_ELEMENT_NODE:
1019 case XML_ENTITY_REF_NODE:
1020 case XML_ENTITY_NODE:
1021 case XML_PI_NODE:
1022 case XML_COMMENT_NODE:
1023 case XML_DOCUMENT_NODE:
1024 case XML_HTML_DOCUMENT_NODE:
1025#ifdef LIBXML_DOCB_ENABLED
1026 case XML_DOCB_DOCUMENT_NODE:
1027#endif
1028 continue;
1029 case XML_ATTRIBUTE_NODE:
1030 xmlGenericError(xmlGenericErrorContext,
1031 "XInclude: XPointer selects an attribute: #%s\n",
1032 fragment);
1033 set->nodeTab[i] = NULL;
1034 continue;
1035 case XML_NAMESPACE_DECL:
1036 xmlGenericError(xmlGenericErrorContext,
1037 "XInclude: XPointer selects a namespace: #%s\n",
1038 fragment);
1039 set->nodeTab[i] = NULL;
1040 continue;
1041 case XML_DOCUMENT_TYPE_NODE:
1042 case XML_DOCUMENT_FRAG_NODE:
1043 case XML_NOTATION_NODE:
1044 case XML_DTD_NODE:
1045 case XML_ELEMENT_DECL:
1046 case XML_ATTRIBUTE_DECL:
1047 case XML_ENTITY_DECL:
1048 case XML_XINCLUDE_START:
1049 case XML_XINCLUDE_END:
1050 xmlGenericError(xmlGenericErrorContext,
1051 "XInclude: XPointer selects unexpected nodes: #%s\n",
1052 fragment);
1053 set->nodeTab[i] = NULL;
1054 set->nodeTab[i] = NULL;
1055 continue; /* for */
1056 }
1057 }
1058 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001059 ctxt->incTab[nr]->inc =
1060 xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
Owen Taylor3473f882001-02-23 17:55:21 +00001061 xmlXPathFreeObject(xptr);
1062 xmlXPathFreeContext(xptrctxt);
1063 xmlFree(fragment);
1064 }
Daniel Veillardc4bad4a2002-08-14 14:45:25 +00001065
1066 /*
1067 * Do the xml:base fixup if needed
1068 */
1069 if ((doc != NULL) && (URL != NULL) && (xmlStrchr(URL, (xmlChar) '/'))) {
1070 xmlNodePtr node;
1071
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001072 node = ctxt->incTab[nr]->inc;
Daniel Veillardc4bad4a2002-08-14 14:45:25 +00001073 while (node != NULL) {
1074 if (node->type == XML_ELEMENT_NODE)
1075 xmlNodeSetBase(node, URL);
1076 node = node->next;
1077 }
1078 }
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001079 if ((nr < ctxt->incNr) && (ctxt->incTab[nr]->doc != NULL) &&
1080 (ctxt->incTab[nr]->count <= 1)) {
1081#ifdef DEBUG_XINCLUDE
1082 printf("freeing %s\n", ctxt->incTab[nr]->doc->URL);
1083#endif
1084 xmlFreeDoc(ctxt->incTab[nr]->doc);
1085 ctxt->incTab[nr]->doc = NULL;
1086 }
Owen Taylor3473f882001-02-23 17:55:21 +00001087 xmlFree(URL);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001088 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00001089}
1090
1091/**
1092 * xmlXIncludeLoadTxt:
1093 * @ctxt: the XInclude context
1094 * @url: the associated URL
1095 * @nr: the xinclude node number
1096 *
1097 * Load the content, and store the result in the XInclude context
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001098 *
1099 * Returns 0 in case of success, -1 in case of failure
Owen Taylor3473f882001-02-23 17:55:21 +00001100 */
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001101static int
Owen Taylor3473f882001-02-23 17:55:21 +00001102xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
1103 xmlParserInputBufferPtr buf;
1104 xmlNodePtr node;
1105 xmlURIPtr uri;
1106 xmlChar *URL;
1107 int i;
1108 /*
1109 * Check the URL and remove any fragment identifier
1110 */
1111 uri = xmlParseURI((const char *)url);
1112 if (uri == NULL) {
1113 xmlGenericError(xmlGenericErrorContext,
1114 "XInclude: invalid value URI %s\n", url);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001115 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001116 }
1117 if (uri->fragment != NULL) {
1118 xmlGenericError(xmlGenericErrorContext,
1119 "XInclude: fragment identifier forbidden for text: %s\n",
1120 uri->fragment);
1121 xmlFreeURI(uri);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001122 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001123 }
1124 URL = xmlSaveUri(uri);
1125 xmlFreeURI(uri);
1126 if (URL == NULL) {
1127 xmlGenericError(xmlGenericErrorContext,
1128 "XInclude: invalid value URI %s\n", url);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001129 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001130 }
1131
1132 /*
1133 * Handling of references to the local document are done
1134 * directly through ctxt->doc.
1135 */
1136 if (URL[0] == 0) {
1137 xmlGenericError(xmlGenericErrorContext,
1138 "XInclude: text serialization of document not available\n");
1139 xmlFree(URL);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001140 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001141 }
1142
1143 /*
1144 * Prevent reloading twice the document.
1145 */
1146 for (i = 0; i < ctxt->txtNr; i++) {
1147 if (xmlStrEqual(URL, ctxt->txturlTab[i])) {
1148 node = xmlCopyNode(ctxt->txtTab[i], 1);
1149 goto loaded;
1150 }
1151 }
1152 /*
1153 * Load it.
1154 * Issue 62: how to detect the encoding
1155 */
1156 buf = xmlParserInputBufferCreateFilename((const char *)URL, 0);
1157 if (buf == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001158 xmlFree(URL);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001159 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001160 }
1161 node = xmlNewText(NULL);
1162
1163 /*
1164 * Scan all chars from the resource and add the to the node
1165 */
1166 while (xmlParserInputBufferRead(buf, 128) > 0) {
1167 int len;
1168 const xmlChar *content;
1169
1170 content = xmlBufferContent(buf->buffer);
1171 len = xmlBufferLength(buf->buffer);
1172 for (i = 0;i < len; i++) {
1173 /*
1174 * TODO: if the encoding issue is solved, scan UTF8 chars instead
1175 */
1176 if (!IS_CHAR(content[i])) {
1177 xmlGenericError(xmlGenericErrorContext,
1178 "XInclude: %s contains invalid char %d\n", URL, content[i]);
1179 } else {
1180 xmlNodeAddContentLen(node, &content[i], 1);
1181 }
1182 }
1183 xmlBufferShrink(buf->buffer, len);
1184 }
1185 xmlFreeParserInputBuffer(buf);
1186 xmlXIncludeAddTxt(ctxt, node, URL);
1187
1188loaded:
1189 /*
1190 * Add the element as the replacement copy.
1191 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001192 ctxt->incTab[nr]->inc = node;
Owen Taylor3473f882001-02-23 17:55:21 +00001193 xmlFree(URL);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001194 return(0);
1195}
1196
1197/**
1198 * xmlXIncludeLoadFallback:
1199 * @ctxt: the XInclude context
1200 * @fallback: the fallback node
1201 * @nr: the xinclude node number
1202 *
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001203 * Load the content of the fallback node, and store the result
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001204 * in the XInclude context
1205 *
1206 * Returns 0 in case of success, -1 in case of failure
1207 */
1208static int
1209xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, int nr) {
1210 if ((fallback == NULL) || (ctxt == NULL))
1211 return(-1);
1212
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001213 ctxt->incTab[nr]->inc = xmlCopyNode(fallback->children, 1);
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001214 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00001215}
1216
1217/************************************************************************
1218 * *
1219 * XInclude Processing *
1220 * *
1221 ************************************************************************/
1222
1223/**
1224 * xmlXIncludePreProcessNode:
1225 * @ctxt: an XInclude context
1226 * @node: an XInclude node
1227 *
Daniel Veillardd16df9f2001-05-23 13:44:21 +00001228 * Implement the XInclude preprocessing, currently just adding the element
1229 * for further processing.
Owen Taylor3473f882001-02-23 17:55:21 +00001230 *
1231 * Returns the result list or NULL in case of error
1232 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001233static xmlNodePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001234xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
1235 xmlXIncludeAddNode(ctxt, node);
1236 return(0);
1237}
1238
1239/**
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001240 * xmlXIncludePreloadNode:
1241 * @ctxt: an XInclude context
1242 * @nr: the node number
1243 *
1244 * Do some precomputations and preload shared documents
1245 *
1246 * Returns 0 if substitution succeeded, -1 if some processing failed
1247 */
1248static int
1249xmlXIncludePreloadNode(xmlXIncludeCtxtPtr ctxt, int nr) {
1250 xmlNodePtr cur;
1251 xmlChar *href;
1252 xmlChar *parse;
1253 xmlChar *base;
1254 xmlChar *URI;
1255 int xml = 1; /* default Issue 64 */
1256 xmlURIPtr uri;
1257 xmlChar *URL;
1258 xmlChar *fragment = NULL;
1259 int i;
1260
1261
1262 if (ctxt == NULL)
1263 return(-1);
1264 if ((nr < 0) || (nr >= ctxt->incNr))
1265 return(-1);
1266 cur = ctxt->incTab[nr]->ref;
1267 if (cur == NULL)
1268 return(-1);
1269
1270 /*
1271 * read the attributes
1272 */
1273 href = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_HREF);
1274 if (href == NULL) {
1275 href = xmlGetProp(cur, XINCLUDE_HREF);
1276 if (href == NULL) {
1277 xmlGenericError(xmlGenericErrorContext, "XInclude: no href\n");
1278 return(-1);
1279 }
1280 }
1281 parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE);
1282 if (parse == NULL) {
1283 parse = xmlGetProp(cur, XINCLUDE_PARSE);
1284 }
1285 if (parse != NULL) {
1286 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
1287 xml = 1;
1288 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
1289 xml = 0;
1290 else {
1291 xmlGenericError(xmlGenericErrorContext,
1292 "XInclude: invalid value %s for %s\n",
1293 parse, XINCLUDE_PARSE);
1294 if (href != NULL)
1295 xmlFree(href);
1296 if (parse != NULL)
1297 xmlFree(parse);
1298 return(-1);
1299 }
1300 }
1301
1302 /*
1303 * compute the URI
1304 */
1305 base = xmlNodeGetBase(ctxt->doc, cur);
1306 if (base == NULL) {
1307 URI = xmlBuildURI(href, ctxt->doc->URL);
1308 } else {
1309 URI = xmlBuildURI(href, base);
1310 }
1311 if (URI == NULL) {
1312 xmlChar *escbase;
1313 xmlChar *eschref;
1314 /*
1315 * Some escaping may be needed
1316 */
1317 escbase = xmlURIEscape(base);
1318 eschref = xmlURIEscape(href);
1319 URI = xmlBuildURI(eschref, escbase);
1320 if (escbase != NULL)
1321 xmlFree(escbase);
1322 if (eschref != NULL)
1323 xmlFree(eschref);
1324 }
1325 if (parse != NULL)
1326 xmlFree(parse);
1327 if (href != NULL)
1328 xmlFree(href);
1329 if (base != NULL)
1330 xmlFree(base);
1331 if (URI == NULL) {
1332 xmlGenericError(xmlGenericErrorContext, "XInclude: failed build URL\n");
1333 return(-1);
1334 }
1335
1336 /*
1337 * Check the URL and remove any fragment identifier
1338 */
1339 uri = xmlParseURI((const char *)URI);
1340 if (uri == NULL) {
1341 xmlGenericError(xmlGenericErrorContext,
1342 "XInclude: invalid value URI %s\n", URI);
1343 xmlFree(URI);
1344 return(-1);
1345 }
1346 if (uri->fragment != NULL) {
1347 fragment = (xmlChar *) uri->fragment;
1348 uri->fragment = NULL;
1349 }
1350 URL = xmlSaveUri(uri);
1351 xmlFreeURI(uri);
1352 if (URL == NULL) {
1353 xmlGenericError(xmlGenericErrorContext,
1354 "XInclude: invalid value URI %s\n", URI);
1355 if (fragment != NULL)
1356 xmlFree(fragment);
1357 xmlFree(URI);
1358 return(-1);
1359 }
1360 xmlFree(URI);
1361 if (fragment != NULL)
1362 xmlFree(fragment);
1363
1364 for (i = 0; i < nr; i++) {
1365 if (xmlStrEqual(URL, ctxt->incTab[i]->URI)) {
1366#ifdef DEBUG_XINCLUDE
1367 printf("Incrementing count for %d : %s\n", i, ctxt->incTab[i]->URI);
1368#endif
1369 ctxt->incTab[i]->count++;
1370 break;
1371 }
1372 }
1373 xmlFree(URL);
1374 return(0);
1375}
1376
1377/**
Owen Taylor3473f882001-02-23 17:55:21 +00001378 * xmlXIncludeLoadNode:
1379 * @ctxt: an XInclude context
1380 * @nr: the node number
1381 *
1382 * Find and load the infoset replacement for the given node.
1383 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001384 * Returns 0 if substitution succeeded, -1 if some processing failed
Owen Taylor3473f882001-02-23 17:55:21 +00001385 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001386static int
Owen Taylor3473f882001-02-23 17:55:21 +00001387xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) {
1388 xmlNodePtr cur;
1389 xmlChar *href;
1390 xmlChar *parse;
1391 xmlChar *base;
1392 xmlChar *URI;
1393 int xml = 1; /* default Issue 64 */
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001394 int ret;
Owen Taylor3473f882001-02-23 17:55:21 +00001395
1396 if (ctxt == NULL)
1397 return(-1);
1398 if ((nr < 0) || (nr >= ctxt->incNr))
1399 return(-1);
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001400 cur = ctxt->incTab[nr]->ref;
Owen Taylor3473f882001-02-23 17:55:21 +00001401 if (cur == NULL)
1402 return(-1);
1403
Owen Taylor3473f882001-02-23 17:55:21 +00001404 /*
1405 * read the attributes
1406 */
1407 href = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_HREF);
1408 if (href == NULL) {
1409 href = xmlGetProp(cur, XINCLUDE_HREF);
1410 if (href == NULL) {
1411 xmlGenericError(xmlGenericErrorContext, "XInclude: no href\n");
1412 return(-1);
1413 }
1414 }
1415 parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE);
1416 if (parse == NULL) {
1417 parse = xmlGetProp(cur, XINCLUDE_PARSE);
1418 }
1419 if (parse != NULL) {
1420 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML))
1421 xml = 1;
1422 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT))
1423 xml = 0;
1424 else {
1425 xmlGenericError(xmlGenericErrorContext,
1426 "XInclude: invalid value %s for %s\n",
1427 parse, XINCLUDE_PARSE);
1428 if (href != NULL)
1429 xmlFree(href);
1430 if (parse != NULL)
1431 xmlFree(parse);
1432 return(-1);
1433 }
1434 }
1435
1436 /*
1437 * compute the URI
1438 */
1439 base = xmlNodeGetBase(ctxt->doc, cur);
1440 if (base == NULL) {
1441 URI = xmlBuildURI(href, ctxt->doc->URL);
1442 } else {
1443 URI = xmlBuildURI(href, base);
1444 }
1445 if (URI == NULL) {
1446 xmlChar *escbase;
1447 xmlChar *eschref;
1448 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001449 * Some escaping may be needed
Owen Taylor3473f882001-02-23 17:55:21 +00001450 */
1451 escbase = xmlURIEscape(base);
1452 eschref = xmlURIEscape(href);
1453 URI = xmlBuildURI(eschref, escbase);
1454 if (escbase != NULL)
1455 xmlFree(escbase);
1456 if (eschref != NULL)
1457 xmlFree(eschref);
1458 }
1459 if (URI == NULL) {
1460 xmlGenericError(xmlGenericErrorContext, "XInclude: failed build URL\n");
1461 if (parse != NULL)
1462 xmlFree(parse);
1463 if (href != NULL)
1464 xmlFree(href);
1465 if (base != NULL)
1466 xmlFree(base);
1467 return(-1);
1468 }
1469#ifdef DEBUG_XINCLUDE
1470 xmlGenericError(xmlGenericErrorContext, "parse: %s\n",
1471 xml ? "xml": "text");
1472 xmlGenericError(xmlGenericErrorContext, "URI: %s\n", URI);
1473#endif
1474
1475 /*
1476 * Cleanup
1477 */
1478 if (xml) {
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001479 ret = xmlXIncludeLoadDoc(ctxt, URI, nr);
Owen Taylor3473f882001-02-23 17:55:21 +00001480 /* xmlXIncludeGetFragment(ctxt, cur, URI); */
1481 } else {
Daniel Veillarde3b7d9a2002-08-14 14:11:30 +00001482 ret = xmlXIncludeLoadTxt(ctxt, URI, nr);
1483 }
1484 if (ret < 0) {
1485 xmlNodePtr children;
1486
1487 /*
1488 * Time to try a fallback if availble
1489 */
1490#ifdef DEBUG_XINCLUDE
1491 xmlGenericError(xmlGenericErrorContext, "error looking for fallback\n");
1492#endif
1493 children = cur->children;
1494 while (children != NULL) {
1495 if ((children->type == XML_ELEMENT_NODE) &&
1496 (children->ns != NULL) &&
1497 (xmlStrEqual(children->name, XINCLUDE_FALLBACK)) &&
1498 (xmlStrEqual(children->ns->href, XINCLUDE_NS))) {
1499 ret = xmlXIncludeLoadFallback(ctxt, children, nr);
1500 if (ret == 0)
1501 break;
1502 }
1503 children = children->next;
1504 }
1505 }
1506 if (ret < 0) {
1507 xmlGenericError(xmlGenericErrorContext,
1508 "XInclude: could not load %s, and no fallback was found\n",
1509 URI);
Owen Taylor3473f882001-02-23 17:55:21 +00001510 }
1511
1512 /*
1513 * Cleanup
1514 */
1515 if (URI != NULL)
1516 xmlFree(URI);
1517 if (parse != NULL)
1518 xmlFree(parse);
1519 if (href != NULL)
1520 xmlFree(href);
1521 if (base != NULL)
1522 xmlFree(base);
1523 return(0);
1524}
1525
1526/**
1527 * xmlXIncludeIncludeNode:
1528 * @ctxt: an XInclude context
1529 * @nr: the node number
1530 *
1531 * Inplement the infoset replacement for the given node
1532 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001533 * Returns 0 if substitution succeeded, -1 if some processing failed
Owen Taylor3473f882001-02-23 17:55:21 +00001534 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001535static int
Owen Taylor3473f882001-02-23 17:55:21 +00001536xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
1537 xmlNodePtr cur, end, list;
1538
1539 if (ctxt == NULL)
1540 return(-1);
1541 if ((nr < 0) || (nr >= ctxt->incNr))
1542 return(-1);
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001543 cur = ctxt->incTab[nr]->ref;
Owen Taylor3473f882001-02-23 17:55:21 +00001544 if (cur == NULL)
1545 return(-1);
1546
1547 /*
1548 * Change the current node as an XInclude start one, and add an
1549 * entity end one
1550 */
1551 cur->type = XML_XINCLUDE_START;
1552 end = xmlNewNode(cur->ns, cur->name);
1553 if (end == NULL) {
1554 xmlGenericError(xmlGenericErrorContext,
1555 "XInclude: failed to build node\n");
1556 return(-1);
1557 }
1558 end->type = XML_XINCLUDE_END;
1559 xmlAddNextSibling(cur, end);
1560
1561 /*
1562 * Add the list of nodes
1563 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001564 list = ctxt->incTab[nr]->inc;
1565 ctxt->incTab[nr]->inc = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001566 while (list != NULL) {
1567 cur = list;
1568 list = list->next;
1569
1570 xmlAddPrevSibling(end, cur);
1571 }
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001572
1573
Owen Taylor3473f882001-02-23 17:55:21 +00001574 return(0);
1575}
1576
1577/**
1578 * xmlXIncludeTestNode:
Owen Taylor3473f882001-02-23 17:55:21 +00001579 * @node: an XInclude node
1580 *
1581 * test if the node is an XInclude node
1582 *
1583 * Returns 1 true, 0 otherwise
1584 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001585static int
1586xmlXIncludeTestNode(xmlNodePtr node) {
Owen Taylor3473f882001-02-23 17:55:21 +00001587 if (node == NULL)
1588 return(0);
1589 if (node->ns == NULL)
1590 return(0);
1591 if ((xmlStrEqual(node->name, XINCLUDE_NODE)) &&
1592 (xmlStrEqual(node->ns->href, XINCLUDE_NS))) return(1);
1593 return(0);
1594}
1595
1596/**
Daniel Veillardd16df9f2001-05-23 13:44:21 +00001597 * xmlXIncludeDoProcess:
1598 * @ctxt:
Owen Taylor3473f882001-02-23 17:55:21 +00001599 * @doc: an XML document
1600 *
1601 * Implement the XInclude substitution on the XML document @doc
1602 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001603 * Returns 0 if no substitution were done, -1 if some processing failed
Owen Taylor3473f882001-02-23 17:55:21 +00001604 * or the number of substitutions done.
1605 */
Daniel Veillardd16df9f2001-05-23 13:44:21 +00001606static int
1607xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001608 xmlNodePtr cur;
1609 int ret = 0;
1610 int i;
1611
1612 if (doc == NULL)
1613 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00001614 if (ctxt == NULL)
1615 return(-1);
1616
1617 /*
1618 * First phase: lookup the elements in the document
1619 */
1620 cur = xmlDocGetRootElement(doc);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001621 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +00001622 xmlXIncludePreProcessNode(ctxt, cur);
1623 while (cur != NULL) {
1624 /* TODO: need to work on entities -> stack */
1625 if ((cur->children != NULL) &&
1626 (cur->children->type != XML_ENTITY_DECL)) {
1627 cur = cur->children;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001628 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +00001629 xmlXIncludePreProcessNode(ctxt, cur);
1630 } else if (cur->next != NULL) {
1631 cur = cur->next;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001632 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +00001633 xmlXIncludePreProcessNode(ctxt, cur);
1634 } else {
1635 do {
1636 cur = cur->parent;
1637 if (cur == NULL) break; /* do */
1638 if (cur->next != NULL) {
1639 cur = cur->next;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001640 if (xmlXIncludeTestNode(cur))
Owen Taylor3473f882001-02-23 17:55:21 +00001641 xmlXIncludePreProcessNode(ctxt, cur);
1642 break; /* do */
1643 }
1644 } while (cur != NULL);
1645 }
1646 }
1647
1648 /*
1649 * Second Phase : collect the infosets fragments
1650 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001651 /*
1652 for (i = ctxt->incBase;i < ctxt->incNr; i++) {
1653 xmlXIncludePreloadNode(ctxt, i);
1654 }
1655 */
1656 for (i = ctxt->incBase;i < ctxt->incNr; i++) {
Owen Taylor3473f882001-02-23 17:55:21 +00001657 xmlXIncludeLoadNode(ctxt, i);
1658 }
1659
1660 /*
1661 * Third phase: extend the original document infoset.
1662 */
Daniel Veillardbbc72c32002-09-05 10:52:10 +00001663 for (i = ctxt->incBase;i < ctxt->incNr; i++) {
Owen Taylor3473f882001-02-23 17:55:21 +00001664 xmlXIncludeIncludeNode(ctxt, i);
1665 }
1666
Daniel Veillardd16df9f2001-05-23 13:44:21 +00001667 return(ret);
1668}
1669
1670/**
1671 * xmlXIncludeProcess:
1672 * @doc: an XML document
1673 *
1674 * Implement the XInclude substitution on the XML document @doc
1675 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001676 * Returns 0 if no substitution were done, -1 if some processing failed
Daniel Veillardd16df9f2001-05-23 13:44:21 +00001677 * or the number of substitutions done.
1678 */
1679int
1680xmlXIncludeProcess(xmlDocPtr doc) {
1681 xmlXIncludeCtxtPtr ctxt;
1682 int ret = 0;
1683
1684 if (doc == NULL)
1685 return(-1);
1686 ctxt = xmlXIncludeNewContext(doc);
1687 if (ctxt == NULL)
1688 return(-1);
1689 ret = xmlXIncludeDoProcess(ctxt, doc);
1690
Owen Taylor3473f882001-02-23 17:55:21 +00001691 xmlXIncludeFreeContext(ctxt);
1692 return(ret);
1693}
1694
1695#else /* !LIBXML_XINCLUDE_ENABLED */
1696#endif