1/* 2 * transform.c: Implementation of the XSL Transformation 1.0 engine 3 * transform part, i.e. applying a Stylesheet to a document 4 * 5 * References: 6 * http://www.w3.org/TR/1999/REC-xslt-19991116 7 * 8 * Michael Kay "XSLT Programmer's Reference" pp 637-643 9 * Writing Multiple Output Files 10 * 11 * XSLT-1.1 Working Draft 12 * http://www.w3.org/TR/xslt11#multiple-output 13 * 14 * See Copyright for the status of this software. 15 * 16 * daniel@veillard.com 17 */ 18 19#define IN_LIBXSLT 20#include "libxslt.h" 21 22#include <string.h> 23#include <stdio.h> 24 25#include <libxml/xmlmemory.h> 26#include <libxml/parser.h> 27#include <libxml/tree.h> 28#include <libxml/valid.h> 29#include <libxml/hash.h> 30#include <libxml/encoding.h> 31#include <libxml/xmlerror.h> 32#include <libxml/xpath.h> 33#include <libxml/parserInternals.h> 34#include <libxml/xpathInternals.h> 35#include <libxml/HTMLtree.h> 36#include <libxml/debugXML.h> 37#include <libxml/uri.h> 38#include "xslt.h" 39#include "xsltInternals.h" 40#include "xsltutils.h" 41#include "pattern.h" 42#include "transform.h" 43#include "variables.h" 44#include "numbersInternals.h" 45#include "namespaces.h" 46#include "attributes.h" 47#include "templates.h" 48#include "imports.h" 49#include "keys.h" 50#include "documents.h" 51#include "extensions.h" 52#include "extra.h" 53#include "preproc.h" 54#include "security.h" 55 56#ifdef WITH_XSLT_DEBUG 57#define WITH_XSLT_DEBUG_EXTRA 58#define WITH_XSLT_DEBUG_PROCESS 59#endif 60 61#define XSLT_GENERATE_HTML_DOCTYPE 62#ifdef XSLT_GENERATE_HTML_DOCTYPE 63static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, 64 const xmlChar **systemID); 65#endif 66 67int xsltMaxDepth = 3000; 68int xsltMaxVars = 15000; 69 70/* 71 * Useful macros 72 */ 73 74#ifndef FALSE 75# define FALSE (0 == 1) 76# define TRUE (!FALSE) 77#endif 78 79#define IS_BLANK_NODE(n) \ 80 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) 81 82 83/* 84* Forward declarations 85*/ 86 87static xmlNsPtr 88xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur); 89 90static xmlNodePtr 91xsltCopyTreeInternal(xsltTransformContextPtr ctxt, 92 xmlNodePtr invocNode, 93 xmlNodePtr node, 94 xmlNodePtr insert, int isLRE, int topElemVisited); 95 96static void 97xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, 98 xmlNodePtr contextNode, xmlNodePtr list, 99 xsltTemplatePtr templ); 100 101static void 102xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, 103 xmlNodePtr contextNode, 104 xmlNodePtr list, 105 xsltTemplatePtr templ, 106 xsltStackElemPtr withParams); 107 108/** 109 * templPush: 110 * @ctxt: the transformation context 111 * @value: the template to push on the stack 112 * 113 * Push a template on the stack 114 * 115 * Returns the new index in the stack or 0 in case of error 116 */ 117static int 118templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value) 119{ 120 if (ctxt->templMax == 0) { 121 ctxt->templMax = 4; 122 ctxt->templTab = 123 (xsltTemplatePtr *) xmlMalloc(ctxt->templMax * 124 sizeof(ctxt->templTab[0])); 125 if (ctxt->templTab == NULL) { 126 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); 127 return (0); 128 } 129 } 130 else if (ctxt->templNr >= ctxt->templMax) { 131 ctxt->templMax *= 2; 132 ctxt->templTab = 133 (xsltTemplatePtr *) xmlRealloc(ctxt->templTab, 134 ctxt->templMax * 135 sizeof(ctxt->templTab[0])); 136 if (ctxt->templTab == NULL) { 137 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); 138 return (0); 139 } 140 } 141 ctxt->templTab[ctxt->templNr] = value; 142 ctxt->templ = value; 143 return (ctxt->templNr++); 144} 145/** 146 * templPop: 147 * @ctxt: the transformation context 148 * 149 * Pop a template value from the stack 150 * 151 * Returns the stored template value 152 */ 153static xsltTemplatePtr 154templPop(xsltTransformContextPtr ctxt) 155{ 156 xsltTemplatePtr ret; 157 158 if (ctxt->templNr <= 0) 159 return (0); 160 ctxt->templNr--; 161 if (ctxt->templNr > 0) 162 ctxt->templ = ctxt->templTab[ctxt->templNr - 1]; 163 else 164 ctxt->templ = (xsltTemplatePtr) 0; 165 ret = ctxt->templTab[ctxt->templNr]; 166 ctxt->templTab[ctxt->templNr] = 0; 167 return (ret); 168} 169 170/** 171 * xsltLocalVariablePop: 172 * @ctxt: the transformation context 173 * @limitNr: number of variables which should remain 174 * @level: the depth in the xsl:template's tree 175 * 176 * Pops all variable values at the given @depth from the stack. 177 * 178 * Returns the stored variable value 179 * **NOTE:** 180 * This is an internal routine and should not be called by users! 181 */ 182void 183xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level) 184{ 185 xsltStackElemPtr variable; 186 187 if (ctxt->varsNr <= 0) 188 return; 189 190 do { 191 if (ctxt->varsNr <= limitNr) 192 break; 193 variable = ctxt->varsTab[ctxt->varsNr - 1]; 194 if (variable->level <= level) 195 break; 196 if (variable->level >= 0) 197 xsltFreeStackElemList(variable); 198 ctxt->varsNr--; 199 } while (ctxt->varsNr != 0); 200 if (ctxt->varsNr > 0) 201 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; 202 else 203 ctxt->vars = NULL; 204} 205 206/** 207 * xsltTemplateParamsCleanup: 208 * 209 * Removes xsl:param and xsl:with-param items from the 210 * variable-stack. Only xsl:with-param items are not freed. 211 */ 212static void 213xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt) 214{ 215 xsltStackElemPtr param; 216 217 for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) { 218 param = ctxt->varsTab[ctxt->varsNr -1]; 219 /* 220 * Free xsl:param items. 221 * xsl:with-param items will have a level of -1 or -2. 222 */ 223 if (param->level >= 0) { 224 xsltFreeStackElemList(param); 225 } 226 } 227 if (ctxt->varsNr > 0) 228 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; 229 else 230 ctxt->vars = NULL; 231} 232 233/** 234 * profPush: 235 * @ctxt: the transformation context 236 * @value: the profiling value to push on the stack 237 * 238 * Push a profiling value on the stack 239 * 240 * Returns the new index in the stack or 0 in case of error 241 */ 242static int 243profPush(xsltTransformContextPtr ctxt, long value) 244{ 245 if (ctxt->profMax == 0) { 246 ctxt->profMax = 4; 247 ctxt->profTab = 248 (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0])); 249 if (ctxt->profTab == NULL) { 250 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); 251 return (0); 252 } 253 } 254 else if (ctxt->profNr >= ctxt->profMax) { 255 ctxt->profMax *= 2; 256 ctxt->profTab = 257 (long *) xmlRealloc(ctxt->profTab, 258 ctxt->profMax * sizeof(ctxt->profTab[0])); 259 if (ctxt->profTab == NULL) { 260 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); 261 return (0); 262 } 263 } 264 ctxt->profTab[ctxt->profNr] = value; 265 ctxt->prof = value; 266 return (ctxt->profNr++); 267} 268/** 269 * profPop: 270 * @ctxt: the transformation context 271 * 272 * Pop a profiling value from the stack 273 * 274 * Returns the stored profiling value 275 */ 276static long 277profPop(xsltTransformContextPtr ctxt) 278{ 279 long ret; 280 281 if (ctxt->profNr <= 0) 282 return (0); 283 ctxt->profNr--; 284 if (ctxt->profNr > 0) 285 ctxt->prof = ctxt->profTab[ctxt->profNr - 1]; 286 else 287 ctxt->prof = (long) 0; 288 ret = ctxt->profTab[ctxt->profNr]; 289 ctxt->profTab[ctxt->profNr] = 0; 290 return (ret); 291} 292 293static void 294profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent) 295{ 296 int i; 297 298 if (templ->templMax == 0) { 299 templ->templMax = 4; 300 templ->templCalledTab = 301 (xsltTemplatePtr *) xmlMalloc(templ->templMax * 302 sizeof(templ->templCalledTab[0])); 303 templ->templCountTab = 304 (int *) xmlMalloc(templ->templMax * 305 sizeof(templ->templCountTab[0])); 306 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { 307 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); 308 return; 309 } 310 } 311 else if (templ->templNr >= templ->templMax) { 312 templ->templMax *= 2; 313 templ->templCalledTab = 314 (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab, 315 templ->templMax * 316 sizeof(templ->templCalledTab[0])); 317 templ->templCountTab = 318 (int *) xmlRealloc(templ->templCountTab, 319 templ->templMax * 320 sizeof(templ->templCountTab[0])); 321 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { 322 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); 323 return; 324 } 325 } 326 327 for (i = 0; i < templ->templNr; i++) { 328 if (templ->templCalledTab[i] == parent) { 329 templ->templCountTab[i]++; 330 break; 331 } 332 } 333 if (i == templ->templNr) { 334 /* not found, add new one */ 335 templ->templCalledTab[templ->templNr] = parent; 336 templ->templCountTab[templ->templNr] = 1; 337 templ->templNr++; 338 } 339} 340 341/************************************************************************ 342 * * 343 * XInclude default settings * 344 * * 345 ************************************************************************/ 346 347static int xsltDoXIncludeDefault = 0; 348 349/** 350 * xsltSetXIncludeDefault: 351 * @xinclude: whether to do XInclude processing 352 * 353 * Set whether XInclude should be processed on document being loaded by default 354 */ 355void 356xsltSetXIncludeDefault(int xinclude) { 357 xsltDoXIncludeDefault = (xinclude != 0); 358} 359 360/** 361 * xsltGetXIncludeDefault: 362 * 363 * Provides the default state for XInclude processing 364 * 365 * Returns 0 if there is no processing 1 otherwise 366 */ 367int 368xsltGetXIncludeDefault(void) { 369 return(xsltDoXIncludeDefault); 370} 371 372unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL; 373 374/** 375 * xsltDebugSetDefaultTrace: 376 * @val: tracing level mask 377 * 378 * Set the default debug tracing level mask 379 */ 380void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) { 381 xsltDefaultTrace = val; 382} 383 384/** 385 * xsltDebugGetDefaultTrace: 386 * 387 * Get the current default debug tracing level mask 388 * 389 * Returns the current default debug tracing level mask 390 */ 391xsltDebugTraceCodes xsltDebugGetDefaultTrace() { 392 return xsltDefaultTrace; 393} 394 395/************************************************************************ 396 * * 397 * Handling of Transformation Contexts * 398 * * 399 ************************************************************************/ 400 401static xsltTransformCachePtr 402xsltTransformCacheCreate(void) 403{ 404 xsltTransformCachePtr ret; 405 406 ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache)); 407 if (ret == NULL) { 408 xsltTransformError(NULL, NULL, NULL, 409 "xsltTransformCacheCreate : malloc failed\n"); 410 return(NULL); 411 } 412 memset(ret, 0, sizeof(xsltTransformCache)); 413 return(ret); 414} 415 416static void 417xsltTransformCacheFree(xsltTransformCachePtr cache) 418{ 419 if (cache == NULL) 420 return; 421 /* 422 * Free tree fragments. 423 */ 424 if (cache->RVT) { 425 xmlDocPtr tmp, cur = cache->RVT; 426 while (cur) { 427 tmp = cur; 428 cur = (xmlDocPtr) cur->next; 429 if (tmp->_private != NULL) { 430 /* 431 * Tree the document info. 432 */ 433 xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private); 434 xmlFree(tmp->_private); 435 } 436 xmlFreeDoc(tmp); 437 } 438 } 439 /* 440 * Free vars/params. 441 */ 442 if (cache->stackItems) { 443 xsltStackElemPtr tmp, cur = cache->stackItems; 444 while (cur) { 445 tmp = cur; 446 cur = cur->next; 447 /* 448 * REVISIT TODO: Should be call a destruction-function 449 * instead? 450 */ 451 xmlFree(tmp); 452 } 453 } 454 xmlFree(cache); 455} 456 457/** 458 * xsltNewTransformContext: 459 * @style: a parsed XSLT stylesheet 460 * @doc: the input document 461 * 462 * Create a new XSLT TransformContext 463 * 464 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error 465 */ 466xsltTransformContextPtr 467xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) { 468 xsltTransformContextPtr cur; 469 xsltDocumentPtr docu; 470 int i; 471 472 xsltInitGlobals(); 473 474 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext)); 475 if (cur == NULL) { 476 xsltTransformError(NULL, NULL, (xmlNodePtr)doc, 477 "xsltNewTransformContext : malloc failed\n"); 478 return(NULL); 479 } 480 memset(cur, 0, sizeof(xsltTransformContext)); 481 482 cur->cache = xsltTransformCacheCreate(); 483 if (cur->cache == NULL) 484 goto internal_err; 485 /* 486 * setup of the dictionary must be done early as some of the 487 * processing later like key handling may need it. 488 */ 489 cur->dict = xmlDictCreateSub(style->dict); 490 cur->internalized = ((style->internalized) && (cur->dict != NULL)); 491#ifdef WITH_XSLT_DEBUG 492 xsltGenericDebug(xsltGenericDebugContext, 493 "Creating sub-dictionary from stylesheet for transformation\n"); 494#endif 495 496 /* 497 * initialize the template stack 498 */ 499 cur->templTab = (xsltTemplatePtr *) 500 xmlMalloc(10 * sizeof(xsltTemplatePtr)); 501 if (cur->templTab == NULL) { 502 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, 503 "xsltNewTransformContext: out of memory\n"); 504 goto internal_err; 505 } 506 cur->templNr = 0; 507 cur->templMax = 5; 508 cur->templ = NULL; 509 cur->maxTemplateDepth = xsltMaxDepth; 510 511 /* 512 * initialize the variables stack 513 */ 514 cur->varsTab = (xsltStackElemPtr *) 515 xmlMalloc(10 * sizeof(xsltStackElemPtr)); 516 if (cur->varsTab == NULL) { 517 xmlGenericError(xmlGenericErrorContext, 518 "xsltNewTransformContext: out of memory\n"); 519 goto internal_err; 520 } 521 cur->varsNr = 0; 522 cur->varsMax = 10; 523 cur->vars = NULL; 524 cur->varsBase = 0; 525 cur->maxTemplateVars = xsltMaxVars; 526 527 /* 528 * the profiling stack is not initialized by default 529 */ 530 cur->profTab = NULL; 531 cur->profNr = 0; 532 cur->profMax = 0; 533 cur->prof = 0; 534 535 cur->style = style; 536 xmlXPathInit(); 537 cur->xpathCtxt = xmlXPathNewContext(doc); 538 if (cur->xpathCtxt == NULL) { 539 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, 540 "xsltNewTransformContext : xmlXPathNewContext failed\n"); 541 goto internal_err; 542 } 543 /* 544 * Create an XPath cache. 545 */ 546 if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1) 547 goto internal_err; 548 /* 549 * Initialize the extras array 550 */ 551 if (style->extrasNr != 0) { 552 cur->extrasMax = style->extrasNr + 20; 553 cur->extras = (xsltRuntimeExtraPtr) 554 xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra)); 555 if (cur->extras == NULL) { 556 xmlGenericError(xmlGenericErrorContext, 557 "xsltNewTransformContext: out of memory\n"); 558 goto internal_err; 559 } 560 cur->extrasNr = style->extrasNr; 561 for (i = 0;i < cur->extrasMax;i++) { 562 cur->extras[i].info = NULL; 563 cur->extras[i].deallocate = NULL; 564 cur->extras[i].val.ptr = NULL; 565 } 566 } else { 567 cur->extras = NULL; 568 cur->extrasNr = 0; 569 cur->extrasMax = 0; 570 } 571 572 XSLT_REGISTER_VARIABLE_LOOKUP(cur); 573 XSLT_REGISTER_FUNCTION_LOOKUP(cur); 574 cur->xpathCtxt->nsHash = style->nsHash; 575 /* 576 * Initialize the registered external modules 577 */ 578 xsltInitCtxtExts(cur); 579 /* 580 * Setup document element ordering for later efficiencies 581 * (bug 133289) 582 */ 583 if (xslDebugStatus == XSLT_DEBUG_NONE) 584 xmlXPathOrderDocElems(doc); 585 /* 586 * Must set parserOptions before calling xsltNewDocument 587 * (bug 164530) 588 */ 589 cur->parserOptions = XSLT_PARSE_OPTIONS; 590 docu = xsltNewDocument(cur, doc); 591 if (docu == NULL) { 592 xsltTransformError(cur, NULL, (xmlNodePtr)doc, 593 "xsltNewTransformContext : xsltNewDocument failed\n"); 594 goto internal_err; 595 } 596 docu->main = 1; 597 cur->document = docu; 598 cur->inst = NULL; 599 cur->outputFile = NULL; 600 cur->sec = xsltGetDefaultSecurityPrefs(); 601 cur->debugStatus = xslDebugStatus; 602 cur->traceCode = (unsigned long*) &xsltDefaultTrace; 603 cur->xinclude = xsltGetXIncludeDefault(); 604 cur->keyInitLevel = 0; 605 606 return(cur); 607 608internal_err: 609 if (cur != NULL) 610 xsltFreeTransformContext(cur); 611 return(NULL); 612} 613 614/** 615 * xsltFreeTransformContext: 616 * @ctxt: an XSLT parser context 617 * 618 * Free up the memory allocated by @ctxt 619 */ 620void 621xsltFreeTransformContext(xsltTransformContextPtr ctxt) { 622 if (ctxt == NULL) 623 return; 624 625 /* 626 * Shutdown the extension modules associated to the stylesheet 627 * used if needed. 628 */ 629 xsltShutdownCtxtExts(ctxt); 630 631 if (ctxt->xpathCtxt != NULL) { 632 ctxt->xpathCtxt->nsHash = NULL; 633 xmlXPathFreeContext(ctxt->xpathCtxt); 634 } 635 if (ctxt->templTab != NULL) 636 xmlFree(ctxt->templTab); 637 if (ctxt->varsTab != NULL) 638 xmlFree(ctxt->varsTab); 639 if (ctxt->profTab != NULL) 640 xmlFree(ctxt->profTab); 641 if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) { 642 int i; 643 644 for (i = 0;i < ctxt->extrasNr;i++) { 645 if ((ctxt->extras[i].deallocate != NULL) && 646 (ctxt->extras[i].info != NULL)) 647 ctxt->extras[i].deallocate(ctxt->extras[i].info); 648 } 649 xmlFree(ctxt->extras); 650 } 651 xsltFreeGlobalVariables(ctxt); 652 xsltFreeDocuments(ctxt); 653 xsltFreeCtxtExts(ctxt); 654 xsltFreeRVTs(ctxt); 655 xsltTransformCacheFree(ctxt->cache); 656 xmlDictFree(ctxt->dict); 657#ifdef WITH_XSLT_DEBUG 658 xsltGenericDebug(xsltGenericDebugContext, 659 "freeing transformation dictionary\n"); 660#endif 661 memset(ctxt, -1, sizeof(xsltTransformContext)); 662 xmlFree(ctxt); 663} 664 665/************************************************************************ 666 * * 667 * Copy of Nodes in an XSLT fashion * 668 * * 669 ************************************************************************/ 670 671xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, 672 xmlNodePtr node, xmlNodePtr insert, int literal); 673 674/** 675 * xsltAddChild: 676 * @parent: the parent node 677 * @cur: the child node 678 * 679 * Wrapper version of xmlAddChild with a more consistent behaviour on 680 * error. One expect the use to be child = xsltAddChild(parent, child); 681 * and the routine will take care of not leaking on errors or node merge 682 * 683 * Returns the child is successfully attached or NULL if merged or freed 684 */ 685static xmlNodePtr 686xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) { 687 xmlNodePtr ret; 688 689 if ((cur == NULL) || (parent == NULL)) 690 return(NULL); 691 if (parent == NULL) { 692 xmlFreeNode(cur); 693 return(NULL); 694 } 695 ret = xmlAddChild(parent, cur); 696 697 return(ret); 698} 699 700/** 701 * xsltAddTextString: 702 * @ctxt: a XSLT process context 703 * @target: the text node where the text will be attached 704 * @string: the text string 705 * @len: the string length in byte 706 * 707 * Extend the current text node with the new string, it handles coalescing 708 * 709 * Returns: the text node 710 */ 711static xmlNodePtr 712xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, 713 const xmlChar *string, int len) { 714 /* 715 * optimization 716 */ 717 if ((len <= 0) || (string == NULL) || (target == NULL)) 718 return(target); 719 720 if (ctxt->lasttext == target->content) { 721 722 if (ctxt->lasttuse + len >= ctxt->lasttsize) { 723 xmlChar *newbuf; 724 int size; 725 726 size = ctxt->lasttsize + len + 100; 727 size *= 2; 728 newbuf = (xmlChar *) xmlRealloc(target->content,size); 729 if (newbuf == NULL) { 730 xsltTransformError(ctxt, NULL, target, 731 "xsltCopyText: text allocation failed\n"); 732 return(NULL); 733 } 734 ctxt->lasttsize = size; 735 ctxt->lasttext = newbuf; 736 target->content = newbuf; 737 } 738 memcpy(&(target->content[ctxt->lasttuse]), string, len); 739 ctxt->lasttuse += len; 740 target->content[ctxt->lasttuse] = 0; 741 } else { 742 xmlNodeAddContent(target, string); 743 ctxt->lasttext = target->content; 744 len = xmlStrlen(target->content); 745 ctxt->lasttsize = len; 746 ctxt->lasttuse = len; 747 } 748 return(target); 749} 750 751/** 752 * xsltCopyTextString: 753 * @ctxt: a XSLT process context 754 * @target: the element where the text will be attached 755 * @string: the text string 756 * @noescape: should disable-escaping be activated for this text node. 757 * 758 * Adds @string to a newly created or an existent text node child of 759 * @target. 760 * 761 * Returns: the text node, where the text content of @cur is copied to. 762 * NULL in case of API or internal errors. 763 */ 764xmlNodePtr 765xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, 766 const xmlChar *string, int noescape) 767{ 768 xmlNodePtr copy; 769 int len; 770 771 if (string == NULL) 772 return(NULL); 773 774#ifdef WITH_XSLT_DEBUG_PROCESS 775 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, 776 "xsltCopyTextString: copy text %s\n", 777 string)); 778#endif 779 780 /* 781 * Play safe and reset the merging mechanism for every new 782 * target node. 783 */ 784 if ((target == NULL) || (target->children == NULL)) { 785 ctxt->lasttext = NULL; 786 } 787 788 /* handle coalescing of text nodes here */ 789 len = xmlStrlen(string); 790 if ((ctxt->type == XSLT_OUTPUT_XML) && 791 (ctxt->style->cdataSection != NULL) && 792 (target != NULL) && 793 (target->type == XML_ELEMENT_NODE) && 794 (((target->ns == NULL) && 795 (xmlHashLookup2(ctxt->style->cdataSection, 796 target->name, NULL) != NULL)) || 797 ((target->ns != NULL) && 798 (xmlHashLookup2(ctxt->style->cdataSection, 799 target->name, target->ns->href) != NULL)))) 800 { 801 /* 802 * Process "cdata-section-elements". 803 */ 804 if ((target->last != NULL) && 805 (target->last->type == XML_CDATA_SECTION_NODE)) 806 { 807 return(xsltAddTextString(ctxt, target->last, string, len)); 808 } 809 copy = xmlNewCDataBlock(ctxt->output, string, len); 810 } else if (noescape) { 811 /* 812 * Process "disable-output-escaping". 813 */ 814 if ((target != NULL) && (target->last != NULL) && 815 (target->last->type == XML_TEXT_NODE) && 816 (target->last->name == xmlStringTextNoenc)) 817 { 818 return(xsltAddTextString(ctxt, target->last, string, len)); 819 } 820 copy = xmlNewTextLen(string, len); 821 if (copy != NULL) 822 copy->name = xmlStringTextNoenc; 823 } else { 824 /* 825 * Default processing. 826 */ 827 if ((target != NULL) && (target->last != NULL) && 828 (target->last->type == XML_TEXT_NODE) && 829 (target->last->name == xmlStringText)) { 830 return(xsltAddTextString(ctxt, target->last, string, len)); 831 } 832 copy = xmlNewTextLen(string, len); 833 } 834 if (copy != NULL) { 835 if (target != NULL) 836 copy = xsltAddChild(target, copy); 837 ctxt->lasttext = copy->content; 838 ctxt->lasttsize = len; 839 ctxt->lasttuse = len; 840 } else { 841 xsltTransformError(ctxt, NULL, target, 842 "xsltCopyTextString: text copy failed\n"); 843 ctxt->lasttext = NULL; 844 } 845 return(copy); 846} 847 848/** 849 * xsltCopyText: 850 * @ctxt: a XSLT process context 851 * @target: the element where the text will be attached 852 * @cur: the text or CDATA node 853 * @interned: the string is in the target doc dictionary 854 * 855 * Copy the text content of @cur and append it to @target's children. 856 * 857 * Returns: the text node, where the text content of @cur is copied to. 858 * NULL in case of API or internal errors. 859 */ 860static xmlNodePtr 861xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, 862 xmlNodePtr cur, int interned) 863{ 864 xmlNodePtr copy; 865 866 if ((cur->type != XML_TEXT_NODE) && 867 (cur->type != XML_CDATA_SECTION_NODE)) 868 return(NULL); 869 if (cur->content == NULL) 870 return(NULL); 871 872#ifdef WITH_XSLT_DEBUG_PROCESS 873 if (cur->type == XML_CDATA_SECTION_NODE) { 874 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, 875 "xsltCopyText: copy CDATA text %s\n", 876 cur->content)); 877 } else if (cur->name == xmlStringTextNoenc) { 878 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, 879 "xsltCopyText: copy unescaped text %s\n", 880 cur->content)); 881 } else { 882 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, 883 "xsltCopyText: copy text %s\n", 884 cur->content)); 885 } 886#endif 887 888 /* 889 * Play save and reset the merging mechanism for every new 890 * target node. 891 */ 892 if ((target == NULL) || (target->children == NULL)) { 893 ctxt->lasttext = NULL; 894 } 895 896 if ((ctxt->style->cdataSection != NULL) && 897 (ctxt->type == XSLT_OUTPUT_XML) && 898 (target != NULL) && 899 (target->type == XML_ELEMENT_NODE) && 900 (((target->ns == NULL) && 901 (xmlHashLookup2(ctxt->style->cdataSection, 902 target->name, NULL) != NULL)) || 903 ((target->ns != NULL) && 904 (xmlHashLookup2(ctxt->style->cdataSection, 905 target->name, target->ns->href) != NULL)))) 906 { 907 /* 908 * Process "cdata-section-elements". 909 */ 910 /* 911 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content. 912 */ 913 /* 914 * TODO: Since this doesn't merge adjacent CDATA-section nodes, 915 * we'll get: <![CDATA[x]]><!CDATA[y]]>. 916 * TODO: Reported in #321505. 917 */ 918 if ((target->last != NULL) && 919 (target->last->type == XML_CDATA_SECTION_NODE)) 920 { 921 /* 922 * Append to existing CDATA-section node. 923 */ 924 copy = xsltAddTextString(ctxt, target->last, cur->content, 925 xmlStrlen(cur->content)); 926 goto exit; 927 } else { 928 unsigned int len; 929 930 len = xmlStrlen(cur->content); 931 copy = xmlNewCDataBlock(ctxt->output, cur->content, len); 932 if (copy == NULL) 933 goto exit; 934 ctxt->lasttext = copy->content; 935 ctxt->lasttsize = len; 936 ctxt->lasttuse = len; 937 } 938 } else if ((target != NULL) && 939 (target->last != NULL) && 940 /* both escaped or both non-escaped text-nodes */ 941 (((target->last->type == XML_TEXT_NODE) && 942 (target->last->name == cur->name)) || 943 /* non-escaped text nodes and CDATA-section nodes */ 944 (((target->last->type == XML_CDATA_SECTION_NODE) && 945 (cur->name == xmlStringTextNoenc))))) 946 { 947 /* 948 * we are appending to an existing text node 949 */ 950 copy = xsltAddTextString(ctxt, target->last, cur->content, 951 xmlStrlen(cur->content)); 952 goto exit; 953 } else if ((interned) && (target != NULL) && 954 (target->doc != NULL) && 955 (target->doc->dict == ctxt->dict)) 956 { 957 /* 958 * TODO: DO we want to use this also for "text" output? 959 */ 960 copy = xmlNewTextLen(NULL, 0); 961 if (copy == NULL) 962 goto exit; 963 if (cur->name == xmlStringTextNoenc) 964 copy->name = xmlStringTextNoenc; 965 966 /* 967 * Must confirm that content is in dict (bug 302821) 968 * TODO: This check should be not needed for text coming 969 * from the stylesheets 970 */ 971 if (xmlDictOwns(ctxt->dict, cur->content)) 972 copy->content = cur->content; 973 else { 974 if ((copy->content = xmlStrdup(cur->content)) == NULL) 975 return NULL; 976 } 977 } else { 978 /* 979 * normal processing. keep counters to extend the text node 980 * in xsltAddTextString if needed. 981 */ 982 unsigned int len; 983 984 len = xmlStrlen(cur->content); 985 copy = xmlNewTextLen(cur->content, len); 986 if (copy == NULL) 987 goto exit; 988 if (cur->name == xmlStringTextNoenc) 989 copy->name = xmlStringTextNoenc; 990 ctxt->lasttext = copy->content; 991 ctxt->lasttsize = len; 992 ctxt->lasttuse = len; 993 } 994 if (copy != NULL) { 995 if (target != NULL) { 996 copy->doc = target->doc; 997 /* 998 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here 999 * to ensure that the optimized text-merging mechanism 1000 * won't interfere with normal node-merging in any case. 1001 */ 1002 copy = xsltAddChild(target, copy); 1003 } 1004 } else { 1005 xsltTransformError(ctxt, NULL, target, 1006 "xsltCopyText: text copy failed\n"); 1007 } 1008 1009exit: 1010 if ((copy == NULL) || (copy->content == NULL)) { 1011 xsltTransformError(ctxt, NULL, target, 1012 "Internal error in xsltCopyText(): " 1013 "Failed to copy the string.\n"); 1014 ctxt->state = XSLT_STATE_STOPPED; 1015 } 1016 return(copy); 1017} 1018 1019/** 1020 * xsltShallowCopyAttr: 1021 * @ctxt: a XSLT process context 1022 * @invocNode: responsible node in the stylesheet; used for error reports 1023 * @target: the element where the attribute will be grafted 1024 * @attr: the attribute to be copied 1025 * 1026 * Do a copy of an attribute. 1027 * Called by: 1028 * - xsltCopyTreeInternal() 1029 * - xsltCopyOf() 1030 * - xsltCopy() 1031 * 1032 * Returns: a new xmlAttrPtr, or NULL in case of error. 1033 */ 1034static xmlAttrPtr 1035xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, 1036 xmlNodePtr target, xmlAttrPtr attr) 1037{ 1038 xmlAttrPtr copy; 1039 xmlChar *value; 1040 1041 if (attr == NULL) 1042 return(NULL); 1043 1044 if (target->type != XML_ELEMENT_NODE) { 1045 xsltTransformError(ctxt, NULL, invocNode, 1046 "Cannot add an attribute node to a non-element node.\n"); 1047 return(NULL); 1048 } 1049 1050 if (target->children != NULL) { 1051 xsltTransformError(ctxt, NULL, invocNode, 1052 "Attribute nodes must be added before " 1053 "any child nodes to an element.\n"); 1054 return(NULL); 1055 } 1056 1057 value = xmlNodeListGetString(attr->doc, attr->children, 1); 1058 if (attr->ns != NULL) { 1059 xmlNsPtr ns; 1060 1061 ns = xsltGetSpecialNamespace(ctxt, invocNode, 1062 attr->ns->href, attr->ns->prefix, target); 1063 if (ns == NULL) { 1064 xsltTransformError(ctxt, NULL, invocNode, 1065 "Namespace fixup error: Failed to acquire an in-scope " 1066 "namespace binding of the copied attribute '{%s}%s'.\n", 1067 attr->ns->href, attr->name); 1068 /* 1069 * TODO: Should we just stop here? 1070 */ 1071 } 1072 /* 1073 * Note that xmlSetNsProp() will take care of duplicates 1074 * and assigns the new namespace even to a duplicate. 1075 */ 1076 copy = xmlSetNsProp(target, ns, attr->name, value); 1077 } else { 1078 copy = xmlSetNsProp(target, NULL, attr->name, value); 1079 } 1080 if (value != NULL) 1081 xmlFree(value); 1082 1083 if (copy == NULL) 1084 return(NULL); 1085 1086#if 0 1087 /* 1088 * NOTE: This was optimized according to bug #342695. 1089 * TODO: Can this further be optimized, if source and target 1090 * share the same dict and attr->children is just 1 text node 1091 * which is in the dict? How probable is such a case? 1092 */ 1093 /* 1094 * TODO: Do we need to create an empty text node if the value 1095 * is the empty string? 1096 */ 1097 value = xmlNodeListGetString(attr->doc, attr->children, 1); 1098 if (value != NULL) { 1099 txtNode = xmlNewDocText(target->doc, NULL); 1100 if (txtNode == NULL) 1101 return(NULL); 1102 if ((target->doc != NULL) && 1103 (target->doc->dict != NULL)) 1104 { 1105 txtNode->content = 1106 (xmlChar *) xmlDictLookup(target->doc->dict, 1107 BAD_CAST value, -1); 1108 xmlFree(value); 1109 } else 1110 txtNode->content = value; 1111 copy->children = txtNode; 1112 } 1113#endif 1114 1115 return(copy); 1116} 1117 1118/** 1119 * xsltCopyAttrListNoOverwrite: 1120 * @ctxt: a XSLT process context 1121 * @invocNode: responsible node in the stylesheet; used for error reports 1122 * @target: the element where the new attributes will be grafted 1123 * @attr: the first attribute in the list to be copied 1124 * 1125 * Copies a list of attribute nodes, starting with @attr, over to the 1126 * @target element node. 1127 * 1128 * Called by: 1129 * - xsltCopyTreeInternal() 1130 * 1131 * Returns 0 on success and -1 on errors and internal errors. 1132 */ 1133static int 1134xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt, 1135 xmlNodePtr invocNode, 1136 xmlNodePtr target, xmlAttrPtr attr) 1137{ 1138 xmlAttrPtr copy; 1139 xmlNsPtr origNs = NULL, copyNs = NULL; 1140 xmlChar *value; 1141 1142 /* 1143 * Don't use xmlCopyProp() here, since it will try to 1144 * reconciliate namespaces. 1145 */ 1146 while (attr != NULL) { 1147 /* 1148 * Find a namespace node in the tree of @target. 1149 * Avoid searching for the same ns. 1150 */ 1151 if (attr->ns != origNs) { 1152 origNs = attr->ns; 1153 if (attr->ns != NULL) { 1154 copyNs = xsltGetSpecialNamespace(ctxt, invocNode, 1155 attr->ns->href, attr->ns->prefix, target); 1156 if (copyNs == NULL) 1157 return(-1); 1158 } else 1159 copyNs = NULL; 1160 } 1161 /* 1162 * If attribute has a value, we need to copy it (watching out 1163 * for possible entities) 1164 */ 1165 if ((attr->children) && (attr->children->type == XML_TEXT_NODE) && 1166 (attr->children->next == NULL)) { 1167 copy = xmlNewNsProp(target, copyNs, attr->name, 1168 attr->children->content); 1169 } else if (attr->children != NULL) { 1170 value = xmlNodeListGetString(attr->doc, attr->children, 1); 1171 copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value); 1172 xmlFree(value); 1173 } else { 1174 copy = xmlNewNsProp(target, copyNs, attr->name, NULL); 1175 } 1176 1177 if (copy == NULL) 1178 return(-1); 1179 1180 attr = attr->next; 1181 } 1182 return(0); 1183} 1184 1185/** 1186 * xsltShallowCopyElem: 1187 * @ctxt: the XSLT process context 1188 * @node: the element node in the source tree 1189 * or the Literal Result Element 1190 * @insert: the parent in the result tree 1191 * @isLRE: if @node is a Literal Result Element 1192 * 1193 * Make a copy of the element node @node 1194 * and insert it as last child of @insert. 1195 * 1196 * URGENT TODO: The problem with this one (for the non-refactored code) 1197 * is that it is used for both, Literal Result Elements *and* 1198 * copying input nodes. 1199 * 1200 * BIG NOTE: This is only called for XML_ELEMENT_NODEs. 1201 * 1202 * Called from: 1203 * xsltApplySequenceConstructor() 1204 * (for Literal Result Elements - which is a problem) 1205 * xsltCopy() (for shallow-copying elements via xsl:copy) 1206 * 1207 * Returns a pointer to the new node, or NULL in case of error 1208 */ 1209static xmlNodePtr 1210xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node, 1211 xmlNodePtr insert, int isLRE) 1212{ 1213 xmlNodePtr copy; 1214 1215 if ((node->type == XML_DTD_NODE) || (insert == NULL)) 1216 return(NULL); 1217 if ((node->type == XML_TEXT_NODE) || 1218 (node->type == XML_CDATA_SECTION_NODE)) 1219 return(xsltCopyText(ctxt, insert, node, 0)); 1220 1221 copy = xmlDocCopyNode(node, insert->doc, 0); 1222 if (copy != NULL) { 1223 copy->doc = ctxt->output; 1224 copy = xsltAddChild(insert, copy); 1225 1226 if (node->type == XML_ELEMENT_NODE) { 1227 /* 1228 * Add namespaces as they are needed 1229 */ 1230 if (node->nsDef != NULL) { 1231 /* 1232 * TODO: Remove the LRE case in the refactored code 1233 * gets enabled. 1234 */ 1235 if (isLRE) 1236 xsltCopyNamespaceList(ctxt, copy, node->nsDef); 1237 else 1238 xsltCopyNamespaceListInternal(copy, node->nsDef); 1239 } 1240 1241 /* 1242 * URGENT TODO: The problem with this is that it does not 1243 * copy over all namespace nodes in scope. 1244 * The damn thing about this is, that we would need to 1245 * use the xmlGetNsList(), for every single node; this is 1246 * also done in xsltCopyTreeInternal(), but only for the top node. 1247 */ 1248 if (node->ns != NULL) { 1249 if (isLRE) { 1250 /* 1251 * REVISIT TODO: Since the non-refactored code still does 1252 * ns-aliasing, we need to call xsltGetNamespace() here. 1253 * Remove this when ready. 1254 */ 1255 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); 1256 } else { 1257 copy->ns = xsltGetSpecialNamespace(ctxt, 1258 node, node->ns->href, node->ns->prefix, copy); 1259 1260 } 1261 } else if ((insert->type == XML_ELEMENT_NODE) && 1262 (insert->ns != NULL)) 1263 { 1264 /* 1265 * "Undeclare" the default namespace. 1266 */ 1267 xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy); 1268 } 1269 } 1270 } else { 1271 xsltTransformError(ctxt, NULL, node, 1272 "xsltShallowCopyElem: copy %s failed\n", node->name); 1273 } 1274 return(copy); 1275} 1276 1277/** 1278 * xsltCopyTreeList: 1279 * @ctxt: a XSLT process context 1280 * @invocNode: responsible node in the stylesheet; used for error reports 1281 * @list: the list of element nodes in the source tree. 1282 * @insert: the parent in the result tree. 1283 * @isLRE: is this a literal result element list 1284 * @topElemVisited: indicates if a top-most element was already processed 1285 * 1286 * Make a copy of the full list of tree @list 1287 * and insert it as last children of @insert 1288 * 1289 * NOTE: Not to be used for Literal Result Elements. 1290 * 1291 * Used by: 1292 * - xsltCopyOf() 1293 * 1294 * Returns a pointer to the new list, or NULL in case of error 1295 */ 1296static xmlNodePtr 1297xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, 1298 xmlNodePtr list, 1299 xmlNodePtr insert, int isLRE, int topElemVisited) 1300{ 1301 xmlNodePtr copy, ret = NULL; 1302 1303 while (list != NULL) { 1304 copy = xsltCopyTreeInternal(ctxt, invocNode, 1305 list, insert, isLRE, topElemVisited); 1306 if (copy != NULL) { 1307 if (ret == NULL) { 1308 ret = copy; 1309 } 1310 } 1311 list = list->next; 1312 } 1313 return(ret); 1314} 1315 1316/** 1317 * xsltCopyNamespaceListInternal: 1318 * @node: the target node 1319 * @cur: the first namespace 1320 * 1321 * Do a copy of a namespace list. If @node is non-NULL the 1322 * new namespaces are added automatically. 1323 * Called by: 1324 * xsltCopyTreeInternal() 1325 * 1326 * QUESTION: What is the exact difference between this function 1327 * and xsltCopyNamespaceList() in "namespaces.c"? 1328 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases. 1329 * 1330 * Returns: a new xmlNsPtr, or NULL in case of error. 1331 */ 1332static xmlNsPtr 1333xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) { 1334 xmlNsPtr ret = NULL; 1335 xmlNsPtr p = NULL, q, luNs; 1336 1337 if (ns == NULL) 1338 return(NULL); 1339 /* 1340 * One can add namespaces only on element nodes 1341 */ 1342 if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) 1343 elem = NULL; 1344 1345 do { 1346 if (ns->type != XML_NAMESPACE_DECL) 1347 break; 1348 /* 1349 * Avoid duplicating namespace declarations on the tree. 1350 */ 1351 if (elem != NULL) { 1352 if ((elem->ns != NULL) && 1353 xmlStrEqual(elem->ns->prefix, ns->prefix) && 1354 xmlStrEqual(elem->ns->href, ns->href)) 1355 { 1356 ns = ns->next; 1357 continue; 1358 } 1359 luNs = xmlSearchNs(elem->doc, elem, ns->prefix); 1360 if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href))) 1361 { 1362 ns = ns->next; 1363 continue; 1364 } 1365 } 1366 q = xmlNewNs(elem, ns->href, ns->prefix); 1367 if (p == NULL) { 1368 ret = p = q; 1369 } else if (q != NULL) { 1370 p->next = q; 1371 p = q; 1372 } 1373 ns = ns->next; 1374 } while (ns != NULL); 1375 return(ret); 1376} 1377 1378/** 1379 * xsltShallowCopyNsNode: 1380 * @ctxt: the XSLT transformation context 1381 * @invocNode: responsible node in the stylesheet; used for error reports 1382 * @insert: the target element node in the result tree 1383 * @ns: the namespace node 1384 * 1385 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy. 1386 * 1387 * Returns a new/existing ns-node, or NULL. 1388 */ 1389static xmlNsPtr 1390xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, 1391 xmlNodePtr invocNode, 1392 xmlNodePtr insert, 1393 xmlNsPtr ns) 1394{ 1395 /* 1396 * TODO: Contrary to header comments, this is declared as int. 1397 * be modified to return a node pointer, or NULL if any error 1398 */ 1399 xmlNsPtr tmpns; 1400 1401 if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) 1402 return(NULL); 1403 1404 if (insert->children != NULL) { 1405 xsltTransformError(ctxt, NULL, invocNode, 1406 "Namespace nodes must be added before " 1407 "any child nodes are added to an element.\n"); 1408 return(NULL); 1409 } 1410 /* 1411 * BIG NOTE: Xalan-J simply overwrites any ns-decls with 1412 * an equal prefix. We definitively won't do that. 1413 * 1414 * MSXML 4.0 and the .NET ignores ns-decls for which an 1415 * equal prefix is already in use. 1416 * 1417 * Saxon raises an error like: 1418 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace 1419 * nodes with the same name". 1420 * 1421 * NOTE: We'll currently follow MSXML here. 1422 * REVISIT TODO: Check if it's better to follow Saxon here. 1423 */ 1424 if (ns->prefix == NULL) { 1425 /* 1426 * If we are adding ns-nodes to an element using e.g. 1427 * <xsl:copy-of select="/foo/namespace::*">, then we need 1428 * to ensure that we don't incorrectly declare a default 1429 * namespace on an element in no namespace, which otherwise 1430 * would move the element incorrectly into a namespace, if 1431 * the node tree is serialized. 1432 */ 1433 if (insert->ns == NULL) 1434 goto occupied; 1435 } else if ((ns->prefix[0] == 'x') && 1436 xmlStrEqual(ns->prefix, BAD_CAST "xml")) 1437 { 1438 /* 1439 * The XML namespace is built in. 1440 */ 1441 return(NULL); 1442 } 1443 1444 if (insert->nsDef != NULL) { 1445 tmpns = insert->nsDef; 1446 do { 1447 if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) { 1448 if ((tmpns->prefix == ns->prefix) || 1449 xmlStrEqual(tmpns->prefix, ns->prefix)) 1450 { 1451 /* 1452 * Same prefix. 1453 */ 1454 if (xmlStrEqual(tmpns->href, ns->href)) 1455 return(NULL); 1456 goto occupied; 1457 } 1458 } 1459 tmpns = tmpns->next; 1460 } while (tmpns != NULL); 1461 } 1462 tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); 1463 if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) 1464 return(NULL); 1465 /* 1466 * Declare a new namespace. 1467 * TODO: The problem (wrt efficiency) with this xmlNewNs() is 1468 * that it will again search the already declared namespaces 1469 * for a duplicate :-/ 1470 */ 1471 return(xmlNewNs(insert, ns->href, ns->prefix)); 1472 1473occupied: 1474 /* 1475 * TODO: We could as well raise an error here (like Saxon does), 1476 * or at least generate a warning. 1477 */ 1478 return(NULL); 1479} 1480 1481/** 1482 * xsltCopyTreeInternal: 1483 * @ctxt: the XSLT transformation context 1484 * @invocNode: responsible node in the stylesheet; used for error reports 1485 * @node: the element node in the source tree 1486 * @insert: the parent in the result tree 1487 * @isLRE: indicates if @node is a Literal Result Element 1488 * @topElemVisited: indicates if a top-most element was already processed 1489 * 1490 * Make a copy of the full tree under the element node @node 1491 * and insert it as last child of @insert 1492 * 1493 * NOTE: Not to be used for Literal Result Elements. 1494 * 1495 * Used by: 1496 * - xsltCopyOf() 1497 * 1498 * Returns a pointer to the new tree, or NULL in case of error 1499 */ 1500static xmlNodePtr 1501xsltCopyTreeInternal(xsltTransformContextPtr ctxt, 1502 xmlNodePtr invocNode, 1503 xmlNodePtr node, 1504 xmlNodePtr insert, int isLRE, int topElemVisited) 1505{ 1506 xmlNodePtr copy; 1507 1508 if (node == NULL) 1509 return(NULL); 1510 switch (node->type) { 1511 case XML_ELEMENT_NODE: 1512 case XML_ENTITY_REF_NODE: 1513 case XML_ENTITY_NODE: 1514 case XML_PI_NODE: 1515 case XML_COMMENT_NODE: 1516 case XML_DOCUMENT_NODE: 1517 case XML_HTML_DOCUMENT_NODE: 1518#ifdef LIBXML_DOCB_ENABLED 1519 case XML_DOCB_DOCUMENT_NODE: 1520#endif 1521 break; 1522 case XML_TEXT_NODE: { 1523 int noenc = (node->name == xmlStringTextNoenc); 1524 return(xsltCopyTextString(ctxt, insert, node->content, noenc)); 1525 } 1526 case XML_CDATA_SECTION_NODE: 1527 return(xsltCopyTextString(ctxt, insert, node->content, 0)); 1528 case XML_ATTRIBUTE_NODE: 1529 return((xmlNodePtr) 1530 xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node)); 1531 case XML_NAMESPACE_DECL: 1532 return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode, 1533 insert, (xmlNsPtr) node)); 1534 1535 case XML_DOCUMENT_TYPE_NODE: 1536 case XML_DOCUMENT_FRAG_NODE: 1537 case XML_NOTATION_NODE: 1538 case XML_DTD_NODE: 1539 case XML_ELEMENT_DECL: 1540 case XML_ATTRIBUTE_DECL: 1541 case XML_ENTITY_DECL: 1542 case XML_XINCLUDE_START: 1543 case XML_XINCLUDE_END: 1544 return(NULL); 1545 } 1546 if (XSLT_IS_RES_TREE_FRAG(node)) { 1547 if (node->children != NULL) 1548 copy = xsltCopyTreeList(ctxt, invocNode, 1549 node->children, insert, 0, 0); 1550 else 1551 copy = NULL; 1552 return(copy); 1553 } 1554 copy = xmlDocCopyNode(node, insert->doc, 0); 1555 if (copy != NULL) { 1556 copy->doc = ctxt->output; 1557 copy = xsltAddChild(insert, copy); 1558 /* 1559 * The node may have been coalesced into another text node. 1560 */ 1561 if (insert->last != copy) 1562 return(insert->last); 1563 copy->next = NULL; 1564 1565 if (node->type == XML_ELEMENT_NODE) { 1566 /* 1567 * Copy in-scope namespace nodes. 1568 * 1569 * REVISIT: Since we try to reuse existing in-scope ns-decls by 1570 * using xmlSearchNsByHref(), this will eventually change 1571 * the prefix of an original ns-binding; thus it might 1572 * break QNames in element/attribute content. 1573 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation 1574 * context, plus a ns-lookup function, which writes directly 1575 * to a given list, then we wouldn't need to create/free the 1576 * nsList every time. 1577 */ 1578 if ((topElemVisited == 0) && 1579 (node->parent != NULL) && 1580 (node->parent->type != XML_DOCUMENT_NODE) && 1581 (node->parent->type != XML_HTML_DOCUMENT_NODE)) 1582 { 1583 xmlNsPtr *nsList, *curns, ns; 1584 1585 /* 1586 * If this is a top-most element in a tree to be 1587 * copied, then we need to ensure that all in-scope 1588 * namespaces are copied over. For nodes deeper in the 1589 * tree, it is sufficient to reconcile only the ns-decls 1590 * (node->nsDef entries). 1591 */ 1592 1593 nsList = xmlGetNsList(node->doc, node); 1594 if (nsList != NULL) { 1595 curns = nsList; 1596 do { 1597 /* 1598 * Search by prefix first in order to break as less 1599 * QNames in element/attribute content as possible. 1600 */ 1601 ns = xmlSearchNs(insert->doc, insert, 1602 (*curns)->prefix); 1603 1604 if ((ns == NULL) || 1605 (! xmlStrEqual(ns->href, (*curns)->href))) 1606 { 1607 ns = NULL; 1608 /* 1609 * Search by namespace name. 1610 * REVISIT TODO: Currently disabled. 1611 */ 1612#if 0 1613 ns = xmlSearchNsByHref(insert->doc, 1614 insert, (*curns)->href); 1615#endif 1616 } 1617 if (ns == NULL) { 1618 /* 1619 * Declare a new namespace on the copied element. 1620 */ 1621 ns = xmlNewNs(copy, (*curns)->href, 1622 (*curns)->prefix); 1623 /* TODO: Handle errors */ 1624 } 1625 if (node->ns == *curns) { 1626 /* 1627 * If this was the original's namespace then set 1628 * the generated counterpart on the copy. 1629 */ 1630 copy->ns = ns; 1631 } 1632 curns++; 1633 } while (*curns != NULL); 1634 xmlFree(nsList); 1635 } 1636 } else if (node->nsDef != NULL) { 1637 /* 1638 * Copy over all namespace declaration attributes. 1639 */ 1640 if (node->nsDef != NULL) { 1641 if (isLRE) 1642 xsltCopyNamespaceList(ctxt, copy, node->nsDef); 1643 else 1644 xsltCopyNamespaceListInternal(copy, node->nsDef); 1645 } 1646 } 1647 /* 1648 * Set the namespace. 1649 */ 1650 if (node->ns != NULL) { 1651 if (copy->ns == NULL) { 1652 /* 1653 * This will map copy->ns to one of the newly created 1654 * in-scope ns-decls, OR create a new ns-decl on @copy. 1655 */ 1656 copy->ns = xsltGetSpecialNamespace(ctxt, invocNode, 1657 node->ns->href, node->ns->prefix, copy); 1658 } 1659 } else if ((insert->type == XML_ELEMENT_NODE) && 1660 (insert->ns != NULL)) 1661 { 1662 /* 1663 * "Undeclare" the default namespace on @copy with xmlns="". 1664 */ 1665 xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy); 1666 } 1667 /* 1668 * Copy attribute nodes. 1669 */ 1670 if (node->properties != NULL) { 1671 xsltCopyAttrListNoOverwrite(ctxt, invocNode, 1672 copy, node->properties); 1673 } 1674 if (topElemVisited == 0) 1675 topElemVisited = 1; 1676 } 1677 /* 1678 * Copy the subtree. 1679 */ 1680 if (node->children != NULL) { 1681 xsltCopyTreeList(ctxt, invocNode, 1682 node->children, copy, isLRE, topElemVisited); 1683 } 1684 } else { 1685 xsltTransformError(ctxt, NULL, invocNode, 1686 "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); 1687 } 1688 return(copy); 1689} 1690 1691/** 1692 * xsltCopyTree: 1693 * @ctxt: the XSLT transformation context 1694 * @node: the element node in the source tree 1695 * @insert: the parent in the result tree 1696 * @literal: indicates if @node is a Literal Result Element 1697 * 1698 * Make a copy of the full tree under the element node @node 1699 * and insert it as last child of @insert 1700 * For literal result element, some of the namespaces may not be copied 1701 * over according to section 7.1. 1702 * TODO: Why is this a public function? 1703 * 1704 * Returns a pointer to the new tree, or NULL in case of error 1705 */ 1706xmlNodePtr 1707xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, 1708 xmlNodePtr insert, int literal) 1709{ 1710 return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0)); 1711 1712} 1713 1714/************************************************************************ 1715 * * 1716 * Error/fallback processing * 1717 * * 1718 ************************************************************************/ 1719 1720/** 1721 * xsltApplyFallbacks: 1722 * @ctxt: a XSLT process context 1723 * @node: the node in the source tree. 1724 * @inst: the node generating the error 1725 * 1726 * Process possible xsl:fallback nodes present under @inst 1727 * 1728 * Returns the number of xsl:fallback element found and processed 1729 */ 1730static int 1731xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node, 1732 xmlNodePtr inst) { 1733 1734 xmlNodePtr child; 1735 int ret = 0; 1736 1737 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || 1738 (inst->children == NULL)) 1739 return(0); 1740 1741 child = inst->children; 1742 while (child != NULL) { 1743 if ((IS_XSLT_ELEM(child)) && 1744 (xmlStrEqual(child->name, BAD_CAST "fallback"))) { 1745#ifdef WITH_XSLT_DEBUG_PARSING 1746 xsltGenericDebug(xsltGenericDebugContext, 1747 "applying xsl:fallback\n"); 1748#endif 1749 ret++; 1750 xsltApplySequenceConstructor(ctxt, node, child->children, 1751 NULL); 1752 } 1753 child = child->next; 1754 } 1755 return(ret); 1756} 1757 1758/************************************************************************ 1759 * * 1760 * Default processing * 1761 * * 1762 ************************************************************************/ 1763 1764/** 1765 * xsltDefaultProcessOneNode: 1766 * @ctxt: a XSLT process context 1767 * @node: the node in the source tree. 1768 * @params: extra parameters passed to the template if any 1769 * 1770 * Process the source node with the default built-in template rule: 1771 * <xsl:template match="*|/"> 1772 * <xsl:apply-templates/> 1773 * </xsl:template> 1774 * 1775 * and 1776 * 1777 * <xsl:template match="text()|@*"> 1778 * <xsl:value-of select="."/> 1779 * </xsl:template> 1780 * 1781 * Note also that namespace declarations are copied directly: 1782 * 1783 * the built-in template rule is the only template rule that is applied 1784 * for namespace nodes. 1785 */ 1786static void 1787xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, 1788 xsltStackElemPtr params) { 1789 xmlNodePtr copy; 1790 xmlNodePtr delete = NULL, cur; 1791 int nbchild = 0, oldSize; 1792 int childno = 0, oldPos; 1793 xsltTemplatePtr template; 1794 1795 CHECK_STOPPED; 1796 /* 1797 * Handling of leaves 1798 */ 1799 switch (node->type) { 1800 case XML_DOCUMENT_NODE: 1801 case XML_HTML_DOCUMENT_NODE: 1802 case XML_ELEMENT_NODE: 1803 break; 1804 case XML_CDATA_SECTION_NODE: 1805#ifdef WITH_XSLT_DEBUG_PROCESS 1806 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1807 "xsltDefaultProcessOneNode: copy CDATA %s\n", 1808 node->content)); 1809#endif 1810 copy = xsltCopyText(ctxt, ctxt->insert, node, 0); 1811 if (copy == NULL) { 1812 xsltTransformError(ctxt, NULL, node, 1813 "xsltDefaultProcessOneNode: cdata copy failed\n"); 1814 } 1815 return; 1816 case XML_TEXT_NODE: 1817#ifdef WITH_XSLT_DEBUG_PROCESS 1818 if (node->content == NULL) { 1819 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1820 "xsltDefaultProcessOneNode: copy empty text\n")); 1821 return; 1822 } else { 1823 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1824 "xsltDefaultProcessOneNode: copy text %s\n", 1825 node->content)); 1826 } 1827#endif 1828 copy = xsltCopyText(ctxt, ctxt->insert, node, 0); 1829 if (copy == NULL) { 1830 xsltTransformError(ctxt, NULL, node, 1831 "xsltDefaultProcessOneNode: text copy failed\n"); 1832 } 1833 return; 1834 case XML_ATTRIBUTE_NODE: 1835 cur = node->children; 1836 while ((cur != NULL) && (cur->type != XML_TEXT_NODE)) 1837 cur = cur->next; 1838 if (cur == NULL) { 1839 xsltTransformError(ctxt, NULL, node, 1840 "xsltDefaultProcessOneNode: no text for attribute\n"); 1841 } else { 1842#ifdef WITH_XSLT_DEBUG_PROCESS 1843 if (cur->content == NULL) { 1844 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1845 "xsltDefaultProcessOneNode: copy empty text\n")); 1846 } else { 1847 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1848 "xsltDefaultProcessOneNode: copy text %s\n", 1849 cur->content)); 1850 } 1851#endif 1852 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); 1853 if (copy == NULL) { 1854 xsltTransformError(ctxt, NULL, node, 1855 "xsltDefaultProcessOneNode: text copy failed\n"); 1856 } 1857 } 1858 return; 1859 default: 1860 return; 1861 } 1862 /* 1863 * Handling of Elements: first pass, cleanup and counting 1864 */ 1865 cur = node->children; 1866 while (cur != NULL) { 1867 switch (cur->type) { 1868 case XML_TEXT_NODE: 1869 case XML_CDATA_SECTION_NODE: 1870 case XML_DOCUMENT_NODE: 1871 case XML_HTML_DOCUMENT_NODE: 1872 case XML_ELEMENT_NODE: 1873 case XML_PI_NODE: 1874 case XML_COMMENT_NODE: 1875 nbchild++; 1876 break; 1877 case XML_DTD_NODE: 1878 /* Unlink the DTD, it's still reachable using doc->intSubset */ 1879 if (cur->next != NULL) 1880 cur->next->prev = cur->prev; 1881 if (cur->prev != NULL) 1882 cur->prev->next = cur->next; 1883 break; 1884 default: 1885#ifdef WITH_XSLT_DEBUG_PROCESS 1886 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1887 "xsltDefaultProcessOneNode: skipping node type %d\n", 1888 cur->type)); 1889#endif 1890 delete = cur; 1891 } 1892 cur = cur->next; 1893 if (delete != NULL) { 1894#ifdef WITH_XSLT_DEBUG_PROCESS 1895 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1896 "xsltDefaultProcessOneNode: removing ignorable blank node\n")); 1897#endif 1898 xmlUnlinkNode(delete); 1899 xmlFreeNode(delete); 1900 delete = NULL; 1901 } 1902 } 1903 if (delete != NULL) { 1904#ifdef WITH_XSLT_DEBUG_PROCESS 1905 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1906 "xsltDefaultProcessOneNode: removing ignorable blank node\n")); 1907#endif 1908 xmlUnlinkNode(delete); 1909 xmlFreeNode(delete); 1910 delete = NULL; 1911 } 1912 1913 /* 1914 * Handling of Elements: second pass, actual processing 1915 */ 1916 oldSize = ctxt->xpathCtxt->contextSize; 1917 oldPos = ctxt->xpathCtxt->proximityPosition; 1918 cur = node->children; 1919 while (cur != NULL) { 1920 childno++; 1921 switch (cur->type) { 1922 case XML_DOCUMENT_NODE: 1923 case XML_HTML_DOCUMENT_NODE: 1924 case XML_ELEMENT_NODE: 1925 ctxt->xpathCtxt->contextSize = nbchild; 1926 ctxt->xpathCtxt->proximityPosition = childno; 1927 xsltProcessOneNode(ctxt, cur, params); 1928 break; 1929 case XML_CDATA_SECTION_NODE: 1930 template = xsltGetTemplate(ctxt, cur, NULL); 1931 if (template) { 1932#ifdef WITH_XSLT_DEBUG_PROCESS 1933 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1934 "xsltDefaultProcessOneNode: applying template for CDATA %s\n", 1935 cur->content)); 1936#endif 1937 /* 1938 * Instantiate the xsl:template. 1939 */ 1940 xsltApplyXSLTTemplate(ctxt, cur, template->content, 1941 template, params); 1942 } else /* if (ctxt->mode == NULL) */ { 1943#ifdef WITH_XSLT_DEBUG_PROCESS 1944 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1945 "xsltDefaultProcessOneNode: copy CDATA %s\n", 1946 cur->content)); 1947#endif 1948 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); 1949 if (copy == NULL) { 1950 xsltTransformError(ctxt, NULL, cur, 1951 "xsltDefaultProcessOneNode: cdata copy failed\n"); 1952 } 1953 } 1954 break; 1955 case XML_TEXT_NODE: 1956 template = xsltGetTemplate(ctxt, cur, NULL); 1957 if (template) { 1958#ifdef WITH_XSLT_DEBUG_PROCESS 1959 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1960 "xsltDefaultProcessOneNode: applying template for text %s\n", 1961 cur->content)); 1962#endif 1963 ctxt->xpathCtxt->contextSize = nbchild; 1964 ctxt->xpathCtxt->proximityPosition = childno; 1965 /* 1966 * Instantiate the xsl:template. 1967 */ 1968 xsltApplyXSLTTemplate(ctxt, cur, template->content, 1969 template, params); 1970 } else /* if (ctxt->mode == NULL) */ { 1971#ifdef WITH_XSLT_DEBUG_PROCESS 1972 if (cur->content == NULL) { 1973 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1974 "xsltDefaultProcessOneNode: copy empty text\n")); 1975 } else { 1976 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1977 "xsltDefaultProcessOneNode: copy text %s\n", 1978 cur->content)); 1979 } 1980#endif 1981 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); 1982 if (copy == NULL) { 1983 xsltTransformError(ctxt, NULL, cur, 1984 "xsltDefaultProcessOneNode: text copy failed\n"); 1985 } 1986 } 1987 break; 1988 case XML_PI_NODE: 1989 case XML_COMMENT_NODE: 1990 template = xsltGetTemplate(ctxt, cur, NULL); 1991 if (template) { 1992#ifdef WITH_XSLT_DEBUG_PROCESS 1993 if (cur->type == XML_PI_NODE) { 1994 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1995 "xsltDefaultProcessOneNode: template found for PI %s\n", 1996 cur->name)); 1997 } else if (cur->type == XML_COMMENT_NODE) { 1998 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 1999 "xsltDefaultProcessOneNode: template found for comment\n")); 2000 } 2001#endif 2002 ctxt->xpathCtxt->contextSize = nbchild; 2003 ctxt->xpathCtxt->proximityPosition = childno; 2004 /* 2005 * Instantiate the xsl:template. 2006 */ 2007 xsltApplyXSLTTemplate(ctxt, cur, template->content, 2008 template, params); 2009 } 2010 break; 2011 default: 2012 break; 2013 } 2014 cur = cur->next; 2015 } 2016 ctxt->xpathCtxt->contextSize = oldSize; 2017 ctxt->xpathCtxt->proximityPosition = oldPos; 2018} 2019 2020/** 2021 * xsltProcessOneNode: 2022 * @ctxt: a XSLT process context 2023 * @contextNode: the "current node" in the source tree 2024 * @withParams: extra parameters (e.g. xsl:with-param) passed to the 2025 * template if any 2026 * 2027 * Process the source node. 2028 */ 2029void 2030xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 2031 xsltStackElemPtr withParams) 2032{ 2033 xsltTemplatePtr templ; 2034 xmlNodePtr oldNode; 2035 2036 templ = xsltGetTemplate(ctxt, contextNode, NULL); 2037 /* 2038 * If no template is found, apply the default rule. 2039 */ 2040 if (templ == NULL) { 2041#ifdef WITH_XSLT_DEBUG_PROCESS 2042 if (contextNode->type == XML_DOCUMENT_NODE) { 2043 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2044 "xsltProcessOneNode: no template found for /\n")); 2045 } else if (contextNode->type == XML_CDATA_SECTION_NODE) { 2046 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2047 "xsltProcessOneNode: no template found for CDATA\n")); 2048 } else if (contextNode->type == XML_ATTRIBUTE_NODE) { 2049 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2050 "xsltProcessOneNode: no template found for attribute %s\n", 2051 ((xmlAttrPtr) contextNode)->name)); 2052 } else { 2053 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2054 "xsltProcessOneNode: no template found for %s\n", contextNode->name)); 2055 } 2056#endif 2057 oldNode = ctxt->node; 2058 ctxt->node = contextNode; 2059 xsltDefaultProcessOneNode(ctxt, contextNode, withParams); 2060 ctxt->node = oldNode; 2061 return; 2062 } 2063 2064 if (contextNode->type == XML_ATTRIBUTE_NODE) { 2065 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; 2066 /* 2067 * Set the "current template rule". 2068 */ 2069 ctxt->currentTemplateRule = templ; 2070 2071#ifdef WITH_XSLT_DEBUG_PROCESS 2072 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2073 "xsltProcessOneNode: applying template '%s' for attribute %s\n", 2074 templ->match, contextNode->name)); 2075#endif 2076 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); 2077 2078 ctxt->currentTemplateRule = oldCurTempRule; 2079 } else { 2080 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; 2081 /* 2082 * Set the "current template rule". 2083 */ 2084 ctxt->currentTemplateRule = templ; 2085 2086#ifdef WITH_XSLT_DEBUG_PROCESS 2087 if (contextNode->type == XML_DOCUMENT_NODE) { 2088 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2089 "xsltProcessOneNode: applying template '%s' for /\n", 2090 templ->match)); 2091 } else { 2092 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, 2093 "xsltProcessOneNode: applying template '%s' for %s\n", 2094 templ->match, contextNode->name)); 2095 } 2096#endif 2097 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); 2098 2099 ctxt->currentTemplateRule = oldCurTempRule; 2100 } 2101} 2102 2103static xmlNodePtr 2104xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt, 2105 xmlNodePtr contextNode, 2106 xmlNodePtr list, 2107 xsltTemplatePtr templ, 2108 int *addCallResult) 2109{ 2110 xmlNodePtr debugedNode = NULL; 2111 2112 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { 2113 if (templ) { 2114 *addCallResult = xslAddCall(templ, templ->elem); 2115 } else { 2116 *addCallResult = xslAddCall(NULL, list); 2117 } 2118 switch (ctxt->debugStatus) { 2119 case XSLT_DEBUG_RUN_RESTART: 2120 case XSLT_DEBUG_QUIT: 2121 if (*addCallResult) 2122 xslDropCall(); 2123 return(NULL); 2124 } 2125 if (templ) { 2126 xslHandleDebugger(templ->elem, contextNode, templ, ctxt); 2127 debugedNode = templ->elem; 2128 } else if (list) { 2129 xslHandleDebugger(list, contextNode, templ, ctxt); 2130 debugedNode = list; 2131 } else if (ctxt->inst) { 2132 xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt); 2133 debugedNode = ctxt->inst; 2134 } 2135 } 2136 return(debugedNode); 2137} 2138 2139/** 2140 * xsltLocalVariablePush: 2141 * @ctxt: the transformation context 2142 * @variable: variable to be pushed to the variable stack 2143 * @level: new value for variable's level 2144 * 2145 * Places the variable onto the local variable stack 2146 * 2147 * Returns: 0 for success, -1 for any error 2148 * **NOTE:** 2149 * This is an internal routine and should not be called by users! 2150 */ 2151int 2152xsltLocalVariablePush(xsltTransformContextPtr ctxt, 2153 xsltStackElemPtr variable, 2154 int level) 2155{ 2156 if (ctxt->varsMax == 0) { 2157 ctxt->varsMax = 10; 2158 ctxt->varsTab = 2159 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * 2160 sizeof(ctxt->varsTab[0])); 2161 if (ctxt->varsTab == NULL) { 2162 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); 2163 return (-1); 2164 } 2165 } 2166 if (ctxt->varsNr >= ctxt->varsMax) { 2167 ctxt->varsMax *= 2; 2168 ctxt->varsTab = 2169 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, 2170 ctxt->varsMax * 2171 sizeof(ctxt->varsTab[0])); 2172 if (ctxt->varsTab == NULL) { 2173 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); 2174 return (-1); 2175 } 2176 } 2177 ctxt->varsTab[ctxt->varsNr++] = variable; 2178 ctxt->vars = variable; 2179 variable->level = level; 2180 return(0); 2181} 2182 2183/** 2184 * xsltReleaseLocalRVTs: 2185 * 2186 * Fragments which are results of extension instructions 2187 * are preserved; all other fragments are freed/cached. 2188 */ 2189static void 2190xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base) 2191{ 2192 xmlDocPtr cur = ctxt->localRVT, tmp; 2193 2194 while ((cur != NULL) && (cur != base)) { 2195 if (cur->psvi == (void *) ((long) 1)) { 2196 cur = (xmlDocPtr) cur->next; 2197 } else { 2198 tmp = cur; 2199 cur = (xmlDocPtr) cur->next; 2200 2201 if (tmp == ctxt->localRVT) 2202 ctxt->localRVT = cur; 2203 2204 /* 2205 * We need ctxt->localRVTBase for extension instructions 2206 * which return values (like EXSLT's function). 2207 */ 2208 if (tmp == ctxt->localRVTBase) 2209 ctxt->localRVTBase = cur; 2210 2211 if (tmp->prev) 2212 tmp->prev->next = (xmlNodePtr) cur; 2213 if (cur) 2214 cur->prev = tmp->prev; 2215 xsltReleaseRVT(ctxt, tmp); 2216 } 2217 } 2218} 2219 2220/** 2221 * xsltApplySequenceConstructor: 2222 * @ctxt: a XSLT process context 2223 * @contextNode: the "current node" in the source tree 2224 * @list: the nodes of a sequence constructor; 2225 * (plus leading xsl:param elements) 2226 * @templ: the compiled xsl:template (optional) 2227 * 2228 * Processes a sequence constructor. 2229 * 2230 * NOTE: ctxt->currentTemplateRule was introduced to reflect the 2231 * semantics of "current template rule". I.e. the field ctxt->templ 2232 * is not intended to reflect this, thus always pushed onto the 2233 * template stack. 2234 */ 2235static void 2236xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, 2237 xmlNodePtr contextNode, xmlNodePtr list, 2238 xsltTemplatePtr templ) 2239{ 2240 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode; 2241 xmlNodePtr cur, insert, copy = NULL; 2242 int level = 0, oldVarsNr; 2243 xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase; 2244 2245#ifdef XSLT_REFACTORED 2246 xsltStylePreCompPtr info; 2247#endif 2248 2249#ifdef WITH_DEBUGGER 2250 int addCallResult = 0; 2251 xmlNodePtr debuggedNode = NULL; 2252#endif 2253 2254 if (ctxt == NULL) 2255 return; 2256 2257#ifdef WITH_DEBUGGER 2258 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { 2259 debuggedNode = 2260 xsltDebuggerStartSequenceConstructor(ctxt, contextNode, 2261 list, templ, &addCallResult); 2262 if (debuggedNode == NULL) 2263 return; 2264 } 2265#endif 2266 2267 if (list == NULL) 2268 return; 2269 CHECK_STOPPED; 2270 2271 oldLocalFragmentTop = ctxt->localRVT; 2272 oldInsert = insert = ctxt->insert; 2273 oldInst = oldCurInst = ctxt->inst; 2274 oldContextNode = ctxt->node; 2275 /* 2276 * Save current number of variables on the stack; new vars are popped when 2277 * exiting. 2278 */ 2279 oldVarsNr = ctxt->varsNr; 2280 /* 2281 * Process the sequence constructor. 2282 */ 2283 cur = list; 2284 while (cur != NULL) { 2285 ctxt->inst = cur; 2286 2287#ifdef WITH_DEBUGGER 2288 switch (ctxt->debugStatus) { 2289 case XSLT_DEBUG_RUN_RESTART: 2290 case XSLT_DEBUG_QUIT: 2291 break; 2292 2293 } 2294#endif 2295 /* 2296 * Test; we must have a valid insertion point. 2297 */ 2298 if (insert == NULL) { 2299 2300#ifdef WITH_XSLT_DEBUG_PROCESS 2301 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2302 "xsltApplySequenceConstructor: insert == NULL !\n")); 2303#endif 2304 goto error; 2305 } 2306 2307#ifdef WITH_DEBUGGER 2308 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur)) 2309 xslHandleDebugger(cur, contextNode, templ, ctxt); 2310#endif 2311 2312#ifdef XSLT_REFACTORED 2313 if (cur->type == XML_ELEMENT_NODE) { 2314 info = (xsltStylePreCompPtr) cur->psvi; 2315 /* 2316 * We expect a compiled representation on: 2317 * 1) XSLT instructions of this XSLT version (1.0) 2318 * (with a few exceptions) 2319 * 2) Literal result elements 2320 * 3) Extension instructions 2321 * 4) XSLT instructions of future XSLT versions 2322 * (forwards-compatible mode). 2323 */ 2324 if (info == NULL) { 2325 /* 2326 * Handle the rare cases where we don't expect a compiled 2327 * representation on an XSLT element. 2328 */ 2329 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { 2330 xsltMessage(ctxt, contextNode, cur); 2331 goto skip_children; 2332 } 2333 /* 2334 * Something really went wrong: 2335 */ 2336 xsltTransformError(ctxt, NULL, cur, 2337 "Internal error in xsltApplySequenceConstructor(): " 2338 "The element '%s' in the stylesheet has no compiled " 2339 "representation.\n", 2340 cur->name); 2341 goto skip_children; 2342 } 2343 2344 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { 2345 xsltStyleItemLRElementInfoPtr lrInfo = 2346 (xsltStyleItemLRElementInfoPtr) info; 2347 /* 2348 * Literal result elements 2349 * -------------------------------------------------------- 2350 */ 2351#ifdef WITH_XSLT_DEBUG_PROCESS 2352 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2353 xsltGenericDebug(xsltGenericDebugContext, 2354 "xsltApplySequenceConstructor: copy literal result " 2355 "element '%s'\n", cur->name)); 2356#endif 2357 /* 2358 * Copy the raw element-node. 2359 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) 2360 * == NULL) 2361 * goto error; 2362 */ 2363 copy = xmlDocCopyNode(cur, insert->doc, 0); 2364 if (copy == NULL) { 2365 xsltTransformError(ctxt, NULL, cur, 2366 "Internal error in xsltApplySequenceConstructor(): " 2367 "Failed to copy literal result element '%s'.\n", 2368 cur->name); 2369 goto error; 2370 } else { 2371 /* 2372 * Add the element-node to the result tree. 2373 */ 2374 copy->doc = ctxt->output; 2375 copy = xsltAddChild(insert, copy); 2376 /* 2377 * Create effective namespaces declarations. 2378 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); 2379 */ 2380 if (lrInfo->effectiveNs != NULL) { 2381 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; 2382 xmlNsPtr ns, lastns = NULL; 2383 2384 while (effNs != NULL) { 2385 /* 2386 * Avoid generating redundant namespace 2387 * declarations; thus lookup if there is already 2388 * such a ns-decl in the result. 2389 */ 2390 ns = xmlSearchNs(copy->doc, copy, effNs->prefix); 2391 if ((ns != NULL) && 2392 (xmlStrEqual(ns->href, effNs->nsName))) 2393 { 2394 effNs = effNs->next; 2395 continue; 2396 } 2397 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix); 2398 if (ns == NULL) { 2399 xsltTransformError(ctxt, NULL, cur, 2400 "Internal error in " 2401 "xsltApplySequenceConstructor(): " 2402 "Failed to copy a namespace " 2403 "declaration.\n"); 2404 goto error; 2405 } 2406 2407 if (lastns == NULL) 2408 copy->nsDef = ns; 2409 else 2410 lastns->next =ns; 2411 lastns = ns; 2412 2413 effNs = effNs->next; 2414 } 2415 2416 } 2417 /* 2418 * NOTE that we don't need to apply ns-alising: this was 2419 * already done at compile-time. 2420 */ 2421 if (cur->ns != NULL) { 2422 /* 2423 * If there's no such ns-decl in the result tree, 2424 * then xsltGetSpecialNamespace() will 2425 * create a ns-decl on the copied node. 2426 */ 2427 copy->ns = xsltGetSpecialNamespace(ctxt, cur, 2428 cur->ns->href, cur->ns->prefix, copy); 2429 } else { 2430 /* 2431 * Undeclare the default namespace if needed. 2432 * This can be skipped, if the result element has 2433 * no ns-decls, in which case the result element 2434 * obviously does not declare a default namespace; 2435 * AND there's either no parent, or the parent 2436 * element is in no namespace; this means there's no 2437 * default namespace is scope to care about. 2438 * 2439 * REVISIT: This might result in massive 2440 * generation of ns-decls if nodes in a default 2441 * namespaces are mixed with nodes in no namespace. 2442 * 2443 */ 2444 if (copy->nsDef || 2445 ((insert != NULL) && 2446 (insert->type == XML_ELEMENT_NODE) && 2447 (insert->ns != NULL))) 2448 { 2449 xsltGetSpecialNamespace(ctxt, cur, 2450 NULL, NULL, copy); 2451 } 2452 } 2453 } 2454 /* 2455 * SPEC XSLT 2.0 "Each attribute of the literal result 2456 * element, other than an attribute in the XSLT namespace, 2457 * is processed to produce an attribute for the element in 2458 * the result tree." 2459 * NOTE: See bug #341325. 2460 */ 2461 if (cur->properties != NULL) { 2462 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); 2463 } 2464 } else if (IS_XSLT_ELEM_FAST(cur)) { 2465 /* 2466 * XSLT instructions 2467 * -------------------------------------------------------- 2468 */ 2469 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { 2470 /* 2471 * We hit an unknown XSLT element. 2472 * Try to apply one of the fallback cases. 2473 */ 2474 ctxt->insert = insert; 2475 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2476 xsltTransformError(ctxt, NULL, cur, 2477 "The is no fallback behaviour defined for " 2478 "the unknown XSLT element '%s'.\n", 2479 cur->name); 2480 } 2481 ctxt->insert = oldInsert; 2482 } else if (info->func != NULL) { 2483 /* 2484 * Execute the XSLT instruction. 2485 */ 2486 ctxt->insert = insert; 2487 2488 info->func(ctxt, contextNode, cur, 2489 (xsltElemPreCompPtr) info); 2490 2491 /* 2492 * Cleanup temporary tree fragments. 2493 */ 2494 if (oldLocalFragmentTop != ctxt->localRVT) 2495 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2496 2497 ctxt->insert = oldInsert; 2498 } else if (info->type == XSLT_FUNC_VARIABLE) { 2499 xsltStackElemPtr tmpvar = ctxt->vars; 2500 2501 xsltParseStylesheetVariable(ctxt, cur); 2502 2503 if (tmpvar != ctxt->vars) { 2504 /* 2505 * TODO: Using a @tmpvar is an annoying workaround, but 2506 * the current mechanisms do not provide any other way 2507 * of knowing if the var was really pushed onto the 2508 * stack. 2509 */ 2510 ctxt->vars->level = level; 2511 } 2512 } else if (info->type == XSLT_FUNC_MESSAGE) { 2513 /* 2514 * TODO: Won't be hit, since we don't compile xsl:message. 2515 */ 2516 xsltMessage(ctxt, contextNode, cur); 2517 } else { 2518 xsltTransformError(ctxt, NULL, cur, 2519 "Unexpected XSLT element '%s'.\n", cur->name); 2520 } 2521 goto skip_children; 2522 2523 } else { 2524 xsltTransformFunction func; 2525 /* 2526 * Extension intructions (elements) 2527 * -------------------------------------------------------- 2528 */ 2529 if (cur->psvi == xsltExtMarker) { 2530 /* 2531 * The xsltExtMarker was set during the compilation 2532 * of extension instructions if there was no registered 2533 * handler for this specific extension function at 2534 * compile-time. 2535 * Libxslt will now lookup if a handler is 2536 * registered in the context of this transformation. 2537 */ 2538 func = (xsltTransformFunction) 2539 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); 2540 } else 2541 func = ((xsltElemPreCompPtr) cur->psvi)->func; 2542 2543 if (func == NULL) { 2544 /* 2545 * No handler available. 2546 * Try to execute fallback behaviour via xsl:fallback. 2547 */ 2548#ifdef WITH_XSLT_DEBUG_PROCESS 2549 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2550 xsltGenericDebug(xsltGenericDebugContext, 2551 "xsltApplySequenceConstructor: unknown extension %s\n", 2552 cur->name)); 2553#endif 2554 ctxt->insert = insert; 2555 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2556 xsltTransformError(ctxt, NULL, cur, 2557 "Unknown extension instruction '{%s}%s'.\n", 2558 cur->ns->href, cur->name); 2559 } 2560 ctxt->insert = oldInsert; 2561 } else { 2562 /* 2563 * Execute the handler-callback. 2564 */ 2565#ifdef WITH_XSLT_DEBUG_PROCESS 2566 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2567 "xsltApplySequenceConstructor: extension construct %s\n", 2568 cur->name)); 2569#endif 2570 ctxt->insert = insert; 2571 /* 2572 * We need the fragment base for extension instructions 2573 * which return values (like EXSLT's function). 2574 */ 2575 oldLocalFragmentBase = ctxt->localRVTBase; 2576 ctxt->localRVTBase = NULL; 2577 2578 func(ctxt, contextNode, cur, cur->psvi); 2579 2580 ctxt->localRVTBase = oldLocalFragmentBase; 2581 /* 2582 * Cleanup temporary tree fragments. 2583 */ 2584 if (oldLocalFragmentTop != ctxt->localRVT) 2585 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2586 2587 ctxt->insert = oldInsert; 2588 } 2589 goto skip_children; 2590 } 2591 2592 } else if (XSLT_IS_TEXT_NODE(cur)) { 2593 /* 2594 * Text 2595 * ------------------------------------------------------------ 2596 */ 2597#ifdef WITH_XSLT_DEBUG_PROCESS 2598 if (cur->name == xmlStringTextNoenc) { 2599 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2600 xsltGenericDebug(xsltGenericDebugContext, 2601 "xsltApplySequenceConstructor: copy unescaped text '%s'\n", 2602 cur->content)); 2603 } else { 2604 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2605 xsltGenericDebug(xsltGenericDebugContext, 2606 "xsltApplySequenceConstructor: copy text '%s'\n", 2607 cur->content)); 2608 } 2609#endif 2610 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) 2611 goto error; 2612 } 2613 2614#else /* XSLT_REFACTORED */ 2615 2616 if (IS_XSLT_ELEM(cur)) { 2617 /* 2618 * This is an XSLT node 2619 */ 2620 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi; 2621 2622 if (info == NULL) { 2623 if (IS_XSLT_NAME(cur, "message")) { 2624 xsltMessage(ctxt, contextNode, cur); 2625 } else { 2626 /* 2627 * That's an error try to apply one of the fallback cases 2628 */ 2629 ctxt->insert = insert; 2630 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2631 xsltGenericError(xsltGenericErrorContext, 2632 "xsltApplySequenceConstructor: %s was not compiled\n", 2633 cur->name); 2634 } 2635 ctxt->insert = oldInsert; 2636 } 2637 goto skip_children; 2638 } 2639 2640 if (info->func != NULL) { 2641 oldCurInst = ctxt->inst; 2642 ctxt->inst = cur; 2643 ctxt->insert = insert; 2644 oldLocalFragmentBase = ctxt->localRVTBase; 2645 ctxt->localRVTBase = NULL; 2646 2647 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info); 2648 2649 ctxt->localRVTBase = oldLocalFragmentBase; 2650 /* 2651 * Cleanup temporary tree fragments. 2652 */ 2653 if (oldLocalFragmentTop != ctxt->localRVT) 2654 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2655 2656 ctxt->insert = oldInsert; 2657 ctxt->inst = oldCurInst; 2658 goto skip_children; 2659 } 2660 2661 if (IS_XSLT_NAME(cur, "variable")) { 2662 xsltStackElemPtr tmpvar = ctxt->vars; 2663 2664 oldCurInst = ctxt->inst; 2665 ctxt->inst = cur; 2666 2667 xsltParseStylesheetVariable(ctxt, cur); 2668 2669 ctxt->inst = oldCurInst; 2670 2671 if (tmpvar != ctxt->vars) { 2672 /* 2673 * TODO: Using a @tmpvar is an annoying workaround, but 2674 * the current mechanisms do not provide any other way 2675 * of knowing if the var was really pushed onto the 2676 * stack. 2677 */ 2678 ctxt->vars->level = level; 2679 } 2680 } else if (IS_XSLT_NAME(cur, "message")) { 2681 xsltMessage(ctxt, contextNode, cur); 2682 } else { 2683 xsltTransformError(ctxt, NULL, cur, 2684 "Unexpected XSLT element '%s'.\n", cur->name); 2685 } 2686 goto skip_children; 2687 } else if ((cur->type == XML_TEXT_NODE) || 2688 (cur->type == XML_CDATA_SECTION_NODE)) { 2689 2690 /* 2691 * This text comes from the stylesheet 2692 * For stylesheets, the set of whitespace-preserving 2693 * element names consists of just xsl:text. 2694 */ 2695#ifdef WITH_XSLT_DEBUG_PROCESS 2696 if (cur->type == XML_CDATA_SECTION_NODE) { 2697 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2698 "xsltApplySequenceConstructor: copy CDATA text %s\n", 2699 cur->content)); 2700 } else if (cur->name == xmlStringTextNoenc) { 2701 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2702 "xsltApplySequenceConstructor: copy unescaped text %s\n", 2703 cur->content)); 2704 } else { 2705 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2706 "xsltApplySequenceConstructor: copy text %s\n", 2707 cur->content)); 2708 } 2709#endif 2710 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) 2711 goto error; 2712 } else if ((cur->type == XML_ELEMENT_NODE) && 2713 (cur->ns != NULL) && (cur->psvi != NULL)) { 2714 xsltTransformFunction function; 2715 2716 oldCurInst = ctxt->inst; 2717 ctxt->inst = cur; 2718 /* 2719 * Flagged as an extension element 2720 */ 2721 if (cur->psvi == xsltExtMarker) 2722 function = (xsltTransformFunction) 2723 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); 2724 else 2725 function = ((xsltElemPreCompPtr) cur->psvi)->func; 2726 2727 if (function == NULL) { 2728 xmlNodePtr child; 2729 int found = 0; 2730 2731#ifdef WITH_XSLT_DEBUG_PROCESS 2732 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2733 "xsltApplySequenceConstructor: unknown extension %s\n", 2734 cur->name)); 2735#endif 2736 /* 2737 * Search if there are fallbacks 2738 */ 2739 child = cur->children; 2740 while (child != NULL) { 2741 if ((IS_XSLT_ELEM(child)) && 2742 (IS_XSLT_NAME(child, "fallback"))) 2743 { 2744 found = 1; 2745 xsltApplySequenceConstructor(ctxt, contextNode, 2746 child->children, NULL); 2747 } 2748 child = child->next; 2749 } 2750 2751 if (!found) { 2752 xsltTransformError(ctxt, NULL, cur, 2753 "xsltApplySequenceConstructor: failed to find extension %s\n", 2754 cur->name); 2755 } 2756 } else { 2757#ifdef WITH_XSLT_DEBUG_PROCESS 2758 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2759 "xsltApplySequenceConstructor: extension construct %s\n", 2760 cur->name)); 2761#endif 2762 2763 ctxt->insert = insert; 2764 /* 2765 * We need the fragment base for extension instructions 2766 * which return values (like EXSLT's function). 2767 */ 2768 oldLocalFragmentBase = ctxt->localRVTBase; 2769 ctxt->localRVTBase = NULL; 2770 2771 function(ctxt, contextNode, cur, cur->psvi); 2772 /* 2773 * Cleanup temporary tree fragments. 2774 */ 2775 if (oldLocalFragmentTop != ctxt->localRVT) 2776 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2777 2778 ctxt->localRVTBase = oldLocalFragmentBase; 2779 ctxt->insert = oldInsert; 2780 2781 } 2782 ctxt->inst = oldCurInst; 2783 goto skip_children; 2784 } else if (cur->type == XML_ELEMENT_NODE) { 2785#ifdef WITH_XSLT_DEBUG_PROCESS 2786 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2787 "xsltApplySequenceConstructor: copy node %s\n", 2788 cur->name)); 2789#endif 2790 oldCurInst = ctxt->inst; 2791 ctxt->inst = cur; 2792 2793 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) 2794 goto error; 2795 /* 2796 * Add extra namespaces inherited from the current template 2797 * if we are in the first level children and this is a 2798 * "real" template. 2799 */ 2800 if ((templ != NULL) && (oldInsert == insert) && 2801 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { 2802 int i; 2803 xmlNsPtr ns, ret; 2804 2805 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { 2806 const xmlChar *URI = NULL; 2807 xsltStylesheetPtr style; 2808 ns = ctxt->templ->inheritedNs[i]; 2809 2810 /* Note that the XSLT namespace was already excluded 2811 * in xsltGetInheritedNsList(). 2812 */ 2813#if 0 2814 if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) 2815 continue; 2816#endif 2817 style = ctxt->style; 2818 while (style != NULL) { 2819 if (style->nsAliases != NULL) 2820 URI = (const xmlChar *) 2821 xmlHashLookup(style->nsAliases, ns->href); 2822 if (URI != NULL) 2823 break; 2824 2825 style = xsltNextImport(style); 2826 } 2827 if (URI == UNDEFINED_DEFAULT_NS) 2828 continue; 2829 if (URI == NULL) 2830 URI = ns->href; 2831 /* 2832 * TODO: The following will still be buggy for the 2833 * non-refactored code. 2834 */ 2835 ret = xmlSearchNs(copy->doc, copy, ns->prefix); 2836 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) 2837 { 2838 xmlNewNs(copy, URI, ns->prefix); 2839 } 2840 } 2841 if (copy->ns != NULL) { 2842 /* 2843 * Fix the node namespace if needed 2844 */ 2845 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); 2846 } 2847 } 2848 /* 2849 * all the attributes are directly inherited 2850 */ 2851 if (cur->properties != NULL) { 2852 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); 2853 } 2854 ctxt->inst = oldCurInst; 2855 } 2856#endif /* else of XSLT_REFACTORED */ 2857 2858 /* 2859 * Descend into content in document order. 2860 */ 2861 if (cur->children != NULL) { 2862 if (cur->children->type != XML_ENTITY_DECL) { 2863 cur = cur->children; 2864 level++; 2865 if (copy != NULL) 2866 insert = copy; 2867 continue; 2868 } 2869 } 2870 2871skip_children: 2872 /* 2873 * If xslt:message was just processed, we might have hit a 2874 * terminate='yes'; if so, then break the loop and clean up. 2875 * TODO: Do we need to check this also before trying to descend 2876 * into the content? 2877 */ 2878 if (ctxt->state == XSLT_STATE_STOPPED) 2879 break; 2880 if (cur->next != NULL) { 2881 cur = cur->next; 2882 continue; 2883 } 2884 2885 do { 2886 cur = cur->parent; 2887 level--; 2888 /* 2889 * Pop variables/params (xsl:variable and xsl:param). 2890 */ 2891 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) { 2892 xsltLocalVariablePop(ctxt, oldVarsNr, level); 2893 } 2894 2895 insert = insert->parent; 2896 if (cur == NULL) 2897 break; 2898 if (cur == list->parent) { 2899 cur = NULL; 2900 break; 2901 } 2902 if (cur->next != NULL) { 2903 cur = cur->next; 2904 break; 2905 } 2906 } while (cur != NULL); 2907 } 2908 2909error: 2910 /* 2911 * In case of errors: pop remaining variables. 2912 */ 2913 if (ctxt->varsNr > oldVarsNr) 2914 xsltLocalVariablePop(ctxt, oldVarsNr, -1); 2915 2916 ctxt->node = oldContextNode; 2917 ctxt->inst = oldInst; 2918 ctxt->insert = oldInsert; 2919 2920#ifdef WITH_DEBUGGER 2921 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { 2922 xslDropCall(); 2923 } 2924#endif 2925} 2926 2927/* 2928* xsltApplyXSLTTemplate: 2929* @ctxt: a XSLT transformation context 2930* @contextNode: the node in the source tree. 2931* @list: the nodes of a sequence constructor; 2932* (plus leading xsl:param elements) 2933* @templ: the compiled xsl:template declaration; 2934* NULL if a sequence constructor 2935* @withParams: a set of caller-parameters (xsl:with-param) or NULL 2936* 2937* Called by: 2938* - xsltApplyImports() 2939* - xsltCallTemplate() 2940* - xsltDefaultProcessOneNode() 2941* - xsltProcessOneNode() 2942*/ 2943static void 2944xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, 2945 xmlNodePtr contextNode, 2946 xmlNodePtr list, 2947 xsltTemplatePtr templ, 2948 xsltStackElemPtr withParams) 2949{ 2950 int oldVarsBase = 0; 2951 long start = 0; 2952 xmlNodePtr cur; 2953 xsltStackElemPtr tmpParam = NULL; 2954 xmlDocPtr oldUserFragmentTop, oldLocalFragmentTop; 2955 2956#ifdef XSLT_REFACTORED 2957 xsltStyleItemParamPtr iparam; 2958#else 2959 xsltStylePreCompPtr iparam; 2960#endif 2961 2962#ifdef WITH_DEBUGGER 2963 int addCallResult = 0; 2964#endif 2965 2966 if (ctxt == NULL) 2967 return; 2968 if (templ == NULL) { 2969 xsltTransformError(ctxt, NULL, list, 2970 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n"); 2971 return; 2972 } 2973 2974#ifdef WITH_DEBUGGER 2975 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { 2976 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode, 2977 list, templ, &addCallResult) == NULL) 2978 return; 2979 } 2980#endif 2981 2982 if (list == NULL) 2983 return; 2984 CHECK_STOPPED; 2985 2986 /* 2987 * Check for infinite recursion: stop if the maximum of nested templates 2988 * is excceeded. Adjust xsltMaxDepth if you need more. 2989 */ 2990 if (ctxt->templNr >= ctxt->maxTemplateDepth) 2991 { 2992 xsltTransformError(ctxt, NULL, list, 2993 "xsltApplyXSLTTemplate: A potential infinite template recursion " 2994 "was detected.\n" 2995 "You can adjust xsltMaxDepth (--maxdepth) in order to " 2996 "raise the maximum number of nested template calls and " 2997 "variables/params (currently set to %d).\n", 2998 ctxt->maxTemplateDepth); 2999 xsltDebug(ctxt, contextNode, list, NULL); 3000 return; 3001 } 3002 3003 if (ctxt->varsNr >= ctxt->maxTemplateVars) 3004 { 3005 xsltTransformError(ctxt, NULL, list, 3006 "xsltApplyXSLTTemplate: A potential infinite template recursion " 3007 "was detected.\n" 3008 "You can adjust maxTemplateVars (--maxvars) in order to " 3009 "raise the maximum number of variables/params (currently set to %d).\n", 3010 ctxt->maxTemplateVars); 3011 xsltDebug(ctxt, contextNode, list, NULL); 3012 return; 3013 } 3014 3015 oldUserFragmentTop = ctxt->tmpRVT; 3016 ctxt->tmpRVT = NULL; 3017 oldLocalFragmentTop = ctxt->localRVT; 3018 3019 /* 3020 * Initiate a distinct scope of local params/variables. 3021 */ 3022 oldVarsBase = ctxt->varsBase; 3023 ctxt->varsBase = ctxt->varsNr; 3024 3025 ctxt->node = contextNode; 3026 if (ctxt->profile) { 3027 templ->nbCalls++; 3028 start = xsltTimestamp(); 3029 profPush(ctxt, 0); 3030 profCallgraphAdd(templ, ctxt->templ); 3031 } 3032 /* 3033 * Push the xsl:template declaration onto the stack. 3034 */ 3035 templPush(ctxt, templ); 3036 3037#ifdef WITH_XSLT_DEBUG_PROCESS 3038 if (templ->name != NULL) 3039 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 3040 "applying xsl:template '%s'\n", templ->name)); 3041#endif 3042 /* 3043 * Process xsl:param instructions and skip those elements for 3044 * further processing. 3045 */ 3046 cur = list; 3047 do { 3048 if (cur->type == XML_TEXT_NODE) { 3049 cur = cur->next; 3050 continue; 3051 } 3052 if ((cur->type != XML_ELEMENT_NODE) || 3053 (cur->name[0] != 'p') || 3054 (cur->psvi == NULL) || 3055 (! xmlStrEqual(cur->name, BAD_CAST "param")) || 3056 (! IS_XSLT_ELEM(cur))) 3057 { 3058 break; 3059 } 3060 3061 list = cur->next; 3062 3063#ifdef XSLT_REFACTORED 3064 iparam = (xsltStyleItemParamPtr) cur->psvi; 3065#else 3066 iparam = (xsltStylePreCompPtr) cur->psvi; 3067#endif 3068 3069 /* 3070 * Substitute xsl:param for a given xsl:with-param. 3071 * Since the XPath expression will reference the params/vars 3072 * by index, we need to slot the xsl:with-params in the 3073 * order of encountered xsl:params to keep the sequence of 3074 * params/variables in the stack exactly as it was at 3075 * compile time, 3076 */ 3077 tmpParam = NULL; 3078 if (withParams) { 3079 tmpParam = withParams; 3080 do { 3081 if ((tmpParam->name == (iparam->name)) && 3082 (tmpParam->nameURI == (iparam->ns))) 3083 { 3084 /* 3085 * Push the caller-parameter. 3086 */ 3087 xsltLocalVariablePush(ctxt, tmpParam, -1); 3088 break; 3089 } 3090 tmpParam = tmpParam->next; 3091 } while (tmpParam != NULL); 3092 } 3093 /* 3094 * Push the xsl:param. 3095 */ 3096 if (tmpParam == NULL) { 3097 /* 3098 * Note that we must assume that the added parameter 3099 * has a @depth of 0. 3100 */ 3101 xsltParseStylesheetParam(ctxt, cur); 3102 } 3103 cur = cur->next; 3104 } while (cur != NULL); 3105 /* 3106 * Process the sequence constructor. 3107 */ 3108 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3109 3110 /* 3111 * Remove remaining xsl:param and xsl:with-param items from 3112 * the stack. Don't free xsl:with-param items. 3113 */ 3114 if (ctxt->varsNr > ctxt->varsBase) 3115 xsltTemplateParamsCleanup(ctxt); 3116 ctxt->varsBase = oldVarsBase; 3117 3118 /* 3119 * Clean up remaining local tree fragments. 3120 * This also frees fragments which are the result of 3121 * extension instructions. Should normally not be hit; but 3122 * just for the case xsltExtensionInstructionResultFinalize() 3123 * was not called by the extension author. 3124 */ 3125 if (oldLocalFragmentTop != ctxt->localRVT) { 3126 xmlDocPtr curdoc = ctxt->localRVT, tmp; 3127 3128 do { 3129 tmp = curdoc; 3130 curdoc = (xmlDocPtr) curdoc->next; 3131 /* Need to housekeep localRVTBase */ 3132 if (tmp == ctxt->localRVTBase) 3133 ctxt->localRVTBase = curdoc; 3134 if (tmp->prev) 3135 tmp->prev->next = (xmlNodePtr) curdoc; 3136 if (curdoc) 3137 curdoc->prev = tmp->prev; 3138 xsltReleaseRVT(ctxt, tmp); 3139 } while (curdoc != oldLocalFragmentTop); 3140 } 3141 ctxt->localRVT = oldLocalFragmentTop; 3142 3143 /* 3144 * Release user-created fragments stored in the scope 3145 * of xsl:template. Note that this mechanism is deprecated: 3146 * user code should now use xsltRegisterLocalRVT() instead 3147 * of the obsolete xsltRegisterTmpRVT(). 3148 */ 3149 if (ctxt->tmpRVT) { 3150 xmlDocPtr curdoc = ctxt->tmpRVT, tmp; 3151 3152 while (curdoc != NULL) { 3153 tmp = curdoc; 3154 curdoc = (xmlDocPtr) curdoc->next; 3155 xsltReleaseRVT(ctxt, tmp); 3156 } 3157 } 3158 ctxt->tmpRVT = oldUserFragmentTop; 3159 3160 /* 3161 * Pop the xsl:template declaration from the stack. 3162 */ 3163 templPop(ctxt); 3164 if (ctxt->profile) { 3165 long spent, child, total, end; 3166 3167 end = xsltTimestamp(); 3168 child = profPop(ctxt); 3169 total = end - start; 3170 spent = total - child; 3171 if (spent <= 0) { 3172 /* 3173 * Not possible unless the original calibration failed 3174 * we can try to correct it on the fly. 3175 */ 3176 xsltCalibrateAdjust(spent); 3177 spent = 0; 3178 } 3179 3180 templ->time += spent; 3181 if (ctxt->profNr > 0) 3182 ctxt->profTab[ctxt->profNr - 1] += total; 3183 } 3184 3185#ifdef WITH_DEBUGGER 3186 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { 3187 xslDropCall(); 3188 } 3189#endif 3190} 3191 3192 3193/** 3194 * xsltApplyOneTemplate: 3195 * @ctxt: a XSLT process context 3196 * @contextNode: the node in the source tree. 3197 * @list: the nodes of a sequence constructor 3198 * @templ: not used 3199 * @params: a set of parameters (xsl:param) or NULL 3200 * 3201 * Processes a sequence constructor on the current node in the source tree. 3202 * 3203 * @params are the already computed variable stack items; this function 3204 * pushes them on the variable stack, and pops them before exiting; it's 3205 * left to the caller to free or reuse @params afterwards. The initial 3206 * states of the variable stack will always be restored before this 3207 * function exits. 3208 * NOTE that this does *not* initiate a new distinct variable scope; i.e. 3209 * variables already on the stack are visible to the process. The caller's 3210 * side needs to start a new variable scope if needed (e.g. in exsl:function). 3211 * 3212 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not 3213 * provide a @templ); a non-NULL @templ might raise an error in the future. 3214 * 3215 * BIG NOTE: This function is not intended to process the content of an 3216 * xsl:template; it does not expect xsl:param instructions in @list and 3217 * will report errors if found. 3218 * 3219 * Called by: 3220 * - xsltEvalVariable() (variables.c) 3221 * - exsltFuncFunctionFunction() (libexsl/functions.c) 3222 */ 3223void 3224xsltApplyOneTemplate(xsltTransformContextPtr ctxt, 3225 xmlNodePtr contextNode, 3226 xmlNodePtr list, 3227 xsltTemplatePtr templ ATTRIBUTE_UNUSED, 3228 xsltStackElemPtr params) 3229{ 3230 if ((ctxt == NULL) || (list == NULL)) 3231 return; 3232 CHECK_STOPPED; 3233 3234 if (params) { 3235 /* 3236 * This code should be obsolete - was previously used 3237 * by libexslt/functions.c, but due to bug 381319 the 3238 * logic there was changed. 3239 */ 3240 int oldVarsNr = ctxt->varsNr; 3241 3242 /* 3243 * Push the given xsl:param(s) onto the variable stack. 3244 */ 3245 while (params != NULL) { 3246 xsltLocalVariablePush(ctxt, params, -1); 3247 params = params->next; 3248 } 3249 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3250 /* 3251 * Pop the given xsl:param(s) from the stack but don't free them. 3252 */ 3253 xsltLocalVariablePop(ctxt, oldVarsNr, -2); 3254 } else 3255 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3256} 3257 3258/************************************************************************ 3259 * * 3260 * XSLT-1.1 extensions * 3261 * * 3262 ************************************************************************/ 3263 3264/** 3265 * xsltDocumentElem: 3266 * @ctxt: an XSLT processing context 3267 * @node: The current node 3268 * @inst: the instruction in the stylesheet 3269 * @castedComp: precomputed information 3270 * 3271 * Process an EXSLT/XSLT-1.1 document element 3272 */ 3273void 3274xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node, 3275 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 3276{ 3277#ifdef XSLT_REFACTORED 3278 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp; 3279#else 3280 xsltStylePreCompPtr comp = castedComp; 3281#endif 3282 xsltStylesheetPtr style = NULL; 3283 int ret; 3284 xmlChar *filename = NULL, *prop, *elements; 3285 xmlChar *element, *end; 3286 xmlDocPtr res = NULL; 3287 xmlDocPtr oldOutput; 3288 xmlNodePtr oldInsert, root; 3289 const char *oldOutputFile; 3290 xsltOutputType oldType; 3291 xmlChar *URL = NULL; 3292 const xmlChar *method; 3293 const xmlChar *doctypePublic; 3294 const xmlChar *doctypeSystem; 3295 const xmlChar *version; 3296 const xmlChar *encoding; 3297 int redirect_write_append = 0; 3298 3299 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 3300 return; 3301 3302 if (comp->filename == NULL) { 3303 3304 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { 3305 /* 3306 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE 3307 * (http://icl.com/saxon) 3308 * The @file is in no namespace. 3309 */ 3310#ifdef WITH_XSLT_DEBUG_EXTRA 3311 xsltGenericDebug(xsltGenericDebugContext, 3312 "Found saxon:output extension\n"); 3313#endif 3314 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3315 (const xmlChar *) "file", 3316 XSLT_SAXON_NAMESPACE); 3317 3318 if (URL == NULL) 3319 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3320 (const xmlChar *) "href", 3321 XSLT_SAXON_NAMESPACE); 3322 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { 3323#ifdef WITH_XSLT_DEBUG_EXTRA 3324 xsltGenericDebug(xsltGenericDebugContext, 3325 "Found xalan:write extension\n"); 3326#endif 3327 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3328 (const xmlChar *) 3329 "select", 3330 XSLT_XALAN_NAMESPACE); 3331 if (URL != NULL) { 3332 xmlXPathCompExprPtr cmp; 3333 xmlChar *val; 3334 3335 /* 3336 * Trying to handle bug #59212 3337 * The value of the "select" attribute is an 3338 * XPath expression. 3339 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect) 3340 */ 3341 cmp = xmlXPathCompile(URL); 3342 val = xsltEvalXPathString(ctxt, cmp); 3343 xmlXPathFreeCompExpr(cmp); 3344 xmlFree(URL); 3345 URL = val; 3346 } 3347 if (URL == NULL) 3348 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3349 (const xmlChar *) 3350 "file", 3351 XSLT_XALAN_NAMESPACE); 3352 if (URL == NULL) 3353 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3354 (const xmlChar *) 3355 "href", 3356 XSLT_XALAN_NAMESPACE); 3357 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { 3358 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3359 (const xmlChar *) "href", 3360 NULL); 3361 } 3362 3363 } else { 3364 URL = xmlStrdup(comp->filename); 3365 } 3366 3367 if (URL == NULL) { 3368 xsltTransformError(ctxt, NULL, inst, 3369 "xsltDocumentElem: href/URI-Reference not found\n"); 3370 return; 3371 } 3372 3373 /* 3374 * If the computation failed, it's likely that the URL wasn't escaped 3375 */ 3376 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile); 3377 if (filename == NULL) { 3378 xmlChar *escURL; 3379 3380 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,"); 3381 if (escURL != NULL) { 3382 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile); 3383 xmlFree(escURL); 3384 } 3385 } 3386 3387 if (filename == NULL) { 3388 xsltTransformError(ctxt, NULL, inst, 3389 "xsltDocumentElem: URL computation failed for %s\n", 3390 URL); 3391 xmlFree(URL); 3392 return; 3393 } 3394 3395 /* 3396 * Security checking: can we write to this resource 3397 */ 3398 if (ctxt->sec != NULL) { 3399 ret = xsltCheckWrite(ctxt->sec, ctxt, filename); 3400 if (ret == 0) { 3401 xsltTransformError(ctxt, NULL, inst, 3402 "xsltDocumentElem: write rights for %s denied\n", 3403 filename); 3404 xmlFree(URL); 3405 xmlFree(filename); 3406 return; 3407 } 3408 } 3409 3410 oldOutputFile = ctxt->outputFile; 3411 oldOutput = ctxt->output; 3412 oldInsert = ctxt->insert; 3413 oldType = ctxt->type; 3414 ctxt->outputFile = (const char *) filename; 3415 3416 style = xsltNewStylesheet(); 3417 if (style == NULL) { 3418 xsltTransformError(ctxt, NULL, inst, 3419 "xsltDocumentElem: out of memory\n"); 3420 goto error; 3421 } 3422 3423 /* 3424 * Version described in 1.1 draft allows full parameterization 3425 * of the output. 3426 */ 3427 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3428 (const xmlChar *) "version", 3429 NULL); 3430 if (prop != NULL) { 3431 if (style->version != NULL) 3432 xmlFree(style->version); 3433 style->version = prop; 3434 } 3435 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3436 (const xmlChar *) "encoding", 3437 NULL); 3438 if (prop != NULL) { 3439 if (style->encoding != NULL) 3440 xmlFree(style->encoding); 3441 style->encoding = prop; 3442 } 3443 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3444 (const xmlChar *) "method", 3445 NULL); 3446 if (prop != NULL) { 3447 const xmlChar *URI; 3448 3449 if (style->method != NULL) 3450 xmlFree(style->method); 3451 style->method = NULL; 3452 if (style->methodURI != NULL) 3453 xmlFree(style->methodURI); 3454 style->methodURI = NULL; 3455 3456 URI = xsltGetQNameURI(inst, &prop); 3457 if (prop == NULL) { 3458 if (style != NULL) style->errors++; 3459 } else if (URI == NULL) { 3460 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || 3461 (xmlStrEqual(prop, (const xmlChar *) "html")) || 3462 (xmlStrEqual(prop, (const xmlChar *) "text"))) { 3463 style->method = prop; 3464 } else { 3465 xsltTransformError(ctxt, NULL, inst, 3466 "invalid value for method: %s\n", prop); 3467 if (style != NULL) style->warnings++; 3468 } 3469 } else { 3470 style->method = prop; 3471 style->methodURI = xmlStrdup(URI); 3472 } 3473 } 3474 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3475 (const xmlChar *) 3476 "doctype-system", NULL); 3477 if (prop != NULL) { 3478 if (style->doctypeSystem != NULL) 3479 xmlFree(style->doctypeSystem); 3480 style->doctypeSystem = prop; 3481 } 3482 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3483 (const xmlChar *) 3484 "doctype-public", NULL); 3485 if (prop != NULL) { 3486 if (style->doctypePublic != NULL) 3487 xmlFree(style->doctypePublic); 3488 style->doctypePublic = prop; 3489 } 3490 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3491 (const xmlChar *) "standalone", 3492 NULL); 3493 if (prop != NULL) { 3494 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3495 style->standalone = 1; 3496 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3497 style->standalone = 0; 3498 } else { 3499 xsltTransformError(ctxt, NULL, inst, 3500 "invalid value for standalone: %s\n", 3501 prop); 3502 if (style != NULL) style->warnings++; 3503 } 3504 xmlFree(prop); 3505 } 3506 3507 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3508 (const xmlChar *) "indent", 3509 NULL); 3510 if (prop != NULL) { 3511 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3512 style->indent = 1; 3513 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3514 style->indent = 0; 3515 } else { 3516 xsltTransformError(ctxt, NULL, inst, 3517 "invalid value for indent: %s\n", prop); 3518 if (style != NULL) style->warnings++; 3519 } 3520 xmlFree(prop); 3521 } 3522 3523 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3524 (const xmlChar *) 3525 "omit-xml-declaration", 3526 NULL); 3527 if (prop != NULL) { 3528 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3529 style->omitXmlDeclaration = 1; 3530 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3531 style->omitXmlDeclaration = 0; 3532 } else { 3533 xsltTransformError(ctxt, NULL, inst, 3534 "invalid value for omit-xml-declaration: %s\n", 3535 prop); 3536 if (style != NULL) style->warnings++; 3537 } 3538 xmlFree(prop); 3539 } 3540 3541 elements = xsltEvalAttrValueTemplate(ctxt, inst, 3542 (const xmlChar *) 3543 "cdata-section-elements", 3544 NULL); 3545 if (elements != NULL) { 3546 if (style->stripSpaces == NULL) 3547 style->stripSpaces = xmlHashCreate(10); 3548 if (style->stripSpaces == NULL) 3549 return; 3550 3551 element = elements; 3552 while (*element != 0) { 3553 while (IS_BLANK_CH(*element)) 3554 element++; 3555 if (*element == 0) 3556 break; 3557 end = element; 3558 while ((*end != 0) && (!IS_BLANK_CH(*end))) 3559 end++; 3560 element = xmlStrndup(element, end - element); 3561 if (element) { 3562 const xmlChar *URI; 3563 3564#ifdef WITH_XSLT_DEBUG_PARSING 3565 xsltGenericDebug(xsltGenericDebugContext, 3566 "add cdata section output element %s\n", 3567 element); 3568#endif 3569 URI = xsltGetQNameURI(inst, &element); 3570 3571 xmlHashAddEntry2(style->stripSpaces, element, URI, 3572 (xmlChar *) "cdata"); 3573 xmlFree(element); 3574 } 3575 element = end; 3576 } 3577 xmlFree(elements); 3578 } 3579 3580 /* 3581 * Create a new document tree and process the element template 3582 */ 3583 XSLT_GET_IMPORT_PTR(method, style, method) 3584 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3585 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3586 XSLT_GET_IMPORT_PTR(version, style, version) 3587 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 3588 3589 if ((method != NULL) && 3590 (!xmlStrEqual(method, (const xmlChar *) "xml"))) { 3591 if (xmlStrEqual(method, (const xmlChar *) "html")) { 3592 ctxt->type = XSLT_OUTPUT_HTML; 3593 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3594 res = htmlNewDoc(doctypeSystem, doctypePublic); 3595 else { 3596 if (version != NULL) { 3597#ifdef XSLT_GENERATE_HTML_DOCTYPE 3598 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 3599#endif 3600 } 3601 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3602 } 3603 if (res == NULL) 3604 goto error; 3605 res->dict = ctxt->dict; 3606 xmlDictReference(res->dict); 3607 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 3608 xsltTransformError(ctxt, NULL, inst, 3609 "xsltDocumentElem: unsupported method xhtml\n", 3610 style->method); 3611 ctxt->type = XSLT_OUTPUT_HTML; 3612 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3613 if (res == NULL) 3614 goto error; 3615 res->dict = ctxt->dict; 3616 xmlDictReference(res->dict); 3617 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 3618 ctxt->type = XSLT_OUTPUT_TEXT; 3619 res = xmlNewDoc(style->version); 3620 if (res == NULL) 3621 goto error; 3622 res->dict = ctxt->dict; 3623 xmlDictReference(res->dict); 3624#ifdef WITH_XSLT_DEBUG 3625 xsltGenericDebug(xsltGenericDebugContext, 3626 "reusing transformation dict for output\n"); 3627#endif 3628 } else { 3629 xsltTransformError(ctxt, NULL, inst, 3630 "xsltDocumentElem: unsupported method %s\n", 3631 style->method); 3632 goto error; 3633 } 3634 } else { 3635 ctxt->type = XSLT_OUTPUT_XML; 3636 res = xmlNewDoc(style->version); 3637 if (res == NULL) 3638 goto error; 3639 res->dict = ctxt->dict; 3640 xmlDictReference(res->dict); 3641#ifdef WITH_XSLT_DEBUG 3642 xsltGenericDebug(xsltGenericDebugContext, 3643 "reusing transformation dict for output\n"); 3644#endif 3645 } 3646 res->charset = XML_CHAR_ENCODING_UTF8; 3647 if (encoding != NULL) 3648 res->encoding = xmlStrdup(encoding); 3649 ctxt->output = res; 3650 ctxt->insert = (xmlNodePtr) res; 3651 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); 3652 3653 /* 3654 * Do some post processing work depending on the generated output 3655 */ 3656 root = xmlDocGetRootElement(res); 3657 if (root != NULL) { 3658 const xmlChar *doctype = NULL; 3659 3660 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 3661 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 3662 if (doctype == NULL) 3663 doctype = root->name; 3664 3665 /* 3666 * Apply the default selection of the method 3667 */ 3668 if ((method == NULL) && 3669 (root->ns == NULL) && 3670 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 3671 xmlNodePtr tmp; 3672 3673 tmp = res->children; 3674 while ((tmp != NULL) && (tmp != root)) { 3675 if (tmp->type == XML_ELEMENT_NODE) 3676 break; 3677 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 3678 break; 3679 tmp = tmp->next; 3680 } 3681 if (tmp == root) { 3682 ctxt->type = XSLT_OUTPUT_HTML; 3683 res->type = XML_HTML_DOCUMENT_NODE; 3684 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 3685 res->intSubset = xmlCreateIntSubset(res, doctype, 3686 doctypePublic, 3687 doctypeSystem); 3688#ifdef XSLT_GENERATE_HTML_DOCTYPE 3689 } else if (version != NULL) { 3690 xsltGetHTMLIDs(version, &doctypePublic, 3691 &doctypeSystem); 3692 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3693 res->intSubset = 3694 xmlCreateIntSubset(res, doctype, 3695 doctypePublic, 3696 doctypeSystem); 3697#endif 3698 } 3699 } 3700 3701 } 3702 if (ctxt->type == XSLT_OUTPUT_XML) { 3703 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3704 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3705 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3706 res->intSubset = xmlCreateIntSubset(res, doctype, 3707 doctypePublic, 3708 doctypeSystem); 3709 } 3710 } 3711 3712 /* 3713 * Calls to redirect:write also take an optional attribute append. 3714 * Attribute append="true|yes" which will attempt to simply append 3715 * to an existing file instead of always opening a new file. The 3716 * default behavior of always overwriting the file still happens 3717 * if we do not specify append. 3718 * Note that append use will forbid use of remote URI target. 3719 */ 3720 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", 3721 NULL); 3722 if (prop != NULL) { 3723 if (xmlStrEqual(prop, (const xmlChar *) "true") || 3724 xmlStrEqual(prop, (const xmlChar *) "yes")) { 3725 style->omitXmlDeclaration = 1; 3726 redirect_write_append = 1; 3727 } else 3728 style->omitXmlDeclaration = 0; 3729 xmlFree(prop); 3730 } 3731 3732 if (redirect_write_append) { 3733 FILE *f; 3734 3735 f = fopen((const char *) filename, "ab"); 3736 if (f == NULL) { 3737 ret = -1; 3738 } else { 3739 ret = xsltSaveResultToFile(f, res, style); 3740 fclose(f); 3741 } 3742 } else { 3743 ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); 3744 } 3745 if (ret < 0) { 3746 xsltTransformError(ctxt, NULL, inst, 3747 "xsltDocumentElem: unable to save to %s\n", 3748 filename); 3749 ctxt->state = XSLT_STATE_ERROR; 3750#ifdef WITH_XSLT_DEBUG_EXTRA 3751 } else { 3752 xsltGenericDebug(xsltGenericDebugContext, 3753 "Wrote %d bytes to %s\n", ret, filename); 3754#endif 3755 } 3756 3757 error: 3758 ctxt->output = oldOutput; 3759 ctxt->insert = oldInsert; 3760 ctxt->type = oldType; 3761 ctxt->outputFile = oldOutputFile; 3762 if (URL != NULL) 3763 xmlFree(URL); 3764 if (filename != NULL) 3765 xmlFree(filename); 3766 if (style != NULL) 3767 xsltFreeStylesheet(style); 3768 if (res != NULL) 3769 xmlFreeDoc(res); 3770} 3771 3772/************************************************************************ 3773 * * 3774 * Most of the XSLT-1.0 transformations * 3775 * * 3776 ************************************************************************/ 3777 3778/** 3779 * xsltSort: 3780 * @ctxt: a XSLT process context 3781 * @node: the node in the source tree. 3782 * @inst: the xslt sort node 3783 * @comp: precomputed information 3784 * 3785 * function attached to xsl:sort nodes, but this should not be 3786 * called directly 3787 */ 3788void 3789xsltSort(xsltTransformContextPtr ctxt, 3790 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, 3791 xsltStylePreCompPtr comp) { 3792 if (comp == NULL) { 3793 xsltTransformError(ctxt, NULL, inst, 3794 "xsl:sort : compilation failed\n"); 3795 return; 3796 } 3797 xsltTransformError(ctxt, NULL, inst, 3798 "xsl:sort : improper use this should not be reached\n"); 3799} 3800 3801/** 3802 * xsltCopy: 3803 * @ctxt: an XSLT process context 3804 * @node: the node in the source tree 3805 * @inst: the element node of the XSLT-copy instruction 3806 * @castedComp: computed information of the XSLT-copy instruction 3807 * 3808 * Execute the XSLT-copy instruction on the source node. 3809 */ 3810void 3811xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, 3812 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 3813{ 3814#ifdef XSLT_REFACTORED 3815 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; 3816#else 3817 xsltStylePreCompPtr comp = castedComp; 3818#endif 3819 xmlNodePtr copy, oldInsert; 3820 3821 oldInsert = ctxt->insert; 3822 if (ctxt->insert != NULL) { 3823 switch (node->type) { 3824 case XML_TEXT_NODE: 3825 case XML_CDATA_SECTION_NODE: 3826 /* 3827 * This text comes from the stylesheet 3828 * For stylesheets, the set of whitespace-preserving 3829 * element names consists of just xsl:text. 3830 */ 3831#ifdef WITH_XSLT_DEBUG_PROCESS 3832 if (node->type == XML_CDATA_SECTION_NODE) { 3833 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3834 "xsltCopy: CDATA text %s\n", node->content)); 3835 } else { 3836 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3837 "xsltCopy: text %s\n", node->content)); 3838 } 3839#endif 3840 xsltCopyText(ctxt, ctxt->insert, node, 0); 3841 break; 3842 case XML_DOCUMENT_NODE: 3843 case XML_HTML_DOCUMENT_NODE: 3844 break; 3845 case XML_ELEMENT_NODE: 3846 /* 3847 * REVISIT NOTE: The "fake" is a doc-node, not an element node. 3848 * REMOVED: 3849 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) 3850 * return; 3851 */ 3852 3853#ifdef WITH_XSLT_DEBUG_PROCESS 3854 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3855 "xsltCopy: node %s\n", node->name)); 3856#endif 3857 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); 3858 ctxt->insert = copy; 3859 if (comp->use != NULL) { 3860 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 3861 } 3862 break; 3863 case XML_ATTRIBUTE_NODE: { 3864#ifdef WITH_XSLT_DEBUG_PROCESS 3865 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3866 "xsltCopy: attribute %s\n", node->name)); 3867#endif 3868 /* 3869 * REVISIT: We could also raise an error if the parent is not 3870 * an element node. 3871 * OPTIMIZE TODO: Can we set the value/children of the 3872 * attribute without an intermediate copy of the string value? 3873 */ 3874 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); 3875 break; 3876 } 3877 case XML_PI_NODE: 3878#ifdef WITH_XSLT_DEBUG_PROCESS 3879 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3880 "xsltCopy: PI %s\n", node->name)); 3881#endif 3882 copy = xmlNewDocPI(ctxt->insert->doc, node->name, 3883 node->content); 3884 copy = xsltAddChild(ctxt->insert, copy); 3885 break; 3886 case XML_COMMENT_NODE: 3887#ifdef WITH_XSLT_DEBUG_PROCESS 3888 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3889 "xsltCopy: comment\n")); 3890#endif 3891 copy = xmlNewComment(node->content); 3892 copy = xsltAddChild(ctxt->insert, copy); 3893 break; 3894 case XML_NAMESPACE_DECL: 3895#ifdef WITH_XSLT_DEBUG_PROCESS 3896 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3897 "xsltCopy: namespace declaration\n")); 3898#endif 3899 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); 3900 break; 3901 default: 3902 break; 3903 3904 } 3905 } 3906 3907 switch (node->type) { 3908 case XML_DOCUMENT_NODE: 3909 case XML_HTML_DOCUMENT_NODE: 3910 case XML_ELEMENT_NODE: 3911 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 3912 NULL); 3913 break; 3914 default: 3915 break; 3916 } 3917 ctxt->insert = oldInsert; 3918} 3919 3920/** 3921 * xsltText: 3922 * @ctxt: a XSLT process context 3923 * @node: the node in the source tree. 3924 * @inst: the xslt text node 3925 * @comp: precomputed information 3926 * 3927 * Process the xslt text node on the source node 3928 */ 3929void 3930xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, 3931 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { 3932 if ((inst->children != NULL) && (comp != NULL)) { 3933 xmlNodePtr text = inst->children; 3934 xmlNodePtr copy; 3935 3936 while (text != NULL) { 3937 if ((text->type != XML_TEXT_NODE) && 3938 (text->type != XML_CDATA_SECTION_NODE)) { 3939 xsltTransformError(ctxt, NULL, inst, 3940 "xsl:text content problem\n"); 3941 break; 3942 } 3943 copy = xmlNewDocText(ctxt->output, text->content); 3944 if (text->type != XML_CDATA_SECTION_NODE) { 3945#ifdef WITH_XSLT_DEBUG_PARSING 3946 xsltGenericDebug(xsltGenericDebugContext, 3947 "Disable escaping: %s\n", text->content); 3948#endif 3949 copy->name = xmlStringTextNoenc; 3950 } 3951 copy = xsltAddChild(ctxt->insert, copy); 3952 text = text->next; 3953 } 3954 } 3955} 3956 3957/** 3958 * xsltElement: 3959 * @ctxt: a XSLT process context 3960 * @node: the node in the source tree. 3961 * @inst: the xslt element node 3962 * @castedComp: precomputed information 3963 * 3964 * Process the xslt element node on the source node 3965 */ 3966void 3967xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, 3968 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 3969#ifdef XSLT_REFACTORED 3970 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; 3971#else 3972 xsltStylePreCompPtr comp = castedComp; 3973#endif 3974 xmlChar *prop = NULL; 3975 const xmlChar *name, *prefix = NULL, *nsName = NULL; 3976 xmlNodePtr copy; 3977 xmlNodePtr oldInsert; 3978 3979 if (ctxt->insert == NULL) 3980 return; 3981 3982 /* 3983 * A comp->has_name == 0 indicates that we need to skip this instruction, 3984 * since it was evaluated to be invalid already during compilation. 3985 */ 3986 if (!comp->has_name) 3987 return; 3988 3989 /* 3990 * stack and saves 3991 */ 3992 oldInsert = ctxt->insert; 3993 3994 if (comp->name == NULL) { 3995 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 3996 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3997 (const xmlChar *) "name", XSLT_NAMESPACE); 3998 if (prop == NULL) { 3999 xsltTransformError(ctxt, NULL, inst, 4000 "xsl:element: The attribute 'name' is missing.\n"); 4001 goto error; 4002 } 4003 if (xmlValidateQName(prop, 0)) { 4004 xsltTransformError(ctxt, NULL, inst, 4005 "xsl:element: The effective name '%s' is not a " 4006 "valid QName.\n", prop); 4007 /* we fall through to catch any further errors, if possible */ 4008 } 4009 name = xsltSplitQName(ctxt->dict, prop, &prefix); 4010 xmlFree(prop); 4011 } else { 4012 /* 4013 * The "name" value was static. 4014 */ 4015#ifdef XSLT_REFACTORED 4016 prefix = comp->nsPrefix; 4017 name = comp->name; 4018#else 4019 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 4020#endif 4021 } 4022 4023 /* 4024 * Create the new element 4025 */ 4026 if (ctxt->output->dict == ctxt->dict) { 4027 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); 4028 } else { 4029 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); 4030 } 4031 if (copy == NULL) { 4032 xsltTransformError(ctxt, NULL, inst, 4033 "xsl:element : creation of %s failed\n", name); 4034 return; 4035 } 4036 copy = xsltAddChild(ctxt->insert, copy); 4037 4038 /* 4039 * Namespace 4040 * --------- 4041 */ 4042 if (comp->has_ns) { 4043 if (comp->ns != NULL) { 4044 /* 4045 * No AVT; just plain text for the namespace name. 4046 */ 4047 if (comp->ns[0] != 0) 4048 nsName = comp->ns; 4049 } else { 4050 xmlChar *tmpNsName; 4051 /* 4052 * Eval the AVT. 4053 */ 4054 /* TODO: check attr acquisition wrt to the XSLT namespace */ 4055 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 4056 (const xmlChar *) "namespace", XSLT_NAMESPACE); 4057 /* 4058 * SPEC XSLT 1.0: 4059 * "If the string is empty, then the expanded-name of the 4060 * attribute has a null namespace URI." 4061 */ 4062 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 4063 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 4064 xmlFree(tmpNsName); 4065 } 4066 4067 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 4068 xsltTransformError(ctxt, NULL, inst, 4069 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 4070 "forbidden.\n"); 4071 goto error; 4072 } 4073 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 4074 prefix = BAD_CAST "xml"; 4075 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 4076 prefix = NULL; 4077 } 4078 } else { 4079 xmlNsPtr ns; 4080 /* 4081 * SPEC XSLT 1.0: 4082 * "If the namespace attribute is not present, then the QName is 4083 * expanded into an expanded-name using the namespace declarations 4084 * in effect for the xsl:element element, including any default 4085 * namespace declaration. 4086 */ 4087 ns = xmlSearchNs(inst->doc, inst, prefix); 4088 if (ns == NULL) { 4089 /* 4090 * TODO: Check this in the compilation layer in case it's a 4091 * static value. 4092 */ 4093 if (prefix != NULL) { 4094 xsltTransformError(ctxt, NULL, inst, 4095 "xsl:element: The QName '%s:%s' has no " 4096 "namespace binding in scope in the stylesheet; " 4097 "this is an error, since the namespace was not " 4098 "specified by the instruction itself.\n", prefix, name); 4099 } 4100 } else 4101 nsName = ns->href; 4102 } 4103 /* 4104 * Find/create a matching ns-decl in the result tree. 4105 */ 4106 if (nsName != NULL) { 4107 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { 4108 /* Don't use a prefix of "xmlns" */ 4109 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 4110 4111 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); 4112 4113 xmlFree(pref); 4114 } else { 4115 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, 4116 copy); 4117 } 4118 } else if ((copy->parent != NULL) && 4119 (copy->parent->type == XML_ELEMENT_NODE) && 4120 (copy->parent->ns != NULL)) 4121 { 4122 /* 4123 * "Undeclare" the default namespace. 4124 */ 4125 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); 4126 } 4127 4128 ctxt->insert = copy; 4129 4130 if (comp->has_use) { 4131 if (comp->use != NULL) { 4132 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 4133 } else { 4134 xmlChar *attrSets = NULL; 4135 /* 4136 * BUG TODO: use-attribute-sets is not a value template. 4137 * use-attribute-sets = qnames 4138 */ 4139 attrSets = xsltEvalAttrValueTemplate(ctxt, inst, 4140 (const xmlChar *)"use-attribute-sets", NULL); 4141 if (attrSets != NULL) { 4142 xsltApplyAttributeSet(ctxt, node, inst, attrSets); 4143 xmlFree(attrSets); 4144 } 4145 } 4146 } 4147 /* 4148 * Instantiate the sequence constructor. 4149 */ 4150 if (inst->children != NULL) 4151 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 4152 NULL); 4153 4154error: 4155 ctxt->insert = oldInsert; 4156 return; 4157} 4158 4159 4160/** 4161 * xsltComment: 4162 * @ctxt: a XSLT process context 4163 * @node: the node in the source tree. 4164 * @inst: the xslt comment node 4165 * @comp: precomputed information 4166 * 4167 * Process the xslt comment node on the source node 4168 */ 4169void 4170xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, 4171 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { 4172 xmlChar *value = NULL; 4173 xmlNodePtr commentNode; 4174 int len; 4175 4176 value = xsltEvalTemplateString(ctxt, node, inst); 4177 /* TODO: use or generate the compiled form */ 4178 len = xmlStrlen(value); 4179 if (len > 0) { 4180 if ((value[len-1] == '-') || 4181 (xmlStrstr(value, BAD_CAST "--"))) { 4182 xsltTransformError(ctxt, NULL, inst, 4183 "xsl:comment : '--' or ending '-' not allowed in comment\n"); 4184 /* fall through to try to catch further errors */ 4185 } 4186 } 4187#ifdef WITH_XSLT_DEBUG_PROCESS 4188 if (value == NULL) { 4189 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4190 "xsltComment: empty\n")); 4191 } else { 4192 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4193 "xsltComment: content %s\n", value)); 4194 } 4195#endif 4196 4197 commentNode = xmlNewComment(value); 4198 commentNode = xsltAddChild(ctxt->insert, commentNode); 4199 4200 if (value != NULL) 4201 xmlFree(value); 4202} 4203 4204/** 4205 * xsltProcessingInstruction: 4206 * @ctxt: a XSLT process context 4207 * @node: the node in the source tree. 4208 * @inst: the xslt processing-instruction node 4209 * @castedComp: precomputed information 4210 * 4211 * Process the xslt processing-instruction node on the source node 4212 */ 4213void 4214xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, 4215 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 4216#ifdef XSLT_REFACTORED 4217 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; 4218#else 4219 xsltStylePreCompPtr comp = castedComp; 4220#endif 4221 const xmlChar *name; 4222 xmlChar *value = NULL; 4223 xmlNodePtr pi; 4224 4225 4226 if (ctxt->insert == NULL) 4227 return; 4228 if (comp->has_name == 0) 4229 return; 4230 if (comp->name == NULL) { 4231 name = xsltEvalAttrValueTemplate(ctxt, inst, 4232 (const xmlChar *)"name", NULL); 4233 if (name == NULL) { 4234 xsltTransformError(ctxt, NULL, inst, 4235 "xsl:processing-instruction : name is missing\n"); 4236 goto error; 4237 } 4238 } else { 4239 name = comp->name; 4240 } 4241 /* TODO: check that it's both an an NCName and a PITarget. */ 4242 4243 4244 value = xsltEvalTemplateString(ctxt, node, inst); 4245 if (xmlStrstr(value, BAD_CAST "?>") != NULL) { 4246 xsltTransformError(ctxt, NULL, inst, 4247 "xsl:processing-instruction: '?>' not allowed within PI content\n"); 4248 goto error; 4249 } 4250#ifdef WITH_XSLT_DEBUG_PROCESS 4251 if (value == NULL) { 4252 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4253 "xsltProcessingInstruction: %s empty\n", name)); 4254 } else { 4255 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4256 "xsltProcessingInstruction: %s content %s\n", name, value)); 4257 } 4258#endif 4259 4260 pi = xmlNewDocPI(ctxt->insert->doc, name, value); 4261 pi = xsltAddChild(ctxt->insert, pi); 4262 4263error: 4264 if ((name != NULL) && (name != comp->name)) 4265 xmlFree((xmlChar *) name); 4266 if (value != NULL) 4267 xmlFree(value); 4268} 4269 4270/** 4271 * xsltCopyOf: 4272 * @ctxt: an XSLT transformation context 4273 * @node: the current node in the source tree 4274 * @inst: the element node of the XSLT copy-of instruction 4275 * @castedComp: precomputed information of the XSLT copy-of instruction 4276 * 4277 * Process the XSLT copy-of instruction. 4278 */ 4279void 4280xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4281 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 4282#ifdef XSLT_REFACTORED 4283 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; 4284#else 4285 xsltStylePreCompPtr comp = castedComp; 4286#endif 4287 xmlXPathObjectPtr res = NULL; 4288 xmlNodeSetPtr list = NULL; 4289 int i; 4290 xmlDocPtr oldXPContextDoc; 4291 xmlNsPtr *oldXPNamespaces; 4292 xmlNodePtr oldXPContextNode; 4293 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 4294 xmlXPathContextPtr xpctxt; 4295 4296 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4297 return; 4298 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4299 xsltTransformError(ctxt, NULL, inst, 4300 "xsl:copy-of : compilation failed\n"); 4301 return; 4302 } 4303 4304 /* 4305 * SPEC XSLT 1.0: 4306 * "The xsl:copy-of element can be used to insert a result tree 4307 * fragment into the result tree, without first converting it to 4308 * a string as xsl:value-of does (see [7.6.1 Generating Text with 4309 * xsl:value-of]). The required select attribute contains an 4310 * expression. When the result of evaluating the expression is a 4311 * result tree fragment, the complete fragment is copied into the 4312 * result tree. When the result is a node-set, all the nodes in the 4313 * set are copied in document order into the result tree; copying 4314 * an element node copies the attribute nodes, namespace nodes and 4315 * children of the element node as well as the element node itself; 4316 * a root node is copied by copying its children. When the result 4317 * is neither a node-set nor a result tree fragment, the result is 4318 * converted to a string and then inserted into the result tree, 4319 * as with xsl:value-of. 4320 */ 4321 4322#ifdef WITH_XSLT_DEBUG_PROCESS 4323 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4324 "xsltCopyOf: select %s\n", comp->select)); 4325#endif 4326 4327 /* 4328 * Evaluate the "select" expression. 4329 */ 4330 xpctxt = ctxt->xpathCtxt; 4331 oldXPContextDoc = xpctxt->doc; 4332 oldXPContextNode = xpctxt->node; 4333 oldXPProximityPosition = xpctxt->proximityPosition; 4334 oldXPContextSize = xpctxt->contextSize; 4335 oldXPNsNr = xpctxt->nsNr; 4336 oldXPNamespaces = xpctxt->namespaces; 4337 4338 xpctxt->node = node; 4339 if (comp != NULL) { 4340 4341#ifdef XSLT_REFACTORED 4342 if (comp->inScopeNs != NULL) { 4343 xpctxt->namespaces = comp->inScopeNs->list; 4344 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 4345 } else { 4346 xpctxt->namespaces = NULL; 4347 xpctxt->nsNr = 0; 4348 } 4349#else 4350 xpctxt->namespaces = comp->nsList; 4351 xpctxt->nsNr = comp->nsNr; 4352#endif 4353 } else { 4354 xpctxt->namespaces = NULL; 4355 xpctxt->nsNr = 0; 4356 } 4357 4358 res = xmlXPathCompiledEval(comp->comp, xpctxt); 4359 4360 xpctxt->doc = oldXPContextDoc; 4361 xpctxt->node = oldXPContextNode; 4362 xpctxt->contextSize = oldXPContextSize; 4363 xpctxt->proximityPosition = oldXPProximityPosition; 4364 xpctxt->nsNr = oldXPNsNr; 4365 xpctxt->namespaces = oldXPNamespaces; 4366 4367 if (res != NULL) { 4368 if (res->type == XPATH_NODESET) { 4369 /* 4370 * Node-set 4371 * -------- 4372 */ 4373#ifdef WITH_XSLT_DEBUG_PROCESS 4374 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4375 "xsltCopyOf: result is a node set\n")); 4376#endif 4377 list = res->nodesetval; 4378 if (list != NULL) { 4379 xmlNodePtr cur; 4380 /* 4381 * The list is already sorted in document order by XPath. 4382 * Append everything in this order under ctxt->insert. 4383 */ 4384 for (i = 0;i < list->nodeNr;i++) { 4385 cur = list->nodeTab[i]; 4386 if (cur == NULL) 4387 continue; 4388 if ((cur->type == XML_DOCUMENT_NODE) || 4389 (cur->type == XML_HTML_DOCUMENT_NODE)) 4390 { 4391 xsltCopyTreeList(ctxt, inst, 4392 cur->children, ctxt->insert, 0, 0); 4393 } else if (cur->type == XML_ATTRIBUTE_NODE) { 4394 xsltShallowCopyAttr(ctxt, inst, 4395 ctxt->insert, (xmlAttrPtr) cur); 4396 } else { 4397 xsltCopyTreeInternal(ctxt, inst, 4398 cur, ctxt->insert, 0, 0); 4399 } 4400 } 4401 } 4402 } else if (res->type == XPATH_XSLT_TREE) { 4403 /* 4404 * Result tree fragment 4405 * -------------------- 4406 * E.g. via <xsl:variable ...><foo/></xsl:variable> 4407 * Note that the root node of such trees is an xmlDocPtr in Libxslt. 4408 */ 4409#ifdef WITH_XSLT_DEBUG_PROCESS 4410 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4411 "xsltCopyOf: result is a result tree fragment\n")); 4412#endif 4413 list = res->nodesetval; 4414 if ((list != NULL) && (list->nodeTab != NULL) && 4415 (list->nodeTab[0] != NULL) && 4416 (IS_XSLT_REAL_NODE(list->nodeTab[0]))) 4417 { 4418 xsltCopyTreeList(ctxt, inst, 4419 list->nodeTab[0]->children, ctxt->insert, 0, 0); 4420 } 4421 } else { 4422 xmlChar *value = NULL; 4423 /* 4424 * Convert to a string. 4425 */ 4426 value = xmlXPathCastToString(res); 4427 if (value == NULL) { 4428 xsltTransformError(ctxt, NULL, inst, 4429 "Internal error in xsltCopyOf(): " 4430 "failed to cast an XPath object to string.\n"); 4431 ctxt->state = XSLT_STATE_STOPPED; 4432 } else { 4433 if (value[0] != 0) { 4434 /* 4435 * Append content as text node. 4436 */ 4437 xsltCopyTextString(ctxt, ctxt->insert, value, 0); 4438 } 4439 xmlFree(value); 4440 4441#ifdef WITH_XSLT_DEBUG_PROCESS 4442 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4443 "xsltCopyOf: result %s\n", res->stringval)); 4444#endif 4445 } 4446 } 4447 } else { 4448 ctxt->state = XSLT_STATE_STOPPED; 4449 } 4450 4451 if (res != NULL) 4452 xmlXPathFreeObject(res); 4453} 4454 4455/** 4456 * xsltValueOf: 4457 * @ctxt: a XSLT process context 4458 * @node: the node in the source tree. 4459 * @inst: the xslt value-of node 4460 * @castedComp: precomputed information 4461 * 4462 * Process the xslt value-of node on the source node 4463 */ 4464void 4465xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4466 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4467{ 4468#ifdef XSLT_REFACTORED 4469 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; 4470#else 4471 xsltStylePreCompPtr comp = castedComp; 4472#endif 4473 xmlXPathObjectPtr res = NULL; 4474 xmlChar *value = NULL; 4475 xmlDocPtr oldXPContextDoc; 4476 xmlNsPtr *oldXPNamespaces; 4477 xmlNodePtr oldXPContextNode; 4478 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 4479 xmlXPathContextPtr xpctxt; 4480 4481 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4482 return; 4483 4484 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4485 xsltTransformError(ctxt, NULL, inst, 4486 "Internal error in xsltValueOf(): " 4487 "The XSLT 'value-of' instruction was not compiled.\n"); 4488 return; 4489 } 4490 4491#ifdef WITH_XSLT_DEBUG_PROCESS 4492 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4493 "xsltValueOf: select %s\n", comp->select)); 4494#endif 4495 4496 xpctxt = ctxt->xpathCtxt; 4497 oldXPContextDoc = xpctxt->doc; 4498 oldXPContextNode = xpctxt->node; 4499 oldXPProximityPosition = xpctxt->proximityPosition; 4500 oldXPContextSize = xpctxt->contextSize; 4501 oldXPNsNr = xpctxt->nsNr; 4502 oldXPNamespaces = xpctxt->namespaces; 4503 4504 xpctxt->node = node; 4505 if (comp != NULL) { 4506 4507#ifdef XSLT_REFACTORED 4508 if (comp->inScopeNs != NULL) { 4509 xpctxt->namespaces = comp->inScopeNs->list; 4510 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 4511 } else { 4512 xpctxt->namespaces = NULL; 4513 xpctxt->nsNr = 0; 4514 } 4515#else 4516 xpctxt->namespaces = comp->nsList; 4517 xpctxt->nsNr = comp->nsNr; 4518#endif 4519 } else { 4520 xpctxt->namespaces = NULL; 4521 xpctxt->nsNr = 0; 4522 } 4523 4524 res = xmlXPathCompiledEval(comp->comp, xpctxt); 4525 4526 xpctxt->doc = oldXPContextDoc; 4527 xpctxt->node = oldXPContextNode; 4528 xpctxt->contextSize = oldXPContextSize; 4529 xpctxt->proximityPosition = oldXPProximityPosition; 4530 xpctxt->nsNr = oldXPNsNr; 4531 xpctxt->namespaces = oldXPNamespaces; 4532 4533 /* 4534 * Cast the XPath object to string. 4535 */ 4536 if (res != NULL) { 4537 value = xmlXPathCastToString(res); 4538 if (value == NULL) { 4539 xsltTransformError(ctxt, NULL, inst, 4540 "Internal error in xsltValueOf(): " 4541 "failed to cast an XPath object to string.\n"); 4542 ctxt->state = XSLT_STATE_STOPPED; 4543 goto error; 4544 } 4545 if (value[0] != 0) { 4546 xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); 4547 } 4548 } else { 4549 xsltTransformError(ctxt, NULL, inst, 4550 "XPath evaluation returned no result.\n"); 4551 ctxt->state = XSLT_STATE_STOPPED; 4552 goto error; 4553 } 4554 4555#ifdef WITH_XSLT_DEBUG_PROCESS 4556 if (value) { 4557 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4558 "xsltValueOf: result '%s'\n", value)); 4559 } 4560#endif 4561 4562error: 4563 if (value != NULL) 4564 xmlFree(value); 4565 if (res != NULL) 4566 xmlXPathFreeObject(res); 4567} 4568 4569/** 4570 * xsltNumber: 4571 * @ctxt: a XSLT process context 4572 * @node: the node in the source tree. 4573 * @inst: the xslt number node 4574 * @castedComp: precomputed information 4575 * 4576 * Process the xslt number node on the source node 4577 */ 4578void 4579xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, 4580 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4581{ 4582#ifdef XSLT_REFACTORED 4583 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; 4584#else 4585 xsltStylePreCompPtr comp = castedComp; 4586#endif 4587 if (comp == NULL) { 4588 xsltTransformError(ctxt, NULL, inst, 4589 "xsl:number : compilation failed\n"); 4590 return; 4591 } 4592 4593 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4594 return; 4595 4596 comp->numdata.doc = inst->doc; 4597 comp->numdata.node = inst; 4598 4599 xsltNumberFormat(ctxt, &comp->numdata, node); 4600} 4601 4602/** 4603 * xsltApplyImports: 4604 * @ctxt: an XSLT transformation context 4605 * @contextNode: the current node in the source tree. 4606 * @inst: the element node of the XSLT 'apply-imports' instruction 4607 * @comp: the compiled instruction 4608 * 4609 * Process the XSLT apply-imports element. 4610 */ 4611void 4612xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 4613 xmlNodePtr inst, 4614 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) 4615{ 4616 xsltTemplatePtr templ; 4617 4618 if ((ctxt == NULL) || (inst == NULL)) 4619 return; 4620 4621 if (comp == NULL) { 4622 xsltTransformError(ctxt, NULL, inst, 4623 "Internal error in xsltApplyImports(): " 4624 "The XSLT 'apply-imports' instruction was not compiled.\n"); 4625 return; 4626 } 4627 /* 4628 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the 4629 * same; the former is the "Current Template Rule" as defined by the 4630 * XSLT spec, the latter is simply the template struct being 4631 * currently processed. 4632 */ 4633 if (ctxt->currentTemplateRule == NULL) { 4634 /* 4635 * SPEC XSLT 2.0: 4636 * "[ERR XTDE0560] It is a non-recoverable dynamic error if 4637 * xsl:apply-imports or xsl:next-match is evaluated when the 4638 * current template rule is null." 4639 */ 4640 xsltTransformError(ctxt, NULL, inst, 4641 "It is an error to call 'apply-imports' " 4642 "when there's no current template rule.\n"); 4643 return; 4644 } 4645 /* 4646 * TODO: Check if this is correct. 4647 */ 4648 templ = xsltGetTemplate(ctxt, contextNode, 4649 ctxt->currentTemplateRule->style); 4650 4651 if (templ != NULL) { 4652 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; 4653 /* 4654 * Set the current template rule. 4655 */ 4656 ctxt->currentTemplateRule = templ; 4657 /* 4658 * URGENT TODO: Need xsl:with-param be handled somehow here? 4659 */ 4660 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, 4661 templ, NULL); 4662 4663 ctxt->currentTemplateRule = oldCurTemplRule; 4664 } 4665} 4666 4667/** 4668 * xsltCallTemplate: 4669 * @ctxt: a XSLT transformation context 4670 * @node: the "current node" in the source tree 4671 * @inst: the XSLT 'call-template' instruction 4672 * @castedComp: the compiled information of the instruction 4673 * 4674 * Processes the XSLT call-template instruction on the source node. 4675 */ 4676void 4677xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, 4678 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4679{ 4680#ifdef XSLT_REFACTORED 4681 xsltStyleItemCallTemplatePtr comp = 4682 (xsltStyleItemCallTemplatePtr) castedComp; 4683#else 4684 xsltStylePreCompPtr comp = castedComp; 4685#endif 4686 xsltStackElemPtr withParams = NULL; 4687 4688 if (ctxt->insert == NULL) 4689 return; 4690 if (comp == NULL) { 4691 xsltTransformError(ctxt, NULL, inst, 4692 "The XSLT 'call-template' instruction was not compiled.\n"); 4693 return; 4694 } 4695 4696 /* 4697 * The template must have been precomputed 4698 */ 4699 if (comp->templ == NULL) { 4700 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); 4701 if (comp->templ == NULL) { 4702 if (comp->ns != NULL) { 4703 xsltTransformError(ctxt, NULL, inst, 4704 "The called template '{%s}%s' was not found.\n", 4705 comp->ns, comp->name); 4706 } else { 4707 xsltTransformError(ctxt, NULL, inst, 4708 "The called template '%s' was not found.\n", 4709 comp->name); 4710 } 4711 return; 4712 } 4713 } 4714 4715#ifdef WITH_XSLT_DEBUG_PROCESS 4716 if ((comp != NULL) && (comp->name != NULL)) 4717 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4718 "call-template: name %s\n", comp->name)); 4719#endif 4720 4721 if (inst->children) { 4722 xmlNodePtr cur; 4723 xsltStackElemPtr param; 4724 4725 cur = inst->children; 4726 while (cur != NULL) { 4727#ifdef WITH_DEBUGGER 4728 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 4729 xslHandleDebugger(cur, node, comp->templ, ctxt); 4730#endif 4731 if (ctxt->state == XSLT_STATE_STOPPED) break; 4732 /* 4733 * TODO: The "with-param"s could be part of the "call-template" 4734 * structure. Avoid to "search" for params dynamically 4735 * in the XML tree every time. 4736 */ 4737 if (IS_XSLT_ELEM(cur)) { 4738 if (IS_XSLT_NAME(cur, "with-param")) { 4739 param = xsltParseStylesheetCallerParam(ctxt, cur); 4740 if (param != NULL) { 4741 param->next = withParams; 4742 withParams = param; 4743 } 4744 } else { 4745 xsltGenericError(xsltGenericErrorContext, 4746 "xsl:call-template: misplaced xsl:%s\n", cur->name); 4747 } 4748 } else { 4749 xsltGenericError(xsltGenericErrorContext, 4750 "xsl:call-template: misplaced %s element\n", cur->name); 4751 } 4752 cur = cur->next; 4753 } 4754 } 4755 /* 4756 * Create a new frame using the params first 4757 */ 4758 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, 4759 withParams); 4760 if (withParams != NULL) 4761 xsltFreeStackElemList(withParams); 4762 4763#ifdef WITH_XSLT_DEBUG_PROCESS 4764 if ((comp != NULL) && (comp->name != NULL)) 4765 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4766 "call-template returned: name %s\n", comp->name)); 4767#endif 4768} 4769 4770/** 4771 * xsltApplyTemplates: 4772 * @ctxt: a XSLT transformation context 4773 * @node: the 'current node' in the source tree 4774 * @inst: the element node of an XSLT 'apply-templates' instruction 4775 * @castedComp: the compiled instruction 4776 * 4777 * Processes the XSLT 'apply-templates' instruction on the current node. 4778 */ 4779void 4780xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, 4781 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4782{ 4783#ifdef XSLT_REFACTORED 4784 xsltStyleItemApplyTemplatesPtr comp = 4785 (xsltStyleItemApplyTemplatesPtr) castedComp; 4786#else 4787 xsltStylePreCompPtr comp = castedComp; 4788#endif 4789 int i; 4790 xmlNodePtr cur, delNode = NULL, oldContextNode; 4791 xmlNodeSetPtr list = NULL, oldList; 4792 xsltStackElemPtr withParams = NULL; 4793 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 4794 const xmlChar *oldMode, *oldModeURI; 4795 xmlDocPtr oldXPDoc; 4796 xsltDocumentPtr oldDocInfo; 4797 xmlXPathContextPtr xpctxt; 4798 xmlNsPtr *oldXPNamespaces; 4799 4800 if (comp == NULL) { 4801 xsltTransformError(ctxt, NULL, inst, 4802 "xsl:apply-templates : compilation failed\n"); 4803 return; 4804 } 4805 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4806 return; 4807 4808#ifdef WITH_XSLT_DEBUG_PROCESS 4809 if ((node != NULL) && (node->name != NULL)) 4810 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4811 "xsltApplyTemplates: node: '%s'\n", node->name)); 4812#endif 4813 4814 xpctxt = ctxt->xpathCtxt; 4815 /* 4816 * Save context states. 4817 */ 4818 oldContextNode = ctxt->node; 4819 oldMode = ctxt->mode; 4820 oldModeURI = ctxt->modeURI; 4821 oldDocInfo = ctxt->document; 4822 oldList = ctxt->nodeList; 4823 4824 /* 4825 * The xpath context size and proximity position, as 4826 * well as the xpath and context documents, may be changed 4827 * so we save their initial state and will restore on exit 4828 */ 4829 oldXPContextSize = xpctxt->contextSize; 4830 oldXPProximityPosition = xpctxt->proximityPosition; 4831 oldXPDoc = xpctxt->doc; 4832 oldXPNsNr = xpctxt->nsNr; 4833 oldXPNamespaces = xpctxt->namespaces; 4834 4835 /* 4836 * Set up contexts. 4837 */ 4838 ctxt->mode = comp->mode; 4839 ctxt->modeURI = comp->modeURI; 4840 4841 if (comp->select != NULL) { 4842 xmlXPathObjectPtr res = NULL; 4843 4844 if (comp->comp == NULL) { 4845 xsltTransformError(ctxt, NULL, inst, 4846 "xsl:apply-templates : compilation failed\n"); 4847 goto error; 4848 } 4849#ifdef WITH_XSLT_DEBUG_PROCESS 4850 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4851 "xsltApplyTemplates: select %s\n", comp->select)); 4852#endif 4853 4854 /* 4855 * Set up XPath. 4856 */ 4857 xpctxt->node = node; /* Set the "context node" */ 4858#ifdef XSLT_REFACTORED 4859 if (comp->inScopeNs != NULL) { 4860 xpctxt->namespaces = comp->inScopeNs->list; 4861 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 4862 } else { 4863 xpctxt->namespaces = NULL; 4864 xpctxt->nsNr = 0; 4865 } 4866#else 4867 xpctxt->namespaces = comp->nsList; 4868 xpctxt->nsNr = comp->nsNr; 4869#endif 4870 res = xmlXPathCompiledEval(comp->comp, xpctxt); 4871 4872 xpctxt->contextSize = oldXPContextSize; 4873 xpctxt->proximityPosition = oldXPProximityPosition; 4874 if (res != NULL) { 4875 if (res->type == XPATH_NODESET) { 4876 list = res->nodesetval; /* consume the node set */ 4877 res->nodesetval = NULL; 4878 } else { 4879 xsltTransformError(ctxt, NULL, inst, 4880 "The 'select' expression did not evaluate to a " 4881 "node set.\n"); 4882 ctxt->state = XSLT_STATE_STOPPED; 4883 xmlXPathFreeObject(res); 4884 goto error; 4885 } 4886 xmlXPathFreeObject(res); 4887 /* 4888 * Note: An xsl:apply-templates with a 'select' attribute, 4889 * can change the current source doc. 4890 */ 4891 } else { 4892 xsltTransformError(ctxt, NULL, inst, 4893 "Failed to evaluate the 'select' expression.\n"); 4894 ctxt->state = XSLT_STATE_STOPPED; 4895 goto error; 4896 } 4897 if (list == NULL) { 4898#ifdef WITH_XSLT_DEBUG_PROCESS 4899 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4900 "xsltApplyTemplates: select didn't evaluate to a node list\n")); 4901#endif 4902 goto exit; 4903 } 4904 /* 4905 * 4906 * NOTE: Previously a document info (xsltDocument) was 4907 * created and attached to the Result Tree Fragment. 4908 * But such a document info is created on demand in 4909 * xsltKeyFunction() (functions.c), so we need to create 4910 * it here beforehand. 4911 * In order to take care of potential keys we need to 4912 * do some extra work for the case when a Result Tree Fragment 4913 * is converted into a nodeset (e.g. exslt:node-set()) : 4914 * We attach a "pseudo-doc" (xsltDocument) to _private. 4915 * This xsltDocument, together with the keyset, will be freed 4916 * when the Result Tree Fragment is freed. 4917 * 4918 */ 4919#if 0 4920 if ((ctxt->nbKeys > 0) && 4921 (list->nodeNr != 0) && 4922 (list->nodeTab[0]->doc != NULL) && 4923 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) 4924 { 4925 /* 4926 * NOTE that it's also OK if @effectiveDocInfo will be 4927 * set to NULL. 4928 */ 4929 isRTF = 1; 4930 effectiveDocInfo = list->nodeTab[0]->doc->_private; 4931 } 4932#endif 4933 } else { 4934 /* 4935 * Build an XPath node set with the children 4936 */ 4937 list = xmlXPathNodeSetCreate(NULL); 4938 if (list == NULL) 4939 goto error; 4940 if (node->type != XML_NAMESPACE_DECL) 4941 cur = node->children; 4942 else 4943 cur = NULL; 4944 while (cur != NULL) { 4945 switch (cur->type) { 4946 case XML_TEXT_NODE: 4947 if ((IS_BLANK_NODE(cur)) && 4948 (cur->parent != NULL) && 4949 (cur->parent->type == XML_ELEMENT_NODE) && 4950 (ctxt->style->stripSpaces != NULL)) { 4951 const xmlChar *val; 4952 4953 if (cur->parent->ns != NULL) { 4954 val = (const xmlChar *) 4955 xmlHashLookup2(ctxt->style->stripSpaces, 4956 cur->parent->name, 4957 cur->parent->ns->href); 4958 if (val == NULL) { 4959 val = (const xmlChar *) 4960 xmlHashLookup2(ctxt->style->stripSpaces, 4961 BAD_CAST "*", 4962 cur->parent->ns->href); 4963 } 4964 } else { 4965 val = (const xmlChar *) 4966 xmlHashLookup2(ctxt->style->stripSpaces, 4967 cur->parent->name, NULL); 4968 } 4969 if ((val != NULL) && 4970 (xmlStrEqual(val, (xmlChar *) "strip"))) { 4971 delNode = cur; 4972 break; 4973 } 4974 } 4975 /* no break on purpose */ 4976 case XML_ELEMENT_NODE: 4977 case XML_DOCUMENT_NODE: 4978 case XML_HTML_DOCUMENT_NODE: 4979 case XML_CDATA_SECTION_NODE: 4980 case XML_PI_NODE: 4981 case XML_COMMENT_NODE: 4982 xmlXPathNodeSetAddUnique(list, cur); 4983 break; 4984 case XML_DTD_NODE: 4985 /* Unlink the DTD, it's still reachable 4986 * using doc->intSubset */ 4987 if (cur->next != NULL) 4988 cur->next->prev = cur->prev; 4989 if (cur->prev != NULL) 4990 cur->prev->next = cur->next; 4991 break; 4992 case XML_NAMESPACE_DECL: 4993 break; 4994 default: 4995#ifdef WITH_XSLT_DEBUG_PROCESS 4996 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4997 "xsltApplyTemplates: skipping cur type %d\n", 4998 cur->type)); 4999#endif 5000 delNode = cur; 5001 } 5002 cur = cur->next; 5003 if (delNode != NULL) { 5004#ifdef WITH_XSLT_DEBUG_PROCESS 5005 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5006 "xsltApplyTemplates: removing ignorable blank cur\n")); 5007#endif 5008 xmlUnlinkNode(delNode); 5009 xmlFreeNode(delNode); 5010 delNode = NULL; 5011 } 5012 } 5013 } 5014 5015#ifdef WITH_XSLT_DEBUG_PROCESS 5016 if (list != NULL) 5017 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5018 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); 5019#endif 5020 5021 if ((list == NULL) || (list->nodeNr == 0)) 5022 goto exit; 5023 5024 /* 5025 * Set the context's node set and size; this is also needed for 5026 * for xsltDoSortFunction(). 5027 */ 5028 ctxt->nodeList = list; 5029 /* 5030 * Process xsl:with-param and xsl:sort instructions. 5031 * (The code became so verbose just to avoid the 5032 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) 5033 * BUG TODO: We are not using namespaced potentially defined on the 5034 * xsl:sort or xsl:with-param elements; XPath expression might fail. 5035 */ 5036 if (inst->children) { 5037 xsltStackElemPtr param; 5038 5039 cur = inst->children; 5040 while (cur) { 5041 5042#ifdef WITH_DEBUGGER 5043 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5044 xslHandleDebugger(cur, node, NULL, ctxt); 5045#endif 5046 if (ctxt->state == XSLT_STATE_STOPPED) 5047 break; 5048 if (cur->type == XML_TEXT_NODE) { 5049 cur = cur->next; 5050 continue; 5051 } 5052 if (! IS_XSLT_ELEM(cur)) 5053 break; 5054 if (IS_XSLT_NAME(cur, "with-param")) { 5055 param = xsltParseStylesheetCallerParam(ctxt, cur); 5056 if (param != NULL) { 5057 param->next = withParams; 5058 withParams = param; 5059 } 5060 } 5061 if (IS_XSLT_NAME(cur, "sort")) { 5062 xsltTemplatePtr oldCurTempRule = 5063 ctxt->currentTemplateRule; 5064 int nbsorts = 0; 5065 xmlNodePtr sorts[XSLT_MAX_SORT]; 5066 5067 sorts[nbsorts++] = cur; 5068 5069 while (cur) { 5070 5071#ifdef WITH_DEBUGGER 5072 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5073 xslHandleDebugger(cur, node, NULL, ctxt); 5074#endif 5075 if (ctxt->state == XSLT_STATE_STOPPED) 5076 break; 5077 5078 if (cur->type == XML_TEXT_NODE) { 5079 cur = cur->next; 5080 continue; 5081 } 5082 5083 if (! IS_XSLT_ELEM(cur)) 5084 break; 5085 if (IS_XSLT_NAME(cur, "with-param")) { 5086 param = xsltParseStylesheetCallerParam(ctxt, cur); 5087 if (param != NULL) { 5088 param->next = withParams; 5089 withParams = param; 5090 } 5091 } 5092 if (IS_XSLT_NAME(cur, "sort")) { 5093 if (nbsorts >= XSLT_MAX_SORT) { 5094 xsltTransformError(ctxt, NULL, cur, 5095 "The number (%d) of xsl:sort instructions exceeds the " 5096 "maximum allowed by this processor's settings.\n", 5097 nbsorts); 5098 ctxt->state = XSLT_STATE_STOPPED; 5099 break; 5100 } else { 5101 sorts[nbsorts++] = cur; 5102 } 5103 } 5104 cur = cur->next; 5105 } 5106 /* 5107 * The "current template rule" is cleared for xsl:sort. 5108 */ 5109 ctxt->currentTemplateRule = NULL; 5110 /* 5111 * Sort. 5112 */ 5113 xsltDoSortFunction(ctxt, sorts, nbsorts); 5114 ctxt->currentTemplateRule = oldCurTempRule; 5115 break; 5116 } 5117 cur = cur->next; 5118 } 5119 } 5120 xpctxt->contextSize = list->nodeNr; 5121 /* 5122 * Apply templates for all selected source nodes. 5123 */ 5124 for (i = 0; i < list->nodeNr; i++) { 5125 cur = list->nodeTab[i]; 5126 /* 5127 * The node becomes the "current node". 5128 */ 5129 ctxt->node = cur; 5130 /* 5131 * An xsl:apply-templates can change the current context doc. 5132 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5133 */ 5134 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5135 xpctxt->doc = cur->doc; 5136 5137 xpctxt->proximityPosition = i + 1; 5138 /* 5139 * Find and apply a template for this node. 5140 */ 5141 xsltProcessOneNode(ctxt, cur, withParams); 5142 } 5143 5144exit: 5145error: 5146 /* 5147 * Free the parameter list. 5148 */ 5149 if (withParams != NULL) 5150 xsltFreeStackElemList(withParams); 5151 if (list != NULL) 5152 xmlXPathFreeNodeSet(list); 5153 /* 5154 * Restore context states. 5155 */ 5156 xpctxt->nsNr = oldXPNsNr; 5157 xpctxt->namespaces = oldXPNamespaces; 5158 xpctxt->doc = oldXPDoc; 5159 xpctxt->contextSize = oldXPContextSize; 5160 xpctxt->proximityPosition = oldXPProximityPosition; 5161 5162 ctxt->document = oldDocInfo; 5163 ctxt->nodeList = oldList; 5164 ctxt->node = oldContextNode; 5165 ctxt->mode = oldMode; 5166 ctxt->modeURI = oldModeURI; 5167} 5168 5169 5170/** 5171 * xsltChoose: 5172 * @ctxt: a XSLT process context 5173 * @contextNode: the current node in the source tree 5174 * @inst: the xsl:choose instruction 5175 * @comp: compiled information of the instruction 5176 * 5177 * Processes the xsl:choose instruction on the source node. 5178 */ 5179void 5180xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5181 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) 5182{ 5183 xmlNodePtr cur; 5184 5185 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5186 return; 5187 5188 /* 5189 * TODO: Content model checks should be done only at compilation 5190 * time. 5191 */ 5192 cur = inst->children; 5193 if (cur == NULL) { 5194 xsltTransformError(ctxt, NULL, inst, 5195 "xsl:choose: The instruction has no content.\n"); 5196 return; 5197 } 5198 5199#ifdef XSLT_REFACTORED 5200 /* 5201 * We don't check the content model during transformation. 5202 */ 5203#else 5204 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { 5205 xsltTransformError(ctxt, NULL, inst, 5206 "xsl:choose: xsl:when expected first\n"); 5207 return; 5208 } 5209#endif 5210 5211 { 5212 int testRes = 0, res = 0; 5213 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 5214 xmlDocPtr oldXPContextDoc = xpctxt->doc; 5215 int oldXPProximityPosition = xpctxt->proximityPosition; 5216 int oldXPContextSize = xpctxt->contextSize; 5217 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces; 5218 int oldXPNsNr = xpctxt->nsNr; 5219 5220#ifdef XSLT_REFACTORED 5221 xsltStyleItemWhenPtr wcomp = NULL; 5222#else 5223 xsltStylePreCompPtr wcomp = NULL; 5224#endif 5225 5226 /* 5227 * Process xsl:when --------------------------------------------------- 5228 */ 5229 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { 5230 wcomp = cur->psvi; 5231 5232 if ((wcomp == NULL) || (wcomp->test == NULL) || 5233 (wcomp->comp == NULL)) 5234 { 5235 xsltTransformError(ctxt, NULL, cur, 5236 "Internal error in xsltChoose(): " 5237 "The XSLT 'when' instruction was not compiled.\n"); 5238 goto error; 5239 } 5240 5241 5242#ifdef WITH_DEBUGGER 5243 if (xslDebugStatus != XSLT_DEBUG_NONE) { 5244 /* 5245 * TODO: Isn't comp->templ always NULL for xsl:choose? 5246 */ 5247 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5248 } 5249#endif 5250#ifdef WITH_XSLT_DEBUG_PROCESS 5251 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5252 "xsltChoose: test %s\n", wcomp->test)); 5253#endif 5254 5255 xpctxt->node = contextNode; 5256 xpctxt->doc = oldXPContextDoc; 5257 xpctxt->proximityPosition = oldXPProximityPosition; 5258 xpctxt->contextSize = oldXPContextSize; 5259 5260#ifdef XSLT_REFACTORED 5261 if (wcomp->inScopeNs != NULL) { 5262 xpctxt->namespaces = wcomp->inScopeNs->list; 5263 xpctxt->nsNr = wcomp->inScopeNs->xpathNumber; 5264 } else { 5265 xpctxt->namespaces = NULL; 5266 xpctxt->nsNr = 0; 5267 } 5268#else 5269 xpctxt->namespaces = wcomp->nsList; 5270 xpctxt->nsNr = wcomp->nsNr; 5271#endif 5272 5273 5274#ifdef XSLT_FAST_IF 5275 res = xmlXPathCompiledEvalToBoolean(wcomp->comp, xpctxt); 5276 5277 if (res == -1) { 5278 ctxt->state = XSLT_STATE_STOPPED; 5279 goto error; 5280 } 5281 testRes = (res == 1) ? 1 : 0; 5282 5283#else /* XSLT_FAST_IF */ 5284 5285 res = xmlXPathCompiledEval(wcomp->comp, xpctxt); 5286 5287 if (res != NULL) { 5288 if (res->type != XPATH_BOOLEAN) 5289 res = xmlXPathConvertBoolean(res); 5290 if (res->type == XPATH_BOOLEAN) 5291 testRes = res->boolval; 5292 else { 5293#ifdef WITH_XSLT_DEBUG_PROCESS 5294 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5295 "xsltChoose: test didn't evaluate to a boolean\n")); 5296#endif 5297 goto error; 5298 } 5299 xmlXPathFreeObject(res); 5300 res = NULL; 5301 } else { 5302 ctxt->state = XSLT_STATE_STOPPED; 5303 goto error; 5304 } 5305 5306#endif /* else of XSLT_FAST_IF */ 5307 5308#ifdef WITH_XSLT_DEBUG_PROCESS 5309 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5310 "xsltChoose: test evaluate to %d\n", testRes)); 5311#endif 5312 if (testRes) 5313 goto test_is_true; 5314 5315 cur = cur->next; 5316 } 5317 5318 /* 5319 * Process xsl:otherwise ---------------------------------------------- 5320 */ 5321 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { 5322 5323#ifdef WITH_DEBUGGER 5324 if (xslDebugStatus != XSLT_DEBUG_NONE) 5325 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5326#endif 5327 5328#ifdef WITH_XSLT_DEBUG_PROCESS 5329 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5330 "evaluating xsl:otherwise\n")); 5331#endif 5332 goto test_is_true; 5333 } 5334 xpctxt->node = contextNode; 5335 xpctxt->doc = oldXPContextDoc; 5336 xpctxt->proximityPosition = oldXPProximityPosition; 5337 xpctxt->contextSize = oldXPContextSize; 5338 xpctxt->namespaces = oldXPNamespaces; 5339 xpctxt->nsNr = oldXPNsNr; 5340 goto exit; 5341 5342test_is_true: 5343 5344 xpctxt->node = contextNode; 5345 xpctxt->doc = oldXPContextDoc; 5346 xpctxt->proximityPosition = oldXPProximityPosition; 5347 xpctxt->contextSize = oldXPContextSize; 5348 xpctxt->namespaces = oldXPNamespaces; 5349 xpctxt->nsNr = oldXPNsNr; 5350 goto process_sequence; 5351 } 5352 5353process_sequence: 5354 5355 /* 5356 * Instantiate the sequence constructor. 5357 */ 5358 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, 5359 NULL); 5360 5361exit: 5362error: 5363 return; 5364} 5365 5366/** 5367 * xsltIf: 5368 * @ctxt: a XSLT process context 5369 * @contextNode: the current node in the source tree 5370 * @inst: the xsl:if instruction 5371 * @castedComp: compiled information of the instruction 5372 * 5373 * Processes the xsl:if instruction on the source node. 5374 */ 5375void 5376xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5377 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 5378{ 5379 int res = 0; 5380 5381#ifdef XSLT_REFACTORED 5382 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; 5383#else 5384 xsltStylePreCompPtr comp = castedComp; 5385#endif 5386 5387 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5388 return; 5389 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { 5390 xsltTransformError(ctxt, NULL, inst, 5391 "Internal error in xsltIf(): " 5392 "The XSLT 'if' instruction was not compiled.\n"); 5393 return; 5394 } 5395 5396#ifdef WITH_XSLT_DEBUG_PROCESS 5397 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5398 "xsltIf: test %s\n", comp->test)); 5399#endif 5400 5401#ifdef XSLT_FAST_IF 5402 { 5403 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 5404 xmlDocPtr oldXPContextDoc = xpctxt->doc; 5405 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces; 5406 xmlNodePtr oldXPContextNode = xpctxt->node; 5407 int oldXPProximityPosition = xpctxt->proximityPosition; 5408 int oldXPContextSize = xpctxt->contextSize; 5409 int oldXPNsNr = xpctxt->nsNr; 5410 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; 5411 5412 xpctxt->node = contextNode; 5413 if (comp != NULL) { 5414 5415#ifdef XSLT_REFACTORED 5416 if (comp->inScopeNs != NULL) { 5417 xpctxt->namespaces = comp->inScopeNs->list; 5418 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 5419 } else { 5420 xpctxt->namespaces = NULL; 5421 xpctxt->nsNr = 0; 5422 } 5423#else 5424 xpctxt->namespaces = comp->nsList; 5425 xpctxt->nsNr = comp->nsNr; 5426#endif 5427 } else { 5428 xpctxt->namespaces = NULL; 5429 xpctxt->nsNr = 0; 5430 } 5431 /* 5432 * This XPath function is optimized for boolean results. 5433 */ 5434 res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt); 5435 5436 /* 5437 * Cleanup fragments created during evaluation of the 5438 * "select" expression. 5439 */ 5440 if (oldLocalFragmentTop != ctxt->localRVT) 5441 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 5442 5443 xpctxt->doc = oldXPContextDoc; 5444 xpctxt->node = oldXPContextNode; 5445 xpctxt->contextSize = oldXPContextSize; 5446 xpctxt->proximityPosition = oldXPProximityPosition; 5447 xpctxt->nsNr = oldXPNsNr; 5448 xpctxt->namespaces = oldXPNamespaces; 5449 } 5450 5451#ifdef WITH_XSLT_DEBUG_PROCESS 5452 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5453 "xsltIf: test evaluate to %d\n", res)); 5454#endif 5455 5456 if (res == -1) { 5457 ctxt->state = XSLT_STATE_STOPPED; 5458 goto error; 5459 } 5460 if (res == 1) { 5461 /* 5462 * Instantiate the sequence constructor of xsl:if. 5463 */ 5464 xsltApplySequenceConstructor(ctxt, 5465 contextNode, inst->children, NULL); 5466 } 5467 5468#else /* XSLT_FAST_IF */ 5469 { 5470 xmlXPathObjectPtr xpobj = NULL; 5471 /* 5472 * OLD CODE: 5473 */ 5474 { 5475 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 5476 xmlDocPtr oldXPContextDoc = xpctxt->doc; 5477 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces; 5478 xmlNodePtr oldXPContextNode = xpctxt->node; 5479 int oldXPProximityPosition = xpctxt->proximityPosition; 5480 int oldXPContextSize = xpctxt->contextSize; 5481 int oldXPNsNr = xpctxt->nsNr; 5482 5483 xpctxt->node = contextNode; 5484 if (comp != NULL) { 5485 5486#ifdef XSLT_REFACTORED 5487 if (comp->inScopeNs != NULL) { 5488 xpctxt->namespaces = comp->inScopeNs->list; 5489 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 5490 } else { 5491 xpctxt->namespaces = NULL; 5492 xpctxt->nsNr = 0; 5493 } 5494#else 5495 xpctxt->namespaces = comp->nsList; 5496 xpctxt->nsNr = comp->nsNr; 5497#endif 5498 } else { 5499 xpctxt->namespaces = NULL; 5500 xpctxt->nsNr = 0; 5501 } 5502 5503 /* 5504 * This XPath function is optimized for boolean results. 5505 */ 5506 xpobj = xmlXPathCompiledEval(comp->comp, xpctxt); 5507 5508 xpctxt->doc = oldXPContextDoc; 5509 xpctxt->node = oldXPContextNode; 5510 xpctxt->contextSize = oldXPContextSize; 5511 xpctxt->proximityPosition = oldXPProximityPosition; 5512 xpctxt->nsNr = oldXPNsNr; 5513 xpctxt->namespaces = oldXPNamespaces; 5514 } 5515 if (xpobj != NULL) { 5516 if (xpobj->type != XPATH_BOOLEAN) 5517 xpobj = xmlXPathConvertBoolean(xpobj); 5518 if (xpobj->type == XPATH_BOOLEAN) { 5519 res = xpobj->boolval; 5520 5521#ifdef WITH_XSLT_DEBUG_PROCESS 5522 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5523 "xsltIf: test evaluate to %d\n", res)); 5524#endif 5525 if (res) { 5526 xsltApplySequenceConstructor(ctxt, 5527 contextNode, inst->children, NULL); 5528 } 5529 } else { 5530 5531#ifdef WITH_XSLT_DEBUG_PROCESS 5532 XSLT_TRACE(ctxt, XSLT_TRACE_IF, 5533 xsltGenericDebug(xsltGenericDebugContext, 5534 "xsltIf: test didn't evaluate to a boolean\n")); 5535#endif 5536 ctxt->state = XSLT_STATE_STOPPED; 5537 } 5538 xmlXPathFreeObject(xpobj); 5539 } else { 5540 ctxt->state = XSLT_STATE_STOPPED; 5541 } 5542 } 5543#endif /* else of XSLT_FAST_IF */ 5544 5545error: 5546 return; 5547} 5548 5549/** 5550 * xsltForEach: 5551 * @ctxt: an XSLT transformation context 5552 * @contextNode: the "current node" in the source tree 5553 * @inst: the element node of the xsl:for-each instruction 5554 * @castedComp: the compiled information of the instruction 5555 * 5556 * Process the xslt for-each node on the source node 5557 */ 5558void 5559xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5560 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 5561{ 5562#ifdef XSLT_REFACTORED 5563 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; 5564#else 5565 xsltStylePreCompPtr comp = castedComp; 5566#endif 5567 int i; 5568 xmlXPathObjectPtr res = NULL; 5569 xmlNodePtr cur, curInst; 5570 xmlNodeSetPtr list = NULL; 5571 xmlNodeSetPtr oldList; 5572 int oldXPProximityPosition, oldXPContextSize; 5573 xmlNodePtr oldContextNode; 5574 xsltTemplatePtr oldCurTemplRule; 5575 xmlDocPtr oldXPDoc; 5576 xsltDocumentPtr oldDocInfo; 5577 xmlXPathContextPtr xpctxt; 5578 5579 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { 5580 xsltGenericError(xsltGenericErrorContext, 5581 "xsltForEach(): Bad arguments.\n"); 5582 return; 5583 } 5584 5585 if (comp == NULL) { 5586 xsltTransformError(ctxt, NULL, inst, 5587 "Internal error in xsltForEach(): " 5588 "The XSLT 'for-each' instruction was not compiled.\n"); 5589 return; 5590 } 5591 if ((comp->select == NULL) || (comp->comp == NULL)) { 5592 xsltTransformError(ctxt, NULL, inst, 5593 "Internal error in xsltForEach(): " 5594 "The selecting expression of the XSLT 'for-each' " 5595 "instruction was not compiled correctly.\n"); 5596 return; 5597 } 5598 xpctxt = ctxt->xpathCtxt; 5599 5600#ifdef WITH_XSLT_DEBUG_PROCESS 5601 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5602 "xsltForEach: select %s\n", comp->select)); 5603#endif 5604 5605 /* 5606 * Save context states. 5607 */ 5608 oldDocInfo = ctxt->document; 5609 oldList = ctxt->nodeList; 5610 oldContextNode = ctxt->node; 5611 /* 5612 * The "current template rule" is cleared for the instantiation of 5613 * xsl:for-each. 5614 */ 5615 oldCurTemplRule = ctxt->currentTemplateRule; 5616 ctxt->currentTemplateRule = NULL; 5617 5618 oldXPDoc = xpctxt->doc; 5619 oldXPProximityPosition = xpctxt->proximityPosition; 5620 oldXPContextSize = xpctxt->contextSize; 5621 /* 5622 * Set up XPath. 5623 */ 5624 xpctxt->node = contextNode; 5625#ifdef XSLT_REFACTORED 5626 if (comp->inScopeNs != NULL) { 5627 xpctxt->namespaces = comp->inScopeNs->list; 5628 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 5629 } else { 5630 xpctxt->namespaces = NULL; 5631 xpctxt->nsNr = 0; 5632 } 5633#else 5634 xpctxt->namespaces = comp->nsList; 5635 xpctxt->nsNr = comp->nsNr; 5636#endif 5637 5638 /* 5639 * Evaluate the 'select' expression. 5640 */ 5641 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); 5642 5643 if (res != NULL) { 5644 if (res->type == XPATH_NODESET) 5645 list = res->nodesetval; 5646 else { 5647 xsltTransformError(ctxt, NULL, inst, 5648 "The 'select' expression does not evaluate to a node set.\n"); 5649 5650#ifdef WITH_XSLT_DEBUG_PROCESS 5651 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5652 "xsltForEach: select didn't evaluate to a node list\n")); 5653#endif 5654 goto error; 5655 } 5656 } else { 5657 xsltTransformError(ctxt, NULL, inst, 5658 "Failed to evaluate the 'select' expression.\n"); 5659 ctxt->state = XSLT_STATE_STOPPED; 5660 goto error; 5661 } 5662 5663 if ((list == NULL) || (list->nodeNr <= 0)) 5664 goto exit; 5665 5666#ifdef WITH_XSLT_DEBUG_PROCESS 5667 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5668 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); 5669#endif 5670 5671 /* 5672 * Restore XPath states for the "current node". 5673 */ 5674 xpctxt->contextSize = oldXPContextSize; 5675 xpctxt->proximityPosition = oldXPProximityPosition; 5676 xpctxt->node = contextNode; 5677 5678 /* 5679 * Set the list; this has to be done already here for xsltDoSortFunction(). 5680 */ 5681 ctxt->nodeList = list; 5682 /* 5683 * Handle xsl:sort instructions and skip them for further processing. 5684 * BUG TODO: We are not using namespaced potentially defined on the 5685 * xsl:sort element; XPath expression might fail. 5686 */ 5687 curInst = inst->children; 5688 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5689 int nbsorts = 0; 5690 xmlNodePtr sorts[XSLT_MAX_SORT]; 5691 5692 sorts[nbsorts++] = curInst; 5693 5694#ifdef WITH_DEBUGGER 5695 if (xslDebugStatus != XSLT_DEBUG_NONE) 5696 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5697#endif 5698 5699 curInst = curInst->next; 5700 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5701 if (nbsorts >= XSLT_MAX_SORT) { 5702 xsltTransformError(ctxt, NULL, curInst, 5703 "The number of xsl:sort instructions exceeds the " 5704 "maximum (%d) allowed by this processor.\n", 5705 XSLT_MAX_SORT); 5706 goto error; 5707 } else { 5708 sorts[nbsorts++] = curInst; 5709 } 5710 5711#ifdef WITH_DEBUGGER 5712 if (xslDebugStatus != XSLT_DEBUG_NONE) 5713 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5714#endif 5715 curInst = curInst->next; 5716 } 5717 xsltDoSortFunction(ctxt, sorts, nbsorts); 5718 } 5719 xpctxt->contextSize = list->nodeNr; 5720 /* 5721 * Instantiate the sequence constructor for each selected node. 5722 */ 5723 for (i = 0; i < list->nodeNr; i++) { 5724 cur = list->nodeTab[i]; 5725 /* 5726 * The selected node becomes the "current node". 5727 */ 5728 ctxt->node = cur; 5729 /* 5730 * An xsl:for-each can change the current context doc. 5731 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5732 */ 5733 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5734 xpctxt->doc = cur->doc; 5735 5736 xpctxt->proximityPosition = i + 1; 5737 5738 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); 5739 } 5740 5741exit: 5742error: 5743 if (res != NULL) 5744 xmlXPathFreeObject(res); 5745 /* 5746 * Restore old states. 5747 */ 5748 ctxt->document = oldDocInfo; 5749 ctxt->nodeList = oldList; 5750 ctxt->node = oldContextNode; 5751 ctxt->currentTemplateRule = oldCurTemplRule; 5752 5753 xpctxt->doc = oldXPDoc; 5754 xpctxt->contextSize = oldXPContextSize; 5755 xpctxt->proximityPosition = oldXPProximityPosition; 5756} 5757 5758/************************************************************************ 5759 * * 5760 * Generic interface * 5761 * * 5762 ************************************************************************/ 5763 5764#ifdef XSLT_GENERATE_HTML_DOCTYPE 5765typedef struct xsltHTMLVersion { 5766 const char *version; 5767 const char *public; 5768 const char *system; 5769} xsltHTMLVersion; 5770 5771static xsltHTMLVersion xsltHTMLVersions[] = { 5772 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5773 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, 5774 { "4.01strict", "-//W3C//DTD HTML 4.01//EN", 5775 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, 5776 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5777 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5778 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", 5779 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5780 { "4.0strict", "-//W3C//DTD HTML 4.01//EN", 5781 "http://www.w3.org/TR/html4/strict.dtd"}, 5782 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5783 "http://www.w3.org/TR/html4/loose.dtd"}, 5784 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5785 "http://www.w3.org/TR/html4/frameset.dtd"}, 5786 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", 5787 "http://www.w3.org/TR/html4/loose.dtd"}, 5788 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } 5789}; 5790 5791/** 5792 * xsltGetHTMLIDs: 5793 * @version: the version string 5794 * @publicID: used to return the public ID 5795 * @systemID: used to return the system ID 5796 * 5797 * Returns -1 if not found, 0 otherwise and the system and public 5798 * Identifier for this given verion of HTML 5799 */ 5800static int 5801xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, 5802 const xmlChar **systemID) { 5803 unsigned int i; 5804 if (version == NULL) 5805 return(-1); 5806 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); 5807 i++) { 5808 if (!xmlStrcasecmp(version, 5809 (const xmlChar *) xsltHTMLVersions[i].version)) { 5810 if (publicID != NULL) 5811 *publicID = (const xmlChar *) xsltHTMLVersions[i].public; 5812 if (systemID != NULL) 5813 *systemID = (const xmlChar *) xsltHTMLVersions[i].system; 5814 return(0); 5815 } 5816 } 5817 return(-1); 5818} 5819#endif 5820 5821/** 5822 * xsltApplyStripSpaces: 5823 * @ctxt: a XSLT process context 5824 * @node: the root of the XML tree 5825 * 5826 * Strip the unwanted ignorable spaces from the input tree 5827 */ 5828void 5829xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { 5830 xmlNodePtr current; 5831#ifdef WITH_XSLT_DEBUG_PROCESS 5832 int nb = 0; 5833#endif 5834 5835 5836 current = node; 5837 while (current != NULL) { 5838 /* 5839 * Cleanup children empty nodes if asked for 5840 */ 5841 if ((IS_XSLT_REAL_NODE(current)) && 5842 (current->children != NULL) && 5843 (xsltFindElemSpaceHandling(ctxt, current))) { 5844 xmlNodePtr delete = NULL, cur = current->children; 5845 5846 while (cur != NULL) { 5847 if (IS_BLANK_NODE(cur)) 5848 delete = cur; 5849 5850 cur = cur->next; 5851 if (delete != NULL) { 5852 xmlUnlinkNode(delete); 5853 xmlFreeNode(delete); 5854 delete = NULL; 5855#ifdef WITH_XSLT_DEBUG_PROCESS 5856 nb++; 5857#endif 5858 } 5859 } 5860 } 5861 5862 /* 5863 * Skip to next node in document order. 5864 */ 5865 if (node->type == XML_ENTITY_REF_NODE) { 5866 /* process deep in entities */ 5867 xsltApplyStripSpaces(ctxt, node->children); 5868 } 5869 if ((current->children != NULL) && 5870 (current->type != XML_ENTITY_REF_NODE)) { 5871 current = current->children; 5872 } else if (current->next != NULL) { 5873 current = current->next; 5874 } else { 5875 do { 5876 current = current->parent; 5877 if (current == NULL) 5878 break; 5879 if (current == node) 5880 goto done; 5881 if (current->next != NULL) { 5882 current = current->next; 5883 break; 5884 } 5885 } while (current != NULL); 5886 } 5887 } 5888 5889done: 5890#ifdef WITH_XSLT_DEBUG_PROCESS 5891 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext, 5892 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); 5893#endif 5894 return; 5895} 5896 5897static int 5898xsltCountKeys(xsltTransformContextPtr ctxt) 5899{ 5900 xsltStylesheetPtr style; 5901 xsltKeyDefPtr keyd; 5902 5903 if (ctxt == NULL) 5904 return(-1); 5905 5906 /* 5907 * Do we have those nastly templates with a key() in the match pattern? 5908 */ 5909 ctxt->hasTemplKeyPatterns = 0; 5910 style = ctxt->style; 5911 while (style != NULL) { 5912 if (style->keyMatch != NULL) { 5913 ctxt->hasTemplKeyPatterns = 1; 5914 break; 5915 } 5916 style = xsltNextImport(style); 5917 } 5918 /* 5919 * Count number of key declarations. 5920 */ 5921 ctxt->nbKeys = 0; 5922 style = ctxt->style; 5923 while (style != NULL) { 5924 keyd = style->keys; 5925 while (keyd) { 5926 ctxt->nbKeys++; 5927 keyd = keyd->next; 5928 } 5929 style = xsltNextImport(style); 5930 } 5931 return(ctxt->nbKeys); 5932} 5933 5934/** 5935 * xsltApplyStylesheetInternal: 5936 * @style: a parsed XSLT stylesheet 5937 * @doc: a parsed XML document 5938 * @params: a NULL terminated array of parameters names/values tuples 5939 * @output: the targetted output 5940 * @profile: profile FILE * output or NULL 5941 * @user: user provided parameter 5942 * 5943 * Apply the stylesheet to the document 5944 * NOTE: This may lead to a non-wellformed output XML wise ! 5945 * 5946 * Returns the result document or NULL in case of error 5947 */ 5948static xmlDocPtr 5949xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, 5950 const char **params, const char *output, 5951 FILE * profile, xsltTransformContextPtr userCtxt) 5952{ 5953 xmlDocPtr res = NULL; 5954 xsltTransformContextPtr ctxt = NULL; 5955 xmlNodePtr root, node; 5956 const xmlChar *method; 5957 const xmlChar *doctypePublic; 5958 const xmlChar *doctypeSystem; 5959 const xmlChar *version; 5960 const xmlChar *encoding; 5961 xsltStackElemPtr variables; 5962 xsltStackElemPtr vptr; 5963 5964 xsltInitGlobals(); 5965 5966 if ((style == NULL) || (doc == NULL)) 5967 return (NULL); 5968 5969 if (style->internalized == 0) { 5970#ifdef WITH_XSLT_DEBUG 5971 xsltGenericDebug(xsltGenericDebugContext, 5972 "Stylesheet was not fully internalized !\n"); 5973#endif 5974 } 5975 if (doc->intSubset != NULL) { 5976 /* 5977 * Avoid hitting the DTD when scanning nodes 5978 * but keep it linked as doc->intSubset 5979 */ 5980 xmlNodePtr cur = (xmlNodePtr) doc->intSubset; 5981 if (cur->next != NULL) 5982 cur->next->prev = cur->prev; 5983 if (cur->prev != NULL) 5984 cur->prev->next = cur->next; 5985 if (doc->children == cur) 5986 doc->children = cur->next; 5987 if (doc->last == cur) 5988 doc->last = cur->prev; 5989 cur->prev = cur->next = NULL; 5990 } 5991 5992 /* 5993 * Check for XPath document order availability 5994 */ 5995 root = xmlDocGetRootElement(doc); 5996 if (root != NULL) { 5997 if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE)) 5998 xmlXPathOrderDocElems(doc); 5999 } 6000 6001 if (userCtxt != NULL) 6002 ctxt = userCtxt; 6003 else 6004 ctxt = xsltNewTransformContext(style, doc); 6005 6006 if (ctxt == NULL) 6007 return (NULL); 6008 6009 ctxt->initialContextDoc = doc; 6010 ctxt->initialContextNode = (xmlNodePtr) doc; 6011 6012 if (profile != NULL) 6013 ctxt->profile = 1; 6014 6015 if (output != NULL) 6016 ctxt->outputFile = output; 6017 else 6018 ctxt->outputFile = NULL; 6019 6020 /* 6021 * internalize the modes if needed 6022 */ 6023 if (ctxt->dict != NULL) { 6024 if (ctxt->mode != NULL) 6025 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); 6026 if (ctxt->modeURI != NULL) 6027 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); 6028 } 6029 6030 XSLT_GET_IMPORT_PTR(method, style, method) 6031 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 6032 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 6033 XSLT_GET_IMPORT_PTR(version, style, version) 6034 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6035 6036 if ((method != NULL) && 6037 (!xmlStrEqual(method, (const xmlChar *) "xml"))) 6038 { 6039 if (xmlStrEqual(method, (const xmlChar *) "html")) { 6040 ctxt->type = XSLT_OUTPUT_HTML; 6041 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6042 res = htmlNewDoc(doctypeSystem, doctypePublic); 6043 } else { 6044 if (version == NULL) { 6045 xmlDtdPtr dtd; 6046 6047 res = htmlNewDoc(NULL, NULL); 6048 /* 6049 * Make sure no DTD node is generated in this case 6050 */ 6051 if (res != NULL) { 6052 dtd = xmlGetIntSubset(res); 6053 if (dtd != NULL) { 6054 xmlUnlinkNode((xmlNodePtr) dtd); 6055 xmlFreeDtd(dtd); 6056 } 6057 res->intSubset = NULL; 6058 res->extSubset = NULL; 6059 } 6060 } else { 6061 6062#ifdef XSLT_GENERATE_HTML_DOCTYPE 6063 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 6064#endif 6065 res = htmlNewDoc(doctypeSystem, doctypePublic); 6066 } 6067 } 6068 if (res == NULL) 6069 goto error; 6070 res->dict = ctxt->dict; 6071 xmlDictReference(res->dict); 6072 6073#ifdef WITH_XSLT_DEBUG 6074 xsltGenericDebug(xsltGenericDebugContext, 6075 "reusing transformation dict for output\n"); 6076#endif 6077 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 6078 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 6079 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n", 6080 style->method); 6081 ctxt->type = XSLT_OUTPUT_HTML; 6082 res = htmlNewDoc(doctypeSystem, doctypePublic); 6083 if (res == NULL) 6084 goto error; 6085 res->dict = ctxt->dict; 6086 xmlDictReference(res->dict); 6087 6088#ifdef WITH_XSLT_DEBUG 6089 xsltGenericDebug(xsltGenericDebugContext, 6090 "reusing transformation dict for output\n"); 6091#endif 6092 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 6093 ctxt->type = XSLT_OUTPUT_TEXT; 6094 res = xmlNewDoc(style->version); 6095 if (res == NULL) 6096 goto error; 6097 res->dict = ctxt->dict; 6098 xmlDictReference(res->dict); 6099 6100#ifdef WITH_XSLT_DEBUG 6101 xsltGenericDebug(xsltGenericDebugContext, 6102 "reusing transformation dict for output\n"); 6103#endif 6104 } else { 6105 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 6106 "xsltApplyStylesheetInternal: unsupported method %s\n", 6107 style->method); 6108 goto error; 6109 } 6110 } else { 6111 ctxt->type = XSLT_OUTPUT_XML; 6112 res = xmlNewDoc(style->version); 6113 if (res == NULL) 6114 goto error; 6115 res->dict = ctxt->dict; 6116 xmlDictReference(ctxt->dict); 6117#ifdef WITH_XSLT_DEBUG 6118 xsltGenericDebug(xsltGenericDebugContext, 6119 "reusing transformation dict for output\n"); 6120#endif 6121 } 6122 res->charset = XML_CHAR_ENCODING_UTF8; 6123 if (encoding != NULL) 6124 res->encoding = xmlStrdup(encoding); 6125 variables = style->variables; 6126 6127 /* 6128 * Start the evaluation, evaluate the params, the stylesheets globals 6129 * and start by processing the top node. 6130 */ 6131 if (xsltNeedElemSpaceHandling(ctxt)) 6132 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); 6133 /* 6134 * Evaluate global params and user-provided params. 6135 */ 6136 ctxt->node = (xmlNodePtr) doc; 6137 if (ctxt->globalVars == NULL) 6138 ctxt->globalVars = xmlHashCreate(20); 6139 if (params != NULL) { 6140 xsltEvalUserParams(ctxt, params); 6141 } 6142 6143 /* need to be called before evaluating global variables */ 6144 xsltCountKeys(ctxt); 6145 6146 xsltEvalGlobalVariables(ctxt); 6147 6148 ctxt->node = (xmlNodePtr) doc; 6149 ctxt->output = res; 6150 ctxt->insert = (xmlNodePtr) res; 6151 ctxt->varsBase = ctxt->varsNr - 1; 6152 6153 ctxt->xpathCtxt->contextSize = 1; 6154 ctxt->xpathCtxt->proximityPosition = 1; 6155 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ 6156 /* 6157 * Start processing the source tree ----------------------------------- 6158 */ 6159 xsltProcessOneNode(ctxt, ctxt->node, NULL); 6160 /* 6161 * Remove all remaining vars from the stack. 6162 */ 6163 xsltLocalVariablePop(ctxt, 0, -2); 6164 xsltShutdownCtxtExts(ctxt); 6165 6166 xsltCleanupTemplates(style); /* TODO: <- style should be read only */ 6167 6168 /* 6169 * Now cleanup our variables so stylesheet can be re-used 6170 * 6171 * TODO: this is not needed anymore global variables are copied 6172 * and not evaluated directly anymore, keep this as a check 6173 */ 6174 if (style->variables != variables) { 6175 vptr = style->variables; 6176 while (vptr->next != variables) 6177 vptr = vptr->next; 6178 vptr->next = NULL; 6179 xsltFreeStackElemList(style->variables); 6180 style->variables = variables; 6181 } 6182 vptr = style->variables; 6183 while (vptr != NULL) { 6184 if (vptr->computed) { 6185 if (vptr->value != NULL) { 6186 xmlXPathFreeObject(vptr->value); 6187 vptr->value = NULL; 6188 vptr->computed = 0; 6189 } 6190 } 6191 vptr = vptr->next; 6192 } 6193#if 0 6194 /* 6195 * code disabled by wmb; awaiting kb's review 6196 * problem is that global variable(s) may contain xpath objects 6197 * from doc associated with RVT, so can't be freed at this point. 6198 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so 6199 * I assume this shouldn't be required at this point. 6200 */ 6201 /* 6202 * Free all remaining tree fragments. 6203 */ 6204 xsltFreeRVTs(ctxt); 6205#endif 6206 /* 6207 * Do some post processing work depending on the generated output 6208 */ 6209 root = xmlDocGetRootElement(res); 6210 if (root != NULL) { 6211 const xmlChar *doctype = NULL; 6212 6213 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 6214 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 6215 if (doctype == NULL) 6216 doctype = root->name; 6217 6218 /* 6219 * Apply the default selection of the method 6220 */ 6221 if ((method == NULL) && 6222 (root->ns == NULL) && 6223 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 6224 xmlNodePtr tmp; 6225 6226 tmp = res->children; 6227 while ((tmp != NULL) && (tmp != root)) { 6228 if (tmp->type == XML_ELEMENT_NODE) 6229 break; 6230 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 6231 break; 6232 tmp = tmp->next; 6233 } 6234 if (tmp == root) { 6235 ctxt->type = XSLT_OUTPUT_HTML; 6236 /* 6237 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the 6238 * transformation on the doc, but functions like 6239 */ 6240 res->type = XML_HTML_DOCUMENT_NODE; 6241 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6242 res->intSubset = xmlCreateIntSubset(res, doctype, 6243 doctypePublic, 6244 doctypeSystem); 6245#ifdef XSLT_GENERATE_HTML_DOCTYPE 6246 } else if (version != NULL) { 6247 xsltGetHTMLIDs(version, &doctypePublic, 6248 &doctypeSystem); 6249 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 6250 res->intSubset = 6251 xmlCreateIntSubset(res, doctype, 6252 doctypePublic, 6253 doctypeSystem); 6254#endif 6255 } 6256 } 6257 6258 } 6259 if (ctxt->type == XSLT_OUTPUT_XML) { 6260 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 6261 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 6262 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6263 xmlNodePtr last; 6264 /* Need a small "hack" here to assure DTD comes before 6265 possible comment nodes */ 6266 node = res->children; 6267 last = res->last; 6268 res->children = NULL; 6269 res->last = NULL; 6270 res->intSubset = xmlCreateIntSubset(res, doctype, 6271 doctypePublic, 6272 doctypeSystem); 6273 if (res->children != NULL) { 6274 res->children->next = node; 6275 node->prev = res->children; 6276 res->last = last; 6277 } else { 6278 res->children = node; 6279 res->last = last; 6280 } 6281 } 6282 } 6283 } 6284 xmlXPathFreeNodeSet(ctxt->nodeList); 6285 if (profile != NULL) { 6286 xsltSaveProfiling(ctxt, profile); 6287 } 6288 6289 /* 6290 * Be pedantic. 6291 */ 6292 if ((ctxt != NULL) && (ctxt->state == XSLT_STATE_ERROR)) { 6293 xmlFreeDoc(res); 6294 res = NULL; 6295 } 6296 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { 6297 int ret; 6298 6299 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); 6300 if (ret == 0) { 6301 xsltTransformError(ctxt, NULL, NULL, 6302 "xsltApplyStylesheet: forbidden to save to %s\n", 6303 output); 6304 } else if (ret < 0) { 6305 xsltTransformError(ctxt, NULL, NULL, 6306 "xsltApplyStylesheet: saving to %s may not be possible\n", 6307 output); 6308 } 6309 } 6310 6311#ifdef XSLT_DEBUG_PROFILE_CACHE 6312 printf("# Cache:\n"); 6313 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6314 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6315#endif 6316 6317 if ((ctxt != NULL) && (userCtxt == NULL)) 6318 xsltFreeTransformContext(ctxt); 6319 6320 return (res); 6321 6322error: 6323 if (res != NULL) 6324 xmlFreeDoc(res); 6325 6326#ifdef XSLT_DEBUG_PROFILE_CACHE 6327 printf("# Cache:\n"); 6328 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6329 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6330#endif 6331 6332 if ((ctxt != NULL) && (userCtxt == NULL)) 6333 xsltFreeTransformContext(ctxt); 6334 return (NULL); 6335} 6336 6337/** 6338 * xsltApplyStylesheet: 6339 * @style: a parsed XSLT stylesheet 6340 * @doc: a parsed XML document 6341 * @params: a NULL terminated arry of parameters names/values tuples 6342 * 6343 * Apply the stylesheet to the document 6344 * NOTE: This may lead to a non-wellformed output XML wise ! 6345 * 6346 * Returns the result document or NULL in case of error 6347 */ 6348xmlDocPtr 6349xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6350 const char **params) 6351{ 6352 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); 6353} 6354 6355/** 6356 * xsltProfileStylesheet: 6357 * @style: a parsed XSLT stylesheet 6358 * @doc: a parsed XML document 6359 * @params: a NULL terminated arry of parameters names/values tuples 6360 * @output: a FILE * for the profiling output 6361 * 6362 * Apply the stylesheet to the document and dump the profiling to 6363 * the given output. 6364 * 6365 * Returns the result document or NULL in case of error 6366 */ 6367xmlDocPtr 6368xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6369 const char **params, FILE * output) 6370{ 6371 xmlDocPtr res; 6372 6373 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); 6374 return (res); 6375} 6376 6377/** 6378 * xsltApplyStylesheetUser: 6379 * @style: a parsed XSLT stylesheet 6380 * @doc: a parsed XML document 6381 * @params: a NULL terminated array of parameters names/values tuples 6382 * @output: the targetted output 6383 * @profile: profile FILE * output or NULL 6384 * @userCtxt: user provided transform context 6385 * 6386 * Apply the stylesheet to the document and allow the user to provide 6387 * its own transformation context. 6388 * 6389 * Returns the result document or NULL in case of error 6390 */ 6391xmlDocPtr 6392xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6393 const char **params, const char *output, 6394 FILE * profile, xsltTransformContextPtr userCtxt) 6395{ 6396 xmlDocPtr res; 6397 6398 res = xsltApplyStylesheetInternal(style, doc, params, output, 6399 profile, userCtxt); 6400 return (res); 6401} 6402 6403/** 6404 * xsltRunStylesheetUser: 6405 * @style: a parsed XSLT stylesheet 6406 * @doc: a parsed XML document 6407 * @params: a NULL terminated array of parameters names/values tuples 6408 * @output: the URL/filename ot the generated resource if available 6409 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6410 * @IObuf: an output buffer for progressive output (not implemented yet) 6411 * @profile: profile FILE * output or NULL 6412 * @userCtxt: user provided transform context 6413 * 6414 * Apply the stylesheet to the document and generate the output according 6415 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6416 * 6417 * NOTE: This may lead to a non-wellformed output XML wise ! 6418 * NOTE: This may also result in multiple files being generated 6419 * NOTE: using IObuf, the result encoding used will be the one used for 6420 * creating the output buffer, use the following macro to read it 6421 * from the stylesheet 6422 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6423 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6424 * since the interface uses only UTF8 6425 * 6426 * Returns the number of by written to the main resource or -1 in case of 6427 * error. 6428 */ 6429int 6430xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6431 const char **params, const char *output, 6432 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, 6433 FILE * profile, xsltTransformContextPtr userCtxt) 6434{ 6435 xmlDocPtr tmp; 6436 int ret; 6437 6438 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) 6439 return (-1); 6440 if ((SAX != NULL) && (IObuf != NULL)) 6441 return (-1); 6442 6443 /* unsupported yet */ 6444 if (SAX != NULL) { 6445 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ 6446 return (-1); 6447 } 6448 6449 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, 6450 userCtxt); 6451 if (tmp == NULL) { 6452 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, 6453 "xsltRunStylesheet : run failed\n"); 6454 return (-1); 6455 } 6456 if (IObuf != NULL) { 6457 /* TODO: incomplete, IObuf output not progressive */ 6458 ret = xsltSaveResultTo(IObuf, tmp, style); 6459 } else { 6460 ret = xsltSaveResultToFilename(output, tmp, style, 0); 6461 } 6462 xmlFreeDoc(tmp); 6463 return (ret); 6464} 6465 6466/** 6467 * xsltRunStylesheet: 6468 * @style: a parsed XSLT stylesheet 6469 * @doc: a parsed XML document 6470 * @params: a NULL terminated array of parameters names/values tuples 6471 * @output: the URL/filename ot the generated resource if available 6472 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6473 * @IObuf: an output buffer for progressive output (not implemented yet) 6474 * 6475 * Apply the stylesheet to the document and generate the output according 6476 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6477 * 6478 * NOTE: This may lead to a non-wellformed output XML wise ! 6479 * NOTE: This may also result in multiple files being generated 6480 * NOTE: using IObuf, the result encoding used will be the one used for 6481 * creating the output buffer, use the following macro to read it 6482 * from the stylesheet 6483 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6484 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6485 * since the interface uses only UTF8 6486 * 6487 * Returns the number of bytes written to the main resource or -1 in case of 6488 * error. 6489 */ 6490int 6491xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6492 const char **params, const char *output, 6493 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) 6494{ 6495 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, 6496 NULL, NULL)); 6497} 6498 6499/** 6500 * xsltRegisterAllElement: 6501 * @ctxt: the XPath context 6502 * 6503 * Registers all default XSLT elements in this context 6504 */ 6505void 6506xsltRegisterAllElement(xsltTransformContextPtr ctxt) 6507{ 6508 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", 6509 XSLT_NAMESPACE, 6510 (xsltTransformFunction) xsltApplyTemplates); 6511 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", 6512 XSLT_NAMESPACE, 6513 (xsltTransformFunction) xsltApplyImports); 6514 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", 6515 XSLT_NAMESPACE, 6516 (xsltTransformFunction) xsltCallTemplate); 6517 xsltRegisterExtElement(ctxt, (const xmlChar *) "element", 6518 XSLT_NAMESPACE, 6519 (xsltTransformFunction) xsltElement); 6520 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", 6521 XSLT_NAMESPACE, 6522 (xsltTransformFunction) xsltAttribute); 6523 xsltRegisterExtElement(ctxt, (const xmlChar *) "text", 6524 XSLT_NAMESPACE, 6525 (xsltTransformFunction) xsltText); 6526 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", 6527 XSLT_NAMESPACE, 6528 (xsltTransformFunction) xsltProcessingInstruction); 6529 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", 6530 XSLT_NAMESPACE, 6531 (xsltTransformFunction) xsltComment); 6532 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", 6533 XSLT_NAMESPACE, 6534 (xsltTransformFunction) xsltCopy); 6535 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", 6536 XSLT_NAMESPACE, 6537 (xsltTransformFunction) xsltValueOf); 6538 xsltRegisterExtElement(ctxt, (const xmlChar *) "number", 6539 XSLT_NAMESPACE, 6540 (xsltTransformFunction) xsltNumber); 6541 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", 6542 XSLT_NAMESPACE, 6543 (xsltTransformFunction) xsltForEach); 6544 xsltRegisterExtElement(ctxt, (const xmlChar *) "if", 6545 XSLT_NAMESPACE, 6546 (xsltTransformFunction) xsltIf); 6547 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", 6548 XSLT_NAMESPACE, 6549 (xsltTransformFunction) xsltChoose); 6550 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", 6551 XSLT_NAMESPACE, 6552 (xsltTransformFunction) xsltSort); 6553 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", 6554 XSLT_NAMESPACE, 6555 (xsltTransformFunction) xsltCopyOf); 6556 xsltRegisterExtElement(ctxt, (const xmlChar *) "message", 6557 XSLT_NAMESPACE, 6558 (xsltTransformFunction) xsltMessage); 6559 6560 /* 6561 * Those don't have callable entry points but are registered anyway 6562 */ 6563 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", 6564 XSLT_NAMESPACE, 6565 (xsltTransformFunction) xsltDebug); 6566 xsltRegisterExtElement(ctxt, (const xmlChar *) "param", 6567 XSLT_NAMESPACE, 6568 (xsltTransformFunction) xsltDebug); 6569 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", 6570 XSLT_NAMESPACE, 6571 (xsltTransformFunction) xsltDebug); 6572 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", 6573 XSLT_NAMESPACE, 6574 (xsltTransformFunction) xsltDebug); 6575 xsltRegisterExtElement(ctxt, (const xmlChar *) "when", 6576 XSLT_NAMESPACE, 6577 (xsltTransformFunction) xsltDebug); 6578 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", 6579 XSLT_NAMESPACE, 6580 (xsltTransformFunction) xsltDebug); 6581 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", 6582 XSLT_NAMESPACE, 6583 (xsltTransformFunction) xsltDebug); 6584 6585} 6586