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 do { 2270 tmp = cur; 2271 cur = (xmlDocPtr) cur->next; 2272 if (tmp->psvi == XSLT_RVT_LOCAL) { 2273 xsltReleaseRVT(ctxt, tmp); 2274 } else if (tmp->psvi == XSLT_RVT_GLOBAL) { 2275 xsltRegisterPersistRVT(ctxt, tmp); 2276 } else if (tmp->psvi != XSLT_RVT_FUNC_RESULT) { 2277 xmlGenericError(xmlGenericErrorContext, 2278 "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n", 2279 tmp->psvi); 2280 } 2281 } while (cur != base); 2282 2283 if (base != NULL) 2284 base->prev = NULL; 2285 ctxt->localRVT = base; 2286 } 2287 2288 /** 2289 * xsltApplySequenceConstructor: 2290 * @ctxt: a XSLT process context 2291 * @contextNode: the "current node" in the source tree 2292 * @list: the nodes of a sequence constructor; 2293 * (plus leading xsl:param elements) 2294 * @templ: the compiled xsl:template (optional) 2295 * 2296 * Processes a sequence constructor. 2297 * 2298 * NOTE: ctxt->currentTemplateRule was introduced to reflect the 2299 * semantics of "current template rule". I.e. the field ctxt->templ 2300 * is not intended to reflect this, thus always pushed onto the 2301 * template stack. 2302 */ 2303 static void 2304 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, 2305 xmlNodePtr contextNode, xmlNodePtr list, 2306 xsltTemplatePtr templ) 2307 { 2308 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode; 2309 xmlNodePtr cur, insert, copy = NULL; 2310 int level = 0, oldVarsNr; 2311 xmlDocPtr oldLocalFragmentTop; 2312 2313 #ifdef XSLT_REFACTORED 2314 xsltStylePreCompPtr info; 2315 #endif 2316 2317 #ifdef WITH_DEBUGGER 2318 int addCallResult = 0; 2319 xmlNodePtr debuggedNode = NULL; 2320 #endif 2321 2322 if (ctxt == NULL) 2323 return; 2324 2325 #ifdef WITH_DEBUGGER 2326 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { 2327 debuggedNode = 2328 xsltDebuggerStartSequenceConstructor(ctxt, contextNode, 2329 list, templ, &addCallResult); 2330 if (debuggedNode == NULL) 2331 return; 2332 } 2333 #endif 2334 2335 if (list == NULL) 2336 return; 2337 CHECK_STOPPED; 2338 2339 /* 2340 * Check for infinite recursion: stop if the maximum of nested templates 2341 * is excceeded. Adjust xsltMaxDepth if you need more. 2342 */ 2343 if (ctxt->depth >= ctxt->maxTemplateDepth) { 2344 xsltTransformError(ctxt, NULL, list, 2345 "xsltApplySequenceConstructor: A potential infinite template " 2346 "recursion was detected.\n" 2347 "You can adjust xsltMaxDepth (--maxdepth) in order to " 2348 "raise the maximum number of nested template calls and " 2349 "variables/params (currently set to %d).\n", 2350 ctxt->maxTemplateDepth); 2351 xsltDebug(ctxt, contextNode, list, NULL); 2352 ctxt->state = XSLT_STATE_STOPPED; 2353 return; 2354 } 2355 ctxt->depth++; 2356 2357 oldLocalFragmentTop = ctxt->localRVT; 2358 oldInsert = insert = ctxt->insert; 2359 oldInst = oldCurInst = ctxt->inst; 2360 oldContextNode = ctxt->node; 2361 /* 2362 * Save current number of variables on the stack; new vars are popped when 2363 * exiting. 2364 */ 2365 oldVarsNr = ctxt->varsNr; 2366 /* 2367 * Process the sequence constructor. 2368 */ 2369 cur = list; 2370 while (cur != NULL) { 2371 ctxt->inst = cur; 2372 2373 #ifdef WITH_DEBUGGER 2374 switch (ctxt->debugStatus) { 2375 case XSLT_DEBUG_RUN_RESTART: 2376 case XSLT_DEBUG_QUIT: 2377 break; 2378 2379 } 2380 #endif 2381 /* 2382 * Test; we must have a valid insertion point. 2383 */ 2384 if (insert == NULL) { 2385 2386 #ifdef WITH_XSLT_DEBUG_PROCESS 2387 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2388 "xsltApplySequenceConstructor: insert == NULL !\n")); 2389 #endif 2390 goto error; 2391 } 2392 2393 #ifdef WITH_DEBUGGER 2394 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur)) 2395 xslHandleDebugger(cur, contextNode, templ, ctxt); 2396 #endif 2397 2398 #ifdef XSLT_REFACTORED 2399 if (cur->type == XML_ELEMENT_NODE) { 2400 info = (xsltStylePreCompPtr) cur->psvi; 2401 /* 2402 * We expect a compiled representation on: 2403 * 1) XSLT instructions of this XSLT version (1.0) 2404 * (with a few exceptions) 2405 * 2) Literal result elements 2406 * 3) Extension instructions 2407 * 4) XSLT instructions of future XSLT versions 2408 * (forwards-compatible mode). 2409 */ 2410 if (info == NULL) { 2411 /* 2412 * Handle the rare cases where we don't expect a compiled 2413 * representation on an XSLT element. 2414 */ 2415 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { 2416 xsltMessage(ctxt, contextNode, cur); 2417 goto skip_children; 2418 } 2419 /* 2420 * Something really went wrong: 2421 */ 2422 xsltTransformError(ctxt, NULL, cur, 2423 "Internal error in xsltApplySequenceConstructor(): " 2424 "The element '%s' in the stylesheet has no compiled " 2425 "representation.\n", 2426 cur->name); 2427 goto skip_children; 2428 } 2429 2430 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { 2431 xsltStyleItemLRElementInfoPtr lrInfo = 2432 (xsltStyleItemLRElementInfoPtr) info; 2433 /* 2434 * Literal result elements 2435 * -------------------------------------------------------- 2436 */ 2437 #ifdef WITH_XSLT_DEBUG_PROCESS 2438 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2439 xsltGenericDebug(xsltGenericDebugContext, 2440 "xsltApplySequenceConstructor: copy literal result " 2441 "element '%s'\n", cur->name)); 2442 #endif 2443 /* 2444 * Copy the raw element-node. 2445 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) 2446 * == NULL) 2447 * goto error; 2448 */ 2449 copy = xmlDocCopyNode(cur, insert->doc, 0); 2450 if (copy == NULL) { 2451 xsltTransformError(ctxt, NULL, cur, 2452 "Internal error in xsltApplySequenceConstructor(): " 2453 "Failed to copy literal result element '%s'.\n", 2454 cur->name); 2455 goto error; 2456 } else { 2457 /* 2458 * Add the element-node to the result tree. 2459 */ 2460 copy->doc = ctxt->output; 2461 copy = xsltAddChild(insert, copy); 2462 /* 2463 * Create effective namespaces declarations. 2464 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); 2465 */ 2466 if (lrInfo->effectiveNs != NULL) { 2467 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; 2468 xmlNsPtr ns, lastns = NULL; 2469 2470 while (effNs != NULL) { 2471 /* 2472 * Avoid generating redundant namespace 2473 * declarations; thus lookup if there is already 2474 * such a ns-decl in the result. 2475 */ 2476 ns = xmlSearchNs(copy->doc, copy, effNs->prefix); 2477 if ((ns != NULL) && 2478 (xmlStrEqual(ns->href, effNs->nsName))) 2479 { 2480 effNs = effNs->next; 2481 continue; 2482 } 2483 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix); 2484 if (ns == NULL) { 2485 xsltTransformError(ctxt, NULL, cur, 2486 "Internal error in " 2487 "xsltApplySequenceConstructor(): " 2488 "Failed to copy a namespace " 2489 "declaration.\n"); 2490 goto error; 2491 } 2492 2493 if (lastns == NULL) 2494 copy->nsDef = ns; 2495 else 2496 lastns->next =ns; 2497 lastns = ns; 2498 2499 effNs = effNs->next; 2500 } 2501 2502 } 2503 /* 2504 * NOTE that we don't need to apply ns-alising: this was 2505 * already done at compile-time. 2506 */ 2507 if (cur->ns != NULL) { 2508 /* 2509 * If there's no such ns-decl in the result tree, 2510 * then xsltGetSpecialNamespace() will 2511 * create a ns-decl on the copied node. 2512 */ 2513 copy->ns = xsltGetSpecialNamespace(ctxt, cur, 2514 cur->ns->href, cur->ns->prefix, copy); 2515 } else { 2516 /* 2517 * Undeclare the default namespace if needed. 2518 * This can be skipped, if the result element has 2519 * no ns-decls, in which case the result element 2520 * obviously does not declare a default namespace; 2521 * AND there's either no parent, or the parent 2522 * element is in no namespace; this means there's no 2523 * default namespace is scope to care about. 2524 * 2525 * REVISIT: This might result in massive 2526 * generation of ns-decls if nodes in a default 2527 * namespaces are mixed with nodes in no namespace. 2528 * 2529 */ 2530 if (copy->nsDef || 2531 ((insert != NULL) && 2532 (insert->type == XML_ELEMENT_NODE) && 2533 (insert->ns != NULL))) 2534 { 2535 xsltGetSpecialNamespace(ctxt, cur, 2536 NULL, NULL, copy); 2537 } 2538 } 2539 } 2540 /* 2541 * SPEC XSLT 2.0 "Each attribute of the literal result 2542 * element, other than an attribute in the XSLT namespace, 2543 * is processed to produce an attribute for the element in 2544 * the result tree." 2545 * NOTE: See bug #341325. 2546 */ 2547 if (cur->properties != NULL) { 2548 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); 2549 } 2550 } else if (IS_XSLT_ELEM_FAST(cur)) { 2551 /* 2552 * XSLT instructions 2553 * -------------------------------------------------------- 2554 */ 2555 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { 2556 /* 2557 * We hit an unknown XSLT element. 2558 * Try to apply one of the fallback cases. 2559 */ 2560 ctxt->insert = insert; 2561 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2562 xsltTransformError(ctxt, NULL, cur, 2563 "The is no fallback behaviour defined for " 2564 "the unknown XSLT element '%s'.\n", 2565 cur->name); 2566 } 2567 ctxt->insert = oldInsert; 2568 } else if (info->func != NULL) { 2569 /* 2570 * Execute the XSLT instruction. 2571 */ 2572 ctxt->insert = insert; 2573 2574 info->func(ctxt, contextNode, cur, 2575 (xsltElemPreCompPtr) info); 2576 2577 /* 2578 * Cleanup temporary tree fragments. 2579 */ 2580 if (oldLocalFragmentTop != ctxt->localRVT) 2581 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2582 2583 ctxt->insert = oldInsert; 2584 } else if (info->type == XSLT_FUNC_VARIABLE) { 2585 xsltStackElemPtr tmpvar = ctxt->vars; 2586 2587 xsltParseStylesheetVariable(ctxt, cur); 2588 2589 if (tmpvar != ctxt->vars) { 2590 /* 2591 * TODO: Using a @tmpvar is an annoying workaround, but 2592 * the current mechanisms do not provide any other way 2593 * of knowing if the var was really pushed onto the 2594 * stack. 2595 */ 2596 ctxt->vars->level = level; 2597 } 2598 } else if (info->type == XSLT_FUNC_MESSAGE) { 2599 /* 2600 * TODO: Won't be hit, since we don't compile xsl:message. 2601 */ 2602 xsltMessage(ctxt, contextNode, cur); 2603 } else { 2604 xsltTransformError(ctxt, NULL, cur, 2605 "Unexpected XSLT element '%s'.\n", cur->name); 2606 } 2607 goto skip_children; 2608 2609 } else { 2610 xsltTransformFunction func; 2611 /* 2612 * Extension intructions (elements) 2613 * -------------------------------------------------------- 2614 */ 2615 if (cur->psvi == xsltExtMarker) { 2616 /* 2617 * The xsltExtMarker was set during the compilation 2618 * of extension instructions if there was no registered 2619 * handler for this specific extension function at 2620 * compile-time. 2621 * Libxslt will now lookup if a handler is 2622 * registered in the context of this transformation. 2623 */ 2624 func = (xsltTransformFunction) 2625 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); 2626 } else 2627 func = ((xsltElemPreCompPtr) cur->psvi)->func; 2628 2629 if (func == NULL) { 2630 /* 2631 * No handler available. 2632 * Try to execute fallback behaviour via xsl:fallback. 2633 */ 2634 #ifdef WITH_XSLT_DEBUG_PROCESS 2635 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2636 xsltGenericDebug(xsltGenericDebugContext, 2637 "xsltApplySequenceConstructor: unknown extension %s\n", 2638 cur->name)); 2639 #endif 2640 ctxt->insert = insert; 2641 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2642 xsltTransformError(ctxt, NULL, cur, 2643 "Unknown extension instruction '{%s}%s'.\n", 2644 cur->ns->href, cur->name); 2645 } 2646 ctxt->insert = oldInsert; 2647 } else { 2648 /* 2649 * Execute the handler-callback. 2650 */ 2651 #ifdef WITH_XSLT_DEBUG_PROCESS 2652 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2653 "xsltApplySequenceConstructor: extension construct %s\n", 2654 cur->name)); 2655 #endif 2656 /* 2657 * Disable the xsltCopyTextString optimization for 2658 * extension elements. Extensions could append text using 2659 * xmlAddChild which will free the buffer pointed to by 2660 * 'lasttext'. This buffer could later be reallocated with 2661 * a different size than recorded in 'lasttsize'. See bug 2662 * #777432. 2663 */ 2664 if (cur->psvi == xsltExtMarker) { 2665 ctxt->lasttext = NULL; 2666 } 2667 2668 ctxt->insert = insert; 2669 2670 func(ctxt, contextNode, cur, cur->psvi); 2671 2672 /* 2673 * Cleanup temporary tree fragments. 2674 */ 2675 if (oldLocalFragmentTop != ctxt->localRVT) 2676 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2677 2678 ctxt->insert = oldInsert; 2679 } 2680 goto skip_children; 2681 } 2682 2683 } else if (XSLT_IS_TEXT_NODE(cur)) { 2684 /* 2685 * Text 2686 * ------------------------------------------------------------ 2687 */ 2688 #ifdef WITH_XSLT_DEBUG_PROCESS 2689 if (cur->name == xmlStringTextNoenc) { 2690 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2691 xsltGenericDebug(xsltGenericDebugContext, 2692 "xsltApplySequenceConstructor: copy unescaped text '%s'\n", 2693 cur->content)); 2694 } else { 2695 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, 2696 xsltGenericDebug(xsltGenericDebugContext, 2697 "xsltApplySequenceConstructor: copy text '%s'\n", 2698 cur->content)); 2699 } 2700 #endif 2701 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) 2702 goto error; 2703 } 2704 2705 #else /* XSLT_REFACTORED */ 2706 2707 if (IS_XSLT_ELEM(cur)) { 2708 /* 2709 * This is an XSLT node 2710 */ 2711 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi; 2712 2713 if (info == NULL) { 2714 if (IS_XSLT_NAME(cur, "message")) { 2715 xsltMessage(ctxt, contextNode, cur); 2716 } else { 2717 /* 2718 * That's an error try to apply one of the fallback cases 2719 */ 2720 ctxt->insert = insert; 2721 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { 2722 xsltGenericError(xsltGenericErrorContext, 2723 "xsltApplySequenceConstructor: %s was not compiled\n", 2724 cur->name); 2725 } 2726 ctxt->insert = oldInsert; 2727 } 2728 goto skip_children; 2729 } 2730 2731 if (info->func != NULL) { 2732 oldCurInst = ctxt->inst; 2733 ctxt->inst = cur; 2734 ctxt->insert = insert; 2735 2736 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info); 2737 2738 /* 2739 * Cleanup temporary tree fragments. 2740 */ 2741 if (oldLocalFragmentTop != ctxt->localRVT) 2742 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2743 2744 ctxt->insert = oldInsert; 2745 ctxt->inst = oldCurInst; 2746 goto skip_children; 2747 } 2748 2749 if (IS_XSLT_NAME(cur, "variable")) { 2750 xsltStackElemPtr tmpvar = ctxt->vars; 2751 2752 oldCurInst = ctxt->inst; 2753 ctxt->inst = cur; 2754 2755 xsltParseStylesheetVariable(ctxt, cur); 2756 2757 ctxt->inst = oldCurInst; 2758 2759 if (tmpvar != ctxt->vars) { 2760 /* 2761 * TODO: Using a @tmpvar is an annoying workaround, but 2762 * the current mechanisms do not provide any other way 2763 * of knowing if the var was really pushed onto the 2764 * stack. 2765 */ 2766 ctxt->vars->level = level; 2767 } 2768 } else if (IS_XSLT_NAME(cur, "message")) { 2769 xsltMessage(ctxt, contextNode, cur); 2770 } else { 2771 xsltTransformError(ctxt, NULL, cur, 2772 "Unexpected XSLT element '%s'.\n", cur->name); 2773 } 2774 goto skip_children; 2775 } else if ((cur->type == XML_TEXT_NODE) || 2776 (cur->type == XML_CDATA_SECTION_NODE)) { 2777 2778 /* 2779 * This text comes from the stylesheet 2780 * For stylesheets, the set of whitespace-preserving 2781 * element names consists of just xsl:text. 2782 */ 2783 #ifdef WITH_XSLT_DEBUG_PROCESS 2784 if (cur->type == XML_CDATA_SECTION_NODE) { 2785 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2786 "xsltApplySequenceConstructor: copy CDATA text %s\n", 2787 cur->content)); 2788 } else if (cur->name == xmlStringTextNoenc) { 2789 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2790 "xsltApplySequenceConstructor: copy unescaped text %s\n", 2791 cur->content)); 2792 } else { 2793 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2794 "xsltApplySequenceConstructor: copy text %s\n", 2795 cur->content)); 2796 } 2797 #endif 2798 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) 2799 goto error; 2800 } else if ((cur->type == XML_ELEMENT_NODE) && 2801 (cur->ns != NULL) && (cur->psvi != NULL)) { 2802 xsltTransformFunction function; 2803 2804 oldCurInst = ctxt->inst; 2805 ctxt->inst = cur; 2806 /* 2807 * Flagged as an extension element 2808 */ 2809 if (cur->psvi == xsltExtMarker) 2810 function = (xsltTransformFunction) 2811 xsltExtElementLookup(ctxt, cur->name, cur->ns->href); 2812 else 2813 function = ((xsltElemPreCompPtr) cur->psvi)->func; 2814 2815 if (function == NULL) { 2816 xmlNodePtr child; 2817 int found = 0; 2818 2819 #ifdef WITH_XSLT_DEBUG_PROCESS 2820 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2821 "xsltApplySequenceConstructor: unknown extension %s\n", 2822 cur->name)); 2823 #endif 2824 /* 2825 * Search if there are fallbacks 2826 */ 2827 child = cur->children; 2828 while (child != NULL) { 2829 if ((IS_XSLT_ELEM(child)) && 2830 (IS_XSLT_NAME(child, "fallback"))) 2831 { 2832 found = 1; 2833 xsltApplySequenceConstructor(ctxt, contextNode, 2834 child->children, NULL); 2835 } 2836 child = child->next; 2837 } 2838 2839 if (!found) { 2840 xsltTransformError(ctxt, NULL, cur, 2841 "xsltApplySequenceConstructor: failed to find extension %s\n", 2842 cur->name); 2843 } 2844 } else { 2845 #ifdef WITH_XSLT_DEBUG_PROCESS 2846 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2847 "xsltApplySequenceConstructor: extension construct %s\n", 2848 cur->name)); 2849 #endif 2850 2851 /* 2852 * Disable the xsltCopyTextString optimization for 2853 * extension elements. Extensions could append text using 2854 * xmlAddChild which will free the buffer pointed to by 2855 * 'lasttext'. This buffer could later be reallocated with 2856 * a different size than recorded in 'lasttsize'. See bug 2857 * #777432. 2858 */ 2859 if (cur->psvi == xsltExtMarker) { 2860 ctxt->lasttext = NULL; 2861 } 2862 2863 ctxt->insert = insert; 2864 2865 function(ctxt, contextNode, cur, cur->psvi); 2866 /* 2867 * Cleanup temporary tree fragments. 2868 */ 2869 if (oldLocalFragmentTop != ctxt->localRVT) 2870 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 2871 2872 ctxt->insert = oldInsert; 2873 2874 } 2875 ctxt->inst = oldCurInst; 2876 goto skip_children; 2877 } else if (cur->type == XML_ELEMENT_NODE) { 2878 #ifdef WITH_XSLT_DEBUG_PROCESS 2879 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 2880 "xsltApplySequenceConstructor: copy node %s\n", 2881 cur->name)); 2882 #endif 2883 oldCurInst = ctxt->inst; 2884 ctxt->inst = cur; 2885 2886 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) 2887 goto error; 2888 /* 2889 * Add extra namespaces inherited from the current template 2890 * if we are in the first level children and this is a 2891 * "real" template. 2892 */ 2893 if ((templ != NULL) && (oldInsert == insert) && 2894 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { 2895 int i; 2896 xmlNsPtr ns, ret; 2897 2898 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { 2899 const xmlChar *URI = NULL; 2900 xsltStylesheetPtr style; 2901 ns = ctxt->templ->inheritedNs[i]; 2902 2903 /* Note that the XSLT namespace was already excluded 2904 * in xsltGetInheritedNsList(). 2905 */ 2906 #if 0 2907 if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) 2908 continue; 2909 #endif 2910 style = ctxt->style; 2911 while (style != NULL) { 2912 if (style->nsAliases != NULL) 2913 URI = (const xmlChar *) 2914 xmlHashLookup(style->nsAliases, ns->href); 2915 if (URI != NULL) 2916 break; 2917 2918 style = xsltNextImport(style); 2919 } 2920 if (URI == UNDEFINED_DEFAULT_NS) 2921 continue; 2922 if (URI == NULL) 2923 URI = ns->href; 2924 /* 2925 * TODO: The following will still be buggy for the 2926 * non-refactored code. 2927 */ 2928 ret = xmlSearchNs(copy->doc, copy, ns->prefix); 2929 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) 2930 { 2931 xmlNewNs(copy, URI, ns->prefix); 2932 } 2933 } 2934 if (copy->ns != NULL) { 2935 /* 2936 * Fix the node namespace if needed 2937 */ 2938 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); 2939 } 2940 } 2941 /* 2942 * all the attributes are directly inherited 2943 */ 2944 if (cur->properties != NULL) { 2945 xsltAttrListTemplateProcess(ctxt, copy, cur->properties); 2946 } 2947 ctxt->inst = oldCurInst; 2948 } 2949 #endif /* else of XSLT_REFACTORED */ 2950 2951 /* 2952 * Descend into content in document order. 2953 */ 2954 if (cur->children != NULL) { 2955 if (cur->children->type != XML_ENTITY_DECL) { 2956 cur = cur->children; 2957 level++; 2958 if (copy != NULL) 2959 insert = copy; 2960 continue; 2961 } 2962 } 2963 2964 skip_children: 2965 /* 2966 * If xslt:message was just processed, we might have hit a 2967 * terminate='yes'; if so, then break the loop and clean up. 2968 * TODO: Do we need to check this also before trying to descend 2969 * into the content? 2970 */ 2971 if (ctxt->state == XSLT_STATE_STOPPED) 2972 break; 2973 if (cur->next != NULL) { 2974 cur = cur->next; 2975 continue; 2976 } 2977 2978 do { 2979 cur = cur->parent; 2980 level--; 2981 /* 2982 * Pop variables/params (xsl:variable and xsl:param). 2983 */ 2984 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) { 2985 xsltLocalVariablePop(ctxt, oldVarsNr, level); 2986 } 2987 2988 insert = insert->parent; 2989 if (cur == NULL) 2990 break; 2991 if (cur == list->parent) { 2992 cur = NULL; 2993 break; 2994 } 2995 if (cur->next != NULL) { 2996 cur = cur->next; 2997 break; 2998 } 2999 } while (cur != NULL); 3000 } 3001 3002 error: 3003 /* 3004 * In case of errors: pop remaining variables. 3005 */ 3006 if (ctxt->varsNr > oldVarsNr) 3007 xsltLocalVariablePop(ctxt, oldVarsNr, -1); 3008 3009 ctxt->node = oldContextNode; 3010 ctxt->inst = oldInst; 3011 ctxt->insert = oldInsert; 3012 3013 ctxt->depth--; 3014 3015 #ifdef WITH_DEBUGGER 3016 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { 3017 xslDropCall(); 3018 } 3019 #endif 3020 } 3021 3022 /* 3023 * xsltApplyXSLTTemplate: 3024 * @ctxt: a XSLT transformation context 3025 * @contextNode: the node in the source tree. 3026 * @list: the nodes of a sequence constructor; 3027 * (plus leading xsl:param elements) 3028 * @templ: the compiled xsl:template declaration; 3029 * NULL if a sequence constructor 3030 * @withParams: a set of caller-parameters (xsl:with-param) or NULL 3031 * 3032 * Called by: 3033 * - xsltApplyImports() 3034 * - xsltCallTemplate() 3035 * - xsltDefaultProcessOneNode() 3036 * - xsltProcessOneNode() 3037 */ 3038 static void 3039 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, 3040 xmlNodePtr contextNode, 3041 xmlNodePtr list, 3042 xsltTemplatePtr templ, 3043 xsltStackElemPtr withParams) 3044 { 3045 int oldVarsBase = 0; 3046 long start = 0; 3047 xmlNodePtr cur; 3048 xsltStackElemPtr tmpParam = NULL; 3049 xmlDocPtr oldUserFragmentTop; 3050 3051 #ifdef XSLT_REFACTORED 3052 xsltStyleItemParamPtr iparam; 3053 #else 3054 xsltStylePreCompPtr iparam; 3055 #endif 3056 3057 #ifdef WITH_DEBUGGER 3058 int addCallResult = 0; 3059 #endif 3060 3061 if (ctxt == NULL) 3062 return; 3063 if (templ == NULL) { 3064 xsltTransformError(ctxt, NULL, list, 3065 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n"); 3066 return; 3067 } 3068 3069 #ifdef WITH_DEBUGGER 3070 if (ctxt->debugStatus != XSLT_DEBUG_NONE) { 3071 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode, 3072 list, templ, &addCallResult) == NULL) 3073 return; 3074 } 3075 #endif 3076 3077 if (list == NULL) 3078 return; 3079 CHECK_STOPPED; 3080 3081 if (ctxt->varsNr >= ctxt->maxTemplateVars) 3082 { 3083 xsltTransformError(ctxt, NULL, list, 3084 "xsltApplyXSLTTemplate: A potential infinite template recursion " 3085 "was detected.\n" 3086 "You can adjust maxTemplateVars (--maxvars) in order to " 3087 "raise the maximum number of variables/params (currently set to %d).\n", 3088 ctxt->maxTemplateVars); 3089 xsltDebug(ctxt, contextNode, list, NULL); 3090 ctxt->state = XSLT_STATE_STOPPED; 3091 return; 3092 } 3093 3094 oldUserFragmentTop = ctxt->tmpRVT; 3095 ctxt->tmpRVT = NULL; 3096 3097 /* 3098 * Initiate a distinct scope of local params/variables. 3099 */ 3100 oldVarsBase = ctxt->varsBase; 3101 ctxt->varsBase = ctxt->varsNr; 3102 3103 ctxt->node = contextNode; 3104 if (ctxt->profile) { 3105 templ->nbCalls++; 3106 start = xsltTimestamp(); 3107 profPush(ctxt, 0); 3108 profCallgraphAdd(templ, ctxt->templ); 3109 } 3110 /* 3111 * Push the xsl:template declaration onto the stack. 3112 */ 3113 templPush(ctxt, templ); 3114 3115 #ifdef WITH_XSLT_DEBUG_PROCESS 3116 if (templ->name != NULL) 3117 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 3118 "applying xsl:template '%s'\n", templ->name)); 3119 #endif 3120 /* 3121 * Process xsl:param instructions and skip those elements for 3122 * further processing. 3123 */ 3124 cur = list; 3125 do { 3126 if (cur->type == XML_TEXT_NODE) { 3127 cur = cur->next; 3128 continue; 3129 } 3130 if ((cur->type != XML_ELEMENT_NODE) || 3131 (cur->name[0] != 'p') || 3132 (cur->psvi == NULL) || 3133 (! xmlStrEqual(cur->name, BAD_CAST "param")) || 3134 (! IS_XSLT_ELEM(cur))) 3135 { 3136 break; 3137 } 3138 3139 list = cur->next; 3140 3141 #ifdef XSLT_REFACTORED 3142 iparam = (xsltStyleItemParamPtr) cur->psvi; 3143 #else 3144 iparam = (xsltStylePreCompPtr) cur->psvi; 3145 #endif 3146 3147 /* 3148 * Substitute xsl:param for a given xsl:with-param. 3149 * Since the XPath expression will reference the params/vars 3150 * by index, we need to slot the xsl:with-params in the 3151 * order of encountered xsl:params to keep the sequence of 3152 * params/variables in the stack exactly as it was at 3153 * compile time, 3154 */ 3155 tmpParam = NULL; 3156 if (withParams) { 3157 tmpParam = withParams; 3158 do { 3159 if ((tmpParam->name == (iparam->name)) && 3160 (tmpParam->nameURI == (iparam->ns))) 3161 { 3162 /* 3163 * Push the caller-parameter. 3164 */ 3165 xsltLocalVariablePush(ctxt, tmpParam, -1); 3166 break; 3167 } 3168 tmpParam = tmpParam->next; 3169 } while (tmpParam != NULL); 3170 } 3171 /* 3172 * Push the xsl:param. 3173 */ 3174 if (tmpParam == NULL) { 3175 /* 3176 * Note that we must assume that the added parameter 3177 * has a @depth of 0. 3178 */ 3179 xsltParseStylesheetParam(ctxt, cur); 3180 } 3181 cur = cur->next; 3182 } while (cur != NULL); 3183 /* 3184 * Process the sequence constructor. 3185 */ 3186 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3187 3188 /* 3189 * Remove remaining xsl:param and xsl:with-param items from 3190 * the stack. Don't free xsl:with-param items. 3191 */ 3192 if (ctxt->varsNr > ctxt->varsBase) 3193 xsltTemplateParamsCleanup(ctxt); 3194 ctxt->varsBase = oldVarsBase; 3195 3196 /* 3197 * Release user-created fragments stored in the scope 3198 * of xsl:template. Note that this mechanism is deprecated: 3199 * user code should now use xsltRegisterLocalRVT() instead 3200 * of the obsolete xsltRegisterTmpRVT(). 3201 */ 3202 if (ctxt->tmpRVT) { 3203 xmlDocPtr curdoc = ctxt->tmpRVT, tmp; 3204 3205 while (curdoc != NULL) { 3206 tmp = curdoc; 3207 curdoc = (xmlDocPtr) curdoc->next; 3208 xsltReleaseRVT(ctxt, tmp); 3209 } 3210 } 3211 ctxt->tmpRVT = oldUserFragmentTop; 3212 3213 /* 3214 * Pop the xsl:template declaration from the stack. 3215 */ 3216 templPop(ctxt); 3217 if (ctxt->profile) { 3218 long spent, child, total, end; 3219 3220 end = xsltTimestamp(); 3221 child = profPop(ctxt); 3222 total = end - start; 3223 spent = total - child; 3224 if (spent <= 0) { 3225 /* 3226 * Not possible unless the original calibration failed 3227 * we can try to correct it on the fly. 3228 */ 3229 xsltCalibrateAdjust(spent); 3230 spent = 0; 3231 } 3232 3233 templ->time += spent; 3234 if (ctxt->profNr > 0) 3235 ctxt->profTab[ctxt->profNr - 1] += total; 3236 } 3237 3238 #ifdef WITH_DEBUGGER 3239 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { 3240 xslDropCall(); 3241 } 3242 #endif 3243 } 3244 3245 3246 /** 3247 * xsltApplyOneTemplate: 3248 * @ctxt: a XSLT process context 3249 * @contextNode: the node in the source tree. 3250 * @list: the nodes of a sequence constructor 3251 * @templ: not used 3252 * @params: a set of parameters (xsl:param) or NULL 3253 * 3254 * Processes a sequence constructor on the current node in the source tree. 3255 * 3256 * @params are the already computed variable stack items; this function 3257 * pushes them on the variable stack, and pops them before exiting; it's 3258 * left to the caller to free or reuse @params afterwards. The initial 3259 * states of the variable stack will always be restored before this 3260 * function exits. 3261 * NOTE that this does *not* initiate a new distinct variable scope; i.e. 3262 * variables already on the stack are visible to the process. The caller's 3263 * side needs to start a new variable scope if needed (e.g. in exsl:function). 3264 * 3265 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not 3266 * provide a @templ); a non-NULL @templ might raise an error in the future. 3267 * 3268 * BIG NOTE: This function is not intended to process the content of an 3269 * xsl:template; it does not expect xsl:param instructions in @list and 3270 * will report errors if found. 3271 * 3272 * Called by: 3273 * - xsltEvalVariable() (variables.c) 3274 * - exsltFuncFunctionFunction() (libexsl/functions.c) 3275 */ 3276 void 3277 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, 3278 xmlNodePtr contextNode, 3279 xmlNodePtr list, 3280 xsltTemplatePtr templ ATTRIBUTE_UNUSED, 3281 xsltStackElemPtr params) 3282 { 3283 if ((ctxt == NULL) || (list == NULL)) 3284 return; 3285 CHECK_STOPPED; 3286 3287 if (params) { 3288 /* 3289 * This code should be obsolete - was previously used 3290 * by libexslt/functions.c, but due to bug 381319 the 3291 * logic there was changed. 3292 */ 3293 int oldVarsNr = ctxt->varsNr; 3294 3295 /* 3296 * Push the given xsl:param(s) onto the variable stack. 3297 */ 3298 while (params != NULL) { 3299 xsltLocalVariablePush(ctxt, params, -1); 3300 params = params->next; 3301 } 3302 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3303 /* 3304 * Pop the given xsl:param(s) from the stack but don't free them. 3305 */ 3306 xsltLocalVariablePop(ctxt, oldVarsNr, -2); 3307 } else 3308 xsltApplySequenceConstructor(ctxt, contextNode, list, templ); 3309 } 3310 3311 /************************************************************************ 3312 * * 3313 * XSLT-1.1 extensions * 3314 * * 3315 ************************************************************************/ 3316 3317 /** 3318 * xsltDocumentElem: 3319 * @ctxt: an XSLT processing context 3320 * @node: The current node 3321 * @inst: the instruction in the stylesheet 3322 * @castedComp: precomputed information 3323 * 3324 * Process an EXSLT/XSLT-1.1 document element 3325 */ 3326 void 3327 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node, 3328 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 3329 { 3330 #ifdef XSLT_REFACTORED 3331 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp; 3332 #else 3333 xsltStylePreCompPtr comp = castedComp; 3334 #endif 3335 xsltStylesheetPtr style = NULL; 3336 int ret; 3337 xmlChar *filename = NULL, *prop, *elements; 3338 xmlChar *element, *end; 3339 xmlDocPtr res = NULL; 3340 xmlDocPtr oldOutput; 3341 xmlNodePtr oldInsert, root; 3342 const char *oldOutputFile; 3343 xsltOutputType oldType; 3344 xmlChar *URL = NULL; 3345 const xmlChar *method; 3346 const xmlChar *doctypePublic; 3347 const xmlChar *doctypeSystem; 3348 const xmlChar *version; 3349 const xmlChar *encoding; 3350 int redirect_write_append = 0; 3351 3352 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 3353 return; 3354 3355 if (comp->filename == NULL) { 3356 3357 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { 3358 /* 3359 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE 3360 * (http://icl.com/saxon) 3361 * The @file is in no namespace. 3362 */ 3363 #ifdef WITH_XSLT_DEBUG_EXTRA 3364 xsltGenericDebug(xsltGenericDebugContext, 3365 "Found saxon:output extension\n"); 3366 #endif 3367 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3368 (const xmlChar *) "file", 3369 XSLT_SAXON_NAMESPACE); 3370 3371 if (URL == NULL) 3372 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3373 (const xmlChar *) "href", 3374 XSLT_SAXON_NAMESPACE); 3375 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { 3376 #ifdef WITH_XSLT_DEBUG_EXTRA 3377 xsltGenericDebug(xsltGenericDebugContext, 3378 "Found xalan:write extension\n"); 3379 #endif 3380 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3381 (const xmlChar *) 3382 "select", 3383 XSLT_XALAN_NAMESPACE); 3384 if (URL != NULL) { 3385 xmlXPathCompExprPtr cmp; 3386 xmlChar *val; 3387 3388 /* 3389 * Trying to handle bug #59212 3390 * The value of the "select" attribute is an 3391 * XPath expression. 3392 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect) 3393 */ 3394 cmp = xmlXPathCompile(URL); 3395 val = xsltEvalXPathString(ctxt, cmp); 3396 xmlXPathFreeCompExpr(cmp); 3397 xmlFree(URL); 3398 URL = val; 3399 } 3400 if (URL == NULL) 3401 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3402 (const xmlChar *) 3403 "file", 3404 XSLT_XALAN_NAMESPACE); 3405 if (URL == NULL) 3406 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3407 (const xmlChar *) 3408 "href", 3409 XSLT_XALAN_NAMESPACE); 3410 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { 3411 URL = xsltEvalAttrValueTemplate(ctxt, inst, 3412 (const xmlChar *) "href", 3413 NULL); 3414 } 3415 3416 } else { 3417 URL = xmlStrdup(comp->filename); 3418 } 3419 3420 if (URL == NULL) { 3421 xsltTransformError(ctxt, NULL, inst, 3422 "xsltDocumentElem: href/URI-Reference not found\n"); 3423 return; 3424 } 3425 3426 /* 3427 * If the computation failed, it's likely that the URL wasn't escaped 3428 */ 3429 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile); 3430 if (filename == NULL) { 3431 xmlChar *escURL; 3432 3433 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,"); 3434 if (escURL != NULL) { 3435 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile); 3436 xmlFree(escURL); 3437 } 3438 } 3439 3440 if (filename == NULL) { 3441 xsltTransformError(ctxt, NULL, inst, 3442 "xsltDocumentElem: URL computation failed for %s\n", 3443 URL); 3444 xmlFree(URL); 3445 return; 3446 } 3447 3448 /* 3449 * Security checking: can we write to this resource 3450 */ 3451 if (ctxt->sec != NULL) { 3452 ret = xsltCheckWrite(ctxt->sec, ctxt, filename); 3453 if (ret == 0) { 3454 xsltTransformError(ctxt, NULL, inst, 3455 "xsltDocumentElem: write rights for %s denied\n", 3456 filename); 3457 xmlFree(URL); 3458 xmlFree(filename); 3459 return; 3460 } 3461 } 3462 3463 oldOutputFile = ctxt->outputFile; 3464 oldOutput = ctxt->output; 3465 oldInsert = ctxt->insert; 3466 oldType = ctxt->type; 3467 ctxt->outputFile = (const char *) filename; 3468 3469 style = xsltNewStylesheet(); 3470 if (style == NULL) { 3471 xsltTransformError(ctxt, NULL, inst, 3472 "xsltDocumentElem: out of memory\n"); 3473 goto error; 3474 } 3475 3476 /* 3477 * Version described in 1.1 draft allows full parameterization 3478 * of the output. 3479 */ 3480 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3481 (const xmlChar *) "version", 3482 NULL); 3483 if (prop != NULL) { 3484 if (style->version != NULL) 3485 xmlFree(style->version); 3486 style->version = prop; 3487 } 3488 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3489 (const xmlChar *) "encoding", 3490 NULL); 3491 if (prop != NULL) { 3492 if (style->encoding != NULL) 3493 xmlFree(style->encoding); 3494 style->encoding = prop; 3495 } 3496 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3497 (const xmlChar *) "method", 3498 NULL); 3499 if (prop != NULL) { 3500 const xmlChar *URI; 3501 3502 if (style->method != NULL) 3503 xmlFree(style->method); 3504 style->method = NULL; 3505 if (style->methodURI != NULL) 3506 xmlFree(style->methodURI); 3507 style->methodURI = NULL; 3508 3509 URI = xsltGetQNameURI(inst, &prop); 3510 if (prop == NULL) { 3511 if (style != NULL) style->errors++; 3512 } else if (URI == NULL) { 3513 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || 3514 (xmlStrEqual(prop, (const xmlChar *) "html")) || 3515 (xmlStrEqual(prop, (const xmlChar *) "text"))) { 3516 style->method = prop; 3517 } else { 3518 xsltTransformError(ctxt, NULL, inst, 3519 "invalid value for method: %s\n", prop); 3520 if (style != NULL) style->warnings++; 3521 } 3522 } else { 3523 style->method = prop; 3524 style->methodURI = xmlStrdup(URI); 3525 } 3526 } 3527 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3528 (const xmlChar *) 3529 "doctype-system", NULL); 3530 if (prop != NULL) { 3531 if (style->doctypeSystem != NULL) 3532 xmlFree(style->doctypeSystem); 3533 style->doctypeSystem = prop; 3534 } 3535 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3536 (const xmlChar *) 3537 "doctype-public", NULL); 3538 if (prop != NULL) { 3539 if (style->doctypePublic != NULL) 3540 xmlFree(style->doctypePublic); 3541 style->doctypePublic = prop; 3542 } 3543 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3544 (const xmlChar *) "standalone", 3545 NULL); 3546 if (prop != NULL) { 3547 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3548 style->standalone = 1; 3549 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3550 style->standalone = 0; 3551 } else { 3552 xsltTransformError(ctxt, NULL, inst, 3553 "invalid value for standalone: %s\n", 3554 prop); 3555 if (style != NULL) style->warnings++; 3556 } 3557 xmlFree(prop); 3558 } 3559 3560 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3561 (const xmlChar *) "indent", 3562 NULL); 3563 if (prop != NULL) { 3564 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3565 style->indent = 1; 3566 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3567 style->indent = 0; 3568 } else { 3569 xsltTransformError(ctxt, NULL, inst, 3570 "invalid value for indent: %s\n", prop); 3571 if (style != NULL) style->warnings++; 3572 } 3573 xmlFree(prop); 3574 } 3575 3576 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3577 (const xmlChar *) 3578 "omit-xml-declaration", 3579 NULL); 3580 if (prop != NULL) { 3581 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3582 style->omitXmlDeclaration = 1; 3583 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3584 style->omitXmlDeclaration = 0; 3585 } else { 3586 xsltTransformError(ctxt, NULL, inst, 3587 "invalid value for omit-xml-declaration: %s\n", 3588 prop); 3589 if (style != NULL) style->warnings++; 3590 } 3591 xmlFree(prop); 3592 } 3593 3594 elements = xsltEvalAttrValueTemplate(ctxt, inst, 3595 (const xmlChar *) 3596 "cdata-section-elements", 3597 NULL); 3598 if (elements != NULL) { 3599 if (style->stripSpaces == NULL) 3600 style->stripSpaces = xmlHashCreate(10); 3601 if (style->stripSpaces == NULL) 3602 return; 3603 3604 element = elements; 3605 while (*element != 0) { 3606 while (IS_BLANK_CH(*element)) 3607 element++; 3608 if (*element == 0) 3609 break; 3610 end = element; 3611 while ((*end != 0) && (!IS_BLANK_CH(*end))) 3612 end++; 3613 element = xmlStrndup(element, end - element); 3614 if (element) { 3615 const xmlChar *URI; 3616 3617 #ifdef WITH_XSLT_DEBUG_PARSING 3618 xsltGenericDebug(xsltGenericDebugContext, 3619 "add cdata section output element %s\n", 3620 element); 3621 #endif 3622 URI = xsltGetQNameURI(inst, &element); 3623 3624 xmlHashAddEntry2(style->stripSpaces, element, URI, 3625 (xmlChar *) "cdata"); 3626 xmlFree(element); 3627 } 3628 element = end; 3629 } 3630 xmlFree(elements); 3631 } 3632 3633 /* 3634 * Create a new document tree and process the element template 3635 */ 3636 XSLT_GET_IMPORT_PTR(method, style, method) 3637 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3638 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3639 XSLT_GET_IMPORT_PTR(version, style, version) 3640 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 3641 3642 if ((method != NULL) && 3643 (!xmlStrEqual(method, (const xmlChar *) "xml"))) { 3644 if (xmlStrEqual(method, (const xmlChar *) "html")) { 3645 ctxt->type = XSLT_OUTPUT_HTML; 3646 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3647 res = htmlNewDoc(doctypeSystem, doctypePublic); 3648 else { 3649 if (version != NULL) { 3650 #ifdef XSLT_GENERATE_HTML_DOCTYPE 3651 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 3652 #endif 3653 } 3654 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3655 } 3656 if (res == NULL) 3657 goto error; 3658 res->dict = ctxt->dict; 3659 xmlDictReference(res->dict); 3660 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 3661 xsltTransformError(ctxt, NULL, inst, 3662 "xsltDocumentElem: unsupported method xhtml\n"); 3663 ctxt->type = XSLT_OUTPUT_HTML; 3664 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3665 if (res == NULL) 3666 goto error; 3667 res->dict = ctxt->dict; 3668 xmlDictReference(res->dict); 3669 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 3670 ctxt->type = XSLT_OUTPUT_TEXT; 3671 res = xmlNewDoc(style->version); 3672 if (res == NULL) 3673 goto error; 3674 res->dict = ctxt->dict; 3675 xmlDictReference(res->dict); 3676 #ifdef WITH_XSLT_DEBUG 3677 xsltGenericDebug(xsltGenericDebugContext, 3678 "reusing transformation dict for output\n"); 3679 #endif 3680 } else { 3681 xsltTransformError(ctxt, NULL, inst, 3682 "xsltDocumentElem: unsupported method (%s)\n", 3683 method); 3684 goto error; 3685 } 3686 } else { 3687 ctxt->type = XSLT_OUTPUT_XML; 3688 res = xmlNewDoc(style->version); 3689 if (res == NULL) 3690 goto error; 3691 res->dict = ctxt->dict; 3692 xmlDictReference(res->dict); 3693 #ifdef WITH_XSLT_DEBUG 3694 xsltGenericDebug(xsltGenericDebugContext, 3695 "reusing transformation dict for output\n"); 3696 #endif 3697 } 3698 res->charset = XML_CHAR_ENCODING_UTF8; 3699 if (encoding != NULL) 3700 res->encoding = xmlStrdup(encoding); 3701 ctxt->output = res; 3702 ctxt->insert = (xmlNodePtr) res; 3703 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); 3704 3705 /* 3706 * Do some post processing work depending on the generated output 3707 */ 3708 root = xmlDocGetRootElement(res); 3709 if (root != NULL) { 3710 const xmlChar *doctype = NULL; 3711 3712 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 3713 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 3714 if (doctype == NULL) 3715 doctype = root->name; 3716 3717 /* 3718 * Apply the default selection of the method 3719 */ 3720 if ((method == NULL) && 3721 (root->ns == NULL) && 3722 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 3723 xmlNodePtr tmp; 3724 3725 tmp = res->children; 3726 while ((tmp != NULL) && (tmp != root)) { 3727 if (tmp->type == XML_ELEMENT_NODE) 3728 break; 3729 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 3730 break; 3731 tmp = tmp->next; 3732 } 3733 if (tmp == root) { 3734 ctxt->type = XSLT_OUTPUT_HTML; 3735 res->type = XML_HTML_DOCUMENT_NODE; 3736 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 3737 res->intSubset = xmlCreateIntSubset(res, doctype, 3738 doctypePublic, 3739 doctypeSystem); 3740 #ifdef XSLT_GENERATE_HTML_DOCTYPE 3741 } else if (version != NULL) { 3742 xsltGetHTMLIDs(version, &doctypePublic, 3743 &doctypeSystem); 3744 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3745 res->intSubset = 3746 xmlCreateIntSubset(res, doctype, 3747 doctypePublic, 3748 doctypeSystem); 3749 #endif 3750 } 3751 } 3752 3753 } 3754 if (ctxt->type == XSLT_OUTPUT_XML) { 3755 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3756 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3757 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3758 res->intSubset = xmlCreateIntSubset(res, doctype, 3759 doctypePublic, 3760 doctypeSystem); 3761 } 3762 } 3763 3764 /* 3765 * Calls to redirect:write also take an optional attribute append. 3766 * Attribute append="true|yes" which will attempt to simply append 3767 * to an existing file instead of always opening a new file. The 3768 * default behavior of always overwriting the file still happens 3769 * if we do not specify append. 3770 * Note that append use will forbid use of remote URI target. 3771 */ 3772 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", 3773 NULL); 3774 if (prop != NULL) { 3775 if (xmlStrEqual(prop, (const xmlChar *) "true") || 3776 xmlStrEqual(prop, (const xmlChar *) "yes")) { 3777 style->omitXmlDeclaration = 1; 3778 redirect_write_append = 1; 3779 } else 3780 style->omitXmlDeclaration = 0; 3781 xmlFree(prop); 3782 } 3783 3784 if (redirect_write_append) { 3785 FILE *f; 3786 3787 f = fopen((const char *) filename, "ab"); 3788 if (f == NULL) { 3789 ret = -1; 3790 } else { 3791 ret = xsltSaveResultToFile(f, res, style); 3792 fclose(f); 3793 } 3794 } else { 3795 ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); 3796 } 3797 if (ret < 0) { 3798 xsltTransformError(ctxt, NULL, inst, 3799 "xsltDocumentElem: unable to save to %s\n", 3800 filename); 3801 #ifdef WITH_XSLT_DEBUG_EXTRA 3802 } else { 3803 xsltGenericDebug(xsltGenericDebugContext, 3804 "Wrote %d bytes to %s\n", ret, filename); 3805 #endif 3806 } 3807 3808 error: 3809 ctxt->output = oldOutput; 3810 ctxt->insert = oldInsert; 3811 ctxt->type = oldType; 3812 ctxt->outputFile = oldOutputFile; 3813 if (URL != NULL) 3814 xmlFree(URL); 3815 if (filename != NULL) 3816 xmlFree(filename); 3817 if (style != NULL) 3818 xsltFreeStylesheet(style); 3819 if (res != NULL) 3820 xmlFreeDoc(res); 3821 } 3822 3823 /************************************************************************ 3824 * * 3825 * Most of the XSLT-1.0 transformations * 3826 * * 3827 ************************************************************************/ 3828 3829 /** 3830 * xsltSort: 3831 * @ctxt: a XSLT process context 3832 * @node: the node in the source tree. 3833 * @inst: the xslt sort node 3834 * @comp: precomputed information 3835 * 3836 * function attached to xsl:sort nodes, but this should not be 3837 * called directly 3838 */ 3839 void 3840 xsltSort(xsltTransformContextPtr ctxt, 3841 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, 3842 xsltStylePreCompPtr comp) { 3843 if (comp == NULL) { 3844 xsltTransformError(ctxt, NULL, inst, 3845 "xsl:sort : compilation failed\n"); 3846 return; 3847 } 3848 xsltTransformError(ctxt, NULL, inst, 3849 "xsl:sort : improper use this should not be reached\n"); 3850 } 3851 3852 /** 3853 * xsltCopy: 3854 * @ctxt: an XSLT process context 3855 * @node: the node in the source tree 3856 * @inst: the element node of the XSLT-copy instruction 3857 * @castedComp: computed information of the XSLT-copy instruction 3858 * 3859 * Execute the XSLT-copy instruction on the source node. 3860 */ 3861 void 3862 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, 3863 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 3864 { 3865 #ifdef XSLT_REFACTORED 3866 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; 3867 #else 3868 xsltStylePreCompPtr comp = castedComp; 3869 #endif 3870 xmlNodePtr copy, oldInsert; 3871 3872 oldInsert = ctxt->insert; 3873 if (ctxt->insert != NULL) { 3874 switch (node->type) { 3875 case XML_TEXT_NODE: 3876 case XML_CDATA_SECTION_NODE: 3877 /* 3878 * This text comes from the stylesheet 3879 * For stylesheets, the set of whitespace-preserving 3880 * element names consists of just xsl:text. 3881 */ 3882 #ifdef WITH_XSLT_DEBUG_PROCESS 3883 if (node->type == XML_CDATA_SECTION_NODE) { 3884 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3885 "xsltCopy: CDATA text %s\n", node->content)); 3886 } else { 3887 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3888 "xsltCopy: text %s\n", node->content)); 3889 } 3890 #endif 3891 xsltCopyText(ctxt, ctxt->insert, node, 0); 3892 break; 3893 case XML_DOCUMENT_NODE: 3894 case XML_HTML_DOCUMENT_NODE: 3895 break; 3896 case XML_ELEMENT_NODE: 3897 /* 3898 * REVISIT NOTE: The "fake" is a doc-node, not an element node. 3899 * REMOVED: 3900 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) 3901 * return; 3902 */ 3903 3904 #ifdef WITH_XSLT_DEBUG_PROCESS 3905 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3906 "xsltCopy: node %s\n", node->name)); 3907 #endif 3908 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); 3909 ctxt->insert = copy; 3910 if (comp->use != NULL) { 3911 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 3912 } 3913 break; 3914 case XML_ATTRIBUTE_NODE: { 3915 #ifdef WITH_XSLT_DEBUG_PROCESS 3916 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3917 "xsltCopy: attribute %s\n", node->name)); 3918 #endif 3919 /* 3920 * REVISIT: We could also raise an error if the parent is not 3921 * an element node. 3922 * OPTIMIZE TODO: Can we set the value/children of the 3923 * attribute without an intermediate copy of the string value? 3924 */ 3925 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); 3926 break; 3927 } 3928 case XML_PI_NODE: 3929 #ifdef WITH_XSLT_DEBUG_PROCESS 3930 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3931 "xsltCopy: PI %s\n", node->name)); 3932 #endif 3933 copy = xmlNewDocPI(ctxt->insert->doc, node->name, 3934 node->content); 3935 copy = xsltAddChild(ctxt->insert, copy); 3936 break; 3937 case XML_COMMENT_NODE: 3938 #ifdef WITH_XSLT_DEBUG_PROCESS 3939 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3940 "xsltCopy: comment\n")); 3941 #endif 3942 copy = xmlNewComment(node->content); 3943 copy = xsltAddChild(ctxt->insert, copy); 3944 break; 3945 case XML_NAMESPACE_DECL: 3946 #ifdef WITH_XSLT_DEBUG_PROCESS 3947 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3948 "xsltCopy: namespace declaration\n")); 3949 #endif 3950 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); 3951 break; 3952 default: 3953 break; 3954 3955 } 3956 } 3957 3958 switch (node->type) { 3959 case XML_DOCUMENT_NODE: 3960 case XML_HTML_DOCUMENT_NODE: 3961 case XML_ELEMENT_NODE: 3962 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 3963 NULL); 3964 break; 3965 default: 3966 break; 3967 } 3968 ctxt->insert = oldInsert; 3969 } 3970 3971 /** 3972 * xsltText: 3973 * @ctxt: a XSLT process context 3974 * @node: the node in the source tree. 3975 * @inst: the xslt text node 3976 * @comp: precomputed information 3977 * 3978 * Process the xslt text node on the source node 3979 */ 3980 void 3981 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, 3982 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { 3983 if ((inst->children != NULL) && (comp != NULL)) { 3984 xmlNodePtr text = inst->children; 3985 xmlNodePtr copy; 3986 3987 while (text != NULL) { 3988 if ((text->type != XML_TEXT_NODE) && 3989 (text->type != XML_CDATA_SECTION_NODE)) { 3990 xsltTransformError(ctxt, NULL, inst, 3991 "xsl:text content problem\n"); 3992 break; 3993 } 3994 copy = xmlNewDocText(ctxt->output, text->content); 3995 if (text->type != XML_CDATA_SECTION_NODE) { 3996 #ifdef WITH_XSLT_DEBUG_PARSING 3997 xsltGenericDebug(xsltGenericDebugContext, 3998 "Disable escaping: %s\n", text->content); 3999 #endif 4000 copy->name = xmlStringTextNoenc; 4001 } 4002 copy = xsltAddChild(ctxt->insert, copy); 4003 text = text->next; 4004 } 4005 } 4006 } 4007 4008 /** 4009 * xsltElement: 4010 * @ctxt: a XSLT process context 4011 * @node: the node in the source tree. 4012 * @inst: the xslt element node 4013 * @castedComp: precomputed information 4014 * 4015 * Process the xslt element node on the source node 4016 */ 4017 void 4018 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, 4019 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 4020 #ifdef XSLT_REFACTORED 4021 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; 4022 #else 4023 xsltStylePreCompPtr comp = castedComp; 4024 #endif 4025 xmlChar *prop = NULL; 4026 const xmlChar *name, *prefix = NULL, *nsName = NULL; 4027 xmlNodePtr copy; 4028 xmlNodePtr oldInsert; 4029 4030 if (ctxt->insert == NULL) 4031 return; 4032 4033 /* 4034 * A comp->has_name == 0 indicates that we need to skip this instruction, 4035 * since it was evaluated to be invalid already during compilation. 4036 */ 4037 if (!comp->has_name) 4038 return; 4039 4040 /* 4041 * stack and saves 4042 */ 4043 oldInsert = ctxt->insert; 4044 4045 if (comp->name == NULL) { 4046 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 4047 prop = xsltEvalAttrValueTemplate(ctxt, inst, 4048 (const xmlChar *) "name", XSLT_NAMESPACE); 4049 if (prop == NULL) { 4050 xsltTransformError(ctxt, NULL, inst, 4051 "xsl:element: The attribute 'name' is missing.\n"); 4052 goto error; 4053 } 4054 if (xmlValidateQName(prop, 0)) { 4055 xsltTransformError(ctxt, NULL, inst, 4056 "xsl:element: The effective name '%s' is not a " 4057 "valid QName.\n", prop); 4058 /* we fall through to catch any further errors, if possible */ 4059 } 4060 name = xsltSplitQName(ctxt->dict, prop, &prefix); 4061 xmlFree(prop); 4062 } else { 4063 /* 4064 * The "name" value was static. 4065 */ 4066 #ifdef XSLT_REFACTORED 4067 prefix = comp->nsPrefix; 4068 name = comp->name; 4069 #else 4070 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 4071 #endif 4072 } 4073 4074 /* 4075 * Create the new element 4076 */ 4077 if (ctxt->output->dict == ctxt->dict) { 4078 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); 4079 } else { 4080 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); 4081 } 4082 if (copy == NULL) { 4083 xsltTransformError(ctxt, NULL, inst, 4084 "xsl:element : creation of %s failed\n", name); 4085 return; 4086 } 4087 copy = xsltAddChild(ctxt->insert, copy); 4088 if (copy == NULL) { 4089 xsltTransformError(ctxt, NULL, inst, 4090 "xsl:element : xsltAddChild failed\n"); 4091 return; 4092 } 4093 4094 /* 4095 * Namespace 4096 * --------- 4097 */ 4098 if (comp->has_ns) { 4099 if (comp->ns != NULL) { 4100 /* 4101 * No AVT; just plain text for the namespace name. 4102 */ 4103 if (comp->ns[0] != 0) 4104 nsName = comp->ns; 4105 } else { 4106 xmlChar *tmpNsName; 4107 /* 4108 * Eval the AVT. 4109 */ 4110 /* TODO: check attr acquisition wrt to the XSLT namespace */ 4111 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 4112 (const xmlChar *) "namespace", XSLT_NAMESPACE); 4113 /* 4114 * SPEC XSLT 1.0: 4115 * "If the string is empty, then the expanded-name of the 4116 * attribute has a null namespace URI." 4117 */ 4118 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 4119 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 4120 xmlFree(tmpNsName); 4121 } 4122 4123 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 4124 xsltTransformError(ctxt, NULL, inst, 4125 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 4126 "forbidden.\n"); 4127 goto error; 4128 } 4129 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 4130 prefix = BAD_CAST "xml"; 4131 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 4132 prefix = NULL; 4133 } 4134 } else { 4135 xmlNsPtr ns; 4136 /* 4137 * SPEC XSLT 1.0: 4138 * "If the namespace attribute is not present, then the QName is 4139 * expanded into an expanded-name using the namespace declarations 4140 * in effect for the xsl:element element, including any default 4141 * namespace declaration. 4142 */ 4143 ns = xmlSearchNs(inst->doc, inst, prefix); 4144 if (ns == NULL) { 4145 /* 4146 * TODO: Check this in the compilation layer in case it's a 4147 * static value. 4148 */ 4149 if (prefix != NULL) { 4150 xsltTransformError(ctxt, NULL, inst, 4151 "xsl:element: The QName '%s:%s' has no " 4152 "namespace binding in scope in the stylesheet; " 4153 "this is an error, since the namespace was not " 4154 "specified by the instruction itself.\n", prefix, name); 4155 } 4156 } else 4157 nsName = ns->href; 4158 } 4159 /* 4160 * Find/create a matching ns-decl in the result tree. 4161 */ 4162 if (nsName != NULL) { 4163 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { 4164 /* Don't use a prefix of "xmlns" */ 4165 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 4166 4167 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); 4168 4169 xmlFree(pref); 4170 } else { 4171 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, 4172 copy); 4173 } 4174 } else if ((copy->parent != NULL) && 4175 (copy->parent->type == XML_ELEMENT_NODE) && 4176 (copy->parent->ns != NULL)) 4177 { 4178 /* 4179 * "Undeclare" the default namespace. 4180 */ 4181 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); 4182 } 4183 4184 ctxt->insert = copy; 4185 4186 if (comp->has_use) { 4187 if (comp->use != NULL) { 4188 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 4189 } else { 4190 xmlChar *attrSets = NULL; 4191 /* 4192 * BUG TODO: use-attribute-sets is not a value template. 4193 * use-attribute-sets = qnames 4194 */ 4195 attrSets = xsltEvalAttrValueTemplate(ctxt, inst, 4196 (const xmlChar *)"use-attribute-sets", NULL); 4197 if (attrSets != NULL) { 4198 xsltApplyAttributeSet(ctxt, node, inst, attrSets); 4199 xmlFree(attrSets); 4200 } 4201 } 4202 } 4203 /* 4204 * Instantiate the sequence constructor. 4205 */ 4206 if (inst->children != NULL) 4207 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 4208 NULL); 4209 4210 error: 4211 ctxt->insert = oldInsert; 4212 return; 4213 } 4214 4215 4216 /** 4217 * xsltComment: 4218 * @ctxt: a XSLT process context 4219 * @node: the node in the source tree. 4220 * @inst: the xslt comment node 4221 * @comp: precomputed information 4222 * 4223 * Process the xslt comment node on the source node 4224 */ 4225 void 4226 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, 4227 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { 4228 xmlChar *value = NULL; 4229 xmlNodePtr commentNode; 4230 int len; 4231 4232 value = xsltEvalTemplateString(ctxt, node, inst); 4233 /* TODO: use or generate the compiled form */ 4234 len = xmlStrlen(value); 4235 if (len > 0) { 4236 if ((value[len-1] == '-') || 4237 (xmlStrstr(value, BAD_CAST "--"))) { 4238 xsltTransformError(ctxt, NULL, inst, 4239 "xsl:comment : '--' or ending '-' not allowed in comment\n"); 4240 /* fall through to try to catch further errors */ 4241 } 4242 } 4243 #ifdef WITH_XSLT_DEBUG_PROCESS 4244 if (value == NULL) { 4245 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4246 "xsltComment: empty\n")); 4247 } else { 4248 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4249 "xsltComment: content %s\n", value)); 4250 } 4251 #endif 4252 4253 commentNode = xmlNewComment(value); 4254 commentNode = xsltAddChild(ctxt->insert, commentNode); 4255 4256 if (value != NULL) 4257 xmlFree(value); 4258 } 4259 4260 /** 4261 * xsltProcessingInstruction: 4262 * @ctxt: a XSLT process context 4263 * @node: the node in the source tree. 4264 * @inst: the xslt processing-instruction node 4265 * @castedComp: precomputed information 4266 * 4267 * Process the xslt processing-instruction node on the source node 4268 */ 4269 void 4270 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, 4271 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 4272 #ifdef XSLT_REFACTORED 4273 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; 4274 #else 4275 xsltStylePreCompPtr comp = castedComp; 4276 #endif 4277 const xmlChar *name; 4278 xmlChar *value = NULL; 4279 xmlNodePtr pi; 4280 4281 4282 if (ctxt->insert == NULL) 4283 return; 4284 if (comp->has_name == 0) 4285 return; 4286 if (comp->name == NULL) { 4287 name = xsltEvalAttrValueTemplate(ctxt, inst, 4288 (const xmlChar *)"name", NULL); 4289 if (name == NULL) { 4290 xsltTransformError(ctxt, NULL, inst, 4291 "xsl:processing-instruction : name is missing\n"); 4292 goto error; 4293 } 4294 } else { 4295 name = comp->name; 4296 } 4297 /* TODO: check that it's both an an NCName and a PITarget. */ 4298 4299 4300 value = xsltEvalTemplateString(ctxt, node, inst); 4301 if (xmlStrstr(value, BAD_CAST "?>") != NULL) { 4302 xsltTransformError(ctxt, NULL, inst, 4303 "xsl:processing-instruction: '?>' not allowed within PI content\n"); 4304 goto error; 4305 } 4306 #ifdef WITH_XSLT_DEBUG_PROCESS 4307 if (value == NULL) { 4308 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4309 "xsltProcessingInstruction: %s empty\n", name)); 4310 } else { 4311 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4312 "xsltProcessingInstruction: %s content %s\n", name, value)); 4313 } 4314 #endif 4315 4316 pi = xmlNewDocPI(ctxt->insert->doc, name, value); 4317 pi = xsltAddChild(ctxt->insert, pi); 4318 4319 error: 4320 if ((name != NULL) && (name != comp->name)) 4321 xmlFree((xmlChar *) name); 4322 if (value != NULL) 4323 xmlFree(value); 4324 } 4325 4326 /** 4327 * xsltCopyOf: 4328 * @ctxt: an XSLT transformation context 4329 * @node: the current node in the source tree 4330 * @inst: the element node of the XSLT copy-of instruction 4331 * @castedComp: precomputed information of the XSLT copy-of instruction 4332 * 4333 * Process the XSLT copy-of instruction. 4334 */ 4335 void 4336 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4337 xmlNodePtr inst, xsltStylePreCompPtr castedComp) { 4338 #ifdef XSLT_REFACTORED 4339 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; 4340 #else 4341 xsltStylePreCompPtr comp = castedComp; 4342 #endif 4343 xmlXPathObjectPtr res = NULL; 4344 xmlNodeSetPtr list = NULL; 4345 int i; 4346 4347 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4348 return; 4349 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4350 xsltTransformError(ctxt, NULL, inst, 4351 "xsl:copy-of : compilation failed\n"); 4352 return; 4353 } 4354 4355 /* 4356 * SPEC XSLT 1.0: 4357 * "The xsl:copy-of element can be used to insert a result tree 4358 * fragment into the result tree, without first converting it to 4359 * a string as xsl:value-of does (see [7.6.1 Generating Text with 4360 * xsl:value-of]). The required select attribute contains an 4361 * expression. When the result of evaluating the expression is a 4362 * result tree fragment, the complete fragment is copied into the 4363 * result tree. When the result is a node-set, all the nodes in the 4364 * set are copied in document order into the result tree; copying 4365 * an element node copies the attribute nodes, namespace nodes and 4366 * children of the element node as well as the element node itself; 4367 * a root node is copied by copying its children. When the result 4368 * is neither a node-set nor a result tree fragment, the result is 4369 * converted to a string and then inserted into the result tree, 4370 * as with xsl:value-of. 4371 */ 4372 4373 #ifdef WITH_XSLT_DEBUG_PROCESS 4374 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4375 "xsltCopyOf: select %s\n", comp->select)); 4376 #endif 4377 4378 /* 4379 * Evaluate the "select" expression. 4380 */ 4381 res = xsltPreCompEval(ctxt, node, comp); 4382 4383 if (res != NULL) { 4384 if (res->type == XPATH_NODESET) { 4385 /* 4386 * Node-set 4387 * -------- 4388 */ 4389 #ifdef WITH_XSLT_DEBUG_PROCESS 4390 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4391 "xsltCopyOf: result is a node set\n")); 4392 #endif 4393 list = res->nodesetval; 4394 if (list != NULL) { 4395 xmlNodePtr cur; 4396 /* 4397 * The list is already sorted in document order by XPath. 4398 * Append everything in this order under ctxt->insert. 4399 */ 4400 for (i = 0;i < list->nodeNr;i++) { 4401 cur = list->nodeTab[i]; 4402 if (cur == NULL) 4403 continue; 4404 if ((cur->type == XML_DOCUMENT_NODE) || 4405 (cur->type == XML_HTML_DOCUMENT_NODE)) 4406 { 4407 xsltCopyTreeList(ctxt, inst, 4408 cur->children, ctxt->insert, 0, 0); 4409 } else if (cur->type == XML_ATTRIBUTE_NODE) { 4410 xsltShallowCopyAttr(ctxt, inst, 4411 ctxt->insert, (xmlAttrPtr) cur); 4412 } else { 4413 xsltCopyTree(ctxt, inst, cur, ctxt->insert, 0, 0); 4414 } 4415 } 4416 } 4417 } else if (res->type == XPATH_XSLT_TREE) { 4418 /* 4419 * Result tree fragment 4420 * -------------------- 4421 * E.g. via <xsl:variable ...><foo/></xsl:variable> 4422 * Note that the root node of such trees is an xmlDocPtr in Libxslt. 4423 */ 4424 #ifdef WITH_XSLT_DEBUG_PROCESS 4425 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4426 "xsltCopyOf: result is a result tree fragment\n")); 4427 #endif 4428 list = res->nodesetval; 4429 if ((list != NULL) && (list->nodeTab != NULL) && 4430 (list->nodeTab[0] != NULL) && 4431 (IS_XSLT_REAL_NODE(list->nodeTab[0]))) 4432 { 4433 xsltCopyTreeList(ctxt, inst, 4434 list->nodeTab[0]->children, ctxt->insert, 0, 0); 4435 } 4436 } else { 4437 xmlChar *value = NULL; 4438 /* 4439 * Convert to a string. 4440 */ 4441 value = xmlXPathCastToString(res); 4442 if (value == NULL) { 4443 xsltTransformError(ctxt, NULL, inst, 4444 "Internal error in xsltCopyOf(): " 4445 "failed to cast an XPath object to string.\n"); 4446 ctxt->state = XSLT_STATE_STOPPED; 4447 } else { 4448 if (value[0] != 0) { 4449 /* 4450 * Append content as text node. 4451 */ 4452 xsltCopyTextString(ctxt, ctxt->insert, value, 0); 4453 } 4454 xmlFree(value); 4455 4456 #ifdef WITH_XSLT_DEBUG_PROCESS 4457 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4458 "xsltCopyOf: result %s\n", res->stringval)); 4459 #endif 4460 } 4461 } 4462 } else { 4463 ctxt->state = XSLT_STATE_STOPPED; 4464 } 4465 4466 if (res != NULL) 4467 xmlXPathFreeObject(res); 4468 } 4469 4470 /** 4471 * xsltValueOf: 4472 * @ctxt: a XSLT process context 4473 * @node: the node in the source tree. 4474 * @inst: the xslt value-of node 4475 * @castedComp: precomputed information 4476 * 4477 * Process the xslt value-of node on the source node 4478 */ 4479 void 4480 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4481 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4482 { 4483 #ifdef XSLT_REFACTORED 4484 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; 4485 #else 4486 xsltStylePreCompPtr comp = castedComp; 4487 #endif 4488 xmlXPathObjectPtr res = NULL; 4489 xmlChar *value = NULL; 4490 4491 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4492 return; 4493 4494 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4495 xsltTransformError(ctxt, NULL, inst, 4496 "Internal error in xsltValueOf(): " 4497 "The XSLT 'value-of' instruction was not compiled.\n"); 4498 return; 4499 } 4500 4501 #ifdef WITH_XSLT_DEBUG_PROCESS 4502 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4503 "xsltValueOf: select %s\n", comp->select)); 4504 #endif 4505 4506 res = xsltPreCompEval(ctxt, node, comp); 4507 4508 /* 4509 * Cast the XPath object to string. 4510 */ 4511 if (res != NULL) { 4512 value = xmlXPathCastToString(res); 4513 if (value == NULL) { 4514 xsltTransformError(ctxt, NULL, inst, 4515 "Internal error in xsltValueOf(): " 4516 "failed to cast an XPath object to string.\n"); 4517 ctxt->state = XSLT_STATE_STOPPED; 4518 goto error; 4519 } 4520 if (value[0] != 0) { 4521 xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); 4522 } 4523 } else { 4524 xsltTransformError(ctxt, NULL, inst, 4525 "XPath evaluation returned no result.\n"); 4526 ctxt->state = XSLT_STATE_STOPPED; 4527 goto error; 4528 } 4529 4530 #ifdef WITH_XSLT_DEBUG_PROCESS 4531 if (value) { 4532 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4533 "xsltValueOf: result '%s'\n", value)); 4534 } 4535 #endif 4536 4537 error: 4538 if (value != NULL) 4539 xmlFree(value); 4540 if (res != NULL) 4541 xmlXPathFreeObject(res); 4542 } 4543 4544 /** 4545 * xsltNumber: 4546 * @ctxt: a XSLT process context 4547 * @node: the node in the source tree. 4548 * @inst: the xslt number node 4549 * @castedComp: precomputed information 4550 * 4551 * Process the xslt number node on the source node 4552 */ 4553 void 4554 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, 4555 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4556 { 4557 #ifdef XSLT_REFACTORED 4558 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; 4559 #else 4560 xsltStylePreCompPtr comp = castedComp; 4561 #endif 4562 xmlXPathContextPtr xpctxt; 4563 xmlNsPtr *oldXPNamespaces; 4564 int oldXPNsNr; 4565 4566 if (comp == NULL) { 4567 xsltTransformError(ctxt, NULL, inst, 4568 "xsl:number : compilation failed\n"); 4569 return; 4570 } 4571 4572 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4573 return; 4574 4575 comp->numdata.doc = inst->doc; 4576 comp->numdata.node = inst; 4577 4578 xpctxt = ctxt->xpathCtxt; 4579 oldXPNsNr = xpctxt->nsNr; 4580 oldXPNamespaces = xpctxt->namespaces; 4581 4582 #ifdef XSLT_REFACTORED 4583 if (comp->inScopeNs != NULL) { 4584 xpctxt->namespaces = comp->inScopeNs->list; 4585 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 4586 } else { 4587 xpctxt->namespaces = NULL; 4588 xpctxt->nsNr = 0; 4589 } 4590 #else 4591 xpctxt->namespaces = comp->nsList; 4592 xpctxt->nsNr = comp->nsNr; 4593 #endif 4594 4595 xsltNumberFormat(ctxt, &comp->numdata, node); 4596 4597 xpctxt->nsNr = oldXPNsNr; 4598 xpctxt->namespaces = oldXPNamespaces; 4599 } 4600 4601 /** 4602 * xsltApplyImports: 4603 * @ctxt: an XSLT transformation context 4604 * @contextNode: the current node in the source tree. 4605 * @inst: the element node of the XSLT 'apply-imports' instruction 4606 * @comp: the compiled instruction 4607 * 4608 * Process the XSLT apply-imports element. 4609 */ 4610 void 4611 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 4612 xmlNodePtr inst, 4613 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) 4614 { 4615 xsltTemplatePtr templ; 4616 4617 if ((ctxt == NULL) || (inst == NULL)) 4618 return; 4619 4620 if (comp == NULL) { 4621 xsltTransformError(ctxt, NULL, inst, 4622 "Internal error in xsltApplyImports(): " 4623 "The XSLT 'apply-imports' instruction was not compiled.\n"); 4624 return; 4625 } 4626 /* 4627 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the 4628 * same; the former is the "Current Template Rule" as defined by the 4629 * XSLT spec, the latter is simply the template struct being 4630 * currently processed. 4631 */ 4632 if (ctxt->currentTemplateRule == NULL) { 4633 /* 4634 * SPEC XSLT 2.0: 4635 * "[ERR XTDE0560] It is a non-recoverable dynamic error if 4636 * xsl:apply-imports or xsl:next-match is evaluated when the 4637 * current template rule is null." 4638 */ 4639 xsltTransformError(ctxt, NULL, inst, 4640 "It is an error to call 'apply-imports' " 4641 "when there's no current template rule.\n"); 4642 return; 4643 } 4644 /* 4645 * TODO: Check if this is correct. 4646 */ 4647 templ = xsltGetTemplate(ctxt, contextNode, 4648 ctxt->currentTemplateRule->style); 4649 4650 if (templ != NULL) { 4651 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; 4652 /* 4653 * Set the current template rule. 4654 */ 4655 ctxt->currentTemplateRule = templ; 4656 /* 4657 * URGENT TODO: Need xsl:with-param be handled somehow here? 4658 */ 4659 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, 4660 templ, NULL); 4661 4662 ctxt->currentTemplateRule = oldCurTemplRule; 4663 } 4664 else { 4665 /* Use built-in templates. */ 4666 xsltDefaultProcessOneNode(ctxt, contextNode, NULL); 4667 } 4668 } 4669 4670 /** 4671 * xsltCallTemplate: 4672 * @ctxt: a XSLT transformation context 4673 * @node: the "current node" in the source tree 4674 * @inst: the XSLT 'call-template' instruction 4675 * @castedComp: the compiled information of the instruction 4676 * 4677 * Processes the XSLT call-template instruction on the source node. 4678 */ 4679 void 4680 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, 4681 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4682 { 4683 #ifdef XSLT_REFACTORED 4684 xsltStyleItemCallTemplatePtr comp = 4685 (xsltStyleItemCallTemplatePtr) castedComp; 4686 #else 4687 xsltStylePreCompPtr comp = castedComp; 4688 #endif 4689 xsltStackElemPtr withParams = NULL; 4690 4691 if (ctxt->insert == NULL) 4692 return; 4693 if (comp == NULL) { 4694 xsltTransformError(ctxt, NULL, inst, 4695 "The XSLT 'call-template' instruction was not compiled.\n"); 4696 return; 4697 } 4698 4699 /* 4700 * The template must have been precomputed 4701 */ 4702 if (comp->templ == NULL) { 4703 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); 4704 if (comp->templ == NULL) { 4705 if (comp->ns != NULL) { 4706 xsltTransformError(ctxt, NULL, inst, 4707 "The called template '{%s}%s' was not found.\n", 4708 comp->ns, comp->name); 4709 } else { 4710 xsltTransformError(ctxt, NULL, inst, 4711 "The called template '%s' was not found.\n", 4712 comp->name); 4713 } 4714 return; 4715 } 4716 } 4717 4718 #ifdef WITH_XSLT_DEBUG_PROCESS 4719 if ((comp != NULL) && (comp->name != NULL)) 4720 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4721 "call-template: name %s\n", comp->name)); 4722 #endif 4723 4724 if (inst->children) { 4725 xmlNodePtr cur; 4726 xsltStackElemPtr param; 4727 4728 cur = inst->children; 4729 while (cur != NULL) { 4730 #ifdef WITH_DEBUGGER 4731 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 4732 xslHandleDebugger(cur, node, comp->templ, ctxt); 4733 #endif 4734 if (ctxt->state == XSLT_STATE_STOPPED) break; 4735 /* 4736 * TODO: The "with-param"s could be part of the "call-template" 4737 * structure. Avoid to "search" for params dynamically 4738 * in the XML tree every time. 4739 */ 4740 if (IS_XSLT_ELEM(cur)) { 4741 if (IS_XSLT_NAME(cur, "with-param")) { 4742 param = xsltParseStylesheetCallerParam(ctxt, cur); 4743 if (param != NULL) { 4744 param->next = withParams; 4745 withParams = param; 4746 } 4747 } else { 4748 xsltGenericError(xsltGenericErrorContext, 4749 "xsl:call-template: misplaced xsl:%s\n", cur->name); 4750 } 4751 } else { 4752 xsltGenericError(xsltGenericErrorContext, 4753 "xsl:call-template: misplaced %s element\n", cur->name); 4754 } 4755 cur = cur->next; 4756 } 4757 } 4758 /* 4759 * Create a new frame using the params first 4760 */ 4761 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, 4762 withParams); 4763 if (withParams != NULL) 4764 xsltFreeStackElemList(withParams); 4765 4766 #ifdef WITH_XSLT_DEBUG_PROCESS 4767 if ((comp != NULL) && (comp->name != NULL)) 4768 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4769 "call-template returned: name %s\n", comp->name)); 4770 #endif 4771 } 4772 4773 /** 4774 * xsltApplyTemplates: 4775 * @ctxt: a XSLT transformation context 4776 * @node: the 'current node' in the source tree 4777 * @inst: the element node of an XSLT 'apply-templates' instruction 4778 * @castedComp: the compiled instruction 4779 * 4780 * Processes the XSLT 'apply-templates' instruction on the current node. 4781 */ 4782 void 4783 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, 4784 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 4785 { 4786 #ifdef XSLT_REFACTORED 4787 xsltStyleItemApplyTemplatesPtr comp = 4788 (xsltStyleItemApplyTemplatesPtr) castedComp; 4789 #else 4790 xsltStylePreCompPtr comp = castedComp; 4791 #endif 4792 int i; 4793 xmlNodePtr cur, delNode = NULL, oldContextNode; 4794 xmlNodeSetPtr list = NULL, oldList; 4795 xsltStackElemPtr withParams = NULL; 4796 int oldXPProximityPosition, oldXPContextSize; 4797 const xmlChar *oldMode, *oldModeURI; 4798 xmlDocPtr oldXPDoc; 4799 xsltDocumentPtr oldDocInfo; 4800 xmlXPathContextPtr xpctxt; 4801 4802 if (comp == NULL) { 4803 xsltTransformError(ctxt, NULL, inst, 4804 "xsl:apply-templates : compilation failed\n"); 4805 return; 4806 } 4807 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4808 return; 4809 4810 #ifdef WITH_XSLT_DEBUG_PROCESS 4811 if ((node != NULL) && (node->name != NULL)) 4812 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4813 "xsltApplyTemplates: node: '%s'\n", node->name)); 4814 #endif 4815 4816 xpctxt = ctxt->xpathCtxt; 4817 /* 4818 * Save context states. 4819 */ 4820 oldContextNode = ctxt->node; 4821 oldMode = ctxt->mode; 4822 oldModeURI = ctxt->modeURI; 4823 oldDocInfo = ctxt->document; 4824 oldList = ctxt->nodeList; 4825 4826 /* 4827 * The xpath context size and proximity position, as 4828 * well as the xpath and context documents, may be changed 4829 * so we save their initial state and will restore on exit 4830 */ 4831 oldXPContextSize = xpctxt->contextSize; 4832 oldXPProximityPosition = xpctxt->proximityPosition; 4833 oldXPDoc = xpctxt->doc; 4834 4835 /* 4836 * Set up contexts. 4837 */ 4838 ctxt->mode = comp->mode; 4839 ctxt->modeURI = comp->modeURI; 4840 4841 if (comp->select != NULL) { 4842 xmlXPathObjectPtr res = NULL; 4843 4844 if (comp->comp == NULL) { 4845 xsltTransformError(ctxt, NULL, inst, 4846 "xsl:apply-templates : compilation failed\n"); 4847 goto error; 4848 } 4849 #ifdef WITH_XSLT_DEBUG_PROCESS 4850 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4851 "xsltApplyTemplates: select %s\n", comp->select)); 4852 #endif 4853 4854 res = xsltPreCompEval(ctxt, node, comp); 4855 4856 if (res != NULL) { 4857 if (res->type == XPATH_NODESET) { 4858 list = res->nodesetval; /* consume the node set */ 4859 res->nodesetval = NULL; 4860 } else { 4861 xsltTransformError(ctxt, NULL, inst, 4862 "The 'select' expression did not evaluate to a " 4863 "node set.\n"); 4864 ctxt->state = XSLT_STATE_STOPPED; 4865 xmlXPathFreeObject(res); 4866 goto error; 4867 } 4868 xmlXPathFreeObject(res); 4869 /* 4870 * Note: An xsl:apply-templates with a 'select' attribute, 4871 * can change the current source doc. 4872 */ 4873 } else { 4874 xsltTransformError(ctxt, NULL, inst, 4875 "Failed to evaluate the 'select' expression.\n"); 4876 ctxt->state = XSLT_STATE_STOPPED; 4877 goto error; 4878 } 4879 if (list == NULL) { 4880 #ifdef WITH_XSLT_DEBUG_PROCESS 4881 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4882 "xsltApplyTemplates: select didn't evaluate to a node list\n")); 4883 #endif 4884 goto exit; 4885 } 4886 /* 4887 * 4888 * NOTE: Previously a document info (xsltDocument) was 4889 * created and attached to the Result Tree Fragment. 4890 * But such a document info is created on demand in 4891 * xsltKeyFunction() (functions.c), so we need to create 4892 * it here beforehand. 4893 * In order to take care of potential keys we need to 4894 * do some extra work for the case when a Result Tree Fragment 4895 * is converted into a nodeset (e.g. exslt:node-set()) : 4896 * We attach a "pseudo-doc" (xsltDocument) to _private. 4897 * This xsltDocument, together with the keyset, will be freed 4898 * when the Result Tree Fragment is freed. 4899 * 4900 */ 4901 #if 0 4902 if ((ctxt->nbKeys > 0) && 4903 (list->nodeNr != 0) && 4904 (list->nodeTab[0]->doc != NULL) && 4905 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) 4906 { 4907 /* 4908 * NOTE that it's also OK if @effectiveDocInfo will be 4909 * set to NULL. 4910 */ 4911 isRTF = 1; 4912 effectiveDocInfo = list->nodeTab[0]->doc->_private; 4913 } 4914 #endif 4915 } else { 4916 /* 4917 * Build an XPath node set with the children 4918 */ 4919 list = xmlXPathNodeSetCreate(NULL); 4920 if (list == NULL) 4921 goto error; 4922 if (node->type != XML_NAMESPACE_DECL) 4923 cur = node->children; 4924 else 4925 cur = NULL; 4926 while (cur != NULL) { 4927 switch (cur->type) { 4928 case XML_TEXT_NODE: 4929 if ((IS_BLANK_NODE(cur)) && 4930 (cur->parent != NULL) && 4931 (cur->parent->type == XML_ELEMENT_NODE) && 4932 (ctxt->style->stripSpaces != NULL)) { 4933 const xmlChar *val; 4934 4935 if (cur->parent->ns != NULL) { 4936 val = (const xmlChar *) 4937 xmlHashLookup2(ctxt->style->stripSpaces, 4938 cur->parent->name, 4939 cur->parent->ns->href); 4940 if (val == NULL) { 4941 val = (const xmlChar *) 4942 xmlHashLookup2(ctxt->style->stripSpaces, 4943 BAD_CAST "*", 4944 cur->parent->ns->href); 4945 } 4946 } else { 4947 val = (const xmlChar *) 4948 xmlHashLookup2(ctxt->style->stripSpaces, 4949 cur->parent->name, NULL); 4950 } 4951 if ((val != NULL) && 4952 (xmlStrEqual(val, (xmlChar *) "strip"))) { 4953 delNode = cur; 4954 break; 4955 } 4956 } 4957 /* no break on purpose */ 4958 case XML_ELEMENT_NODE: 4959 case XML_DOCUMENT_NODE: 4960 case XML_HTML_DOCUMENT_NODE: 4961 case XML_CDATA_SECTION_NODE: 4962 case XML_PI_NODE: 4963 case XML_COMMENT_NODE: 4964 xmlXPathNodeSetAddUnique(list, cur); 4965 break; 4966 case XML_DTD_NODE: 4967 /* Unlink the DTD, it's still reachable 4968 * using doc->intSubset */ 4969 if (cur->next != NULL) 4970 cur->next->prev = cur->prev; 4971 if (cur->prev != NULL) 4972 cur->prev->next = cur->next; 4973 break; 4974 case XML_NAMESPACE_DECL: 4975 break; 4976 default: 4977 #ifdef WITH_XSLT_DEBUG_PROCESS 4978 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4979 "xsltApplyTemplates: skipping cur type %d\n", 4980 cur->type)); 4981 #endif 4982 delNode = cur; 4983 } 4984 cur = cur->next; 4985 if (delNode != NULL) { 4986 #ifdef WITH_XSLT_DEBUG_PROCESS 4987 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4988 "xsltApplyTemplates: removing ignorable blank cur\n")); 4989 #endif 4990 xmlUnlinkNode(delNode); 4991 xmlFreeNode(delNode); 4992 delNode = NULL; 4993 } 4994 } 4995 } 4996 4997 #ifdef WITH_XSLT_DEBUG_PROCESS 4998 if (list != NULL) 4999 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5000 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); 5001 #endif 5002 5003 if ((list == NULL) || (list->nodeNr == 0)) 5004 goto exit; 5005 5006 /* 5007 * Set the context's node set and size; this is also needed for 5008 * for xsltDoSortFunction(). 5009 */ 5010 ctxt->nodeList = list; 5011 /* 5012 * Process xsl:with-param and xsl:sort instructions. 5013 * (The code became so verbose just to avoid the 5014 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) 5015 * BUG TODO: We are not using namespaced potentially defined on the 5016 * xsl:sort or xsl:with-param elements; XPath expression might fail. 5017 */ 5018 if (inst->children) { 5019 xsltStackElemPtr param; 5020 5021 cur = inst->children; 5022 while (cur) { 5023 5024 #ifdef WITH_DEBUGGER 5025 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5026 xslHandleDebugger(cur, node, NULL, ctxt); 5027 #endif 5028 if (ctxt->state == XSLT_STATE_STOPPED) 5029 break; 5030 if (cur->type == XML_TEXT_NODE) { 5031 cur = cur->next; 5032 continue; 5033 } 5034 if (! IS_XSLT_ELEM(cur)) 5035 break; 5036 if (IS_XSLT_NAME(cur, "with-param")) { 5037 param = xsltParseStylesheetCallerParam(ctxt, cur); 5038 if (param != NULL) { 5039 param->next = withParams; 5040 withParams = param; 5041 } 5042 } 5043 if (IS_XSLT_NAME(cur, "sort")) { 5044 xsltTemplatePtr oldCurTempRule = 5045 ctxt->currentTemplateRule; 5046 int nbsorts = 0; 5047 xmlNodePtr sorts[XSLT_MAX_SORT]; 5048 5049 sorts[nbsorts++] = cur; 5050 5051 while (cur) { 5052 5053 #ifdef WITH_DEBUGGER 5054 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5055 xslHandleDebugger(cur, node, NULL, ctxt); 5056 #endif 5057 if (ctxt->state == XSLT_STATE_STOPPED) 5058 break; 5059 5060 if (cur->type == XML_TEXT_NODE) { 5061 cur = cur->next; 5062 continue; 5063 } 5064 5065 if (! IS_XSLT_ELEM(cur)) 5066 break; 5067 if (IS_XSLT_NAME(cur, "with-param")) { 5068 param = xsltParseStylesheetCallerParam(ctxt, cur); 5069 if (param != NULL) { 5070 param->next = withParams; 5071 withParams = param; 5072 } 5073 } 5074 if (IS_XSLT_NAME(cur, "sort")) { 5075 if (nbsorts >= XSLT_MAX_SORT) { 5076 xsltTransformError(ctxt, NULL, cur, 5077 "The number (%d) of xsl:sort instructions exceeds the " 5078 "maximum allowed by this processor's settings.\n", 5079 nbsorts); 5080 ctxt->state = XSLT_STATE_STOPPED; 5081 break; 5082 } else { 5083 sorts[nbsorts++] = cur; 5084 } 5085 } 5086 cur = cur->next; 5087 } 5088 /* 5089 * The "current template rule" is cleared for xsl:sort. 5090 */ 5091 ctxt->currentTemplateRule = NULL; 5092 /* 5093 * Sort. 5094 */ 5095 xsltDoSortFunction(ctxt, sorts, nbsorts); 5096 ctxt->currentTemplateRule = oldCurTempRule; 5097 break; 5098 } 5099 cur = cur->next; 5100 } 5101 } 5102 xpctxt->contextSize = list->nodeNr; 5103 /* 5104 * Apply templates for all selected source nodes. 5105 */ 5106 for (i = 0; i < list->nodeNr; i++) { 5107 cur = list->nodeTab[i]; 5108 /* 5109 * The node becomes the "current node". 5110 */ 5111 ctxt->node = cur; 5112 /* 5113 * An xsl:apply-templates can change the current context doc. 5114 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5115 */ 5116 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5117 xpctxt->doc = cur->doc; 5118 5119 xpctxt->proximityPosition = i + 1; 5120 /* 5121 * Find and apply a template for this node. 5122 */ 5123 xsltProcessOneNode(ctxt, cur, withParams); 5124 } 5125 5126 exit: 5127 error: 5128 /* 5129 * Free the parameter list. 5130 */ 5131 if (withParams != NULL) 5132 xsltFreeStackElemList(withParams); 5133 if (list != NULL) 5134 xmlXPathFreeNodeSet(list); 5135 /* 5136 * Restore context states. 5137 */ 5138 xpctxt->doc = oldXPDoc; 5139 xpctxt->contextSize = oldXPContextSize; 5140 xpctxt->proximityPosition = oldXPProximityPosition; 5141 5142 ctxt->document = oldDocInfo; 5143 ctxt->nodeList = oldList; 5144 ctxt->node = oldContextNode; 5145 ctxt->mode = oldMode; 5146 ctxt->modeURI = oldModeURI; 5147 } 5148 5149 5150 /** 5151 * xsltChoose: 5152 * @ctxt: a XSLT process context 5153 * @contextNode: the current node in the source tree 5154 * @inst: the xsl:choose instruction 5155 * @comp: compiled information of the instruction 5156 * 5157 * Processes the xsl:choose instruction on the source node. 5158 */ 5159 void 5160 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5161 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) 5162 { 5163 xmlNodePtr cur; 5164 5165 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5166 return; 5167 5168 /* 5169 * TODO: Content model checks should be done only at compilation 5170 * time. 5171 */ 5172 cur = inst->children; 5173 if (cur == NULL) { 5174 xsltTransformError(ctxt, NULL, inst, 5175 "xsl:choose: The instruction has no content.\n"); 5176 return; 5177 } 5178 5179 #ifdef XSLT_REFACTORED 5180 /* 5181 * We don't check the content model during transformation. 5182 */ 5183 #else 5184 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { 5185 xsltTransformError(ctxt, NULL, inst, 5186 "xsl:choose: xsl:when expected first\n"); 5187 return; 5188 } 5189 #endif 5190 5191 { 5192 int testRes = 0, res = 0; 5193 5194 #ifdef XSLT_REFACTORED 5195 xsltStyleItemWhenPtr wcomp = NULL; 5196 #else 5197 xsltStylePreCompPtr wcomp = NULL; 5198 #endif 5199 5200 /* 5201 * Process xsl:when --------------------------------------------------- 5202 */ 5203 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { 5204 wcomp = cur->psvi; 5205 5206 if ((wcomp == NULL) || (wcomp->test == NULL) || 5207 (wcomp->comp == NULL)) 5208 { 5209 xsltTransformError(ctxt, NULL, cur, 5210 "Internal error in xsltChoose(): " 5211 "The XSLT 'when' instruction was not compiled.\n"); 5212 goto error; 5213 } 5214 5215 5216 #ifdef WITH_DEBUGGER 5217 if (xslDebugStatus != XSLT_DEBUG_NONE) { 5218 /* 5219 * TODO: Isn't comp->templ always NULL for xsl:choose? 5220 */ 5221 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5222 } 5223 #endif 5224 #ifdef WITH_XSLT_DEBUG_PROCESS 5225 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5226 "xsltChoose: test %s\n", wcomp->test)); 5227 #endif 5228 5229 #ifdef XSLT_FAST_IF 5230 res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp); 5231 5232 if (res == -1) { 5233 ctxt->state = XSLT_STATE_STOPPED; 5234 goto error; 5235 } 5236 testRes = (res == 1) ? 1 : 0; 5237 5238 #else /* XSLT_FAST_IF */ 5239 5240 res = xsltPreCompEval(ctxt, cotextNode, wcomp); 5241 5242 if (res != NULL) { 5243 if (res->type != XPATH_BOOLEAN) 5244 res = xmlXPathConvertBoolean(res); 5245 if (res->type == XPATH_BOOLEAN) 5246 testRes = res->boolval; 5247 else { 5248 #ifdef WITH_XSLT_DEBUG_PROCESS 5249 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5250 "xsltChoose: test didn't evaluate to a boolean\n")); 5251 #endif 5252 goto error; 5253 } 5254 xmlXPathFreeObject(res); 5255 res = NULL; 5256 } else { 5257 ctxt->state = XSLT_STATE_STOPPED; 5258 goto error; 5259 } 5260 5261 #endif /* else of XSLT_FAST_IF */ 5262 5263 #ifdef WITH_XSLT_DEBUG_PROCESS 5264 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5265 "xsltChoose: test evaluate to %d\n", testRes)); 5266 #endif 5267 if (testRes) 5268 goto test_is_true; 5269 5270 cur = cur->next; 5271 } 5272 5273 /* 5274 * Process xsl:otherwise ---------------------------------------------- 5275 */ 5276 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { 5277 5278 #ifdef WITH_DEBUGGER 5279 if (xslDebugStatus != XSLT_DEBUG_NONE) 5280 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5281 #endif 5282 5283 #ifdef WITH_XSLT_DEBUG_PROCESS 5284 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5285 "evaluating xsl:otherwise\n")); 5286 #endif 5287 goto test_is_true; 5288 } 5289 goto exit; 5290 5291 test_is_true: 5292 5293 goto process_sequence; 5294 } 5295 5296 process_sequence: 5297 5298 /* 5299 * Instantiate the sequence constructor. 5300 */ 5301 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, 5302 NULL); 5303 5304 exit: 5305 error: 5306 return; 5307 } 5308 5309 /** 5310 * xsltIf: 5311 * @ctxt: a XSLT process context 5312 * @contextNode: the current node in the source tree 5313 * @inst: the xsl:if instruction 5314 * @castedComp: compiled information of the instruction 5315 * 5316 * Processes the xsl:if instruction on the source node. 5317 */ 5318 void 5319 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5320 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 5321 { 5322 int res = 0; 5323 5324 #ifdef XSLT_REFACTORED 5325 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; 5326 #else 5327 xsltStylePreCompPtr comp = castedComp; 5328 #endif 5329 5330 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5331 return; 5332 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { 5333 xsltTransformError(ctxt, NULL, inst, 5334 "Internal error in xsltIf(): " 5335 "The XSLT 'if' instruction was not compiled.\n"); 5336 return; 5337 } 5338 5339 #ifdef WITH_XSLT_DEBUG_PROCESS 5340 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5341 "xsltIf: test %s\n", comp->test)); 5342 #endif 5343 5344 #ifdef XSLT_FAST_IF 5345 { 5346 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; 5347 5348 res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); 5349 5350 /* 5351 * Cleanup fragments created during evaluation of the 5352 * "select" expression. 5353 */ 5354 if (oldLocalFragmentTop != ctxt->localRVT) 5355 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 5356 } 5357 5358 #ifdef WITH_XSLT_DEBUG_PROCESS 5359 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5360 "xsltIf: test evaluate to %d\n", res)); 5361 #endif 5362 5363 if (res == -1) { 5364 ctxt->state = XSLT_STATE_STOPPED; 5365 goto error; 5366 } 5367 if (res == 1) { 5368 /* 5369 * Instantiate the sequence constructor of xsl:if. 5370 */ 5371 xsltApplySequenceConstructor(ctxt, 5372 contextNode, inst->children, NULL); 5373 } 5374 5375 #else /* XSLT_FAST_IF */ 5376 { 5377 /* 5378 * OLD CODE: 5379 */ 5380 xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp); 5381 if (xpobj != NULL) { 5382 if (xpobj->type != XPATH_BOOLEAN) 5383 xpobj = xmlXPathConvertBoolean(xpobj); 5384 if (xpobj->type == XPATH_BOOLEAN) { 5385 res = xpobj->boolval; 5386 5387 #ifdef WITH_XSLT_DEBUG_PROCESS 5388 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5389 "xsltIf: test evaluate to %d\n", res)); 5390 #endif 5391 if (res) { 5392 xsltApplySequenceConstructor(ctxt, 5393 contextNode, inst->children, NULL); 5394 } 5395 } else { 5396 5397 #ifdef WITH_XSLT_DEBUG_PROCESS 5398 XSLT_TRACE(ctxt, XSLT_TRACE_IF, 5399 xsltGenericDebug(xsltGenericDebugContext, 5400 "xsltIf: test didn't evaluate to a boolean\n")); 5401 #endif 5402 ctxt->state = XSLT_STATE_STOPPED; 5403 } 5404 xmlXPathFreeObject(xpobj); 5405 } else { 5406 ctxt->state = XSLT_STATE_STOPPED; 5407 } 5408 } 5409 #endif /* else of XSLT_FAST_IF */ 5410 5411 error: 5412 return; 5413 } 5414 5415 /** 5416 * xsltForEach: 5417 * @ctxt: an XSLT transformation context 5418 * @contextNode: the "current node" in the source tree 5419 * @inst: the element node of the xsl:for-each instruction 5420 * @castedComp: the compiled information of the instruction 5421 * 5422 * Process the xslt for-each node on the source node 5423 */ 5424 void 5425 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5426 xmlNodePtr inst, xsltStylePreCompPtr castedComp) 5427 { 5428 #ifdef XSLT_REFACTORED 5429 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; 5430 #else 5431 xsltStylePreCompPtr comp = castedComp; 5432 #endif 5433 int i; 5434 xmlXPathObjectPtr res = NULL; 5435 xmlNodePtr cur, curInst; 5436 xmlNodeSetPtr list = NULL; 5437 xmlNodeSetPtr oldList; 5438 int oldXPProximityPosition, oldXPContextSize; 5439 xmlNodePtr oldContextNode; 5440 xsltTemplatePtr oldCurTemplRule; 5441 xmlDocPtr oldXPDoc; 5442 xsltDocumentPtr oldDocInfo; 5443 xmlXPathContextPtr xpctxt; 5444 5445 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { 5446 xsltGenericError(xsltGenericErrorContext, 5447 "xsltForEach(): Bad arguments.\n"); 5448 return; 5449 } 5450 5451 if (comp == NULL) { 5452 xsltTransformError(ctxt, NULL, inst, 5453 "Internal error in xsltForEach(): " 5454 "The XSLT 'for-each' instruction was not compiled.\n"); 5455 return; 5456 } 5457 if ((comp->select == NULL) || (comp->comp == NULL)) { 5458 xsltTransformError(ctxt, NULL, inst, 5459 "Internal error in xsltForEach(): " 5460 "The selecting expression of the XSLT 'for-each' " 5461 "instruction was not compiled correctly.\n"); 5462 return; 5463 } 5464 xpctxt = ctxt->xpathCtxt; 5465 5466 #ifdef WITH_XSLT_DEBUG_PROCESS 5467 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5468 "xsltForEach: select %s\n", comp->select)); 5469 #endif 5470 5471 /* 5472 * Save context states. 5473 */ 5474 oldDocInfo = ctxt->document; 5475 oldList = ctxt->nodeList; 5476 oldContextNode = ctxt->node; 5477 /* 5478 * The "current template rule" is cleared for the instantiation of 5479 * xsl:for-each. 5480 */ 5481 oldCurTemplRule = ctxt->currentTemplateRule; 5482 ctxt->currentTemplateRule = NULL; 5483 5484 oldXPDoc = xpctxt->doc; 5485 oldXPProximityPosition = xpctxt->proximityPosition; 5486 oldXPContextSize = xpctxt->contextSize; 5487 5488 /* 5489 * Evaluate the 'select' expression. 5490 */ 5491 res = xsltPreCompEval(ctxt, contextNode, comp); 5492 5493 if (res != NULL) { 5494 if (res->type == XPATH_NODESET) 5495 list = res->nodesetval; 5496 else { 5497 xsltTransformError(ctxt, NULL, inst, 5498 "The 'select' expression does not evaluate to a node set.\n"); 5499 5500 #ifdef WITH_XSLT_DEBUG_PROCESS 5501 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5502 "xsltForEach: select didn't evaluate to a node list\n")); 5503 #endif 5504 goto error; 5505 } 5506 } else { 5507 xsltTransformError(ctxt, NULL, inst, 5508 "Failed to evaluate the 'select' expression.\n"); 5509 ctxt->state = XSLT_STATE_STOPPED; 5510 goto error; 5511 } 5512 5513 if ((list == NULL) || (list->nodeNr <= 0)) 5514 goto exit; 5515 5516 #ifdef WITH_XSLT_DEBUG_PROCESS 5517 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5518 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); 5519 #endif 5520 5521 /* 5522 * Set the list; this has to be done already here for xsltDoSortFunction(). 5523 */ 5524 ctxt->nodeList = list; 5525 /* 5526 * Handle xsl:sort instructions and skip them for further processing. 5527 * BUG TODO: We are not using namespaced potentially defined on the 5528 * xsl:sort element; XPath expression might fail. 5529 */ 5530 curInst = inst->children; 5531 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5532 int nbsorts = 0; 5533 xmlNodePtr sorts[XSLT_MAX_SORT]; 5534 5535 sorts[nbsorts++] = curInst; 5536 5537 #ifdef WITH_DEBUGGER 5538 if (xslDebugStatus != XSLT_DEBUG_NONE) 5539 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5540 #endif 5541 5542 curInst = curInst->next; 5543 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5544 if (nbsorts >= XSLT_MAX_SORT) { 5545 xsltTransformError(ctxt, NULL, curInst, 5546 "The number of xsl:sort instructions exceeds the " 5547 "maximum (%d) allowed by this processor.\n", 5548 XSLT_MAX_SORT); 5549 goto error; 5550 } else { 5551 sorts[nbsorts++] = curInst; 5552 } 5553 5554 #ifdef WITH_DEBUGGER 5555 if (xslDebugStatus != XSLT_DEBUG_NONE) 5556 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5557 #endif 5558 curInst = curInst->next; 5559 } 5560 xsltDoSortFunction(ctxt, sorts, nbsorts); 5561 } 5562 xpctxt->contextSize = list->nodeNr; 5563 /* 5564 * Instantiate the sequence constructor for each selected node. 5565 */ 5566 for (i = 0; i < list->nodeNr; i++) { 5567 cur = list->nodeTab[i]; 5568 /* 5569 * The selected node becomes the "current node". 5570 */ 5571 ctxt->node = cur; 5572 /* 5573 * An xsl:for-each can change the current context doc. 5574 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5575 */ 5576 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5577 xpctxt->doc = cur->doc; 5578 5579 xpctxt->proximityPosition = i + 1; 5580 5581 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); 5582 } 5583 5584 exit: 5585 error: 5586 if (res != NULL) 5587 xmlXPathFreeObject(res); 5588 /* 5589 * Restore old states. 5590 */ 5591 ctxt->document = oldDocInfo; 5592 ctxt->nodeList = oldList; 5593 ctxt->node = oldContextNode; 5594 ctxt->currentTemplateRule = oldCurTemplRule; 5595 5596 xpctxt->doc = oldXPDoc; 5597 xpctxt->contextSize = oldXPContextSize; 5598 xpctxt->proximityPosition = oldXPProximityPosition; 5599 } 5600 5601 /************************************************************************ 5602 * * 5603 * Generic interface * 5604 * * 5605 ************************************************************************/ 5606 5607 #ifdef XSLT_GENERATE_HTML_DOCTYPE 5608 typedef struct xsltHTMLVersion { 5609 const char *version; 5610 const char *public; 5611 const char *system; 5612 } xsltHTMLVersion; 5613 5614 static xsltHTMLVersion xsltHTMLVersions[] = { 5615 { "5", NULL, "about:legacy-compat" }, 5616 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5617 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, 5618 { "4.01strict", "-//W3C//DTD HTML 4.01//EN", 5619 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, 5620 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5621 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5622 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", 5623 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5624 { "4.0strict", "-//W3C//DTD HTML 4.01//EN", 5625 "http://www.w3.org/TR/html4/strict.dtd"}, 5626 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5627 "http://www.w3.org/TR/html4/loose.dtd"}, 5628 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5629 "http://www.w3.org/TR/html4/frameset.dtd"}, 5630 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", 5631 "http://www.w3.org/TR/html4/loose.dtd"}, 5632 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } 5633 }; 5634 5635 /** 5636 * xsltGetHTMLIDs: 5637 * @version: the version string 5638 * @publicID: used to return the public ID 5639 * @systemID: used to return the system ID 5640 * 5641 * Returns -1 if not found, 0 otherwise and the system and public 5642 * Identifier for this given verion of HTML 5643 */ 5644 static int 5645 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, 5646 const xmlChar **systemID) { 5647 unsigned int i; 5648 if (version == NULL) 5649 return(-1); 5650 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); 5651 i++) { 5652 if (!xmlStrcasecmp(version, 5653 (const xmlChar *) xsltHTMLVersions[i].version)) { 5654 if (publicID != NULL) 5655 *publicID = (const xmlChar *) xsltHTMLVersions[i].public; 5656 if (systemID != NULL) 5657 *systemID = (const xmlChar *) xsltHTMLVersions[i].system; 5658 return(0); 5659 } 5660 } 5661 return(-1); 5662 } 5663 #endif 5664 5665 /** 5666 * xsltApplyStripSpaces: 5667 * @ctxt: a XSLT process context 5668 * @node: the root of the XML tree 5669 * 5670 * Strip the unwanted ignorable spaces from the input tree 5671 */ 5672 void 5673 xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { 5674 xmlNodePtr current; 5675 #ifdef WITH_XSLT_DEBUG_PROCESS 5676 int nb = 0; 5677 #endif 5678 5679 5680 current = node; 5681 while (current != NULL) { 5682 /* 5683 * Cleanup children empty nodes if asked for 5684 */ 5685 if ((IS_XSLT_REAL_NODE(current)) && 5686 (current->children != NULL) && 5687 (xsltFindElemSpaceHandling(ctxt, current))) { 5688 xmlNodePtr delete = NULL, cur = current->children; 5689 5690 while (cur != NULL) { 5691 if (IS_BLANK_NODE(cur)) 5692 delete = cur; 5693 5694 cur = cur->next; 5695 if (delete != NULL) { 5696 xmlUnlinkNode(delete); 5697 xmlFreeNode(delete); 5698 delete = NULL; 5699 #ifdef WITH_XSLT_DEBUG_PROCESS 5700 nb++; 5701 #endif 5702 } 5703 } 5704 } 5705 5706 /* 5707 * Skip to next node in document order. 5708 */ 5709 if (node->type == XML_ENTITY_REF_NODE) { 5710 /* process deep in entities */ 5711 xsltApplyStripSpaces(ctxt, node->children); 5712 } 5713 if ((current->children != NULL) && 5714 (current->type != XML_ENTITY_REF_NODE)) { 5715 current = current->children; 5716 } else if (current->next != NULL) { 5717 current = current->next; 5718 } else { 5719 do { 5720 current = current->parent; 5721 if (current == NULL) 5722 break; 5723 if (current == node) 5724 goto done; 5725 if (current->next != NULL) { 5726 current = current->next; 5727 break; 5728 } 5729 } while (current != NULL); 5730 } 5731 } 5732 5733 done: 5734 #ifdef WITH_XSLT_DEBUG_PROCESS 5735 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext, 5736 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); 5737 #endif 5738 return; 5739 } 5740 5741 static int 5742 xsltCountKeys(xsltTransformContextPtr ctxt) 5743 { 5744 xsltStylesheetPtr style; 5745 xsltKeyDefPtr keyd; 5746 5747 if (ctxt == NULL) 5748 return(-1); 5749 5750 /* 5751 * Do we have those nastly templates with a key() in the match pattern? 5752 */ 5753 ctxt->hasTemplKeyPatterns = 0; 5754 style = ctxt->style; 5755 while (style != NULL) { 5756 if (style->keyMatch != NULL) { 5757 ctxt->hasTemplKeyPatterns = 1; 5758 break; 5759 } 5760 style = xsltNextImport(style); 5761 } 5762 /* 5763 * Count number of key declarations. 5764 */ 5765 ctxt->nbKeys = 0; 5766 style = ctxt->style; 5767 while (style != NULL) { 5768 keyd = style->keys; 5769 while (keyd) { 5770 ctxt->nbKeys++; 5771 keyd = keyd->next; 5772 } 5773 style = xsltNextImport(style); 5774 } 5775 return(ctxt->nbKeys); 5776 } 5777 5778 /** 5779 * xsltApplyStylesheetInternal: 5780 * @style: a parsed XSLT stylesheet 5781 * @doc: a parsed XML document 5782 * @params: a NULL terminated array of parameters names/values tuples 5783 * @output: the targetted output 5784 * @profile: profile FILE * output or NULL 5785 * @user: user provided parameter 5786 * 5787 * Apply the stylesheet to the document 5788 * NOTE: This may lead to a non-wellformed output XML wise ! 5789 * 5790 * Returns the result document or NULL in case of error 5791 */ 5792 static xmlDocPtr 5793 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, 5794 const char **params, const char *output, 5795 FILE * profile, xsltTransformContextPtr userCtxt) 5796 { 5797 xmlDocPtr res = NULL; 5798 xsltTransformContextPtr ctxt = NULL; 5799 xmlNodePtr root, node; 5800 const xmlChar *method; 5801 const xmlChar *doctypePublic; 5802 const xmlChar *doctypeSystem; 5803 const xmlChar *version; 5804 const xmlChar *encoding; 5805 xsltStackElemPtr variables; 5806 xsltStackElemPtr vptr; 5807 5808 xsltInitGlobals(); 5809 5810 if ((style == NULL) || (doc == NULL)) 5811 return (NULL); 5812 5813 if (style->internalized == 0) { 5814 #ifdef WITH_XSLT_DEBUG 5815 xsltGenericDebug(xsltGenericDebugContext, 5816 "Stylesheet was not fully internalized !\n"); 5817 #endif 5818 } 5819 if (doc->intSubset != NULL) { 5820 /* 5821 * Avoid hitting the DTD when scanning nodes 5822 * but keep it linked as doc->intSubset 5823 */ 5824 xmlNodePtr cur = (xmlNodePtr) doc->intSubset; 5825 if (cur->next != NULL) 5826 cur->next->prev = cur->prev; 5827 if (cur->prev != NULL) 5828 cur->prev->next = cur->next; 5829 if (doc->children == cur) 5830 doc->children = cur->next; 5831 if (doc->last == cur) 5832 doc->last = cur->prev; 5833 cur->prev = cur->next = NULL; 5834 } 5835 5836 /* 5837 * Check for XPath document order availability 5838 */ 5839 root = xmlDocGetRootElement(doc); 5840 if (root != NULL) { 5841 if (((ptrdiff_t) root->content >= 0) && 5842 (xslDebugStatus == XSLT_DEBUG_NONE)) 5843 xmlXPathOrderDocElems(doc); 5844 } 5845 5846 if (userCtxt != NULL) 5847 ctxt = userCtxt; 5848 else 5849 ctxt = xsltNewTransformContext(style, doc); 5850 5851 if (ctxt == NULL) 5852 return (NULL); 5853 5854 ctxt->initialContextDoc = doc; 5855 ctxt->initialContextNode = (xmlNodePtr) doc; 5856 5857 if (profile != NULL) 5858 ctxt->profile = 1; 5859 5860 if (output != NULL) 5861 ctxt->outputFile = output; 5862 else 5863 ctxt->outputFile = NULL; 5864 5865 /* 5866 * internalize the modes if needed 5867 */ 5868 if (ctxt->dict != NULL) { 5869 if (ctxt->mode != NULL) 5870 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); 5871 if (ctxt->modeURI != NULL) 5872 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); 5873 } 5874 5875 XSLT_GET_IMPORT_PTR(method, style, method) 5876 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 5877 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 5878 XSLT_GET_IMPORT_PTR(version, style, version) 5879 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 5880 5881 if ((method != NULL) && 5882 (!xmlStrEqual(method, (const xmlChar *) "xml"))) 5883 { 5884 if (xmlStrEqual(method, (const xmlChar *) "html")) { 5885 ctxt->type = XSLT_OUTPUT_HTML; 5886 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 5887 res = htmlNewDoc(doctypeSystem, doctypePublic); 5888 } else { 5889 if (version == NULL) { 5890 xmlDtdPtr dtd; 5891 5892 res = htmlNewDoc(NULL, NULL); 5893 /* 5894 * Make sure no DTD node is generated in this case 5895 */ 5896 if (res != NULL) { 5897 dtd = xmlGetIntSubset(res); 5898 if (dtd != NULL) { 5899 xmlUnlinkNode((xmlNodePtr) dtd); 5900 xmlFreeDtd(dtd); 5901 } 5902 res->intSubset = NULL; 5903 res->extSubset = NULL; 5904 } 5905 } else { 5906 5907 #ifdef XSLT_GENERATE_HTML_DOCTYPE 5908 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 5909 #endif 5910 res = htmlNewDoc(doctypeSystem, doctypePublic); 5911 } 5912 } 5913 if (res == NULL) 5914 goto error; 5915 res->dict = ctxt->dict; 5916 xmlDictReference(res->dict); 5917 5918 #ifdef WITH_XSLT_DEBUG 5919 xsltGenericDebug(xsltGenericDebugContext, 5920 "reusing transformation dict for output\n"); 5921 #endif 5922 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 5923 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 5924 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n"); 5925 ctxt->type = XSLT_OUTPUT_HTML; 5926 res = htmlNewDoc(doctypeSystem, doctypePublic); 5927 if (res == NULL) 5928 goto error; 5929 res->dict = ctxt->dict; 5930 xmlDictReference(res->dict); 5931 5932 #ifdef WITH_XSLT_DEBUG 5933 xsltGenericDebug(xsltGenericDebugContext, 5934 "reusing transformation dict for output\n"); 5935 #endif 5936 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 5937 ctxt->type = XSLT_OUTPUT_TEXT; 5938 res = xmlNewDoc(style->version); 5939 if (res == NULL) 5940 goto error; 5941 res->dict = ctxt->dict; 5942 xmlDictReference(res->dict); 5943 5944 #ifdef WITH_XSLT_DEBUG 5945 xsltGenericDebug(xsltGenericDebugContext, 5946 "reusing transformation dict for output\n"); 5947 #endif 5948 } else { 5949 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 5950 "xsltApplyStylesheetInternal: unsupported method (%s)\n", 5951 method); 5952 goto error; 5953 } 5954 } else { 5955 ctxt->type = XSLT_OUTPUT_XML; 5956 res = xmlNewDoc(style->version); 5957 if (res == NULL) 5958 goto error; 5959 res->dict = ctxt->dict; 5960 xmlDictReference(ctxt->dict); 5961 #ifdef WITH_XSLT_DEBUG 5962 xsltGenericDebug(xsltGenericDebugContext, 5963 "reusing transformation dict for output\n"); 5964 #endif 5965 } 5966 res->charset = XML_CHAR_ENCODING_UTF8; 5967 if (encoding != NULL) 5968 res->encoding = xmlStrdup(encoding); 5969 variables = style->variables; 5970 5971 /* 5972 * Start the evaluation, evaluate the params, the stylesheets globals 5973 * and start by processing the top node. 5974 */ 5975 if (xsltNeedElemSpaceHandling(ctxt)) 5976 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); 5977 /* 5978 * Evaluate global params and user-provided params. 5979 */ 5980 ctxt->node = (xmlNodePtr) doc; 5981 if (ctxt->globalVars == NULL) 5982 ctxt->globalVars = xmlHashCreate(20); 5983 if (params != NULL) { 5984 xsltEvalUserParams(ctxt, params); 5985 } 5986 5987 /* need to be called before evaluating global variables */ 5988 xsltCountKeys(ctxt); 5989 5990 xsltEvalGlobalVariables(ctxt); 5991 5992 /* Clean up any unused RVTs. */ 5993 xsltReleaseLocalRVTs(ctxt, NULL); 5994 5995 ctxt->node = (xmlNodePtr) doc; 5996 ctxt->output = res; 5997 ctxt->insert = (xmlNodePtr) res; 5998 ctxt->varsBase = ctxt->varsNr - 1; 5999 6000 ctxt->xpathCtxt->contextSize = 1; 6001 ctxt->xpathCtxt->proximityPosition = 1; 6002 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ 6003 /* 6004 * Start processing the source tree ----------------------------------- 6005 */ 6006 xsltProcessOneNode(ctxt, ctxt->node, NULL); 6007 /* 6008 * Remove all remaining vars from the stack. 6009 */ 6010 xsltLocalVariablePop(ctxt, 0, -2); 6011 xsltShutdownCtxtExts(ctxt); 6012 6013 xsltCleanupTemplates(style); /* TODO: <- style should be read only */ 6014 6015 /* 6016 * Now cleanup our variables so stylesheet can be re-used 6017 * 6018 * TODO: this is not needed anymore global variables are copied 6019 * and not evaluated directly anymore, keep this as a check 6020 */ 6021 if (style->variables != variables) { 6022 vptr = style->variables; 6023 while (vptr->next != variables) 6024 vptr = vptr->next; 6025 vptr->next = NULL; 6026 xsltFreeStackElemList(style->variables); 6027 style->variables = variables; 6028 } 6029 vptr = style->variables; 6030 while (vptr != NULL) { 6031 if (vptr->computed) { 6032 if (vptr->value != NULL) { 6033 xmlXPathFreeObject(vptr->value); 6034 vptr->value = NULL; 6035 vptr->computed = 0; 6036 } 6037 } 6038 vptr = vptr->next; 6039 } 6040 #if 0 6041 /* 6042 * code disabled by wmb; awaiting kb's review 6043 * problem is that global variable(s) may contain xpath objects 6044 * from doc associated with RVT, so can't be freed at this point. 6045 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so 6046 * I assume this shouldn't be required at this point. 6047 */ 6048 /* 6049 * Free all remaining tree fragments. 6050 */ 6051 xsltFreeRVTs(ctxt); 6052 #endif 6053 /* 6054 * Do some post processing work depending on the generated output 6055 */ 6056 root = xmlDocGetRootElement(res); 6057 if (root != NULL) { 6058 const xmlChar *doctype = NULL; 6059 6060 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 6061 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 6062 if (doctype == NULL) 6063 doctype = root->name; 6064 6065 /* 6066 * Apply the default selection of the method 6067 */ 6068 if ((method == NULL) && 6069 (root->ns == NULL) && 6070 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 6071 xmlNodePtr tmp; 6072 6073 tmp = res->children; 6074 while ((tmp != NULL) && (tmp != root)) { 6075 if (tmp->type == XML_ELEMENT_NODE) 6076 break; 6077 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 6078 break; 6079 tmp = tmp->next; 6080 } 6081 if (tmp == root) { 6082 ctxt->type = XSLT_OUTPUT_HTML; 6083 /* 6084 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the 6085 * transformation on the doc, but functions like 6086 */ 6087 res->type = XML_HTML_DOCUMENT_NODE; 6088 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6089 res->intSubset = xmlCreateIntSubset(res, doctype, 6090 doctypePublic, 6091 doctypeSystem); 6092 #ifdef XSLT_GENERATE_HTML_DOCTYPE 6093 } else if (version != NULL) { 6094 xsltGetHTMLIDs(version, &doctypePublic, 6095 &doctypeSystem); 6096 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 6097 res->intSubset = 6098 xmlCreateIntSubset(res, doctype, 6099 doctypePublic, 6100 doctypeSystem); 6101 #endif 6102 } 6103 } 6104 6105 } 6106 if (ctxt->type == XSLT_OUTPUT_XML) { 6107 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 6108 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 6109 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6110 xmlNodePtr last; 6111 /* Need a small "hack" here to assure DTD comes before 6112 possible comment nodes */ 6113 node = res->children; 6114 last = res->last; 6115 res->children = NULL; 6116 res->last = NULL; 6117 res->intSubset = xmlCreateIntSubset(res, doctype, 6118 doctypePublic, 6119 doctypeSystem); 6120 if (res->children != NULL) { 6121 res->children->next = node; 6122 node->prev = res->children; 6123 res->last = last; 6124 } else { 6125 res->children = node; 6126 res->last = last; 6127 } 6128 } 6129 } 6130 } 6131 xmlXPathFreeNodeSet(ctxt->nodeList); 6132 if (profile != NULL) { 6133 xsltSaveProfiling(ctxt, profile); 6134 } 6135 6136 /* 6137 * Be pedantic. 6138 */ 6139 if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) { 6140 xmlFreeDoc(res); 6141 res = NULL; 6142 } 6143 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { 6144 int ret; 6145 6146 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); 6147 if (ret == 0) { 6148 xsltTransformError(ctxt, NULL, NULL, 6149 "xsltApplyStylesheet: forbidden to save to %s\n", 6150 output); 6151 } else if (ret < 0) { 6152 xsltTransformError(ctxt, NULL, NULL, 6153 "xsltApplyStylesheet: saving to %s may not be possible\n", 6154 output); 6155 } 6156 } 6157 6158 #ifdef XSLT_DEBUG_PROFILE_CACHE 6159 printf("# Cache:\n"); 6160 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6161 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6162 #endif 6163 6164 if ((ctxt != NULL) && (userCtxt == NULL)) 6165 xsltFreeTransformContext(ctxt); 6166 6167 return (res); 6168 6169 error: 6170 if (res != NULL) 6171 xmlFreeDoc(res); 6172 6173 #ifdef XSLT_DEBUG_PROFILE_CACHE 6174 printf("# Cache:\n"); 6175 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6176 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6177 #endif 6178 6179 if ((ctxt != NULL) && (userCtxt == NULL)) 6180 xsltFreeTransformContext(ctxt); 6181 return (NULL); 6182 } 6183 6184 /** 6185 * xsltApplyStylesheet: 6186 * @style: a parsed XSLT stylesheet 6187 * @doc: a parsed XML document 6188 * @params: a NULL terminated arry of parameters names/values tuples 6189 * 6190 * Apply the stylesheet to the document 6191 * NOTE: This may lead to a non-wellformed output XML wise ! 6192 * 6193 * Returns the result document or NULL in case of error 6194 */ 6195 xmlDocPtr 6196 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6197 const char **params) 6198 { 6199 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); 6200 } 6201 6202 /** 6203 * xsltProfileStylesheet: 6204 * @style: a parsed XSLT stylesheet 6205 * @doc: a parsed XML document 6206 * @params: a NULL terminated arry of parameters names/values tuples 6207 * @output: a FILE * for the profiling output 6208 * 6209 * Apply the stylesheet to the document and dump the profiling to 6210 * the given output. 6211 * 6212 * Returns the result document or NULL in case of error 6213 */ 6214 xmlDocPtr 6215 xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6216 const char **params, FILE * output) 6217 { 6218 xmlDocPtr res; 6219 6220 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); 6221 return (res); 6222 } 6223 6224 /** 6225 * xsltApplyStylesheetUser: 6226 * @style: a parsed XSLT stylesheet 6227 * @doc: a parsed XML document 6228 * @params: a NULL terminated array of parameters names/values tuples 6229 * @output: the targetted output 6230 * @profile: profile FILE * output or NULL 6231 * @userCtxt: user provided transform context 6232 * 6233 * Apply the stylesheet to the document and allow the user to provide 6234 * its own transformation context. 6235 * 6236 * Returns the result document or NULL in case of error 6237 */ 6238 xmlDocPtr 6239 xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6240 const char **params, const char *output, 6241 FILE * profile, xsltTransformContextPtr userCtxt) 6242 { 6243 xmlDocPtr res; 6244 6245 res = xsltApplyStylesheetInternal(style, doc, params, output, 6246 profile, userCtxt); 6247 return (res); 6248 } 6249 6250 /** 6251 * xsltRunStylesheetUser: 6252 * @style: a parsed XSLT stylesheet 6253 * @doc: a parsed XML document 6254 * @params: a NULL terminated array of parameters names/values tuples 6255 * @output: the URL/filename ot the generated resource if available 6256 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6257 * @IObuf: an output buffer for progressive output (not implemented yet) 6258 * @profile: profile FILE * output or NULL 6259 * @userCtxt: user provided transform context 6260 * 6261 * Apply the stylesheet to the document and generate the output according 6262 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6263 * 6264 * NOTE: This may lead to a non-wellformed output XML wise ! 6265 * NOTE: This may also result in multiple files being generated 6266 * NOTE: using IObuf, the result encoding used will be the one used for 6267 * creating the output buffer, use the following macro to read it 6268 * from the stylesheet 6269 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6270 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6271 * since the interface uses only UTF8 6272 * 6273 * Returns the number of by written to the main resource or -1 in case of 6274 * error. 6275 */ 6276 int 6277 xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6278 const char **params, const char *output, 6279 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, 6280 FILE * profile, xsltTransformContextPtr userCtxt) 6281 { 6282 xmlDocPtr tmp; 6283 int ret; 6284 6285 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) 6286 return (-1); 6287 if ((SAX != NULL) && (IObuf != NULL)) 6288 return (-1); 6289 6290 /* unsupported yet */ 6291 if (SAX != NULL) { 6292 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ 6293 return (-1); 6294 } 6295 6296 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, 6297 userCtxt); 6298 if (tmp == NULL) { 6299 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, 6300 "xsltRunStylesheet : run failed\n"); 6301 return (-1); 6302 } 6303 if (IObuf != NULL) { 6304 /* TODO: incomplete, IObuf output not progressive */ 6305 ret = xsltSaveResultTo(IObuf, tmp, style); 6306 } else { 6307 ret = xsltSaveResultToFilename(output, tmp, style, 0); 6308 } 6309 xmlFreeDoc(tmp); 6310 return (ret); 6311 } 6312 6313 /** 6314 * xsltRunStylesheet: 6315 * @style: a parsed XSLT stylesheet 6316 * @doc: a parsed XML document 6317 * @params: a NULL terminated array of parameters names/values tuples 6318 * @output: the URL/filename ot the generated resource if available 6319 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6320 * @IObuf: an output buffer for progressive output (not implemented yet) 6321 * 6322 * Apply the stylesheet to the document and generate the output according 6323 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6324 * 6325 * NOTE: This may lead to a non-wellformed output XML wise ! 6326 * NOTE: This may also result in multiple files being generated 6327 * NOTE: using IObuf, the result encoding used will be the one used for 6328 * creating the output buffer, use the following macro to read it 6329 * from the stylesheet 6330 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6331 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6332 * since the interface uses only UTF8 6333 * 6334 * Returns the number of bytes written to the main resource or -1 in case of 6335 * error. 6336 */ 6337 int 6338 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6339 const char **params, const char *output, 6340 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) 6341 { 6342 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, 6343 NULL, NULL)); 6344 } 6345 6346 /** 6347 * xsltRegisterAllElement: 6348 * @ctxt: the XPath context 6349 * 6350 * Registers all default XSLT elements in this context 6351 */ 6352 void 6353 xsltRegisterAllElement(xsltTransformContextPtr ctxt) 6354 { 6355 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", 6356 XSLT_NAMESPACE, 6357 (xsltTransformFunction) xsltApplyTemplates); 6358 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", 6359 XSLT_NAMESPACE, 6360 (xsltTransformFunction) xsltApplyImports); 6361 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", 6362 XSLT_NAMESPACE, 6363 (xsltTransformFunction) xsltCallTemplate); 6364 xsltRegisterExtElement(ctxt, (const xmlChar *) "element", 6365 XSLT_NAMESPACE, 6366 (xsltTransformFunction) xsltElement); 6367 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", 6368 XSLT_NAMESPACE, 6369 (xsltTransformFunction) xsltAttribute); 6370 xsltRegisterExtElement(ctxt, (const xmlChar *) "text", 6371 XSLT_NAMESPACE, 6372 (xsltTransformFunction) xsltText); 6373 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", 6374 XSLT_NAMESPACE, 6375 (xsltTransformFunction) xsltProcessingInstruction); 6376 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", 6377 XSLT_NAMESPACE, 6378 (xsltTransformFunction) xsltComment); 6379 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", 6380 XSLT_NAMESPACE, 6381 (xsltTransformFunction) xsltCopy); 6382 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", 6383 XSLT_NAMESPACE, 6384 (xsltTransformFunction) xsltValueOf); 6385 xsltRegisterExtElement(ctxt, (const xmlChar *) "number", 6386 XSLT_NAMESPACE, 6387 (xsltTransformFunction) xsltNumber); 6388 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", 6389 XSLT_NAMESPACE, 6390 (xsltTransformFunction) xsltForEach); 6391 xsltRegisterExtElement(ctxt, (const xmlChar *) "if", 6392 XSLT_NAMESPACE, 6393 (xsltTransformFunction) xsltIf); 6394 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", 6395 XSLT_NAMESPACE, 6396 (xsltTransformFunction) xsltChoose); 6397 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", 6398 XSLT_NAMESPACE, 6399 (xsltTransformFunction) xsltSort); 6400 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", 6401 XSLT_NAMESPACE, 6402 (xsltTransformFunction) xsltCopyOf); 6403 xsltRegisterExtElement(ctxt, (const xmlChar *) "message", 6404 XSLT_NAMESPACE, 6405 (xsltTransformFunction) xsltMessage); 6406 6407 /* 6408 * Those don't have callable entry points but are registered anyway 6409 */ 6410 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", 6411 XSLT_NAMESPACE, 6412 (xsltTransformFunction) xsltDebug); 6413 xsltRegisterExtElement(ctxt, (const xmlChar *) "param", 6414 XSLT_NAMESPACE, 6415 (xsltTransformFunction) xsltDebug); 6416 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", 6417 XSLT_NAMESPACE, 6418 (xsltTransformFunction) xsltDebug); 6419 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", 6420 XSLT_NAMESPACE, 6421 (xsltTransformFunction) xsltDebug); 6422 xsltRegisterExtElement(ctxt, (const xmlChar *) "when", 6423 XSLT_NAMESPACE, 6424 (xsltTransformFunction) xsltDebug); 6425 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", 6426 XSLT_NAMESPACE, 6427 (xsltTransformFunction) xsltDebug); 6428 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", 6429 XSLT_NAMESPACE, 6430 (xsltTransformFunction) xsltDebug); 6431 6432 } 6433