| /* |
| * variables.c: Implementation of the variable storage and lookup |
| * |
| * Reference: |
| * http://www.w3.org/TR/1999/REC-xslt-19991116 |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| */ |
| |
| #define IN_LIBXSLT |
| #include "libxslt.h" |
| |
| #include <string.h> |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/valid.h> |
| #include <libxml/hash.h> |
| #include <libxml/xmlerror.h> |
| #include <libxml/xpath.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/dict.h> |
| #include "xslt.h" |
| #include "xsltInternals.h" |
| #include "xsltutils.h" |
| #include "variables.h" |
| #include "transform.h" |
| #include "imports.h" |
| #include "preproc.h" |
| #include "keys.h" |
| |
| #ifdef WITH_XSLT_DEBUG |
| #define WITH_XSLT_DEBUG_VARIABLE |
| #endif |
| |
| #ifdef XSLT_REFACTORED |
| const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; |
| #endif |
| |
| const xmlChar *xsltComputingGlobalVarMarker = |
| (const xmlChar *) " var/param being computed"; |
| |
| #define XSLT_VAR_GLOBAL 1<<0 |
| #define XSLT_VAR_IN_SELECT 1<<1 |
| #define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable) |
| |
| /************************************************************************ |
| * * |
| * Result Value Tree (Result Tree Fragment) interfaces * |
| * * |
| ************************************************************************/ |
| /** |
| * xsltCreateRVT: |
| * @ctxt: an XSLT transformation context |
| * |
| * Creates a Result Value Tree |
| * (the XSLT 1.0 term for this is "Result Tree Fragment") |
| * |
| * Returns the result value tree or NULL in case of API or internal errors. |
| */ |
| xmlDocPtr |
| xsltCreateRVT(xsltTransformContextPtr ctxt) |
| { |
| xmlDocPtr container; |
| |
| /* |
| * Question: Why is this function public? |
| * Answer: It is called by the EXSLT module. |
| */ |
| if (ctxt == NULL) |
| return(NULL); |
| |
| /* |
| * Reuse a RTF from the cache if available. |
| */ |
| if (ctxt->cache->RVT) { |
| container = ctxt->cache->RVT; |
| ctxt->cache->RVT = (xmlDocPtr) container->next; |
| /* clear the internal pointers */ |
| container->next = NULL; |
| container->prev = NULL; |
| if (ctxt->cache->nbRVT > 0) |
| ctxt->cache->nbRVT--; |
| #ifdef XSLT_DEBUG_PROFILE_CACHE |
| ctxt->cache->dbgReusedRVTs++; |
| #endif |
| return(container); |
| } |
| |
| container = xmlNewDoc(NULL); |
| if (container == NULL) |
| return(NULL); |
| container->dict = ctxt->dict; |
| xmlDictReference(container->dict); |
| XSLT_MARK_RES_TREE_FRAG(container); |
| container->doc = container; |
| container->parent = NULL; |
| return(container); |
| } |
| |
| /** |
| * xsltRegisterTmpRVT: |
| * @ctxt: an XSLT transformation context |
| * @RVT: a result value tree (Result Tree Fragment) |
| * |
| * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment) |
| * in the garbage collector. |
| * The fragment will be freed at the exit of the currently |
| * instantiated xsl:template. |
| * Obsolete; this function might produce massive memory overhead, |
| * since the fragment is only freed when the current xsl:template |
| * exits. Use xsltRegisterLocalRVT() instead. |
| * |
| * Returns 0 in case of success and -1 in case of API or internal errors. |
| */ |
| int |
| xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) |
| { |
| if ((ctxt == NULL) || (RVT == NULL)) |
| return(-1); |
| |
| /* |
| * We'll restrict the lifetime of user-created fragments |
| * insinde an xsl:variable and xsl:param to the lifetime of the |
| * var/param itself. |
| */ |
| if (ctxt->contextVariable != NULL) { |
| RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; |
| XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; |
| return(0); |
| } |
| |
| RVT->next = (xmlNodePtr) ctxt->tmpRVT; |
| if (ctxt->tmpRVT != NULL) |
| ctxt->tmpRVT->prev = (xmlNodePtr) RVT; |
| ctxt->tmpRVT = RVT; |
| return(0); |
| } |
| |
| /** |
| * xsltRegisterLocalRVT: |
| * @ctxt: an XSLT transformation context |
| * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr) |
| * |
| * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment) |
| * in the RVT garbage collector. |
| * The fragment will be freed when the instruction which created the |
| * fragment exits. |
| * |
| * Returns 0 in case of success and -1 in case of API or internal errors. |
| */ |
| int |
| xsltRegisterLocalRVT(xsltTransformContextPtr ctxt, |
| xmlDocPtr RVT) |
| { |
| if ((ctxt == NULL) || (RVT == NULL)) |
| return(-1); |
| |
| /* |
| * When evaluating "select" expressions of xsl:variable |
| * and xsl:param, we need to bind newly created tree fragments |
| * to the variable itself; otherwise the tragment will be |
| * freed before we leave the scope of a var. |
| */ |
| if ((ctxt->contextVariable != NULL) && |
| (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT)) |
| { |
| RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; |
| XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; |
| return(0); |
| } |
| /* |
| * Store the fragment in the scope of the current instruction. |
| * If not reference by a returning instruction (like EXSLT's function), |
| * then this fragment will be freed, when the instruction exits. |
| */ |
| RVT->next = (xmlNodePtr) ctxt->localRVT; |
| if (ctxt->localRVT != NULL) |
| ctxt->localRVT->prev = (xmlNodePtr) RVT; |
| ctxt->localRVT = RVT; |
| /* |
| * We need to keep track of the first registered fragment |
| * for extension instructions which return fragments |
| * (e.g. EXSLT'S function), in order to let |
| * xsltExtensionInstructionResultFinalize() clear the |
| * preserving flag on the fragments. |
| */ |
| if (ctxt->localRVTBase == NULL) |
| ctxt->localRVTBase = RVT; |
| return(0); |
| } |
| |
| /** |
| * xsltExtensionInstructionResultFinalize: |
| * @ctxt: an XSLT transformation context |
| * |
| * Finalizes the data (e.g. result tree fragments) created |
| * within a value-returning process (e.g. EXSLT's function). |
| * Tree fragments marked as being returned by a function are |
| * set to normal state, which means that the fragment garbage |
| * collector will free them after the function-calling process exits. |
| * |
| * Returns 0 in case of success and -1 in case of API or internal errors. |
| */ |
| int |
| xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt) |
| { |
| xmlDocPtr cur; |
| |
| if (ctxt == NULL) |
| return(-1); |
| if (ctxt->localRVTBase == NULL) |
| return(0); |
| /* |
| * Enable remaining local tree fragments to be freed |
| * by the fragment garbage collector. |
| */ |
| cur = ctxt->localRVTBase; |
| do { |
| cur->psvi = NULL; |
| cur = (xmlDocPtr) cur->next; |
| } while (cur != NULL); |
| return(0); |
| } |
| |
| /** |
| * xsltExtensionInstructionResultRegister: |
| * @ctxt: an XSLT transformation context |
| * @obj: an XPath object to be inspected for result tree fragments |
| * |
| * Marks the result of a value-returning extension instruction |
| * in order to avoid it being garbage collected before the |
| * extension instruction exits. |
| * Note that one still has to additionally register any newly created |
| * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT(). |
| * |
| * Returns 0 in case of success and -1 in case of error. |
| */ |
| int |
| xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt, |
| xmlXPathObjectPtr obj) |
| { |
| int i; |
| xmlNodePtr cur; |
| xmlDocPtr doc; |
| |
| if ((ctxt == NULL) || (obj == NULL)) |
| return(-1); |
| |
| /* |
| * OPTIMIZE TODO: If no local variables/params and no local tree |
| * fragments were created, then we don't need to analyse the XPath |
| * objects for tree fragments. |
| */ |
| |
| if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE)) |
| return(0); |
| if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) |
| return(0); |
| |
| for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
| cur = obj->nodesetval->nodeTab[i]; |
| if (cur->type == XML_NAMESPACE_DECL) { |
| /* |
| * The XPath module sets the owner element of a ns-node on |
| * the ns->next field. |
| */ |
| if ((((xmlNsPtr) cur)->next != NULL) && |
| (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE)) |
| { |
| cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; |
| doc = cur->doc; |
| } else { |
| xsltTransformError(ctxt, NULL, ctxt->inst, |
| "Internal error in " |
| "xsltExtensionInstructionResultRegister(): " |
| "Cannot retrieve the doc of a namespace node.\n"); |
| goto error; |
| } |
| } else { |
| doc = cur->doc; |
| } |
| if (doc == NULL) { |
| xsltTransformError(ctxt, NULL, ctxt->inst, |
| "Internal error in " |
| "xsltExtensionInstructionResultRegister(): " |
| "Cannot retrieve the doc of a node.\n"); |
| goto error; |
| } |
| if (doc->name && (doc->name[0] == ' ')) { |
| /* |
| * This is a result tree fragment. |
| * We'll use the @psvi field for reference counting. |
| * TODO: How do we know if this is a value of a |
| * global variable or a doc acquired via the |
| * document() function? |
| */ |
| doc->psvi = (void *) ((long) 1); |
| } |
| } |
| |
| return(0); |
| error: |
| return(-1); |
| } |
| |
| /** |
| * xsltReleaseRVT: |
| * @ctxt: an XSLT transformation context |
| * @RVT: a result value tree (Result Tree Fragment) |
| * |
| * Either frees the RVT (which is an xmlDoc) or stores |
| * it in the context's cache for later reuse. |
| */ |
| void |
| xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) |
| { |
| if (RVT == NULL) |
| return; |
| |
| if (ctxt && (ctxt->cache->nbRVT < 40)) { |
| /* |
| * Store the Result Tree Fragment. |
| * Free the document info. |
| */ |
| if (RVT->_private != NULL) { |
| xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); |
| xmlFree(RVT->_private); |
| RVT->_private = NULL; |
| } |
| /* |
| * Clear the document tree. |
| * REVISIT TODO: Do we expect ID/IDREF tables to be existent? |
| */ |
| if (RVT->children != NULL) { |
| xmlFreeNodeList(RVT->children); |
| RVT->children = NULL; |
| RVT->last = NULL; |
| } |
| if (RVT->ids != NULL) { |
| xmlFreeIDTable((xmlIDTablePtr) RVT->ids); |
| RVT->ids = NULL; |
| } |
| if (RVT->refs != NULL) { |
| xmlFreeRefTable((xmlRefTablePtr) RVT->refs); |
| RVT->refs = NULL; |
| } |
| |
| /* |
| * Reset the reference counter. |
| */ |
| RVT->psvi = 0; |
| |
| RVT->next = (xmlNodePtr) ctxt->cache->RVT; |
| ctxt->cache->RVT = RVT; |
| |
| ctxt->cache->nbRVT++; |
| |
| #ifdef XSLT_DEBUG_PROFILE_CACHE |
| ctxt->cache->dbgCachedRVTs++; |
| #endif |
| return; |
| } |
| /* |
| * Free it. |
| */ |
| if (RVT->_private != NULL) { |
| xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); |
| xmlFree(RVT->_private); |
| } |
| xmlFreeDoc(RVT); |
| } |
| |
| /** |
| * xsltRegisterPersistRVT: |
| * @ctxt: an XSLT transformation context |
| * @RVT: a result value tree (Result Tree Fragment) |
| * |
| * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) |
| * in the fragment garbage collector. |
| * The fragment will be freed when the transformation context is |
| * freed. |
| * |
| * Returns 0 in case of success and -1 in case of error. |
| */ |
| int |
| xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) |
| { |
| if ((ctxt == NULL) || (RVT == NULL)) return(-1); |
| |
| RVT->next = (xmlNodePtr) ctxt->persistRVT; |
| if (ctxt->persistRVT != NULL) |
| ctxt->persistRVT->prev = (xmlNodePtr) RVT; |
| ctxt->persistRVT = RVT; |
| return(0); |
| } |
| |
| /** |
| * xsltFreeRVTs: |
| * @ctxt: an XSLT transformation context |
| * |
| * Frees all registered result value trees (Result Tree Fragments) |
| * of the transformation. Internal function; should not be called |
| * by user-code. |
| */ |
| void |
| xsltFreeRVTs(xsltTransformContextPtr ctxt) |
| { |
| xmlDocPtr cur, next; |
| |
| if (ctxt == NULL) |
| return; |
| /* |
| * Local fragments. |
| */ |
| cur = ctxt->localRVT; |
| while (cur != NULL) { |
| next = (xmlDocPtr) cur->next; |
| if (cur->_private != NULL) { |
| xsltFreeDocumentKeys(cur->_private); |
| xmlFree(cur->_private); |
| } |
| xmlFreeDoc(cur); |
| cur = next; |
| } |
| ctxt->localRVT = NULL; |
| /* |
| * User-created per-template fragments. |
| */ |
| cur = ctxt->tmpRVT; |
| while (cur != NULL) { |
| next = (xmlDocPtr) cur->next; |
| if (cur->_private != NULL) { |
| xsltFreeDocumentKeys(cur->_private); |
| xmlFree(cur->_private); |
| } |
| xmlFreeDoc(cur); |
| cur = next; |
| } |
| ctxt->tmpRVT = NULL; |
| /* |
| * Global fragments. |
| */ |
| cur = ctxt->persistRVT; |
| while (cur != NULL) { |
| next = (xmlDocPtr) cur->next; |
| if (cur->_private != NULL) { |
| xsltFreeDocumentKeys(cur->_private); |
| xmlFree(cur->_private); |
| } |
| xmlFreeDoc(cur); |
| cur = next; |
| } |
| ctxt->persistRVT = NULL; |
| } |
| |
| /************************************************************************ |
| * * |
| * Module interfaces * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltNewStackElem: |
| * |
| * Create a new XSLT ParserContext |
| * |
| * Returns the newly allocated xsltParserStackElem or NULL in case of error |
| */ |
| static xsltStackElemPtr |
| xsltNewStackElem(xsltTransformContextPtr ctxt) |
| { |
| xsltStackElemPtr ret; |
| /* |
| * Reuse a stack item from the cache if available. |
| */ |
| if (ctxt && ctxt->cache->stackItems) { |
| ret = ctxt->cache->stackItems; |
| ctxt->cache->stackItems = ret->next; |
| ret->next = NULL; |
| ctxt->cache->nbStackItems--; |
| #ifdef XSLT_DEBUG_PROFILE_CACHE |
| ctxt->cache->dbgReusedVars++; |
| #endif |
| return(ret); |
| } |
| ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); |
| if (ret == NULL) { |
| xsltTransformError(NULL, NULL, NULL, |
| "xsltNewStackElem : malloc failed\n"); |
| return(NULL); |
| } |
| memset(ret, 0, sizeof(xsltStackElem)); |
| ret->context = ctxt; |
| return(ret); |
| } |
| |
| /** |
| * xsltCopyStackElem: |
| * @elem: an XSLT stack element |
| * |
| * Makes a copy of the stack element |
| * |
| * Returns the copy of NULL |
| */ |
| static xsltStackElemPtr |
| xsltCopyStackElem(xsltStackElemPtr elem) { |
| xsltStackElemPtr cur; |
| |
| cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); |
| if (cur == NULL) { |
| xsltTransformError(NULL, NULL, NULL, |
| "xsltCopyStackElem : malloc failed\n"); |
| return(NULL); |
| } |
| memset(cur, 0, sizeof(xsltStackElem)); |
| cur->context = elem->context; |
| cur->name = elem->name; |
| cur->nameURI = elem->nameURI; |
| cur->select = elem->select; |
| cur->tree = elem->tree; |
| cur->comp = elem->comp; |
| return(cur); |
| } |
| |
| /** |
| * xsltFreeStackElem: |
| * @elem: an XSLT stack element |
| * |
| * Free up the memory allocated by @elem |
| */ |
| static void |
| xsltFreeStackElem(xsltStackElemPtr elem) { |
| if (elem == NULL) |
| return; |
| if (elem->value != NULL) |
| xmlXPathFreeObject(elem->value); |
| /* |
| * Release the list of temporary Result Tree Fragments. |
| */ |
| if (elem->fragment) { |
| xmlDocPtr cur; |
| |
| while (elem->fragment != NULL) { |
| cur = elem->fragment; |
| elem->fragment = (xmlDocPtr) cur->next; |
| |
| if (elem->context && |
| (cur->psvi == (void *) ((long) 1))) |
| { |
| /* |
| * This fragment is a result of an extension instruction |
| * (e.g. XSLT's function) and needs to be preserved until |
| * the instruction exits. |
| * Example: The fragment of the variable must not be freed |
| * since it is returned by the EXSLT function: |
| * <f:function name="foo"> |
| * <xsl:variable name="bar"> |
| * <bar/> |
| * </xsl:variable> |
| * <f:result select="$bar"/> |
| * </f:function> |
| * |
| */ |
| xsltRegisterLocalRVT(elem->context, cur); |
| } else { |
| xsltReleaseRVT((xsltTransformContextPtr) elem->context, |
| cur); |
| } |
| } |
| } |
| /* |
| * Cache or free the variable structure. |
| */ |
| if (elem->context && (elem->context->cache->nbStackItems < 50)) { |
| /* |
| * Store the item in the cache. |
| */ |
| xsltTransformContextPtr ctxt = elem->context; |
| memset(elem, 0, sizeof(xsltStackElem)); |
| elem->context = ctxt; |
| elem->next = ctxt->cache->stackItems; |
| ctxt->cache->stackItems = elem; |
| ctxt->cache->nbStackItems++; |
| #ifdef XSLT_DEBUG_PROFILE_CACHE |
| ctxt->cache->dbgCachedVars++; |
| #endif |
| return; |
| } |
| xmlFree(elem); |
| } |
| |
| /** |
| * xsltFreeStackElemList: |
| * @elem: an XSLT stack element |
| * |
| * Free up the memory allocated by @elem |
| */ |
| void |
| xsltFreeStackElemList(xsltStackElemPtr elem) { |
| xsltStackElemPtr next; |
| |
| while (elem != NULL) { |
| next = elem->next; |
| xsltFreeStackElem(elem); |
| elem = next; |
| } |
| } |
| |
| /** |
| * xsltStackLookup: |
| * @ctxt: an XSLT transformation context |
| * @name: the local part of the name |
| * @nameURI: the URI part of the name |
| * |
| * Locate an element in the stack based on its name. |
| */ |
| #if 0 /* TODO: Those seem to have been used for debugging. */ |
| static int stack_addr = 0; |
| static int stack_cmp = 0; |
| #endif |
| |
| static xsltStackElemPtr |
| xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, |
| const xmlChar *nameURI) { |
| int i; |
| xsltStackElemPtr cur; |
| |
| if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) |
| return(NULL); |
| |
| /* |
| * Do the lookup from the top of the stack, but |
| * don't use params being computed in a call-param |
| * First lookup expects the variable name and URI to |
| * come from the disctionnary and hence pointer comparison. |
| */ |
| for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { |
| cur = ctxt->varsTab[i-1]; |
| while (cur != NULL) { |
| if ((cur->name == name) && (cur->nameURI == nameURI)) { |
| #if 0 |
| stack_addr++; |
| #endif |
| return(cur); |
| } |
| cur = cur->next; |
| } |
| } |
| |
| /* |
| * Redo the lookup with interned string compares |
| * to avoid string compares. |
| */ |
| name = xmlDictLookup(ctxt->dict, name, -1); |
| if (nameURI != NULL) |
| nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); |
| |
| for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { |
| cur = ctxt->varsTab[i-1]; |
| while (cur != NULL) { |
| if ((cur->name == name) && (cur->nameURI == nameURI)) { |
| #if 0 |
| stack_cmp++; |
| #endif |
| return(cur); |
| } |
| cur = cur->next; |
| } |
| } |
| |
| return(NULL); |
| } |
| |
| /** |
| * xsltCheckStackElem: |
| * @ctxt: xn XSLT transformation context |
| * @name: the variable name |
| * @nameURI: the variable namespace URI |
| * |
| * Checks whether a variable or param is already defined. |
| * |
| * URGENT TODO: Checks for redefinition of vars/params should be |
| * done only at compilation time. |
| * |
| * Returns 1 if variable is present, 2 if param is present, 3 if this |
| * is an inherited param, 0 if not found, -1 in case of failure. |
| */ |
| static int |
| xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, |
| const xmlChar *nameURI) { |
| xsltStackElemPtr cur; |
| |
| if ((ctxt == NULL) || (name == NULL)) |
| return(-1); |
| |
| cur = xsltStackLookup(ctxt, name, nameURI); |
| if (cur == NULL) |
| return(0); |
| if (cur->comp != NULL) { |
| if (cur->comp->type == XSLT_FUNC_WITHPARAM) |
| return(3); |
| else if (cur->comp->type == XSLT_FUNC_PARAM) |
| return(2); |
| } |
| |
| return(1); |
| } |
| |
| /** |
| * xsltAddStackElem: |
| * @ctxt: xn XSLT transformation context |
| * @elem: a stack element |
| * |
| * Push an element (or list) onto the stack. |
| * In case of a list, each member will be pushed into |
| * a seperate slot; i.e. there's always 1 stack entry for |
| * 1 stack element. |
| * |
| * Returns 0 in case of success, -1 in case of failure. |
| */ |
| static int |
| xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) |
| { |
| if ((ctxt == NULL) || (elem == NULL)) |
| return(-1); |
| |
| do { |
| if (ctxt->varsMax == 0) { |
| ctxt->varsMax = 10; |
| ctxt->varsTab = |
| (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * |
| sizeof(ctxt->varsTab[0])); |
| if (ctxt->varsTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); |
| return (-1); |
| } |
| } |
| if (ctxt->varsNr >= ctxt->varsMax) { |
| ctxt->varsMax *= 2; |
| ctxt->varsTab = |
| (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, |
| ctxt->varsMax * |
| sizeof(ctxt->varsTab[0])); |
| if (ctxt->varsTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); |
| return (-1); |
| } |
| } |
| ctxt->varsTab[ctxt->varsNr++] = elem; |
| ctxt->vars = elem; |
| |
| elem = elem->next; |
| } while (elem != NULL); |
| |
| return(0); |
| } |
| |
| /** |
| * xsltAddStackElemList: |
| * @ctxt: xn XSLT transformation context |
| * @elems: a stack element list |
| * |
| * Push an element list onto the stack. |
| * |
| * Returns 0 in case of success, -1 in case of failure. |
| */ |
| int |
| xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) |
| { |
| return(xsltAddStackElem(ctxt, elems)); |
| } |
| |
| /************************************************************************ |
| * * |
| * Module interfaces * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltEvalVariable: |
| * @ctxt: the XSLT transformation context |
| * @variable: the variable or parameter item |
| * @comp: the compiled XSLT instruction |
| * |
| * Evaluate a variable value. |
| * |
| * Returns the XPath Object value or NULL in case of error |
| */ |
| static xmlXPathObjectPtr |
| xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, |
| xsltStylePreCompPtr castedComp) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemVariablePtr comp = |
| (xsltStyleItemVariablePtr) castedComp; |
| #else |
| xsltStylePreCompPtr comp = castedComp; |
| #endif |
| xmlXPathObjectPtr result = NULL; |
| xmlNodePtr oldInst; |
| |
| if ((ctxt == NULL) || (variable == NULL)) |
| return(NULL); |
| |
| /* |
| * A variable or parameter are evaluated on demand; thus the |
| * context (of XSLT and XPath) need to be temporarily adjusted and |
| * restored on exit. |
| */ |
| oldInst = ctxt->inst; |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Evaluating variable '%s'\n", variable->name)); |
| #endif |
| if (variable->select != NULL) { |
| xmlXPathCompExprPtr xpExpr = NULL; |
| xmlDocPtr oldXPDoc; |
| xmlNodePtr oldXPContextNode; |
| int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; |
| xmlNsPtr *oldXPNamespaces; |
| xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; |
| xsltStackElemPtr oldVar = ctxt->contextVariable; |
| |
| if ((comp != NULL) && (comp->comp != NULL)) { |
| xpExpr = comp->comp; |
| } else { |
| xpExpr = xmlXPathCompile(variable->select); |
| } |
| if (xpExpr == NULL) |
| return(NULL); |
| /* |
| * Save context states. |
| */ |
| oldXPDoc = xpctxt->doc; |
| oldXPContextNode = xpctxt->node; |
| oldXPProximityPosition = xpctxt->proximityPosition; |
| oldXPContextSize = xpctxt->contextSize; |
| oldXPNamespaces = xpctxt->namespaces; |
| oldXPNsNr = xpctxt->nsNr; |
| |
| xpctxt->node = ctxt->node; |
| /* |
| * OPTIMIZE TODO: Lame try to set the context doc. |
| * Get rid of this somehow in xpath.c. |
| */ |
| if ((ctxt->node->type != XML_NAMESPACE_DECL) && |
| ctxt->node->doc) |
| xpctxt->doc = ctxt->node->doc; |
| /* |
| * BUG TODO: The proximity position and the context size will |
| * potentially be wrong. |
| * Example: |
| * <xsl:template select="foo"> |
| * <xsl:variable name="pos" select="position()"/> |
| * <xsl:for-each select="bar"> |
| * <xsl:value-of select="$pos"/> |
| * </xsl:for-each> |
| * </xsl:template> |
| * Here the proximity position and context size are changed |
| * to the context of <xsl:for-each select="bar">, but |
| * the variable needs to be evaluated in the context of |
| * <xsl:template select="foo">. |
| */ |
| if (comp != NULL) { |
| |
| #ifdef XSLT_REFACTORED |
| if (comp->inScopeNs != NULL) { |
| xpctxt->namespaces = comp->inScopeNs->list; |
| xpctxt->nsNr = comp->inScopeNs->xpathNumber; |
| } else { |
| xpctxt->namespaces = NULL; |
| xpctxt->nsNr = 0; |
| } |
| #else |
| xpctxt->namespaces = comp->nsList; |
| xpctxt->nsNr = comp->nsNr; |
| #endif |
| } else { |
| xpctxt->namespaces = NULL; |
| xpctxt->nsNr = 0; |
| } |
| |
| /* |
| * We need to mark that we are "selecting" a var's value; |
| * if any tree fragments are created inside the expression, |
| * then those need to be stored inside the variable; otherwise |
| * we'll eventually free still referenced fragments, before |
| * we leave the scope of the variable. |
| */ |
| ctxt->contextVariable = variable; |
| variable->flags |= XSLT_VAR_IN_SELECT; |
| |
| result = xmlXPathCompiledEval(xpExpr, xpctxt); |
| |
| variable->flags ^= XSLT_VAR_IN_SELECT; |
| /* |
| * Restore Context states. |
| */ |
| ctxt->contextVariable = oldVar; |
| |
| xpctxt->doc = oldXPDoc; |
| xpctxt->node = oldXPContextNode; |
| xpctxt->contextSize = oldXPContextSize; |
| xpctxt->proximityPosition = oldXPProximityPosition; |
| xpctxt->namespaces = oldXPNamespaces; |
| xpctxt->nsNr = oldXPNsNr; |
| |
| if ((comp == NULL) || (comp->comp == NULL)) |
| xmlXPathFreeCompExpr(xpExpr); |
| if (result == NULL) { |
| xsltTransformError(ctxt, NULL, |
| (comp != NULL) ? comp->inst : NULL, |
| "Failed to evaluate the expression of variable '%s'.\n", |
| variable->name); |
| ctxt->state = XSLT_STATE_STOPPED; |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| #ifdef LIBXML_DEBUG_ENABLED |
| } else { |
| if ((xsltGenericDebugContext == stdout) || |
| (xsltGenericDebugContext == stderr)) |
| xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, |
| result, 0); |
| #endif |
| #endif |
| } |
| } else { |
| if (variable->tree == NULL) { |
| result = xmlXPathNewCString(""); |
| } else { |
| if (variable->tree) { |
| xmlDocPtr container; |
| xmlNodePtr oldInsert; |
| xmlDocPtr oldOutput; |
| xsltStackElemPtr oldVar = ctxt->contextVariable; |
| |
| /* |
| * Generate a result tree fragment. |
| */ |
| container = xsltCreateRVT(ctxt); |
| if (container == NULL) |
| goto error; |
| /* |
| * NOTE: Local Result Tree Fragments of params/variables |
| * are not registered globally anymore; the life-time |
| * is not directly dependant of the param/variable itself. |
| * |
| * OLD: xsltRegisterTmpRVT(ctxt, container); |
| */ |
| /* |
| * Attach the Result Tree Fragment to the variable; |
| * when the variable is freed, it will also free |
| * the Result Tree Fragment. |
| */ |
| variable->fragment = container; |
| |
| oldOutput = ctxt->output; |
| oldInsert = ctxt->insert; |
| |
| ctxt->output = container; |
| ctxt->insert = (xmlNodePtr) container; |
| ctxt->contextVariable = variable; |
| /* |
| * Process the sequence constructor (variable->tree). |
| * The resulting tree will be held by @container. |
| */ |
| xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree, |
| NULL, NULL); |
| |
| ctxt->contextVariable = oldVar; |
| ctxt->insert = oldInsert; |
| ctxt->output = oldOutput; |
| |
| result = xmlXPathNewValueTree((xmlNodePtr) container); |
| } |
| if (result == NULL) { |
| result = xmlXPathNewCString(""); |
| } else { |
| /* |
| * Freeing is not handled there anymore. |
| * QUESTION TODO: What does the above comment mean? |
| */ |
| result->boolval = 0; |
| } |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| #ifdef LIBXML_DEBUG_ENABLED |
| |
| if ((xsltGenericDebugContext == stdout) || |
| (xsltGenericDebugContext == stderr)) |
| xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, |
| result, 0); |
| #endif |
| #endif |
| } |
| } |
| |
| error: |
| ctxt->inst = oldInst; |
| return(result); |
| } |
| |
| /** |
| * xsltEvalGlobalVariable: |
| * @elem: the variable or parameter |
| * @ctxt: the XSLT transformation context |
| * |
| * Evaluates a the value of a global xsl:variable or |
| * xsl:param declaration. |
| * |
| * Returns the XPath Object value or NULL in case of error |
| */ |
| static xmlXPathObjectPtr |
| xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) |
| { |
| xmlXPathObjectPtr result = NULL; |
| xmlNodePtr oldInst; |
| const xmlChar* oldVarName; |
| |
| #ifdef XSLT_REFACTORED |
| xsltStyleBasicItemVariablePtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| |
| if ((ctxt == NULL) || (elem == NULL)) |
| return(NULL); |
| if (elem->computed) |
| return(elem->value); |
| |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Evaluating global variable %s\n", elem->name)); |
| #endif |
| |
| #ifdef WITH_DEBUGGER |
| if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && |
| elem->comp && elem->comp->inst) |
| xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); |
| #endif |
| |
| oldInst = ctxt->inst; |
| comp = elem->comp; |
| oldVarName = elem->name; |
| elem->name = xsltComputingGlobalVarMarker; |
| /* |
| * OPTIMIZE TODO: We should consider instantiating global vars/params |
| * on-demand. The vars/params don't need to be evaluated if never |
| * called; and in the case of global params, if values for such params |
| * are provided by the user. |
| */ |
| if (elem->select != NULL) { |
| xmlXPathCompExprPtr xpExpr = NULL; |
| xmlDocPtr oldXPDoc; |
| xmlNodePtr oldXPContextNode; |
| int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; |
| xmlNsPtr *oldXPNamespaces; |
| xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; |
| |
| if ((comp != NULL) && (comp->comp != NULL)) { |
| xpExpr = comp->comp; |
| } else { |
| xpExpr = xmlXPathCompile(elem->select); |
| } |
| if (xpExpr == NULL) |
| goto error; |
| |
| |
| if (comp != NULL) |
| ctxt->inst = comp->inst; |
| else |
| ctxt->inst = NULL; |
| /* |
| * SPEC XSLT 1.0: |
| * "At top-level, the expression or template specifying the |
| * variable value is evaluated with the same context as that used |
| * to process the root node of the source document: the current |
| * node is the root node of the source document and the current |
| * node list is a list containing just the root node of the source |
| * document." |
| */ |
| /* |
| * Save context states. |
| */ |
| oldXPDoc = xpctxt->doc; |
| oldXPContextNode = xpctxt->node; |
| oldXPProximityPosition = xpctxt->proximityPosition; |
| oldXPContextSize = xpctxt->contextSize; |
| oldXPNamespaces = xpctxt->namespaces; |
| oldXPNsNr = xpctxt->nsNr; |
| |
| xpctxt->node = ctxt->initialContextNode; |
| xpctxt->doc = ctxt->initialContextDoc; |
| xpctxt->contextSize = 1; |
| xpctxt->proximityPosition = 1; |
| |
| if (comp != NULL) { |
| |
| #ifdef XSLT_REFACTORED |
| if (comp->inScopeNs != NULL) { |
| xpctxt->namespaces = comp->inScopeNs->list; |
| xpctxt->nsNr = comp->inScopeNs->xpathNumber; |
| } else { |
| xpctxt->namespaces = NULL; |
| xpctxt->nsNr = 0; |
| } |
| #else |
| xpctxt->namespaces = comp->nsList; |
| xpctxt->nsNr = comp->nsNr; |
| #endif |
| } else { |
| xpctxt->namespaces = NULL; |
| xpctxt->nsNr = 0; |
| } |
| |
| result = xmlXPathCompiledEval(xpExpr, xpctxt); |
| |
| /* |
| * Restore Context states. |
| */ |
| xpctxt->doc = oldXPDoc; |
| xpctxt->node = oldXPContextNode; |
| xpctxt->contextSize = oldXPContextSize; |
| xpctxt->proximityPosition = oldXPProximityPosition; |
| xpctxt->namespaces = oldXPNamespaces; |
| xpctxt->nsNr = oldXPNsNr; |
| |
| if ((comp == NULL) || (comp->comp == NULL)) |
| xmlXPathFreeCompExpr(xpExpr); |
| if (result == NULL) { |
| if (comp == NULL) |
| xsltTransformError(ctxt, NULL, NULL, |
| "Evaluating global variable %s failed\n", elem->name); |
| else |
| xsltTransformError(ctxt, NULL, comp->inst, |
| "Evaluating global variable %s failed\n", elem->name); |
| ctxt->state = XSLT_STATE_STOPPED; |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| #ifdef LIBXML_DEBUG_ENABLED |
| } else { |
| if ((xsltGenericDebugContext == stdout) || |
| (xsltGenericDebugContext == stderr)) |
| xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, |
| result, 0); |
| #endif |
| #endif |
| } |
| } else { |
| if (elem->tree == NULL) { |
| result = xmlXPathNewCString(""); |
| } else { |
| xmlDocPtr container; |
| xmlNodePtr oldInsert; |
| xmlDocPtr oldOutput, oldXPDoc; |
| /* |
| * Generate a result tree fragment. |
| */ |
| container = xsltCreateRVT(ctxt); |
| if (container == NULL) |
| goto error; |
| /* |
| * Let the lifetime of the tree fragment be handled by |
| * the Libxslt's garbage collector. |
| */ |
| xsltRegisterPersistRVT(ctxt, container); |
| |
| oldOutput = ctxt->output; |
| oldInsert = ctxt->insert; |
| |
| oldXPDoc = ctxt->xpathCtxt->doc; |
| |
| ctxt->output = container; |
| ctxt->insert = (xmlNodePtr) container; |
| |
| ctxt->xpathCtxt->doc = ctxt->initialContextDoc; |
| /* |
| * Process the sequence constructor. |
| */ |
| xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); |
| |
| ctxt->xpathCtxt->doc = oldXPDoc; |
| |
| ctxt->insert = oldInsert; |
| ctxt->output = oldOutput; |
| |
| result = xmlXPathNewValueTree((xmlNodePtr) container); |
| if (result == NULL) { |
| result = xmlXPathNewCString(""); |
| } else { |
| result->boolval = 0; /* Freeing is not handled there anymore */ |
| } |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| #ifdef LIBXML_DEBUG_ENABLED |
| if ((xsltGenericDebugContext == stdout) || |
| (xsltGenericDebugContext == stderr)) |
| xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, |
| result, 0); |
| #endif |
| #endif |
| } |
| } |
| |
| error: |
| elem->name = oldVarName; |
| ctxt->inst = oldInst; |
| if (result != NULL) { |
| elem->value = result; |
| elem->computed = 1; |
| } |
| return(result); |
| } |
| |
| /** |
| * xsltEvalGlobalVariables: |
| * @ctxt: the XSLT transformation context |
| * |
| * Evaluates all global variables and parameters of a stylesheet. |
| * For internal use only. This is called at start of a transformation. |
| * |
| * Returns 0 in case of success, -1 in case of error |
| */ |
| int |
| xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { |
| xsltStackElemPtr elem; |
| xsltStylesheetPtr style; |
| |
| if ((ctxt == NULL) || (ctxt->document == NULL)) |
| return(-1); |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Registering global variables\n")); |
| #endif |
| /* |
| * Walk the list from the stylesheets and populate the hash table |
| */ |
| style = ctxt->style; |
| while (style != NULL) { |
| elem = style->variables; |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| if ((style->doc != NULL) && (style->doc->URL != NULL)) { |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Registering global variables from %s\n", |
| style->doc->URL)); |
| } |
| #endif |
| |
| while (elem != NULL) { |
| xsltStackElemPtr def; |
| |
| /* |
| * Global variables are stored in the variables pool. |
| */ |
| def = (xsltStackElemPtr) |
| xmlHashLookup2(ctxt->globalVars, |
| elem->name, elem->nameURI); |
| if (def == NULL) { |
| |
| def = xsltCopyStackElem(elem); |
| xmlHashAddEntry2(ctxt->globalVars, |
| elem->name, elem->nameURI, def); |
| } else if ((elem->comp != NULL) && |
| (elem->comp->type == XSLT_FUNC_VARIABLE)) { |
| /* |
| * Redefinition of variables from a different stylesheet |
| * should not generate a message. |
| */ |
| if ((elem->comp->inst != NULL) && |
| (def->comp != NULL) && (def->comp->inst != NULL) && |
| (elem->comp->inst->doc == def->comp->inst->doc)) |
| { |
| xsltTransformError(ctxt, style, elem->comp->inst, |
| "Global variable %s already defined\n", elem->name); |
| if (style != NULL) style->errors++; |
| } |
| } |
| elem = elem->next; |
| } |
| |
| style = xsltNextImport(style); |
| } |
| |
| /* |
| * This part does the actual evaluation |
| */ |
| xmlHashScan(ctxt->globalVars, |
| (xmlHashScanner) xsltEvalGlobalVariable, ctxt); |
| |
| return(0); |
| } |
| |
| /** |
| * xsltRegisterGlobalVariable: |
| * @style: the XSLT transformation context |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * @sel: the expression which need to be evaluated to generate a value |
| * @tree: the subtree if sel is NULL |
| * @comp: the precompiled value |
| * @value: the string value if available |
| * |
| * Register a new variable value. If @value is NULL it unregisters |
| * the variable |
| * |
| * Returns 0 in case of success, -1 in case of error |
| */ |
| static int |
| xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, |
| const xmlChar *ns_uri, const xmlChar *sel, |
| xmlNodePtr tree, xsltStylePreCompPtr comp, |
| const xmlChar *value) { |
| xsltStackElemPtr elem, tmp; |
| if (style == NULL) |
| return(-1); |
| if (name == NULL) |
| return(-1); |
| if (comp == NULL) |
| return(-1); |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| if (comp->type == XSLT_FUNC_PARAM) |
| xsltGenericDebug(xsltGenericDebugContext, |
| "Defining global param %s\n", name); |
| else |
| xsltGenericDebug(xsltGenericDebugContext, |
| "Defining global variable %s\n", name); |
| #endif |
| |
| elem = xsltNewStackElem(NULL); |
| if (elem == NULL) |
| return(-1); |
| elem->comp = comp; |
| elem->name = xmlDictLookup(style->dict, name, -1); |
| elem->select = xmlDictLookup(style->dict, sel, -1); |
| if (ns_uri) |
| elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); |
| elem->tree = tree; |
| tmp = style->variables; |
| if (tmp == NULL) { |
| elem->next = NULL; |
| style->variables = elem; |
| } else { |
| while (tmp != NULL) { |
| if ((elem->comp->type == XSLT_FUNC_VARIABLE) && |
| (tmp->comp->type == XSLT_FUNC_VARIABLE) && |
| (xmlStrEqual(elem->name, tmp->name)) && |
| ((elem->nameURI == tmp->nameURI) || |
| (xmlStrEqual(elem->nameURI, tmp->nameURI)))) |
| { |
| xsltTransformError(NULL, style, comp->inst, |
| "redefinition of global variable %s\n", elem->name); |
| style->errors++; |
| } |
| if (tmp->next == NULL) |
| break; |
| tmp = tmp->next; |
| } |
| elem->next = NULL; |
| tmp->next = elem; |
| } |
| if (value != NULL) { |
| elem->computed = 1; |
| elem->value = xmlXPathNewString(value); |
| } |
| return(0); |
| } |
| |
| /** |
| * xsltProcessUserParamInternal |
| * |
| * @ctxt: the XSLT transformation context |
| * @name: a null terminated parameter name |
| * @value: a null terminated value (may be an XPath expression) |
| * @eval: 0 to treat the value literally, else evaluate as XPath expression |
| * |
| * If @eval is 0 then @value is treated literally and is stored in the global |
| * parameter/variable table without any change. |
| * |
| * Uf @eval is 1 then @value is treated as an XPath expression and is |
| * evaluated. In this case, if you want to pass a string which will be |
| * interpreted literally then it must be enclosed in single or double quotes. |
| * If the string contains single quotes (double quotes) then it cannot be |
| * enclosed single quotes (double quotes). If the string which you want to |
| * be treated literally contains both single and double quotes (e.g. Meet |
| * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable |
| * quoting character. You cannot use ' or " inside the string |
| * because the replacement of character entities with their equivalents is |
| * done at a different stage of processing. The solution is to call |
| * xsltQuoteUserParams or xsltQuoteOneUserParam. |
| * |
| * This needs to be done on parsed stylesheets before starting to apply |
| * transformations. Normally this will be called (directly or indirectly) |
| * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, |
| * or xsltQuoteOneUserParam. |
| * |
| * Returns 0 in case of success, -1 in case of error |
| */ |
| |
| static |
| int |
| xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, |
| const xmlChar * name, |
| const xmlChar * value, |
| int eval) { |
| |
| xsltStylesheetPtr style; |
| const xmlChar *prefix; |
| const xmlChar *href; |
| xmlXPathCompExprPtr xpExpr; |
| xmlXPathObjectPtr result; |
| |
| xsltStackElemPtr elem; |
| int res; |
| void *res_ptr; |
| |
| if (ctxt == NULL) |
| return(-1); |
| if (name == NULL) |
| return(0); |
| if (value == NULL) |
| return(0); |
| |
| style = ctxt->style; |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Evaluating user parameter %s=%s\n", name, value)); |
| #endif |
| |
| /* |
| * Name lookup |
| */ |
| |
| name = xsltSplitQName(ctxt->dict, name, &prefix); |
| href = NULL; |
| if (prefix != NULL) { |
| xmlNsPtr ns; |
| |
| ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), |
| prefix); |
| if (ns == NULL) { |
| xsltTransformError(ctxt, style, NULL, |
| "user param : no namespace bound to prefix %s\n", prefix); |
| href = NULL; |
| } else { |
| href = ns->href; |
| } |
| } |
| |
| if (name == NULL) |
| return (-1); |
| |
| res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); |
| if (res_ptr != 0) { |
| xsltTransformError(ctxt, style, NULL, |
| "Global parameter %s already defined\n", name); |
| } |
| if (ctxt->globalVars == NULL) |
| ctxt->globalVars = xmlHashCreate(20); |
| |
| /* |
| * do not overwrite variables with parameters from the command line |
| */ |
| while (style != NULL) { |
| elem = ctxt->style->variables; |
| while (elem != NULL) { |
| if ((elem->comp != NULL) && |
| (elem->comp->type == XSLT_FUNC_VARIABLE) && |
| (xmlStrEqual(elem->name, name)) && |
| (xmlStrEqual(elem->nameURI, href))) { |
| return(0); |
| } |
| elem = elem->next; |
| } |
| style = xsltNextImport(style); |
| } |
| style = ctxt->style; |
| elem = NULL; |
| |
| /* |
| * Do the evaluation if @eval is non-zero. |
| */ |
| |
| result = NULL; |
| if (eval != 0) { |
| xpExpr = xmlXPathCompile(value); |
| if (xpExpr != NULL) { |
| xmlDocPtr oldXPDoc; |
| xmlNodePtr oldXPContextNode; |
| int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; |
| xmlNsPtr *oldXPNamespaces; |
| xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; |
| |
| /* |
| * Save context states. |
| */ |
| oldXPDoc = xpctxt->doc; |
| oldXPContextNode = xpctxt->node; |
| oldXPProximityPosition = xpctxt->proximityPosition; |
| oldXPContextSize = xpctxt->contextSize; |
| oldXPNamespaces = xpctxt->namespaces; |
| oldXPNsNr = xpctxt->nsNr; |
| |
| /* |
| * SPEC XSLT 1.0: |
| * "At top-level, the expression or template specifying the |
| * variable value is evaluated with the same context as that used |
| * to process the root node of the source document: the current |
| * node is the root node of the source document and the current |
| * node list is a list containing just the root node of the source |
| * document." |
| */ |
| xpctxt->doc = ctxt->initialContextDoc; |
| xpctxt->node = ctxt->initialContextNode; |
| xpctxt->contextSize = 1; |
| xpctxt->proximityPosition = 1; |
| /* |
| * There is really no in scope namespace for parameters on the |
| * command line. |
| */ |
| xpctxt->namespaces = NULL; |
| xpctxt->nsNr = 0; |
| |
| result = xmlXPathCompiledEval(xpExpr, xpctxt); |
| |
| /* |
| * Restore Context states. |
| */ |
| xpctxt->doc = oldXPDoc; |
| xpctxt->node = oldXPContextNode; |
| xpctxt->contextSize = oldXPContextSize; |
| xpctxt->proximityPosition = oldXPProximityPosition; |
| xpctxt->namespaces = oldXPNamespaces; |
| xpctxt->nsNr = oldXPNsNr; |
| |
| xmlXPathFreeCompExpr(xpExpr); |
| } |
| if (result == NULL) { |
| xsltTransformError(ctxt, style, NULL, |
| "Evaluating user parameter %s failed\n", name); |
| ctxt->state = XSLT_STATE_STOPPED; |
| return(-1); |
| } |
| } |
| |
| /* |
| * If @eval is 0 then @value is to be taken literally and result is NULL |
| * |
| * If @eval is not 0, then @value is an XPath expression and has been |
| * successfully evaluated and result contains the resulting value and |
| * is not NULL. |
| * |
| * Now create an xsltStackElemPtr for insertion into the context's |
| * global variable/parameter hash table. |
| */ |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| #ifdef LIBXML_DEBUG_ENABLED |
| if ((xsltGenericDebugContext == stdout) || |
| (xsltGenericDebugContext == stderr)) |
| xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, |
| result, 0); |
| #endif |
| #endif |
| |
| elem = xsltNewStackElem(NULL); |
| if (elem != NULL) { |
| elem->name = name; |
| elem->select = xmlDictLookup(ctxt->dict, value, -1); |
| if (href != NULL) |
| elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); |
| elem->tree = NULL; |
| elem->computed = 1; |
| if (eval == 0) { |
| elem->value = xmlXPathNewString(value); |
| } |
| else { |
| elem->value = result; |
| } |
| } |
| |
| /* |
| * Global parameters are stored in the XPath context variables pool. |
| */ |
| |
| res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); |
| if (res != 0) { |
| xsltFreeStackElem(elem); |
| xsltTransformError(ctxt, style, NULL, |
| "Global parameter %s already defined\n", name); |
| } |
| return(0); |
| } |
| |
| /** |
| * xsltEvalUserParams: |
| * |
| * @ctxt: the XSLT transformation context |
| * @params: a NULL terminated array of parameters name/value tuples |
| * |
| * Evaluate the global variables of a stylesheet. This needs to be |
| * done on parsed stylesheets before starting to apply transformations. |
| * Each of the parameters is evaluated as an XPath expression and stored |
| * in the global variables/parameter hash table. If you want your |
| * parameter used literally, use xsltQuoteUserParams. |
| * |
| * Returns 0 in case of success, -1 in case of error |
| */ |
| |
| int |
| xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { |
| int indx = 0; |
| const xmlChar *name; |
| const xmlChar *value; |
| |
| if (params == NULL) |
| return(0); |
| while (params[indx] != NULL) { |
| name = (const xmlChar *) params[indx++]; |
| value = (const xmlChar *) params[indx++]; |
| if (xsltEvalOneUserParam(ctxt, name, value) != 0) |
| return(-1); |
| } |
| return 0; |
| } |
| |
| /** |
| * xsltQuoteUserParams: |
| * |
| * @ctxt: the XSLT transformation context |
| * @params: a NULL terminated arry of parameters names/values tuples |
| * |
| * Similar to xsltEvalUserParams, but the values are treated literally and |
| * are * *not* evaluated as XPath expressions. This should be done on parsed |
| * stylesheets before starting to apply transformations. |
| * |
| * Returns 0 in case of success, -1 in case of error. |
| */ |
| |
| int |
| xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { |
| int indx = 0; |
| const xmlChar *name; |
| const xmlChar *value; |
| |
| if (params == NULL) |
| return(0); |
| while (params[indx] != NULL) { |
| name = (const xmlChar *) params[indx++]; |
| value = (const xmlChar *) params[indx++]; |
| if (xsltQuoteOneUserParam(ctxt, name, value) != 0) |
| return(-1); |
| } |
| return 0; |
| } |
| |
| /** |
| * xsltEvalOneUserParam: |
| * @ctxt: the XSLT transformation context |
| * @name: a null terminated string giving the name of the parameter |
| * @value: a null terminated string giving the XPath expression to be evaluated |
| * |
| * This is normally called from xsltEvalUserParams to process a single |
| * parameter from a list of parameters. The @value is evaluated as an |
| * XPath expression and the result is stored in the context's global |
| * variable/parameter hash table. |
| * |
| * To have a parameter treated literally (not as an XPath expression) |
| * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more |
| * details see description of xsltProcessOneUserParamInternal. |
| * |
| * Returns 0 in case of success, -1 in case of error. |
| */ |
| |
| int |
| xsltEvalOneUserParam(xsltTransformContextPtr ctxt, |
| const xmlChar * name, |
| const xmlChar * value) { |
| return xsltProcessUserParamInternal(ctxt, name, value, |
| 1 /* xpath eval ? */); |
| } |
| |
| /** |
| * xsltQuoteOneUserParam: |
| * @ctxt: the XSLT transformation context |
| * @name: a null terminated string giving the name of the parameter |
| * @value: a null terminated string giving the parameter value |
| * |
| * This is normally called from xsltQuoteUserParams to process a single |
| * parameter from a list of parameters. The @value is stored in the |
| * context's global variable/parameter hash table. |
| * |
| * Returns 0 in case of success, -1 in case of error. |
| */ |
| |
| int |
| xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, |
| const xmlChar * name, |
| const xmlChar * value) { |
| return xsltProcessUserParamInternal(ctxt, name, value, |
| 0 /* xpath eval ? */); |
| } |
| |
| /** |
| * xsltBuildVariable: |
| * @ctxt: the XSLT transformation context |
| * @comp: the precompiled form |
| * @tree: the tree if select is NULL |
| * |
| * Computes a new variable value. |
| * |
| * Returns the xsltStackElemPtr or NULL in case of error |
| */ |
| static xsltStackElemPtr |
| xsltBuildVariable(xsltTransformContextPtr ctxt, |
| xsltStylePreCompPtr castedComp, |
| xmlNodePtr tree) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleBasicItemVariablePtr comp = |
| (xsltStyleBasicItemVariablePtr) castedComp; |
| #else |
| xsltStylePreCompPtr comp = castedComp; |
| #endif |
| xsltStackElemPtr elem; |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Building variable %s", comp->name)); |
| if (comp->select != NULL) |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| " select %s", comp->select)); |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n")); |
| #endif |
| |
| elem = xsltNewStackElem(ctxt); |
| if (elem == NULL) |
| return(NULL); |
| elem->comp = (xsltStylePreCompPtr) comp; |
| elem->name = comp->name; |
| elem->select = comp->select; |
| elem->nameURI = comp->ns; |
| elem->tree = tree; |
| elem->value = xsltEvalVariable(ctxt, elem, |
| (xsltStylePreCompPtr) comp); |
| if (elem->value != NULL) |
| elem->computed = 1; |
| return(elem); |
| } |
| |
| /** |
| * xsltRegisterVariable: |
| * @ctxt: the XSLT transformation context |
| * @comp: the compiled XSLT-variable (or param) instruction |
| * @tree: the tree if select is NULL |
| * @isParam: indicates if this is a parameter |
| * |
| * Computes and registers a new variable. |
| * |
| * Returns 0 in case of success, -1 in case of error |
| */ |
| static int |
| xsltRegisterVariable(xsltTransformContextPtr ctxt, |
| xsltStylePreCompPtr castedComp, |
| xmlNodePtr tree, int isParam) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleBasicItemVariablePtr comp = |
| (xsltStyleBasicItemVariablePtr) castedComp; |
| #else |
| xsltStylePreCompPtr comp = castedComp; |
| int present; |
| #endif |
| xsltStackElemPtr variable; |
| |
| #ifdef XSLT_REFACTORED |
| /* |
| * REFACTORED NOTE: Redefinitions of vars/params are checked |
| * at compilation time in the refactored code. |
| * xsl:with-param parameters are checked in xsltApplyXSLTTemplate(). |
| */ |
| #else |
| present = xsltCheckStackElem(ctxt, comp->name, comp->ns); |
| if (isParam == 0) { |
| if ((present != 0) && (present != 3)) { |
| /* TODO: report QName. */ |
| xsltTransformError(ctxt, NULL, comp->inst, |
| "XSLT-variable: Redefinition of variable '%s'.\n", comp->name); |
| return(0); |
| } |
| } else if (present != 0) { |
| if ((present == 1) || (present == 2)) { |
| /* TODO: report QName. */ |
| xsltTransformError(ctxt, NULL, comp->inst, |
| "XSLT-param: Redefinition of parameter '%s'.\n", comp->name); |
| return(0); |
| } |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "param %s defined by caller\n", comp->name)); |
| #endif |
| return(0); |
| } |
| #endif /* else of XSLT_REFACTORED */ |
| |
| variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); |
| xsltAddStackElem(ctxt, variable); |
| return(0); |
| } |
| |
| /** |
| * xsltGlobalVariableLookup: |
| * @ctxt: the XSLT transformation context |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * |
| * Search in the Variable array of the context for the given |
| * variable value. |
| * |
| * Returns the value or NULL if not found |
| */ |
| static xmlXPathObjectPtr |
| xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, |
| const xmlChar *ns_uri) { |
| xsltStackElemPtr elem; |
| xmlXPathObjectPtr ret = NULL; |
| |
| /* |
| * Lookup the global variables in XPath global variable hash table |
| */ |
| if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) |
| return(NULL); |
| elem = (xsltStackElemPtr) |
| xmlHashLookup2(ctxt->globalVars, name, ns_uri); |
| if (elem == NULL) { |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "global variable not found %s\n", name)); |
| #endif |
| return(NULL); |
| } |
| /* |
| * URGENT TODO: Move the detection of recursive definitions |
| * to compile-time. |
| */ |
| if (elem->computed == 0) { |
| if (elem->name == xsltComputingGlobalVarMarker) { |
| xsltTransformError(ctxt, NULL, elem->comp->inst, |
| "Recursive definition of %s\n", name); |
| return(NULL); |
| } |
| ret = xsltEvalGlobalVariable(elem, ctxt); |
| } else |
| ret = elem->value; |
| return(xmlXPathObjectCopy(ret)); |
| } |
| |
| /** |
| * xsltVariableLookup: |
| * @ctxt: the XSLT transformation context |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * |
| * Search in the Variable array of the context for the given |
| * variable value. |
| * |
| * Returns the value or NULL if not found |
| */ |
| xmlXPathObjectPtr |
| xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, |
| const xmlChar *ns_uri) { |
| xsltStackElemPtr elem; |
| |
| if (ctxt == NULL) |
| return(NULL); |
| |
| elem = xsltStackLookup(ctxt, name, ns_uri); |
| if (elem == NULL) { |
| return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); |
| } |
| if (elem->computed == 0) { |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "uncomputed variable %s\n", name)); |
| #endif |
| elem->value = xsltEvalVariable(ctxt, elem, NULL); |
| elem->computed = 1; |
| } |
| if (elem->value != NULL) |
| return(xmlXPathObjectCopy(elem->value)); |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "variable not found %s\n", name)); |
| #endif |
| return(NULL); |
| } |
| |
| /** |
| * xsltParseStylesheetCallerParam: |
| * @ctxt: the XSLT transformation context |
| * @inst: the xsl:with-param instruction element |
| * |
| * Processes an xsl:with-param instruction at transformation time. |
| * The value is compute, but not recorded. |
| * NOTE that this is also called with an *xsl:param* element |
| * from exsltFuncFunctionFunction(). |
| * |
| * Returns the new xsltStackElemPtr or NULL |
| */ |
| |
| xsltStackElemPtr |
| xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleBasicItemVariablePtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| xmlNodePtr tree = NULL; /* The first child node of the instruction or |
| the instruction itself. */ |
| xsltStackElemPtr param = NULL; |
| |
| if ((ctxt == NULL) || (inst == NULL)) |
| return(NULL); |
| |
| #ifdef XSLT_REFACTORED |
| comp = (xsltStyleBasicItemVariablePtr) inst->psvi; |
| #else |
| comp = (xsltStylePreCompPtr) inst->psvi; |
| #endif |
| |
| if (comp == NULL) { |
| xsltTransformError(ctxt, NULL, inst, |
| "Internal error in xsltParseStylesheetCallerParam(): " |
| "The XSLT 'with-param' instruction was not compiled.\n"); |
| return(NULL); |
| } |
| if (comp->name == NULL) { |
| xsltTransformError(ctxt, NULL, inst, |
| "Internal error in xsltParseStylesheetCallerParam(): " |
| "XSLT 'with-param': The attribute 'name' was not compiled.\n"); |
| return(NULL); |
| } |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Handling xsl:with-param %s\n", comp->name)); |
| #endif |
| |
| if (comp->select == NULL) { |
| tree = inst->children; |
| } else { |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| " select %s\n", comp->select)); |
| #endif |
| tree = inst; |
| } |
| |
| param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); |
| |
| return(param); |
| } |
| |
| /** |
| * xsltParseGlobalVariable: |
| * @style: the XSLT stylesheet |
| * @cur: the "variable" element |
| * |
| * Parses a global XSLT 'variable' declaration at compilation time |
| * and registers it |
| */ |
| void |
| xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemVariablePtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| |
| if ((cur == NULL) || (style == NULL)) |
| return; |
| |
| #ifdef XSLT_REFACTORED |
| /* |
| * Note that xsltStylePreCompute() will be called from |
| * xslt.c only. |
| */ |
| comp = (xsltStyleItemVariablePtr) cur->psvi; |
| #else |
| xsltStylePreCompute(style, cur); |
| comp = (xsltStylePreCompPtr) cur->psvi; |
| #endif |
| if (comp == NULL) { |
| xsltTransformError(NULL, style, cur, |
| "xsl:variable : compilation failed\n"); |
| return; |
| } |
| |
| if (comp->name == NULL) { |
| xsltTransformError(NULL, style, cur, |
| "xsl:variable : missing name attribute\n"); |
| return; |
| } |
| |
| /* |
| * Parse the content (a sequence constructor) of xsl:variable. |
| */ |
| if (cur->children != NULL) { |
| #ifdef XSLT_REFACTORED |
| xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); |
| #else |
| xsltParseTemplateContent(style, cur); |
| #endif |
| } |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| xsltGenericDebug(xsltGenericDebugContext, |
| "Registering global variable %s\n", comp->name); |
| #endif |
| |
| xsltRegisterGlobalVariable(style, comp->name, comp->ns, |
| comp->select, cur->children, (xsltStylePreCompPtr) comp, |
| NULL); |
| } |
| |
| /** |
| * xsltParseGlobalParam: |
| * @style: the XSLT stylesheet |
| * @cur: the "param" element |
| * |
| * parse an XSLT transformation param declaration and record |
| * its value. |
| */ |
| |
| void |
| xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemParamPtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| |
| if ((cur == NULL) || (style == NULL)) |
| return; |
| |
| #ifdef XSLT_REFACTORED |
| /* |
| * Note that xsltStylePreCompute() will be called from |
| * xslt.c only. |
| */ |
| comp = (xsltStyleItemParamPtr) cur->psvi; |
| #else |
| xsltStylePreCompute(style, cur); |
| comp = (xsltStylePreCompPtr) cur->psvi; |
| #endif |
| if (comp == NULL) { |
| xsltTransformError(NULL, style, cur, |
| "xsl:param : compilation failed\n"); |
| return; |
| } |
| |
| if (comp->name == NULL) { |
| xsltTransformError(NULL, style, cur, |
| "xsl:param : missing name attribute\n"); |
| return; |
| } |
| |
| /* |
| * Parse the content (a sequence constructor) of xsl:param. |
| */ |
| if (cur->children != NULL) { |
| #ifdef XSLT_REFACTORED |
| xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); |
| #else |
| xsltParseTemplateContent(style, cur); |
| #endif |
| } |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| xsltGenericDebug(xsltGenericDebugContext, |
| "Registering global param %s\n", comp->name); |
| #endif |
| |
| xsltRegisterGlobalVariable(style, comp->name, comp->ns, |
| comp->select, cur->children, (xsltStylePreCompPtr) comp, |
| NULL); |
| } |
| |
| /** |
| * xsltParseStylesheetVariable: |
| * @ctxt: the XSLT transformation context |
| * @inst: the xsl:variable instruction element |
| * |
| * Registers a local XSLT 'variable' instruction at transformation time |
| * and evaluates its value. |
| */ |
| void |
| xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemVariablePtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| |
| if ((inst == NULL) || (ctxt == NULL)) |
| return; |
| |
| comp = inst->psvi; |
| if (comp == NULL) { |
| xsltTransformError(ctxt, NULL, inst, |
| "Internal error in xsltParseStylesheetVariable(): " |
| "The XSLT 'variable' instruction was not compiled.\n"); |
| return; |
| } |
| if (comp->name == NULL) { |
| xsltTransformError(ctxt, NULL, inst, |
| "Internal error in xsltParseStylesheetVariable(): " |
| "The attribute 'name' was not compiled.\n"); |
| return; |
| } |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Registering variable '%s'\n", comp->name)); |
| #endif |
| |
| xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0); |
| } |
| |
| /** |
| * xsltParseStylesheetParam: |
| * @ctxt: the XSLT transformation context |
| * @cur: the XSLT 'param' element |
| * |
| * Registers a local XSLT 'param' declaration at transformation time and |
| * evaluates its value. |
| */ |
| void |
| xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) |
| { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemParamPtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| |
| if ((cur == NULL) || (ctxt == NULL)) |
| return; |
| |
| comp = cur->psvi; |
| if ((comp == NULL) || (comp->name == NULL)) { |
| xsltTransformError(ctxt, NULL, cur, |
| "Internal error in xsltParseStylesheetParam(): " |
| "The XSLT 'param' declaration was not compiled correctly.\n"); |
| return; |
| } |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Registering param %s\n", comp->name)); |
| #endif |
| |
| xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); |
| } |
| |
| /** |
| * xsltFreeGlobalVariables: |
| * @ctxt: the XSLT transformation context |
| * |
| * Free up the data associated to the global variables |
| * its value. |
| */ |
| |
| void |
| xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { |
| xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem); |
| } |
| |
| /** |
| * xsltXPathVariableLookup: |
| * @ctxt: a void * but the the XSLT transformation context actually |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * |
| * This is the entry point when a varibale is needed by the XPath |
| * interpretor. |
| * |
| * Returns the value or NULL if not found |
| */ |
| xmlXPathObjectPtr |
| xsltXPathVariableLookup(void *ctxt, const xmlChar *name, |
| const xmlChar *ns_uri) { |
| xsltTransformContextPtr tctxt; |
| xmlXPathObjectPtr valueObj = NULL; |
| |
| if ((ctxt == NULL) || (name == NULL)) |
| return(NULL); |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "Lookup variable '%s'\n", name)); |
| #endif |
| |
| tctxt = (xsltTransformContextPtr) ctxt; |
| /* |
| * Local variables/params --------------------------------------------- |
| * |
| * Do the lookup from the top of the stack, but |
| * don't use params being computed in a call-param |
| * First lookup expects the variable name and URI to |
| * come from the disctionnary and hence pointer comparison. |
| */ |
| if (tctxt->varsNr != 0) { |
| int i; |
| xsltStackElemPtr variable = NULL, cur; |
| |
| for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { |
| cur = tctxt->varsTab[i-1]; |
| if ((cur->name == name) && (cur->nameURI == ns_uri)) { |
| #if 0 |
| stack_addr++; |
| #endif |
| variable = cur; |
| goto local_variable_found; |
| } |
| cur = cur->next; |
| } |
| /* |
| * Redo the lookup with interned strings to avoid string comparison. |
| * |
| * OPTIMIZE TODO: The problem here is, that if we request a |
| * global variable, then this will be also executed. |
| */ |
| { |
| const xmlChar *tmpName = name, *tmpNsName = ns_uri; |
| |
| name = xmlDictLookup(tctxt->dict, name, -1); |
| if (ns_uri) |
| ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1); |
| if ((tmpName != name) || (tmpNsName != ns_uri)) { |
| for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { |
| cur = tctxt->varsTab[i-1]; |
| if ((cur->name == name) && (cur->nameURI == ns_uri)) { |
| #if 0 |
| stack_cmp++; |
| #endif |
| variable = cur; |
| goto local_variable_found; |
| } |
| } |
| } |
| } |
| |
| local_variable_found: |
| |
| if (variable) { |
| if (variable->computed == 0) { |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "uncomputed variable '%s'\n", name)); |
| #endif |
| variable->value = xsltEvalVariable(tctxt, variable, NULL); |
| variable->computed = 1; |
| } |
| if (variable->value != NULL) { |
| valueObj = xmlXPathObjectCopy(variable->value); |
| } |
| return(valueObj); |
| } |
| } |
| /* |
| * Global variables/params -------------------------------------------- |
| */ |
| if (tctxt->globalVars) { |
| valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri); |
| } |
| |
| if (valueObj == NULL) { |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "variable not found '%s'\n", name)); |
| #endif |
| |
| if (ns_uri) { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "Variable '{%s}%s' has not been declared.\n", ns_uri, name); |
| } else { |
| xsltTransformError(tctxt, NULL, tctxt->inst, |
| "Variable '%s' has not been declared.\n", name); |
| } |
| } else { |
| |
| #ifdef WITH_XSLT_DEBUG_VARIABLE |
| XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, |
| "found variable '%s'\n", name)); |
| #endif |
| } |
| |
| return(valueObj); |
| } |
| |
| |