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