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