1 /* 2 * variables.c: Implementation of the variable storage and lookup 3 * 4 * Reference: 5 * http://www.w3.org/TR/1999/REC-xslt-19991116 6 * 7 * See Copyright for the status of this software. 8 * 9 * daniel@veillard.com 10 */ 11 12 #include "precomp.h" 13 14 #ifdef WITH_XSLT_DEBUG 15 #define WITH_XSLT_DEBUG_VARIABLE 16 #endif 17 18 #ifdef XSLT_REFACTORED 19 const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; 20 #endif 21 22 static const xmlChar *xsltComputingGlobalVarMarker = 23 (const xmlChar *) " var/param being computed"; 24 25 #define XSLT_VAR_GLOBAL (1<<0) 26 #define XSLT_VAR_IN_SELECT (1<<1) 27 #define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable) 28 29 /************************************************************************ 30 * * 31 * Result Value Tree (Result Tree Fragment) interfaces * 32 * * 33 ************************************************************************/ 34 /** 35 * xsltCreateRVT: 36 * @ctxt: an XSLT transformation context 37 * 38 * Creates a Result Value Tree 39 * (the XSLT 1.0 term for this is "Result Tree Fragment") 40 * 41 * Returns the result value tree or NULL in case of API or internal errors. 42 */ 43 xmlDocPtr 44 xsltCreateRVT(xsltTransformContextPtr ctxt) 45 { 46 xmlDocPtr container; 47 48 /* 49 * Question: Why is this function public? 50 * Answer: It is called by the EXSLT module. 51 */ 52 if (ctxt == NULL) 53 return(NULL); 54 55 /* 56 * Reuse a RTF from the cache if available. 57 */ 58 if (ctxt->cache->RVT) { 59 container = ctxt->cache->RVT; 60 ctxt->cache->RVT = (xmlDocPtr) container->next; 61 /* clear the internal pointers */ 62 container->next = NULL; 63 container->prev = NULL; 64 if (ctxt->cache->nbRVT > 0) 65 ctxt->cache->nbRVT--; 66 #ifdef XSLT_DEBUG_PROFILE_CACHE 67 ctxt->cache->dbgReusedRVTs++; 68 #endif 69 return(container); 70 } 71 72 container = xmlNewDoc(NULL); 73 if (container == NULL) 74 return(NULL); 75 container->dict = ctxt->dict; 76 xmlDictReference(container->dict); 77 XSLT_MARK_RES_TREE_FRAG(container); 78 container->doc = container; 79 container->parent = NULL; 80 return(container); 81 } 82 83 /** 84 * xsltRegisterTmpRVT: 85 * @ctxt: an XSLT transformation context 86 * @RVT: a result value tree (Result Tree Fragment) 87 * 88 * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment) 89 * in the garbage collector. 90 * The fragment will be freed at the exit of the currently 91 * instantiated xsl:template. 92 * Obsolete; this function might produce massive memory overhead, 93 * since the fragment is only freed when the current xsl:template 94 * exits. Use xsltRegisterLocalRVT() instead. 95 * 96 * Returns 0 in case of success and -1 in case of API or internal errors. 97 */ 98 int 99 xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) 100 { 101 if ((ctxt == NULL) || (RVT == NULL)) 102 return(-1); 103 104 RVT->prev = NULL; 105 RVT->psvi = XSLT_RVT_LOCAL; 106 107 /* 108 * We'll restrict the lifetime of user-created fragments 109 * insinde an xsl:variable and xsl:param to the lifetime of the 110 * var/param itself. 111 */ 112 if (ctxt->contextVariable != NULL) { 113 RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; 114 XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; 115 return(0); 116 } 117 118 RVT->next = (xmlNodePtr) ctxt->tmpRVT; 119 if (ctxt->tmpRVT != NULL) 120 ctxt->tmpRVT->prev = (xmlNodePtr) RVT; 121 ctxt->tmpRVT = RVT; 122 return(0); 123 } 124 125 /** 126 * xsltRegisterLocalRVT: 127 * @ctxt: an XSLT transformation context 128 * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr) 129 * 130 * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment) 131 * in the RVT garbage collector. 132 * The fragment will be freed when the instruction which created the 133 * fragment exits. 134 * 135 * Returns 0 in case of success and -1 in case of API or internal errors. 136 */ 137 int 138 xsltRegisterLocalRVT(xsltTransformContextPtr ctxt, 139 xmlDocPtr RVT) 140 { 141 if ((ctxt == NULL) || (RVT == NULL)) 142 return(-1); 143 144 RVT->prev = NULL; 145 RVT->psvi = XSLT_RVT_LOCAL; 146 147 /* 148 * When evaluating "select" expressions of xsl:variable 149 * and xsl:param, we need to bind newly created tree fragments 150 * to the variable itself; otherwise the fragment will be 151 * freed before we leave the scope of a var. 152 */ 153 if ((ctxt->contextVariable != NULL) && 154 (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT)) 155 { 156 RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; 157 XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; 158 return(0); 159 } 160 /* 161 * Store the fragment in the scope of the current instruction. 162 * If not reference by a returning instruction (like EXSLT's function), 163 * then this fragment will be freed, when the instruction exits. 164 */ 165 RVT->next = (xmlNodePtr) ctxt->localRVT; 166 if (ctxt->localRVT != NULL) 167 ctxt->localRVT->prev = (xmlNodePtr) RVT; 168 ctxt->localRVT = RVT; 169 return(0); 170 } 171 172 /** 173 * xsltExtensionInstructionResultFinalize: 174 * @ctxt: an XSLT transformation context 175 * 176 * Finalizes the data (e.g. result tree fragments) created 177 * within a value-returning process (e.g. EXSLT's function). 178 * Tree fragments marked as being returned by a function are 179 * set to normal state, which means that the fragment garbage 180 * collector will free them after the function-calling process exits. 181 * 182 * Returns 0 in case of success and -1 in case of API or internal errors. 183 * 184 * This function is unsupported in newer releases of libxslt. 185 */ 186 int 187 xsltExtensionInstructionResultFinalize( 188 xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED) 189 { 190 xmlGenericError(xmlGenericErrorContext, 191 "xsltExtensionInstructionResultFinalize is unsupported " 192 "in this release of libxslt.\n"); 193 return(-1); 194 } 195 196 /** 197 * xsltExtensionInstructionResultRegister: 198 * @ctxt: an XSLT transformation context 199 * @obj: an XPath object to be inspected for result tree fragments 200 * 201 * Marks the result of a value-returning extension instruction 202 * in order to avoid it being garbage collected before the 203 * extension instruction exits. 204 * Note that one still has to additionally register any newly created 205 * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT(). 206 * 207 * Returns 0 in case of success and -1 in case of error. 208 * 209 * It isn't necessary to call this function in newer releases of 210 * libxslt. 211 */ 212 int 213 xsltExtensionInstructionResultRegister( 214 xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, 215 xmlXPathObjectPtr obj ATTRIBUTE_UNUSED) 216 { 217 return(0); 218 } 219 220 /** 221 * xsltFlagRVTs: 222 * @ctxt: an XSLT transformation context 223 * @obj: an XPath object to be inspected for result tree fragments 224 * @val: the flag value 225 * 226 * Updates ownership information of RVTs in @obj according to @val. 227 * 228 * @val = XSLT_RVT_FUNC_RESULT for the result of an extension function, so its 229 * RVTs won't be destroyed after leaving the returning scope. 230 * @val = XSLT_RVT_LOCAL for the result of an extension function to reset 231 * the state of its RVTs after it was returned to a new scope. 232 * @val = XSLT_RVT_GLOBAL for parts of global variables. 233 * 234 * Returns 0 in case of success and -1 in case of error. 235 */ 236 int 237 xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) { 238 int i; 239 xmlNodePtr cur; 240 xmlDocPtr doc; 241 242 if ((ctxt == NULL) || (obj == NULL)) 243 return(-1); 244 245 /* 246 * OPTIMIZE TODO: If no local variables/params and no local tree 247 * fragments were created, then we don't need to analyse the XPath 248 * objects for tree fragments. 249 */ 250 251 if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE)) 252 return(0); 253 if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) 254 return(0); 255 256 for (i = 0; i < obj->nodesetval->nodeNr; i++) { 257 cur = obj->nodesetval->nodeTab[i]; 258 if (cur->type == XML_NAMESPACE_DECL) { 259 /* 260 * The XPath module sets the owner element of a ns-node on 261 * the ns->next field. 262 */ 263 if ((((xmlNsPtr) cur)->next != NULL) && 264 (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE)) 265 { 266 cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; 267 doc = cur->doc; 268 } else { 269 xsltTransformError(ctxt, NULL, ctxt->inst, 270 "Internal error in xsltFlagRVTs(): " 271 "Cannot retrieve the doc of a namespace node.\n"); 272 return(-1); 273 } 274 } else { 275 doc = cur->doc; 276 } 277 if (doc == NULL) { 278 xsltTransformError(ctxt, NULL, ctxt->inst, 279 "Internal error in xsltFlagRVTs(): " 280 "Cannot retrieve the doc of a node.\n"); 281 return(-1); 282 } 283 if (doc->name && (doc->name[0] == ' ') && 284 doc->psvi != XSLT_RVT_GLOBAL) { 285 /* 286 * This is a result tree fragment. 287 * We store ownership information in the @psvi field. 288 * TODO: How do we know if this is a doc acquired via the 289 * document() function? 290 */ 291 #ifdef WITH_XSLT_DEBUG_VARIABLE 292 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 293 "Flagging RVT %p: %p -> %p\n", doc, doc->psvi, val)); 294 #endif 295 296 if (val == XSLT_RVT_LOCAL) { 297 if (doc->psvi == XSLT_RVT_FUNC_RESULT) 298 doc->psvi = XSLT_RVT_LOCAL; 299 } else if (val == XSLT_RVT_GLOBAL) { 300 if (doc->psvi != XSLT_RVT_LOCAL) { 301 xmlGenericError(xmlGenericErrorContext, 302 "xsltFlagRVTs: Invalid transition %p => GLOBAL\n", 303 doc->psvi); 304 doc->psvi = XSLT_RVT_GLOBAL; 305 return(-1); 306 } 307 308 /* Will be registered as persistant in xsltReleaseLocalRVTs. */ 309 doc->psvi = XSLT_RVT_GLOBAL; 310 } else if (val == XSLT_RVT_FUNC_RESULT) { 311 doc->psvi = val; 312 } 313 } 314 } 315 316 return(0); 317 } 318 319 /** 320 * xsltReleaseRVT: 321 * @ctxt: an XSLT transformation context 322 * @RVT: a result value tree (Result Tree Fragment) 323 * 324 * Either frees the RVT (which is an xmlDoc) or stores 325 * it in the context's cache for later reuse. 326 */ 327 void 328 xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) 329 { 330 if (RVT == NULL) 331 return; 332 333 if (ctxt && (ctxt->cache->nbRVT < 40)) { 334 /* 335 * Store the Result Tree Fragment. 336 * Free the document info. 337 */ 338 if (RVT->_private != NULL) { 339 xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); 340 xmlFree(RVT->_private); 341 RVT->_private = NULL; 342 } 343 /* 344 * Clear the document tree. 345 */ 346 if (RVT->children != NULL) { 347 xmlFreeNodeList(RVT->children); 348 RVT->children = NULL; 349 RVT->last = NULL; 350 } 351 if (RVT->ids != NULL) { 352 xmlFreeIDTable((xmlIDTablePtr) RVT->ids); 353 RVT->ids = NULL; 354 } 355 356 /* 357 * Reset the ownership information. 358 */ 359 RVT->psvi = NULL; 360 361 RVT->next = (xmlNodePtr) ctxt->cache->RVT; 362 ctxt->cache->RVT = RVT; 363 364 ctxt->cache->nbRVT++; 365 366 #ifdef XSLT_DEBUG_PROFILE_CACHE 367 ctxt->cache->dbgCachedRVTs++; 368 #endif 369 return; 370 } 371 /* 372 * Free it. 373 */ 374 if (RVT->_private != NULL) { 375 xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); 376 xmlFree(RVT->_private); 377 } 378 xmlFreeDoc(RVT); 379 } 380 381 /** 382 * xsltRegisterPersistRVT: 383 * @ctxt: an XSLT transformation context 384 * @RVT: a result value tree (Result Tree Fragment) 385 * 386 * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) 387 * in the fragment garbage collector. 388 * The fragment will be freed when the transformation context is 389 * freed. 390 * 391 * Returns 0 in case of success and -1 in case of error. 392 */ 393 int 394 xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) 395 { 396 if ((ctxt == NULL) || (RVT == NULL)) return(-1); 397 398 RVT->psvi = XSLT_RVT_GLOBAL; 399 RVT->prev = NULL; 400 RVT->next = (xmlNodePtr) ctxt->persistRVT; 401 if (ctxt->persistRVT != NULL) 402 ctxt->persistRVT->prev = (xmlNodePtr) RVT; 403 ctxt->persistRVT = RVT; 404 return(0); 405 } 406 407 /** 408 * xsltFreeRVTs: 409 * @ctxt: an XSLT transformation context 410 * 411 * Frees all registered result value trees (Result Tree Fragments) 412 * of the transformation. Internal function; should not be called 413 * by user-code. 414 */ 415 void 416 xsltFreeRVTs(xsltTransformContextPtr ctxt) 417 { 418 xmlDocPtr cur, next; 419 420 if (ctxt == NULL) 421 return; 422 /* 423 * Local fragments. 424 */ 425 cur = ctxt->localRVT; 426 while (cur != NULL) { 427 next = (xmlDocPtr) cur->next; 428 if (cur->_private != NULL) { 429 xsltFreeDocumentKeys(cur->_private); 430 xmlFree(cur->_private); 431 } 432 xmlFreeDoc(cur); 433 cur = next; 434 } 435 ctxt->localRVT = NULL; 436 /* 437 * User-created per-template fragments. 438 */ 439 cur = ctxt->tmpRVT; 440 while (cur != NULL) { 441 next = (xmlDocPtr) cur->next; 442 if (cur->_private != NULL) { 443 xsltFreeDocumentKeys(cur->_private); 444 xmlFree(cur->_private); 445 } 446 xmlFreeDoc(cur); 447 cur = next; 448 } 449 ctxt->tmpRVT = NULL; 450 /* 451 * Global fragments. 452 */ 453 cur = ctxt->persistRVT; 454 while (cur != NULL) { 455 next = (xmlDocPtr) cur->next; 456 if (cur->_private != NULL) { 457 xsltFreeDocumentKeys(cur->_private); 458 xmlFree(cur->_private); 459 } 460 xmlFreeDoc(cur); 461 cur = next; 462 } 463 ctxt->persistRVT = NULL; 464 } 465 466 /************************************************************************ 467 * * 468 * Module interfaces * 469 * * 470 ************************************************************************/ 471 472 /** 473 * xsltNewStackElem: 474 * 475 * Create a new XSLT ParserContext 476 * 477 * Returns the newly allocated xsltParserStackElem or NULL in case of error 478 */ 479 static xsltStackElemPtr 480 xsltNewStackElem(xsltTransformContextPtr ctxt) 481 { 482 xsltStackElemPtr ret; 483 /* 484 * Reuse a stack item from the cache if available. 485 */ 486 if (ctxt && ctxt->cache->stackItems) { 487 ret = ctxt->cache->stackItems; 488 ctxt->cache->stackItems = ret->next; 489 ret->next = NULL; 490 ctxt->cache->nbStackItems--; 491 #ifdef XSLT_DEBUG_PROFILE_CACHE 492 ctxt->cache->dbgReusedVars++; 493 #endif 494 return(ret); 495 } 496 ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); 497 if (ret == NULL) { 498 xsltTransformError(NULL, NULL, NULL, 499 "xsltNewStackElem : malloc failed\n"); 500 return(NULL); 501 } 502 memset(ret, 0, sizeof(xsltStackElem)); 503 ret->context = ctxt; 504 return(ret); 505 } 506 507 /** 508 * xsltCopyStackElem: 509 * @elem: an XSLT stack element 510 * 511 * Makes a copy of the stack element 512 * 513 * Returns the copy of NULL 514 */ 515 static xsltStackElemPtr 516 xsltCopyStackElem(xsltStackElemPtr elem) { 517 xsltStackElemPtr cur; 518 519 cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); 520 if (cur == NULL) { 521 xsltTransformError(NULL, NULL, NULL, 522 "xsltCopyStackElem : malloc failed\n"); 523 return(NULL); 524 } 525 memset(cur, 0, sizeof(xsltStackElem)); 526 cur->context = elem->context; 527 cur->name = elem->name; 528 cur->nameURI = elem->nameURI; 529 cur->select = elem->select; 530 cur->tree = elem->tree; 531 cur->comp = elem->comp; 532 return(cur); 533 } 534 535 /** 536 * xsltFreeStackElem: 537 * @elem: an XSLT stack element 538 * 539 * Free up the memory allocated by @elem 540 */ 541 static void 542 xsltFreeStackElem(xsltStackElemPtr elem) { 543 if (elem == NULL) 544 return; 545 if (elem->value != NULL) 546 xmlXPathFreeObject(elem->value); 547 /* 548 * Release the list of temporary Result Tree Fragments. 549 */ 550 if (elem->context) { 551 xmlDocPtr cur; 552 553 while (elem->fragment != NULL) { 554 cur = elem->fragment; 555 elem->fragment = (xmlDocPtr) cur->next; 556 557 if (cur->psvi == XSLT_RVT_LOCAL) { 558 xsltReleaseRVT(elem->context, cur); 559 } else if (cur->psvi == XSLT_RVT_FUNC_RESULT) { 560 xsltRegisterLocalRVT(elem->context, cur); 561 cur->psvi = XSLT_RVT_FUNC_RESULT; 562 } else { 563 xmlGenericError(xmlGenericErrorContext, 564 "xsltFreeStackElem: Unexpected RVT flag %p\n", 565 cur->psvi); 566 } 567 } 568 } 569 /* 570 * Cache or free the variable structure. 571 */ 572 if (elem->context && (elem->context->cache->nbStackItems < 50)) { 573 /* 574 * Store the item in the cache. 575 */ 576 xsltTransformContextPtr ctxt = elem->context; 577 memset(elem, 0, sizeof(xsltStackElem)); 578 elem->context = ctxt; 579 elem->next = ctxt->cache->stackItems; 580 ctxt->cache->stackItems = elem; 581 ctxt->cache->nbStackItems++; 582 #ifdef XSLT_DEBUG_PROFILE_CACHE 583 ctxt->cache->dbgCachedVars++; 584 #endif 585 return; 586 } 587 xmlFree(elem); 588 } 589 590 static void 591 xsltFreeStackElemEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { 592 xsltFreeStackElem((xsltStackElemPtr) payload); 593 } 594 595 596 /** 597 * xsltFreeStackElemList: 598 * @elem: an XSLT stack element 599 * 600 * Free up the memory allocated by @elem 601 */ 602 void 603 xsltFreeStackElemList(xsltStackElemPtr elem) { 604 xsltStackElemPtr next; 605 606 while (elem != NULL) { 607 next = elem->next; 608 xsltFreeStackElem(elem); 609 elem = next; 610 } 611 } 612 613 /** 614 * xsltStackLookup: 615 * @ctxt: an XSLT transformation context 616 * @name: the local part of the name 617 * @nameURI: the URI part of the name 618 * 619 * Locate an element in the stack based on its name. 620 */ 621 #if 0 /* TODO: Those seem to have been used for debugging. */ 622 static int stack_addr = 0; 623 static int stack_cmp = 0; 624 #endif 625 626 static xsltStackElemPtr 627 xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, 628 const xmlChar *nameURI) { 629 int i; 630 xsltStackElemPtr cur; 631 632 if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) 633 return(NULL); 634 635 /* 636 * Do the lookup from the top of the stack, but 637 * don't use params being computed in a call-param 638 * First lookup expects the variable name and URI to 639 * come from the disctionnary and hence pointer comparison. 640 */ 641 for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { 642 cur = ctxt->varsTab[i-1]; 643 while (cur != NULL) { 644 if ((cur->name == name) && (cur->nameURI == nameURI)) { 645 #if 0 646 stack_addr++; 647 #endif 648 return(cur); 649 } 650 cur = cur->next; 651 } 652 } 653 654 /* 655 * Redo the lookup with interned string compares 656 * to avoid string compares. 657 */ 658 name = xmlDictLookup(ctxt->dict, name, -1); 659 if (nameURI != NULL) 660 nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); 661 662 for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { 663 cur = ctxt->varsTab[i-1]; 664 while (cur != NULL) { 665 if ((cur->name == name) && (cur->nameURI == nameURI)) { 666 #if 0 667 stack_cmp++; 668 #endif 669 return(cur); 670 } 671 cur = cur->next; 672 } 673 } 674 675 return(NULL); 676 } 677 678 #ifdef XSLT_REFACTORED 679 #else 680 681 /** 682 * xsltCheckStackElem: 683 * @ctxt: xn XSLT transformation context 684 * @name: the variable name 685 * @nameURI: the variable namespace URI 686 * 687 * Checks whether a variable or param is already defined. 688 * 689 * URGENT TODO: Checks for redefinition of vars/params should be 690 * done only at compilation time. 691 * 692 * Returns 1 if variable is present, 2 if param is present, 3 if this 693 * is an inherited param, 0 if not found, -1 in case of failure. 694 */ 695 static int 696 xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, 697 const xmlChar *nameURI) { 698 xsltStackElemPtr cur; 699 700 if ((ctxt == NULL) || (name == NULL)) 701 return(-1); 702 703 cur = xsltStackLookup(ctxt, name, nameURI); 704 if (cur == NULL) 705 return(0); 706 if (cur->comp != NULL) { 707 if (cur->comp->type == XSLT_FUNC_WITHPARAM) 708 return(3); 709 else if (cur->comp->type == XSLT_FUNC_PARAM) 710 return(2); 711 } 712 713 return(1); 714 } 715 716 #endif /* XSLT_REFACTORED */ 717 718 /** 719 * xsltAddStackElem: 720 * @ctxt: xn XSLT transformation context 721 * @elem: a stack element 722 * 723 * Push an element (or list) onto the stack. 724 * In case of a list, each member will be pushed into 725 * a seperate slot; i.e. there's always 1 stack entry for 726 * 1 stack element. 727 * 728 * Returns 0 in case of success, -1 in case of failure. 729 */ 730 static int 731 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) 732 { 733 if ((ctxt == NULL) || (elem == NULL)) 734 return(-1); 735 736 do { 737 if (ctxt->varsMax == 0) { 738 ctxt->varsMax = 10; 739 ctxt->varsTab = 740 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * 741 sizeof(ctxt->varsTab[0])); 742 if (ctxt->varsTab == NULL) { 743 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); 744 return (-1); 745 } 746 } 747 if (ctxt->varsNr >= ctxt->varsMax) { 748 ctxt->varsMax *= 2; 749 ctxt->varsTab = 750 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, 751 ctxt->varsMax * 752 sizeof(ctxt->varsTab[0])); 753 if (ctxt->varsTab == NULL) { 754 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); 755 return (-1); 756 } 757 } 758 ctxt->varsTab[ctxt->varsNr++] = elem; 759 ctxt->vars = elem; 760 761 elem = elem->next; 762 } while (elem != NULL); 763 764 return(0); 765 } 766 767 /** 768 * xsltAddStackElemList: 769 * @ctxt: xn XSLT transformation context 770 * @elems: a stack element list 771 * 772 * Push an element list onto the stack. 773 * 774 * Returns 0 in case of success, -1 in case of failure. 775 */ 776 int 777 xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) 778 { 779 return(xsltAddStackElem(ctxt, elems)); 780 } 781 782 /************************************************************************ 783 * * 784 * Module interfaces * 785 * * 786 ************************************************************************/ 787 788 /** 789 * xsltEvalVariable: 790 * @ctxt: the XSLT transformation context 791 * @variable: the variable or parameter item 792 * @comp: the compiled XSLT instruction 793 * 794 * Evaluate a variable value. 795 * 796 * Returns the XPath Object value or NULL in case of error 797 */ 798 static xmlXPathObjectPtr 799 xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, 800 xsltStylePreCompPtr castedComp) 801 { 802 #ifdef XSLT_REFACTORED 803 xsltStyleItemVariablePtr comp = 804 (xsltStyleItemVariablePtr) castedComp; 805 #else 806 xsltStylePreCompPtr comp = castedComp; 807 #endif 808 xmlXPathObjectPtr result = NULL; 809 xmlNodePtr oldInst; 810 811 if ((ctxt == NULL) || (variable == NULL)) 812 return(NULL); 813 814 /* 815 * A variable or parameter are evaluated on demand; thus the 816 * context (of XSLT and XPath) need to be temporarily adjusted and 817 * restored on exit. 818 */ 819 oldInst = ctxt->inst; 820 821 #ifdef WITH_XSLT_DEBUG_VARIABLE 822 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 823 "Evaluating variable '%s'\n", variable->name)); 824 #endif 825 if (variable->select != NULL) { 826 xmlXPathCompExprPtr xpExpr = NULL; 827 xmlDocPtr oldXPDoc; 828 xmlNodePtr oldXPContextNode; 829 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 830 xmlNsPtr *oldXPNamespaces; 831 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 832 xsltStackElemPtr oldVar = ctxt->contextVariable; 833 834 if ((comp != NULL) && (comp->comp != NULL)) { 835 xpExpr = comp->comp; 836 } else { 837 xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, variable->select); 838 } 839 if (xpExpr == NULL) 840 return(NULL); 841 /* 842 * Save context states. 843 */ 844 oldXPDoc = xpctxt->doc; 845 oldXPContextNode = xpctxt->node; 846 oldXPProximityPosition = xpctxt->proximityPosition; 847 oldXPContextSize = xpctxt->contextSize; 848 oldXPNamespaces = xpctxt->namespaces; 849 oldXPNsNr = xpctxt->nsNr; 850 851 xpctxt->node = ctxt->node; 852 /* 853 * OPTIMIZE TODO: Lame try to set the context doc. 854 * Get rid of this somehow in xpath.c. 855 */ 856 if ((ctxt->node->type != XML_NAMESPACE_DECL) && 857 ctxt->node->doc) 858 xpctxt->doc = ctxt->node->doc; 859 /* 860 * BUG TODO: The proximity position and the context size will 861 * potentially be wrong. 862 * Example: 863 * <xsl:template select="foo"> 864 * <xsl:variable name="pos" select="position()"/> 865 * <xsl:for-each select="bar"> 866 * <xsl:value-of select="$pos"/> 867 * </xsl:for-each> 868 * </xsl:template> 869 * Here the proximity position and context size are changed 870 * to the context of <xsl:for-each select="bar">, but 871 * the variable needs to be evaluated in the context of 872 * <xsl:template select="foo">. 873 */ 874 if (comp != NULL) { 875 876 #ifdef XSLT_REFACTORED 877 if (comp->inScopeNs != NULL) { 878 xpctxt->namespaces = comp->inScopeNs->list; 879 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 880 } else { 881 xpctxt->namespaces = NULL; 882 xpctxt->nsNr = 0; 883 } 884 #else 885 xpctxt->namespaces = comp->nsList; 886 xpctxt->nsNr = comp->nsNr; 887 #endif 888 } else { 889 xpctxt->namespaces = NULL; 890 xpctxt->nsNr = 0; 891 } 892 893 /* 894 * We need to mark that we are "selecting" a var's value; 895 * if any tree fragments are created inside the expression, 896 * then those need to be stored inside the variable; otherwise 897 * we'll eventually free still referenced fragments, before 898 * we leave the scope of the variable. 899 */ 900 ctxt->contextVariable = variable; 901 variable->flags |= XSLT_VAR_IN_SELECT; 902 903 result = xmlXPathCompiledEval(xpExpr, xpctxt); 904 905 variable->flags ^= XSLT_VAR_IN_SELECT; 906 /* 907 * Restore Context states. 908 */ 909 ctxt->contextVariable = oldVar; 910 911 xpctxt->doc = oldXPDoc; 912 xpctxt->node = oldXPContextNode; 913 xpctxt->contextSize = oldXPContextSize; 914 xpctxt->proximityPosition = oldXPProximityPosition; 915 xpctxt->namespaces = oldXPNamespaces; 916 xpctxt->nsNr = oldXPNsNr; 917 918 if ((comp == NULL) || (comp->comp == NULL)) 919 xmlXPathFreeCompExpr(xpExpr); 920 if (result == NULL) { 921 xsltTransformError(ctxt, NULL, 922 (comp != NULL) ? comp->inst : NULL, 923 "Failed to evaluate the expression of variable '%s'.\n", 924 variable->name); 925 ctxt->state = XSLT_STATE_STOPPED; 926 927 #ifdef WITH_XSLT_DEBUG_VARIABLE 928 #ifdef LIBXML_DEBUG_ENABLED 929 } else { 930 if ((xsltGenericDebugContext == stdout) || 931 (xsltGenericDebugContext == stderr)) 932 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, 933 result, 0); 934 #endif 935 #endif 936 } 937 } else { 938 if (variable->tree == NULL) { 939 result = xmlXPathNewCString(""); 940 } else { 941 if (variable->tree) { 942 xmlDocPtr container; 943 xmlNodePtr oldInsert; 944 xmlDocPtr oldOutput; 945 const xmlChar *oldLastText; 946 int oldLastTextSize, oldLastTextUse; 947 xsltStackElemPtr oldVar = ctxt->contextVariable; 948 949 /* 950 * Generate a result tree fragment. 951 */ 952 container = xsltCreateRVT(ctxt); 953 if (container == NULL) 954 goto error; 955 /* 956 * NOTE: Local Result Tree Fragments of params/variables 957 * are not registered globally anymore; the life-time 958 * is not directly dependant of the param/variable itself. 959 * 960 * OLD: xsltRegisterTmpRVT(ctxt, container); 961 */ 962 /* 963 * Attach the Result Tree Fragment to the variable; 964 * when the variable is freed, it will also free 965 * the Result Tree Fragment. 966 */ 967 variable->fragment = container; 968 container->psvi = XSLT_RVT_LOCAL; 969 970 oldOutput = ctxt->output; 971 oldInsert = ctxt->insert; 972 oldLastText = ctxt->lasttext; 973 oldLastTextSize = ctxt->lasttsize; 974 oldLastTextUse = ctxt->lasttuse; 975 976 ctxt->output = container; 977 ctxt->insert = (xmlNodePtr) container; 978 ctxt->contextVariable = variable; 979 /* 980 * Process the sequence constructor (variable->tree). 981 * The resulting tree will be held by @container. 982 */ 983 xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree, 984 NULL, NULL); 985 986 ctxt->contextVariable = oldVar; 987 ctxt->insert = oldInsert; 988 ctxt->output = oldOutput; 989 ctxt->lasttext = oldLastText; 990 ctxt->lasttsize = oldLastTextSize; 991 ctxt->lasttuse = oldLastTextUse; 992 993 result = xmlXPathNewValueTree((xmlNodePtr) container); 994 } 995 if (result == NULL) { 996 result = xmlXPathNewCString(""); 997 } else { 998 /* 999 * Freeing is not handled there anymore. 1000 * QUESTION TODO: What does the above comment mean? 1001 */ 1002 result->boolval = 0; 1003 } 1004 #ifdef WITH_XSLT_DEBUG_VARIABLE 1005 #ifdef LIBXML_DEBUG_ENABLED 1006 1007 if ((xsltGenericDebugContext == stdout) || 1008 (xsltGenericDebugContext == stderr)) 1009 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, 1010 result, 0); 1011 #endif 1012 #endif 1013 } 1014 } 1015 1016 error: 1017 ctxt->inst = oldInst; 1018 return(result); 1019 } 1020 1021 /** 1022 * xsltEvalGlobalVariable: 1023 * @elem: the variable or parameter 1024 * @ctxt: the XSLT transformation context 1025 * 1026 * Evaluates a the value of a global xsl:variable or 1027 * xsl:param declaration. 1028 * 1029 * Returns the XPath Object value or NULL in case of error 1030 */ 1031 static xmlXPathObjectPtr 1032 xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) 1033 { 1034 xmlXPathObjectPtr result = NULL; 1035 xmlNodePtr oldInst; 1036 const xmlChar* oldVarName; 1037 1038 #ifdef XSLT_REFACTORED 1039 xsltStyleBasicItemVariablePtr comp; 1040 #else 1041 xsltStylePreCompPtr comp; 1042 #endif 1043 1044 if ((ctxt == NULL) || (elem == NULL)) 1045 return(NULL); 1046 if (elem->computed) 1047 return(elem->value); 1048 1049 1050 #ifdef WITH_XSLT_DEBUG_VARIABLE 1051 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1052 "Evaluating global variable %s\n", elem->name)); 1053 #endif 1054 1055 #ifdef WITH_DEBUGGER 1056 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && 1057 elem->comp && elem->comp->inst) 1058 xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); 1059 #endif 1060 1061 oldInst = ctxt->inst; 1062 #ifdef XSLT_REFACTORED 1063 comp = (xsltStyleBasicItemVariablePtr) elem->comp; 1064 #else 1065 comp = elem->comp; 1066 #endif 1067 oldVarName = elem->name; 1068 elem->name = xsltComputingGlobalVarMarker; 1069 /* 1070 * OPTIMIZE TODO: We should consider instantiating global vars/params 1071 * on-demand. The vars/params don't need to be evaluated if never 1072 * called; and in the case of global params, if values for such params 1073 * are provided by the user. 1074 */ 1075 if (elem->select != NULL) { 1076 xmlXPathCompExprPtr xpExpr = NULL; 1077 xmlDocPtr oldXPDoc; 1078 xmlNodePtr oldXPContextNode; 1079 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 1080 xmlNsPtr *oldXPNamespaces; 1081 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 1082 1083 if ((comp != NULL) && (comp->comp != NULL)) { 1084 xpExpr = comp->comp; 1085 } else { 1086 xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, elem->select); 1087 } 1088 if (xpExpr == NULL) 1089 goto error; 1090 1091 1092 if (comp != NULL) 1093 ctxt->inst = comp->inst; 1094 else 1095 ctxt->inst = NULL; 1096 /* 1097 * SPEC XSLT 1.0: 1098 * "At top-level, the expression or template specifying the 1099 * variable value is evaluated with the same context as that used 1100 * to process the root node of the source document: the current 1101 * node is the root node of the source document and the current 1102 * node list is a list containing just the root node of the source 1103 * document." 1104 */ 1105 /* 1106 * Save context states. 1107 */ 1108 oldXPDoc = xpctxt->doc; 1109 oldXPContextNode = xpctxt->node; 1110 oldXPProximityPosition = xpctxt->proximityPosition; 1111 oldXPContextSize = xpctxt->contextSize; 1112 oldXPNamespaces = xpctxt->namespaces; 1113 oldXPNsNr = xpctxt->nsNr; 1114 1115 xpctxt->node = ctxt->initialContextNode; 1116 xpctxt->doc = ctxt->initialContextDoc; 1117 xpctxt->contextSize = 1; 1118 xpctxt->proximityPosition = 1; 1119 1120 if (comp != NULL) { 1121 1122 #ifdef XSLT_REFACTORED 1123 if (comp->inScopeNs != NULL) { 1124 xpctxt->namespaces = comp->inScopeNs->list; 1125 xpctxt->nsNr = comp->inScopeNs->xpathNumber; 1126 } else { 1127 xpctxt->namespaces = NULL; 1128 xpctxt->nsNr = 0; 1129 } 1130 #else 1131 xpctxt->namespaces = comp->nsList; 1132 xpctxt->nsNr = comp->nsNr; 1133 #endif 1134 } else { 1135 xpctxt->namespaces = NULL; 1136 xpctxt->nsNr = 0; 1137 } 1138 1139 result = xmlXPathCompiledEval(xpExpr, xpctxt); 1140 1141 /* 1142 * Restore Context states. 1143 */ 1144 xpctxt->doc = oldXPDoc; 1145 xpctxt->node = oldXPContextNode; 1146 xpctxt->contextSize = oldXPContextSize; 1147 xpctxt->proximityPosition = oldXPProximityPosition; 1148 xpctxt->namespaces = oldXPNamespaces; 1149 xpctxt->nsNr = oldXPNsNr; 1150 1151 if ((comp == NULL) || (comp->comp == NULL)) 1152 xmlXPathFreeCompExpr(xpExpr); 1153 if (result == NULL) { 1154 if (comp == NULL) 1155 xsltTransformError(ctxt, NULL, NULL, 1156 "Evaluating global variable %s failed\n", elem->name); 1157 else 1158 xsltTransformError(ctxt, NULL, comp->inst, 1159 "Evaluating global variable %s failed\n", elem->name); 1160 ctxt->state = XSLT_STATE_STOPPED; 1161 goto error; 1162 } 1163 1164 /* 1165 * Mark all RVTs that are referenced from result as part 1166 * of this variable so they won't be freed too early. 1167 */ 1168 xsltFlagRVTs(ctxt, result, XSLT_RVT_GLOBAL); 1169 1170 #ifdef WITH_XSLT_DEBUG_VARIABLE 1171 #ifdef LIBXML_DEBUG_ENABLED 1172 if ((xsltGenericDebugContext == stdout) || 1173 (xsltGenericDebugContext == stderr)) 1174 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, 1175 result, 0); 1176 #endif 1177 #endif 1178 } else { 1179 if (elem->tree == NULL) { 1180 result = xmlXPathNewCString(""); 1181 } else { 1182 xmlDocPtr container; 1183 xmlNodePtr oldInsert; 1184 xmlDocPtr oldOutput, oldXPDoc; 1185 /* 1186 * Generate a result tree fragment. 1187 */ 1188 container = xsltCreateRVT(ctxt); 1189 if (container == NULL) 1190 goto error; 1191 /* 1192 * Let the lifetime of the tree fragment be handled by 1193 * the Libxslt's garbage collector. 1194 */ 1195 xsltRegisterPersistRVT(ctxt, container); 1196 1197 oldOutput = ctxt->output; 1198 oldInsert = ctxt->insert; 1199 1200 oldXPDoc = ctxt->xpathCtxt->doc; 1201 1202 ctxt->output = container; 1203 ctxt->insert = (xmlNodePtr) container; 1204 1205 ctxt->xpathCtxt->doc = ctxt->initialContextDoc; 1206 /* 1207 * Process the sequence constructor. 1208 */ 1209 xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); 1210 1211 ctxt->xpathCtxt->doc = oldXPDoc; 1212 1213 ctxt->insert = oldInsert; 1214 ctxt->output = oldOutput; 1215 1216 result = xmlXPathNewValueTree((xmlNodePtr) container); 1217 if (result == NULL) { 1218 result = xmlXPathNewCString(""); 1219 } else { 1220 result->boolval = 0; /* Freeing is not handled there anymore */ 1221 } 1222 #ifdef WITH_XSLT_DEBUG_VARIABLE 1223 #ifdef LIBXML_DEBUG_ENABLED 1224 if ((xsltGenericDebugContext == stdout) || 1225 (xsltGenericDebugContext == stderr)) 1226 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, 1227 result, 0); 1228 #endif 1229 #endif 1230 } 1231 } 1232 1233 error: 1234 elem->name = oldVarName; 1235 ctxt->inst = oldInst; 1236 if (result != NULL) { 1237 elem->value = result; 1238 elem->computed = 1; 1239 } 1240 return(result); 1241 } 1242 1243 static void 1244 xsltEvalGlobalVariableWrapper(void *payload, void *data, 1245 const xmlChar *name ATTRIBUTE_UNUSED) { 1246 xsltEvalGlobalVariable((xsltStackElemPtr) payload, 1247 (xsltTransformContextPtr) data); 1248 } 1249 1250 /** 1251 * xsltEvalGlobalVariables: 1252 * @ctxt: the XSLT transformation context 1253 * 1254 * Evaluates all global variables and parameters of a stylesheet. 1255 * For internal use only. This is called at start of a transformation. 1256 * 1257 * Returns 0 in case of success, -1 in case of error 1258 */ 1259 int 1260 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { 1261 xsltStackElemPtr elem; 1262 xsltStylesheetPtr style; 1263 1264 if ((ctxt == NULL) || (ctxt->document == NULL)) 1265 return(-1); 1266 1267 #ifdef WITH_XSLT_DEBUG_VARIABLE 1268 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1269 "Registering global variables\n")); 1270 #endif 1271 /* 1272 * Walk the list from the stylesheets and populate the hash table 1273 */ 1274 style = ctxt->style; 1275 while (style != NULL) { 1276 elem = style->variables; 1277 1278 #ifdef WITH_XSLT_DEBUG_VARIABLE 1279 if ((style->doc != NULL) && (style->doc->URL != NULL)) { 1280 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1281 "Registering global variables from %s\n", 1282 style->doc->URL)); 1283 } 1284 #endif 1285 1286 while (elem != NULL) { 1287 xsltStackElemPtr def; 1288 1289 /* 1290 * Global variables are stored in the variables pool. 1291 */ 1292 def = (xsltStackElemPtr) 1293 xmlHashLookup2(ctxt->globalVars, 1294 elem->name, elem->nameURI); 1295 if (def == NULL) { 1296 1297 def = xsltCopyStackElem(elem); 1298 xmlHashAddEntry2(ctxt->globalVars, 1299 elem->name, elem->nameURI, def); 1300 } else if ((elem->comp != NULL) && 1301 (elem->comp->type == XSLT_FUNC_VARIABLE)) { 1302 /* 1303 * Redefinition of variables from a different stylesheet 1304 * should not generate a message. 1305 */ 1306 if ((elem->comp->inst != NULL) && 1307 (def->comp != NULL) && (def->comp->inst != NULL) && 1308 (elem->comp->inst->doc == def->comp->inst->doc)) 1309 { 1310 xsltTransformError(ctxt, style, elem->comp->inst, 1311 "Global variable %s already defined\n", elem->name); 1312 if (style != NULL) style->errors++; 1313 } 1314 } 1315 elem = elem->next; 1316 } 1317 1318 style = xsltNextImport(style); 1319 } 1320 1321 /* 1322 * This part does the actual evaluation 1323 */ 1324 xmlHashScan(ctxt->globalVars, xsltEvalGlobalVariableWrapper, ctxt); 1325 1326 return(0); 1327 } 1328 1329 /** 1330 * xsltRegisterGlobalVariable: 1331 * @style: the XSLT transformation context 1332 * @name: the variable name 1333 * @ns_uri: the variable namespace URI 1334 * @sel: the expression which need to be evaluated to generate a value 1335 * @tree: the subtree if sel is NULL 1336 * @comp: the precompiled value 1337 * @value: the string value if available 1338 * 1339 * Register a new variable value. If @value is NULL it unregisters 1340 * the variable 1341 * 1342 * Returns 0 in case of success, -1 in case of error 1343 */ 1344 static int 1345 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, 1346 const xmlChar *ns_uri, const xmlChar *sel, 1347 xmlNodePtr tree, xsltStylePreCompPtr comp, 1348 const xmlChar *value) { 1349 xsltStackElemPtr elem, tmp; 1350 if (style == NULL) 1351 return(-1); 1352 if (name == NULL) 1353 return(-1); 1354 if (comp == NULL) 1355 return(-1); 1356 1357 #ifdef WITH_XSLT_DEBUG_VARIABLE 1358 if (comp->type == XSLT_FUNC_PARAM) 1359 xsltGenericDebug(xsltGenericDebugContext, 1360 "Defining global param %s\n", name); 1361 else 1362 xsltGenericDebug(xsltGenericDebugContext, 1363 "Defining global variable %s\n", name); 1364 #endif 1365 1366 elem = xsltNewStackElem(NULL); 1367 if (elem == NULL) 1368 return(-1); 1369 elem->comp = comp; 1370 elem->name = xmlDictLookup(style->dict, name, -1); 1371 elem->select = xmlDictLookup(style->dict, sel, -1); 1372 if (ns_uri) 1373 elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); 1374 elem->tree = tree; 1375 tmp = style->variables; 1376 if (tmp == NULL) { 1377 elem->next = NULL; 1378 style->variables = elem; 1379 } else { 1380 while (tmp != NULL) { 1381 if ((elem->comp->type == XSLT_FUNC_VARIABLE) && 1382 (tmp->comp->type == XSLT_FUNC_VARIABLE) && 1383 (xmlStrEqual(elem->name, tmp->name)) && 1384 ((elem->nameURI == tmp->nameURI) || 1385 (xmlStrEqual(elem->nameURI, tmp->nameURI)))) 1386 { 1387 xsltTransformError(NULL, style, comp->inst, 1388 "redefinition of global variable %s\n", elem->name); 1389 style->errors++; 1390 } 1391 if (tmp->next == NULL) 1392 break; 1393 tmp = tmp->next; 1394 } 1395 elem->next = NULL; 1396 tmp->next = elem; 1397 } 1398 if (value != NULL) { 1399 elem->computed = 1; 1400 elem->value = xmlXPathNewString(value); 1401 } 1402 return(0); 1403 } 1404 1405 /** 1406 * xsltProcessUserParamInternal 1407 * 1408 * @ctxt: the XSLT transformation context 1409 * @name: a null terminated parameter name 1410 * @value: a null terminated value (may be an XPath expression) 1411 * @eval: 0 to treat the value literally, else evaluate as XPath expression 1412 * 1413 * If @eval is 0 then @value is treated literally and is stored in the global 1414 * parameter/variable table without any change. 1415 * 1416 * Uf @eval is 1 then @value is treated as an XPath expression and is 1417 * evaluated. In this case, if you want to pass a string which will be 1418 * interpreted literally then it must be enclosed in single or double quotes. 1419 * If the string contains single quotes (double quotes) then it cannot be 1420 * enclosed single quotes (double quotes). If the string which you want to 1421 * be treated literally contains both single and double quotes (e.g. Meet 1422 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable 1423 * quoting character. You cannot use ' or " inside the string 1424 * because the replacement of character entities with their equivalents is 1425 * done at a different stage of processing. The solution is to call 1426 * xsltQuoteUserParams or xsltQuoteOneUserParam. 1427 * 1428 * This needs to be done on parsed stylesheets before starting to apply 1429 * transformations. Normally this will be called (directly or indirectly) 1430 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, 1431 * or xsltQuoteOneUserParam. 1432 * 1433 * Returns 0 in case of success, -1 in case of error 1434 */ 1435 1436 static 1437 int 1438 xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, 1439 const xmlChar * name, 1440 const xmlChar * value, 1441 int eval) { 1442 1443 xsltStylesheetPtr style; 1444 const xmlChar *prefix; 1445 const xmlChar *href; 1446 xmlXPathCompExprPtr xpExpr; 1447 xmlXPathObjectPtr result; 1448 1449 xsltStackElemPtr elem; 1450 int res; 1451 void *res_ptr; 1452 1453 if (ctxt == NULL) 1454 return(-1); 1455 if (name == NULL) 1456 return(0); 1457 if (value == NULL) 1458 return(0); 1459 1460 style = ctxt->style; 1461 1462 #ifdef WITH_XSLT_DEBUG_VARIABLE 1463 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1464 "Evaluating user parameter %s=%s\n", name, value)); 1465 #endif 1466 1467 /* 1468 * Name lookup 1469 */ 1470 href = NULL; 1471 1472 if (name[0] == '{') { 1473 int len = 0; 1474 1475 while ((name[len] != 0) && (name[len] != '}')) len++; 1476 if (name[len] == 0) { 1477 xsltTransformError(ctxt, style, NULL, 1478 "user param : malformed parameter name : %s\n", name); 1479 } else { 1480 href = xmlDictLookup(ctxt->dict, &name[1], len-1); 1481 name = xmlDictLookup(ctxt->dict, &name[len + 1], -1); 1482 } 1483 } 1484 else { 1485 name = xsltSplitQName(ctxt->dict, name, &prefix); 1486 if (prefix != NULL) { 1487 xmlNsPtr ns; 1488 1489 ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), 1490 prefix); 1491 if (ns == NULL) { 1492 xsltTransformError(ctxt, style, NULL, 1493 "user param : no namespace bound to prefix %s\n", prefix); 1494 href = NULL; 1495 } else { 1496 href = ns->href; 1497 } 1498 } 1499 } 1500 1501 if (name == NULL) 1502 return (-1); 1503 1504 res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); 1505 if (res_ptr != 0) { 1506 xsltTransformError(ctxt, style, NULL, 1507 "Global parameter %s already defined\n", name); 1508 } 1509 if (ctxt->globalVars == NULL) 1510 ctxt->globalVars = xmlHashCreate(20); 1511 1512 /* 1513 * do not overwrite variables with parameters from the command line 1514 */ 1515 while (style != NULL) { 1516 elem = ctxt->style->variables; 1517 while (elem != NULL) { 1518 if ((elem->comp != NULL) && 1519 (elem->comp->type == XSLT_FUNC_VARIABLE) && 1520 (xmlStrEqual(elem->name, name)) && 1521 (xmlStrEqual(elem->nameURI, href))) { 1522 return(0); 1523 } 1524 elem = elem->next; 1525 } 1526 style = xsltNextImport(style); 1527 } 1528 style = ctxt->style; 1529 elem = NULL; 1530 1531 /* 1532 * Do the evaluation if @eval is non-zero. 1533 */ 1534 1535 result = NULL; 1536 if (eval != 0) { 1537 xpExpr = xmlXPathCtxtCompile(ctxt->xpathCtxt, value); 1538 if (xpExpr != NULL) { 1539 xmlDocPtr oldXPDoc; 1540 xmlNodePtr oldXPContextNode; 1541 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; 1542 xmlNsPtr *oldXPNamespaces; 1543 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; 1544 1545 /* 1546 * Save context states. 1547 */ 1548 oldXPDoc = xpctxt->doc; 1549 oldXPContextNode = xpctxt->node; 1550 oldXPProximityPosition = xpctxt->proximityPosition; 1551 oldXPContextSize = xpctxt->contextSize; 1552 oldXPNamespaces = xpctxt->namespaces; 1553 oldXPNsNr = xpctxt->nsNr; 1554 1555 /* 1556 * SPEC XSLT 1.0: 1557 * "At top-level, the expression or template specifying the 1558 * variable value is evaluated with the same context as that used 1559 * to process the root node of the source document: the current 1560 * node is the root node of the source document and the current 1561 * node list is a list containing just the root node of the source 1562 * document." 1563 */ 1564 xpctxt->doc = ctxt->initialContextDoc; 1565 xpctxt->node = ctxt->initialContextNode; 1566 xpctxt->contextSize = 1; 1567 xpctxt->proximityPosition = 1; 1568 /* 1569 * There is really no in scope namespace for parameters on the 1570 * command line. 1571 */ 1572 xpctxt->namespaces = NULL; 1573 xpctxt->nsNr = 0; 1574 1575 result = xmlXPathCompiledEval(xpExpr, xpctxt); 1576 1577 /* 1578 * Restore Context states. 1579 */ 1580 xpctxt->doc = oldXPDoc; 1581 xpctxt->node = oldXPContextNode; 1582 xpctxt->contextSize = oldXPContextSize; 1583 xpctxt->proximityPosition = oldXPProximityPosition; 1584 xpctxt->namespaces = oldXPNamespaces; 1585 xpctxt->nsNr = oldXPNsNr; 1586 1587 xmlXPathFreeCompExpr(xpExpr); 1588 } 1589 if (result == NULL) { 1590 xsltTransformError(ctxt, style, NULL, 1591 "Evaluating user parameter %s failed\n", name); 1592 ctxt->state = XSLT_STATE_STOPPED; 1593 return(-1); 1594 } 1595 } 1596 1597 /* 1598 * If @eval is 0 then @value is to be taken literally and result is NULL 1599 * 1600 * If @eval is not 0, then @value is an XPath expression and has been 1601 * successfully evaluated and result contains the resulting value and 1602 * is not NULL. 1603 * 1604 * Now create an xsltStackElemPtr for insertion into the context's 1605 * global variable/parameter hash table. 1606 */ 1607 1608 #ifdef WITH_XSLT_DEBUG_VARIABLE 1609 #ifdef LIBXML_DEBUG_ENABLED 1610 if ((xsltGenericDebugContext == stdout) || 1611 (xsltGenericDebugContext == stderr)) 1612 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, 1613 result, 0); 1614 #endif 1615 #endif 1616 1617 elem = xsltNewStackElem(NULL); 1618 if (elem != NULL) { 1619 elem->name = name; 1620 elem->select = xmlDictLookup(ctxt->dict, value, -1); 1621 if (href != NULL) 1622 elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); 1623 elem->tree = NULL; 1624 elem->computed = 1; 1625 if (eval == 0) { 1626 elem->value = xmlXPathNewString(value); 1627 } 1628 else { 1629 elem->value = result; 1630 } 1631 } 1632 1633 /* 1634 * Global parameters are stored in the XPath context variables pool. 1635 */ 1636 1637 res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); 1638 if (res != 0) { 1639 xsltFreeStackElem(elem); 1640 xsltTransformError(ctxt, style, NULL, 1641 "Global parameter %s already defined\n", name); 1642 } 1643 return(0); 1644 } 1645 1646 /** 1647 * xsltEvalUserParams: 1648 * 1649 * @ctxt: the XSLT transformation context 1650 * @params: a NULL terminated array of parameters name/value tuples 1651 * 1652 * Evaluate the global variables of a stylesheet. This needs to be 1653 * done on parsed stylesheets before starting to apply transformations. 1654 * Each of the parameters is evaluated as an XPath expression and stored 1655 * in the global variables/parameter hash table. If you want your 1656 * parameter used literally, use xsltQuoteUserParams. 1657 * 1658 * Returns 0 in case of success, -1 in case of error 1659 */ 1660 1661 int 1662 xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { 1663 int indx = 0; 1664 const xmlChar *name; 1665 const xmlChar *value; 1666 1667 if (params == NULL) 1668 return(0); 1669 while (params[indx] != NULL) { 1670 name = (const xmlChar *) params[indx++]; 1671 value = (const xmlChar *) params[indx++]; 1672 if (xsltEvalOneUserParam(ctxt, name, value) != 0) 1673 return(-1); 1674 } 1675 return 0; 1676 } 1677 1678 /** 1679 * xsltQuoteUserParams: 1680 * 1681 * @ctxt: the XSLT transformation context 1682 * @params: a NULL terminated arry of parameters names/values tuples 1683 * 1684 * Similar to xsltEvalUserParams, but the values are treated literally and 1685 * are * *not* evaluated as XPath expressions. This should be done on parsed 1686 * stylesheets before starting to apply transformations. 1687 * 1688 * Returns 0 in case of success, -1 in case of error. 1689 */ 1690 1691 int 1692 xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { 1693 int indx = 0; 1694 const xmlChar *name; 1695 const xmlChar *value; 1696 1697 if (params == NULL) 1698 return(0); 1699 while (params[indx] != NULL) { 1700 name = (const xmlChar *) params[indx++]; 1701 value = (const xmlChar *) params[indx++]; 1702 if (xsltQuoteOneUserParam(ctxt, name, value) != 0) 1703 return(-1); 1704 } 1705 return 0; 1706 } 1707 1708 /** 1709 * xsltEvalOneUserParam: 1710 * @ctxt: the XSLT transformation context 1711 * @name: a null terminated string giving the name of the parameter 1712 * @value: a null terminated string giving the XPath expression to be evaluated 1713 * 1714 * This is normally called from xsltEvalUserParams to process a single 1715 * parameter from a list of parameters. The @value is evaluated as an 1716 * XPath expression and the result is stored in the context's global 1717 * variable/parameter hash table. 1718 * 1719 * To have a parameter treated literally (not as an XPath expression) 1720 * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more 1721 * details see description of xsltProcessOneUserParamInternal. 1722 * 1723 * Returns 0 in case of success, -1 in case of error. 1724 */ 1725 1726 int 1727 xsltEvalOneUserParam(xsltTransformContextPtr ctxt, 1728 const xmlChar * name, 1729 const xmlChar * value) { 1730 return xsltProcessUserParamInternal(ctxt, name, value, 1731 1 /* xpath eval ? */); 1732 } 1733 1734 /** 1735 * xsltQuoteOneUserParam: 1736 * @ctxt: the XSLT transformation context 1737 * @name: a null terminated string giving the name of the parameter 1738 * @value: a null terminated string giving the parameter value 1739 * 1740 * This is normally called from xsltQuoteUserParams to process a single 1741 * parameter from a list of parameters. The @value is stored in the 1742 * context's global variable/parameter hash table. 1743 * 1744 * Returns 0 in case of success, -1 in case of error. 1745 */ 1746 1747 int 1748 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, 1749 const xmlChar * name, 1750 const xmlChar * value) { 1751 return xsltProcessUserParamInternal(ctxt, name, value, 1752 0 /* xpath eval ? */); 1753 } 1754 1755 /** 1756 * xsltBuildVariable: 1757 * @ctxt: the XSLT transformation context 1758 * @comp: the precompiled form 1759 * @tree: the tree if select is NULL 1760 * 1761 * Computes a new variable value. 1762 * 1763 * Returns the xsltStackElemPtr or NULL in case of error 1764 */ 1765 static xsltStackElemPtr 1766 xsltBuildVariable(xsltTransformContextPtr ctxt, 1767 xsltStylePreCompPtr castedComp, 1768 xmlNodePtr tree) 1769 { 1770 #ifdef XSLT_REFACTORED 1771 xsltStyleBasicItemVariablePtr comp = 1772 (xsltStyleBasicItemVariablePtr) castedComp; 1773 #else 1774 xsltStylePreCompPtr comp = castedComp; 1775 #endif 1776 xsltStackElemPtr elem; 1777 1778 #ifdef WITH_XSLT_DEBUG_VARIABLE 1779 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1780 "Building variable %s", comp->name)); 1781 if (comp->select != NULL) 1782 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1783 " select %s", comp->select)); 1784 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n")); 1785 #endif 1786 1787 elem = xsltNewStackElem(ctxt); 1788 if (elem == NULL) 1789 return(NULL); 1790 elem->comp = (xsltStylePreCompPtr) comp; 1791 elem->name = comp->name; 1792 elem->select = comp->select; 1793 elem->nameURI = comp->ns; 1794 elem->tree = tree; 1795 elem->value = xsltEvalVariable(ctxt, elem, 1796 (xsltStylePreCompPtr) comp); 1797 elem->computed = 1; 1798 return(elem); 1799 } 1800 1801 /** 1802 * xsltRegisterVariable: 1803 * @ctxt: the XSLT transformation context 1804 * @comp: the compiled XSLT-variable (or param) instruction 1805 * @tree: the tree if select is NULL 1806 * @isParam: indicates if this is a parameter 1807 * 1808 * Computes and registers a new variable. 1809 * 1810 * Returns 0 in case of success, -1 in case of error 1811 */ 1812 static int 1813 xsltRegisterVariable(xsltTransformContextPtr ctxt, 1814 xsltStylePreCompPtr castedComp, 1815 xmlNodePtr tree, int isParam) 1816 { 1817 #ifdef XSLT_REFACTORED 1818 xsltStyleBasicItemVariablePtr comp = 1819 (xsltStyleBasicItemVariablePtr) castedComp; 1820 #else 1821 xsltStylePreCompPtr comp = castedComp; 1822 int present; 1823 #endif 1824 xsltStackElemPtr variable; 1825 1826 #ifdef XSLT_REFACTORED 1827 /* 1828 * REFACTORED NOTE: Redefinitions of vars/params are checked 1829 * at compilation time in the refactored code. 1830 * xsl:with-param parameters are checked in xsltApplyXSLTTemplate(). 1831 */ 1832 #else 1833 present = xsltCheckStackElem(ctxt, comp->name, comp->ns); 1834 if (isParam == 0) { 1835 if ((present != 0) && (present != 3)) { 1836 /* TODO: report QName. */ 1837 xsltTransformError(ctxt, NULL, comp->inst, 1838 "XSLT-variable: Redefinition of variable '%s'.\n", comp->name); 1839 return(0); 1840 } 1841 } else if (present != 0) { 1842 if ((present == 1) || (present == 2)) { 1843 /* TODO: report QName. */ 1844 xsltTransformError(ctxt, NULL, comp->inst, 1845 "XSLT-param: Redefinition of parameter '%s'.\n", comp->name); 1846 return(0); 1847 } 1848 #ifdef WITH_XSLT_DEBUG_VARIABLE 1849 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1850 "param %s defined by caller\n", comp->name)); 1851 #endif 1852 return(0); 1853 } 1854 #endif /* else of XSLT_REFACTORED */ 1855 1856 variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); 1857 xsltAddStackElem(ctxt, variable); 1858 return(0); 1859 } 1860 1861 /** 1862 * xsltGlobalVariableLookup: 1863 * @ctxt: the XSLT transformation context 1864 * @name: the variable name 1865 * @ns_uri: the variable namespace URI 1866 * 1867 * Search in the Variable array of the context for the given 1868 * variable value. 1869 * 1870 * Returns the value or NULL if not found 1871 */ 1872 static xmlXPathObjectPtr 1873 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, 1874 const xmlChar *ns_uri) { 1875 xsltStackElemPtr elem; 1876 xmlXPathObjectPtr ret = NULL; 1877 1878 /* 1879 * Lookup the global variables in XPath global variable hash table 1880 */ 1881 if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) 1882 return(NULL); 1883 elem = (xsltStackElemPtr) 1884 xmlHashLookup2(ctxt->globalVars, name, ns_uri); 1885 if (elem == NULL) { 1886 #ifdef WITH_XSLT_DEBUG_VARIABLE 1887 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1888 "global variable not found %s\n", name)); 1889 #endif 1890 return(NULL); 1891 } 1892 /* 1893 * URGENT TODO: Move the detection of recursive definitions 1894 * to compile-time. 1895 */ 1896 if (elem->computed == 0) { 1897 if (elem->name == xsltComputingGlobalVarMarker) { 1898 xsltTransformError(ctxt, NULL, elem->comp->inst, 1899 "Recursive definition of %s\n", name); 1900 return(NULL); 1901 } 1902 ret = xsltEvalGlobalVariable(elem, ctxt); 1903 } else 1904 ret = elem->value; 1905 return(xmlXPathObjectCopy(ret)); 1906 } 1907 1908 /** 1909 * xsltVariableLookup: 1910 * @ctxt: the XSLT transformation context 1911 * @name: the variable name 1912 * @ns_uri: the variable namespace URI 1913 * 1914 * Search in the Variable array of the context for the given 1915 * variable value. 1916 * 1917 * Returns the value or NULL if not found 1918 */ 1919 xmlXPathObjectPtr 1920 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, 1921 const xmlChar *ns_uri) { 1922 xsltStackElemPtr elem; 1923 1924 if (ctxt == NULL) 1925 return(NULL); 1926 1927 elem = xsltStackLookup(ctxt, name, ns_uri); 1928 if (elem == NULL) { 1929 return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); 1930 } 1931 if (elem->computed == 0) { 1932 #ifdef WITH_XSLT_DEBUG_VARIABLE 1933 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1934 "uncomputed variable %s\n", name)); 1935 #endif 1936 elem->value = xsltEvalVariable(ctxt, elem, NULL); 1937 elem->computed = 1; 1938 } 1939 if (elem->value != NULL) 1940 return(xmlXPathObjectCopy(elem->value)); 1941 #ifdef WITH_XSLT_DEBUG_VARIABLE 1942 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1943 "variable not found %s\n", name)); 1944 #endif 1945 return(NULL); 1946 } 1947 1948 /** 1949 * xsltParseStylesheetCallerParam: 1950 * @ctxt: the XSLT transformation context 1951 * @inst: the xsl:with-param instruction element 1952 * 1953 * Processes an xsl:with-param instruction at transformation time. 1954 * The value is computed, but not recorded. 1955 * NOTE that this is also called with an *xsl:param* element 1956 * from exsltFuncFunctionFunction(). 1957 * 1958 * Returns the new xsltStackElemPtr or NULL 1959 */ 1960 1961 xsltStackElemPtr 1962 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst) 1963 { 1964 #ifdef XSLT_REFACTORED 1965 xsltStyleBasicItemVariablePtr comp; 1966 #else 1967 xsltStylePreCompPtr comp; 1968 #endif 1969 xmlNodePtr tree = NULL; /* The first child node of the instruction or 1970 the instruction itself. */ 1971 xsltStackElemPtr param = NULL; 1972 1973 if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) 1974 return(NULL); 1975 1976 #ifdef XSLT_REFACTORED 1977 comp = (xsltStyleBasicItemVariablePtr) inst->psvi; 1978 #else 1979 comp = (xsltStylePreCompPtr) inst->psvi; 1980 #endif 1981 1982 if (comp == NULL) { 1983 xsltTransformError(ctxt, NULL, inst, 1984 "Internal error in xsltParseStylesheetCallerParam(): " 1985 "The XSLT 'with-param' instruction was not compiled.\n"); 1986 return(NULL); 1987 } 1988 if (comp->name == NULL) { 1989 xsltTransformError(ctxt, NULL, inst, 1990 "Internal error in xsltParseStylesheetCallerParam(): " 1991 "XSLT 'with-param': The attribute 'name' was not compiled.\n"); 1992 return(NULL); 1993 } 1994 1995 #ifdef WITH_XSLT_DEBUG_VARIABLE 1996 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 1997 "Handling xsl:with-param %s\n", comp->name)); 1998 #endif 1999 2000 if (comp->select == NULL) { 2001 tree = inst->children; 2002 } else { 2003 #ifdef WITH_XSLT_DEBUG_VARIABLE 2004 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2005 " select %s\n", comp->select)); 2006 #endif 2007 tree = inst; 2008 } 2009 2010 param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); 2011 2012 return(param); 2013 } 2014 2015 /** 2016 * xsltParseGlobalVariable: 2017 * @style: the XSLT stylesheet 2018 * @cur: the "variable" element 2019 * 2020 * Parses a global XSLT 'variable' declaration at compilation time 2021 * and registers it 2022 */ 2023 void 2024 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) 2025 { 2026 #ifdef XSLT_REFACTORED 2027 xsltStyleItemVariablePtr comp; 2028 #else 2029 xsltStylePreCompPtr comp; 2030 #endif 2031 2032 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) 2033 return; 2034 2035 #ifdef XSLT_REFACTORED 2036 /* 2037 * Note that xsltStylePreCompute() will be called from 2038 * xslt.c only. 2039 */ 2040 comp = (xsltStyleItemVariablePtr) cur->psvi; 2041 #else 2042 xsltStylePreCompute(style, cur); 2043 comp = (xsltStylePreCompPtr) cur->psvi; 2044 #endif 2045 if (comp == NULL) { 2046 xsltTransformError(NULL, style, cur, 2047 "xsl:variable : compilation failed\n"); 2048 return; 2049 } 2050 2051 if (comp->name == NULL) { 2052 xsltTransformError(NULL, style, cur, 2053 "xsl:variable : missing name attribute\n"); 2054 return; 2055 } 2056 2057 /* 2058 * Parse the content (a sequence constructor) of xsl:variable. 2059 */ 2060 if (cur->children != NULL) { 2061 #ifdef XSLT_REFACTORED 2062 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); 2063 #else 2064 xsltParseTemplateContent(style, cur); 2065 #endif 2066 } 2067 #ifdef WITH_XSLT_DEBUG_VARIABLE 2068 xsltGenericDebug(xsltGenericDebugContext, 2069 "Registering global variable %s\n", comp->name); 2070 #endif 2071 2072 xsltRegisterGlobalVariable(style, comp->name, comp->ns, 2073 comp->select, cur->children, (xsltStylePreCompPtr) comp, 2074 NULL); 2075 } 2076 2077 /** 2078 * xsltParseGlobalParam: 2079 * @style: the XSLT stylesheet 2080 * @cur: the "param" element 2081 * 2082 * parse an XSLT transformation param declaration and record 2083 * its value. 2084 */ 2085 2086 void 2087 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { 2088 #ifdef XSLT_REFACTORED 2089 xsltStyleItemParamPtr comp; 2090 #else 2091 xsltStylePreCompPtr comp; 2092 #endif 2093 2094 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) 2095 return; 2096 2097 #ifdef XSLT_REFACTORED 2098 /* 2099 * Note that xsltStylePreCompute() will be called from 2100 * xslt.c only. 2101 */ 2102 comp = (xsltStyleItemParamPtr) cur->psvi; 2103 #else 2104 xsltStylePreCompute(style, cur); 2105 comp = (xsltStylePreCompPtr) cur->psvi; 2106 #endif 2107 if (comp == NULL) { 2108 xsltTransformError(NULL, style, cur, 2109 "xsl:param : compilation failed\n"); 2110 return; 2111 } 2112 2113 if (comp->name == NULL) { 2114 xsltTransformError(NULL, style, cur, 2115 "xsl:param : missing name attribute\n"); 2116 return; 2117 } 2118 2119 /* 2120 * Parse the content (a sequence constructor) of xsl:param. 2121 */ 2122 if (cur->children != NULL) { 2123 #ifdef XSLT_REFACTORED 2124 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); 2125 #else 2126 xsltParseTemplateContent(style, cur); 2127 #endif 2128 } 2129 2130 #ifdef WITH_XSLT_DEBUG_VARIABLE 2131 xsltGenericDebug(xsltGenericDebugContext, 2132 "Registering global param %s\n", comp->name); 2133 #endif 2134 2135 xsltRegisterGlobalVariable(style, comp->name, comp->ns, 2136 comp->select, cur->children, (xsltStylePreCompPtr) comp, 2137 NULL); 2138 } 2139 2140 /** 2141 * xsltParseStylesheetVariable: 2142 * @ctxt: the XSLT transformation context 2143 * @inst: the xsl:variable instruction element 2144 * 2145 * Registers a local XSLT 'variable' instruction at transformation time 2146 * and evaluates its value. 2147 */ 2148 void 2149 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst) 2150 { 2151 #ifdef XSLT_REFACTORED 2152 xsltStyleItemVariablePtr comp; 2153 #else 2154 xsltStylePreCompPtr comp; 2155 #endif 2156 2157 if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE)) 2158 return; 2159 2160 comp = inst->psvi; 2161 if (comp == NULL) { 2162 xsltTransformError(ctxt, NULL, inst, 2163 "Internal error in xsltParseStylesheetVariable(): " 2164 "The XSLT 'variable' instruction was not compiled.\n"); 2165 return; 2166 } 2167 if (comp->name == NULL) { 2168 xsltTransformError(ctxt, NULL, inst, 2169 "Internal error in xsltParseStylesheetVariable(): " 2170 "The attribute 'name' was not compiled.\n"); 2171 return; 2172 } 2173 2174 #ifdef WITH_XSLT_DEBUG_VARIABLE 2175 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2176 "Registering variable '%s'\n", comp->name)); 2177 #endif 2178 2179 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0); 2180 } 2181 2182 /** 2183 * xsltParseStylesheetParam: 2184 * @ctxt: the XSLT transformation context 2185 * @cur: the XSLT 'param' element 2186 * 2187 * Registers a local XSLT 'param' declaration at transformation time and 2188 * evaluates its value. 2189 */ 2190 void 2191 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) 2192 { 2193 #ifdef XSLT_REFACTORED 2194 xsltStyleItemParamPtr comp; 2195 #else 2196 xsltStylePreCompPtr comp; 2197 #endif 2198 2199 if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE)) 2200 return; 2201 2202 comp = cur->psvi; 2203 if ((comp == NULL) || (comp->name == NULL)) { 2204 xsltTransformError(ctxt, NULL, cur, 2205 "Internal error in xsltParseStylesheetParam(): " 2206 "The XSLT 'param' declaration was not compiled correctly.\n"); 2207 return; 2208 } 2209 2210 #ifdef WITH_XSLT_DEBUG_VARIABLE 2211 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2212 "Registering param %s\n", comp->name)); 2213 #endif 2214 2215 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); 2216 } 2217 2218 /** 2219 * xsltFreeGlobalVariables: 2220 * @ctxt: the XSLT transformation context 2221 * 2222 * Free up the data associated to the global variables 2223 * its value. 2224 */ 2225 2226 void 2227 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { 2228 xmlHashFree(ctxt->globalVars, xsltFreeStackElemEntry); 2229 } 2230 2231 /** 2232 * xsltXPathVariableLookup: 2233 * @ctxt: a void * but the the XSLT transformation context actually 2234 * @name: the variable name 2235 * @ns_uri: the variable namespace URI 2236 * 2237 * This is the entry point when a varibale is needed by the XPath 2238 * interpretor. 2239 * 2240 * Returns the value or NULL if not found 2241 */ 2242 xmlXPathObjectPtr 2243 xsltXPathVariableLookup(void *ctxt, const xmlChar *name, 2244 const xmlChar *ns_uri) { 2245 xsltTransformContextPtr tctxt; 2246 xmlXPathObjectPtr valueObj = NULL; 2247 2248 if ((ctxt == NULL) || (name == NULL)) 2249 return(NULL); 2250 2251 #ifdef WITH_XSLT_DEBUG_VARIABLE 2252 XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2253 "Lookup variable '%s'\n", name)); 2254 #endif 2255 2256 tctxt = (xsltTransformContextPtr) ctxt; 2257 /* 2258 * Local variables/params --------------------------------------------- 2259 * 2260 * Do the lookup from the top of the stack, but 2261 * don't use params being computed in a call-param 2262 * First lookup expects the variable name and URI to 2263 * come from the disctionnary and hence pointer comparison. 2264 */ 2265 if (tctxt->varsNr != 0) { 2266 int i; 2267 xsltStackElemPtr variable = NULL, cur; 2268 2269 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { 2270 cur = tctxt->varsTab[i-1]; 2271 if ((cur->name == name) && (cur->nameURI == ns_uri)) { 2272 #if 0 2273 stack_addr++; 2274 #endif 2275 variable = cur; 2276 goto local_variable_found; 2277 } 2278 cur = cur->next; 2279 } 2280 /* 2281 * Redo the lookup with interned strings to avoid string comparison. 2282 * 2283 * OPTIMIZE TODO: The problem here is, that if we request a 2284 * global variable, then this will be also executed. 2285 */ 2286 { 2287 const xmlChar *tmpName = name, *tmpNsName = ns_uri; 2288 2289 name = xmlDictLookup(tctxt->dict, name, -1); 2290 if (ns_uri) 2291 ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1); 2292 if ((tmpName != name) || (tmpNsName != ns_uri)) { 2293 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { 2294 cur = tctxt->varsTab[i-1]; 2295 if ((cur->name == name) && (cur->nameURI == ns_uri)) { 2296 #if 0 2297 stack_cmp++; 2298 #endif 2299 variable = cur; 2300 goto local_variable_found; 2301 } 2302 } 2303 } 2304 } 2305 2306 local_variable_found: 2307 2308 if (variable) { 2309 if (variable->computed == 0) { 2310 2311 #ifdef WITH_XSLT_DEBUG_VARIABLE 2312 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2313 "uncomputed variable '%s'\n", name)); 2314 #endif 2315 variable->value = xsltEvalVariable(tctxt, variable, NULL); 2316 variable->computed = 1; 2317 } 2318 if (variable->value != NULL) { 2319 valueObj = xmlXPathObjectCopy(variable->value); 2320 } 2321 return(valueObj); 2322 } 2323 } 2324 /* 2325 * Global variables/params -------------------------------------------- 2326 */ 2327 if (tctxt->globalVars) { 2328 valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri); 2329 } 2330 2331 if (valueObj == NULL) { 2332 2333 #ifdef WITH_XSLT_DEBUG_VARIABLE 2334 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2335 "variable not found '%s'\n", name)); 2336 #endif 2337 2338 if (ns_uri) { 2339 xsltTransformError(tctxt, NULL, tctxt->inst, 2340 "Variable '{%s}%s' has not been declared.\n", ns_uri, name); 2341 } else { 2342 xsltTransformError(tctxt, NULL, tctxt->inst, 2343 "Variable '%s' has not been declared.\n", name); 2344 } 2345 } else { 2346 2347 #ifdef WITH_XSLT_DEBUG_VARIABLE 2348 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, 2349 "found variable '%s'\n", name)); 2350 #endif 2351 } 2352 2353 return(valueObj); 2354 } 2355 2356 2357