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