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