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