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