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