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