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