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