/** * xsltEvalAttrValueTemplate: * @ctxt: the XSLT transformation context * @inst: the instruction (or LRE) in the stylesheet holding the * attribute with an AVT * @name: the attribute QName * @ns: the attribute namespace URI * * Evaluate a attribute value template, i.e. the attribute value can * contain expressions contained in curly braces ({}) and those are * substituted by they computed value. * * Returns the computed string value or NULL, must be deallocated by the * caller. */ xmlChar * xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, const xmlChar *name, const xmlChar *ns) { xmlChar *ret; xmlChar *expr; if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) return(NULL); expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) return(NULL); /* * TODO: though now {} is detected ahead, it would still be good to * optimize both functions to keep the splitted value if the * attribute content and the XPath precompiled expressions around */ ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); #endif if (expr != NULL) xmlFree(expr); return(ret); }
/** * xsltInitCtxtKeys: * @ctxt: an XSLT transformation context * @idoc: a document info * * Computes all the keys tables for the current input document. * Should be done before global varibales are initialized. * NOTE: Not used anymore in the refactored code. */ void xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { xsltStylesheetPtr style; xsltKeyDefPtr keyDef; if ((ctxt == NULL) || (idoc == NULL)) return; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKeys on document\n"); #endif #ifdef WITH_XSLT_DEBUG_KEYS if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", idoc->doc->URL)); #endif style = ctxt->style; while (style != NULL) { keyDef = (xsltKeyDefPtr) style->keys; while (keyDef != NULL) { xsltInitCtxtKey(ctxt, idoc, keyDef); keyDef = keyDef->next; } style = xsltNextImport(style); } #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKeys on document: done\n"); #endif }
/** * xsltEvalXPathPredicate: * @ctxt: the XSLT transformation context * @comp: the XPath compiled expression * @nsList: the namespaces in scope * @nsNr: the number of namespaces in scope * * Process the expression using XPath and evaluate the result as * an XPath predicate * * Returns 1 is the predicate was true, 0 otherwise */ int xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, xmlNsPtr *nsList, int nsNr) { int ret; xmlXPathObjectPtr res; int oldNsNr; xmlNsPtr *oldNamespaces; xmlNodePtr oldInst; int oldProximityPosition, oldContextSize; oldContextSize = ctxt->xpathCtxt->contextSize; oldProximityPosition = ctxt->xpathCtxt->proximityPosition; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; oldInst = ctxt->inst; ctxt->xpathCtxt->node = ctxt->node; ctxt->xpathCtxt->namespaces = nsList; ctxt->xpathCtxt->nsNr = nsNr; res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); if (res != NULL) { ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); xmlXPathFreeObject(res); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathPredicate: returns %d\n", ret)); #endif } else { #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathPredicate: failed\n")); #endif ctxt->state = XSLT_STATE_STOPPED; ret = 0; } ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->inst = oldInst; ctxt->xpathCtxt->contextSize = oldContextSize; ctxt->xpathCtxt->proximityPosition = oldProximityPosition; return(ret); }
/** * xsltEvalXPathStringNs: * @ctxt: the XSLT transformation context * @comp: the compiled XPath expression * @nsNr: the number of namespaces in the list * @nsList: the list of in-scope namespaces to use * * Process the expression using XPath, allowing to pass a namespace mapping * context and get a string * * Returns the computed string value or NULL, must be deallocated by the * caller. */ xmlChar * xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, int nsNr, xmlNsPtr *nsList) { xmlChar *ret = NULL; xmlXPathObjectPtr res; xmlNodePtr oldInst; xmlNodePtr oldNode; int oldPos, oldSize; int oldNsNr; xmlNsPtr *oldNamespaces; oldInst = ctxt->inst; oldNode = ctxt->node; oldPos = ctxt->xpathCtxt->proximityPosition; oldSize = ctxt->xpathCtxt->contextSize; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; ctxt->xpathCtxt->node = ctxt->node; /* TODO: do we need to propagate the namespaces here ? */ ctxt->xpathCtxt->namespaces = nsList; ctxt->xpathCtxt->nsNr = nsNr; res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); if (res != NULL) { if (res->type != XPATH_STRING) res = xmlXPathConvertString(res); if (res->type == XPATH_STRING) { ret = res->stringval; res->stringval = NULL; } else { xsltTransformError(ctxt, NULL, NULL, "xpath : string() function didn't return a String\n"); } xmlXPathFreeObject(res); } else { ctxt->state = XSLT_STATE_STOPPED; } #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathString: returns %s\n", ret)); #endif ctxt->inst = oldInst; ctxt->node = oldNode; ctxt->xpathCtxt->contextSize = oldSize; ctxt->xpathCtxt->proximityPosition = oldPos; ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; return(ret); }
/** * xsltInitDocKeyTable: * * INTERNAL ROUTINE ONLY * * Check if any keys on the current document need to be computed */ static int xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *nameURI) { xsltStylesheetPtr style; xsltKeyDefPtr keyd = NULL; int found = 0; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitDocKeyTable %s\n", name); #endif style = ctxt->style; while (style != NULL) { keyd = (xsltKeyDefPtr) style->keys; while (keyd != NULL) { if (((keyd->nameURI != NULL) == (nameURI != NULL)) && xmlStrEqual(keyd->name, name) && xmlStrEqual(keyd->nameURI, nameURI)) { xsltInitCtxtKey(ctxt, ctxt->document, keyd); if (ctxt->document->nbKeysComputed == ctxt->nbKeys) return(0); found = 1; } keyd = keyd->next; } style = xsltNextImport(style); } if (found == 0) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitDocKeyTable: did not found %s\n", name)); #endif xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL, "Failed to find key definition for %s\n", name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitDocKeyTable %s done\n", name); #endif return(0); }
xmlChar * xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, const xmlChar *name, const xmlChar *ns) { xmlChar *ret; xmlChar *expr; if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) return(NULL); expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) return(NULL); ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); #endif if (expr != NULL) xmlFree(expr); return(ret); }
/** * xsltInitCtxtKeys: * @ctxt: an XSLT transformation context * @doc: an XSLT document * * Computes all the keys tables for the current input document. * Should be done before global varibales are initialized. * NOTE: Not used anymore in the refactored code. */ void xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) { xsltStylesheetPtr style; xsltKeyDefPtr keyd; if ((ctxt == NULL) || (doc == NULL)) return; #ifdef WITH_XSLT_DEBUG_KEYS if ((doc->doc != NULL) && (doc->doc->URL != NULL)) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", doc->doc->URL)); #endif style = ctxt->style; while (style != NULL) { keyd = (xsltKeyDefPtr) style->keys; while (keyd != NULL) { xsltInitCtxtKey(ctxt, doc, keyd); keyd = keyd->next; } style = xsltNextImport(style); } }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @idoc: the document information (holds key values) * @keyDef: the key definition * * Computes the key tables this key and for the current input document. * * Returns: 0 on success, -1 on error */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, xsltKeyDefPtr keyDef) { int i, len, k; xmlNodeSetPtr matchList = NULL, keylist; xmlXPathObjectPtr matchRes = NULL, useRes = NULL; xmlChar *str = NULL; xsltKeyTablePtr table; xmlNodePtr oldInst, cur; xmlNodePtr oldContextNode; xsltDocumentPtr oldDocInfo; int oldXPPos, oldXPSize; xmlDocPtr oldXPDoc; int oldXPNsNr; xmlNsPtr *oldXPNamespaces; xmlXPathContextPtr xpctxt; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); #endif if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) return(-1); /* * Detect recursive keys */ if (ctxt->keyInitLevel > ctxt->nbKeys) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: key definition of %s is recursive\n", keyDef->name)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Key definition for %s is recursive\n", keyDef->name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } ctxt->keyInitLevel++; xpctxt = ctxt->xpathCtxt; idoc->nbKeysComputed++; /* * Save context state. */ oldInst = ctxt->inst; oldDocInfo = ctxt->document; oldContextNode = ctxt->node; oldXPDoc = xpctxt->doc; oldXPPos = xpctxt->proximityPosition; oldXPSize = xpctxt->contextSize; oldXPNsNr = xpctxt->nsNr; oldXPNamespaces = xpctxt->namespaces; /* * Set up contexts. */ ctxt->document = idoc; ctxt->node = (xmlNodePtr) idoc->doc; ctxt->inst = keyDef->inst; xpctxt->doc = idoc->doc; xpctxt->node = (xmlNodePtr) idoc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ xpctxt->namespaces = keyDef->nsList; xpctxt->nsNr = keyDef->nsNr; /* * Evaluate the 'match' expression of the xsl:key. * TODO: The 'match' is a *pattern*. */ matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); if (matchRes == NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'match' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } else { if (matchRes->type == XPATH_NODESET) { matchList = matchRes->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (matchList != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyDef->match, matchList->nodeNr)); #endif } else { /* * Is not a node set, but must be. */ #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "The 'match' expression did not evaluate to a node set.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } } if ((matchList == NULL) || (matchList->nodeNr <= 0)) goto exit; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) idoc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyDef->name) && (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyDef->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); if (table == NULL) goto error; table->next = idoc->keys; idoc->keys = table; } /* * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) * "...the use attribute of the xsl:key element is evaluated with x as " the current node and with a node list containing just x as the * current node list" */ xpctxt->contextSize = 1; xpctxt->proximityPosition = 1; for (i = 0; i < matchList->nodeNr; i++) { cur = matchList->nodeTab[i]; if (! IS_XSLT_REAL_NODE(cur)) continue; xpctxt->node = cur; /* * Process the 'use' of the xsl:key. * SPEC XSLT 1.0: * "The use attribute is an expression specifying the values of * the key; the expression is evaluated once for each node that * matches the pattern." */ if (useRes != NULL) xmlXPathFreeObject(useRes); useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); if (useRes == NULL) { xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'use' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; break; } if (useRes->type == XPATH_NODESET) { if ((useRes->nodesetval != NULL) && (useRes->nodesetval->nodeNr != 0)) { len = useRes->nodesetval->nodeNr; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); } else { continue; } } else { len = 1; if (useRes->type == XPATH_STRING) { /* * Consume the string value. */ str = useRes->stringval; useRes->stringval = NULL; } else { str = xmlXPathCastToString(useRes); } } /* * Process all strings. */ k = 0; while (1) { if (str == NULL) goto next_string; #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(cur); if (keylist == NULL) goto error; xmlHashAddEntry(table->keys, str, keylist); } else { /* * TODO: How do we know if this function failed? */ xmlXPathNodeSetAdd(keylist, cur); } switch (cur->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: cur->psvi = keyDef; break; case XML_ATTRIBUTE_NODE: ((xmlAttrPtr) cur)->psvi = keyDef; break; case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: ((xmlDocPtr) cur)->psvi = keyDef; break; default: break; } xmlFree(str); str = NULL; next_string: k++; if (k >= len) break; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); } } exit: error: ctxt->keyInitLevel--; /* * Restore context state. */ xpctxt->doc = oldXPDoc; xpctxt->nsNr = oldXPNsNr; xpctxt->namespaces = oldXPNamespaces; xpctxt->proximityPosition = oldXPPos; xpctxt->contextSize = oldXPSize; ctxt->node = oldContextNode; ctxt->document = oldDocInfo; ctxt->inst = oldInst; if (str) xmlFree(str); if (useRes != NULL) xmlXPathFreeObject(useRes); if (matchRes != NULL) xmlXPathFreeObject(matchRes); return(0); }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @doc: an XSLT document * @keyd: the key definition * * Computes the key tables this key and for the current input document. */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc, xsltKeyDefPtr keyd) { int i; xmlNodeSetPtr nodelist = NULL, keylist; xmlXPathObjectPtr res = NULL; xmlChar *str, **list; xsltKeyTablePtr table; int oldPos, oldSize; xmlNodePtr oldInst; xmlNodePtr oldNode; xsltDocumentPtr oldDoc; xmlDocPtr oldXDoc; int oldNsNr; xmlNsPtr *oldNamespaces; doc->nbKeysComputed++; /* * Evaluate the nodelist */ oldXDoc= ctxt->xpathCtxt->doc; oldPos = ctxt->xpathCtxt->proximityPosition; oldSize = ctxt->xpathCtxt->contextSize; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; oldInst = ctxt->inst; oldDoc = ctxt->document; oldNode = ctxt->node; if (keyd->comp == NULL) goto error; if (keyd->usecomp == NULL) goto error; ctxt->document = doc; ctxt->xpathCtxt->doc = doc->doc; ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc; ctxt->node = (xmlNodePtr) doc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ ctxt->xpathCtxt->namespaces = keyd->nsList; ctxt->xpathCtxt->nsNr = keyd->nsNr; ctxt->inst = keyd->inst; res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt); ctxt->xpathCtxt->contextSize = oldSize; ctxt->xpathCtxt->proximityPosition = oldPos; ctxt->inst = oldInst; if (res != NULL) { if (res->type == XPATH_NODESET) { nodelist = res->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (nodelist != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyd->match, nodelist->nodeNr)); #endif } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyd->match)); #endif goto error; } } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyd->match)); #endif ctxt->state = XSLT_STATE_STOPPED; goto error; } /* * for each node in the list evaluate the key and insert the node */ if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) goto error; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) doc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyd->name) && (((keyd->nameURI == NULL) && (table->nameURI == NULL)) || ((keyd->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyd->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyd->name, keyd->nameURI); if (table == NULL) goto error; table->next = doc->keys; doc->keys = table; } for (i = 0;i < nodelist->nodeNr;i++) { if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) { ctxt->node = nodelist->nodeTab[i]; list = xsltEvalXPathKeys(ctxt, keyd->usecomp, keyd); if (list != NULL) { int ix = 0; str = list[ix++]; while (str != NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to(%s,%s)\n", keyd->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]); xmlHashAddEntry(table->keys, str, keylist); } else { xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]); } switch (nodelist->nodeTab[i]->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: nodelist->nodeTab[i]->psvi = keyd; break; case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) nodelist->nodeTab[i]; attr->psvi = keyd; break; } case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: { xmlDocPtr kdoc = (xmlDocPtr) nodelist->nodeTab[i]; kdoc->psvi = keyd; break; } default: break; } xmlFree(str); str = list[ix++]; } xmlFree(list); #ifdef WITH_XSLT_DEBUG_KEYS } else { XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : use %s failed to return strings\n", keyd->use)); #endif } } } error: ctxt->document = oldDoc; ctxt->xpathCtxt->doc = oldXDoc; ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->node = oldNode; if (res != NULL) xmlXPathFreeObject(res); return(0); }