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