| #define IN_LIBEXSLT |
| #include "libexslt/libexslt.h" |
| |
| #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) |
| #include <win32config.h> |
| #else |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| |
| #include <libxml/tree.h> |
| #include <libxml/xpath.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/hash.h> |
| #include <libxml/debugXML.h> |
| |
| #include <libxslt/xsltutils.h> |
| #include <libxslt/variables.h> |
| #include <libxslt/xsltInternals.h> |
| #include <libxslt/extensions.h> |
| #include <libxslt/transform.h> |
| #include <libxslt/imports.h> |
| |
| #include "exslt.h" |
| |
| typedef struct _exsltFuncFunctionData exsltFuncFunctionData; |
| struct _exsltFuncFunctionData { |
| int nargs; /* number of arguments to the function */ |
| xmlNodePtr content; /* the func:fuction template content */ |
| }; |
| |
| typedef struct _exsltFuncData exsltFuncData; |
| struct _exsltFuncData { |
| xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ |
| xmlXPathObjectPtr result; /* returned by func:result */ |
| int error; /* did an error occur? */ |
| xmlDocPtr RVT; /* result tree fragment */ |
| }; |
| |
| typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; |
| struct _exsltFuncResultPreComp { |
| xsltElemPreComp comp; |
| xmlXPathCompExprPtr select; |
| xmlNsPtr *nsList; |
| int nsNr; |
| }; |
| |
| /* Used for callback function in exsltInitFunc */ |
| typedef struct _exsltFuncImportRegData exsltFuncImportRegData; |
| struct _exsltFuncImportRegData { |
| xsltTransformContextPtr ctxt; |
| xmlHashTablePtr hash; |
| }; |
| |
| static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, |
| int nargs); |
| static exsltFuncFunctionData *exsltFuncNewFunctionData(void); |
| |
| #define MAX_FUNC_RECURSION 1000 |
| |
| /*static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result";*/ |
| |
| /** |
| * exsltFuncRegisterFunc: |
| * @func: the #exsltFuncFunctionData for the function |
| * @ctxt: an XSLT transformation context |
| * @URI: the function namespace URI |
| * @name: the function name |
| * |
| * Registers a function declared by a func:function element |
| */ |
| static void |
| exsltFuncRegisterFunc (exsltFuncFunctionData *data, |
| xsltTransformContextPtr ctxt, |
| const xmlChar *URI, const xmlChar *name, |
| ATTRIBUTE_UNUSED const xmlChar *ignored) { |
| if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) |
| return; |
| |
| xsltGenericDebug(xsltGenericDebugContext, |
| "exsltFuncRegisterFunc: register {%s}%s\n", |
| URI, name); |
| xsltRegisterExtFunction(ctxt, name, URI, |
| exsltFuncFunctionFunction); |
| } |
| |
| /* |
| * exsltFuncRegisterImportFunc |
| * @data: the exsltFuncFunctionData for the function |
| * @ch: structure containing context and hash table |
| * @URI: the function namespace URI |
| * @name: the function name |
| * |
| * Checks if imported function is already registered in top-level |
| * stylesheet. If not, copies function data and registers function |
| */ |
| static void |
| exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, |
| exsltFuncImportRegData *ch, |
| const xmlChar *URI, const xmlChar *name, |
| ATTRIBUTE_UNUSED const xmlChar *ignored) { |
| exsltFuncFunctionData *func=NULL; |
| |
| if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) |
| return; |
| |
| if (ch->ctxt == NULL || ch->hash == NULL) |
| return; |
| |
| /* Check if already present */ |
| func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); |
| if (func == NULL) { /* Not yet present - copy it in */ |
| func = exsltFuncNewFunctionData(); |
| memcpy(func, data, sizeof(exsltFuncFunctionData)); |
| if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { |
| xsltGenericError(xsltGenericErrorContext, |
| "Failed to register function {%s}%s\n", |
| URI, name); |
| } else { /* Do the registration */ |
| xsltGenericDebug(xsltGenericDebugContext, |
| "exsltFuncRegisterImportFunc: register {%s}%s\n", |
| URI, name); |
| xsltRegisterExtFunction(ch->ctxt, name, URI, |
| exsltFuncFunctionFunction); |
| } |
| } |
| } |
| |
| /** |
| * exsltFuncInit: |
| * @ctxt: an XSLT transformation context |
| * @URI: the namespace URI for the extension |
| * |
| * Initializes the EXSLT - Functions module. |
| * Called at transformation-time; merges all |
| * functions declared in the import tree taking |
| * import precedence into account, i.e. overriding |
| * functions with lower import precedence. |
| * |
| * Returns the data for this transformation |
| */ |
| static exsltFuncData * |
| exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) { |
| exsltFuncData *ret; |
| xsltStylesheetPtr tmp; |
| exsltFuncImportRegData ch; |
| xmlHashTablePtr hash; |
| |
| ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData)); |
| if (ret == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncInit: not enough memory\n"); |
| return(NULL); |
| } |
| memset(ret, 0, sizeof(exsltFuncData)); |
| |
| ret->result = NULL; |
| ret->error = 0; |
| |
| ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI); |
| ret->funcs = ch.hash; |
| xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt); |
| tmp = ctxt->style; |
| ch.ctxt = ctxt; |
| while ((tmp=xsltNextImport(tmp))!=NULL) { |
| hash = xsltGetExtInfo(tmp, URI); |
| if (hash != NULL) { |
| xmlHashScanFull(hash, |
| (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch); |
| } |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * exsltFuncShutdown: |
| * @ctxt: an XSLT transformation context |
| * @URI: the namespace URI for the extension |
| * @data: the module data to free up |
| * |
| * Shutdown the EXSLT - Functions module |
| * Called at transformation-time. |
| */ |
| static void |
| exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, |
| const xmlChar *URI ATTRIBUTE_UNUSED, |
| exsltFuncData *data) { |
| if (data->result != NULL) |
| xmlXPathFreeObject(data->result); |
| xmlFree(data); |
| } |
| |
| /** |
| * exsltFuncStyleInit: |
| * @style: an XSLT stylesheet |
| * @URI: the namespace URI for the extension |
| * |
| * Allocates the stylesheet data for EXSLT - Function |
| * Called at compile-time. |
| * |
| * Returns the allocated data |
| */ |
| static xmlHashTablePtr |
| exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED, |
| const xmlChar *URI ATTRIBUTE_UNUSED) { |
| return xmlHashCreate(1); |
| } |
| |
| /** |
| * exsltFuncStyleShutdown: |
| * @style: an XSLT stylesheet |
| * @URI: the namespace URI for the extension |
| * @data: the stylesheet data to free up |
| * |
| * Shutdown the EXSLT - Function module |
| * Called at compile-time. |
| */ |
| static void |
| exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED, |
| const xmlChar *URI ATTRIBUTE_UNUSED, |
| xmlHashTablePtr data) { |
| xmlHashFree(data, (xmlHashDeallocator) xmlFree); |
| } |
| |
| /** |
| * exsltFuncNewFunctionData: |
| * |
| * Allocates an #exslFuncFunctionData object |
| * |
| * Returns the new structure |
| */ |
| static exsltFuncFunctionData * |
| exsltFuncNewFunctionData (void) { |
| exsltFuncFunctionData *ret; |
| |
| ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData)); |
| if (ret == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncNewFunctionData: not enough memory\n"); |
| return (NULL); |
| } |
| memset(ret, 0, sizeof(exsltFuncFunctionData)); |
| |
| ret->nargs = 0; |
| ret->content = NULL; |
| |
| return(ret); |
| } |
| |
| /** |
| * exsltFreeFuncResultPreComp: |
| * @comp: the #exsltFuncResultPreComp to free up |
| * |
| * Deallocates an #exsltFuncResultPreComp |
| */ |
| static void |
| exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) { |
| if (comp == NULL) |
| return; |
| |
| if (comp->select != NULL) |
| xmlXPathFreeCompExpr (comp->select); |
| if (comp->nsList != NULL) |
| xmlFree(comp->nsList); |
| xmlFree(comp); |
| } |
| |
| /** |
| * exsltFuncFunctionFunction: |
| * @ctxt: an XPath parser context |
| * @nargs: the number of arguments |
| * |
| * Evaluates the func:function element that defines the called function. |
| */ |
| static void |
| exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) { |
| xmlXPathObjectPtr oldResult, ret; |
| exsltFuncData *data; |
| exsltFuncFunctionData *func; |
| xmlNodePtr paramNode, oldInsert, fake; |
| int oldBase; |
| xsltStackElemPtr params = NULL, param; |
| xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); |
| int i, notSet; |
| struct objChain { |
| struct objChain *next; |
| xmlXPathObjectPtr obj; |
| }; |
| struct objChain *savedObjChain = NULL, *savedObj; |
| |
| /* |
| * retrieve func:function template |
| */ |
| data = (exsltFuncData *) xsltGetExtData (tctxt, |
| EXSLT_FUNCTIONS_NAMESPACE); |
| oldResult = data->result; |
| data->result = NULL; |
| |
| func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs, |
| ctxt->context->functionURI, |
| ctxt->context->function); |
| |
| /* |
| * params handling |
| */ |
| if (nargs > func->nargs) { |
| xsltGenericError(xsltGenericErrorContext, |
| "{%s}%s: called with too many arguments\n", |
| ctxt->context->functionURI, ctxt->context->function); |
| ctxt->error = XPATH_INVALID_ARITY; |
| return; |
| } |
| if (func->content != NULL) { |
| paramNode = func->content->prev; |
| } |
| else |
| paramNode = NULL; |
| if ((paramNode == NULL) && (func->nargs != 0)) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncFunctionFunction: nargs != 0 and " |
| "param == NULL\n"); |
| return; |
| } |
| if (tctxt->funcLevel > MAX_FUNC_RECURSION) { |
| xsltGenericError(xsltGenericErrorContext, |
| "{%s}%s: detected a recursion\n", |
| ctxt->context->functionURI, ctxt->context->function); |
| ctxt->error = XPATH_MEMORY_ERROR; |
| return; |
| } |
| tctxt->funcLevel++; |
| |
| /* |
| * We have a problem with the evaluation of function parameters. |
| * The original library code did not evaluate XPath expressions until |
| * the last moment. After version 1.1.17 of the libxslt, the logic |
| * of other parts of the library was changed, and the evaluation of |
| * XPath expressions within parameters now takes place as soon as the |
| * parameter is parsed/evaluated (xsltParseStylesheetCallerParam). |
| * This means that the parameters need to be evaluated in lexical |
| * order (since a variable is "in scope" as soon as it is declared). |
| * However, on entry to this routine, the values (from the caller) are |
| * in reverse order (held on the XPath context variable stack). To |
| * accomplish what is required, I have added code to pop the XPath |
| * objects off of the stack at the beginning and save them, then use |
| * them (in the reverse order) as the params are evaluated. This |
| * requires an xmlMalloc/xmlFree for each param set by the caller, |
| * which is not very nice. There is probably a much better solution |
| * (like change other code to delay the evaluation). |
| */ |
| /* |
| * In order to give the function params and variables a new 'scope' |
| * we change varsBase in the context. |
| */ |
| oldBase = tctxt->varsBase; |
| tctxt->varsBase = tctxt->varsNr; |
| /* If there are any parameters */ |
| if (paramNode != NULL) { |
| /* Fetch the stored argument values from the caller */ |
| for (i = 0; i < nargs; i++) { |
| savedObj = xmlMalloc(sizeof(struct objChain)); |
| savedObj->next = savedObjChain; |
| savedObj->obj = valuePop(ctxt); |
| savedObjChain = savedObj; |
| } |
| |
| /* |
| * Prepare to process params in reverse order. First, go to |
| * the beginning of the param chain. |
| */ |
| for (i = 1; i <= func->nargs; i++) { |
| if (paramNode->prev == NULL) |
| break; |
| paramNode = paramNode->prev; |
| } |
| /* |
| * i has total # params found, nargs is number which are present |
| * as arguments from the caller |
| * Calculate the number of un-set parameters |
| */ |
| notSet = func->nargs - nargs; |
| for (; i > 0; i--) { |
| param = xsltParseStylesheetCallerParam (tctxt, paramNode); |
| if (i > notSet) { /* if parameter value set */ |
| param->computed = 1; |
| if (param->value != NULL) |
| xmlXPathFreeObject(param->value); |
| savedObj = savedObjChain; /* get next val from chain */ |
| param->value = savedObj->obj; |
| savedObjChain = savedObjChain->next; |
| xmlFree(savedObj); |
| } |
| xsltLocalVariablePush(tctxt, param, -1); |
| param->next = params; |
| params = param; |
| paramNode = paramNode->next; |
| } |
| } |
| /* |
| * actual processing |
| */ |
| fake = xmlNewDocNode(tctxt->output, NULL, |
| (const xmlChar *)"fake", NULL); |
| oldInsert = tctxt->insert; |
| tctxt->insert = fake; |
| xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), |
| func->content, NULL, NULL); |
| xsltLocalVariablePop(tctxt, tctxt->varsBase, -2); |
| tctxt->insert = oldInsert; |
| tctxt->varsBase = oldBase; /* restore original scope */ |
| if (params != NULL) |
| xsltFreeStackElemList(params); |
| |
| if (data->error != 0) |
| goto error; |
| |
| if (data->result != NULL) { |
| ret = data->result; |
| } else |
| ret = xmlXPathNewCString(""); |
| |
| data->result = oldResult; |
| |
| /* |
| * It is an error if the instantiation of the template results in |
| * the generation of result nodes. |
| */ |
| if (fake->children != NULL) { |
| #ifdef LIBXML_DEBUG_ENABLED |
| xmlDebugDumpNode (stderr, fake, 1); |
| #endif |
| xsltGenericError(xsltGenericErrorContext, |
| "{%s}%s: cannot write to result tree while " |
| "executing a function\n", |
| ctxt->context->functionURI, ctxt->context->function); |
| xmlFreeNode(fake); |
| goto error; |
| } |
| xmlFreeNode(fake); |
| valuePush(ctxt, ret); |
| |
| error: |
| /* |
| * IMPORTANT: This enables previously tree fragments marked as |
| * being results of a function, to be garbage-collected after |
| * the calling process exits. |
| */ |
| xsltExtensionInstructionResultFinalize(tctxt); |
| tctxt->funcLevel--; |
| } |
| |
| |
| static void |
| exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) { |
| xmlChar *name, *prefix; |
| xmlNsPtr ns; |
| xmlHashTablePtr data; |
| exsltFuncFunctionData *func; |
| |
| if ((style == NULL) || (inst == NULL)) |
| return; |
| |
| |
| { |
| xmlChar *qname; |
| |
| qname = xmlGetProp(inst, (const xmlChar *) "name"); |
| name = xmlSplitQName2 (qname, &prefix); |
| xmlFree(qname); |
| } |
| if ((name == NULL) || (prefix == NULL)) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:function: not a QName\n"); |
| if (name != NULL) |
| xmlFree(name); |
| return; |
| } |
| /* namespace lookup */ |
| ns = xmlSearchNs (inst->doc, inst, prefix); |
| if (ns == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:function: undeclared prefix %s\n", |
| prefix); |
| xmlFree(name); |
| xmlFree(prefix); |
| return; |
| } |
| xmlFree(prefix); |
| |
| /* |
| * Create function data |
| */ |
| func = exsltFuncNewFunctionData(); |
| func->content = inst->children; |
| while (IS_XSLT_ELEM(func->content) && |
| IS_XSLT_NAME(func->content, "param")) { |
| func->content = func->content->next; |
| func->nargs++; |
| } |
| |
| xsltParseTemplateContent(style, inst); |
| |
| /* |
| * Register the function data such that it can be retrieved |
| * by exslFuncFunctionFunction |
| */ |
| #ifdef XSLT_REFACTORED |
| /* |
| * Ensure that the hash table will be stored in the *current* |
| * stylesheet level in order to correctly evaluate the |
| * import precedence. |
| */ |
| data = (xmlHashTablePtr) |
| xsltStyleStylesheetLevelGetExtData(style, |
| EXSLT_FUNCTIONS_NAMESPACE); |
| #else |
| data = (xmlHashTablePtr) |
| xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE); |
| #endif |
| if (data == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncFunctionComp: no stylesheet data\n"); |
| xmlFree(name); |
| return; |
| } |
| |
| if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) { |
| xsltTransformError(NULL, style, inst, |
| "Failed to register function {%s}%s\n", |
| ns->href, name); |
| style->errors++; |
| } else { |
| xsltGenericDebug(xsltGenericDebugContext, |
| "exsltFuncFunctionComp: register {%s}%s\n", |
| ns->href, name); |
| } |
| xmlFree(name); |
| } |
| |
| static xsltElemPreCompPtr |
| exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst, |
| xsltTransformFunction function) { |
| xmlNodePtr test; |
| xmlChar *sel; |
| exsltFuncResultPreComp *ret; |
| |
| /* |
| * "Validity" checking |
| */ |
| /* it is an error to have any following sibling elements aside |
| * from the xsl:fallback element. |
| */ |
| for (test = inst->next; test != NULL; test = test->next) { |
| if (test->type != XML_ELEMENT_NODE) |
| continue; |
| if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback")) |
| continue; |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncResultElem: only xsl:fallback is " |
| "allowed to follow func:result\n"); |
| return (NULL); |
| } |
| /* it is an error for a func:result element to not be a descendant |
| * of func:function. |
| * it is an error if a func:result occurs within a func:result |
| * element. |
| * it is an error if instanciating the content of a variable |
| * binding element (i.e. xsl:variable, xsl:param) results in the |
| * instanciation of a func:result element. |
| */ |
| for (test = inst->parent; test != NULL; test = test->parent) { |
| if (IS_XSLT_ELEM(test) && |
| IS_XSLT_NAME(test, "stylesheet")) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:result element not a descendant " |
| "of a func:function\n"); |
| return (NULL); |
| } |
| if ((test->ns != NULL) && |
| (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) { |
| if (xmlStrEqual(test->name, (const xmlChar *) "function")) { |
| break; |
| } |
| if (xmlStrEqual(test->name, (const xmlChar *) "result")) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:result element not allowed within" |
| " another func:result element\n"); |
| return (NULL); |
| } |
| } |
| if (IS_XSLT_ELEM(test) && |
| (IS_XSLT_NAME(test, "variable") || |
| IS_XSLT_NAME(test, "param"))) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:result element not allowed within" |
| " a variable binding element\n"); |
| return (NULL); |
| } |
| } |
| |
| /* |
| * Precomputation |
| */ |
| ret = (exsltFuncResultPreComp *) |
| xmlMalloc (sizeof(exsltFuncResultPreComp)); |
| if (ret == NULL) { |
| xsltPrintErrorContext(NULL, NULL, NULL); |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncResultComp : malloc failed\n"); |
| return (NULL); |
| } |
| memset(ret, 0, sizeof(exsltFuncResultPreComp)); |
| |
| xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function, |
| (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp); |
| ret->select = NULL; |
| |
| /* |
| * Precompute the select attribute |
| */ |
| sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL); |
| if (sel != NULL) { |
| ret->select = xmlXPathCompile (sel); |
| xmlFree(sel); |
| } |
| /* |
| * Precompute the namespace list |
| */ |
| ret->nsList = xmlGetNsList(inst->doc, inst); |
| if (ret->nsList != NULL) { |
| int i = 0; |
| while (ret->nsList[i] != NULL) |
| i++; |
| ret->nsNr = i; |
| } |
| return ((xsltElemPreCompPtr) ret); |
| } |
| |
| static void |
| exsltFuncResultElem (xsltTransformContextPtr ctxt, |
| xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, |
| exsltFuncResultPreComp *comp) { |
| exsltFuncData *data; |
| xmlXPathObjectPtr ret; |
| |
| |
| /* It is an error if instantiating the content of the |
| * func:function element results in the instantiation of more than |
| * one func:result elements. |
| */ |
| data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE); |
| if (data == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncReturnElem: data == NULL\n"); |
| return; |
| } |
| if (data->result != NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:result already instanciated\n"); |
| data->error = 1; |
| return; |
| } |
| /* |
| * Processing |
| */ |
| if (comp->select != NULL) { |
| xmlNsPtr *oldXPNsList; |
| int oldXPNsNr; |
| xmlNodePtr oldXPContextNode; |
| /* If the func:result element has a select attribute, then the |
| * value of the attribute must be an expression and the |
| * returned value is the object that results from evaluating |
| * the expression. In this case, the content must be empty. |
| */ |
| if (inst->children != NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "func:result content must be empty if" |
| " the function has a select attribute\n"); |
| data->error = 1; |
| return; |
| } |
| oldXPNsList = ctxt->xpathCtxt->namespaces; |
| oldXPNsNr = ctxt->xpathCtxt->nsNr; |
| oldXPContextNode = ctxt->xpathCtxt->node; |
| |
| ctxt->xpathCtxt->namespaces = comp->nsList; |
| ctxt->xpathCtxt->nsNr = comp->nsNr; |
| |
| ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); |
| |
| ctxt->xpathCtxt->node = oldXPContextNode; |
| ctxt->xpathCtxt->nsNr = oldXPNsNr; |
| ctxt->xpathCtxt->namespaces = oldXPNsList; |
| |
| if (ret == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncResultElem: ret == NULL\n"); |
| return; |
| } |
| /* |
| * Mark it as a function result in order to avoid garbage |
| * collecting of tree fragments before the function exits. |
| */ |
| xsltExtensionInstructionResultRegister(ctxt, ret); |
| } else if (inst->children != NULL) { |
| /* If the func:result element does not have a select attribute |
| * and has non-empty content (i.e. the func:result element has |
| * one or more child nodes), then the content of the |
| * func:result element specifies the value. |
| */ |
| xmlNodePtr oldInsert; |
| xmlDocPtr container; |
| |
| container = xsltCreateRVT(ctxt); |
| if (container == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncResultElem: out of memory\n"); |
| data->error = 1; |
| return; |
| } |
| xsltRegisterLocalRVT(ctxt, container); |
| |
| oldInsert = ctxt->insert; |
| ctxt->insert = (xmlNodePtr) container; |
| xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, |
| inst->children, NULL, NULL); |
| ctxt->insert = oldInsert; |
| |
| ret = xmlXPathNewValueTree((xmlNodePtr) container); |
| if (ret == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "exsltFuncResultElem: ret == NULL\n"); |
| data->error = 1; |
| } else { |
| ret->boolval = 0; /* Freeing is not handled there anymore */ |
| /* |
| * Mark it as a function result in order to avoid garbage |
| * collecting of tree fragments before the function exits. |
| */ |
| xsltExtensionInstructionResultRegister(ctxt, ret); |
| } |
| } else { |
| /* If the func:result element has empty content and does not |
| * have a select attribute, then the returned value is an |
| * empty string. |
| */ |
| ret = xmlXPathNewCString(""); |
| } |
| data->result = ret; |
| } |
| |
| /** |
| * exsltFuncRegister: |
| * |
| * Registers the EXSLT - Functions module |
| */ |
| void |
| exsltFuncRegister (void) { |
| xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE, |
| (xsltExtInitFunction) exsltFuncInit, |
| (xsltExtShutdownFunction) exsltFuncShutdown, |
| (xsltStyleExtInitFunction) exsltFuncStyleInit, |
| (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown); |
| |
| xsltRegisterExtModuleTopLevel ((const xmlChar *) "function", |
| EXSLT_FUNCTIONS_NAMESPACE, |
| exsltFuncFunctionComp); |
| xsltRegisterExtModuleElement ((const xmlChar *) "result", |
| EXSLT_FUNCTIONS_NAMESPACE, |
| (xsltPreComputeFunction)exsltFuncResultComp, |
| (xsltTransformFunction) exsltFuncResultElem); |
| } |