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