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 xsltTransformError(ctxt, NULL, inst, 3498 "xsltDocumentElem: write rights for %s denied\n", 3499 filename); 3500 xmlFree(URL); 3501 xmlFree(filename); 3502 return; 3503 } 3504 } 3505 3506 oldOutputFile = ctxt->outputFile; 3507 oldOutput = ctxt->output; 3508 oldInsert = ctxt->insert; 3509 oldType = ctxt->type; 3510 ctxt->outputFile = (const char *) filename; 3511 3512 style = xsltNewStylesheet(); 3513 if (style == NULL) { 3514 xsltTransformError(ctxt, NULL, inst, 3515 "xsltDocumentElem: out of memory\n"); 3516 goto error; 3517 } 3518 3519 /* 3520 * Version described in 1.1 draft allows full parameterization 3521 * of the output. 3522 */ 3523 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3524 (const xmlChar *) "version", 3525 NULL); 3526 if (prop != NULL) { 3527 if (style->version != NULL) 3528 xmlFree(style->version); 3529 style->version = prop; 3530 } 3531 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3532 (const xmlChar *) "encoding", 3533 NULL); 3534 if (prop != NULL) { 3535 if (style->encoding != NULL) 3536 xmlFree(style->encoding); 3537 style->encoding = prop; 3538 } 3539 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3540 (const xmlChar *) "method", 3541 NULL); 3542 if (prop != NULL) { 3543 const xmlChar *URI; 3544 3545 if (style->method != NULL) 3546 xmlFree(style->method); 3547 style->method = NULL; 3548 if (style->methodURI != NULL) 3549 xmlFree(style->methodURI); 3550 style->methodURI = NULL; 3551 3552 URI = xsltGetQNameURI(inst, &prop); 3553 if (prop == NULL) { 3554 if (style != NULL) style->errors++; 3555 } else if (URI == NULL) { 3556 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || 3557 (xmlStrEqual(prop, (const xmlChar *) "html")) || 3558 (xmlStrEqual(prop, (const xmlChar *) "text"))) { 3559 style->method = prop; 3560 } else { 3561 xsltTransformError(ctxt, NULL, inst, 3562 "invalid value for method: %s\n", prop); 3563 if (style != NULL) style->warnings++; 3564 } 3565 } else { 3566 style->method = prop; 3567 style->methodURI = xmlStrdup(URI); 3568 } 3569 } 3570 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3571 (const xmlChar *) 3572 "doctype-system", NULL); 3573 if (prop != NULL) { 3574 if (style->doctypeSystem != NULL) 3575 xmlFree(style->doctypeSystem); 3576 style->doctypeSystem = prop; 3577 } 3578 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3579 (const xmlChar *) 3580 "doctype-public", NULL); 3581 if (prop != NULL) { 3582 if (style->doctypePublic != NULL) 3583 xmlFree(style->doctypePublic); 3584 style->doctypePublic = prop; 3585 } 3586 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3587 (const xmlChar *) "standalone", 3588 NULL); 3589 if (prop != NULL) { 3590 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3591 style->standalone = 1; 3592 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3593 style->standalone = 0; 3594 } else { 3595 xsltTransformError(ctxt, NULL, inst, 3596 "invalid value for standalone: %s\n", 3597 prop); 3598 if (style != NULL) style->warnings++; 3599 } 3600 xmlFree(prop); 3601 } 3602 3603 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3604 (const xmlChar *) "indent", 3605 NULL); 3606 if (prop != NULL) { 3607 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3608 style->indent = 1; 3609 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3610 style->indent = 0; 3611 } else { 3612 xsltTransformError(ctxt, NULL, inst, 3613 "invalid value for indent: %s\n", prop); 3614 if (style != NULL) style->warnings++; 3615 } 3616 xmlFree(prop); 3617 } 3618 3619 prop = xsltEvalAttrValueTemplate(ctxt, inst, 3620 (const xmlChar *) 3621 "omit-xml-declaration", 3622 NULL); 3623 if (prop != NULL) { 3624 if (xmlStrEqual(prop, (const xmlChar *) "yes")) { 3625 style->omitXmlDeclaration = 1; 3626 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { 3627 style->omitXmlDeclaration = 0; 3628 } else { 3629 xsltTransformError(ctxt, NULL, inst, 3630 "invalid value for omit-xml-declaration: %s\n", 3631 prop); 3632 if (style != NULL) style->warnings++; 3633 } 3634 xmlFree(prop); 3635 } 3636 3637 elements = xsltEvalAttrValueTemplate(ctxt, inst, 3638 (const xmlChar *) 3639 "cdata-section-elements", 3640 NULL); 3641 if (elements != NULL) { 3642 if (style->stripSpaces == NULL) 3643 style->stripSpaces = xmlHashCreate(10); 3644 if (style->stripSpaces == NULL) 3645 return; 3646 3647 element = elements; 3648 while (*element != 0) { 3649 while (IS_BLANK_CH(*element)) 3650 element++; 3651 if (*element == 0) 3652 break; 3653 end = element; 3654 while ((*end != 0) && (!IS_BLANK_CH(*end))) 3655 end++; 3656 element = xmlStrndup(element, end - element); 3657 if (element) { 3658 const xmlChar *URI; 3659 3660 #ifdef WITH_XSLT_DEBUG_PARSING 3661 xsltGenericDebug(xsltGenericDebugContext, 3662 "add cdata section output element %s\n", 3663 element); 3664 #endif 3665 URI = xsltGetQNameURI(inst, &element); 3666 3667 xmlHashAddEntry2(style->stripSpaces, element, URI, 3668 (xmlChar *) "cdata"); 3669 xmlFree(element); 3670 } 3671 element = end; 3672 } 3673 xmlFree(elements); 3674 } 3675 3676 /* 3677 * Create a new document tree and process the element template 3678 */ 3679 XSLT_GET_IMPORT_PTR(method, style, method) 3680 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3681 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3682 XSLT_GET_IMPORT_PTR(version, style, version) 3683 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 3684 3685 if ((method != NULL) && 3686 (!xmlStrEqual(method, (const xmlChar *) "xml"))) { 3687 if (xmlStrEqual(method, (const xmlChar *) "html")) { 3688 ctxt->type = XSLT_OUTPUT_HTML; 3689 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3690 res = htmlNewDoc(doctypeSystem, doctypePublic); 3691 else { 3692 if (version != NULL) { 3693 #ifdef XSLT_GENERATE_HTML_DOCTYPE 3694 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 3695 #endif 3696 } 3697 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3698 } 3699 if (res == NULL) 3700 goto error; 3701 res->dict = ctxt->dict; 3702 xmlDictReference(res->dict); 3703 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 3704 xsltTransformError(ctxt, NULL, inst, 3705 "xsltDocumentElem: unsupported method xhtml\n"); 3706 ctxt->type = XSLT_OUTPUT_HTML; 3707 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); 3708 if (res == NULL) 3709 goto error; 3710 res->dict = ctxt->dict; 3711 xmlDictReference(res->dict); 3712 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 3713 ctxt->type = XSLT_OUTPUT_TEXT; 3714 res = xmlNewDoc(style->version); 3715 if (res == NULL) 3716 goto error; 3717 res->dict = ctxt->dict; 3718 xmlDictReference(res->dict); 3719 #ifdef WITH_XSLT_DEBUG 3720 xsltGenericDebug(xsltGenericDebugContext, 3721 "reusing transformation dict for output\n"); 3722 #endif 3723 } else { 3724 xsltTransformError(ctxt, NULL, inst, 3725 "xsltDocumentElem: unsupported method (%s)\n", 3726 method); 3727 goto error; 3728 } 3729 } else { 3730 ctxt->type = XSLT_OUTPUT_XML; 3731 res = xmlNewDoc(style->version); 3732 if (res == NULL) 3733 goto error; 3734 res->dict = ctxt->dict; 3735 xmlDictReference(res->dict); 3736 #ifdef WITH_XSLT_DEBUG 3737 xsltGenericDebug(xsltGenericDebugContext, 3738 "reusing transformation dict for output\n"); 3739 #endif 3740 } 3741 res->charset = XML_CHAR_ENCODING_UTF8; 3742 if (encoding != NULL) 3743 res->encoding = xmlStrdup(encoding); 3744 ctxt->output = res; 3745 ctxt->insert = (xmlNodePtr) res; 3746 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); 3747 3748 /* 3749 * Do some post processing work depending on the generated output 3750 */ 3751 root = xmlDocGetRootElement(res); 3752 if (root != NULL) { 3753 const xmlChar *doctype = NULL; 3754 3755 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 3756 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 3757 if (doctype == NULL) 3758 doctype = root->name; 3759 3760 /* 3761 * Apply the default selection of the method 3762 */ 3763 if ((method == NULL) && 3764 (root->ns == NULL) && 3765 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 3766 xmlNodePtr tmp; 3767 3768 tmp = res->children; 3769 while ((tmp != NULL) && (tmp != root)) { 3770 if (tmp->type == XML_ELEMENT_NODE) 3771 break; 3772 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 3773 break; 3774 tmp = tmp->next; 3775 } 3776 if (tmp == root) { 3777 ctxt->type = XSLT_OUTPUT_HTML; 3778 res->type = XML_HTML_DOCUMENT_NODE; 3779 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 3780 res->intSubset = xmlCreateIntSubset(res, doctype, 3781 doctypePublic, 3782 doctypeSystem); 3783 #ifdef XSLT_GENERATE_HTML_DOCTYPE 3784 } else if (version != NULL) { 3785 xsltGetHTMLIDs(version, &doctypePublic, 3786 &doctypeSystem); 3787 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3788 res->intSubset = 3789 xmlCreateIntSubset(res, doctype, 3790 doctypePublic, 3791 doctypeSystem); 3792 #endif 3793 } 3794 } 3795 3796 } 3797 if (ctxt->type == XSLT_OUTPUT_XML) { 3798 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 3799 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 3800 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 3801 res->intSubset = xmlCreateIntSubset(res, doctype, 3802 doctypePublic, 3803 doctypeSystem); 3804 } 3805 } 3806 3807 /* 3808 * Calls to redirect:write also take an optional attribute append. 3809 * Attribute append="true|yes" which will attempt to simply append 3810 * to an existing file instead of always opening a new file. The 3811 * default behavior of always overwriting the file still happens 3812 * if we do not specify append. 3813 * Note that append use will forbid use of remote URI target. 3814 */ 3815 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", 3816 NULL); 3817 if (prop != NULL) { 3818 if (xmlStrEqual(prop, (const xmlChar *) "true") || 3819 xmlStrEqual(prop, (const xmlChar *) "yes")) { 3820 style->omitXmlDeclaration = 1; 3821 redirect_write_append = 1; 3822 } else 3823 style->omitXmlDeclaration = 0; 3824 xmlFree(prop); 3825 } 3826 3827 if (redirect_write_append) { 3828 FILE *f; 3829 3830 f = fopen((const char *) filename, "ab"); 3831 if (f == NULL) { 3832 ret = -1; 3833 } else { 3834 ret = xsltSaveResultToFile(f, res, style); 3835 fclose(f); 3836 } 3837 } else { 3838 ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); 3839 } 3840 if (ret < 0) { 3841 xsltTransformError(ctxt, NULL, inst, 3842 "xsltDocumentElem: unable to save to %s\n", 3843 filename); 3844 #ifdef WITH_XSLT_DEBUG_EXTRA 3845 } else { 3846 xsltGenericDebug(xsltGenericDebugContext, 3847 "Wrote %d bytes to %s\n", ret, filename); 3848 #endif 3849 } 3850 3851 error: 3852 ctxt->output = oldOutput; 3853 ctxt->insert = oldInsert; 3854 ctxt->type = oldType; 3855 ctxt->outputFile = oldOutputFile; 3856 if (URL != NULL) 3857 xmlFree(URL); 3858 if (filename != NULL) 3859 xmlFree(filename); 3860 if (style != NULL) 3861 xsltFreeStylesheet(style); 3862 if (res != NULL) 3863 xmlFreeDoc(res); 3864 } 3865 3866 /************************************************************************ 3867 * * 3868 * Most of the XSLT-1.0 transformations * 3869 * * 3870 ************************************************************************/ 3871 3872 /** 3873 * xsltSort: 3874 * @ctxt: a XSLT process context 3875 * @node: the node in the source tree. 3876 * @inst: the xslt sort node 3877 * @comp: precomputed information 3878 * 3879 * function attached to xsl:sort nodes, but this should not be 3880 * called directly 3881 */ 3882 void 3883 xsltSort(xsltTransformContextPtr ctxt, 3884 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, 3885 xsltElemPreCompPtr comp) { 3886 if (comp == NULL) { 3887 xsltTransformError(ctxt, NULL, inst, 3888 "xsl:sort : compilation failed\n"); 3889 return; 3890 } 3891 xsltTransformError(ctxt, NULL, inst, 3892 "xsl:sort : improper use this should not be reached\n"); 3893 } 3894 3895 /** 3896 * xsltCopy: 3897 * @ctxt: an XSLT process context 3898 * @node: the node in the source tree 3899 * @inst: the element node of the XSLT-copy instruction 3900 * @castedComp: computed information of the XSLT-copy instruction 3901 * 3902 * Execute the XSLT-copy instruction on the source node. 3903 */ 3904 void 3905 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, 3906 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 3907 { 3908 #ifdef XSLT_REFACTORED 3909 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; 3910 #else 3911 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 3912 #endif 3913 xmlNodePtr copy, oldInsert; 3914 3915 oldInsert = ctxt->insert; 3916 if (ctxt->insert != NULL) { 3917 switch (node->type) { 3918 case XML_TEXT_NODE: 3919 case XML_CDATA_SECTION_NODE: 3920 /* 3921 * This text comes from the stylesheet 3922 * For stylesheets, the set of whitespace-preserving 3923 * element names consists of just xsl:text. 3924 */ 3925 #ifdef WITH_XSLT_DEBUG_PROCESS 3926 if (node->type == XML_CDATA_SECTION_NODE) { 3927 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3928 "xsltCopy: CDATA text %s\n", node->content)); 3929 } else { 3930 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3931 "xsltCopy: text %s\n", node->content)); 3932 } 3933 #endif 3934 xsltCopyText(ctxt, ctxt->insert, node, 0); 3935 break; 3936 case XML_DOCUMENT_NODE: 3937 case XML_HTML_DOCUMENT_NODE: 3938 break; 3939 case XML_ELEMENT_NODE: 3940 /* 3941 * REVISIT NOTE: The "fake" is a doc-node, not an element node. 3942 * REMOVED: 3943 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) 3944 * return; 3945 */ 3946 3947 #ifdef WITH_XSLT_DEBUG_PROCESS 3948 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3949 "xsltCopy: node %s\n", node->name)); 3950 #endif 3951 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); 3952 ctxt->insert = copy; 3953 if (comp->use != NULL) { 3954 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 3955 } 3956 break; 3957 case XML_ATTRIBUTE_NODE: { 3958 #ifdef WITH_XSLT_DEBUG_PROCESS 3959 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3960 "xsltCopy: attribute %s\n", node->name)); 3961 #endif 3962 /* 3963 * REVISIT: We could also raise an error if the parent is not 3964 * an element node. 3965 * OPTIMIZE TODO: Can we set the value/children of the 3966 * attribute without an intermediate copy of the string value? 3967 */ 3968 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); 3969 break; 3970 } 3971 case XML_PI_NODE: 3972 #ifdef WITH_XSLT_DEBUG_PROCESS 3973 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3974 "xsltCopy: PI %s\n", node->name)); 3975 #endif 3976 copy = xmlNewDocPI(ctxt->insert->doc, node->name, 3977 node->content); 3978 copy = xsltAddChild(ctxt->insert, copy); 3979 break; 3980 case XML_COMMENT_NODE: 3981 #ifdef WITH_XSLT_DEBUG_PROCESS 3982 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3983 "xsltCopy: comment\n")); 3984 #endif 3985 copy = xmlNewComment(node->content); 3986 copy = xsltAddChild(ctxt->insert, copy); 3987 break; 3988 case XML_NAMESPACE_DECL: 3989 #ifdef WITH_XSLT_DEBUG_PROCESS 3990 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, 3991 "xsltCopy: namespace declaration\n")); 3992 #endif 3993 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); 3994 break; 3995 default: 3996 break; 3997 3998 } 3999 } 4000 4001 switch (node->type) { 4002 case XML_DOCUMENT_NODE: 4003 case XML_HTML_DOCUMENT_NODE: 4004 case XML_ELEMENT_NODE: 4005 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 4006 NULL); 4007 break; 4008 default: 4009 break; 4010 } 4011 ctxt->insert = oldInsert; 4012 } 4013 4014 /** 4015 * xsltText: 4016 * @ctxt: a XSLT process context 4017 * @node: the node in the source tree. 4018 * @inst: the xslt text node 4019 * @comp: precomputed information 4020 * 4021 * Process the xslt text node on the source node 4022 */ 4023 void 4024 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, 4025 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) { 4026 if ((inst->children != NULL) && (comp != NULL)) { 4027 xmlNodePtr text = inst->children; 4028 xmlNodePtr copy; 4029 4030 while (text != NULL) { 4031 if ((text->type != XML_TEXT_NODE) && 4032 (text->type != XML_CDATA_SECTION_NODE)) { 4033 xsltTransformError(ctxt, NULL, inst, 4034 "xsl:text content problem\n"); 4035 break; 4036 } 4037 copy = xmlNewDocText(ctxt->output, text->content); 4038 if (text->type != XML_CDATA_SECTION_NODE) { 4039 #ifdef WITH_XSLT_DEBUG_PARSING 4040 xsltGenericDebug(xsltGenericDebugContext, 4041 "Disable escaping: %s\n", text->content); 4042 #endif 4043 copy->name = xmlStringTextNoenc; 4044 } 4045 copy = xsltAddChild(ctxt->insert, copy); 4046 text = text->next; 4047 } 4048 } 4049 } 4050 4051 /** 4052 * xsltElement: 4053 * @ctxt: a XSLT process context 4054 * @node: the node in the source tree. 4055 * @inst: the xslt element node 4056 * @castedComp: precomputed information 4057 * 4058 * Process the xslt element node on the source node 4059 */ 4060 void 4061 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, 4062 xmlNodePtr inst, xsltElemPreCompPtr castedComp) { 4063 #ifdef XSLT_REFACTORED 4064 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; 4065 #else 4066 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4067 #endif 4068 xmlChar *prop = NULL; 4069 const xmlChar *name, *prefix = NULL, *nsName = NULL; 4070 xmlNodePtr copy; 4071 xmlNodePtr oldInsert; 4072 4073 if (ctxt->insert == NULL) 4074 return; 4075 4076 /* 4077 * A comp->has_name == 0 indicates that we need to skip this instruction, 4078 * since it was evaluated to be invalid already during compilation. 4079 */ 4080 if (!comp->has_name) 4081 return; 4082 4083 /* 4084 * stack and saves 4085 */ 4086 oldInsert = ctxt->insert; 4087 4088 if (comp->name == NULL) { 4089 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 4090 prop = xsltEvalAttrValueTemplate(ctxt, inst, 4091 (const xmlChar *) "name", XSLT_NAMESPACE); 4092 if (prop == NULL) { 4093 xsltTransformError(ctxt, NULL, inst, 4094 "xsl:element: The attribute 'name' is missing.\n"); 4095 goto error; 4096 } 4097 if (xmlValidateQName(prop, 0)) { 4098 xsltTransformError(ctxt, NULL, inst, 4099 "xsl:element: The effective name '%s' is not a " 4100 "valid QName.\n", prop); 4101 /* we fall through to catch any further errors, if possible */ 4102 } 4103 name = xsltSplitQName(ctxt->dict, prop, &prefix); 4104 xmlFree(prop); 4105 } else { 4106 /* 4107 * The "name" value was static. 4108 */ 4109 #ifdef XSLT_REFACTORED 4110 prefix = comp->nsPrefix; 4111 name = comp->name; 4112 #else 4113 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 4114 #endif 4115 } 4116 4117 /* 4118 * Create the new element 4119 */ 4120 if (ctxt->output->dict == ctxt->dict) { 4121 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); 4122 } else { 4123 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); 4124 } 4125 if (copy == NULL) { 4126 xsltTransformError(ctxt, NULL, inst, 4127 "xsl:element : creation of %s failed\n", name); 4128 return; 4129 } 4130 copy = xsltAddChild(ctxt->insert, copy); 4131 if (copy == NULL) { 4132 xsltTransformError(ctxt, NULL, inst, 4133 "xsl:element : xsltAddChild failed\n"); 4134 return; 4135 } 4136 4137 /* 4138 * Namespace 4139 * --------- 4140 */ 4141 if (comp->has_ns) { 4142 if (comp->ns != NULL) { 4143 /* 4144 * No AVT; just plain text for the namespace name. 4145 */ 4146 if (comp->ns[0] != 0) 4147 nsName = comp->ns; 4148 } else { 4149 xmlChar *tmpNsName; 4150 /* 4151 * Eval the AVT. 4152 */ 4153 /* TODO: check attr acquisition wrt to the XSLT namespace */ 4154 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 4155 (const xmlChar *) "namespace", XSLT_NAMESPACE); 4156 /* 4157 * SPEC XSLT 1.0: 4158 * "If the string is empty, then the expanded-name of the 4159 * attribute has a null namespace URI." 4160 */ 4161 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 4162 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 4163 xmlFree(tmpNsName); 4164 } 4165 4166 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 4167 xsltTransformError(ctxt, NULL, inst, 4168 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 4169 "forbidden.\n"); 4170 goto error; 4171 } 4172 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 4173 prefix = BAD_CAST "xml"; 4174 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 4175 prefix = NULL; 4176 } 4177 } else { 4178 xmlNsPtr ns; 4179 /* 4180 * SPEC XSLT 1.0: 4181 * "If the namespace attribute is not present, then the QName is 4182 * expanded into an expanded-name using the namespace declarations 4183 * in effect for the xsl:element element, including any default 4184 * namespace declaration. 4185 */ 4186 ns = xmlSearchNs(inst->doc, inst, prefix); 4187 if (ns == NULL) { 4188 /* 4189 * TODO: Check this in the compilation layer in case it's a 4190 * static value. 4191 */ 4192 if (prefix != NULL) { 4193 xsltTransformError(ctxt, NULL, inst, 4194 "xsl:element: The QName '%s:%s' has no " 4195 "namespace binding in scope in the stylesheet; " 4196 "this is an error, since the namespace was not " 4197 "specified by the instruction itself.\n", prefix, name); 4198 } 4199 } else 4200 nsName = ns->href; 4201 } 4202 /* 4203 * Find/create a matching ns-decl in the result tree. 4204 */ 4205 if (nsName != NULL) { 4206 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { 4207 /* Don't use a prefix of "xmlns" */ 4208 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 4209 4210 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); 4211 4212 xmlFree(pref); 4213 } else { 4214 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, 4215 copy); 4216 } 4217 } else if ((copy->parent != NULL) && 4218 (copy->parent->type == XML_ELEMENT_NODE) && 4219 (copy->parent->ns != NULL)) 4220 { 4221 /* 4222 * "Undeclare" the default namespace. 4223 */ 4224 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); 4225 } 4226 4227 ctxt->insert = copy; 4228 4229 if (comp->has_use) { 4230 if (comp->use != NULL) { 4231 xsltApplyAttributeSet(ctxt, node, inst, comp->use); 4232 } else { 4233 xmlChar *attrSets = NULL; 4234 /* 4235 * BUG TODO: use-attribute-sets is not a value template. 4236 * use-attribute-sets = qnames 4237 */ 4238 attrSets = xsltEvalAttrValueTemplate(ctxt, inst, 4239 (const xmlChar *)"use-attribute-sets", NULL); 4240 if (attrSets != NULL) { 4241 xsltApplyAttributeSet(ctxt, node, inst, attrSets); 4242 xmlFree(attrSets); 4243 } 4244 } 4245 } 4246 /* 4247 * Instantiate the sequence constructor. 4248 */ 4249 if (inst->children != NULL) 4250 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, 4251 NULL); 4252 4253 error: 4254 ctxt->insert = oldInsert; 4255 return; 4256 } 4257 4258 4259 /** 4260 * xsltComment: 4261 * @ctxt: a XSLT process context 4262 * @node: the node in the source tree. 4263 * @inst: the xslt comment node 4264 * @comp: precomputed information 4265 * 4266 * Process the xslt comment node on the source node 4267 */ 4268 void 4269 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, 4270 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) { 4271 xmlChar *value = NULL; 4272 xmlNodePtr commentNode; 4273 int len; 4274 4275 value = xsltEvalTemplateString(ctxt, node, inst); 4276 /* TODO: use or generate the compiled form */ 4277 len = xmlStrlen(value); 4278 if (len > 0) { 4279 if ((value[len-1] == '-') || 4280 (xmlStrstr(value, BAD_CAST "--"))) { 4281 xsltTransformError(ctxt, NULL, inst, 4282 "xsl:comment : '--' or ending '-' not allowed in comment\n"); 4283 /* fall through to try to catch further errors */ 4284 } 4285 } 4286 #ifdef WITH_XSLT_DEBUG_PROCESS 4287 if (value == NULL) { 4288 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4289 "xsltComment: empty\n")); 4290 } else { 4291 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, 4292 "xsltComment: content %s\n", value)); 4293 } 4294 #endif 4295 4296 commentNode = xmlNewComment(value); 4297 commentNode = xsltAddChild(ctxt->insert, commentNode); 4298 4299 if (value != NULL) 4300 xmlFree(value); 4301 } 4302 4303 /** 4304 * xsltProcessingInstruction: 4305 * @ctxt: a XSLT process context 4306 * @node: the node in the source tree. 4307 * @inst: the xslt processing-instruction node 4308 * @castedComp: precomputed information 4309 * 4310 * Process the xslt processing-instruction node on the source node 4311 */ 4312 void 4313 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, 4314 xmlNodePtr inst, xsltElemPreCompPtr castedComp) { 4315 #ifdef XSLT_REFACTORED 4316 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; 4317 #else 4318 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4319 #endif 4320 const xmlChar *name; 4321 xmlChar *value = NULL; 4322 xmlNodePtr pi; 4323 4324 4325 if (ctxt->insert == NULL) 4326 return; 4327 if (comp->has_name == 0) 4328 return; 4329 if (comp->name == NULL) { 4330 name = xsltEvalAttrValueTemplate(ctxt, inst, 4331 (const xmlChar *)"name", NULL); 4332 if (name == NULL) { 4333 xsltTransformError(ctxt, NULL, inst, 4334 "xsl:processing-instruction : name is missing\n"); 4335 goto error; 4336 } 4337 } else { 4338 name = comp->name; 4339 } 4340 /* TODO: check that it's both an an NCName and a PITarget. */ 4341 4342 4343 value = xsltEvalTemplateString(ctxt, node, inst); 4344 if (xmlStrstr(value, BAD_CAST "?>") != NULL) { 4345 xsltTransformError(ctxt, NULL, inst, 4346 "xsl:processing-instruction: '?>' not allowed within PI content\n"); 4347 goto error; 4348 } 4349 #ifdef WITH_XSLT_DEBUG_PROCESS 4350 if (value == NULL) { 4351 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4352 "xsltProcessingInstruction: %s empty\n", name)); 4353 } else { 4354 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, 4355 "xsltProcessingInstruction: %s content %s\n", name, value)); 4356 } 4357 #endif 4358 4359 pi = xmlNewDocPI(ctxt->insert->doc, name, value); 4360 pi = xsltAddChild(ctxt->insert, pi); 4361 4362 error: 4363 if ((name != NULL) && (name != comp->name)) 4364 xmlFree((xmlChar *) name); 4365 if (value != NULL) 4366 xmlFree(value); 4367 } 4368 4369 /** 4370 * xsltCopyOf: 4371 * @ctxt: an XSLT transformation context 4372 * @node: the current node in the source tree 4373 * @inst: the element node of the XSLT copy-of instruction 4374 * @castedComp: precomputed information of the XSLT copy-of instruction 4375 * 4376 * Process the XSLT copy-of instruction. 4377 */ 4378 void 4379 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4380 xmlNodePtr inst, xsltElemPreCompPtr castedComp) { 4381 #ifdef XSLT_REFACTORED 4382 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; 4383 #else 4384 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4385 #endif 4386 xmlXPathObjectPtr res = NULL; 4387 xmlNodeSetPtr list = NULL; 4388 int i; 4389 4390 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4391 return; 4392 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4393 xsltTransformError(ctxt, NULL, inst, 4394 "xsl:copy-of : compilation failed\n"); 4395 return; 4396 } 4397 4398 /* 4399 * SPEC XSLT 1.0: 4400 * "The xsl:copy-of element can be used to insert a result tree 4401 * fragment into the result tree, without first converting it to 4402 * a string as xsl:value-of does (see [7.6.1 Generating Text with 4403 * xsl:value-of]). The required select attribute contains an 4404 * expression. When the result of evaluating the expression is a 4405 * result tree fragment, the complete fragment is copied into the 4406 * result tree. When the result is a node-set, all the nodes in the 4407 * set are copied in document order into the result tree; copying 4408 * an element node copies the attribute nodes, namespace nodes and 4409 * children of the element node as well as the element node itself; 4410 * a root node is copied by copying its children. When the result 4411 * is neither a node-set nor a result tree fragment, the result is 4412 * converted to a string and then inserted into the result tree, 4413 * as with xsl:value-of. 4414 */ 4415 4416 #ifdef WITH_XSLT_DEBUG_PROCESS 4417 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4418 "xsltCopyOf: select %s\n", comp->select)); 4419 #endif 4420 4421 /* 4422 * Evaluate the "select" expression. 4423 */ 4424 res = xsltPreCompEval(ctxt, node, comp); 4425 4426 if (res != NULL) { 4427 if (res->type == XPATH_NODESET) { 4428 /* 4429 * Node-set 4430 * -------- 4431 */ 4432 #ifdef WITH_XSLT_DEBUG_PROCESS 4433 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4434 "xsltCopyOf: result is a node set\n")); 4435 #endif 4436 list = res->nodesetval; 4437 if (list != NULL) { 4438 xmlNodePtr cur; 4439 /* 4440 * The list is already sorted in document order by XPath. 4441 * Append everything in this order under ctxt->insert. 4442 */ 4443 for (i = 0;i < list->nodeNr;i++) { 4444 cur = list->nodeTab[i]; 4445 if (cur == NULL) 4446 continue; 4447 if ((cur->type == XML_DOCUMENT_NODE) || 4448 (cur->type == XML_HTML_DOCUMENT_NODE)) 4449 { 4450 xsltCopyTreeList(ctxt, inst, 4451 cur->children, ctxt->insert, 0, 0); 4452 } else if (cur->type == XML_ATTRIBUTE_NODE) { 4453 xsltShallowCopyAttr(ctxt, inst, 4454 ctxt->insert, (xmlAttrPtr) cur); 4455 } else { 4456 xsltCopyTree(ctxt, inst, cur, ctxt->insert, 0, 0); 4457 } 4458 } 4459 } 4460 } else if (res->type == XPATH_XSLT_TREE) { 4461 /* 4462 * Result tree fragment 4463 * -------------------- 4464 * E.g. via <xsl:variable ...><foo/></xsl:variable> 4465 * Note that the root node of such trees is an xmlDocPtr in Libxslt. 4466 */ 4467 #ifdef WITH_XSLT_DEBUG_PROCESS 4468 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4469 "xsltCopyOf: result is a result tree fragment\n")); 4470 #endif 4471 list = res->nodesetval; 4472 if ((list != NULL) && (list->nodeTab != NULL) && 4473 (list->nodeTab[0] != NULL) && 4474 (IS_XSLT_REAL_NODE(list->nodeTab[0]))) 4475 { 4476 xsltCopyTreeList(ctxt, inst, 4477 list->nodeTab[0]->children, ctxt->insert, 0, 0); 4478 } 4479 } else { 4480 xmlChar *value = NULL; 4481 /* 4482 * Convert to a string. 4483 */ 4484 value = xmlXPathCastToString(res); 4485 if (value == NULL) { 4486 xsltTransformError(ctxt, NULL, inst, 4487 "Internal error in xsltCopyOf(): " 4488 "failed to cast an XPath object to string.\n"); 4489 ctxt->state = XSLT_STATE_STOPPED; 4490 } else { 4491 if (value[0] != 0) { 4492 /* 4493 * Append content as text node. 4494 */ 4495 xsltCopyTextString(ctxt, ctxt->insert, value, 0); 4496 } 4497 xmlFree(value); 4498 4499 #ifdef WITH_XSLT_DEBUG_PROCESS 4500 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, 4501 "xsltCopyOf: result %s\n", res->stringval)); 4502 #endif 4503 } 4504 } 4505 } else { 4506 ctxt->state = XSLT_STATE_STOPPED; 4507 } 4508 4509 if (res != NULL) 4510 xmlXPathFreeObject(res); 4511 } 4512 4513 /** 4514 * xsltValueOf: 4515 * @ctxt: a XSLT process context 4516 * @node: the node in the source tree. 4517 * @inst: the xslt value-of node 4518 * @castedComp: precomputed information 4519 * 4520 * Process the xslt value-of node on the source node 4521 */ 4522 void 4523 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, 4524 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 4525 { 4526 #ifdef XSLT_REFACTORED 4527 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; 4528 #else 4529 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4530 #endif 4531 xmlXPathObjectPtr res = NULL; 4532 xmlChar *value = NULL; 4533 4534 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) 4535 return; 4536 4537 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { 4538 xsltTransformError(ctxt, NULL, inst, 4539 "Internal error in xsltValueOf(): " 4540 "The XSLT 'value-of' instruction was not compiled.\n"); 4541 return; 4542 } 4543 4544 #ifdef WITH_XSLT_DEBUG_PROCESS 4545 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4546 "xsltValueOf: select %s\n", comp->select)); 4547 #endif 4548 4549 res = xsltPreCompEval(ctxt, node, comp); 4550 4551 /* 4552 * Cast the XPath object to string. 4553 */ 4554 if (res != NULL) { 4555 value = xmlXPathCastToString(res); 4556 if (value == NULL) { 4557 xsltTransformError(ctxt, NULL, inst, 4558 "Internal error in xsltValueOf(): " 4559 "failed to cast an XPath object to string.\n"); 4560 ctxt->state = XSLT_STATE_STOPPED; 4561 goto error; 4562 } 4563 if (value[0] != 0) { 4564 xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); 4565 } 4566 } else { 4567 xsltTransformError(ctxt, NULL, inst, 4568 "XPath evaluation returned no result.\n"); 4569 ctxt->state = XSLT_STATE_STOPPED; 4570 goto error; 4571 } 4572 4573 #ifdef WITH_XSLT_DEBUG_PROCESS 4574 if (value) { 4575 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, 4576 "xsltValueOf: result '%s'\n", value)); 4577 } 4578 #endif 4579 4580 error: 4581 if (value != NULL) 4582 xmlFree(value); 4583 if (res != NULL) 4584 xmlXPathFreeObject(res); 4585 } 4586 4587 /** 4588 * xsltNumber: 4589 * @ctxt: a XSLT process context 4590 * @node: the node in the source tree. 4591 * @inst: the xslt number node 4592 * @castedComp: precomputed information 4593 * 4594 * Process the xslt number node on the source node 4595 */ 4596 void 4597 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, 4598 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 4599 { 4600 #ifdef XSLT_REFACTORED 4601 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; 4602 #else 4603 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4604 #endif 4605 xmlXPathContextPtr xpctxt; 4606 xmlNsPtr *oldXPNamespaces; 4607 int oldXPNsNr; 4608 4609 if (comp == NULL) { 4610 xsltTransformError(ctxt, NULL, inst, 4611 "xsl:number : compilation failed\n"); 4612 return; 4613 } 4614 4615 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4616 return; 4617 4618 comp->numdata.doc = inst->doc; 4619 comp->numdata.node = inst; 4620 4621 xpctxt = ctxt->xpathCtxt; 4622 oldXPNsNr = xpctxt->nsNr; 4623 oldXPNamespaces = xpctxt->namespaces; 4624 4625 #ifdef XSLT_REFACTORED 4626 if (comp->inScopeNs != NULL) { 4627 xpctxt->namespaces = comp->inScopeNs->list; 4628 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 4629 } else { 4630 xpctxt->namespaces = NULL; 4631 xpctxt->nsNr = 0; 4632 } 4633 #else 4634 xpctxt->namespaces = comp->nsList; 4635 xpctxt->nsNr = comp->nsNr; 4636 #endif 4637 4638 xsltNumberFormat(ctxt, &comp->numdata, node); 4639 4640 xpctxt->nsNr = oldXPNsNr; 4641 xpctxt->namespaces = oldXPNamespaces; 4642 } 4643 4644 /** 4645 * xsltApplyImports: 4646 * @ctxt: an XSLT transformation context 4647 * @contextNode: the current node in the source tree. 4648 * @inst: the element node of the XSLT 'apply-imports' instruction 4649 * @comp: the compiled instruction 4650 * 4651 * Process the XSLT apply-imports element. 4652 */ 4653 void 4654 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 4655 xmlNodePtr inst, 4656 xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) 4657 { 4658 xsltTemplatePtr templ; 4659 4660 if ((ctxt == NULL) || (inst == NULL)) 4661 return; 4662 4663 if (comp == NULL) { 4664 xsltTransformError(ctxt, NULL, inst, 4665 "Internal error in xsltApplyImports(): " 4666 "The XSLT 'apply-imports' instruction was not compiled.\n"); 4667 return; 4668 } 4669 /* 4670 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the 4671 * same; the former is the "Current Template Rule" as defined by the 4672 * XSLT spec, the latter is simply the template struct being 4673 * currently processed. 4674 */ 4675 if (ctxt->currentTemplateRule == NULL) { 4676 /* 4677 * SPEC XSLT 2.0: 4678 * "[ERR XTDE0560] It is a non-recoverable dynamic error if 4679 * xsl:apply-imports or xsl:next-match is evaluated when the 4680 * current template rule is null." 4681 */ 4682 xsltTransformError(ctxt, NULL, inst, 4683 "It is an error to call 'apply-imports' " 4684 "when there's no current template rule.\n"); 4685 return; 4686 } 4687 /* 4688 * TODO: Check if this is correct. 4689 */ 4690 templ = xsltGetTemplate(ctxt, contextNode, 4691 ctxt->currentTemplateRule->style); 4692 4693 if (templ != NULL) { 4694 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; 4695 /* 4696 * Set the current template rule. 4697 */ 4698 ctxt->currentTemplateRule = templ; 4699 /* 4700 * URGENT TODO: Need xsl:with-param be handled somehow here? 4701 */ 4702 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, 4703 templ, NULL); 4704 4705 ctxt->currentTemplateRule = oldCurTemplRule; 4706 } 4707 else { 4708 /* Use built-in templates. */ 4709 xsltDefaultProcessOneNode(ctxt, contextNode, NULL); 4710 } 4711 } 4712 4713 /** 4714 * xsltCallTemplate: 4715 * @ctxt: a XSLT transformation context 4716 * @node: the "current node" in the source tree 4717 * @inst: the XSLT 'call-template' instruction 4718 * @castedComp: the compiled information of the instruction 4719 * 4720 * Processes the XSLT call-template instruction on the source node. 4721 */ 4722 void 4723 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, 4724 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 4725 { 4726 #ifdef XSLT_REFACTORED 4727 xsltStyleItemCallTemplatePtr comp = 4728 (xsltStyleItemCallTemplatePtr) castedComp; 4729 #else 4730 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4731 #endif 4732 xsltStackElemPtr withParams = NULL; 4733 4734 if (ctxt->insert == NULL) 4735 return; 4736 if (comp == NULL) { 4737 xsltTransformError(ctxt, NULL, inst, 4738 "The XSLT 'call-template' instruction was not compiled.\n"); 4739 return; 4740 } 4741 4742 /* 4743 * The template must have been precomputed 4744 */ 4745 if (comp->templ == NULL) { 4746 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); 4747 if (comp->templ == NULL) { 4748 if (comp->ns != NULL) { 4749 xsltTransformError(ctxt, NULL, inst, 4750 "The called template '{%s}%s' was not found.\n", 4751 comp->ns, comp->name); 4752 } else { 4753 xsltTransformError(ctxt, NULL, inst, 4754 "The called template '%s' was not found.\n", 4755 comp->name); 4756 } 4757 return; 4758 } 4759 } 4760 4761 #ifdef WITH_XSLT_DEBUG_PROCESS 4762 if ((comp != NULL) && (comp->name != NULL)) 4763 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4764 "call-template: name %s\n", comp->name)); 4765 #endif 4766 4767 if (inst->children) { 4768 xmlNodePtr cur; 4769 xsltStackElemPtr param; 4770 4771 cur = inst->children; 4772 while (cur != NULL) { 4773 #ifdef WITH_DEBUGGER 4774 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 4775 xslHandleDebugger(cur, node, comp->templ, ctxt); 4776 #endif 4777 if (ctxt->state == XSLT_STATE_STOPPED) break; 4778 /* 4779 * TODO: The "with-param"s could be part of the "call-template" 4780 * structure. Avoid to "search" for params dynamically 4781 * in the XML tree every time. 4782 */ 4783 if (IS_XSLT_ELEM(cur)) { 4784 if (IS_XSLT_NAME(cur, "with-param")) { 4785 param = xsltParseStylesheetCallerParam(ctxt, cur); 4786 if (param != NULL) { 4787 param->next = withParams; 4788 withParams = param; 4789 } 4790 } else { 4791 xsltGenericError(xsltGenericErrorContext, 4792 "xsl:call-template: misplaced xsl:%s\n", cur->name); 4793 } 4794 } else { 4795 xsltGenericError(xsltGenericErrorContext, 4796 "xsl:call-template: misplaced %s element\n", cur->name); 4797 } 4798 cur = cur->next; 4799 } 4800 } 4801 /* 4802 * Create a new frame using the params first 4803 */ 4804 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, 4805 withParams); 4806 if (withParams != NULL) 4807 xsltFreeStackElemList(withParams); 4808 4809 #ifdef WITH_XSLT_DEBUG_PROCESS 4810 if ((comp != NULL) && (comp->name != NULL)) 4811 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, 4812 "call-template returned: name %s\n", comp->name)); 4813 #endif 4814 } 4815 4816 /** 4817 * xsltApplyTemplates: 4818 * @ctxt: a XSLT transformation context 4819 * @node: the 'current node' in the source tree 4820 * @inst: the element node of an XSLT 'apply-templates' instruction 4821 * @castedComp: the compiled instruction 4822 * 4823 * Processes the XSLT 'apply-templates' instruction on the current node. 4824 */ 4825 void 4826 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, 4827 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 4828 { 4829 #ifdef XSLT_REFACTORED 4830 xsltStyleItemApplyTemplatesPtr comp = 4831 (xsltStyleItemApplyTemplatesPtr) castedComp; 4832 #else 4833 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 4834 #endif 4835 int i; 4836 xmlNodePtr cur, delNode = NULL, oldContextNode; 4837 xmlNodeSetPtr list = NULL, oldList; 4838 xsltStackElemPtr withParams = NULL; 4839 int oldXPProximityPosition, oldXPContextSize; 4840 const xmlChar *oldMode, *oldModeURI; 4841 xmlDocPtr oldXPDoc; 4842 xsltDocumentPtr oldDocInfo; 4843 xmlXPathContextPtr xpctxt; 4844 4845 if (comp == NULL) { 4846 xsltTransformError(ctxt, NULL, inst, 4847 "xsl:apply-templates : compilation failed\n"); 4848 return; 4849 } 4850 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) 4851 return; 4852 4853 #ifdef WITH_XSLT_DEBUG_PROCESS 4854 if ((node != NULL) && (node->name != NULL)) 4855 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4856 "xsltApplyTemplates: node: '%s'\n", node->name)); 4857 #endif 4858 4859 xpctxt = ctxt->xpathCtxt; 4860 /* 4861 * Save context states. 4862 */ 4863 oldContextNode = ctxt->node; 4864 oldMode = ctxt->mode; 4865 oldModeURI = ctxt->modeURI; 4866 oldDocInfo = ctxt->document; 4867 oldList = ctxt->nodeList; 4868 4869 /* 4870 * The xpath context size and proximity position, as 4871 * well as the xpath and context documents, may be changed 4872 * so we save their initial state and will restore on exit 4873 */ 4874 oldXPContextSize = xpctxt->contextSize; 4875 oldXPProximityPosition = xpctxt->proximityPosition; 4876 oldXPDoc = xpctxt->doc; 4877 4878 /* 4879 * Set up contexts. 4880 */ 4881 ctxt->mode = comp->mode; 4882 ctxt->modeURI = comp->modeURI; 4883 4884 if (comp->select != NULL) { 4885 xmlXPathObjectPtr res = NULL; 4886 4887 if (comp->comp == NULL) { 4888 xsltTransformError(ctxt, NULL, inst, 4889 "xsl:apply-templates : compilation failed\n"); 4890 goto error; 4891 } 4892 #ifdef WITH_XSLT_DEBUG_PROCESS 4893 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4894 "xsltApplyTemplates: select %s\n", comp->select)); 4895 #endif 4896 4897 res = xsltPreCompEval(ctxt, node, comp); 4898 4899 if (res != NULL) { 4900 if (res->type == XPATH_NODESET) { 4901 list = res->nodesetval; /* consume the node set */ 4902 res->nodesetval = NULL; 4903 } else { 4904 xsltTransformError(ctxt, NULL, inst, 4905 "The 'select' expression did not evaluate to a " 4906 "node set.\n"); 4907 ctxt->state = XSLT_STATE_STOPPED; 4908 xmlXPathFreeObject(res); 4909 goto error; 4910 } 4911 xmlXPathFreeObject(res); 4912 /* 4913 * Note: An xsl:apply-templates with a 'select' attribute, 4914 * can change the current source doc. 4915 */ 4916 } else { 4917 xsltTransformError(ctxt, NULL, inst, 4918 "Failed to evaluate the 'select' expression.\n"); 4919 ctxt->state = XSLT_STATE_STOPPED; 4920 goto error; 4921 } 4922 if (list == NULL) { 4923 #ifdef WITH_XSLT_DEBUG_PROCESS 4924 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 4925 "xsltApplyTemplates: select didn't evaluate to a node list\n")); 4926 #endif 4927 goto exit; 4928 } 4929 /* 4930 * 4931 * NOTE: Previously a document info (xsltDocument) was 4932 * created and attached to the Result Tree Fragment. 4933 * But such a document info is created on demand in 4934 * xsltKeyFunction() (functions.c), so we need to create 4935 * it here beforehand. 4936 * In order to take care of potential keys we need to 4937 * do some extra work for the case when a Result Tree Fragment 4938 * is converted into a nodeset (e.g. exslt:node-set()) : 4939 * We attach a "pseudo-doc" (xsltDocument) to _private. 4940 * This xsltDocument, together with the keyset, will be freed 4941 * when the Result Tree Fragment is freed. 4942 * 4943 */ 4944 #if 0 4945 if ((ctxt->nbKeys > 0) && 4946 (list->nodeNr != 0) && 4947 (list->nodeTab[0]->doc != NULL) && 4948 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) 4949 { 4950 /* 4951 * NOTE that it's also OK if @effectiveDocInfo will be 4952 * set to NULL. 4953 */ 4954 isRTF = 1; 4955 effectiveDocInfo = list->nodeTab[0]->doc->_private; 4956 } 4957 #endif 4958 } else { 4959 /* 4960 * Build an XPath node set with the children 4961 */ 4962 list = xmlXPathNodeSetCreate(NULL); 4963 if (list == NULL) 4964 goto error; 4965 if (node->type != XML_NAMESPACE_DECL) 4966 cur = node->children; 4967 else 4968 cur = NULL; 4969 while (cur != NULL) { 4970 switch (cur->type) { 4971 case XML_TEXT_NODE: 4972 if ((IS_BLANK_NODE(cur)) && 4973 (cur->parent != NULL) && 4974 (cur->parent->type == XML_ELEMENT_NODE) && 4975 (ctxt->style->stripSpaces != NULL)) { 4976 const xmlChar *val; 4977 4978 if (cur->parent->ns != NULL) { 4979 val = (const xmlChar *) 4980 xmlHashLookup2(ctxt->style->stripSpaces, 4981 cur->parent->name, 4982 cur->parent->ns->href); 4983 if (val == NULL) { 4984 val = (const xmlChar *) 4985 xmlHashLookup2(ctxt->style->stripSpaces, 4986 BAD_CAST "*", 4987 cur->parent->ns->href); 4988 } 4989 } else { 4990 val = (const xmlChar *) 4991 xmlHashLookup2(ctxt->style->stripSpaces, 4992 cur->parent->name, NULL); 4993 } 4994 if ((val != NULL) && 4995 (xmlStrEqual(val, (xmlChar *) "strip"))) { 4996 delNode = cur; 4997 break; 4998 } 4999 } 5000 /* no break on purpose */ 5001 case XML_ELEMENT_NODE: 5002 case XML_DOCUMENT_NODE: 5003 case XML_HTML_DOCUMENT_NODE: 5004 case XML_CDATA_SECTION_NODE: 5005 case XML_PI_NODE: 5006 case XML_COMMENT_NODE: 5007 xmlXPathNodeSetAddUnique(list, cur); 5008 break; 5009 case XML_DTD_NODE: 5010 /* Unlink the DTD, it's still reachable 5011 * using doc->intSubset */ 5012 if (cur->next != NULL) 5013 cur->next->prev = cur->prev; 5014 if (cur->prev != NULL) 5015 cur->prev->next = cur->next; 5016 break; 5017 case XML_NAMESPACE_DECL: 5018 break; 5019 default: 5020 #ifdef WITH_XSLT_DEBUG_PROCESS 5021 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5022 "xsltApplyTemplates: skipping cur type %d\n", 5023 cur->type)); 5024 #endif 5025 delNode = cur; 5026 } 5027 cur = cur->next; 5028 if (delNode != NULL) { 5029 #ifdef WITH_XSLT_DEBUG_PROCESS 5030 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5031 "xsltApplyTemplates: removing ignorable blank cur\n")); 5032 #endif 5033 xmlUnlinkNode(delNode); 5034 xmlFreeNode(delNode); 5035 delNode = NULL; 5036 } 5037 } 5038 } 5039 5040 #ifdef WITH_XSLT_DEBUG_PROCESS 5041 if (list != NULL) 5042 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 5043 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); 5044 #endif 5045 5046 if ((list == NULL) || (list->nodeNr == 0)) 5047 goto exit; 5048 5049 /* 5050 * Set the context's node set and size; this is also needed for 5051 * for xsltDoSortFunction(). 5052 */ 5053 ctxt->nodeList = list; 5054 /* 5055 * Process xsl:with-param and xsl:sort instructions. 5056 * (The code became so verbose just to avoid the 5057 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) 5058 * BUG TODO: We are not using namespaced potentially defined on the 5059 * xsl:sort or xsl:with-param elements; XPath expression might fail. 5060 */ 5061 if (inst->children) { 5062 xsltStackElemPtr param; 5063 5064 cur = inst->children; 5065 while (cur) { 5066 5067 #ifdef WITH_DEBUGGER 5068 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5069 xslHandleDebugger(cur, node, NULL, ctxt); 5070 #endif 5071 if (ctxt->state == XSLT_STATE_STOPPED) 5072 break; 5073 if (cur->type == XML_TEXT_NODE) { 5074 cur = cur->next; 5075 continue; 5076 } 5077 if (! IS_XSLT_ELEM(cur)) 5078 break; 5079 if (IS_XSLT_NAME(cur, "with-param")) { 5080 param = xsltParseStylesheetCallerParam(ctxt, cur); 5081 if (param != NULL) { 5082 param->next = withParams; 5083 withParams = param; 5084 } 5085 } 5086 if (IS_XSLT_NAME(cur, "sort")) { 5087 xsltTemplatePtr oldCurTempRule = 5088 ctxt->currentTemplateRule; 5089 int nbsorts = 0; 5090 xmlNodePtr sorts[XSLT_MAX_SORT]; 5091 5092 sorts[nbsorts++] = cur; 5093 5094 while (cur) { 5095 5096 #ifdef WITH_DEBUGGER 5097 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 5098 xslHandleDebugger(cur, node, NULL, ctxt); 5099 #endif 5100 if (ctxt->state == XSLT_STATE_STOPPED) 5101 break; 5102 5103 if (cur->type == XML_TEXT_NODE) { 5104 cur = cur->next; 5105 continue; 5106 } 5107 5108 if (! IS_XSLT_ELEM(cur)) 5109 break; 5110 if (IS_XSLT_NAME(cur, "with-param")) { 5111 param = xsltParseStylesheetCallerParam(ctxt, cur); 5112 if (param != NULL) { 5113 param->next = withParams; 5114 withParams = param; 5115 } 5116 } 5117 if (IS_XSLT_NAME(cur, "sort")) { 5118 if (nbsorts >= XSLT_MAX_SORT) { 5119 xsltTransformError(ctxt, NULL, cur, 5120 "The number (%d) of xsl:sort instructions exceeds the " 5121 "maximum allowed by this processor's settings.\n", 5122 nbsorts); 5123 ctxt->state = XSLT_STATE_STOPPED; 5124 break; 5125 } else { 5126 sorts[nbsorts++] = cur; 5127 } 5128 } 5129 cur = cur->next; 5130 } 5131 /* 5132 * The "current template rule" is cleared for xsl:sort. 5133 */ 5134 ctxt->currentTemplateRule = NULL; 5135 /* 5136 * Sort. 5137 */ 5138 xsltDoSortFunction(ctxt, sorts, nbsorts); 5139 ctxt->currentTemplateRule = oldCurTempRule; 5140 break; 5141 } 5142 cur = cur->next; 5143 } 5144 } 5145 xpctxt->contextSize = list->nodeNr; 5146 /* 5147 * Apply templates for all selected source nodes. 5148 */ 5149 for (i = 0; i < list->nodeNr; i++) { 5150 cur = list->nodeTab[i]; 5151 /* 5152 * The node becomes the "current node". 5153 */ 5154 ctxt->node = cur; 5155 /* 5156 * An xsl:apply-templates can change the current context doc. 5157 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5158 */ 5159 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5160 xpctxt->doc = cur->doc; 5161 5162 xpctxt->proximityPosition = i + 1; 5163 /* 5164 * Find and apply a template for this node. 5165 */ 5166 xsltProcessOneNode(ctxt, cur, withParams); 5167 } 5168 5169 exit: 5170 error: 5171 /* 5172 * Free the parameter list. 5173 */ 5174 if (withParams != NULL) 5175 xsltFreeStackElemList(withParams); 5176 if (list != NULL) 5177 xmlXPathFreeNodeSet(list); 5178 /* 5179 * Restore context states. 5180 */ 5181 xpctxt->doc = oldXPDoc; 5182 xpctxt->contextSize = oldXPContextSize; 5183 xpctxt->proximityPosition = oldXPProximityPosition; 5184 5185 ctxt->document = oldDocInfo; 5186 ctxt->nodeList = oldList; 5187 ctxt->node = oldContextNode; 5188 ctxt->mode = oldMode; 5189 ctxt->modeURI = oldModeURI; 5190 } 5191 5192 5193 /** 5194 * xsltChoose: 5195 * @ctxt: a XSLT process context 5196 * @contextNode: the current node in the source tree 5197 * @inst: the xsl:choose instruction 5198 * @comp: compiled information of the instruction 5199 * 5200 * Processes the xsl:choose instruction on the source node. 5201 */ 5202 void 5203 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5204 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) 5205 { 5206 xmlNodePtr cur; 5207 5208 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5209 return; 5210 5211 /* 5212 * TODO: Content model checks should be done only at compilation 5213 * time. 5214 */ 5215 cur = inst->children; 5216 if (cur == NULL) { 5217 xsltTransformError(ctxt, NULL, inst, 5218 "xsl:choose: The instruction has no content.\n"); 5219 return; 5220 } 5221 5222 #ifdef XSLT_REFACTORED 5223 /* 5224 * We don't check the content model during transformation. 5225 */ 5226 #else 5227 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { 5228 xsltTransformError(ctxt, NULL, inst, 5229 "xsl:choose: xsl:when expected first\n"); 5230 return; 5231 } 5232 #endif 5233 5234 { 5235 int testRes = 0, res = 0; 5236 5237 #ifdef XSLT_REFACTORED 5238 xsltStyleItemWhenPtr wcomp = NULL; 5239 #else 5240 xsltStylePreCompPtr wcomp = NULL; 5241 #endif 5242 5243 /* 5244 * Process xsl:when --------------------------------------------------- 5245 */ 5246 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { 5247 wcomp = cur->psvi; 5248 5249 if ((wcomp == NULL) || (wcomp->test == NULL) || 5250 (wcomp->comp == NULL)) 5251 { 5252 xsltTransformError(ctxt, NULL, cur, 5253 "Internal error in xsltChoose(): " 5254 "The XSLT 'when' instruction was not compiled.\n"); 5255 goto error; 5256 } 5257 5258 5259 #ifdef WITH_DEBUGGER 5260 if (xslDebugStatus != XSLT_DEBUG_NONE) { 5261 /* 5262 * TODO: Isn't comp->templ always NULL for xsl:choose? 5263 */ 5264 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5265 } 5266 #endif 5267 #ifdef WITH_XSLT_DEBUG_PROCESS 5268 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5269 "xsltChoose: test %s\n", wcomp->test)); 5270 #endif 5271 5272 #ifdef XSLT_FAST_IF 5273 res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp); 5274 5275 if (res == -1) { 5276 ctxt->state = XSLT_STATE_STOPPED; 5277 goto error; 5278 } 5279 testRes = (res == 1) ? 1 : 0; 5280 5281 #else /* XSLT_FAST_IF */ 5282 5283 res = xsltPreCompEval(ctxt, cotextNode, wcomp); 5284 5285 if (res != NULL) { 5286 if (res->type != XPATH_BOOLEAN) 5287 res = xmlXPathConvertBoolean(res); 5288 if (res->type == XPATH_BOOLEAN) 5289 testRes = res->boolval; 5290 else { 5291 #ifdef WITH_XSLT_DEBUG_PROCESS 5292 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5293 "xsltChoose: test didn't evaluate to a boolean\n")); 5294 #endif 5295 goto error; 5296 } 5297 xmlXPathFreeObject(res); 5298 res = NULL; 5299 } else { 5300 ctxt->state = XSLT_STATE_STOPPED; 5301 goto error; 5302 } 5303 5304 #endif /* else of XSLT_FAST_IF */ 5305 5306 #ifdef WITH_XSLT_DEBUG_PROCESS 5307 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5308 "xsltChoose: test evaluate to %d\n", testRes)); 5309 #endif 5310 if (testRes) 5311 goto test_is_true; 5312 5313 cur = cur->next; 5314 } 5315 5316 /* 5317 * Process xsl:otherwise ---------------------------------------------- 5318 */ 5319 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { 5320 5321 #ifdef WITH_DEBUGGER 5322 if (xslDebugStatus != XSLT_DEBUG_NONE) 5323 xslHandleDebugger(cur, contextNode, NULL, ctxt); 5324 #endif 5325 5326 #ifdef WITH_XSLT_DEBUG_PROCESS 5327 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, 5328 "evaluating xsl:otherwise\n")); 5329 #endif 5330 goto test_is_true; 5331 } 5332 goto exit; 5333 5334 test_is_true: 5335 5336 goto process_sequence; 5337 } 5338 5339 process_sequence: 5340 5341 /* 5342 * Instantiate the sequence constructor. 5343 */ 5344 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, 5345 NULL); 5346 5347 exit: 5348 error: 5349 return; 5350 } 5351 5352 /** 5353 * xsltIf: 5354 * @ctxt: a XSLT process context 5355 * @contextNode: the current node in the source tree 5356 * @inst: the xsl:if instruction 5357 * @castedComp: compiled information of the instruction 5358 * 5359 * Processes the xsl:if instruction on the source node. 5360 */ 5361 void 5362 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5363 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 5364 { 5365 int res = 0; 5366 5367 #ifdef XSLT_REFACTORED 5368 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; 5369 #else 5370 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 5371 #endif 5372 5373 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 5374 return; 5375 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { 5376 xsltTransformError(ctxt, NULL, inst, 5377 "Internal error in xsltIf(): " 5378 "The XSLT 'if' instruction was not compiled.\n"); 5379 return; 5380 } 5381 5382 #ifdef WITH_XSLT_DEBUG_PROCESS 5383 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5384 "xsltIf: test %s\n", comp->test)); 5385 #endif 5386 5387 #ifdef XSLT_FAST_IF 5388 { 5389 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; 5390 5391 res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); 5392 5393 /* 5394 * Cleanup fragments created during evaluation of the 5395 * "select" expression. 5396 */ 5397 if (oldLocalFragmentTop != ctxt->localRVT) 5398 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); 5399 } 5400 5401 #ifdef WITH_XSLT_DEBUG_PROCESS 5402 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5403 "xsltIf: test evaluate to %d\n", res)); 5404 #endif 5405 5406 if (res == -1) { 5407 ctxt->state = XSLT_STATE_STOPPED; 5408 goto error; 5409 } 5410 if (res == 1) { 5411 /* 5412 * Instantiate the sequence constructor of xsl:if. 5413 */ 5414 xsltApplySequenceConstructor(ctxt, 5415 contextNode, inst->children, NULL); 5416 } 5417 5418 #else /* XSLT_FAST_IF */ 5419 { 5420 /* 5421 * OLD CODE: 5422 */ 5423 xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp); 5424 if (xpobj != NULL) { 5425 if (xpobj->type != XPATH_BOOLEAN) 5426 xpobj = xmlXPathConvertBoolean(xpobj); 5427 if (xpobj->type == XPATH_BOOLEAN) { 5428 res = xpobj->boolval; 5429 5430 #ifdef WITH_XSLT_DEBUG_PROCESS 5431 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, 5432 "xsltIf: test evaluate to %d\n", res)); 5433 #endif 5434 if (res) { 5435 xsltApplySequenceConstructor(ctxt, 5436 contextNode, inst->children, NULL); 5437 } 5438 } else { 5439 5440 #ifdef WITH_XSLT_DEBUG_PROCESS 5441 XSLT_TRACE(ctxt, XSLT_TRACE_IF, 5442 xsltGenericDebug(xsltGenericDebugContext, 5443 "xsltIf: test didn't evaluate to a boolean\n")); 5444 #endif 5445 ctxt->state = XSLT_STATE_STOPPED; 5446 } 5447 xmlXPathFreeObject(xpobj); 5448 } else { 5449 ctxt->state = XSLT_STATE_STOPPED; 5450 } 5451 } 5452 #endif /* else of XSLT_FAST_IF */ 5453 5454 error: 5455 return; 5456 } 5457 5458 /** 5459 * xsltForEach: 5460 * @ctxt: an XSLT transformation context 5461 * @contextNode: the "current node" in the source tree 5462 * @inst: the element node of the xsl:for-each instruction 5463 * @castedComp: the compiled information of the instruction 5464 * 5465 * Process the xslt for-each node on the source node 5466 */ 5467 void 5468 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, 5469 xmlNodePtr inst, xsltElemPreCompPtr castedComp) 5470 { 5471 #ifdef XSLT_REFACTORED 5472 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; 5473 #else 5474 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 5475 #endif 5476 int i; 5477 xmlXPathObjectPtr res = NULL; 5478 xmlNodePtr cur, curInst; 5479 xmlNodeSetPtr list = NULL; 5480 xmlNodeSetPtr oldList; 5481 int oldXPProximityPosition, oldXPContextSize; 5482 xmlNodePtr oldContextNode; 5483 xsltTemplatePtr oldCurTemplRule; 5484 xmlDocPtr oldXPDoc; 5485 xsltDocumentPtr oldDocInfo; 5486 xmlXPathContextPtr xpctxt; 5487 5488 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { 5489 xsltGenericError(xsltGenericErrorContext, 5490 "xsltForEach(): Bad arguments.\n"); 5491 return; 5492 } 5493 5494 if (comp == NULL) { 5495 xsltTransformError(ctxt, NULL, inst, 5496 "Internal error in xsltForEach(): " 5497 "The XSLT 'for-each' instruction was not compiled.\n"); 5498 return; 5499 } 5500 if ((comp->select == NULL) || (comp->comp == NULL)) { 5501 xsltTransformError(ctxt, NULL, inst, 5502 "Internal error in xsltForEach(): " 5503 "The selecting expression of the XSLT 'for-each' " 5504 "instruction was not compiled correctly.\n"); 5505 return; 5506 } 5507 xpctxt = ctxt->xpathCtxt; 5508 5509 #ifdef WITH_XSLT_DEBUG_PROCESS 5510 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5511 "xsltForEach: select %s\n", comp->select)); 5512 #endif 5513 5514 /* 5515 * Save context states. 5516 */ 5517 oldDocInfo = ctxt->document; 5518 oldList = ctxt->nodeList; 5519 oldContextNode = ctxt->node; 5520 /* 5521 * The "current template rule" is cleared for the instantiation of 5522 * xsl:for-each. 5523 */ 5524 oldCurTemplRule = ctxt->currentTemplateRule; 5525 ctxt->currentTemplateRule = NULL; 5526 5527 oldXPDoc = xpctxt->doc; 5528 oldXPProximityPosition = xpctxt->proximityPosition; 5529 oldXPContextSize = xpctxt->contextSize; 5530 5531 /* 5532 * Evaluate the 'select' expression. 5533 */ 5534 res = xsltPreCompEval(ctxt, contextNode, comp); 5535 5536 if (res != NULL) { 5537 if (res->type == XPATH_NODESET) 5538 list = res->nodesetval; 5539 else { 5540 xsltTransformError(ctxt, NULL, inst, 5541 "The 'select' expression does not evaluate to a node set.\n"); 5542 5543 #ifdef WITH_XSLT_DEBUG_PROCESS 5544 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5545 "xsltForEach: select didn't evaluate to a node list\n")); 5546 #endif 5547 goto error; 5548 } 5549 } else { 5550 xsltTransformError(ctxt, NULL, inst, 5551 "Failed to evaluate the 'select' expression.\n"); 5552 ctxt->state = XSLT_STATE_STOPPED; 5553 goto error; 5554 } 5555 5556 if ((list == NULL) || (list->nodeNr <= 0)) 5557 goto exit; 5558 5559 #ifdef WITH_XSLT_DEBUG_PROCESS 5560 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, 5561 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); 5562 #endif 5563 5564 /* 5565 * Set the list; this has to be done already here for xsltDoSortFunction(). 5566 */ 5567 ctxt->nodeList = list; 5568 /* 5569 * Handle xsl:sort instructions and skip them for further processing. 5570 * BUG TODO: We are not using namespaced potentially defined on the 5571 * xsl:sort element; XPath expression might fail. 5572 */ 5573 curInst = inst->children; 5574 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5575 int nbsorts = 0; 5576 xmlNodePtr sorts[XSLT_MAX_SORT]; 5577 5578 sorts[nbsorts++] = curInst; 5579 5580 #ifdef WITH_DEBUGGER 5581 if (xslDebugStatus != XSLT_DEBUG_NONE) 5582 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5583 #endif 5584 5585 curInst = curInst->next; 5586 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { 5587 if (nbsorts >= XSLT_MAX_SORT) { 5588 xsltTransformError(ctxt, NULL, curInst, 5589 "The number of xsl:sort instructions exceeds the " 5590 "maximum (%d) allowed by this processor.\n", 5591 XSLT_MAX_SORT); 5592 goto error; 5593 } else { 5594 sorts[nbsorts++] = curInst; 5595 } 5596 5597 #ifdef WITH_DEBUGGER 5598 if (xslDebugStatus != XSLT_DEBUG_NONE) 5599 xslHandleDebugger(curInst, contextNode, NULL, ctxt); 5600 #endif 5601 curInst = curInst->next; 5602 } 5603 xsltDoSortFunction(ctxt, sorts, nbsorts); 5604 } 5605 xpctxt->contextSize = list->nodeNr; 5606 /* 5607 * Instantiate the sequence constructor for each selected node. 5608 */ 5609 for (i = 0; i < list->nodeNr; i++) { 5610 cur = list->nodeTab[i]; 5611 /* 5612 * The selected node becomes the "current node". 5613 */ 5614 ctxt->node = cur; 5615 /* 5616 * An xsl:for-each can change the current context doc. 5617 * OPTIMIZE TODO: Get rid of the need to set the context doc. 5618 */ 5619 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) 5620 xpctxt->doc = cur->doc; 5621 5622 xpctxt->proximityPosition = i + 1; 5623 5624 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); 5625 } 5626 5627 exit: 5628 error: 5629 if (res != NULL) 5630 xmlXPathFreeObject(res); 5631 /* 5632 * Restore old states. 5633 */ 5634 ctxt->document = oldDocInfo; 5635 ctxt->nodeList = oldList; 5636 ctxt->node = oldContextNode; 5637 ctxt->currentTemplateRule = oldCurTemplRule; 5638 5639 xpctxt->doc = oldXPDoc; 5640 xpctxt->contextSize = oldXPContextSize; 5641 xpctxt->proximityPosition = oldXPProximityPosition; 5642 } 5643 5644 /************************************************************************ 5645 * * 5646 * Generic interface * 5647 * * 5648 ************************************************************************/ 5649 5650 #ifdef XSLT_GENERATE_HTML_DOCTYPE 5651 typedef struct xsltHTMLVersion { 5652 const char *version; 5653 const char *public; 5654 const char *system; 5655 } xsltHTMLVersion; 5656 5657 static xsltHTMLVersion xsltHTMLVersions[] = { 5658 { "5", NULL, "about:legacy-compat" }, 5659 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5660 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, 5661 { "4.01strict", "-//W3C//DTD HTML 4.01//EN", 5662 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, 5663 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5664 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5665 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", 5666 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, 5667 { "4.0strict", "-//W3C//DTD HTML 4.01//EN", 5668 "http://www.w3.org/TR/html4/strict.dtd"}, 5669 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", 5670 "http://www.w3.org/TR/html4/loose.dtd"}, 5671 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", 5672 "http://www.w3.org/TR/html4/frameset.dtd"}, 5673 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", 5674 "http://www.w3.org/TR/html4/loose.dtd"}, 5675 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } 5676 }; 5677 5678 /** 5679 * xsltGetHTMLIDs: 5680 * @version: the version string 5681 * @publicID: used to return the public ID 5682 * @systemID: used to return the system ID 5683 * 5684 * Returns -1 if not found, 0 otherwise and the system and public 5685 * Identifier for this given verion of HTML 5686 */ 5687 static int 5688 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, 5689 const xmlChar **systemID) { 5690 unsigned int i; 5691 if (version == NULL) 5692 return(-1); 5693 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); 5694 i++) { 5695 if (!xmlStrcasecmp(version, 5696 (const xmlChar *) xsltHTMLVersions[i].version)) { 5697 if (publicID != NULL) 5698 *publicID = (const xmlChar *) xsltHTMLVersions[i].public; 5699 if (systemID != NULL) 5700 *systemID = (const xmlChar *) xsltHTMLVersions[i].system; 5701 return(0); 5702 } 5703 } 5704 return(-1); 5705 } 5706 #endif 5707 5708 /** 5709 * xsltApplyStripSpaces: 5710 * @ctxt: a XSLT process context 5711 * @node: the root of the XML tree 5712 * 5713 * Strip the unwanted ignorable spaces from the input tree 5714 */ 5715 void 5716 xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { 5717 xmlNodePtr current; 5718 #ifdef WITH_XSLT_DEBUG_PROCESS 5719 int nb = 0; 5720 #endif 5721 5722 5723 current = node; 5724 while (current != NULL) { 5725 /* 5726 * Cleanup children empty nodes if asked for 5727 */ 5728 if ((IS_XSLT_REAL_NODE(current)) && 5729 (current->children != NULL) && 5730 (xsltFindElemSpaceHandling(ctxt, current))) { 5731 xmlNodePtr delete = NULL, cur = current->children; 5732 5733 while (cur != NULL) { 5734 if (IS_BLANK_NODE(cur)) 5735 delete = cur; 5736 5737 cur = cur->next; 5738 if (delete != NULL) { 5739 xmlUnlinkNode(delete); 5740 xmlFreeNode(delete); 5741 delete = NULL; 5742 #ifdef WITH_XSLT_DEBUG_PROCESS 5743 nb++; 5744 #endif 5745 } 5746 } 5747 } 5748 5749 /* 5750 * Skip to next node in document order. 5751 */ 5752 if (node->type == XML_ENTITY_REF_NODE) { 5753 /* process deep in entities */ 5754 xsltApplyStripSpaces(ctxt, node->children); 5755 } 5756 if ((current->children != NULL) && 5757 (current->type != XML_ENTITY_REF_NODE)) { 5758 current = current->children; 5759 } else if (current->next != NULL) { 5760 current = current->next; 5761 } else { 5762 do { 5763 current = current->parent; 5764 if (current == NULL) 5765 break; 5766 if (current == node) 5767 goto done; 5768 if (current->next != NULL) { 5769 current = current->next; 5770 break; 5771 } 5772 } while (current != NULL); 5773 } 5774 } 5775 5776 done: 5777 #ifdef WITH_XSLT_DEBUG_PROCESS 5778 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext, 5779 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); 5780 #endif 5781 return; 5782 } 5783 5784 static int 5785 xsltCountKeys(xsltTransformContextPtr ctxt) 5786 { 5787 xsltStylesheetPtr style; 5788 xsltKeyDefPtr keyd; 5789 5790 if (ctxt == NULL) 5791 return(-1); 5792 5793 /* 5794 * Do we have those nastly templates with a key() in the match pattern? 5795 */ 5796 ctxt->hasTemplKeyPatterns = 0; 5797 style = ctxt->style; 5798 while (style != NULL) { 5799 if (style->keyMatch != NULL) { 5800 ctxt->hasTemplKeyPatterns = 1; 5801 break; 5802 } 5803 style = xsltNextImport(style); 5804 } 5805 /* 5806 * Count number of key declarations. 5807 */ 5808 ctxt->nbKeys = 0; 5809 style = ctxt->style; 5810 while (style != NULL) { 5811 keyd = style->keys; 5812 while (keyd) { 5813 ctxt->nbKeys++; 5814 keyd = keyd->next; 5815 } 5816 style = xsltNextImport(style); 5817 } 5818 return(ctxt->nbKeys); 5819 } 5820 5821 /** 5822 * xsltApplyStylesheetInternal: 5823 * @style: a parsed XSLT stylesheet 5824 * @doc: a parsed XML document 5825 * @params: a NULL terminated array of parameters names/values tuples 5826 * @output: the targetted output 5827 * @profile: profile FILE * output or NULL 5828 * @user: user provided parameter 5829 * 5830 * Apply the stylesheet to the document 5831 * NOTE: This may lead to a non-wellformed output XML wise ! 5832 * 5833 * Returns the result document or NULL in case of error 5834 */ 5835 static xmlDocPtr 5836 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, 5837 const char **params, const char *output, 5838 FILE * profile, xsltTransformContextPtr userCtxt) 5839 { 5840 xmlDocPtr res = NULL; 5841 xsltTransformContextPtr ctxt = NULL; 5842 xmlNodePtr root, node; 5843 const xmlChar *method; 5844 const xmlChar *doctypePublic; 5845 const xmlChar *doctypeSystem; 5846 const xmlChar *version; 5847 const xmlChar *encoding; 5848 xsltStackElemPtr variables; 5849 xsltStackElemPtr vptr; 5850 5851 xsltInitGlobals(); 5852 5853 if ((style == NULL) || (doc == NULL)) 5854 return (NULL); 5855 5856 if (style->internalized == 0) { 5857 #ifdef WITH_XSLT_DEBUG 5858 xsltGenericDebug(xsltGenericDebugContext, 5859 "Stylesheet was not fully internalized !\n"); 5860 #endif 5861 } 5862 if (doc->intSubset != NULL) { 5863 /* 5864 * Avoid hitting the DTD when scanning nodes 5865 * but keep it linked as doc->intSubset 5866 */ 5867 xmlNodePtr cur = (xmlNodePtr) doc->intSubset; 5868 if (cur->next != NULL) 5869 cur->next->prev = cur->prev; 5870 if (cur->prev != NULL) 5871 cur->prev->next = cur->next; 5872 if (doc->children == cur) 5873 doc->children = cur->next; 5874 if (doc->last == cur) 5875 doc->last = cur->prev; 5876 cur->prev = cur->next = NULL; 5877 } 5878 5879 /* 5880 * Check for XPath document order availability 5881 */ 5882 root = xmlDocGetRootElement(doc); 5883 if (root != NULL) { 5884 if (((ptrdiff_t) root->content >= 0) && 5885 (xslDebugStatus == XSLT_DEBUG_NONE)) 5886 xmlXPathOrderDocElems(doc); 5887 } 5888 5889 if (userCtxt != NULL) 5890 ctxt = userCtxt; 5891 else 5892 ctxt = xsltNewTransformContext(style, doc); 5893 5894 if (ctxt == NULL) 5895 return (NULL); 5896 5897 ctxt->initialContextDoc = doc; 5898 ctxt->initialContextNode = (xmlNodePtr) doc; 5899 5900 if (profile != NULL) 5901 ctxt->profile = 1; 5902 5903 if (output != NULL) 5904 ctxt->outputFile = output; 5905 else 5906 ctxt->outputFile = NULL; 5907 5908 /* 5909 * internalize the modes if needed 5910 */ 5911 if (ctxt->dict != NULL) { 5912 if (ctxt->mode != NULL) 5913 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); 5914 if (ctxt->modeURI != NULL) 5915 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); 5916 } 5917 5918 XSLT_GET_IMPORT_PTR(method, style, method) 5919 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 5920 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 5921 XSLT_GET_IMPORT_PTR(version, style, version) 5922 XSLT_GET_IMPORT_PTR(encoding, style, encoding) 5923 5924 if ((method != NULL) && 5925 (!xmlStrEqual(method, (const xmlChar *) "xml"))) 5926 { 5927 if (xmlStrEqual(method, (const xmlChar *) "html")) { 5928 ctxt->type = XSLT_OUTPUT_HTML; 5929 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 5930 res = htmlNewDoc(doctypeSystem, doctypePublic); 5931 } else { 5932 if (version == NULL) { 5933 xmlDtdPtr dtd; 5934 5935 res = htmlNewDoc(NULL, NULL); 5936 /* 5937 * Make sure no DTD node is generated in this case 5938 */ 5939 if (res != NULL) { 5940 dtd = xmlGetIntSubset(res); 5941 if (dtd != NULL) { 5942 xmlUnlinkNode((xmlNodePtr) dtd); 5943 xmlFreeDtd(dtd); 5944 } 5945 res->intSubset = NULL; 5946 res->extSubset = NULL; 5947 } 5948 } else { 5949 5950 #ifdef XSLT_GENERATE_HTML_DOCTYPE 5951 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); 5952 #endif 5953 res = htmlNewDoc(doctypeSystem, doctypePublic); 5954 } 5955 } 5956 if (res == NULL) 5957 goto error; 5958 res->dict = ctxt->dict; 5959 xmlDictReference(res->dict); 5960 5961 #ifdef WITH_XSLT_DEBUG 5962 xsltGenericDebug(xsltGenericDebugContext, 5963 "reusing transformation dict for output\n"); 5964 #endif 5965 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { 5966 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 5967 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n"); 5968 ctxt->type = XSLT_OUTPUT_HTML; 5969 res = htmlNewDoc(doctypeSystem, doctypePublic); 5970 if (res == NULL) 5971 goto error; 5972 res->dict = ctxt->dict; 5973 xmlDictReference(res->dict); 5974 5975 #ifdef WITH_XSLT_DEBUG 5976 xsltGenericDebug(xsltGenericDebugContext, 5977 "reusing transformation dict for output\n"); 5978 #endif 5979 } else if (xmlStrEqual(method, (const xmlChar *) "text")) { 5980 ctxt->type = XSLT_OUTPUT_TEXT; 5981 res = xmlNewDoc(style->version); 5982 if (res == NULL) 5983 goto error; 5984 res->dict = ctxt->dict; 5985 xmlDictReference(res->dict); 5986 5987 #ifdef WITH_XSLT_DEBUG 5988 xsltGenericDebug(xsltGenericDebugContext, 5989 "reusing transformation dict for output\n"); 5990 #endif 5991 } else { 5992 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, 5993 "xsltApplyStylesheetInternal: unsupported method (%s)\n", 5994 method); 5995 goto error; 5996 } 5997 } else { 5998 ctxt->type = XSLT_OUTPUT_XML; 5999 res = xmlNewDoc(style->version); 6000 if (res == NULL) 6001 goto error; 6002 res->dict = ctxt->dict; 6003 xmlDictReference(ctxt->dict); 6004 #ifdef WITH_XSLT_DEBUG 6005 xsltGenericDebug(xsltGenericDebugContext, 6006 "reusing transformation dict for output\n"); 6007 #endif 6008 } 6009 res->charset = XML_CHAR_ENCODING_UTF8; 6010 if (encoding != NULL) 6011 res->encoding = xmlStrdup(encoding); 6012 variables = style->variables; 6013 6014 /* 6015 * Start the evaluation, evaluate the params, the stylesheets globals 6016 * and start by processing the top node. 6017 */ 6018 if (xsltNeedElemSpaceHandling(ctxt)) 6019 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); 6020 /* 6021 * Evaluate global params and user-provided params. 6022 */ 6023 ctxt->node = (xmlNodePtr) doc; 6024 if (ctxt->globalVars == NULL) 6025 ctxt->globalVars = xmlHashCreate(20); 6026 if (params != NULL) { 6027 xsltEvalUserParams(ctxt, params); 6028 } 6029 6030 /* need to be called before evaluating global variables */ 6031 xsltCountKeys(ctxt); 6032 6033 xsltEvalGlobalVariables(ctxt); 6034 6035 /* Clean up any unused RVTs. */ 6036 xsltReleaseLocalRVTs(ctxt, NULL); 6037 6038 ctxt->node = (xmlNodePtr) doc; 6039 ctxt->output = res; 6040 ctxt->insert = (xmlNodePtr) res; 6041 ctxt->varsBase = ctxt->varsNr - 1; 6042 6043 ctxt->xpathCtxt->contextSize = 1; 6044 ctxt->xpathCtxt->proximityPosition = 1; 6045 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ 6046 /* 6047 * Start processing the source tree ----------------------------------- 6048 */ 6049 xsltProcessOneNode(ctxt, ctxt->node, NULL); 6050 /* 6051 * Remove all remaining vars from the stack. 6052 */ 6053 xsltLocalVariablePop(ctxt, 0, -2); 6054 xsltShutdownCtxtExts(ctxt); 6055 6056 xsltCleanupTemplates(style); /* TODO: <- style should be read only */ 6057 6058 /* 6059 * Now cleanup our variables so stylesheet can be re-used 6060 * 6061 * TODO: this is not needed anymore global variables are copied 6062 * and not evaluated directly anymore, keep this as a check 6063 */ 6064 if (style->variables != variables) { 6065 vptr = style->variables; 6066 while (vptr->next != variables) 6067 vptr = vptr->next; 6068 vptr->next = NULL; 6069 xsltFreeStackElemList(style->variables); 6070 style->variables = variables; 6071 } 6072 vptr = style->variables; 6073 while (vptr != NULL) { 6074 if (vptr->computed) { 6075 if (vptr->value != NULL) { 6076 xmlXPathFreeObject(vptr->value); 6077 vptr->value = NULL; 6078 vptr->computed = 0; 6079 } 6080 } 6081 vptr = vptr->next; 6082 } 6083 #if 0 6084 /* 6085 * code disabled by wmb; awaiting kb's review 6086 * problem is that global variable(s) may contain xpath objects 6087 * from doc associated with RVT, so can't be freed at this point. 6088 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so 6089 * I assume this shouldn't be required at this point. 6090 */ 6091 /* 6092 * Free all remaining tree fragments. 6093 */ 6094 xsltFreeRVTs(ctxt); 6095 #endif 6096 /* 6097 * Do some post processing work depending on the generated output 6098 */ 6099 root = xmlDocGetRootElement(res); 6100 if (root != NULL) { 6101 const xmlChar *doctype = NULL; 6102 6103 if ((root->ns != NULL) && (root->ns->prefix != NULL)) 6104 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); 6105 if (doctype == NULL) 6106 doctype = root->name; 6107 6108 /* 6109 * Apply the default selection of the method 6110 */ 6111 if ((method == NULL) && 6112 (root->ns == NULL) && 6113 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { 6114 xmlNodePtr tmp; 6115 6116 tmp = res->children; 6117 while ((tmp != NULL) && (tmp != root)) { 6118 if (tmp->type == XML_ELEMENT_NODE) 6119 break; 6120 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) 6121 break; 6122 tmp = tmp->next; 6123 } 6124 if (tmp == root) { 6125 ctxt->type = XSLT_OUTPUT_HTML; 6126 /* 6127 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the 6128 * transformation on the doc, but functions like 6129 */ 6130 res->type = XML_HTML_DOCUMENT_NODE; 6131 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6132 res->intSubset = xmlCreateIntSubset(res, doctype, 6133 doctypePublic, 6134 doctypeSystem); 6135 #ifdef XSLT_GENERATE_HTML_DOCTYPE 6136 } else if (version != NULL) { 6137 xsltGetHTMLIDs(version, &doctypePublic, 6138 &doctypeSystem); 6139 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) 6140 res->intSubset = 6141 xmlCreateIntSubset(res, doctype, 6142 doctypePublic, 6143 doctypeSystem); 6144 #endif 6145 } 6146 } 6147 6148 } 6149 if (ctxt->type == XSLT_OUTPUT_XML) { 6150 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) 6151 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) 6152 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { 6153 xmlNodePtr last; 6154 /* Need a small "hack" here to assure DTD comes before 6155 possible comment nodes */ 6156 node = res->children; 6157 last = res->last; 6158 res->children = NULL; 6159 res->last = NULL; 6160 res->intSubset = xmlCreateIntSubset(res, doctype, 6161 doctypePublic, 6162 doctypeSystem); 6163 if (res->children != NULL) { 6164 res->children->next = node; 6165 node->prev = res->children; 6166 res->last = last; 6167 } else { 6168 res->children = node; 6169 res->last = last; 6170 } 6171 } 6172 } 6173 } 6174 xmlXPathFreeNodeSet(ctxt->nodeList); 6175 if (profile != NULL) { 6176 xsltSaveProfiling(ctxt, profile); 6177 } 6178 6179 /* 6180 * Be pedantic. 6181 */ 6182 if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) { 6183 xmlFreeDoc(res); 6184 res = NULL; 6185 } 6186 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { 6187 int ret; 6188 6189 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); 6190 if (ret == 0) { 6191 xsltTransformError(ctxt, NULL, NULL, 6192 "xsltApplyStylesheet: forbidden to save to %s\n", 6193 output); 6194 } else if (ret < 0) { 6195 xsltTransformError(ctxt, NULL, NULL, 6196 "xsltApplyStylesheet: saving to %s may not be possible\n", 6197 output); 6198 } 6199 } 6200 6201 #ifdef XSLT_DEBUG_PROFILE_CACHE 6202 printf("# Cache:\n"); 6203 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6204 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6205 #endif 6206 6207 if ((ctxt != NULL) && (userCtxt == NULL)) 6208 xsltFreeTransformContext(ctxt); 6209 6210 return (res); 6211 6212 error: 6213 if (res != NULL) 6214 xmlFreeDoc(res); 6215 6216 #ifdef XSLT_DEBUG_PROFILE_CACHE 6217 printf("# Cache:\n"); 6218 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); 6219 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); 6220 #endif 6221 6222 if ((ctxt != NULL) && (userCtxt == NULL)) 6223 xsltFreeTransformContext(ctxt); 6224 return (NULL); 6225 } 6226 6227 /** 6228 * xsltApplyStylesheet: 6229 * @style: a parsed XSLT stylesheet 6230 * @doc: a parsed XML document 6231 * @params: a NULL terminated arry of parameters names/values tuples 6232 * 6233 * Apply the stylesheet to the document 6234 * NOTE: This may lead to a non-wellformed output XML wise ! 6235 * 6236 * Returns the result document or NULL in case of error 6237 */ 6238 xmlDocPtr 6239 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6240 const char **params) 6241 { 6242 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); 6243 } 6244 6245 /** 6246 * xsltProfileStylesheet: 6247 * @style: a parsed XSLT stylesheet 6248 * @doc: a parsed XML document 6249 * @params: a NULL terminated arry of parameters names/values tuples 6250 * @output: a FILE * for the profiling output 6251 * 6252 * Apply the stylesheet to the document and dump the profiling to 6253 * the given output. 6254 * 6255 * Returns the result document or NULL in case of error 6256 */ 6257 xmlDocPtr 6258 xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6259 const char **params, FILE * output) 6260 { 6261 xmlDocPtr res; 6262 6263 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); 6264 return (res); 6265 } 6266 6267 /** 6268 * xsltApplyStylesheetUser: 6269 * @style: a parsed XSLT stylesheet 6270 * @doc: a parsed XML document 6271 * @params: a NULL terminated array of parameters names/values tuples 6272 * @output: the targetted output 6273 * @profile: profile FILE * output or NULL 6274 * @userCtxt: user provided transform context 6275 * 6276 * Apply the stylesheet to the document and allow the user to provide 6277 * its own transformation context. 6278 * 6279 * Returns the result document or NULL in case of error 6280 */ 6281 xmlDocPtr 6282 xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6283 const char **params, const char *output, 6284 FILE * profile, xsltTransformContextPtr userCtxt) 6285 { 6286 xmlDocPtr res; 6287 6288 res = xsltApplyStylesheetInternal(style, doc, params, output, 6289 profile, userCtxt); 6290 return (res); 6291 } 6292 6293 /** 6294 * xsltRunStylesheetUser: 6295 * @style: a parsed XSLT stylesheet 6296 * @doc: a parsed XML document 6297 * @params: a NULL terminated array of parameters names/values tuples 6298 * @output: the URL/filename ot the generated resource if available 6299 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6300 * @IObuf: an output buffer for progressive output (not implemented yet) 6301 * @profile: profile FILE * output or NULL 6302 * @userCtxt: user provided transform context 6303 * 6304 * Apply the stylesheet to the document and generate the output according 6305 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6306 * 6307 * NOTE: This may lead to a non-wellformed output XML wise ! 6308 * NOTE: This may also result in multiple files being generated 6309 * NOTE: using IObuf, the result encoding used will be the one used for 6310 * creating the output buffer, use the following macro to read it 6311 * from the stylesheet 6312 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6313 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6314 * since the interface uses only UTF8 6315 * 6316 * Returns the number of by written to the main resource or -1 in case of 6317 * error. 6318 */ 6319 int 6320 xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, 6321 const char **params, const char *output, 6322 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, 6323 FILE * profile, xsltTransformContextPtr userCtxt) 6324 { 6325 xmlDocPtr tmp; 6326 int ret; 6327 6328 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) 6329 return (-1); 6330 if ((SAX != NULL) && (IObuf != NULL)) 6331 return (-1); 6332 6333 /* unsupported yet */ 6334 if (SAX != NULL) { 6335 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ 6336 return (-1); 6337 } 6338 6339 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, 6340 userCtxt); 6341 if (tmp == NULL) { 6342 xsltTransformError(NULL, NULL, (xmlNodePtr) doc, 6343 "xsltRunStylesheet : run failed\n"); 6344 return (-1); 6345 } 6346 if (IObuf != NULL) { 6347 /* TODO: incomplete, IObuf output not progressive */ 6348 ret = xsltSaveResultTo(IObuf, tmp, style); 6349 } else { 6350 ret = xsltSaveResultToFilename(output, tmp, style, 0); 6351 } 6352 xmlFreeDoc(tmp); 6353 return (ret); 6354 } 6355 6356 /** 6357 * xsltRunStylesheet: 6358 * @style: a parsed XSLT stylesheet 6359 * @doc: a parsed XML document 6360 * @params: a NULL terminated array of parameters names/values tuples 6361 * @output: the URL/filename ot the generated resource if available 6362 * @SAX: a SAX handler for progressive callback output (not implemented yet) 6363 * @IObuf: an output buffer for progressive output (not implemented yet) 6364 * 6365 * Apply the stylesheet to the document and generate the output according 6366 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. 6367 * 6368 * NOTE: This may lead to a non-wellformed output XML wise ! 6369 * NOTE: This may also result in multiple files being generated 6370 * NOTE: using IObuf, the result encoding used will be the one used for 6371 * creating the output buffer, use the following macro to read it 6372 * from the stylesheet 6373 * XSLT_GET_IMPORT_PTR(encoding, style, encoding) 6374 * NOTE: using SAX, any encoding specified in the stylesheet will be lost 6375 * since the interface uses only UTF8 6376 * 6377 * Returns the number of bytes written to the main resource or -1 in case of 6378 * error. 6379 */ 6380 int 6381 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, 6382 const char **params, const char *output, 6383 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) 6384 { 6385 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, 6386 NULL, NULL)); 6387 } 6388 6389 static void 6390 xsltMessageWrapper(xsltTransformContextPtr ctxt, xmlNodePtr node, 6391 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) { 6392 xsltMessage(ctxt, node, inst); 6393 } 6394 6395 /** 6396 * xsltRegisterAllElement: 6397 * @ctxt: the XPath context 6398 * 6399 * Registers all default XSLT elements in this context 6400 */ 6401 void 6402 xsltRegisterAllElement(xsltTransformContextPtr ctxt) 6403 { 6404 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", 6405 XSLT_NAMESPACE, 6406 xsltApplyTemplates); 6407 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", 6408 XSLT_NAMESPACE, 6409 xsltApplyImports); 6410 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", 6411 XSLT_NAMESPACE, 6412 xsltCallTemplate); 6413 xsltRegisterExtElement(ctxt, (const xmlChar *) "element", 6414 XSLT_NAMESPACE, 6415 xsltElement); 6416 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", 6417 XSLT_NAMESPACE, 6418 xsltAttribute); 6419 xsltRegisterExtElement(ctxt, (const xmlChar *) "text", 6420 XSLT_NAMESPACE, 6421 xsltText); 6422 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", 6423 XSLT_NAMESPACE, 6424 xsltProcessingInstruction); 6425 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", 6426 XSLT_NAMESPACE, 6427 xsltComment); 6428 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", 6429 XSLT_NAMESPACE, 6430 xsltCopy); 6431 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", 6432 XSLT_NAMESPACE, 6433 xsltValueOf); 6434 xsltRegisterExtElement(ctxt, (const xmlChar *) "number", 6435 XSLT_NAMESPACE, 6436 xsltNumber); 6437 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", 6438 XSLT_NAMESPACE, 6439 xsltForEach); 6440 xsltRegisterExtElement(ctxt, (const xmlChar *) "if", 6441 XSLT_NAMESPACE, 6442 xsltIf); 6443 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", 6444 XSLT_NAMESPACE, 6445 xsltChoose); 6446 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", 6447 XSLT_NAMESPACE, 6448 xsltSort); 6449 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", 6450 XSLT_NAMESPACE, 6451 xsltCopyOf); 6452 xsltRegisterExtElement(ctxt, (const xmlChar *) "message", 6453 XSLT_NAMESPACE, 6454 xsltMessageWrapper); 6455 6456 /* 6457 * Those don't have callable entry points but are registered anyway 6458 */ 6459 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", 6460 XSLT_NAMESPACE, 6461 xsltDebug); 6462 xsltRegisterExtElement(ctxt, (const xmlChar *) "param", 6463 XSLT_NAMESPACE, 6464 xsltDebug); 6465 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", 6466 XSLT_NAMESPACE, 6467 xsltDebug); 6468 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", 6469 XSLT_NAMESPACE, 6470 xsltDebug); 6471 xsltRegisterExtElement(ctxt, (const xmlChar *) "when", 6472 XSLT_NAMESPACE, 6473 xsltDebug); 6474 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", 6475 XSLT_NAMESPACE, 6476 xsltDebug); 6477 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", 6478 XSLT_NAMESPACE, 6479 xsltDebug); 6480 6481 }