--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xml/libxml2libs/src/libxml2/libxml2_xpath.c Thu Dec 17 09:29:21 2009 +0200
@@ -0,0 +1,12346 @@
+/*
+ * libxml2_xpath.c: XML Path Language implementation
+ * XPath is a language for addressing parts of an XML document,
+ * designed to be used by both XSLT and XPointer
+ *
+ * Reference: W3C Recommendation 16 November 1999
+ * http://www.w3.org/TR/1999/REC-xpath-19991116
+ * Public reference:
+ * http://www.w3.org/TR/xpath
+ *
+ * See Copyright for the status of this software
+ *
+ * Author: daniel@veillard.com
+ * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
+ *
+ */
+
+#define IN_LIBXML
+
+#include "xmlenglibxml.h"
+
+#include <string.h>
+
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#if defined(HAVE_CTYPE_H)
+#include <ctype.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include <stdapis/libxml2/libxml2_globals.h>
+#include <stdapis/libxml2/libxml2_xpathinternals.h>
+#include <stdapis/libxml2/libxml2_parserinternals.h>
+#include "libxml2_xmlerror2.h"
+
+#ifdef LIBXML_XPTR_ENABLED
+#include <stdapis/libxml2/libxml2_xpointer.h>
+#endif
+
+#ifdef LIBXML_DEBUG_ENABLED
+#include "libxml2_debugxml.h"
+#endif
+
+#include <ctype.h>
+
+
+#define TODO \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("Unimplemented block at %s:%d\n"), \
+ __FILE__, __LINE__);
+
+#if defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XPATH_ENABLED)
+/************************************************************************
+ * *
+ * Floating point stuff *
+ * *
+ ************************************************************************/
+
+#ifndef TRIO_REPLACE_STDIO
+//#define TRIO_PUBLIC static
+#endif
+#include "libxml2_trionan.inc"
+
+/**
+ * xmlXPathInit:
+ *
+ * Initialize the XPath environment
+ *
+ * OOM: never (so far -- soon hash tables will be created here)
+ */
+XMLPUBFUNEXPORT void
+xmlXPathInit(void) {
+ LOAD_GS_DIRECT
+ if (xmlXPathInitialized)
+ return;
+
+ xmlXPathPINF = trio_pinf();
+ xmlXPathNINF = trio_ninf();
+ xmlXPathNAN = trio_nan();
+ xmlXPathNZERO = trio_nzero();
+
+ xmlXPathInitialized = 1;
+
+}
+
+/**
+ * xeXPathCleanup:
+ *
+ * Free any reusable by XPath module resources
+ */
+void xeXPathCleanup()
+{
+ LOAD_GS_DIRECT
+ if (xmlXPathDefaultFunctionsHash){
+ xmlHashFree(xmlXPathDefaultFunctionsHash, NULL);
+ xmlXPathDefaultFunctionsHash = NULL;
+ }
+
+ if (xmlXPathIntermediaryExtensionFunctionsHash){
+ xmlHashFree(xmlXPathIntermediaryExtensionFunctionsHash, NULL);
+ xmlXPathIntermediaryExtensionFunctionsHash = NULL;
+ }
+}
+
+// DONE: OPTIMIZE: Define as macro or inliner
+/**
+ * xmlXPathIsNaN:
+ * @param val a double value
+ *
+ * Provides a portable isnan() function to detect whether a double
+ * is a NotaNumber. Based on trio code
+ * http://sourceforge.net/projects/ctrio/
+ *
+ * Returns 1 if the value is a NaN, 0 otherwise
+ */
+/*
+inline int xmlXPathIsNaN(double val) {
+ return(trio_isnan(val));
+}*/
+//#define xmlXPathIsNaN(val) trio_isnan(val)
+
+//#define xmlXPathIsNaN(val) trio_isnan(val)
+
+// DONE: OPTIMIZE: Define as macro or inliner
+/**
+ * xmlXPathIsInf:
+ * @param val a double value
+ *
+ * Provides a portable isinf() function to detect whether a double
+ * is a +Infinite or -Infinite. Based on trio code
+ * http://sourceforge.net/projects/ctrio/
+ *
+ * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
+ */
+/*
+inline int xmlXPathIsInf(double val) {
+ return(trio_isinf(val));
+}
+*/
+#define xmlXPathIsInf(val) trio_isinf(val)
+
+#endif /* SCHEMAS or XPATH */
+
+
+#ifdef LIBXML_XPATH_ENABLED
+
+
+
+/**
+ * xmlXPathGetSign:
+ * @param val a double value
+ *
+ * Provides a portable function to detect the sign of a double
+ * Modified from trio code
+ * http://sourceforge.net/projects/ctrio/
+ *
+ * Returns 1 if the value is Negative, 0 if positive
+ */
+/*
+inline int xmlXPathGetSign(double val) {
+ return(trio_signbit(val));
+}
+*/
+#define xmlXPathGetSign(val) trio_signbit(val)
+
+
+/*
+ *
+ *
+ */
+/* #define DEBUG */
+/* #define DEBUG_STEP */
+/* #define DEBUG_STEP_NTH */
+/* #define DEBUG_EXPR */
+/* #define DEBUG_EVAL_COUNTS */
+
+
+
+static const xmlNs xmlXPathXMLNamespaceStruct = {
+ NULL,
+ XML_NAMESPACE_DECL,
+ XML_XML_NAMESPACE,
+ BAD_CAST "xml",
+ NULL
+};
+static const xmlNsPtr xmlXPathXMLNamespace = (xmlNsPtr)&xmlXPathXMLNamespaceStruct;
+
+/************************************************************************
+ * *
+ * Error handling routines *
+ * *
+ ************************************************************************/
+
+/*
+ * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
+ */
+const char *const xmlXPathErrorMessages[] = {
+ EMBED_ERRTXT("Ok\n"),
+ EMBED_ERRTXT("Number encoding\n"),
+ EMBED_ERRTXT("Unfinished literal\n"),
+ EMBED_ERRTXT("Start of literal\n"),
+ EMBED_ERRTXT("Expected $ for variable reference\n"),
+ EMBED_ERRTXT("Undefined variable\n"),
+ EMBED_ERRTXT("Invalid predicate\n"),
+ EMBED_ERRTXT("Invalid expression\n"),
+ EMBED_ERRTXT("Missing closing curly brace\n"),
+ EMBED_ERRTXT("Unregistered function\n"),
+ EMBED_ERRTXT("Invalid operand\n"),
+ EMBED_ERRTXT("Invalid type\n"),
+ EMBED_ERRTXT("Invalid number of arguments\n"),
+ EMBED_ERRTXT("Invalid context size\n"),
+ EMBED_ERRTXT("Invalid context position\n"),
+ EMBED_ERRTXT("Memory allocation error\n"),
+ EMBED_ERRTXT("Syntax error\n"),
+ EMBED_ERRTXT("Resource error\n"),
+ EMBED_ERRTXT("Sub resource error\n"),
+ EMBED_ERRTXT("Undefined namespace prefix\n"),
+ EMBED_ERRTXT("Encoding error\n"),
+ EMBED_ERRTXT("Char out of XML range\n")
+};
+
+
+
+/**
+ * xmlXPathErrMemory:
+ * @param ctxt an XPath context
+ * @param extra extra informations
+ *
+ * Handle a redefinition of attribute error
+ */
+static void
+xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
+{
+ if (ctxt != NULL) {
+/* Disabled -- do not allocate memory during OOM handling!!!
+
+ if (extra) {
+ xmlChar buf[200];
+
+ xmlStrPrintf(buf, 200,
+ BAD_CAST EMBED_ERRTXT("Memory allocation failed : %s\n"), extra);
+ ctxt->lastError.message = (char *) xmlStrdup(buf);
+ } else {
+ ctxt->lastError.message = (char *)
+ xmlStrdup(BAD_CAST EMBED_ERRTXT("Memory allocation failed\n"));
+ }
+
+*/
+ ctxt->lastError.domain = XML_FROM_XPATH;
+ ctxt->lastError.code = XML_ERR_NO_MEMORY;
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData, &ctxt->lastError);
+ } else {
+ if (extra)
+ __xmlRaiseError(NULL, NULL, NULL,
+ NULL, NULL, XML_FROM_XPATH,
+ XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
+ extra, NULL, NULL, 0, 0,
+ EMBED_ERRTXT("Memory allocation failed : %s\n"), extra);
+ else
+ __xmlRaiseError(NULL, NULL, NULL,
+ NULL, NULL, XML_FROM_XPATH,
+ XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
+ NULL, NULL, NULL, 0, 0,
+ EMBED_ERRTXT("Memory allocation failed\n"));
+ }
+}
+
+/**
+ * xmlXPathErrMemory:
+ * @param ctxt an XPath parser context
+ * @param extra extra informations
+ *
+ * Handle a redefinition of attribute error
+ */
+
+static void
+xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
+{
+ ctxt->error = XPATH_MEMORY_ERROR;
+ if (ctxt == NULL)
+ xmlXPathErrMemory(NULL, extra);
+ else
+ xmlXPathErrMemory(ctxt->context, extra);
+}
+
+/**
+ * xmlXPathErr:
+ * @param ctxt a XPath parser context
+ * @param error the error code
+ *
+ * Handle a Relax NG Parsing error
+ */
+XMLPUBFUNEXPORT void
+xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
+{
+
+ if (ctxt == NULL) {
+ __xmlRaiseError(NULL, NULL, NULL,
+ NULL, NULL, XML_FROM_XPATH,
+ error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
+ XML_ERR_ERROR, NULL, 0,
+ NULL, NULL, NULL, 0, 0,
+ xmlXPathErrorMessages[error]);
+ return;
+ }
+ ctxt->error = error;
+ if (ctxt->context == NULL) {
+ __xmlRaiseError(NULL, NULL, NULL,
+ NULL, NULL, XML_FROM_XPATH,
+ error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
+ XML_ERR_ERROR, NULL, 0,
+ (const char *) ctxt->base, NULL, NULL,
+ ctxt->cur - ctxt->base, 0,
+ xmlXPathErrorMessages[error]);
+ return;
+ }
+
+ ctxt->context->lastError.domain = XML_FROM_XPATH;
+ ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK;
+ ctxt->context->lastError.level = XML_ERR_ERROR;
+ if (ctxt->context->lastError.str1){
+ xmlFree(ctxt->context->lastError.str1);
+ ctxt->context->lastError.str1 = NULL;
+ }
+ // Avoid memory allocation if we are in OOM !
+ if(error != XPATH_MEMORY_ERROR){
+ ctxt->context->lastError.str1 = (char*) xmlStrdup(ctxt->base);
+ }
+ ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
+ ctxt->context->lastError.node = ctxt->context->debugNode;
+
+ if (ctxt->context->error != NULL) {
+ ctxt->context->error(ctxt->context->userData, &ctxt->context->lastError);
+ } else {
+ __xmlRaiseError(NULL, NULL, NULL,
+ NULL, ctxt->context->debugNode, XML_FROM_XPATH,
+ error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
+ XML_ERR_ERROR, NULL, 0,
+ (const char*) ctxt->base, NULL, NULL,
+ ctxt->cur - ctxt->base, 0,
+ xmlXPathErrorMessages[error]);
+ }
+}
+
+// DONE: OPTIMIZE: Use macro instead of function call
+// Macro added in xpathInternals.h
+/**
+ * xmlXPatherror:
+ * @param ctxt the XPath Parser context
+ * @param file the file name
+ * @param line the line number
+ * @param no the error number
+ *
+ * Formats an error message.
+ */
+//void
+//xmlXPatherror(xmlXPathParserContextPtr ctxt, const char* file ATTRIBUTE_UNUSED,
+// int line ATTRIBUTE_UNUSED , int no)
+//{
+// xmlXPathErr(ctxt, no);
+//}
+
+
+/************************************************************************
+ * *
+ * Parser Types *
+ * *
+ ************************************************************************/
+
+/*
+ * Types are private:
+ */
+typedef enum {
+ AXIS_ANCESTOR = 1,
+ AXIS_ANCESTOR_OR_SELF,
+ AXIS_ATTRIBUTE,
+ AXIS_CHILD,
+ AXIS_DESCENDANT,
+ AXIS_DESCENDANT_OR_SELF,
+ AXIS_FOLLOWING,
+ AXIS_FOLLOWING_SIBLING,
+ AXIS_NAMESPACE,
+ AXIS_PARENT,
+ AXIS_PRECEDING,
+ AXIS_PRECEDING_SIBLING,
+ AXIS_SELF
+} xmlXPathAxisVal;
+
+typedef enum {
+ NODE_TEST_NONE = 0,
+ NODE_TEST_TYPE = 1,
+ NODE_TEST_PI = 2,
+ NODE_TEST_ALL = 3,
+ NODE_TEST_NS = 4,
+ NODE_TEST_NAME = 5
+} xmlXPathTestVal;
+
+typedef enum {
+ NODE_TYPE_NODE = 0,
+ NODE_TYPE_COMMENT = XML_COMMENT_NODE,
+ NODE_TYPE_TEXT = XML_TEXT_NODE,
+ NODE_TYPE_PI = XML_PI_NODE
+} xmlXPathTypeVal;
+
+
+
+
+/************************************************************************
+ * *
+ * Parser Type functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathNewCompExpr:
+ *
+ * Create a new Xpath component
+ *
+ * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
+ */
+static xmlXPathCompExprPtr
+xmlXPathNewCompExpr(void) {
+ xmlXPathCompExprPtr cur;
+
+ cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
+ if (cur == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating component\n"));
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xmlXPathCompExpr));
+ cur->maxStep = XPATH_STEPS_GRANULARITY;
+ cur->nbStep = 0;
+ cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * sizeof(xmlXPathStepOp));
+ if (cur->steps == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating steps\n"));
+ xmlFree(cur);
+ return(NULL);
+ }
+ memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
+ cur->last = -1;
+#ifdef DEBUG_EVAL_COUNTS
+ cur->nb = 0;
+#endif
+ return(cur);
+}
+
+/**
+ * xmlXPathFreeCompExpr:
+ * @param comp an XPATH comp
+ *
+ * Free up the memory allocated by comp
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
+{
+ xmlXPathStepOpPtr op;
+ int i;
+
+ if (comp == NULL)
+ return;
+ if (comp->dict == NULL) {
+ for (i = 0; i < comp->nbStep; i++) {
+ op = &comp->steps[i];
+ if (op->value4 != NULL) {
+ if (op->op == XPATH_OP_VALUE)
+ xmlXPathFreeObject((xmlXPathObjectPtr)op->value4);
+ else
+ xmlFree(op->value4);
+ }
+ if (op->value5 != NULL)
+ xmlFree(op->value5);
+ }
+ } else {
+ for (i = 0; i < comp->nbStep; i++) {
+ op = &comp->steps[i];
+ if (op->value4 != NULL) {
+ if (op->op == XPATH_OP_VALUE)
+ xmlXPathFreeObject((xmlXPathObjectPtr)op->value4);
+ }
+ }
+ xmlDictFree(comp->dict);
+ }
+
+ if (comp->steps != NULL) {
+ xmlFree(comp->steps);
+ }
+#ifdef DEBUG_EVAL_COUNTS
+ if (comp->string != NULL) {
+ xmlFree(comp->string);
+ }
+#endif
+ if (comp->expr != NULL) {
+ xmlFree(comp->expr);
+ }
+
+ xmlFree(comp);
+}
+
+/**
+ * xmlXPathCompExprAdd:
+ * @param comp the compiled expression
+ * @param ch1 first child index
+ * @param ch2 second child index
+ * @param op an op
+ * @param value the first int value
+ * @param value2 the second int value
+ * @param value3 the third int value
+ * @param value4 the first string value
+ * @param value5 the second string value
+ *
+ * Add a step to an XPath Compiled Expression
+ *
+ * Returns -1 in case of failure, the index otherwise
+ */
+static int
+xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
+ xmlXPathOp op, int value,
+ int value2, int value3, void *value4, void *value5)
+{
+ xmlXPathStepOpPtr step;
+
+ if (comp->nbStep >= comp->maxStep)
+ {
+ xmlXPathStepOp *real;
+
+ comp->maxStep *= 2;
+ real = (xmlXPathStepOp *) xmlRealloc(comp->steps, comp->maxStep * sizeof(xmlXPathStepOp));
+ if (real == NULL) {
+ comp->maxStep /= 2;
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("adding step\n"));
+ return(-1);
+ }
+ comp->steps = real;
+ }
+ // DONE: OPTIMIZE: make a proxy for comp->steps[comp->nbStep]
+ step = &(comp->steps[comp->nbStep]);
+ //
+ comp->last = comp->nbStep;
+ step->ch1 = ch1;
+ step->ch2 = ch2;
+ step->op = op;
+ step->value = value;
+ step->value2 = value2;
+ step->value3 = value3;
+
+ if ((comp->dict != NULL) &&
+ ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
+ (op == XPATH_OP_COLLECT)))
+ {
+ if (value4 != NULL) {
+ step->value4 = (xmlChar*)
+ (void*)xmlDictLookup(comp->dict, (xmlChar*) value4, -1);
+ xmlFree(value4);
+ } else
+ step->value4 = NULL;
+
+ if (value5 != NULL) {
+ step->value5 = (xmlChar*)
+ (void*)xmlDictLookup(comp->dict, (xmlChar*)value5, -1);
+ xmlFree(value5);
+ }else
+ step->value5 = NULL;
+ }
+ else
+ {
+ step->value4 = value4;
+ step->value5 = value5;
+ }
+ step->cache = NULL;
+ return(comp->nbStep++);
+}
+
+/**
+ * xmlXPathCompSwap:
+ * @param comp the compiled expression
+ * @param op operation index
+ *
+ * Swaps 2 operations in the compiled expression
+ */
+static void
+xmlXPathCompSwap(xmlXPathStepOpPtr op) {
+ LOAD_GS_DIRECT
+ int tmp;
+
+#ifndef LIBXML_THREAD_ENABLED
+ /*
+ * Since this manipulates possibly shared variables, this is
+ * disabled if one detects that the library is used in a multithreaded
+ * application
+ */
+ if (xmlXPathDisableOptimizer)
+ return;
+#endif
+
+ tmp = op->ch1;
+ op->ch1 = op->ch2;
+ op->ch2 = tmp;
+}
+
+
+#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
+ xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), (op), (val), (val2), (val3), (val4), (val5))
+
+#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
+ xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, (op), (val), (val2), (val3), (val4), (val5))
+
+#define PUSH_LEAVE_EXPR(op, val, val2) \
+ xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
+
+#define PUSH_UNARY_EXPR(op, ch, val, val2) \
+ xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
+
+#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
+ xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
+
+/************************************************************************
+ * *
+ * Debugging related functions *
+ * *
+ ************************************************************************/
+
+
+#define STRANGE \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("Internal error at %s:%d\n"), \
+ __FILE__, __LINE__);
+
+#ifdef LIBXML_DEBUG_ENABLED
+static void
+xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+ if (cur == NULL) {
+ fprintf(output, shift);
+ fprintf(output, "Node is NULL !\n");
+ return;
+
+ }
+
+ if ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE)) {
+ fprintf(output, shift);
+ fprintf(output, " /\n");
+ } else if (cur->type == XML_ATTRIBUTE_NODE)
+ xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
+ else
+ xmlDebugDumpOneNode(output, cur, depth);
+}
+
+static void
+xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
+ xmlNodePtr tmp;
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+ if (cur == NULL) {
+ fprintf(output, shift);
+ fprintf(output, "Node is NULL !\n");
+ return;
+ }
+
+ while (cur != NULL) {
+ tmp = cur;
+ cur = cur->next;
+ xmlDebugDumpOneNode(output, tmp, depth);
+ }
+}
+
+static void
+xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ if (cur == NULL) {
+ fprintf(output, shift);
+ fprintf(output, "NodeSet is NULL !\n");
+ return;
+ }
+
+ if (cur != NULL) {
+ fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
+ for (i = 0;i < cur->nodeNr;i++) {
+ fprintf(output, shift);
+ fprintf(output, "%d", i + 1);
+ xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
+ }
+ }
+}
+
+static void
+xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
+ fprintf(output, shift);
+ fprintf(output, "Value Tree is NULL !\n");
+ return;
+
+ }
+
+ fprintf(output, shift);
+ fprintf(output, "%d", i + 1);
+ xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
+}
+#if defined(LIBXML_XPTR_ENABLED)
+static void
+xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ if (cur == NULL) {
+ fprintf(output, shift);
+ fprintf(output, "LocationSet is NULL !\n");
+ return;
+
+ }
+
+ for (i = 0;i < cur->locNr;i++) {
+ fprintf(output, shift);
+ fprintf(output, "%d : ", i + 1);
+ xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
+ }
+}
+#endif /* LIBXML_XPTR_ENABLED */
+
+/**
+ * xmlXPathDebugDumpObject:
+ * @param output the FILE * to dump the output
+ * @param cur the object to inspect
+ * @param depth indentation level
+ *
+ * Dump the content of the object for debugging purposes
+ */
+void
+xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ fprintf(output, shift);
+
+ if (cur == NULL) {
+ fprintf(output, "Object is empty (NULL)\n");
+ return;
+ }
+ switch(cur->type) {
+ case XPATH_UNDEFINED:
+ fprintf(output, "Object is uninitialized\n");
+ break;
+ case XPATH_NODESET:
+ fprintf(output, "Object is a Node Set :\n");
+ xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
+ break;
+ case XPATH_XSLT_TREE:
+ fprintf(output, "Object is an XSLT value tree :\n");
+ xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
+ break;
+ case XPATH_BOOLEAN:
+ fprintf(output, "Object is a Boolean : ");
+ if (cur->boolval) fprintf(output, "true\n");
+ else fprintf(output, "false\n");
+ break;
+ case XPATH_NUMBER:
+ switch (xmlXPathIsInf(cur->floatval)) {
+ case 1:
+ fprintf(output, "Object is a number : Infinity\n");
+ break;
+ case -1:
+ fprintf(output, "Object is a number : -Infinity\n");
+ break;
+ default:
+ if (xmlXPathIsNaN(cur->floatval)) {
+ fprintf(output, "Object is a number : NaN\n");
+ } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) {
+ fprintf(output, "Object is a number : 0\n");
+ } else {
+ fprintf(output, "Object is a number : %0g\n", cur->floatval);
+ }
+ }
+ break;
+ case XPATH_STRING:
+ fprintf(output, "Object is a string : ");
+ xmlDebugDumpString(output, cur->stringval);
+ fprintf(output, "\n");
+ break;
+ case XPATH_POINT:
+ fprintf(output, "Object is a point : index %d in node", cur->index);
+ xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
+ fprintf(output, "\n");
+ break;
+ case XPATH_RANGE:
+ if ((cur->user2 == NULL) ||
+ ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
+ fprintf(output, "Object is a collapsed range :\n");
+ fprintf(output, shift);
+ if (cur->index >= 0)
+ fprintf(output, "index %d in ", cur->index);
+ fprintf(output, "node\n");
+ xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
+ depth + 1);
+ } else {
+ fprintf(output, "Object is a range :\n");
+ fprintf(output, shift);
+ fprintf(output, "From ");
+ if (cur->index >= 0)
+ fprintf(output, "index %d in ", cur->index);
+ fprintf(output, "node\n");
+ xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
+ depth + 1);
+ fprintf(output, shift);
+ fprintf(output, "To ");
+ if (cur->index2 >= 0)
+ fprintf(output, "index %d in ", cur->index2);
+ fprintf(output, "node\n");
+ xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
+ depth + 1);
+ fprintf(output, "\n");
+ }
+ break;
+ case XPATH_LOCATIONSET:
+#if defined(LIBXML_XPTR_ENABLED)
+ fprintf(output, "Object is a Location Set:\n");
+ xmlXPathDebugDumpLocationSet(output,
+ (xmlLocationSetPtr) cur->user, depth);
+#endif
+ break;
+ case XPATH_USERS:
+ fprintf(output, "Object is user defined\n");
+ break;
+ }
+}
+
+static void
+xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
+ xmlXPathStepOpPtr op, int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ fprintf(output, shift);
+ if (op == NULL) {
+ fprintf(output, "Step is NULL\n");
+ return;
+ }
+ switch (op->op) {
+ case XPATH_OP_END:
+ fprintf(output, "END"); break;
+ case XPATH_OP_AND:
+ fprintf(output, "AND"); break;
+ case XPATH_OP_OR:
+ fprintf(output, "OR"); break;
+ case XPATH_OP_EQUAL:
+ if (op->value)
+ fprintf(output, "EQUAL =");
+ else
+ fprintf(output, "EQUAL !=");
+ break;
+ case XPATH_OP_CMP:
+ if (op->value)
+ fprintf(output, "CMP <");
+ else
+ fprintf(output, "CMP >");
+ if (!op->value2)
+ fprintf(output, "=");
+ break;
+ case XPATH_OP_PLUS:
+ if (op->value == 0)
+ fprintf(output, "PLUS -");
+ else if (op->value == 1)
+ fprintf(output, "PLUS +");
+ else if (op->value == 2)
+ fprintf(output, "PLUS unary -");
+ else if (op->value == 3)
+ fprintf(output, "PLUS unary - -");
+ break;
+ case XPATH_OP_MULT:
+ if (op->value == 0)
+ fprintf(output, "MULT *");
+ else if (op->value == 1)
+ fprintf(output, "MULT div");
+ else
+ fprintf(output, "MULT mod");
+ break;
+ case XPATH_OP_UNION:
+ fprintf(output, "UNION"); break;
+ case XPATH_OP_ROOT:
+ fprintf(output, "ROOT"); break;
+ case XPATH_OP_NODE:
+ fprintf(output, "NODE"); break;
+ case XPATH_OP_RESET:
+ fprintf(output, "RESET"); break;
+ case XPATH_OP_SORT:
+ fprintf(output, "SORT"); break;
+ case XPATH_OP_COLLECT: {
+ xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
+ xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
+ xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
+ const xmlChar *prefix = op->value4;
+ const xmlChar *name = op->value5;
+
+ fprintf(output, "COLLECT ");
+ switch (axis) {
+ case AXIS_ANCESTOR:
+ fprintf(output, " 'ancestors' "); break;
+ case AXIS_ANCESTOR_OR_SELF:
+ fprintf(output, " 'ancestors-or-self' "); break;
+ case AXIS_ATTRIBUTE:
+ fprintf(output, " 'attributes' "); break;
+ case AXIS_CHILD:
+ fprintf(output, " 'child' "); break;
+ case AXIS_DESCENDANT:
+ fprintf(output, " 'descendant' "); break;
+ case AXIS_DESCENDANT_OR_SELF:
+ fprintf(output, " 'descendant-or-self' "); break;
+ case AXIS_FOLLOWING:
+ fprintf(output, " 'following' "); break;
+ case AXIS_FOLLOWING_SIBLING:
+ fprintf(output, " 'following-siblings' "); break;
+ case AXIS_NAMESPACE:
+ fprintf(output, " 'namespace' "); break;
+ case AXIS_PARENT:
+ fprintf(output, " 'parent' "); break;
+ case AXIS_PRECEDING:
+ fprintf(output, " 'preceding' "); break;
+ case AXIS_PRECEDING_SIBLING:
+ fprintf(output, " 'preceding-sibling' "); break;
+ case AXIS_SELF:
+ fprintf(output, " 'self' "); break;
+ }
+ switch (test) {
+ case NODE_TEST_NONE:
+ fprintf(output, "'none' "); break;
+ case NODE_TEST_TYPE:
+ fprintf(output, "'type' "); break;
+ case NODE_TEST_PI:
+ fprintf(output, "'PI' "); break;
+ case NODE_TEST_ALL:
+ fprintf(output, "'all' "); break;
+ case NODE_TEST_NS:
+ fprintf(output, "'namespace' "); break;
+ case NODE_TEST_NAME:
+ fprintf(output, "'name' "); break;
+ }
+ switch (type) {
+ case NODE_TYPE_NODE:
+ fprintf(output, "'node' "); break;
+ case NODE_TYPE_COMMENT:
+ fprintf(output, "'comment' "); break;
+ case NODE_TYPE_TEXT:
+ fprintf(output, "'text' "); break;
+ case NODE_TYPE_PI:
+ fprintf(output, "'PI' "); break;
+ }
+ if (prefix != NULL)
+ fprintf(output, "%s:", prefix);
+ if (name != NULL)
+ fprintf(output, "%s", (const char *) name);
+ break;
+
+ }
+ case XPATH_OP_VALUE: {
+ xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
+
+ fprintf(output, "ELEM ");
+ xmlXPathDebugDumpObject(output, object, 0);
+ goto finish;
+ }
+ case XPATH_OP_VARIABLE: {
+ const xmlChar *prefix = op->value5;
+ const xmlChar *name = op->value4;
+
+ if (prefix != NULL)
+ fprintf(output, "VARIABLE %s:%s", prefix, name);
+ else
+ fprintf(output, "VARIABLE %s", name);
+ break;
+ }
+ case XPATH_OP_FUNCTION: {
+ int nbargs = op->value;
+ const xmlChar *prefix = op->value5;
+ const xmlChar *name = op->value4;
+
+ if (prefix != NULL)
+ fprintf(output, "FUNCTION %s:%s(%d args)",
+ prefix, name, nbargs);
+ else
+ fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
+ break;
+ }
+ case XPATH_OP_ARG: fprintf(output, "ARG"); break;
+ case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
+ case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
+#ifdef LIBXML_XPTR_ENABLED
+ case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
+#endif
+ default:
+ fprintf(output, "UNKNOWN %d\n", op->op); return;
+ }
+ fprintf(output, "\n");
+finish:
+ if (op->ch1 >= 0)
+ xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
+ if (op->ch2 >= 0)
+ xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
+}
+
+/**
+ * xmlXPathDebugDumpCompExpr:
+ * @param output the FILE * for the output
+ * @param comp the precompiled XPath expression
+ * @param depth the indentation level.
+ *
+ * Dumps the tree of the compiled XPath expression.
+ */
+void
+xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
+ int depth) {
+ int i;
+ char shift[100];
+
+ for (i = 0;((i < depth) && (i < 25));i++)
+ shift[2 * i] = shift[2 * i + 1] = ' ';
+ shift[2 * i] = shift[2 * i + 1] = 0;
+
+ fprintf(output, shift);
+
+ if (comp == NULL) {
+ fprintf(output, "Compiled Expression is NULL\n");
+ return;
+ }
+ fprintf(output, "Compiled Expression : %d elements\n",
+ comp->nbStep);
+ i = comp->last;
+ xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
+}
+#endif /* LIBXML_DEBUG_ENABLED */
+
+/************************************************************************
+ * *
+ * Parser stacks related functions and macros *
+ * *
+ ************************************************************************/
+
+/**
+ * valuePop:
+ * @param ctxt an XPath evaluation context
+ *
+ * Pops the top XPath object from the value stack
+ *
+ * Returns the XPath object just removed
+ */
+XMLPUBFUNEXPORT extern xmlXPathObjectPtr
+valuePop(xmlXPathParserContextPtr ctxt)
+{
+ xmlXPathObjectPtr ret;
+
+ if (ctxt->valueNr <= 0)
+ return (0);
+ ctxt->valueNr--;
+ if (ctxt->valueNr > 0)
+ ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
+ else
+ ctxt->value = NULL;
+ ret = ctxt->valueTab[ctxt->valueNr];
+ ctxt->valueTab[ctxt->valueNr] = 0;
+ return (ret);
+}
+
+/**
+ * valuePush:
+ * @param ctxt an XPath evaluation context
+ * @param value the XPath object
+ *
+ * Pushes a new XPath object on top of the value stack
+ *
+ * returns the number of items on the value stack
+ *
+ * OOM: possible --> returns 0
+ */
+XMLPUBFUNEXPORT int
+valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
+{
+ if (ctxt->valueNr >= ctxt->valueMax)
+ {
+ xmlXPathObjectPtr* tmp;
+ // DONE: Fix xmlRealloc
+ tmp = (xmlXPathObjectPtr*)xmlRealloc(ctxt->valueTab,
+ ctxt->valueMax *
+ 2 * sizeof(ctxt->valueTab[0]));
+ if (!tmp) {
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("realloc failed !\n"));
+ return (0);
+ }
+ ctxt->valueTab = tmp;
+ ctxt->valueMax *= 2;
+ }
+ ctxt->valueTab[ctxt->valueNr] = value;
+ ctxt->value = value;
+ return (ctxt->valueNr++);
+}
+
+/**
+ * xmlXPathPopBoolean:
+ * @param ctxt an XPath parser context
+ *
+ * Pops a boolean from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the boolean
+ */
+XMLPUBFUNEXPORT int
+xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr obj;
+ int ret;
+
+ obj = valuePop(ctxt);
+ if (obj == NULL) {
+ xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+ return(0);
+ }
+ if (obj->type != XPATH_BOOLEAN)
+ ret = xmlXPathCastToBoolean(obj);
+ else
+ ret = obj->boolval;
+ xmlXPathFreeObject(obj);
+ return(ret);
+}
+
+/**
+ * xmlXPathPopNumber:
+ * @param ctxt an XPath parser context
+ *
+ * Pops a number from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the number
+ */
+XMLPUBFUNEXPORT double
+xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr obj;
+ double ret;
+
+ obj = valuePop(ctxt);
+ if (obj == NULL) {
+ xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+ return(0);
+ }
+ if (obj->type != XPATH_NUMBER)
+ ret = xmlXPathCastToNumber(obj);
+ else
+ ret = obj->floatval;
+ xmlXPathFreeObject(obj);
+ return(ret);
+}
+
+/**
+ * xmlXPathPopString:
+ * @param ctxt an XPath parser context
+ *
+ * Pops a string from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the string
+ */
+XMLPUBFUNEXPORT xmlChar *
+xmlXPathPopString (xmlXPathParserContextPtr ctxt)
+{
+ xmlXPathObjectPtr obj;
+ xmlChar* ret;
+
+ obj = valuePop(ctxt);
+ if (!obj) {
+ xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+ return(NULL);
+ }
+ ret = xmlXPathCastToString(obj); /* this does required strdup */
+
+ if (obj->stringval == ret)
+ obj->stringval = NULL;
+ xmlXPathFreeObject(obj);
+ return(ret);
+}
+
+/**
+ * xmlXPathPopNodeSet:
+ * @param ctxt an XPath parser context
+ *
+ * Pops a node-set from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the node-set
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr obj;
+ xmlNodeSetPtr ret;
+
+ if (!ctxt->value) {
+ xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+ return(NULL);
+ }
+ if (!xmlXPathStackIsNodeSet(ctxt)) {
+ xmlXPathSetTypeError(ctxt);
+ return(NULL);
+ }
+ obj = valuePop(ctxt);
+ ret = obj->nodesetval;
+ /* to fix memory leak of not clearing obj->user */
+ if (obj->boolval && obj->user)
+ xmlFreeNodeList((xmlNodePtr) obj->user);
+
+ xmlXPathFreeNodeSetList(obj);
+ return(ret);
+}
+
+/**
+ * xmlXPathPopExternal:
+ * @param ctxt an XPath parser context
+ *
+ * Pops an external object from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the object
+ */
+XMLPUBFUNEXPORT void *
+xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr obj;
+ void* ret;
+
+ if (!ctxt->value) {
+ xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+ return(NULL);
+ }
+
+ if (ctxt->value->type != XPATH_USERS) {
+ xmlXPathSetTypeError(ctxt);
+ return(NULL);
+ }
+ obj = valuePop(ctxt);
+ ret = obj->user;
+ xmlXPathFreeObject(obj);
+ return(ret);
+}
+
+/*
+ * Macros for accessing the content. Those should be used only by the parser,
+ * and not exported.
+ *
+ * Dirty macros, i.e. one need to make assumption on the context to use them
+ *
+ * CUR_PTR return the current pointer to the xmlChar to be parsed.
+ * CUR returns the current xmlChar value, i.e. a 8 bit value
+ * in ISO-Latin or UTF-8.
+ * This should be used internally by the parser
+ * only to compare to ASCII values otherwise it would break when
+ * running with UTF-8 encoding.
+ * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
+ * to compare on ASCII based substring.
+ * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
+ * strings within the parser.
+ * CURRENT Returns the current char value, with the full decoding of
+ * UTF-8 if we are using this mode. It returns an int.
+ * NEXT Skip to the next character, this does the proper decoding
+ * in UTF-8 mode. It also pop-up unfinished entities on the fly.
+ * It returns the pointer to the current xmlChar.
+ */
+
+#define CUR (*ctxt->cur)
+#define SKIP(val) ctxt->cur += (val)
+#define NXT(val) ctxt->cur[(val)]
+#define CUR_PTR ctxt->cur
+#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
+
+#define COPY_BUF(len,b,i,v) \
+ if (len == 1) b[i++] = (xmlChar) v; \
+ else i += xmlCopyChar(len,&b[i],v)
+
+#define NEXTL(k) ctxt->cur += k
+
+// XMLENGINE: NEXT was replaced with ctxt->cur++ since *ctxt->cur always !=0 there
+// OOM: never
+#define SKIP_BLANKS while (IS_BLANK_CH(*(ctxt->cur))) ctxt->cur++
+
+#define CURRENT (*ctxt->cur)
+#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
+
+
+#ifndef DBL_DIG
+#define DBL_DIG 16
+#endif
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 1E-9
+#endif
+
+#define UPPER_DOUBLE 1E9
+#define LOWER_DOUBLE 1E-5
+
+#define INTEGER_DIGITS DBL_DIG
+#define FRACTION_DIGITS (DBL_DIG + 1)
+#define EXPONENT_DIGITS (3 + 2)
+
+#if (_MSC_VER >= 1300) && (WINVER < 0x0500)
+//VC7 or later, building with pre-VC7 runtime libraries
+//extern "C" long _ftol( double ); //defined by VC6 C libs
+//extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); }
+#endif
+
+/**
+ * xmlXPathFormatNumber:
+ * @param number number to format
+ * @param buffer output buffer
+ * @param buffersize size of output buffer
+ *
+ * Convert the number into a string representation.
+ *
+ * OOM: never
+ */
+static void
+xmlXPathFormatNumber(double number, char buffer[], int buffersize)
+{
+
+ switch (xmlXPathIsInf(number)) {
+ case 1:
+ if (buffersize > (int)sizeof("Infinity"))
+ snprintf(buffer, buffersize, "Infinity");
+ break;
+ case -1:
+ if (buffersize > (int)sizeof("-Infinity"))
+ snprintf(buffer, buffersize, "-Infinity");
+ break;
+ default:
+ if (xmlXPathIsNaN(number)) {
+ if (buffersize > (int)sizeof("NaN"))
+ snprintf(buffer, buffersize, "NaN");
+ } else
+ if (number == 0 && xmlXPathGetSign(number) != 0) {
+ snprintf(buffer, buffersize, "0");
+ } else
+ if (number == ((int) number)) {
+ char work[30];
+ char *ptr, *cur;
+ int res, value = (int) number;
+
+ ptr = &buffer[0];
+ if (value < 0) {
+ *ptr++ = '-';
+ value = -value;
+ }
+ if (value == 0) {
+ *ptr++ = '0';
+ } else {
+ cur = &work[0];
+ while (value != 0) {
+ res = value % 10;
+ value = value / 10;
+ *cur++ = '0' + res;
+ }
+ cur--;
+ while ((cur >= &work[0]) && (ptr - buffer < buffersize)) {
+ *ptr++ = *cur--;
+ }
+ }
+ if (ptr - buffer < buffersize) {
+ *ptr = 0;
+ } else if (buffersize > 0) {
+ ptr--;
+ *ptr = 0;
+ }
+ } else {
+ /* 3 is sign, decimal point, and terminating zero */
+ char work[DBL_DIG + EXPONENT_DIGITS + 3];
+ int integer_place, fraction_place;
+ char *ptr;
+ char *after_fraction;
+ double absolute_value;
+ int size;
+
+ absolute_value = fabs(number);
+
+ /*
+ * First choose format - scientific or regular floating point.
+ * In either case, result is in work, and after_fraction points
+ * just past the fractional part.
+ */
+ if (((absolute_value > UPPER_DOUBLE) || (absolute_value < LOWER_DOUBLE)) &&
+ (absolute_value != 0.0))
+ {
+ /* Use scientific notation */
+ integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
+ fraction_place = DBL_DIG - 1;
+ snprintf(work, sizeof(work),"%*.*e",
+ integer_place, fraction_place, number);
+ after_fraction = strchr(work + DBL_DIG, 'e');
+ }
+ else {
+ /* Use regular notation */
+ if (absolute_value > 0.0)
+ integer_place = 1 + (int)log10(absolute_value);
+ else
+ integer_place = 0;
+
+ fraction_place = (integer_place > 0)
+ ? DBL_DIG - integer_place
+ : DBL_DIG;
+ size = snprintf(work, sizeof(work), "%0.*f", fraction_place, number);
+ after_fraction = work + size;
+ }
+
+ /* Remove fractional trailing zeroes */
+ ptr = after_fraction;
+ while (*(--ptr) == '0') {}// EMPTY LOOP
+
+ if (*ptr != '.')
+ ptr++;
+ while ((*ptr++ = *after_fraction++) != 0){} // EMPTY LOOP
+
+ /* Finally copy result back to caller */
+ size = strlen(work) + 1;
+ if (size > buffersize) {
+ work[buffersize - 1] = 0;
+ size = buffersize;
+ }
+ memmove(buffer, work, size);
+ }
+ break;
+ }
+}
+
+
+/************************************************************************
+ * *
+ * Routines to handle NodeSets *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathOrderDocElems:
+ * @param doc an input document
+ *
+ * Call this routine to speed up XPath computation on static documents.
+ * This stamps all the element nodes with the document order
+ * Like for line information, the order is kept in the element->content
+ * field, the value stored is actually - the node number (starting at -1)
+ * to be able to differentiate from line numbers.
+ *
+ * Returns the number of elements found in the document or -1 in case
+ * of error.
+ */
+XMLPUBFUNEXPORT long
+xmlXPathOrderDocElems(xmlDocPtr doc) {
+ long count = 0;
+ xmlNodePtr cur;
+
+ if (!doc)
+ return(-1);
+ cur = doc->children;
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ cur->content = (xmlChar*)((void *) (-(++count)));
+ if (cur->children != NULL) {
+ cur = cur->children;
+ continue;
+ }
+ }
+ if (cur->next) {
+ cur = cur->next;
+ continue;
+ }
+ do {
+ cur = cur->parent;
+ if (!cur)
+ break;
+ if (cur == (xmlNodePtr) doc) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur);
+ }
+ return(count);
+}
+
+/**
+ * xmlXPathCmpNodes:
+ * @param node1 the first node
+ * @param node2 the second node
+ *
+ * Compare two nodes w.r.t document order
+ *
+ * Returns -2 in case of error 1 if first point < second point, 0 if
+ * it's the same node, -1 otherwise
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
+ int depth1, depth2;
+ int attr1 = 0, attr2 = 0;
+ xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
+ xmlNodePtr cur, root;
+
+ if ((node1 == NULL) || (node2 == NULL))
+ return(-2);
+ /*
+ * a couple of optimizations which will avoid computations in most cases
+ */
+ if (node1->type == XML_ATTRIBUTE_NODE) {
+ attr1 = 1;
+ attrNode1 = node1;
+ node1 = node1->parent;
+ }
+ if (node2->type == XML_ATTRIBUTE_NODE) {
+ attr2 = 1;
+ attrNode2 = node2;
+ node2 = node2->parent;
+ }
+ if (node1 == node2) {
+ if (attr1 == attr2) {
+ /* not required, but we keep attributes in order */
+ if (attr1 != 0) {
+ cur = attrNode2->prev;
+ while (cur != NULL) {
+ if (cur == attrNode1)
+ return (1);
+ cur = cur->prev;
+ }
+ return (-1);
+ }
+ return(0);
+ }
+ if (attr2 == 1)
+ return(1);
+ return(-1);
+ }
+ if ((node1->type == XML_NAMESPACE_DECL) ||
+ (node2->type == XML_NAMESPACE_DECL))
+ return(1);
+ if (node1 == node2->prev)
+ return(1);
+ if (node1 == node2->next)
+ return(-1);
+
+ /*
+ * Speedup using document order if availble.
+ */
+ if ((node1->type == XML_ELEMENT_NODE) &&
+ (node2->type == XML_ELEMENT_NODE) &&
+ (0 > (long) node1->content) &&
+ (0 > (long) node2->content) &&
+ (node1->doc == node2->doc)) {
+ long l1, l2;
+
+ l1 = -((long) node1->content);
+ l2 = -((long) node2->content);
+ if (l1 < l2)
+ return(1);
+ if (l1 > l2)
+ return(-1);
+ }
+
+ /*
+ * compute depth to root
+ */
+ for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
+ if (cur == node1)
+ return(1);
+ depth2++;
+ }
+ root = cur;
+ for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
+ if (cur == node2)
+ return(-1);
+ depth1++;
+ }
+ /*
+ * Distinct document (or distinct entities :-( ) case.
+ */
+ if (root != cur) {
+ return(-2);
+ }
+ /*
+ * get the nearest common ancestor.
+ */
+ while (depth1 > depth2) {
+ depth1--;
+ node1 = node1->parent;
+ }
+ while (depth2 > depth1) {
+ depth2--;
+ node2 = node2->parent;
+ }
+ while (node1->parent != node2->parent) {
+ node1 = node1->parent;
+ node2 = node2->parent;
+ /* should not happen but just in case ... */
+ if ((node1 == NULL) || (node2 == NULL))
+ return(-2);
+ }
+ /*
+ * Find who's first.
+ */
+ if (node1 == node2->prev)
+ return(1);
+ if (node1 == node2->next)
+ return(-1);
+ /*
+ * Speedup using document order if availble.
+ */
+ if ((node1->type == XML_ELEMENT_NODE) &&
+ (node2->type == XML_ELEMENT_NODE) &&
+ (0 > (long) node1->content) &&
+ (0 > (long) node2->content) &&
+ (node1->doc == node2->doc)) {
+ long l1, l2;
+
+ l1 = -((long) node1->content);
+ l2 = -((long) node2->content);
+ if (l1 < l2)
+ return(1);
+ if (l1 > l2)
+ return(-1);
+ }
+
+ for (cur = node1->next;cur != NULL;cur = cur->next)
+ if (cur == node2)
+ return(1);
+ return(-1); /* assume there is no sibling list corruption */
+}
+
+/**
+ * xmlXPathNodeSetSort:
+ * @param set the node set
+ *
+ * Sort the node set in document order
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetSort(xmlNodeSetPtr set) {
+ int i, j, incr, len;
+ xmlNodePtr tmp;
+
+ if (set == NULL)
+ return;
+
+ /* Use Shell's sort to sort the node-set */
+ len = set->nodeNr;
+ for (incr = len / 2; incr > 0; incr /= 2) {
+ for (i = incr; i < len; i++) {
+ j = i - incr;
+ while (j >= 0) {
+ if (xmlXPathCmpNodes(set->nodeTab[j], set->nodeTab[j + incr]) == -1)
+ {
+ tmp = set->nodeTab[j];
+ set->nodeTab[j] = set->nodeTab[j + incr];
+ set->nodeTab[j + incr] = tmp;
+ j -= incr;
+ } else
+ break;
+ }
+ }
+ }
+}
+
+#define XML_NODESET_DEFAULT 10
+/**
+ * xmlXPathNodeSetDupNs:
+ * @param node the parent node of the namespace XPath node
+ * @param ns the libxml namespace declaration node.
+ *
+ * Namespace node in libxml don't match the XPath semantic. In a node set
+ * the namespace nodes are duplicated and the next pointer is set to the
+ * parent node in the XPath semantic.
+ *
+ * Returns the newly created object.
+ *
+ * OOM: possible --> returns NULL for valid arguments, sets OOM flag
+ */
+static xmlNodePtr
+xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
+ xmlNsPtr cur;
+
+ if (!ns || ns->type != XML_NAMESPACE_DECL)
+ return(NULL);
+ if (!node || node->type == XML_NAMESPACE_DECL)
+ return((xmlNodePtr) ns);
+
+ /*
+ * Allocate a new Namespace and fill the fields.
+ */
+ cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
+ if (!cur) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("duplicating namespace\n"));
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xmlNs));
+ cur->type = XML_NAMESPACE_DECL;
+ if (ns->href)
+ {
+ cur->href = xmlStrdup(ns->href);
+ if(!cur->href)
+ goto OOM;
+ }
+ if (ns->prefix)
+ {
+ cur->prefix = xmlStrdup(ns->prefix);
+ if(!cur->prefix)
+ goto OOM;
+ }
+ cur->next = (xmlNsPtr) node;
+ return((xmlNodePtr) cur);
+//---------------------
+OOM:
+ xmlFree(cur);
+ return NULL;
+}
+
+/**
+ * xmlXPathNodeSetFreeNs:
+ * @param ns the XPath namespace node found in a nodeset.
+ *
+ * Namespace nodes in libxml don't match the XPath semantic. In a node set
+ * the namespace nodes are duplicated and the next pointer is set to the
+ * parent node in the XPath semantic. Check if such a node needs to be freed
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
+ if (!ns || ns->type != XML_NAMESPACE_DECL)
+ return;
+
+ if (ns->next && ns->next->type != XML_NAMESPACE_DECL) {
+ if (ns->href)
+ xmlFree((xmlChar*)ns->href);
+ if (ns->prefix)
+ xmlFree((xmlChar*)ns->prefix);
+ xmlFree(ns);
+ }
+}
+
+/**
+ * xmlXPathNodeSetCreate:
+ * @param val an initial xmlNodePtr, or NULL
+ *
+ * Create a new xmlNodeSetPtr of type double and of value val
+ *
+ * Returns the newly created object.
+ *
+ * OOM: possible --> returns NULL and sets OOM flag
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeSetCreate(xmlNodePtr val) {
+ xmlNodeSetPtr ret;
+ LOAD_GS_DIRECT
+
+ ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
+
+ if (val) {
+ xmlNodePtr nval;
+
+ ret->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
+ if (!ret->nodeTab) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
+ goto OOM;
+ }
+ memset(ret->nodeTab, 0, XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ ret->nodeMax = XML_NODESET_DEFAULT;
+ if (val->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) val;
+ nval = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
+
+ if(OOM_FLAG)
+ goto OOM;
+ } else {
+ nval = val;
+ }
+ ret->nodeTab[ret->nodeNr++] = nval;
+ }
+ return(ret);
+//-------------------
+OOM:
+ xmlFree(ret);
+ return(NULL);
+}
+
+/**
+ * xmlXPathNodeSetContains:
+ * @param cur the node-set
+ * @param val the node
+ *
+ * checks whether cur contains val
+ *
+ * Returns true (1) if cur contains val, false (0) otherwise
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT int
+xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
+ int i;
+
+ if (val->type == XML_NAMESPACE_DECL) {
+ for (i = 0; i < cur->nodeNr; i++) {
+ if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns1, ns2;
+
+ ns1 = (xmlNsPtr) val;
+ ns2 = (xmlNsPtr) cur->nodeTab[i];
+ if (ns1 == ns2)
+ return(1);
+ if ((ns1->next != NULL) &&
+ (ns2->next == ns1->next) &&
+ (xmlStrEqual(ns1->prefix, ns2->prefix)))
+ return(1);
+ }
+ }
+ } else {
+ for (i = 0; i < cur->nodeNr; i++) {
+ if (cur->nodeTab[i] == val)
+ return(1);
+ }
+ }
+ return(0);
+}
+
+
+
+/*
+ OPTIMIZATION: xmlXPathNodeSetAddNs, xmlXPathNodeSetAdd and xmlXPathNodeSetAddUnique are the same code
+
+*/
+
+/**
+ * xmlXPathNodeSetAddNs:
+ * @param cur the initial node set
+ * @param node the hosting node
+ * @param ns a the namespace node
+ *
+ * add a new namespace node to an existing NodeSet
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns)
+{
+ int i;
+
+ if (!ns || !node ||
+ (ns->type != XML_NAMESPACE_DECL) ||
+ (node->type != XML_ELEMENT_NODE))
+ return;
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ /*
+ * prevent duplicates
+ */
+ for (i = 0;i < cur->nodeNr;i++) {
+ if (cur->nodeTab[i] &&
+ (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
+ (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
+ (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
+ {
+ return;
+ }
+ }
+
+ /*
+ * grow the nodeTab if needed
+ */
+ if (cur->nodeMax == 0) {
+ cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NODESET_DEFAULT *
+ sizeof(xmlNodePtr));
+ if (!cur->nodeTab) {
+OOM_exit:
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
+ return;
+ }
+ memset(cur->nodeTab, 0 ,
+ XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ cur->nodeMax = XML_NODESET_DEFAULT;
+ } else if (cur->nodeNr == cur->nodeMax) {
+ xmlNodePtr *temp;
+
+ cur->nodeMax *= 2;
+ temp = (xmlNodePtr*) xmlRealloc(cur->nodeTab, cur->nodeMax *
+ sizeof(xmlNodePtr));
+ if (!temp) {
+ cur->nodeMax /= 2;
+ goto OOM_exit;
+ }
+ cur->nodeTab = temp;
+ }
+ cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
+}
+
+/**
+ * xmlXPathNodeSetAdd:
+ * @param cur the initial node set
+ * @param val a new xmlNodePtr
+ *
+ * add a new xmlNodePtr to an existing NodeSet
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
+ int i;
+
+ if (!val) return;
+
+#if 0
+ if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
+ return; /* an XSLT fake node */
+#endif
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ /*
+ * prevent duplcates
+ */
+ for (i = 0;i < cur->nodeNr;i++)
+ if (cur->nodeTab[i] == val) return;
+
+ /*
+ * grow the nodeTab if needed
+ */
+ if (cur->nodeMax == 0)
+ {
+ cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
+ if (!cur->nodeTab) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
+ return;
+ }
+ memset(cur->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ cur->nodeMax = XML_NODESET_DEFAULT;
+ }
+ else if (cur->nodeNr == cur->nodeMax)
+ {
+ xmlNodePtr* temp;
+
+ cur->nodeMax *= 2;
+ temp = (xmlNodePtr*) xmlRealloc(cur->nodeTab, cur->nodeMax * sizeof(xmlNodePtr));
+ if (!temp) {
+ cur->nodeMax /= 2;
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
+ return;
+ }
+ cur->nodeTab = temp;
+ }
+
+ if (val->type == XML_NAMESPACE_DECL)
+ {
+ xmlNsPtr ns = (xmlNsPtr) val;
+
+ cur->nodeTab[cur->nodeNr++] =
+ xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
+ }
+ else
+ {
+ cur->nodeTab[cur->nodeNr++] = val;
+ }
+}
+
+/**
+ * xmlXPathNodeSetAddUnique:
+ * @param cur the initial node set
+ * @param val a new xmlNodePtr
+ *
+ * add a new xmlNodePtr to an existing NodeSet, optimized version
+ * when we are sure the node is not already in the set.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
+ if (val == NULL) return;
+
+
+#if 0
+ if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
+ return; /* an XSLT fake node */
+#endif
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ /*
+ * grow the nodeTab if needed
+ */
+ if (cur->nodeMax == 0) {
+ cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
+
+ if (cur->nodeTab == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
+ return;
+ }
+ memset(cur->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ cur->nodeMax = XML_NODESET_DEFAULT;
+ } else if (cur->nodeNr == cur->nodeMax) {
+ xmlNodePtr *temp;
+
+ cur->nodeMax *= 2;
+ temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * sizeof(xmlNodePtr));
+ if (temp == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
+ return;
+ }
+ cur->nodeTab = temp;
+ }
+ if (val->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) val;
+
+ cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
+ } else
+ cur->nodeTab[cur->nodeNr++] = val;
+}
+
+/**
+ * xmlXPathNodeSetMerge:
+ * @param val1 the first NodeSet or NULL
+ * @param val2 the second NodeSet
+ *
+ * Merges two nodesets, all nodes from val2 are added to val1
+ * if val1 is NULL, a new set is created and copied from val2
+ *
+ * Returns val1 once extended or NULL in case of error.
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
+ int i, j, initNr, skip;
+
+ if (!val2)
+ return(val1);
+ if (!val1) {
+ val1 = xmlXPathNodeSetCreate(NULL);
+ }
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ initNr = val1->nodeNr;
+
+ for (i = 0;i < val2->nodeNr;i++) {
+ /*
+ * check against duplicates
+ */
+ skip = 0;
+ for (j = 0; j < initNr; j++) {
+ if (val1->nodeTab[j] == val2->nodeTab[i]) {
+ skip = 1;
+ break;
+ } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) &&
+ (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) {
+ xmlNsPtr ns1, ns2;
+ ns1 = (xmlNsPtr) val1->nodeTab[j];
+ ns2 = (xmlNsPtr) val2->nodeTab[i];
+ if ((ns1->next == ns2->next) &&
+ (xmlStrEqual(ns1->prefix, ns2->prefix))) {
+ skip = 1;
+ break;
+ }
+ }
+ }
+ if (skip)
+ continue;
+
+ /*
+ * grow the nodeTab if needed
+ */
+ if (val1->nodeMax == 0) {
+ val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
+ sizeof(xmlNodePtr));
+ if (val1->nodeTab == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
+ return(NULL);
+ }
+ memset(val1->nodeTab, 0 ,
+ XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ val1->nodeMax = XML_NODESET_DEFAULT;
+ } else if (val1->nodeNr == val1->nodeMax) {
+ xmlNodePtr *temp;
+
+ val1->nodeMax *= 2;
+ temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
+ sizeof(xmlNodePtr));
+ if (temp == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
+ return(NULL);
+ }
+ val1->nodeTab = temp;
+ }
+ if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
+
+ val1->nodeTab[val1->nodeNr++] =
+ xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
+ } else
+ val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
+ }
+
+ return(val1);
+}
+
+/**
+ * xmlXPathNodeSetMergeUnique:
+ * @param val1 the first NodeSet or NULL
+ * @param val2 the second NodeSet
+ *
+ * Merges two nodesets, all nodes from val2 are added to val1
+ * if val1 is NULL, a new set is created and copied from val2
+ *
+ * Returns val1 once extended or NULL in case of error.
+ */
+static xmlNodeSetPtr
+xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2)
+{
+ int i;
+
+ if (val2 == NULL) return(val1);
+ if (val1 == NULL) {
+ val1 = xmlXPathNodeSetCreate(NULL);
+ }
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+
+ for (i = 0;i < val2->nodeNr;i++) {
+ /*
+ * grow the nodeTab if needed
+ */
+ if (val1->nodeMax == 0) {
+ val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
+ sizeof(xmlNodePtr));
+ if (val1->nodeTab == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
+ return(NULL);
+ }
+ memset(val1->nodeTab, 0 ,
+ XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
+ val1->nodeMax = XML_NODESET_DEFAULT;
+ } else if (val1->nodeNr == val1->nodeMax) {
+ xmlNodePtr *temp;
+
+ val1->nodeMax *= 2;
+ temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
+ sizeof(xmlNodePtr));
+ if (temp == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
+ return(NULL);
+ }
+ val1->nodeTab = temp;
+ }
+ if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
+
+ val1->nodeTab[val1->nodeNr++] =
+ xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
+ } else
+ val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
+ }
+
+ return(val1);
+}
+
+/**
+ * xmlXPathNodeSetDel:
+ * @param cur the initial node set
+ * @param val an xmlNodePtr
+ *
+ * Removes an xmlNodePtr from an existing NodeSet
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
+ int i;
+
+ if (cur == NULL) return;
+ if (val == NULL) return;
+
+ /*
+ * find node in nodeTab
+ */
+ for (i = 0;i < cur->nodeNr;i++)
+ if (cur->nodeTab[i] == val) break;
+
+ if (i >= cur->nodeNr) { /* not found */
+#ifdef DEBUG
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
+ val->name);
+#endif
+ return;
+ }
+ if ((cur->nodeTab[i] != NULL) &&
+ (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
+ xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
+ cur->nodeNr--;
+ for (;i < cur->nodeNr;i++)
+ cur->nodeTab[i] = cur->nodeTab[i + 1];
+ cur->nodeTab[cur->nodeNr] = NULL;
+}
+
+/**
+ * xmlXPathNodeSetRemove:
+ * @param cur the initial node set
+ * @param val the index to remove
+ *
+ * Removes an entry from an existing NodeSet list.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
+ if (!cur || val >= cur->nodeNr)
+ return;
+
+ if (cur->nodeTab[val] &&
+ cur->nodeTab[val]->type == XML_NAMESPACE_DECL)
+ {
+ xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
+ }
+ cur->nodeNr--;
+ for (;val < cur->nodeNr;val++)
+ cur->nodeTab[val] = cur->nodeTab[val + 1];
+ cur->nodeTab[cur->nodeNr] = NULL;
+}
+
+/**
+ * xmlXPathFreeNodeSet:
+ * @param obj the xmlNodeSetPtr to free
+ *
+ * Free the NodeSet compound (not the actual nodes !).
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
+ if (!obj)
+ return;
+ if (obj->nodeTab)
+ {
+ int i;
+
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ for (i = 0; i < obj->nodeNr; i++)
+ if (obj->nodeTab[i] &&
+ obj->nodeTab[i]->type == XML_NAMESPACE_DECL)
+ {
+ xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
+ }
+ xmlFree(obj->nodeTab);
+ }
+ xmlFree(obj);
+}
+
+/**
+ * xmlXPathFreeValueTree:
+ * @param obj the xmlNodeSetPtr to free
+ *
+ * Free the NodeSet compound and the actual tree, this is different
+ * from xmlXPathFreeNodeSet()
+ */
+static void
+xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
+ int i;
+
+ if (!obj)
+ return;
+
+ if (obj->nodeTab) {
+ for (i = 0;i < obj->nodeNr;i++) {
+ if (obj->nodeTab[i]) {
+ if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
+ xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
+ } else {
+ xmlFreeNodeList(obj->nodeTab[i]);
+ }
+ }
+ }
+ xmlFree(obj->nodeTab);
+ }
+ xmlFree(obj);
+}
+
+#if defined(DEBUG) || defined(DEBUG_STEP)
+/**
+ * xmlGenericErrorContextNodeSet:
+ * @param output a FILE * for the output
+ * @param obj the xmlNodeSetPtr to display
+ *
+ * Quick display of a NodeSet
+ */
+void
+xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
+ int i;
+
+ if (output == NULL) output = xmlGenericErrorContext;
+ if (obj == NULL) {
+ fprintf(output, "NodeSet == NULL !\n");
+ return;
+ }
+ if (obj->nodeNr == 0) {
+ fprintf(output, "NodeSet is empty\n");
+ return;
+ }
+ if (obj->nodeTab == NULL) {
+ fprintf(output, " nodeTab == NULL !\n");
+ return;
+ }
+ for (i = 0; i < obj->nodeNr; i++) {
+ if (obj->nodeTab[i] == NULL) {
+ fprintf(output, " NULL !\n");
+ return;
+ }
+ if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
+ (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
+ fprintf(output, " /");
+ else if (obj->nodeTab[i]->name == NULL)
+ fprintf(output, " noname!");
+ else fprintf(output, " %s", obj->nodeTab[i]->name);
+ }
+ fprintf(output, "\n");
+}
+#endif
+
+/**
+ * xmlXPathNewNodeSet:
+ * @param val the NodePtr value
+ *
+ * Create a new xmlXPathObjectPtr of type NodeSet and initialize
+ * it with the single Node val
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewNodeSet(xmlNodePtr val) {
+ xmlXPathObjectPtr ret;
+ LOAD_GS_DIRECT
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_NODESET;
+ ret->boolval = 0;
+ ret->nodesetval = xmlXPathNodeSetCreate(val);
+ if(OOM_FLAG)
+ {
+ xmlXPathFreeObject(ret);
+ return(NULL);
+ }
+ /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+ return(ret);
+}
+
+/**
+ * xmlXPathNewValueTree:
+ * @param val the NodePtr value
+ *
+ * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
+ * it with the tree root val
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewValueTree(xmlNodePtr val) {
+ xmlXPathObjectPtr ret;
+ LOAD_GS_DIRECT
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating result value tree\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_XSLT_TREE;
+ ret->boolval = 1;
+ ret->user = (void *) val;
+ ret->nodesetval = xmlXPathNodeSetCreate(val);
+ if(OOM_FLAG)
+ {
+ xmlXPathFreeObject(ret);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathNewNodeSetList:
+ * @param val an existing NodeSet
+ *
+ * Create a new xmlXPathObjectPtr of type NodeSet and initialize
+ * it with the Nodeset val
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewNodeSetList(xmlNodeSetPtr val)
+{
+ xmlXPathObjectPtr ret;
+ int i;
+
+ if (!val)
+ ret = NULL;
+ else if (!val->nodeTab)
+ ret = xmlXPathNewNodeSet(NULL);
+ else {
+ ret = xmlXPathNewNodeSet(val->nodeTab[0]);
+ for (i = 1; i < val->nodeNr; ++i)
+ xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
+ }
+
+ return (ret);
+}
+
+/**
+ * xmlXPathWrapNodeSet:
+ * @param val the NodePtr value
+ *
+ * Wrap the Nodeset val in a new xmlXPathObjectPtr
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating node set object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_NODESET;
+ ret->nodesetval = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeNodeSetList:
+ * @param obj an existing NodeSetList object
+ *
+ * Free up the xmlXPathObjectPtr obj but don't deallocate the objects in
+ * the list contrary to xmlXPathFreeObject().
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
+ if (obj)
+ xmlFree(obj);
+}
+
+/**
+ * xmlXPathDifference:
+ * @param nodes1 a node-set
+ * @param nodes2 a node-set
+ *
+ * Implements the EXSLT - Sets difference() function:
+ * node-set set:difference (node-set, node-set)
+ *
+ * Returns the difference between the two node sets, or nodes1 if
+ * nodes2 is empty
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ xmlNodeSetPtr ret;
+ int i, l1;
+ xmlNodePtr cur;
+
+ if (xmlXPathNodeSetIsEmpty(nodes2))
+ return(nodes1);
+
+ ret = xmlXPathNodeSetCreate(NULL);
+ if (xmlXPathNodeSetIsEmpty(nodes1))
+ return(ret);
+
+ l1 = xmlXPathNodeSetGetLength(nodes1);
+
+ for (i = 0; i < l1; i++) {
+ cur = xmlXPathNodeSetItem(nodes1, i);
+ if (!xmlXPathNodeSetContains(nodes2, cur))
+ xmlXPathNodeSetAddUnique(ret, cur);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathIntersection:
+ * @param nodes1 a node-set
+ * @param nodes2 a node-set
+ *
+ * Implements the EXSLT - Sets intersection() function:
+ * node-set set:intersection (node-set, node-set)
+ *
+ * Returns a node set comprising the nodes that are within both the
+ * node sets passed as arguments
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
+ int i, len;
+ xmlNodePtr cur;
+
+ if (xmlXPathNodeSetIsEmpty(nodes1) ||
+ xmlXPathNodeSetIsEmpty(nodes2))
+ {
+ return(ret);
+ }
+ len = xmlXPathNodeSetGetLength(nodes1);
+
+ // [more efficient search in xmlXPathNodeSetContains() with smaller nodeset ]
+ for (i = 0; i < len; i++) {
+ cur = xmlXPathNodeSetItem(nodes1, i);
+ if (xmlXPathNodeSetContains(nodes2, cur))
+ xmlXPathNodeSetAddUnique(ret, cur);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathDistinctSorted:
+ * @param nodes a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets distinct() function:
+ * node-set set:distinct (node-set)
+ *
+ * Returns a subset of the nodes contained in nodes, or nodes if
+ * it is empty
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathDistinctSorted (xmlNodeSetPtr nodes)
+{
+ xmlNodeSetPtr ret;
+ xmlHashTablePtr hash;
+ int i, l;
+ xmlChar * strval;
+ xmlNodePtr cur;
+
+ if (xmlXPathNodeSetIsEmpty(nodes))
+ return(nodes);
+
+ ret = xmlXPathNodeSetCreate(NULL);
+ l = xmlXPathNodeSetGetLength(nodes);
+ hash = xmlHashCreate (l);
+ for (i = 0; i < l; i++) {
+ cur = xmlXPathNodeSetItem(nodes, i);
+ strval = xmlXPathCastNodeToString(cur);
+ if (xmlHashLookup(hash, strval) == NULL) {
+ xmlHashAddEntry(hash, strval, strval);
+ xmlXPathNodeSetAddUnique(ret, cur);
+ } else {
+ xmlFree(strval);
+ }
+ }
+ xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
+ return(ret);
+}
+
+/**
+ * xmlXPathDistinct:
+ * @param nodes a node-set
+ *
+ * Implements the EXSLT - Sets distinct() function:
+ * node-set set:distinct (node-set)
+ * nodes is sorted by document order, then #exslSetsDistinctSorted
+ * is called with the sorted node-set
+ *
+ * Returns a subset of the nodes contained in nodes, or nodes if
+ * it is empty
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathDistinct (xmlNodeSetPtr nodes) {
+ if (xmlXPathNodeSetIsEmpty(nodes))
+ return(nodes);
+
+ xmlXPathNodeSetSort(nodes);
+ return(xmlXPathDistinctSorted(nodes));
+}
+
+/**
+ * xmlXPathHasSameNodes:
+ * @param nodes1 a node-set
+ * @param nodes2 a node-set
+ *
+ * Implements the EXSLT - Sets has-same-nodes function:
+ * boolean set:has-same-node(node-set, node-set)
+ *
+ * Returns true (1) if nodes1 shares any node with nodes2, false (0)
+ * otherwise
+ */
+XMLPUBFUNEXPORT int
+xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ int i, l;
+ xmlNodePtr cur;
+
+ if (xmlXPathNodeSetIsEmpty(nodes1) ||
+ xmlXPathNodeSetIsEmpty(nodes2))
+ return(0);
+
+ l = xmlXPathNodeSetGetLength(nodes1);
+ for (i = 0; i < l; i++) {
+ cur = xmlXPathNodeSetItem(nodes1, i);
+ if (xmlXPathNodeSetContains(nodes2, cur))
+ return(1);
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathNodeLeadingSorted:
+ * @param nodes a node-set, sorted by document order
+ * @param node a node
+ *
+ * Implements the EXSLT - Sets leading() function:
+ * node-set set:leading (node-set, node-set)
+ *
+ * Returns the nodes in nodes that precede node in document order,
+ * nodes if node is NULL or an empty node-set if nodes
+ * doesn't contain node
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
+ int i, l;
+ xmlNodePtr cur;
+ xmlNodeSetPtr ret;
+
+ if (node == NULL)
+ return(nodes);
+
+ ret = xmlXPathNodeSetCreate(NULL);
+ if (xmlXPathNodeSetIsEmpty(nodes) ||
+ (!xmlXPathNodeSetContains(nodes, node)))
+ return(ret);
+
+ l = xmlXPathNodeSetGetLength(nodes);
+ for (i = 0; i < l; i++) {
+ cur = xmlXPathNodeSetItem(nodes, i);
+ if (cur == node)
+ break;
+ xmlXPathNodeSetAddUnique(ret, cur);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathNodeLeading:
+ * @param nodes a node-set
+ * @param node a node
+ *
+ * Implements the EXSLT - Sets leading() function:
+ * node-set set:leading (node-set, node-set)
+ * nodes is sorted by document order, then #exslSetsNodeLeadingSorted
+ * is called.
+ *
+ * Returns the nodes in nodes that precede node in document order,
+ * nodes if node is NULL or an empty node-set if nodes
+ * doesn't contain node
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
+ xmlXPathNodeSetSort(nodes);
+ return(xmlXPathNodeLeadingSorted(nodes, node));
+}
+
+/**
+ * xmlXPathLeadingSorted:
+ * @param nodes1 a node-set, sorted by document order
+ * @param nodes2 a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets leading() function:
+ * node-set set:leading (node-set, node-set)
+ *
+ * Returns the nodes in nodes1 that precede the first node in nodes2
+ * in document order, nodes1 if nodes2 is NULL or empty or
+ * an empty node-set if nodes1 doesn't contain nodes2
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ if (xmlXPathNodeSetIsEmpty(nodes2))
+ return(nodes1);
+ return(xmlXPathNodeLeadingSorted(nodes1,
+ xmlXPathNodeSetItem(nodes2, 1)));
+}
+
+/**
+ * xmlXPathLeading:
+ * @param nodes1 a node-set
+ * @param nodes2 a node-set
+ *
+ * Implements the EXSLT - Sets leading() function:
+ * node-set set:leading (node-set, node-set)
+ * nodes1 and nodes2 are sorted by document order, then
+ * #exslSetsLeadingSorted is called.
+ *
+ * Returns the nodes in nodes1 that precede the first node in nodes2
+ * in document order, nodes1 if nodes2 is NULL or empty or
+ * an empty node-set if nodes1 doesn't contain nodes2
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ if (xmlXPathNodeSetIsEmpty(nodes2))
+ return(nodes1);
+ if (xmlXPathNodeSetIsEmpty(nodes1))
+ return(xmlXPathNodeSetCreate(NULL));
+ xmlXPathNodeSetSort(nodes1);
+ xmlXPathNodeSetSort(nodes2);
+ return(xmlXPathNodeLeadingSorted(nodes1,
+ xmlXPathNodeSetItem(nodes2, 1)));
+}
+
+/**
+ * xmlXPathNodeTrailingSorted:
+ * @param nodes a node-set, sorted by document order
+ * @param node a node
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ * node-set set:trailing (node-set, node-set)
+ *
+ * Returns the nodes in nodes that follow node in document order,
+ * nodes if node is NULL or an empty node-set if nodes
+ * doesn't contain node
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
+ int i, l;
+ xmlNodePtr cur;
+ xmlNodeSetPtr ret;
+
+ if (node == NULL)
+ return(nodes);
+
+ ret = xmlXPathNodeSetCreate(NULL);
+ if (xmlXPathNodeSetIsEmpty(nodes) ||
+ (!xmlXPathNodeSetContains(nodes, node)))
+ return(ret);
+
+ l = xmlXPathNodeSetGetLength(nodes);
+ for (i = l; i > 0; i--) {
+ cur = xmlXPathNodeSetItem(nodes, i);
+ if (cur == node)
+ break;
+ xmlXPathNodeSetAddUnique(ret, cur);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathNodeTrailing:
+ * @param nodes a node-set
+ * @param node a node
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ * node-set set:trailing (node-set, node-set)
+ * nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
+ * is called.
+ *
+ * Returns the nodes in nodes that follow node in document order,
+ * nodes if node is NULL or an empty node-set if nodes
+ * doesn't contain node
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
+ xmlXPathNodeSetSort(nodes);
+ return(xmlXPathNodeTrailingSorted(nodes, node));
+}
+
+/**
+ * xmlXPathTrailingSorted:
+ * @param nodes1 a node-set, sorted by document order
+ * @param nodes2 a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ * node-set set:trailing (node-set, node-set)
+ *
+ * Returns the nodes in nodes1 that follow the first node in nodes2
+ * in document order, nodes1 if nodes2 is NULL or empty or
+ * an empty node-set if nodes1 doesn't contain nodes2
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+ if (xmlXPathNodeSetIsEmpty(nodes2))
+ return(nodes1);
+ return(xmlXPathNodeTrailingSorted(nodes1,
+ xmlXPathNodeSetItem(nodes2, 0)));
+}
+
+/**
+ * xmlXPathTrailing:
+ * @param nodes1 a node-set
+ * @param nodes2 a node-set
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ * node-set set:trailing (node-set, node-set)
+ * nodes1 and nodes2 are sorted by document order, then
+ * #xmlXPathTrailingSorted is called.
+ *
+ * Returns the nodes in nodes1 that follow the first node in nodes2
+ * in document order, nodes1 if nodes2 is NULL or empty or
+ * an empty node-set if nodes1 doesn't contain nodes2
+ */
+XMLPUBFUNEXPORT xmlNodeSetPtr
+xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2)
+{
+ if (xmlXPathNodeSetIsEmpty(nodes2))
+ return(nodes1);
+ if (xmlXPathNodeSetIsEmpty(nodes1))
+ return(xmlXPathNodeSetCreate(NULL));
+ xmlXPathNodeSetSort(nodes1);
+ xmlXPathNodeSetSort(nodes2);
+ return xmlXPathNodeTrailingSorted(nodes1, xmlXPathNodeSetItem(nodes2, 0));
+}
+
+/************************************************************************
+ * *
+ * Routines to handle extra functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathRegisterFunc:
+ * @param ctxt the XPath context
+ * @param name the function name
+ * @param f the function implementation or NULL
+ *
+ * Register a new function. If f is NULL it unregisters the function
+ *
+ * Returns 0 in case of success, -1 in case of error
+ *
+ * Prerequisites: TRUE( ctxt && ctxt->funcHash )
+ */
+XMLPUBFUNEXPORT int
+xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, xmlXPathFunction f)
+{
+ return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
+}
+
+/**
+ * xmlXPathRegisterFuncNS:
+ * @param ctxt the XPath context
+ * @param name the function name
+ * @param ns_uri the function namespace URI
+ * @param f the function implementation or NULL
+ *
+ * Register a new function. If f is NULL it unregisters the function
+ *
+ * Returns 0 in case of success, -1 in case of error
+ *
+ * Prerequisites: TRUE( ctxt && ctxt->funcHash )
+ */
+XMLPUBFUNEXPORT int
+xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
+ const xmlChar *ns_uri, xmlXPathFunction f)
+{
+ LOAD_GS_DIRECT
+ if (OOM_FLAG || !name)
+ return -1;
+
+ //if(!ctxt || !ctxt->funcHash)
+ // return(-1);
+
+// Disabled: ctxt->funcHash is always initialized prior registering functions
+// if (ctxt->funcHash == NULL)
+// ctxt->funcHash = xmlHashCreate(0);
+
+ return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *)f));
+}
+
+/**
+ * xmlXPathRegisterFuncLookup:
+ * @param ctxt the XPath context
+ * @param f the lookup function
+ * @param funcCtxt the lookup data
+ *
+ * Registers an external mechanism to do function lookup.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt, xmlXPathFuncLookupFunc f, void *funcCtxt)
+{
+ if (!ctxt)
+ return;
+ ctxt->funcLookupFunc = (void *) f;
+ ctxt->funcLookupData = funcCtxt;
+}
+
+/**
+ * xmlXPathFunctionLookup:
+ * @param ctxt the XPath context
+ * @param name the function name
+ *
+ * Search in the Function array of the context for the given
+ * function.
+ *
+ * Returns the xmlXPathFunction or NULL if not found
+ */
+XMLPUBFUNEXPORT xmlXPathFunction
+xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name)
+{
+ if (!ctxt)
+ return (NULL);
+
+ if (ctxt->funcLookupFunc) {
+ xmlXPathFunction ret;
+ xmlXPathFuncLookupFunc f;
+
+ f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc;
+ ret = f(ctxt->funcLookupData, name, NULL);
+ if (ret)
+ return(ret);
+ }
+ return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
+}
+
+/**
+ * xmlXPathFunctionLookupNS:
+ * @param ctxt the XPath context
+ * @param name the function name
+ * @param ns_uri the function namespace URI
+ *
+ * Search in the Function array of the context for the given
+ * function.
+ *
+ * Returns the xmlXPathFunction or NULL if not found
+ */
+XMLPUBFUNEXPORT xmlXPathFunction
+xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri)
+{
+ if (!ctxt || !name)
+ return(NULL);
+
+ if (ctxt->funcLookupFunc) {
+ xmlXPathFunction ret;
+ xmlXPathFuncLookupFunc f;
+
+ f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc;
+ ret = f(ctxt->funcLookupData, name, ns_uri);
+ if (ret != NULL)
+ return(ret);
+ }
+
+ if (!ctxt->funcHash)
+ return(NULL);
+
+ return (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
+}
+
+/**
+ * xmlXPathRegisteredFuncsCleanup:
+ * @param ctxt the XPath context
+ *
+ * Cleanup the XPath context data associated to registered functions
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
+ if (!ctxt)
+ return;
+
+ xmlHashFree(ctxt->funcHash, NULL);
+ ctxt->funcHash = NULL;
+}
+
+/************************************************************************
+ * *
+ * Routines to handle Variables *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathRegisterVariable:
+ * @param ctxt the XPath context
+ * @param name the variable name
+ * @param value the variable value or NULL
+ *
+ * Register a new variable value. If value is NULL it unregisters
+ * the variable
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+XMLPUBFUNEXPORT int
+xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
+ xmlXPathObjectPtr value) {
+ return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
+}
+
+/**
+ * xmlXPathRegisterVariableNS:
+ * @param ctxt the XPath context
+ * @param name the variable name
+ * @param ns_uri the variable namespace URI
+ * @param value the variable value or NULL
+ *
+ * Register a new variable value. If value is NULL it unregisters
+ * the variable
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+XMLPUBFUNEXPORT int
+xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
+ const xmlChar *ns_uri,
+ xmlXPathObjectPtr value) {
+ if (ctxt == NULL)
+ return(-1);
+ if (name == NULL)
+ return(-1);
+
+ if (ctxt->varHash == NULL)
+ ctxt->varHash = xmlHashCreate(0);
+ if (ctxt->varHash == NULL)
+ return(-1);
+ if (value == NULL)
+ return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
+ (xmlHashDeallocator)xmlXPathFreeObject));
+ return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
+ (void *) value,
+ (xmlHashDeallocator)xmlXPathFreeObject));
+}
+
+/**
+ * xmlXPathRegisterVariableLookup:
+ * @param ctxt the XPath context
+ * @param f the lookup function
+ * @param data the lookup data
+ *
+ * register an external mechanism to do variable lookup
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
+ xmlXPathVariableLookupFunc f, void *data) {
+ if (!ctxt)
+ return;
+ ctxt->varLookupFunc = (void *) f;
+ ctxt->varLookupData = data;
+}
+
+/**
+ * xmlXPathVariableLookup:
+ * @param ctxt the XPath context
+ * @param name the variable name
+ *
+ * Search in the Variable array of the context for the given
+ * variable value.
+ *
+ * Returns a copy of the value or NULL if not found
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
+ if (ctxt == NULL)
+ return(NULL);
+
+ if (ctxt->varLookupFunc) {
+ xmlXPathVariableLookupFunc func = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc;
+ xmlXPathObjectPtr ret = func(ctxt->varLookupData, name, NULL);
+ return(ret);
+ }
+ return(xmlXPathVariableLookupNS(ctxt, name, NULL));
+}
+
+/**
+ * xmlXPathVariableLookupNS:
+ * @param ctxt the XPath context
+ * @param name the variable name
+ * @param ns_uri the variable namespace URI
+ *
+ * Search in the Variable array of the context for the given
+ * variable value.
+ *
+ * Returns the a copy of the value or NULL if not found
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
+ const xmlChar *ns_uri) {
+ if (ctxt == NULL)
+ return(NULL);
+
+ if (ctxt->varLookupFunc != NULL) {
+ xmlXPathVariableLookupFunc lookupFunc = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc;
+ xmlXPathObjectPtr ret = lookupFunc(ctxt->varLookupData, name, ns_uri);
+ if (ret != NULL)
+ return(ret);
+ }
+
+ if (ctxt->varHash == NULL)
+ return(NULL);
+ if (name == NULL)
+ return(NULL);
+
+ return(xmlXPathObjectCopy((xmlXPathObjectPtr)
+ xmlHashLookup2(ctxt->varHash, name, ns_uri)));
+}
+
+/**
+ * xmlXPathRegisteredVariablesCleanup:
+ * @param ctxt the XPath context
+ *
+ * Cleanup the XPath context data associated to registered variables
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt)
+{
+ if (ctxt == NULL)
+ return;
+
+ xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
+ ctxt->varHash = NULL;
+}
+
+/**
+ * xmlXPathRegisterNs:
+ * @param ctxt the XPath context
+ * @param prefix the namespace prefix
+ * @param ns_uri the namespace name
+ *
+ * Register a new namespace. If ns_uri is NULL it unregisters
+ * the namespace
+ *
+ * Returns 0 in case of success, -1 in case of error
+ *
+ * OOM: possible --> returns -1
+ */
+XMLPUBFUNEXPORT int
+xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar* prefix,
+ const xmlChar* ns_uri)
+{
+ int result = -1;
+ xmlChar* uri;
+ if (!ctxt || !prefix)
+ return(-1);
+
+ if (ctxt->nsHash == NULL)
+ ctxt->nsHash = xmlHashCreate(10);
+ if (ctxt->nsHash == NULL)
+ return(-1); // OOM
+ if (!ns_uri)
+ return(xmlHashRemoveEntry(ctxt->nsHash, prefix,(xmlHashDeallocator)xmlFree));
+ uri = xmlStrdup(ns_uri);
+ if (uri)
+ {
+ result = xmlHashUpdateEntry(ctxt->nsHash, prefix, (void*)uri,
+ (xmlHashDeallocator)xmlFree);
+ if ( result == -1)
+ xmlFree( uri );
+ }
+ return( result );
+}
+
+/**
+ * xmlXPathNsLookup:
+ * @param ctxt the XPath context
+ * @param prefix the namespace prefix value
+ *
+ * Search in the namespace declaration array of the context for the given
+ * namespace name associated to the given prefix
+ *
+ * Returns the value or NULL if not found
+ */
+XMLPUBFUNEXPORT const xmlChar*
+xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix)
+{
+ if (!ctxt || !prefix)
+ return(NULL);
+
+#ifdef XML_XML_NAMESPACE
+ if (xmlStrEqual(prefix, (const xmlChar*)"xml"))
+ return(XML_XML_NAMESPACE);
+#endif
+
+ if (ctxt->namespaces) {
+ int i;
+
+ for (i = 0;i < ctxt->nsNr;i++) {
+ if (ctxt->namespaces[i] &&
+ xmlStrEqual(ctxt->namespaces[i]->prefix, prefix))
+ {
+ return(ctxt->namespaces[i]->href);
+ }
+ }
+ }
+
+ return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
+}
+
+/**
+ * xmlXPathRegisteredNsCleanup:
+ * @param ctxt the XPath context
+ *
+ * Cleanup the XPath context data associated to registered variables
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
+ if (!ctxt)
+ return;
+
+ xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree);
+ ctxt->nsHash = NULL;
+}
+
+/************************************************************************
+ * *
+ * Routines to handle Values *
+ * *
+ ************************************************************************/
+
+/* Allocations are terrible, one needs to optimize all this !!! */
+
+/**
+ * xmlXPathNewFloat:
+ * @param val the double value
+ *
+ * Create a new xmlXPathObjectPtr of type double and of value val
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewFloat(double val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating float object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_NUMBER;
+ ret->floatval = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathNewBoolean:
+ * @param val the boolean value
+ *
+ * Create a new xmlXPathObjectPtr of type boolean and of value val
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewBoolean(int val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating boolean object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_BOOLEAN;
+ ret->boolval = (val != 0);
+ return(ret);
+}
+
+/**
+ * xmlXPathNewString:
+ * @param val the xmlChar * value
+ *
+ * Create a new xmlXPathObjectPtr of type string and of value val
+ *
+ * Returns the newly created object or NULL if OOM
+ *
+ * OOM: possible --> sets OOM flag
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewString(const xmlChar *val) {
+ LOAD_GS_DIRECT
+ xmlXPathObjectPtr ret;
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_STRING;
+ ret->stringval = xmlStrdup(val ? val : (const xmlChar *)"");
+ if( OOM_FLAG )
+ {
+ xmlXPathFreeObject(ret);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathWrapString:
+ * @param val the xmlChar * value
+ *
+ * Wraps the val string into an XPath object.
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathWrapString (xmlChar *val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_STRING;
+ ret->stringval = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathNewCString:
+ * @param val the char * value
+ *
+ * Create a new xmlXPathObjectPtr of type string and of value val
+ *
+ * Returns the newly created object.
+ *
+ * OOM: possible --> NULL is returned
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathNewCString(const char *val)
+{ // Note: this function is used mostly for creating empty string objects!
+
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret)
+ goto OOM_exit;
+
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_STRING;
+ if(val){
+ ret->stringval = xmlStrdup(BAD_CAST val);
+ if(!ret->stringval){
+ xmlXPathFreeObject(ret);
+OOM_exit:
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
+ return(NULL);
+ }
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathWrapCString:
+ * @param val the char * value
+ *
+ * Wraps a string into an XPath object.
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathWrapCString (char* val)
+{
+ return(xmlXPathWrapString((xmlChar*)(val)));
+}
+
+/**
+ * xmlXPathWrapExternal:
+ * @param val the user data
+ *
+ * Wraps the val data into an XPath object.
+ *
+ * Returns the newly created object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathWrapExternal (void *val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating user object\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_USERS;
+ ret->user = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathObjectCopy:
+ * @param val the original object
+ *
+ * allocate a new copy of a given object
+ *
+ * Returns the newly created object or NULL if OOM
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathObjectCopy(xmlXPathObjectPtr val) {
+ LOAD_GS_DIRECT
+ xmlXPathObjectPtr ret;
+
+ if (!val)
+ return(NULL);
+
+ ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+ if (!ret) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("copying object\n"));
+ return(NULL);
+ }
+ memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
+ switch (val->type) {
+ case XPATH_STRING:
+
+ ret->stringval = xmlStrdup(val->stringval);
+ if( OOM_FLAG )
+ {
+ xmlXPathFreeObject(ret);
+ return(NULL);
+ }
+ break;
+ case XPATH_XSLT_TREE:
+ if (val->nodesetval && val->nodesetval->nodeTab)
+ {
+ xmlNodePtr cur, tmp;
+ xmlDocPtr top;
+
+ ret->boolval = 1; /* Deallocate the copied tree value */
+ top = xmlNewDoc(NULL);
+ top->name = (char *)
+ xmlStrdup(val->nodesetval->nodeTab[0]->name);
+ ret->user = top;
+ if (top) {
+ top->doc = top;
+ cur = val->nodesetval->nodeTab[0]->children;
+ while (cur) {
+ tmp = xmlDocCopyNode(cur, top, 1);
+ xmlAddChild((xmlNodePtr) top, tmp);
+ cur = cur->next;
+ }
+ }
+ ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
+ } else
+ ret->nodesetval = xmlXPathNodeSetCreate(NULL);
+ /* Deallocate the copied tree value */
+ break;
+ case XPATH_NODESET:
+ ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
+ /* Do not deallocate the copied tree value */
+ ret->boolval = 0;
+ break;
+
+ // DONE: OPTIMIZED: moved as less probable branches
+ case XPATH_BOOLEAN:
+ case XPATH_NUMBER:
+ break;
+
+ case XPATH_LOCATIONSET:
+#ifdef LIBXML_XPTR_ENABLED
+ {
+ xmlLocationSetPtr loc = val->user;
+ ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
+ break;
+ }
+#endif
+ case XPATH_USERS:
+ ret->user = val->user;
+ break;
+ case XPATH_UNDEFINED:
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathObjectCopy: unsupported type %d\n"),
+ val->type);
+ break;
+ /* DONE: Optimized: defaulted (rarely used!)
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ */
+ default:
+ break;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeObject:
+ * @param obj the object to free
+ *
+ * Free up an xmlXPathObjectPtr object.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeObject(xmlXPathObjectPtr obj)
+{
+ if (!obj)
+ return;
+ if (obj->type == XPATH_NODESET ||
+ obj->type == XPATH_XSLT_TREE)
+ {
+ if (obj->boolval) {
+ if (obj->user) {
+ xmlXPathFreeNodeSet(obj->nodesetval);
+ xmlFreeNodeList((xmlNodePtr) obj->user);
+ } else
+ if (obj->nodesetval)
+ xmlXPathFreeValueTree(obj->nodesetval);
+ } else {
+ if (obj->nodesetval)
+ xmlXPathFreeNodeSet(obj->nodesetval);
+ }
+#ifdef LIBXML_XPTR_ENABLED
+ }
+ else if (obj->type == XPATH_LOCATIONSET)
+ {
+ if (obj->user)
+ xmlXPtrFreeLocationSet(obj->user);
+#endif
+ }
+ else if (obj->type == XPATH_STRING)
+ {
+ if (obj->stringval != NULL)
+ xmlFree(obj->stringval);
+ }
+
+ xmlFree(obj);
+}
+
+
+/************************************************************************
+ * *
+ * Type Casting Routines *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathCastBooleanToString:
+ * @param val a boolean
+ *
+ * Converts a boolean to its string value.
+ *
+ * Returns a newly allocated string.
+ * OOM: possible --> returns NULL
+ */
+XMLPUBFUNEXPORT xmlChar *
+xmlXPathCastBooleanToString (int val)
+{
+ xmlChar *ret;
+ if (val)
+ ret = xmlStrdup((const xmlChar*) "true");
+ else
+ ret = xmlStrdup((const xmlChar*) "false");
+ return(ret);
+}
+
+/**
+ * xmlXPathCastNumberToString:
+ * @param val a number
+ *
+ * Converts a number to its string value.
+ *
+ * Returns a newly allocated string.
+ *
+ * OOM: possible --> returns NULL
+ */
+XMLPUBFUNEXPORT xmlChar*
+xmlXPathCastNumberToString (double val)
+{
+ xmlChar *ret;
+ switch (xmlXPathIsInf(val))
+ {
+ case 1:
+ ret = xmlStrdup((const xmlChar*) "Infinity");
+ break;
+ case -1:
+ ret = xmlStrdup((const xmlChar*) "-Infinity");
+ break;
+ default:
+ if (xmlXPathIsNaN(val)) {
+ ret = xmlStrdup((const xmlChar*) "NaN");
+ } else if (val == 0 && xmlXPathGetSign(val) != 0) {
+ ret = xmlStrdup((const xmlChar*) "0");
+ } else {
+
+ char buf[100];
+ xmlXPathFormatNumber(val, buf, 100);
+ ret = xmlStrdup((const xmlChar *) buf);
+ }
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathCastNodeToString:
+ * @param node a node
+ *
+ * Converts a node to its string value.
+ *
+ * Returns a newly allocated string.
+ *
+ * OOM: possible --> returns NULL; OOM flag must be checked ALWAYS
+ */
+XMLPUBFUNEXPORT xmlChar*
+xmlXPathCastNodeToString(xmlNodePtr node)
+{
+ return xmlNodeGetContent(node);
+}
+
+/**
+ * xmlXPathCastNodeSetToString:
+ * @param ns a node-set
+ *
+ * Converts a node-set to its string value.
+ *
+ * Returns a newly allocated string.
+ *
+ * OOM: possible --> OOM flag must be checked
+ */
+XMLPUBFUNEXPORT xmlChar*
+xmlXPathCastNodeSetToString(xmlNodeSetPtr ns)
+{ // ISSUE: what are rules for string xmlXPathObject? can value be NULL or
+ // must be "" instead always?
+ if (!ns || (ns->nodeNr == 0) || !ns->nodeTab)
+ return(xmlStrdup((const xmlChar *) ""));
+
+ xmlXPathNodeSetSort(ns);
+ return(xmlXPathCastNodeToString(ns->nodeTab[0]));
+}
+
+/**
+ * xmlXPathCastToString:
+ * @param val an XPath object
+ *
+ * Converts an existing object to its string() equivalent
+ *
+ * Returns the string value of the object, NULL in case of error.
+ * A new string is allocated only if needed (val isn't a
+ * string object).
+ */
+XMLPUBFUNEXPORT xmlChar *
+xmlXPathCastToString(xmlXPathObjectPtr val)
+{
+
+
+ xmlChar *ret = NULL;
+
+ if (val == NULL)
+ return(xmlStrdup((const xmlChar *) ""));
+
+ switch (val->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
+#endif
+ ret = xmlStrdup((const xmlChar *) "");
+ break;
+ case XPATH_NUMBER:
+ ret = xmlXPathCastNumberToString(val->floatval);
+ break;
+
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathCastNodeSetToString(val->nodesetval);
+ break;
+
+ case XPATH_BOOLEAN:
+ ret = xmlXPathCastBooleanToString(val->boolval);
+ break;
+
+ case XPATH_STRING:
+ return(xmlStrdup(val->stringval));
+
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+
+ ret = xmlStrdup((const xmlChar *) "");
+ break;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathConvertString:
+ * @param val an XPath object
+ *
+ * Converts an existing object to its string() equivalent
+ *
+ * Returns the new object, the old one is freed (or the operation
+ * is done directly on val)
+ * OOM: returns NULL if OOM;
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathConvertString(xmlXPathObjectPtr val)
+{
+ LOAD_GS_DIRECT
+ xmlChar* res;
+ xmlXPathObjectPtr ret;
+
+ if (!val)
+ return(xmlXPathNewCString(""));
+
+ switch (val->type)
+ {
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ res = xmlXPathCastNodeSetToString(val->nodesetval);
+ if(OOM_FLAG && res){
+ xmlFree(res);
+ res = NULL;
+ }
+ break;
+
+ case XPATH_STRING:
+ return(val);
+
+ case XPATH_BOOLEAN:
+ res = xmlXPathCastBooleanToString(val->boolval);
+ break;
+
+ case XPATH_NUMBER:
+ res = xmlXPathCastNumberToString(val->floatval);
+ break;
+
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
+#endif
+ //break;
+ //-- FALLTHROUGH into the default branch --//
+ /* defaulted:
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ */
+ default:
+ // Nothing to convert or unknown object, so return ""
+ xmlXPathFreeObject(val);
+ return xmlXPathNewCString("");
+ } // end switch
+
+
+ xmlXPathFreeObject(val);
+ // Note: by now 'res' is NULL only if OOM happend --
+ // functions above never return NULL normally or 'res' is set to NULL in OOM
+ if(!res)
+ return(NULL);
+
+ ret = xmlXPathWrapString(res);
+ if(!ret) // OOM!
+ {
+ xmlFree(res);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathCastBooleanToNumber:
+ * @param val a boolean
+ *
+ * Converts a boolean to its number value
+ *
+ * Returns the number value
+ */
+XMLPUBFUNEXPORT double
+xmlXPathCastBooleanToNumber(int val) {
+ return val ? (1.0) :(0.0);
+}
+
+/**
+ * xmlXPathCastStringToNumber:
+ * @param val a string
+ *
+ * Converts a string to its number value
+ *
+ * Returns the number value
+ *
+ * OOM: Never
+ */
+XMLPUBFUNEXPORT double
+xmlXPathCastStringToNumber(const xmlChar * val) {
+ return(xmlXPathStringEvalNumber(val));
+}
+
+/**
+ * xmlXPathCastNodeToNumber:
+ * @param node a node
+ *
+ * Converts a node to its number value
+ *
+ * Returns the number value
+ */
+XMLPUBFUNEXPORT double
+xmlXPathCastNodeToNumber (xmlNodePtr node) {
+ xmlChar *strval;
+ double ret;
+ LOAD_GS_DIRECT
+
+ if (node == NULL)
+ return(xmlXPathNAN);
+ strval = xmlXPathCastNodeToString(node);
+ if (!strval)
+ return(xmlXPathNAN);
+
+ ret = xmlXPathCastStringToNumber(strval);
+ xmlFree(strval);
+
+ return(ret);
+}
+
+/**
+ * xmlXPathCastNodeSetToNumber:
+ * @param ns a node-set
+ *
+ * Converts a node-set to its number value
+ *
+ * Returns the number value
+ *
+ * OOM: possible --> OOM flag must be checked
+ */
+XMLPUBFUNEXPORT double
+xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
+ LOAD_GS_DIRECT
+ xmlChar *str;
+ double ret;
+
+ if (ns == NULL)
+ return(xmlXPathNAN);
+ str = xmlXPathCastNodeSetToString(ns); // OOM is possible
+ if(str){
+ ret = xmlXPathCastStringToNumber(str);
+ xmlFree(str);
+ return(ret);
+ }
+ return 0;
+}
+
+/**
+ * xmlXPathCastToNumber:
+ * @param val an XPath object
+ *
+ * Converts an XPath object to its number value
+ *
+ * Returns the number value
+ */
+XMLPUBFUNEXPORT double
+xmlXPathCastToNumber(xmlXPathObjectPtr val) {
+ LOAD_GS_DIRECT
+ double ret = 0.0;
+
+ if (val == NULL)
+ return(xmlXPathNAN);
+ switch (val->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEGUB_EXPR
+ xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
+#endif
+ ret = xmlXPathNAN;
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
+ break;
+ case XPATH_STRING:
+ ret = xmlXPathCastStringToNumber(val->stringval);
+ break;
+ case XPATH_NUMBER:
+ ret = val->floatval;
+ break;
+ case XPATH_BOOLEAN:
+ ret = xmlXPathCastBooleanToNumber(val->boolval);
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO;
+ ret = xmlXPathNAN;
+ break;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathConvertNumber:
+ * @param val an XPath object
+ *
+ * Converts an existing object to its number() equivalent
+ *
+ * Returns the new object, the old one is freed (or the operation
+ * is done directly on val)
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathConvertNumber(xmlXPathObjectPtr val) {
+ xmlXPathObjectPtr ret;
+
+ if (val == NULL)
+ return(xmlXPathNewFloat(0.0));
+ if (val->type == XPATH_NUMBER)
+ return(val);
+ ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
+ xmlXPathFreeObject(val);
+ return(ret);
+}
+
+/**
+ * xmlXPathCastNumberToBoolean:
+ * @param val a number
+ *
+ * Converts a number to its boolean value
+ *
+ * Returns the boolean value
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCastNumberToBoolean (double val) {
+ if (xmlXPathIsNaN(val) || (val == 0.0))
+ return(0);
+ return(1);
+}
+
+/**
+ * xmlXPathCastStringToBoolean:
+ * @param val a string
+ *
+ * Converts a string to its boolean value
+ *
+ * Returns the boolean value
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCastStringToBoolean (const xmlChar *val) {
+ if ((val == NULL) || (xmlStrlen(val) == 0))
+ return(0);
+ return(1);
+}
+
+/**
+ * xmlXPathCastNodeSetToBoolean:
+ * @param ns a node-set
+ *
+ * Converts a node-set to its boolean value
+ *
+ * Returns the boolean value
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
+ if ((ns == NULL) || (ns->nodeNr == 0))
+ return(0);
+ return(1);
+}
+
+/**
+ * xmlXPathCastToBoolean:
+ * @param val an XPath object
+ *
+ * Converts an XPath object to its boolean value
+ *
+ * Returns the boolean value
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
+ int ret = 0;
+
+ if (val == NULL)
+ return(0);
+ switch (val->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
+#endif
+ ret = 0;
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
+ break;
+ case XPATH_STRING:
+ ret = xmlXPathCastStringToBoolean(val->stringval);
+ break;
+ case XPATH_NUMBER:
+ ret = xmlXPathCastNumberToBoolean(val->floatval);
+ break;
+ case XPATH_BOOLEAN:
+ ret = val->boolval;
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO;
+ ret = 0;
+ break;
+ }
+ return(ret);
+}
+
+
+/**
+ * xmlXPathConvertBoolean:
+ * @param val an XPath object
+ *
+ * Converts an existing object to its boolean() equivalent
+ *
+ * Returns the new object, the old one is freed (or the operation
+ * is done directly on val)
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
+ xmlXPathObjectPtr ret;
+
+ if (val == NULL)
+ return(xmlXPathNewBoolean(0));
+ if (val->type == XPATH_BOOLEAN)
+ return(val);
+ ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
+ xmlXPathFreeObject(val);
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle XPath contexts *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlHashCopier:
+ *
+ *Prototyped by:
+ * typedef void *(*xmlHashCopier)(void *payload, xmlChar *name);
+ */
+void* xeSimpleHashEntryCopier(void *payload, xmlChar* name)
+{
+ return payload; // just return the same value
+}
+
+/**
+ * xmlXPathNewContext:
+ * @param doc the XML document
+ *
+ * Create a new xmlXPathContext
+ *
+ * Returns the xmlXPathContext just allocated. The caller will need to free it.
+ */
+XMLPUBFUNEXPORT xmlXPathContextPtr
+xmlXPathNewContext(xmlDocPtr doc)
+{
+ xmlXPathContextPtr ret;
+ xmlHashTablePtr gFuncHash;
+ LOAD_GS_DIRECT
+
+ ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
+ if (!ret)
+ goto OOM;
+
+ memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
+
+ ret->doc = doc; // this is a context node in general, not always a document node
+
+ // XMLENGINE: these are nullified already
+ //ret->node = NULL;
+ //ret->varHash = NULL;
+ //ret->nb_types = 0;
+ //ret->max_types = 0;
+ //ret->types = NULL;
+
+// XMLENGINE: BEGIN REPLACE CODE
+ /* REPLACED original code
+ ret->funcHash = xmlHashCreate(0);
+ xmlXPathRegisterAllFunctions(ret);
+ */
+
+ // xmlXPathDefaultFunctionsHash is the new field in xmlGlobalState structure
+ gFuncHash = xmlXPathDefaultFunctionsHash;
+ if(!gFuncHash)
+ { // global reusable hash table was not initialized yet; it will be done here:
+ xmlXPathRegisterAllFunctions(ret);
+ if(OOM_FLAG)
+ goto OOM_ret;
+
+ if(xmlXPathDefineExtensionFunctionsGlobally)
+ {
+ // Now the hash table is owned by XML Engine and will be reused:
+ xmlXPathDefaultFunctionsHash = ret->funcHash;
+ }
+ else
+ {
+ // Cache initialized function hash table; it will be copied later into each XPath context
+ xmlXPathDefaultFunctionsHash = xmlHashCopy(ret->funcHash, xeSimpleHashEntryCopier);
+ }
+ }
+ else
+ { // copy previously initialized hash table OR
+ // reuse the global hash, then, no cleanup is needed when
+ // XPath context is freed
+ if(xmlXPathDefineExtensionFunctionsGlobally)
+ {
+ // reuse global function hash
+ ret->funcHash = gFuncHash;
+ }
+ else
+ {
+ // use cached hash table for creating hash table for the given context
+ ret->funcHash = xmlHashCopy( gFuncHash, xeSimpleHashEntryCopier );
+ if(OOM_FLAG)
+ goto OOM_ret;
+ }
+ }
+// XMLENGINE: END REPLACE CODE
+
+ // XMLENGINE: these are nullified already
+ //ret->nb_axis = 0;
+ //ret->max_axis = 0;
+ //ret->axis = NULL;
+ //ret->nsHash = NULL;
+ //ret->user = NULL;
+
+ ret->contextSize = -1;
+ ret->proximityPosition = -1;
+
+ return(ret);
+
+//---- OOM
+OOM_ret:
+ xmlFree(ret);
+OOM:
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating context\n"));
+ return(NULL);
+}
+
+/**
+ * xmlXPathFreeContext:
+ * @param ctxt the context to free
+ *
+ * Free up an xmlXPathContext
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
+ LOAD_GS_DIRECT
+
+
+ if (&ctxt->lastError)
+ xmlResetError(&ctxt->lastError);
+
+ xmlXPathRegisteredNsCleanup(ctxt);
+
+// XMLENGINE: BEGIN REPLACE CODE
+ /* ORIGINAL CODE
+ xmlXPathRegisteredFuncsCleanup(ctxt);
+ */
+
+ // DO NOTHING HERE if global hash table is reused.
+ // The function hash is destroyed only if it is not reused,
+ // otherwise the reference to it is stored in global:
+ // xmlXPathDefaultFunctionsHash variable
+ // and the hash table is freed during XML Engine cleanup with
+ // xeXPathCleanup()
+ if(!xmlXPathDefineExtensionFunctionsGlobally)
+ {
+ xmlXPathRegisteredFuncsCleanup(ctxt);
+ }
+// XMLENGINE: END REPLACE CODE
+
+ xmlXPathRegisteredVariablesCleanup(ctxt);
+ xmlFree(ctxt);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle XPath parser contexts *
+ * *
+ ************************************************************************/
+
+#define CHECK_CTXT(ctxt) \
+ if (ctxt == NULL) { \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("%s:%d Internal error: ctxt == NULL\n"), \
+ __FILE__, __LINE__); \
+ } \
+
+
+
+#define CHECK_CONTEXT(ctxt) \
+ if (ctxt == NULL) { \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("%s:%d Internal error: no context\n"), \
+ __FILE__, __LINE__); \
+ } \
+ else if (ctxt->doc == NULL) { \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("%s:%d Internal error: no document\n"), \
+ __FILE__, __LINE__); \
+ } \
+ else if (ctxt->doc->children == NULL) { \
+ xmlGenericError(xmlGenericErrorContext, \
+ EMBED_ERRTXT("%s:%d Internal error: document without root\n"), \
+ __FILE__, __LINE__); \
+ } \
+
+
+/**
+ * xmlXPathNewParserContext:
+ * @param str the XPath expression
+ * @param ctxt the XPath context
+ *
+ * Create a new xmlXPathParserContext
+ *
+ * Returns the xmlXPathParserContext just allocated.
+ */
+XMLPUBFUNEXPORT xmlXPathParserContextPtr
+xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr ret;
+
+ ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
+ if (ret == NULL) {
+ xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating parser context\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
+ ret->cur = ret->base = str;
+ ret->context = ctxt;
+
+ ret->comp = xmlXPathNewCompExpr();
+ if (ret->comp == NULL) {
+ xmlFree(ret->valueTab);
+ xmlFree(ret);
+ return(NULL);
+ }
+ if ((ctxt != NULL) && (ctxt->dict != NULL)) {
+ ret->comp->dict = ctxt->dict;
+ xmlDictReference(ret->comp->dict);
+ }
+
+ return(ret);
+}
+
+/**
+ * xmlXPathCompParserContext:
+ * @param comp the XPath compiled expression
+ * @param ctxt the XPath context
+ *
+ * Create a new xmlXPathParserContext when processing a compiled expression
+ *
+ * Returns the xmlXPathParserContext just allocated.
+ */
+static xmlXPathParserContextPtr
+xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr ret;
+
+ ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
+ if (!ret) {
+ xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n"));
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
+
+ /* Allocate the value stack */
+ ret->valueTab = (xmlXPathObjectPtr *)
+ xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
+ if (!ret->valueTab) {
+ xmlFree(ret);
+ xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n"));
+ return(NULL);
+ }
+ // all other fields were nullified already
+ //ret->valueNr = 0;
+ ret->valueMax = 10;
+ //ret->value = NULL;
+
+ ret->context = ctxt;
+ ret->comp = comp;
+
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeParserContext:
+ * @param ctxt the context to free
+ *
+ * Free up an xmlXPathParserContext
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
+ if (ctxt->valueTab != NULL) {
+ xmlFree(ctxt->valueTab);
+ }
+ if (ctxt->comp)
+ xmlXPathFreeCompExpr(ctxt->comp);
+ xmlFree(ctxt);
+}
+
+/************************************************************************
+ * *
+ * The implicit core function library *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathNodeValHash:
+ * @param node a node pointer
+ *
+ * Function computing the beginning of the string value of the node,
+ * used to speed up comparisons
+ *
+ * Returns an int usable as a hash
+ */
+static unsigned int
+xmlXPathNodeValHash(xmlNodePtr node) {
+ int len = 2;
+ const xmlChar * string = NULL;
+ xmlNodePtr tmp = NULL;
+ unsigned int ret = 0;
+
+ if (node == NULL)
+ return(0);
+
+ if (node->type == XML_DOCUMENT_NODE) {
+ tmp = xmlDocGetRootElement((xmlDocPtr) node);
+ if (tmp == NULL)
+ node = node->children;
+ else
+ node = tmp;
+
+ if (node == NULL)
+ return(0);
+ }
+
+ switch (node->type) {
+ case XML_COMMENT_NODE:
+ case XML_PI_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_TEXT_NODE:
+ string = node->content;
+ if (string == NULL)
+ return(0);
+ if (string[0] == 0)
+ return(0);
+ return(((unsigned int) string[0]) +
+ (((unsigned int) string[1]) << 8));
+ case XML_NAMESPACE_DECL:
+ string = ((xmlNsPtr)node)->href;
+ if (string == NULL)
+ return(0);
+ if (string[0] == 0)
+ return(0);
+ return(((unsigned int) string[0]) +
+ (((unsigned int) string[1]) << 8));
+ case XML_ATTRIBUTE_NODE:
+ tmp = ((xmlAttrPtr) node)->children;
+ break;
+ case XML_ELEMENT_NODE:
+ tmp = node->children;
+ break;
+ default:
+ return(0);
+ }
+ while (tmp != NULL) {
+ switch (tmp->type) {
+ case XML_COMMENT_NODE:
+ case XML_PI_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_TEXT_NODE:
+ string = tmp->content;
+ break;
+ case XML_NAMESPACE_DECL:
+ string = ((xmlNsPtr)tmp)->href;
+ break;
+ default:
+ break;
+ }
+ if ((string != NULL) && (string[0] != 0)) {
+ if (len == 1) {
+ return(ret + (((unsigned int) string[0]) << 8));
+ }
+ if (string[1] == 0) {
+ len = 1;
+ ret = (unsigned int) string[0];
+ } else {
+ return(((unsigned int) string[0]) +
+ (((unsigned int) string[1]) << 8));
+ }
+ }
+ /*
+ * Skip to next node
+ */
+ if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
+ if (tmp->children->type != XML_ENTITY_DECL) {
+ tmp = tmp->children;
+ continue;
+ }
+ }
+ if (tmp == node)
+ break;
+
+ if (tmp->next != NULL) {
+ tmp = tmp->next;
+ continue;
+ }
+
+ do {
+ tmp = tmp->parent;
+ if (tmp == NULL)
+ break;
+ if (tmp == node) {
+ tmp = NULL;
+ break;
+ }
+ if (tmp->next != NULL) {
+ tmp = tmp->next;
+ break;
+ }
+ } while (tmp != NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathStringHash:
+ * @param string a string
+ *
+ * Function computing the beginning of the string value of the node,
+ * used to speed up comparisons
+ *
+ * Returns an int usable as a hash
+ */
+static unsigned int
+xmlXPathStringHash(const xmlChar * string) {
+ if (string == NULL)
+ return((unsigned int) 0);
+ if (string[0] == 0)
+ return(0);
+ return(((unsigned int) string[0]) +
+ (((unsigned int) string[1]) << 8));
+}
+
+/**
+ * xmlXPathCompareNodeSetFloat:
+ * @param ctxt the XPath Parser context
+ * @param inf less than (1) or greater than (0)
+ * @param strict is the comparison strict
+ * @param arg the node set
+ * @param f the value
+ *
+ * Implement the compare operation between a nodeset and a number
+ * ns < val (1, 1, ...
+ * ns <= val (1, 0, ...
+ * ns > val (0, 1, ...
+ * ns >= val (0, 0, ...
+ *
+ * If one object to be compared is a node-set and the other is a number,
+ * then the comparison will be true if and only if there is a node in the
+ * node-set such that the result of performing the comparison on the number
+ * to be compared and on the result of converting the string-value of that
+ * node to a number using the number function is true.
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
+ xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
+ int i, ret = 0;
+ xmlNodeSetPtr ns;
+ xmlChar *str2;
+
+ if ((f == NULL) || (arg == NULL) ||
+ ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
+ xmlXPathFreeObject(arg);
+ xmlXPathFreeObject(f);
+ return(0);
+ }
+ ns = arg->nodesetval;
+ if (ns != NULL) {
+ for (i = 0;i < ns->nodeNr;i++) {
+ str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
+ if (str2 != NULL) {
+ valuePush(ctxt,
+ xmlXPathNewString(str2));
+ xmlFree(str2);
+ xmlXPathNumberFunction(ctxt, 1);
+ valuePush(ctxt, xmlXPathObjectCopy(f));
+ ret = xmlXPathCompareValues(ctxt, inf, strict);
+ if (ret)
+ break;
+ }
+ }
+ }
+ xmlXPathFreeObject(arg);
+ xmlXPathFreeObject(f);
+ return(ret);
+}
+
+/**
+ * xmlXPathCompareNodeSetString:
+ * @param ctxt the XPath Parser context
+ * @param inf less than (1) or greater than (0)
+ * @param strict is the comparison strict
+ * @param arg the node set
+ * @param s the value
+ *
+ * Implement the compare operation between a nodeset and a string
+ * ns < val (1, 1, ...
+ * ns <= val (1, 0, ...
+ * ns > val (0, 1, ...
+ * ns >= val (0, 0, ...
+ *
+ * If one object to be compared is a node-set and the other is a string,
+ * then the comparison will be true if and only if there is a node in
+ * the node-set such that the result of performing the comparison on the
+ * string-value of the node and the other string is true.
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
+ xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
+ int i, ret = 0;
+ xmlNodeSetPtr ns;
+ xmlChar *str2;
+
+ if ((s == NULL) || (arg == NULL) ||
+ ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
+ xmlXPathFreeObject(arg);
+ xmlXPathFreeObject(s);
+ return(0);
+ }
+ ns = arg->nodesetval;
+ if (ns != NULL) {
+ for (i = 0;i < ns->nodeNr;i++) {
+ str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
+ if (str2 != NULL) {
+ valuePush(ctxt,
+ xmlXPathNewString(str2));
+ xmlFree(str2);
+ valuePush(ctxt, xmlXPathObjectCopy(s));
+ ret = xmlXPathCompareValues(ctxt, inf, strict);
+ if (ret)
+ break;
+ }
+ }
+ }
+ xmlXPathFreeObject(arg);
+ xmlXPathFreeObject(s);
+ return(ret);
+}
+
+/**
+ * xmlXPathCompareNodeSets:
+ * @param inf less than (1) or greater than (0)
+ * @param strict is the comparison strict
+ * @param arg1 the first node set object
+ * @param arg2 the second node set object
+ *
+ * Implement the compare operation on nodesets:
+ *
+ * If both objects to be compared are node-sets, then the comparison
+ * will be true if and only if there is a node in the first node-set
+ * and a node in the second node-set such that the result of performing
+ * the comparison on the string-values of the two nodes is true.
+ * ....
+ * When neither object to be compared is a node-set and the operator
+ * is <=, <, >= or >, then the objects are compared by converting both
+ * objects to numbers and comparing the numbers according to IEEE 754.
+ * ....
+ * The number function converts its argument to a number as follows:
+ * - a string that consists of optional whitespace followed by an
+ * optional minus sign followed by a Number followed by whitespace
+ * is converted to the IEEE 754 number that is nearest (according
+ * to the IEEE 754 round-to-nearest rule) to the mathematical value
+ * represented by the string; any other string is converted to NaN
+ *
+ * Conclusion all nodes need to be converted first to their string value
+ * and then the comparison must be done when possible
+ */
+static int
+xmlXPathCompareNodeSets(int inf, int strict,
+ xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
+ int i, j, init = 0;
+ double val1;
+ double *values2;
+ int ret = 0;
+ xmlNodeSetPtr ns1;
+ xmlNodeSetPtr ns2;
+
+ if ((arg1 == NULL) ||
+ ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+ if ((arg2 == NULL) ||
+ ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+
+ ns1 = arg1->nodesetval;
+ ns2 = arg2->nodesetval;
+
+ if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+ if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+
+ values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
+ if (values2 == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+ for (i = 0;i < ns1->nodeNr;i++) {
+ val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
+ if (xmlXPathIsNaN(val1))
+ continue;
+ for (j = 0;j < ns2->nodeNr;j++) {
+ if (init == 0) {
+ values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
+ }
+ if (xmlXPathIsNaN(values2[j]))
+ continue;
+ if (inf && strict)
+ ret = (val1 < values2[j]);
+ else if (inf && !strict)
+ ret = (val1 <= values2[j]);
+ else if (!inf && strict)
+ ret = (val1 > values2[j]);
+ else if (!inf && !strict)
+ ret = (val1 >= values2[j]);
+ if (ret)
+ break;
+ }
+ if (ret)
+ break;
+ init = 1;
+ }
+ xmlFree(values2);
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(ret);
+}
+
+/**
+ * xmlXPathCompareNodeSetValue:
+ * @param ctxt the XPath Parser context
+ * @param inf less than (1) or greater than (0)
+ * @param strict is the comparison strict
+ * @param arg the node set
+ * @param val the value
+ *
+ * Implement the compare operation between a nodeset and a value
+ * ns < val (1, 1, ...
+ * ns <= val (1, 0, ...
+ * ns > val (0, 1, ...
+ * ns >= val (0, 0, ...
+ *
+ * If one object to be compared is a node-set and the other is a boolean,
+ * then the comparison will be true if and only if the result of performing
+ * the comparison on the boolean and on the result of converting
+ * the node-set to a boolean using the boolean function is true.
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
+ xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
+ if ((val == NULL) || (arg == NULL) ||
+ ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
+ return(0);
+
+ switch(val->type) {
+ case XPATH_NUMBER:
+ return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ return(xmlXPathCompareNodeSets(inf, strict, arg, val));
+ case XPATH_STRING:
+ return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
+ case XPATH_BOOLEAN:
+ valuePush(ctxt, arg);
+ xmlXPathBooleanFunction(ctxt, 1);
+ valuePush(ctxt, val);
+ return(xmlXPathCompareValues(ctxt, inf, strict));
+ default:
+ TODO
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathEqualNodeSetString:
+ * @param arg the nodeset object argument
+ * @param str the string to compare to.
+ * @param neq flag to show whether for '=' (0) or '!=' (1)
+ *
+ * Implement the equal operation on XPath objects content: arg1 == arg2
+ * If one object to be compared is a node-set and the other is a string,
+ * then the comparison will be true if and only if there is a node in
+ * the node-set such that the result of performing the comparison on the
+ * string-value of the node and the other string is true.
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
+{
+ int i;
+ xmlNodeSetPtr ns;
+ xmlChar *str2;
+ unsigned int hash;
+
+ if ((str == NULL) || (arg == NULL) ||
+ ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
+ return (0);
+ ns = arg->nodesetval;
+ /*
+ * A NULL nodeset compared with a string is always false
+ * (since there is no node equal, and no node not equal)
+ */
+ if ((ns == NULL) || (ns->nodeNr <= 0) )
+ return (0);
+ hash = xmlXPathStringHash(str);
+ for (i = 0; i < ns->nodeNr; i++) {
+ if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
+ str2 = xmlNodeGetContent(ns->nodeTab[i]);
+ if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
+ xmlFree(str2);
+ if (neq)
+ continue;
+ return (1);
+ } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
+ if (neq)
+ continue;
+ return (1);
+ } else if (neq) {
+ if (str2 != NULL)
+ xmlFree(str2);
+ return (1);
+ }
+ if (str2 != NULL)
+ xmlFree(str2);
+ } else if (neq)
+ return (1);
+ }
+ return (0);
+}
+
+/**
+ * xmlXPathEqualNodeSetFloat:
+ * @param arg the nodeset object argument
+ * @param f the float to compare to
+ * @param neq flag to show whether to compare '=' (0) or '!=' (1)
+ *
+ * Implement the equal operation on XPath objects content: arg1 == arg2
+ * If one object to be compared is a node-set and the other is a number,
+ * then the comparison will be true if and only if there is a node in
+ * the node-set such that the result of performing the comparison on the
+ * number to be compared and on the result of converting the string-value
+ * of that node to a number using the number function is true.
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
+ xmlXPathObjectPtr arg, double f, int neq) {
+ int i, ret=0;
+ xmlNodeSetPtr ns;
+ xmlChar *str2;
+ xmlXPathObjectPtr val;
+ double v;
+
+ if ((arg == NULL) ||
+ ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
+ return(0);
+
+ ns = arg->nodesetval;
+ if (ns != NULL) {
+ for (i=0;i<ns->nodeNr;i++) {
+ str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
+ if (str2 != NULL) {
+ valuePush(ctxt, xmlXPathNewString(str2));
+ xmlFree(str2);
+ xmlXPathNumberFunction(ctxt, 1);
+ val = valuePop(ctxt);
+ v = val->floatval;
+ xmlXPathFreeObject(val);
+ if (!xmlXPathIsNaN(v)) {
+ if ((!neq) && (v==f)) {
+ ret = 1;
+ break;
+ } else if ((neq) && (v!=f)) {
+ ret = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+/**
+ * xmlXPathEqualNodeSets:
+ * @param arg1 first nodeset object argument
+ * @param arg2 second nodeset object argument
+ * @param neq flag to show whether to test '=' (0) or '!=' (1)
+ *
+ * Implement the equal / not equal operation on XPath nodesets:
+ * arg1 == arg2 or arg1 != arg2
+ * If both objects to be compared are node-sets, then the comparison
+ * will be true if and only if there is a node in the first node-set and
+ * a node in the second node-set such that the result of performing the
+ * comparison on the string-values of the two nodes is true.
+ *
+ * (needless to say, this is a costly operation)
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+static int
+xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
+ int i, j;
+ unsigned int *hashs1;
+ unsigned int *hashs2;
+ xmlChar **values1;
+ xmlChar **values2;
+ int ret = 0;
+ xmlNodeSetPtr ns1;
+ xmlNodeSetPtr ns2;
+
+ if ((arg1 == NULL) ||
+ ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
+ return(0);
+ if ((arg2 == NULL) ||
+ ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
+ return(0);
+
+ ns1 = arg1->nodesetval;
+ ns2 = arg2->nodesetval;
+
+ if ((ns1 == NULL) || (ns1->nodeNr <= 0))
+ return(0);
+ if ((ns2 == NULL) || (ns2->nodeNr <= 0))
+ return(0);
+
+ /*
+ * for equal, check if there is a node pertaining to both sets
+ */
+ if (neq == 0)
+ for (i = 0;i < ns1->nodeNr;i++)
+ for (j = 0;j < ns2->nodeNr;j++)
+ if (ns1->nodeTab[i] == ns2->nodeTab[j])
+ return(1);
+
+ values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
+ if (values1 == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
+ return(0);
+ }
+ hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
+ if (hashs1 == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
+ xmlFree(values1);
+ return(0);
+ }
+ memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
+ values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
+ if (values2 == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
+ xmlFree(hashs1);
+ xmlFree(values1);
+ return(0);
+ }
+ hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
+ if (hashs2 == NULL) {
+ xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
+ xmlFree(hashs1);
+ xmlFree(values1);
+ xmlFree(values2);
+ return(0);
+ }
+ memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
+ for (i = 0;i < ns1->nodeNr;i++) {
+ hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
+ for (j = 0;j < ns2->nodeNr;j++) {
+ if (i == 0)
+ hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
+ if (hashs1[i] != hashs2[j]) {
+ if (neq) {
+ ret = 1;
+ break;
+ }
+ }
+ else {
+ if (values1[i] == NULL)
+ values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
+ if (values2[j] == NULL)
+ values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
+ ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
+ if (ret)
+ break;
+ }
+ }
+ if (ret)
+ break;
+ }
+ for (i = 0;i < ns1->nodeNr;i++)
+ if (values1[i] != NULL)
+ xmlFree(values1[i]);
+ for (j = 0;j < ns2->nodeNr;j++)
+ if (values2[j] != NULL)
+ xmlFree(values2[j]);
+ xmlFree(values1);
+ xmlFree(values2);
+ xmlFree(hashs1);
+ xmlFree(hashs2);
+ return(ret);
+}
+
+static int
+xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
+ xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
+ int ret = 0;
+ /*
+ *At this point we are assured neither arg1 nor arg2
+ *is a nodeset, so we can just pick the appropriate routine.
+ */
+ switch (arg1->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: undefined\n");
+#endif
+ break;
+ case XPATH_BOOLEAN:
+ switch (arg2->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: undefined\n");
+#endif
+ break;
+ case XPATH_BOOLEAN:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: %d boolean %d \n",
+ arg1->boolval, arg2->boolval);
+#endif
+ ret = (arg1->boolval == arg2->boolval);
+ break;
+ case XPATH_NUMBER:
+ ret = (arg1->boolval ==
+ xmlXPathCastNumberToBoolean(arg2->floatval));
+ break;
+ case XPATH_STRING:
+ if ((arg2->stringval == NULL) ||
+ (arg2->stringval[0] == 0)) ret = 0;
+ else
+ ret = 1;
+ ret = (arg1->boolval == ret);
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ break;
+ }
+ break;
+ case XPATH_NUMBER:
+ switch (arg2->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: undefined\n");
+#endif
+ break;
+ case XPATH_BOOLEAN:
+ ret = (arg2->boolval==
+ xmlXPathCastNumberToBoolean(arg1->floatval));
+ break;
+ case XPATH_STRING:
+ valuePush(ctxt, arg2);
+ xmlXPathNumberFunction(ctxt, 1);
+ arg2 = valuePop(ctxt);
+ /* no break on purpose */
+ case XPATH_NUMBER:
+ /* Hand check NaN and Infinity equalities */
+ if (xmlXPathIsNaN(arg1->floatval) ||
+ xmlXPathIsNaN(arg2->floatval)) {
+ ret = 0;
+ } else if (xmlXPathIsInf(arg1->floatval) == 1) {
+ if (xmlXPathIsInf(arg2->floatval) == 1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg1->floatval) == -1) {
+ if (xmlXPathIsInf(arg2->floatval) == -1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg2->floatval) == 1) {
+ if (xmlXPathIsInf(arg1->floatval) == 1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg2->floatval) == -1) {
+ if (xmlXPathIsInf(arg1->floatval) == -1)
+ ret = 1;
+ else
+ ret = 0;
+ } else {
+ ret = (arg1->floatval == arg2->floatval);
+ }
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ break;
+ }
+ break;
+ case XPATH_STRING:
+ switch (arg2->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: undefined\n");
+#endif
+ break;
+ case XPATH_BOOLEAN:
+ if ((arg1->stringval == NULL) ||
+ (arg1->stringval[0] == 0)) ret = 0;
+ else
+ ret = 1;
+ ret = (arg2->boolval == ret);
+ break;
+ case XPATH_STRING:
+ ret = xmlStrEqual(arg1->stringval, arg2->stringval);
+ break;
+ case XPATH_NUMBER:
+ valuePush(ctxt, arg1);
+ xmlXPathNumberFunction(ctxt, 1);
+ arg1 = valuePop(ctxt);
+ /* Hand check NaN and Infinity equalities */
+ if (xmlXPathIsNaN(arg1->floatval) ||
+ xmlXPathIsNaN(arg2->floatval)) {
+ ret = 0;
+ } else if (xmlXPathIsInf(arg1->floatval) == 1) {
+ if (xmlXPathIsInf(arg2->floatval) == 1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg1->floatval) == -1) {
+ if (xmlXPathIsInf(arg2->floatval) == -1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg2->floatval) == 1) {
+ if (xmlXPathIsInf(arg1->floatval) == 1)
+ ret = 1;
+ else
+ ret = 0;
+ } else if (xmlXPathIsInf(arg2->floatval) == -1) {
+ if (xmlXPathIsInf(arg1->floatval) == -1)
+ ret = 1;
+ else
+ ret = 0;
+ } else {
+ ret = (arg1->floatval == arg2->floatval);
+ }
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ break;
+ }
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ break;
+ }
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(ret);
+}
+
+/**
+ * xmlXPathEqualValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the equal operation on XPath objects content: arg1 == arg2
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+XMLPUBFUNEXPORT int
+xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg1, arg2, argtmp;
+ int ret = 0;
+
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ if ((arg1 == NULL) || (arg2 == NULL)) {
+ if (arg1 != NULL)
+ xmlXPathFreeObject(arg1);
+ else
+ xmlXPathFreeObject(arg2);
+ XP_ERROR0(XPATH_INVALID_OPERAND);
+ }
+
+ if (arg1 == arg2) {
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: by pointer\n");
+#endif
+ return(1);
+ }
+
+ /*
+ *If either argument is a nodeset, it's a 'special case'
+ */
+ if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
+ (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
+ /*
+ *
+ */
+ if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
+ argtmp = arg2;
+ arg2 = arg1;
+ arg1 = argtmp;
+ }
+ switch (arg2->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "Equal: undefined\n");
+#endif
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
+ break;
+ case XPATH_BOOLEAN:
+ if ((arg1->nodesetval == NULL) ||
+ (arg1->nodesetval->nodeNr == 0)) ret = 0;
+ else
+ ret = 1;
+ ret = (ret == arg2->boolval);
+ break;
+ case XPATH_NUMBER:
+ ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
+ break;
+ case XPATH_STRING:
+ ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ }
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(ret);
+ }
+
+ return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
+}
+
+/**
+ * xmlXPathNotEqualValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the equal operation on XPath objects content: arg1 == arg2
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+XMLPUBFUNEXPORT int
+xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg1, arg2, argtmp;
+ int ret = 0;
+
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ if ((arg1 == NULL) || (arg2 == NULL)) {
+ if (arg1 != NULL)
+ xmlXPathFreeObject(arg1);
+ else
+ xmlXPathFreeObject(arg2);
+ XP_ERROR0(XPATH_INVALID_OPERAND);
+ }
+
+ if (arg1 == arg2) {
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "NotEqual: by pointer\n");
+#endif
+ return(0);
+ }
+
+ /*
+ *If either argument is a nodeset, it's a 'special case'
+ */
+ if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
+ (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
+ /*
+ *
+ */
+ if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
+ argtmp = arg2;
+ arg2 = arg1;
+ arg1 = argtmp;
+ }
+ switch (arg2->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "NotEqual: undefined\n");
+#endif
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
+ break;
+ case XPATH_BOOLEAN:
+ if ((arg1->nodesetval == NULL) ||
+ (arg1->nodesetval->nodeNr == 0)) ret = 0;
+ else
+ ret = 1;
+ ret = (ret != arg2->boolval);
+ break;
+ case XPATH_NUMBER:
+ ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
+ break;
+ case XPATH_STRING:
+ ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO
+ break;
+ }
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(ret);
+ }
+
+ return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
+}
+
+/**
+ * xmlXPathCompareValues:
+ * @param ctxt the XPath Parser context
+ * @param inf less than (1) or greater than (0)
+ * @param strict is the comparison strict
+ *
+ * Implement the compare operation on XPath objects:
+ * arg1 < arg2 (1, 1, ...
+ * arg1 <= arg2 (1, 0, ...
+ * arg1 > arg2 (0, 1, ...
+ * arg1 >= arg2 (0, 0, ...
+ *
+ * When neither object to be compared is a node-set and the operator is
+ * <=, <, >=, >, then the objects are compared by converted both objects
+ * to numbers and comparing the numbers according to IEEE 754. The <
+ * comparison will be true if and only if the first number is less than the
+ * second number. The <= comparison will be true if and only if the first
+ * number is less than or equal to the second number. The > comparison
+ * will be true if and only if the first number is greater than the second
+ * number. The >= comparison will be true if and only if the first number
+ * is greater than or equal to the second number.
+ *
+ * Returns 1 if the comparison succeeded, 0 if it failed
+ */
+XMLPUBFUNEXPORT int
+xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
+ int ret = 0, arg1i = 0, arg2i = 0;
+ xmlXPathObjectPtr arg1, arg2;
+
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ if ((arg1 == NULL) || (arg2 == NULL)) {
+ if (arg1 != NULL)
+ xmlXPathFreeObject(arg1);
+ else
+ xmlXPathFreeObject(arg2);
+ XP_ERROR0(XPATH_INVALID_OPERAND);
+ }
+
+ if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
+ (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
+ if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
+ ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
+ ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
+ } else {
+ if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
+ ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
+ arg1, arg2);
+ } else {
+ ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
+ arg2, arg1);
+ }
+ }
+ return(ret);
+ }
+
+ if (arg1->type != XPATH_NUMBER) {
+ valuePush(ctxt, arg1);
+ xmlXPathNumberFunction(ctxt, 1);
+ arg1 = valuePop(ctxt);
+ }
+ if (arg1->type != XPATH_NUMBER) {
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ XP_ERROR0(XPATH_INVALID_OPERAND);
+ }
+ if (arg2->type != XPATH_NUMBER) {
+ valuePush(ctxt, arg2);
+ xmlXPathNumberFunction(ctxt, 1);
+ arg2 = valuePop(ctxt);
+ }
+ if (arg2->type != XPATH_NUMBER) {
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ XP_ERROR0(XPATH_INVALID_OPERAND);
+ }
+ /*
+ * Add tests for infinity and nan
+ * => feedback on 3.4 for Inf and NaN
+ */
+ /* Hand check NaN and Infinity comparisons */
+ if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
+ ret=0;
+ } else {
+ arg1i=xmlXPathIsInf(arg1->floatval);
+ arg2i=xmlXPathIsInf(arg2->floatval);
+ if (inf && strict) {
+ if ((arg1i == -1 && arg2i != -1) ||
+ (arg2i == 1 && arg1i != 1)) {
+ ret = 1;
+ } else if (arg1i == 0 && arg2i == 0) {
+ ret = (arg1->floatval < arg2->floatval);
+ } else {
+ ret = 0;
+ }
+ }
+ else if (inf && !strict) {
+ if (arg1i == -1 || arg2i == 1) {
+ ret = 1;
+ } else if (arg1i == 0 && arg2i == 0) {
+ ret = (arg1->floatval <= arg2->floatval);
+ } else {
+ ret = 0;
+ }
+ }
+ else if (!inf && strict) {
+ if ((arg1i == 1 && arg2i != 1) ||
+ (arg2i == -1 && arg1i != -1)) {
+ ret = 1;
+ } else if (arg1i == 0 && arg2i == 0) {
+ ret = (arg1->floatval > arg2->floatval);
+ } else {
+ ret = 0;
+ }
+ }
+ else if (!inf && !strict) {
+ if (arg1i == 1 || arg2i == -1) {
+ ret = 1;
+ } else if (arg1i == 0 && arg2i == 0) {
+ ret = (arg1->floatval >= arg2->floatval);
+ } else {
+ ret = 0;
+ }
+ }
+ }
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ return(ret);
+}
+
+/**
+ * xmlXPathValueFlipSign:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the unary - operation on an XPath object
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ if (xmlXPathIsNaN(ctxt->value->floatval))
+ ctxt->value->floatval=xmlXPathNAN;
+ else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
+ ctxt->value->floatval=xmlXPathNINF;
+ else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
+ ctxt->value->floatval=xmlXPathPINF;
+ else if (ctxt->value->floatval == 0) {
+ if (xmlXPathGetSign(ctxt->value->floatval) == 0)
+ ctxt->value->floatval = xmlXPathNZERO;
+ else
+ ctxt->value->floatval = 0;
+ }
+ else
+ ctxt->value->floatval = - ctxt->value->floatval;
+}
+
+/**
+ * xmlXPathAddValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the add operation on XPath objects:
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ double val;
+
+ arg = valuePop(ctxt);
+ if (arg == NULL)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ val = xmlXPathCastToNumber(arg);
+ xmlXPathFreeObject(arg);
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ ctxt->value->floatval += val;
+}
+
+/**
+ * xmlXPathSubValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the subtraction operation on XPath objects:
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ double val;
+
+ arg = valuePop(ctxt);
+ if (arg == NULL)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ val = xmlXPathCastToNumber(arg);
+ xmlXPathFreeObject(arg);
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ ctxt->value->floatval -= val;
+}
+
+/**
+ * xmlXPathMultValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the multiply operation on XPath objects:
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ double val;
+
+ arg = valuePop(ctxt);
+ if (arg == NULL)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ val = xmlXPathCastToNumber(arg);
+ xmlXPathFreeObject(arg);
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ ctxt->value->floatval *= val;
+}
+
+/**
+ * xmlXPathDivValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the div operation on XPath objects arg1 / arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ double val;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ arg = valuePop(ctxt);
+ if (arg == NULL)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ val = xmlXPathCastToNumber(arg);
+ xmlXPathFreeObject(arg);
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
+ ctxt->value->floatval = xmlXPathNAN;
+ else if (val == 0 && xmlXPathGetSign(val) != 0) {
+ if (ctxt->value->floatval == 0)
+ ctxt->value->floatval = xmlXPathNAN;
+ else if (ctxt->value->floatval > 0)
+ ctxt->value->floatval = xmlXPathNINF;
+ else if (ctxt->value->floatval < 0)
+ ctxt->value->floatval = xmlXPathPINF;
+ }
+ else if (val == 0) {
+ if (ctxt->value->floatval == 0)
+ ctxt->value->floatval = xmlXPathNAN;
+ else if (ctxt->value->floatval > 0)
+ ctxt->value->floatval = xmlXPathPINF;
+ else if (ctxt->value->floatval < 0)
+ ctxt->value->floatval = xmlXPathNINF;
+ } else
+ ctxt->value->floatval /= val;
+}
+
+/**
+ * xmlXPathModValues:
+ * @param ctxt the XPath Parser context
+ *
+ * Implement the mod operation on XPath objects: arg1 / arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ double arg1, arg2;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ arg = valuePop(ctxt);
+ if (arg == NULL)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ arg2 = xmlXPathCastToNumber(arg);
+ xmlXPathFreeObject(arg);
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ arg1 = ctxt->value->floatval;
+ if (arg2 == 0)
+ ctxt->value->floatval = xmlXPathNAN;
+ else {
+ ctxt->value->floatval = fmod(arg1, arg2);
+ }
+}
+
+/************************************************************************
+ * *
+ * The traversal functions *
+ * *
+ ************************************************************************/
+
+/*
+ * A traversal function enumerates nodes along an axis.
+ * Initially it must be called with NULL, and it indicates
+ * termination on the axis by returning NULL.
+ */
+typedef xmlNodePtr (*xmlXPathTraversalFunction)
+ (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
+
+/**
+ * xmlXPathNextSelf:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "self" direction
+ * The self axis contains just the context node itself
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
+{
+ if (cur == NULL)
+ return(ctxt->context->node);
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextChild:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "child" direction
+ * The child axis contains the children of the context node in document order.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL) {
+ if (ctxt->context->node == NULL) return(NULL);
+ switch (ctxt->context->node->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ return(ctxt->context->node->children);
+ case XML_DOCUMENT_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ return(((xmlDocPtr) ctxt->context->node)->children);
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_ATTRIBUTE_NODE:
+ case XML_NAMESPACE_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ return(NULL);
+ }
+ return(NULL);
+ }
+ if ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE))
+ return(NULL);
+ return(cur->next);
+}
+
+/**
+ * xmlXPathNextDescendant:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "descendant" direction
+ * the descendant axis contains the descendants of the context node in document
+ * order; a descendant is a child or a child of a child and so on.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL) {
+ if (ctxt->context->node == NULL)
+ return(NULL);
+ if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
+ (ctxt->context->node->type == XML_NAMESPACE_DECL))
+ return(NULL);
+
+ if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
+ return(ctxt->context->doc->children);
+ return(ctxt->context->node->children);
+ }
+
+ if (cur->children != NULL) {
+ /*
+ * Do not descend on entities declarations
+ */
+ if (cur->children->type != XML_ENTITY_DECL) {
+ cur = cur->children;
+ /*
+ * Skip DTDs
+ */
+ if (cur->type != XML_DTD_NODE)
+ return(cur);
+ }
+ }
+
+ if (cur == ctxt->context->node) return(NULL);
+
+ while (cur->next != NULL) {
+ cur = cur->next;
+ if ((cur->type != XML_ENTITY_DECL) &&
+ (cur->type != XML_DTD_NODE))
+ return(cur);
+ }
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == ctxt->context->node) return(NULL);
+ if (cur->next != NULL) {
+ cur = cur->next;
+ return(cur);
+ }
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * xmlXPathNextDescendantOrSelf:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "descendant-or-self" direction
+ * the descendant-or-self axis contains the context node and the descendants
+ * of the context node in document order; thus the context node is the first
+ * node on the axis, and the first child of the context node is the second node
+ * on the axis
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL) {
+ if (ctxt->context->node == NULL)
+ return(NULL);
+ if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
+ (ctxt->context->node->type == XML_NAMESPACE_DECL))
+ return(NULL);
+ return(ctxt->context->node);
+ }
+
+ return(xmlXPathNextDescendant(ctxt, cur));
+}
+
+/**
+ * xmlXPathNextParent:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "parent" direction
+ * The parent axis contains the parent of the context node, if there is one.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ /*
+ * the parent of an attribute or namespace node is the element
+ * to which the attribute or namespace node is attached
+ * Namespace handling !!!
+ */
+ if (cur == NULL) {
+ if (ctxt->context->node == NULL) return(NULL);
+ switch (ctxt->context->node->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ case XML_ENTITY_DECL:
+ if (ctxt->context->node->parent == NULL)
+ return((xmlNodePtr) ctxt->context->doc);
+ if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
+ ((ctxt->context->node->parent->name[0] == ' ') ||
+ (xmlStrEqual(ctxt->context->node->parent->name,
+ BAD_CAST "fake node libxslt"))))
+ return(NULL);
+ return(ctxt->context->node->parent);
+ case XML_ATTRIBUTE_NODE: {
+ xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
+
+ return(att->parent);
+ }
+ case XML_DOCUMENT_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ return(NULL);
+ case XML_NAMESPACE_DECL: {
+ xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
+
+ if ((ns->next != NULL) &&
+ (ns->next->type != XML_NAMESPACE_DECL))
+ return((xmlNodePtr) ns->next);
+ return(NULL);
+ }
+ }
+ }
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextAncestor:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "ancestor" direction
+ * the ancestor axis contains the ancestors of the context node; the ancestors
+ * of the context node consist of the parent of context node and the parent's
+ * parent and so on; the nodes are ordered in reverse document order; thus the
+ * parent is the first node on the axis, and the parent's parent is the second
+ * node on the axis
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ /*
+ * the parent of an attribute or namespace node is the element
+ * to which the attribute or namespace node is attached
+ * !!!!!!!!!!!!!
+ */
+ if (cur == NULL) {
+ if (ctxt->context->node == NULL) return(NULL);
+ switch (ctxt->context->node->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_NOTATION_NODE:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ if (ctxt->context->node->parent == NULL)
+ return((xmlNodePtr) ctxt->context->doc);
+ if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
+ ((ctxt->context->node->parent->name[0] == ' ') ||
+ (xmlStrEqual(ctxt->context->node->parent->name,
+ BAD_CAST "fake node libxslt"))))
+ return(NULL);
+ return(ctxt->context->node->parent);
+ case XML_ATTRIBUTE_NODE: {
+ xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
+
+ return(tmp->parent);
+ }
+ case XML_DOCUMENT_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ return(NULL);
+ case XML_NAMESPACE_DECL: {
+ xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
+
+ if ((ns->next != NULL) &&
+ (ns->next->type != XML_NAMESPACE_DECL))
+ return((xmlNodePtr) ns->next);
+ /* Bad, how did that namespace end up here ? */
+ return(NULL);
+ }
+ }
+ return(NULL);
+ }
+ if (cur == ctxt->context->doc->children)
+ return((xmlNodePtr) ctxt->context->doc);
+ if (cur == (xmlNodePtr) ctxt->context->doc)
+ return(NULL);
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ if (cur->parent == NULL)
+ return(NULL);
+ if ((cur->parent->type == XML_ELEMENT_NODE) &&
+ ((cur->parent->name[0] == ' ') ||
+ (xmlStrEqual(cur->parent->name,
+ BAD_CAST "fake node libxslt"))))
+ return(NULL);
+ return(cur->parent);
+ case XML_ATTRIBUTE_NODE: {
+ xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
+
+ return(att->parent);
+ }
+ case XML_NAMESPACE_DECL: {
+ xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
+
+ if ((ns->next != NULL) &&
+ (ns->next->type != XML_NAMESPACE_DECL))
+ return((xmlNodePtr) ns->next);
+ /* Bad, how did that namespace end up here ? */
+ return(NULL);
+ }
+ case XML_DOCUMENT_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ return(NULL);
+ }
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextAncestorOrSelf:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "ancestor-or-self" direction
+ * he ancestor-or-self axis contains the context node and ancestors of
+ * the context node in reverse document order; thus the context node is
+ * the first node on the axis, and the context node's parent the second;
+ * parent here is defined the same as with the parent axis.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node);
+ return(xmlXPathNextAncestor(ctxt, cur));
+}
+
+/**
+ * xmlXPathNextFollowingSibling:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "following-sibling" direction
+ * The following-sibling axis contains the following siblings of the context
+ * node in document order.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
+ (ctxt->context->node->type == XML_NAMESPACE_DECL))
+ return(NULL);
+ if (cur == (xmlNodePtr) ctxt->context->doc)
+ return(NULL);
+ if (cur == NULL)
+ return(ctxt->context->node->next);
+ return(cur->next);
+}
+
+/**
+ * xmlXPathNextPrecedingSibling:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "preceding-sibling" direction
+ * The preceding-sibling axis contains the preceding siblings of the context
+ * node in reverse document order; the first preceding sibling is first on the
+ * axis; the sibling preceding that node is the second on the axis and so on.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
+ (ctxt->context->node->type == XML_NAMESPACE_DECL))
+ return(NULL);
+ if (cur == (xmlNodePtr) ctxt->context->doc)
+ return(NULL);
+ if (cur == NULL)
+ return(ctxt->context->node->prev);
+ if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
+ cur = cur->prev;
+ if (cur == NULL)
+ return(ctxt->context->node->prev);
+ }
+ return(cur->prev);
+}
+
+/**
+ * xmlXPathNextFollowing:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "following" direction
+ * The following axis contains all nodes in the same document as the context
+ * node that are after the context node in document order, excluding any
+ * descendants and excluding attribute nodes and namespace nodes; the nodes
+ * are ordered in document order
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur != NULL && cur->children != NULL)
+ return cur->children ;
+ if (cur == NULL) cur = ctxt->context->node;
+ if (cur == NULL) return(NULL) ; /* ERROR */
+ if (cur->next != NULL) return(cur->next) ;
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
+ if (cur->next != NULL) return(cur->next);
+ } while (cur != NULL);
+ return(cur);
+}
+
+/*
+ * xmlXPathIsAncestor:
+ * @param ancestor the ancestor node
+ * @param node the current node
+ *
+ * Check that ancestor is a node's ancestor
+ *
+ * returns 1 if ancestor is a node's ancestor, 0 otherwise.
+ */
+static int
+xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
+ if ((ancestor == NULL) || (node == NULL)) return(0);
+ /* nodes need to be in the same document */
+ if (ancestor->doc != node->doc) return(0);
+ /* avoid searching if ancestor or node is the root node */
+ if (ancestor == (xmlNodePtr) node->doc) return(1);
+ if (node == (xmlNodePtr) ancestor->doc) return(0);
+ while (node->parent != NULL) {
+ if (node->parent == ancestor)
+ return(1);
+ node = node->parent;
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathNextPreceding:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "preceding" direction
+ * the preceding axis contains all nodes in the same document as the context
+ * node that are before the context node in document order, excluding any
+ * ancestors and excluding attribute nodes and namespace nodes; the nodes are
+ * ordered in reverse document order
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
+{
+ if (cur == NULL)
+ cur = ctxt->context->node;
+ if (cur == NULL)
+ return (NULL);
+ if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
+ cur = cur->prev;
+ do {
+ if (cur->prev != NULL) {
+ for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
+ return (cur);
+ }
+
+ cur = cur->parent;
+ if (cur == NULL)
+ return (NULL);
+ if (cur == ctxt->context->doc->children)
+ return (NULL);
+ } while (xmlXPathIsAncestor(cur, ctxt->context->node));
+ return (cur);
+}
+
+/**
+ * xmlXPathNextPrecedingInternal:
+ * @param ctxt the XPath Parser context
+ * @param cur the current node in the traversal
+ *
+ * Traversal function for the "preceding" direction
+ * the preceding axis contains all nodes in the same document as the context
+ * node that are before the context node in document order, excluding any
+ * ancestors and excluding attribute nodes and namespace nodes; the nodes are
+ * ordered in reverse document order
+ * This is a faster implementation but internal only since it requires a
+ * state kept in the parser context: ctxt->ancestor.
+ *
+ * Returns the next element following that axis
+ */
+static xmlNodePtr
+xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
+ xmlNodePtr cur)
+{
+ if (cur == NULL) {
+ cur = ctxt->context->node;
+ if (cur == NULL)
+ return (NULL);
+ if (cur->type == XML_NAMESPACE_DECL)
+ cur = (xmlNodePtr)((xmlNsPtr)cur)->next;
+ ctxt->ancestor = cur->parent;
+ }
+ if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
+ cur = cur->prev;
+ while (cur->prev == NULL) {
+ cur = cur->parent;
+ if (cur == NULL)
+ return (NULL);
+ if (cur == ctxt->context->doc->children)
+ return (NULL);
+ if (cur != ctxt->ancestor)
+ return (cur);
+ ctxt->ancestor = cur->parent;
+ }
+ cur = cur->prev;
+ while (cur->last != NULL)
+ cur = cur->last;
+ return (cur);
+}
+
+/**
+ * xmlXPathNextNamespace:
+ * @param ctxt the XPath Parser context
+ * @param cur the current attribute in the traversal
+ *
+ * Traversal function for the "namespace" direction
+ * the namespace axis contains the namespace nodes of the context node;
+ * the order of nodes on this axis is implementation-defined; the axis will
+ * be empty unless the context node is an element
+ *
+ * We keep the XML namespace node at the end of the list.
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (ctxt->context->node->type != XML_ELEMENT_NODE)
+ return(NULL);
+ if (ctxt->context->tmpNsList == NULL &&
+ cur != (xmlNodePtr) xmlXPathXMLNamespace)
+ {
+ if (ctxt->context->tmpNsList != NULL)
+ xmlFree(ctxt->context->tmpNsList);
+ ctxt->context->tmpNsList =
+ xmlGetNsList(ctxt->context->doc, ctxt->context->node);
+ ctxt->context->tmpNsNr = 0;
+ if (ctxt->context->tmpNsList != NULL) {
+ while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
+ ctxt->context->tmpNsNr++;
+ }
+ }
+ return((xmlNodePtr) xmlXPathXMLNamespace);
+ }
+ if (ctxt->context->tmpNsNr > 0) {
+ return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
+ } else {
+ if (ctxt->context->tmpNsList != NULL)
+ xmlFree(ctxt->context->tmpNsList);
+ ctxt->context->tmpNsList = NULL;
+ return(NULL);
+ }
+}
+
+/**
+ * xmlXPathNextAttribute:
+ * @param ctxt the XPath Parser context
+ * @param cur the current attribute in the traversal
+ *
+ * Traversal function for the "attribute" direction
+ *
+ *
+ * Returns the next element following that axis
+ */
+XMLPUBFUNEXPORT xmlNodePtr
+xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (ctxt->context->node == NULL)
+ return(NULL);
+ if (ctxt->context->node->type != XML_ELEMENT_NODE)
+ return(NULL);
+ if (cur == NULL) {
+ if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
+ return(NULL);
+ return((xmlNodePtr)ctxt->context->node->properties);
+ }
+ return((xmlNodePtr)cur->next);
+}
+
+/************************************************************************
+ * *
+ * NodeTest Functions *
+ * *
+ ************************************************************************/
+
+#define IS_FUNCTION 200
+
+
+/************************************************************************
+ * *
+ * Implicit tree core function library *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathRoot:
+ * @param ctxt the XPath Parser context
+ *
+ * Initialize the context to the root of the document
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
+ ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+}
+
+/************************************************************************
+ * *
+ * The explicit core function library *
+ *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
+ * *
+ ************************************************************************/
+
+
+/**
+ * xmlXPathLastFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the last() XPath function
+ * number last()
+ * The last function returns the number of nodes in the context node list.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ if (ctxt->context->contextSize >= 0) {
+ valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext,
+ "last() : %d\n", ctxt->context->contextSize);
+#endif
+ } else {
+ XP_ERROR(XPATH_INVALID_CTXT_SIZE);
+ }
+}
+
+/**
+ * xmlXPathPositionFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the position() XPath function
+ * number position()
+ * The position function returns the position of the context node in the
+ * context node list. The first position is 1, and so the last position
+ * will be equal to last().
+ */
+XMLPUBFUNEXPORT void
+xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ if (ctxt->context->proximityPosition >= 0) {
+ valuePush(ctxt,
+ xmlXPathNewFloat((double) ctxt->context->proximityPosition));
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
+ ctxt->context->proximityPosition);
+#endif
+ } else {
+ XP_ERROR(XPATH_INVALID_CTXT_POSITION);
+ }
+}
+
+/**
+ * xmlXPathCountFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the count() XPath function
+ * number count(node-set)
+ */
+XMLPUBFUNEXPORT void
+xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_NODESET) &&
+ (ctxt->value->type != XPATH_XSLT_TREE)))
+ XP_ERROR(XPATH_INVALID_TYPE);
+ cur = valuePop(ctxt);
+
+ if ((cur == NULL) || (cur->nodesetval == NULL))
+ valuePush(ctxt, xmlXPathNewFloat((double) 0));
+ else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
+ valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
+ } else {
+ if ((cur->nodesetval->nodeNr != 1) ||
+ (cur->nodesetval->nodeTab == NULL)) {
+ valuePush(ctxt, xmlXPathNewFloat((double) 0));
+ } else {
+ xmlNodePtr tmp;
+ int i = 0;
+
+ tmp = cur->nodesetval->nodeTab[0];
+ if (tmp != NULL) {
+ tmp = tmp->children;
+ while (tmp != NULL) {
+ tmp = tmp->next;
+ i++;
+ }
+ }
+ valuePush(ctxt, xmlXPathNewFloat((double) i));
+ }
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathGetElementsByIds:
+ * @param doc the document
+ * @param ids a whitespace separated list of IDs
+ *
+ * Selects elements by their unique ID.
+ *
+ * Returns a node-set of selected elements.
+ */
+static xmlNodeSetPtr
+xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar* ids)
+{
+ xmlNodeSetPtr ret;
+ const xmlChar* cur = ids;
+ xmlChar *ID;
+ xmlAttrPtr attr;
+ xmlNodePtr elem;// Unneeded initialization: // = NULL;
+
+ if (!ids)
+ return(NULL);
+
+ ret = xmlXPathNodeSetCreate(NULL);
+
+ if ( !ret )
+ return NULL;
+
+ while (IS_BLANK_CH(*cur))
+ cur++;
+
+ while (*cur != 0)
+ {
+ while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
+ cur++;
+
+ ID = xmlStrndup(ids, cur - ids);
+ if (ID != NULL) {
+ /*
+ * We used to check the fact that the value passed
+ * was an NCName, but this generated much troubles for
+ * me and Aleksey Sanin, people blatantly violated that
+ * constaint, like Visa3D spec.
+ * if (xmlValidateNCName(ID, 1) == 0)
+ */
+ attr = xmlGetID(doc, ID);
+ if (attr != NULL)
+ {
+ // Note: xmlGetID CAN return xmlDocPtr instead of xmlAttrPtr!!!
+ if (attr->type == XML_ATTRIBUTE_NODE)
+ elem = attr->parent;
+ else if (attr->type == XML_ELEMENT_NODE)
+ elem = (xmlNodePtr) attr;
+ else
+ elem = NULL;
+ if (elem)
+ xmlXPathNodeSetAdd(ret, elem);
+ }
+ xmlFree(ID);
+ }
+
+ while (IS_BLANK_CH(*cur))
+ cur++;
+ ids = cur;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathIdFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the id() XPath function
+ * node-set id(object)
+ * The id function selects elements by their unique ID
+ * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
+ * then the result is the union of the result of applying id to the
+ * string value of each of the nodes in the argument node-set. When the
+ * argument to id is of any other type, the argument is converted to a
+ * string as if by a call to the string function; the string is split
+ * into a whitespace-separated list of tokens (whitespace is any sequence
+ * of characters matching the production S); the result is a node-set
+ * containing the elements in the same document as the context node that
+ * have a unique ID equal to any of the tokens in the list.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlChar *tokens;
+ xmlNodeSetPtr ret;
+ xmlXPathObjectPtr obj;
+ xmlXPathObjectPtr wrappedNodeSet;
+
+ CHECK_ARITY(1);
+ obj = valuePop(ctxt);
+ if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
+ if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
+ xmlNodeSetPtr ns;
+ int i;
+
+ ret = xmlXPathNodeSetCreate(NULL);
+
+ if (obj->nodesetval != NULL) {
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ tokens =
+ xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
+ ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
+ ret = xmlXPathNodeSetMerge(ret, ns);
+ xmlXPathFreeNodeSet(ns);
+ if (tokens != NULL)
+ xmlFree(tokens);
+ }
+ }
+
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathWrapNodeSet(ret));
+ return;
+ }
+ obj = xmlXPathConvertString(obj);
+
+ ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
+ wrappedNodeSet = xmlXPathWrapNodeSet(ret);
+ if ( !wrappedNodeSet )
+ {
+ xmlXPathFreeNodeSet( ret );
+ }
+ valuePush(ctxt, wrappedNodeSet);
+
+ xmlXPathFreeObject(obj);
+ return;
+}
+
+/**
+ * xmlXPathLocalNameFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the local-name() XPath function
+ * string local-name(node-set?)
+ * The local-name function returns a string containing the local part
+ * of the name of the node in the argument node-set that is first in
+ * document order. If the node-set is empty or the first node has no
+ * name, an empty string is returned. If the argument is omitted it
+ * defaults to the context node.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ nargs = 1;
+ }
+
+ CHECK_ARITY(1);
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_NODESET) &&
+ (ctxt->value->type != XPATH_XSLT_TREE)))
+ XP_ERROR(XPATH_INVALID_TYPE);
+ cur = valuePop(ctxt);
+
+ if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
+ valuePush(ctxt, xmlXPathNewCString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+ switch (cur->nodesetval->nodeTab[i]->type) {
+ case XML_ELEMENT_NODE:
+ case XML_ATTRIBUTE_NODE:
+ case XML_PI_NODE:
+ if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
+ valuePush(ctxt, xmlXPathNewCString(""));
+ else
+ valuePush(ctxt,
+ xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
+ break;
+ case XML_NAMESPACE_DECL:
+ valuePush(ctxt, xmlXPathNewString(
+ ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
+ break;
+ default:
+ valuePush(ctxt, xmlXPathNewCString(""));
+ }
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathNamespaceURIFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the namespace-uri() XPath function
+ * string namespace-uri(node-set?)
+ * The namespace-uri function returns a string containing the
+ * namespace URI of the expanded name of the node in the argument
+ * node-set that is first in document order. If the node-set is empty,
+ * the first node has no name, or the expanded name has no namespace
+ * URI, an empty string is returned. If the argument is omitted it
+ * defaults to the context node.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ nargs = 1;
+ }
+ CHECK_ARITY(1);
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_NODESET) &&
+ (ctxt->value->type != XPATH_XSLT_TREE)))
+ XP_ERROR(XPATH_INVALID_TYPE);
+ cur = valuePop(ctxt);
+
+ if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
+ valuePush(ctxt, xmlXPathNewCString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+ switch (cur->nodesetval->nodeTab[i]->type) {
+ case XML_ELEMENT_NODE:
+ case XML_ATTRIBUTE_NODE:
+ if (cur->nodesetval->nodeTab[i]->ns == NULL)
+ valuePush(ctxt, xmlXPathNewCString(""));
+ else
+ valuePush(ctxt, xmlXPathNewString(
+ cur->nodesetval->nodeTab[i]->ns->href));
+ break;
+ default:
+ valuePush(ctxt, xmlXPathNewCString(""));
+ }
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathNameFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the name() XPath function
+ * string name(node-set?)
+ * The name function returns a string containing a QName representing
+ * the name of the node in the argument node-set that is first in document
+ * order. The QName must represent the name with respect to the namespace
+ * declarations in effect on the node whose name is being represented.
+ * Typically, this will be the form in which the name occurred in the XML
+ * source. This need not be the case if there are namespace declarations
+ * in effect on the node that associate multiple prefixes with the same
+ * namespace. However, an implementation may include information about
+ * the original prefix in its representation of nodes; in this case, an
+ * implementation can ensure that the returned string is always the same
+ * as the QName used in the XML source. If the argument it omitted it
+ * defaults to the context node.
+ * Libxml keep the original prefix so the "real qualified name" used is
+ * returned.
+ */
+static void
+xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ nargs = 1;
+ }
+
+ CHECK_ARITY(1);
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_NODESET) &&
+ (ctxt->value->type != XPATH_XSLT_TREE)))
+ XP_ERROR(XPATH_INVALID_TYPE);
+ cur = valuePop(ctxt);
+
+ if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
+ valuePush(ctxt, xmlXPathNewCString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+
+ switch (cur->nodesetval->nodeTab[i]->type) {
+ case XML_ELEMENT_NODE:
+ case XML_ATTRIBUTE_NODE:
+ if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
+ valuePush(ctxt, xmlXPathNewCString(""));
+ else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
+ (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
+ valuePush(ctxt,
+ xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
+
+ } else {
+ xmlChar *fullname;
+
+ fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
+ cur->nodesetval->nodeTab[i]->ns->prefix,
+ NULL, 0);
+ if (fullname == cur->nodesetval->nodeTab[i]->name)
+ fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
+ if (fullname == NULL) {
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ valuePush(ctxt, xmlXPathWrapString(fullname));
+ }
+ break;
+ default:
+ valuePush(ctxt,
+ xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
+ xmlXPathLocalNameFunction(ctxt, 1);
+ }
+ }
+ xmlXPathFreeObject(cur);
+}
+
+
+/**
+ * xmlXPathStringFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the string() XPath function
+ * string string(object?)
+ * The string function converts an object to a string as follows:
+ * - A node-set is converted to a string by returning the value of
+ * the node in the node-set that is first in document order.
+ * If the node-set is empty, an empty string is returned.
+ * - A number is converted to a string as follows
+ * + NaN is converted to the string NaN
+ * + positive zero is converted to the string 0
+ * + negative zero is converted to the string 0
+ * + positive infinity is converted to the string Infinity
+ * + negative infinity is converted to the string -Infinity
+ * + if the number is an integer, the number is represented in
+ * decimal form as a Number with no decimal point and no leading
+ * zeros, preceded by a minus sign (-) if the number is negative
+ * + otherwise, the number is represented in decimal form as a
+ * Number including a decimal point with at least one digit
+ * before the decimal point and at least one digit after the
+ * decimal point, preceded by a minus sign (-) if the number
+ * is negative; there must be no leading zeros before the decimal
+ * point apart possibly from the one required digit immediately
+ * before the decimal point; beyond the one required digit
+ * after the decimal point there must be as many, but only as
+ * many, more digits as are needed to uniquely distinguish the
+ * number from all other IEEE 754 numeric values.
+ * - The boolean false value is converted to the string false.
+ * The boolean true value is converted to the string true.
+ *
+ * If the argument is omitted, it defaults to a node-set with the
+ * context node as its only member.
+ *
+ * OOM: possible
+ */
+XMLPUBFUNEXPORT void
+xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ valuePush(ctxt,
+ xmlXPathWrapString(
+ xmlXPathCastNodeToString(ctxt->context->node)));
+ return;
+ }
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ if (!cur)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ cur = xmlXPathConvertString(cur); // returns NULL if OOM
+
+ if(!cur)
+ XP_ERROR(XPATH_MEMORY_ERROR); // OOM
+
+ valuePush(ctxt, cur);
+}
+
+/**
+ * xmlXPathStringLengthFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the string-length() XPath function
+ * number string-length(string?)
+ * The string-length returns the number of characters in the string
+ * (see [3.6 Strings]). If the argument is omitted, it defaults to
+ * the context node converted to a string, in other words the value
+ * of the context node.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ if (ctxt->context->node == NULL) {
+ valuePush(ctxt, xmlXPathNewFloat(0));
+ } else {
+ xmlChar *content;
+
+ content = xmlXPathCastNodeToString(ctxt->context->node);
+ valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
+ xmlFree(content);
+ }
+ return;
+ }
+ CHECK_ARITY(1);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ cur = valuePop(ctxt);
+ valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathConcatFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the concat() XPath function
+ * string concat(string, string, string*)
+ * The concat function returns the concatenation of its arguments.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur, newobj;
+ xmlChar *tmp;
+
+ if (nargs < 2) {
+ CHECK_ARITY(2);
+ }
+
+ CAST_TO_STRING;
+ cur = valuePop(ctxt);
+ if ((cur == NULL) || (cur->type != XPATH_STRING)) {
+ xmlXPathFreeObject(cur);
+ return;
+ }
+ nargs--;
+
+ while (nargs > 0) {
+ CAST_TO_STRING;
+ newobj = valuePop(ctxt);
+ if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
+ xmlXPathFreeObject(newobj);
+ xmlXPathFreeObject(cur);
+ XP_ERROR(XPATH_INVALID_TYPE);
+ }
+ tmp = xmlStrcat(newobj->stringval, cur->stringval);
+ newobj->stringval = cur->stringval;
+ cur->stringval = tmp;
+
+ xmlXPathFreeObject(newobj);
+ nargs--;
+ }
+ valuePush(ctxt, cur);
+}
+
+/**
+ * xmlXPathContainsFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the contains() XPath function
+ * boolean contains(string, string)
+ * The contains function returns true if the first argument string
+ * contains the second argument string, and otherwise returns false.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr hay, needle;
+
+ CHECK_ARITY(2);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ needle = valuePop(ctxt);
+ CAST_TO_STRING;
+ hay = valuePop(ctxt);
+ if ((hay == NULL) || (hay->type != XPATH_STRING)) {
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+ XP_ERROR(XPATH_INVALID_TYPE);
+ }
+ if (xmlStrstr(hay->stringval, needle->stringval))
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ else
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+}
+
+/**
+ * xmlXPathStartsWithFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the starts-with() XPath function
+ * boolean starts-with(string, string)
+ * The starts-with function returns true if the first argument string
+ * starts with the second argument string, and otherwise returns false.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr hay, needle;
+ int n;
+
+ CHECK_ARITY(2);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ needle = valuePop(ctxt);
+ CAST_TO_STRING;
+ hay = valuePop(ctxt);
+ if ((hay == NULL) || (hay->type != XPATH_STRING)) {
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+ XP_ERROR(XPATH_INVALID_TYPE);
+ }
+ n = xmlStrlen(needle->stringval);
+ if (xmlStrncmp(hay->stringval, needle->stringval, n))
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ else
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+}
+
+/**
+ * xmlXPathSubstringFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the substring() XPath function
+ * string substring(string, number, number?)
+ * The substring function returns the substring of the first argument
+ * starting at the position specified in the second argument with
+ * length specified in the third argument. For example,
+ * substring("12345",2,3) returns "234". If the third argument is not
+ * specified, it returns the substring starting at the position specified
+ * in the second argument and continuing to the end of the string. For
+ * example, substring("12345",2) returns "2345". More precisely, each
+ * character in the string (see [3.6 Strings]) is considered to have a
+ * numeric position: the position of the first character is 1, the position
+ * of the second character is 2 and so on. The returned substring contains
+ * those characters for which the position of the character is greater than
+ * or equal to the second argument and, if the third argument is specified,
+ * less than the sum of the second and third arguments; the comparisons
+ * and addition used for the above follow the standard IEEE 754 rules. Thus:
+ * - substring("12345", 1.5, 2.6) returns "234"
+ * - substring("12345", 0, 3) returns "12"
+ * - substring("12345", 0 div 0, 3) returns ""
+ * - substring("12345", 1, 0 div 0) returns ""
+ * - substring("12345", -42, 1 div 0) returns "12345"
+ * - substring("12345", -1 div 0, 1 div 0) returns ""
+ */
+XMLPUBFUNEXPORT void
+xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str, start, len;
+ double le=0, in;
+
+ int i, l, m;
+ xmlChar *ret;
+
+ if (nargs < 2) {
+
+ // -- make check manually OR define normal macro for that
+ CHECK_ARITY(2);
+ }
+ if (nargs > 3) {
+ CHECK_ARITY(3);
+ }
+ /*
+ * take care of possible last (position) argument
+ */
+ if (nargs == 3) {
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ len = valuePop(ctxt);
+ le = len->floatval;
+ xmlXPathFreeObject(len);
+ }
+
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+ start = valuePop(ctxt);
+ in = start->floatval;
+ xmlXPathFreeObject(start);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ str = valuePop(ctxt);
+ m = xmlUTF8Strlen((const unsigned char *)str->stringval);
+
+ /*
+ * If last pos not present, calculate last position
+ */
+ if (nargs != 3) {
+ le = (double)m;
+ if (in < 1.0)
+ in = 1.0;
+ }
+
+ /* Need to check for the special cases where either
+ * the index is NaN, the length is NaN, or both
+ * arguments are infinity (relying on Inf + -Inf = NaN)
+ */
+ if (!xmlXPathIsNaN(in + le) && !xmlXPathIsInf(in)) {
+ /*
+ * To meet the requirements of the spec, the arguments
+ * must be converted to integer format before
+ * initial index calculations are done
+ *
+ * First we go to integer form, rounding up
+ * and checking for special cases
+ */
+ i = (int) in;
+ if (((double)i)+0.5 <= in) i++;
+
+ if (xmlXPathIsInf(le) == 1) {
+ l = m;
+ if (i < 1)
+ i = 1;
+ }
+ else if (xmlXPathIsInf(le) == -1 || le < 0.0)
+ l = 0;
+ else {
+ l = (int) le;
+ if (((double)l)+0.5 <= le) l++;
+ }
+
+ /* Now we normalize inidices */
+ i -= 1;
+ l += i;
+ if (i < 0)
+ i = 0;
+ if (l > m)
+ l = m;
+
+ /* number of chars to copy */
+ l -= i;
+
+ ret = xmlUTF8Strsub(str->stringval, i, l);
+ }
+ else {
+ ret = NULL;
+ }
+
+ if (ret == NULL)
+ valuePush(ctxt, xmlXPathNewCString(""));
+ else {
+ valuePush(ctxt, xmlXPathNewString(ret));
+ xmlFree(ret);
+ }
+
+ xmlXPathFreeObject(str);
+}
+
+/**
+ * xmlXPathSubstringBeforeFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the substring-before() XPath function
+ * string substring-before(string, string)
+ * The substring-before function returns the substring of the first
+ * argument string that precedes the first occurrence of the second
+ * argument string in the first argument string, or the empty string
+ * if the first argument string does not contain the second argument
+ * string. For example, substring-before("1999/04/01","/") returns 1999.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str;
+ xmlXPathObjectPtr find;
+ xmlBufferPtr target;
+ const xmlChar *point;
+ int offset;
+
+ CHECK_ARITY(2);
+ CAST_TO_STRING;
+ find = valuePop(ctxt);
+ CAST_TO_STRING;
+ str = valuePop(ctxt);
+
+ target = xmlBufferCreate();
+ if (target) {
+ point = xmlStrstr(str->stringval, find->stringval);
+ if (point) {
+ offset = (int)(point - str->stringval);
+ xmlBufferAdd(target, str->stringval, offset);
+ }
+ valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ xmlBufferFree(target);
+ }
+
+ xmlXPathFreeObject(str);
+ xmlXPathFreeObject(find);
+}
+
+/**
+ * xmlXPathSubstringAfterFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the substring-after() XPath function
+ * string substring-after(string, string)
+ * The substring-after function returns the substring of the first
+ * argument string that follows the first occurrence of the second
+ * argument string in the first argument string, or the empty stringi
+ * if the first argument string does not contain the second argument
+ * string. For example, substring-after("1999/04/01","/") returns 04/01,
+ * and substring-after("1999/04/01","19") returns 99/04/01.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str;
+ xmlXPathObjectPtr find;
+ xmlBufferPtr target;
+ const xmlChar *point;
+ int offset;
+
+ CHECK_ARITY(2);
+ CAST_TO_STRING;
+ find = valuePop(ctxt);
+ CAST_TO_STRING;
+ str = valuePop(ctxt);
+
+ target = xmlBufferCreate();
+ if (target) {
+ point = xmlStrstr(str->stringval, find->stringval);
+ if (point) {
+ offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
+ xmlBufferAdd(target, &str->stringval[offset],
+ xmlStrlen(str->stringval) - offset);
+ }
+ valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ xmlBufferFree(target);
+ }
+
+ xmlXPathFreeObject(str);
+ xmlXPathFreeObject(find);
+}
+
+/**
+ * xmlXPathNormalizeFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the normalize-space() XPath function
+ * string normalize-space(string?)
+ * The normalize-space function returns the argument string with white
+ * space normalized by stripping leading and trailing whitespace
+ * and replacing sequences of whitespace characters by a single
+ * space. Whitespace characters are the same allowed by the S production
+ * in XML. If the argument is omitted, it defaults to the context
+ * node converted to a string, in other words the value of the context node.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr obj = NULL;
+ xmlChar *source = NULL;
+ xmlBufferPtr target;
+ xmlChar blank;
+
+ if (nargs == 0) {
+ /* Use current context node */
+ valuePush(ctxt,
+ xmlXPathWrapString(
+ xmlXPathCastNodeToString(ctxt->context->node)));
+ nargs = 1;
+ }
+
+ CHECK_ARITY(1);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ obj = valuePop(ctxt);
+ source = obj->stringval;
+
+ target = xmlBufferCreate();
+ if (target && source) {
+
+ /* Skip leading whitespaces */
+ while (IS_BLANK_CH(*source))
+ source++;
+
+ /* Collapse intermediate whitespaces, and skip trailing whitespaces */
+ blank = 0;
+ while (*source) {
+ if (IS_BLANK_CH(*source)) {
+ blank = 0x20;
+ } else {
+ if (blank) {
+ xmlBufferAdd(target, &blank, 1);
+ blank = 0;
+ }
+ xmlBufferAdd(target, source, 1);
+ }
+ source++;
+ }
+
+ valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ xmlBufferFree(target);
+ }
+ xmlXPathFreeObject(obj);
+}
+
+/**
+ * xmlXPathTranslateFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the translate() XPath function
+ * string translate(string, string, string)
+ * The translate function returns the first argument string with
+ * occurrences of characters in the second argument string replaced
+ * by the character at the corresponding position in the third argument
+ * string. For example, translate("bar","abc","ABC") returns the string
+ * BAr. If there is a character in the second argument string with no
+ * character at a corresponding position in the third argument string
+ * (because the second argument string is longer than the third argument
+ * string), then occurrences of that character in the first argument
+ * string are removed. For example, translate("--aaa--","abc-","ABC")
+ * returns "AAA". If a character occurs more than once in second
+ * argument string, then the first occurrence determines the replacement
+ * character. If the third argument string is longer than the second
+ * argument string, then excess characters are ignored.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str;
+ xmlXPathObjectPtr from;
+ xmlXPathObjectPtr to;
+ xmlBufferPtr target;
+ int offset, max;
+ xmlChar ch;
+ xmlChar *point;
+ xmlChar *cptr;
+
+ CHECK_ARITY(3);
+
+ CAST_TO_STRING;
+ to = valuePop(ctxt);
+ CAST_TO_STRING;
+ from = valuePop(ctxt);
+ CAST_TO_STRING;
+ str = valuePop(ctxt);
+
+ target = xmlBufferCreate();
+ if (target) {
+ max = xmlUTF8Strlen(to->stringval);
+ for (cptr = str->stringval; (ch=*cptr); ) {
+ offset = xmlUTF8Strloc(from->stringval, cptr);
+ if (offset >= 0) {
+ if (offset < max) {
+ point = xmlUTF8Strpos(to->stringval, offset);
+ if (point)
+ xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
+ }
+ } else
+ xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
+
+ /* Step to next character in input */
+ cptr++;
+ if ( ch & 0x80 ) {
+ /* if not simple ascii, verify proper format */
+ if ( (ch & 0xc0) != 0xc0 ) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n"));
+ break;
+ }
+ /* then skip over remaining bytes for this char */
+ while ( (ch <<= 1) & 0x80 )
+ if ( (*cptr++ & 0xc0) != 0x80 ) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n"));
+ break;
+ }
+ if (ch & 0x80) /* must have had error encountered */
+ break;
+ }
+ }
+ }
+ valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ xmlBufferFree(target);
+ xmlXPathFreeObject(str);
+ xmlXPathFreeObject(from);
+ xmlXPathFreeObject(to);
+}
+
+/**
+ * xmlXPathBooleanFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the boolean() XPath function
+ * boolean boolean(object)
+ * The boolean function converts its argument to a boolean as follows:
+ * - a number is true if and only if it is neither positive or
+ * negative zero nor NaN
+ * - a node-set is true if and only if it is non-empty
+ * - a string is true if and only if its length is non-zero
+ */
+XMLPUBFUNEXPORT void
+xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
+ cur = xmlXPathConvertBoolean(cur);
+ valuePush(ctxt, cur);
+}
+
+/**
+ * xmlXPathNotFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the not() XPath function
+ * boolean not(boolean)
+ * The not function returns true if its argument is false,
+ * and false otherwise.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ CAST_TO_BOOLEAN;
+ CHECK_TYPE(XPATH_BOOLEAN);
+ ctxt->value->boolval = ! ctxt->value->boolval;
+}
+
+/**
+ * xmlXPathTrueFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the true() XPath function
+ * boolean true()
+ */
+XMLPUBFUNEXPORT void
+xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+}
+
+/**
+ * xmlXPathFalseFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the false() XPath function
+ * boolean false()
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+}
+
+/**
+ * xmlXPathLangFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the lang() XPath function
+ * boolean lang(string)
+ * The lang function returns true or false depending on whether the
+ * language of the context node as specified by xml:lang attributes
+ * is the same as or is a sublanguage of the language specified by
+ * the argument string. The language of the context node is determined
+ * by the value of the xml:lang attribute on the context node, or, if
+ * the context node has no xml:lang attribute, by the value of the
+ * xml:lang attribute on the nearest ancestor of the context node that
+ * has an xml:lang attribute. If there is no such attribute, then lang
+ * returns false. If there is such an attribute, then lang returns
+ * true if the attribute value is equal to the argument ignoring case,
+ * or if there is some suffix starting with - such that the attribute
+ * value is equal to the argument ignoring that suffix of the attribute
+ * value and ignoring case.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr val;
+ const xmlChar *theLang;
+ const xmlChar *lang;
+ int ret = 0;
+ int i;
+
+ CHECK_ARITY(1);
+ CAST_TO_STRING;
+ CHECK_TYPE(XPATH_STRING);
+ val = valuePop(ctxt);
+ lang = val->stringval;
+ theLang = xmlNodeGetLang(ctxt->context->node);
+ if ((theLang != NULL) && (lang != NULL)) {
+ for (i = 0;lang[i] != 0;i++)
+ if (toupper(lang[i]) != toupper(theLang[i]))
+ goto not_equal;
+ ret = 1;
+ }
+not_equal:
+ if (theLang)
+ xmlFree((void *)theLang);
+ xmlXPathFreeObject(val);
+ valuePush(ctxt, xmlXPathNewBoolean(ret));
+}
+
+/**
+ * xmlXPathNumberFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the number() XPath function
+ * number number(object?)
+ */
+XMLPUBFUNEXPORT void
+xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+ double res;
+
+ if (nargs == 0) {
+ if (!ctxt->context->node) {
+ valuePush(ctxt, xmlXPathNewFloat(0.0));
+ } else {
+ xmlChar* content = xmlNodeGetContent(ctxt->context->node);
+
+ res = xmlXPathStringEvalNumber(content);
+ valuePush(ctxt, xmlXPathNewFloat(res));
+ xmlFree(content);
+ }
+ return;
+ }
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ cur = xmlXPathConvertNumber(cur);
+ valuePush(ctxt, cur);
+}
+
+/**
+ * xmlXPathSumFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the sum() XPath function
+ * number sum(node-set)
+ * The sum function returns the sum of the values of the nodes in
+ * the argument node-set.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+ int i;
+ double res = 0.0;
+
+ CHECK_ARITY(1);
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_NODESET) &&
+ (ctxt->value->type != XPATH_XSLT_TREE)))
+ XP_ERROR(XPATH_INVALID_TYPE);
+ cur = valuePop(ctxt);
+
+ if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
+ for (i = 0; i < cur->nodesetval->nodeNr; i++) {
+ res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
+ }
+ }
+ valuePush(ctxt, xmlXPathNewFloat(res));
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathFloorFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the floor() XPath function
+ * number floor(number)
+ * The floor function returns the largest (closest to positive infinity)
+ * number that is not greater than the argument and that is an integer.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ double f;
+
+ CHECK_ARITY(1);
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+
+ f = (double)((int) ctxt->value->floatval);
+ if (f != ctxt->value->floatval) {
+ if (ctxt->value->floatval > 0)
+ ctxt->value->floatval = f;
+ else
+ ctxt->value->floatval = f - 1;
+ }
+}
+
+/**
+ * xmlXPathCeilingFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the ceiling() XPath function
+ * number ceiling(number)
+ * The ceiling function returns the smallest (closest to negative infinity)
+ * number that is not less than the argument and that is an integer.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ double f;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ CHECK_ARITY(1);
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+
+#if 0
+ ctxt->value->floatval = ceil(ctxt->value->floatval);
+#else
+ f = (double)((int) ctxt->value->floatval);
+ if (f != ctxt->value->floatval) {
+ if (ctxt->value->floatval > 0)
+ ctxt->value->floatval = f + 1;
+ else {
+ if (ctxt->value->floatval < 0 && f == 0)
+ ctxt->value->floatval = xmlXPathNZERO;
+ else
+ ctxt->value->floatval = f;
+ }
+
+ }
+#endif
+}
+
+/**
+ * xmlXPathRoundFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the round() XPath function
+ * number round(number)
+ * The round function returns the number that is closest to the
+ * argument and that is an integer. If there are two such numbers,
+ * then the one that is even is returned.
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ double f;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ CHECK_ARITY(1);
+ CAST_TO_NUMBER;
+ CHECK_TYPE(XPATH_NUMBER);
+
+ if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
+ (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
+ (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
+ (ctxt->value->floatval == 0.0))
+ return;
+
+ f = (double)((int) ctxt->value->floatval);
+ if (ctxt->value->floatval < 0) {
+ if (ctxt->value->floatval < f - 0.5)
+ ctxt->value->floatval = f - 1;
+ else
+ ctxt->value->floatval = f;
+ if (ctxt->value->floatval == 0)
+ ctxt->value->floatval = xmlXPathNZERO;
+ } else {
+ if (ctxt->value->floatval < f + 0.5)
+ ctxt->value->floatval = f;
+ else
+ ctxt->value->floatval = f + 1;
+ }
+}
+
+/************************************************************************
+ * *
+ * The Parser *
+ * *
+ ************************************************************************/
+
+/*
+ * a few forward declarations since we use a recursive call based
+ * implementation.
+ */
+static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
+static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
+static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
+static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
+static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
+ int qualified);
+
+/**
+ * xmlXPathCurrentChar:
+ * @param ctxt the XPath parser context
+ * @param cur pointer to the beginning of the char
+ * @param len pointer to the length of the char read
+ *
+ * The current char value, if using UTF-8 this may actually span multiple
+ * bytes in the input buffer.
+ *
+ * Returns the current char value and its length
+ */
+
+static int
+xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
+ unsigned char c;
+ unsigned int val;
+ const xmlChar *cur;
+
+ if (ctxt == NULL)
+ return(0);
+ cur = ctxt->cur;
+
+ /*
+ * We are supposed to handle UTF8, check it's valid
+ * From rfc2044: encoding of the Unicode values on UTF-8:
+ *
+ * UCS-4 range (hex.) UTF-8 octet sequence (binary)
+ * 0000 0000-0000 007F 0xxxxxxx
+ * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
+ *
+ * Check for the 0x110000 limit too
+ */
+ c = *cur;
+ if (c & 0x80) {
+ if ((cur[1] & 0xc0) != 0x80)
+ goto encoding_error;
+ if ((c & 0xe0) == 0xe0) {
+
+ if ((cur[2] & 0xc0) != 0x80)
+ goto encoding_error;
+ if ((c & 0xf0) == 0xf0) {
+ if (((c & 0xf8) != 0xf0) ||
+ ((cur[3] & 0xc0) != 0x80))
+ goto encoding_error;
+ /* 4-byte code */
+ *len = 4;
+ val = (cur[0] & 0x7) << 18;
+ val |= (cur[1] & 0x3f) << 12;
+ val |= (cur[2] & 0x3f) << 6;
+ val |= cur[3] & 0x3f;
+ } else {
+ /* 3-byte code */
+ *len = 3;
+ val = (cur[0] & 0xf) << 12;
+ val |= (cur[1] & 0x3f) << 6;
+ val |= cur[2] & 0x3f;
+ }
+ } else {
+ /* 2-byte code */
+ *len = 2;
+ val = (cur[0] & 0x1f) << 6;
+ val |= cur[1] & 0x3f;
+ }
+ if (!IS_CHAR(val)) {
+ XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
+ }
+ return(val);
+ } else {
+ /* 1-byte code */
+ *len = 1;
+ return((int) *cur);
+ }
+encoding_error:
+ /*
+ * If we detect an UTF8 error that probably means that the
+ * input encoding didn't get properly advertised in the
+ * declaration header. Report the error and switch the encoding
+ * to ISO-Latin-1 (if you don't like this policy, just declare the
+ * encoding !)
+ */
+ *len = 0;
+ XP_ERROR0(XPATH_ENCODING_ERROR);
+}
+
+/**
+ * xmlXPathParseNCName:
+ * @param ctxt the XPath Parser context
+ *
+ * parse an XML namespace non qualified name.
+ *
+ * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
+ *
+ * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
+ * CombiningChar | Extender
+ *
+ * Returns the namespace name or NULL
+ * OOM: possible --> returns NULL, but OOM flag should be checked
+ * (can return NULL in non-OOM)
+ */
+XMLPUBFUNEXPORT xmlChar*
+xmlXPathParseNCName(xmlXPathParserContextPtr ctxt)
+{
+ const xmlChar* in;
+ /*
+ * Accelerator for simple ASCII names
+ */
+ in = ctxt->cur;
+ if (((*in >= 0x61) && (*in <= 0x7A)) ||
+ ((*in >= 0x41) && (*in <= 0x5A)) ||
+ (*in == '_'))
+ {
+ in++;
+ while (((*in >= 0x61) && (*in <= 0x7A)) ||
+ ((*in >= 0x41) && (*in <= 0x5A)) ||
+ ((*in >= 0x30) && (*in <= 0x39)) ||
+ (*in == '_') ||
+ (*in == '.') ||
+ (*in == '-'))
+ {
+ in++;
+ }
+ if ((*in == ' ') || (*in == '>') || (*in == '/') ||
+ (*in == '[') || (*in == ']') || (*in == ':') ||
+ (*in == '@') || (*in == '*'))
+ {
+ xmlChar* ret;
+ int count = in - ctxt->cur;
+ // NotE: count always > 0 here since in++ was done
+ //if (count == 0)
+ // return(NULL);
+ ret = xmlStrndup(ctxt->cur, count);
+ //if(!ret)
+ // xmlXPathErr(ctxt, XPATH_MEMORY_ERROR);
+ ctxt->cur = in;
+ return(ret);
+ }
+ }
+ return(xmlXPathParseNameComplex(ctxt, 0));
+}
+
+
+/**
+ * xmlXPathParseQName:
+ * @param ctxt the XPath Parser context
+ * @param prefix a xmlChar **
+ *
+ * parse an XML qualified name
+ *
+ * [NS 5] QName ::= (Prefix ':')? LocalPart
+ *
+ * [NS 6] Prefix ::= NCName
+ *
+ * [NS 7] LocalPart ::= NCName
+ *
+ * Returns the function returns the local part, and prefix is updated
+ * to get the Prefix if any.
+ */
+
+static xmlChar *
+xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix)
+{
+ xmlChar *ret = NULL;
+
+ *prefix = NULL;
+ ret = xmlXPathParseNCName(ctxt);
+ if (CUR == ':') {
+ *prefix = ret;
+ NEXT;
+ ret = xmlXPathParseNCName(ctxt);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathParseName:
+ * @param ctxt the XPath Parser context
+ *
+ * parse an XML name
+ *
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+ * CombiningChar | Extender
+ *
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * Returns the namespace name or NULL
+ */
+
+XMLPUBFUNEXPORT xmlChar *
+xmlXPathParseName(xmlXPathParserContextPtr ctxt)
+{
+ const xmlChar *in;
+
+ /*
+ * Accelerator for simple ASCII names
+ */
+ in = ctxt->cur;
+ if (((*in >= 0x61) && (*in <= 0x7A)) ||
+ ((*in >= 0x41) && (*in <= 0x5A)) ||
+ (*in == '_') || (*in == ':'))
+ {
+ in++;
+ while (((*in >= 0x61) && (*in <= 0x7A)) ||
+ ((*in >= 0x41) && (*in <= 0x5A)) ||
+ ((*in >= 0x30) && (*in <= 0x39)) ||
+ (*in == '_') || (*in == '-') ||
+ (*in == ':') || (*in == '.'))
+ in++;
+ if ((*in > 0) && (*in < 0x80))
+ {
+ int count = in - ctxt->cur;
+ xmlChar* ret= xmlStrndup(ctxt->cur, count);
+ ctxt->cur = in;
+ return(ret);
+ }
+ }
+ return(xmlXPathParseNameComplex(ctxt, 1));
+}
+
+/*
+ * OOM: possible --> check OOM flag
+ */
+static xmlChar*
+xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified)
+{
+ xmlChar buf[XML_MAX_NAMELEN + 5];
+ int len = 0, l;
+ int c;
+
+ /*
+ * Handler for more complex cases
+ */
+ c = CUR_CHAR(l);
+ if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
+ (c == '[') || (c == ']') || (c == '@') || /* accelerators */
+ (c == '*') || /* accelerators */
+ (!IS_LETTER(c) &&
+ (c != '_') &&
+ qualified &&
+ (c != ':')
+ )
+ )
+ {
+ return(NULL);
+ }
+
+ while((c != ' ') && (c != '>') && (c != '/')
+ && /* test bigname.xml */
+ (
+ IS_LETTER(c) ||
+ IS_DIGIT(c) ||
+ (c == '.') ||
+ (c == '-') ||
+ (c == '_') ||
+ (qualified && (c == ':')) ||
+ IS_COMBINING(c) ||
+ IS_EXTENDER(c)
+ )
+ )
+ {
+ COPY_BUF(l,buf,len,c);
+ NEXTL(l);
+ c = CUR_CHAR(l);
+ if (len >= XML_MAX_NAMELEN)
+ {
+ /*
+ * Okay someone managed to make a huge name, so he's ready to pay
+ * for the processing speed.
+ */
+ xmlChar* buffer;
+ int max = len * 2;
+
+ buffer = (xmlChar*) xmlMallocAtomic(max * sizeof(xmlChar));
+ if (!buffer) {
+ XP_ERROR0(XPATH_MEMORY_ERROR);
+ }
+ memcpy(buffer, buf, len);
+ while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
+ (c == '.') || (c == '-') ||
+ (c == '_') || ((qualified) && (c == ':')) ||
+ (IS_COMBINING(c)) ||
+ (IS_EXTENDER(c)))
+ {
+ if (len + 10 > max)
+ {
+ xmlChar* tmp;
+ max *= 2;
+ // DONE: Fix xmlRealloc
+ tmp = (xmlChar*) xmlRealloc(buffer, max * sizeof(xmlChar));
+ if (!tmp) {
+ xmlFree(buffer);
+ XP_ERROR0(XPATH_MEMORY_ERROR);
+ }
+ buffer = tmp;
+ }
+ COPY_BUF(l,buffer,len,c);
+ NEXTL(l);
+ c = CUR_CHAR(l);
+ }
+ buffer[len] = 0;
+ return(buffer);
+ }
+ }
+ if (len == 0)
+ return(NULL);
+ return(xmlStrndup(buf, len));
+}
+
+#define MAX_FRAC 20
+
+/*
+ * These are used as divisors for the fractional part of a number.
+ * Since the table includes 1.0 (representing '0' fractional digits),
+ * it must be dimensioned at MAX_FRAC+1 (bug133921)
+ */
+static const double my_pow10[MAX_FRAC+1] = {
+ 1.0, 10.0, 100.0, 1000.0, 10000.0,
+ 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
+ 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
+ 100000000000000.0,
+ 1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
+ 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
+};
+
+
+/**
+ * xmlXPathStringEvalNumber:
+ * @param str A string to scan
+ *
+ * [30a] Float ::= Number ('e' Digits?)?
+ *
+ * [30] Number ::= Digits ('.' Digits?)?
+ * | '.' Digits
+ * [31] Digits ::= [0-9]+
+ *
+ * Compile a Number in the string
+ * In complement of the Number expression, this function also handles
+ * negative values : '-' Number.
+ *
+ * Returns the double value.
+ *
+ * OOM: never
+ */
+XMLPUBFUNEXPORT double
+xmlXPathStringEvalNumber(const xmlChar *str) {
+ LOAD_GS_DIRECT
+ const xmlChar *cur = str;
+ double ret;
+ int ok = 0;
+ int isneg = 0;
+ int exponent = 0;
+ int is_exponent_negative = 0;
+#ifdef __GNUC__
+ unsigned long tmp = 0;
+ double temp;
+#endif
+ if (cur == NULL)
+ return(0);
+ //
+ while (IS_BLANK_CH(*cur))
+ {
+ cur++;
+ }
+
+ if ((*cur != '.') &&
+ ((*cur < '0') || (*cur > '9')) &&
+ (*cur != '-'))
+ {
+ return(xmlXPathNAN);
+ }
+ if (*cur == '-') {
+ isneg = 1;
+ cur++;
+ }
+
+#ifdef __GNUC__
+ /*
+ * tmp/temp is a workaround against a gcc compilerBug
+ * http://veillard.com/gcc.bug
+ */
+ ret = 0;
+ while ((*cur >= '0') && (*cur <= '9'))
+ {
+ ret = ret * 10;
+ tmp = (*cur - '0');
+ ok = 1;
+ cur++;
+ temp = (double) tmp;
+ ret = ret + temp;
+ }
+#else
+ ret = 0;
+ while ((*cur >= '0') && (*cur <= '9'))
+ {
+ ret = ret * 10 + (*cur - '0');
+ ok = 1;
+ cur++;
+ }
+#endif
+
+ if (*cur == '.')
+ {
+ int v, frac = 0;
+ double fraction = 0;
+
+ cur++;
+ if (((*cur < '0') || (*cur > '9')) && (!ok))
+ {
+ return(xmlXPathNAN);
+ }
+ while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC))
+ {
+ v = (*cur - '0');
+ fraction = fraction * 10 + v;
+ frac = frac + 1;
+ cur++;
+ }
+ fraction /= my_pow10[frac];
+ ret = ret + fraction;
+ while ((*cur >= '0') && (*cur <= '9'))
+ {
+ cur++;
+ }
+ }
+ if ((*cur == 'e') || (*cur == 'E'))
+ {
+ cur++;
+ if (*cur == '-')
+ {
+ is_exponent_negative = 1;
+ cur++;
+ }
+ while ((*cur >= '0') && (*cur <= '9'))
+ {
+ exponent = exponent * 10 + (*cur - '0');
+ cur++;
+ }
+ }
+ while (IS_BLANK_CH(*cur))
+ {
+ cur++;
+ }
+ if (*cur != 0)
+ return(xmlXPathNAN);
+ if (isneg)
+ ret = -ret;
+ if (is_exponent_negative)
+ exponent = -exponent;
+ //
+ ret *= pow(10.0, (double)exponent);
+ return(ret);
+}
+
+/**
+ * xmlXPathCompNumber:
+ * @param ctxt the XPath Parser context
+ *
+ * [30] Number ::= Digits ('.' Digits?)?
+ * | '.' Digits
+ * [31] Digits ::= [0-9]+
+ *
+ * Compile a Number, then push it on the stack
+ *
+ */
+static void
+xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
+{
+ double ret = 0.0;
+ double mult = 1;
+ int ok = 0;
+ int exponent = 0;
+ int is_exponent_negative = 0;
+#ifdef __GNUC__
+ unsigned long tmp = 0;
+ double temp;
+#endif
+
+ CHECK_ERROR;
+ if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
+ XP_ERROR(XPATH_NUMBER_ERROR);
+ }
+#ifdef __GNUC__
+ /*
+ * tmp/temp is a workaround against a gcc compilerBug
+ * http://veillard.com/gcc.bug
+ */
+ ret = 0;
+ while ((CUR >= '0') && (CUR <= '9')) {
+ ret = ret * 10;
+ tmp = (CUR - '0');
+ ok = 1;
+ NEXT;
+ temp = (double) tmp;
+ ret = ret + temp;
+ }
+#else
+ ret = 0;
+ while ((CUR >= '0') && (CUR <= '9')) {
+ ret = ret * 10 + (CUR - '0');
+ ok = 1;
+ NEXT;
+ }
+#endif
+ if (CUR == '.') {
+ NEXT;
+ if (((CUR < '0') || (CUR > '9')) && (!ok)) {
+ XP_ERROR(XPATH_NUMBER_ERROR);
+ }
+ while ((CUR >= '0') && (CUR <= '9')) {
+ mult /= 10;
+ ret = ret + (CUR - '0') * mult;
+ NEXT;
+ }
+ }
+ if ((CUR == 'e') || (CUR == 'E')) {
+ NEXT;
+ if (CUR == '-') {
+ is_exponent_negative = 1;
+ NEXT;
+ }
+ while ((CUR >= '0') && (CUR <= '9')) {
+ exponent = exponent * 10 + (CUR - '0');
+ NEXT;
+ }
+ if (is_exponent_negative)
+ exponent = -exponent;
+ ret *= pow(10.0, (double) exponent);
+ }
+ PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
+ xmlXPathNewFloat(ret), NULL);
+}
+
+/**
+ * xmlXPathParseLiteral:
+ * @param ctxt the XPath Parser context
+ *
+ * Parse a Literal
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ * | "'" [^']* "'"
+ *
+ * Returns the value found or NULL in case of error
+ * OOM: possible --> NULL is returned; parser is set into error state
+ */
+static xmlChar*
+xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt)
+{
+ const xmlChar *q;
+ xmlChar* ret;
+ const xmlChar quote = CUR;
+ // DONE: Optimize: cases with ' and " have almost identical code ==> combine
+ if (quote == '"' || quote == '\'') {
+ NEXT;
+ q = CUR_PTR;
+ while ((IS_CHAR_CH(CUR)) && (CUR != quote))
+ {
+ NEXT;
+ }
+ if (!IS_CHAR_CH(CUR)) {
+ XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
+ } else {
+ ret = xmlStrndup(q, CUR_PTR - q);
+ if(!ret){
+ XP_ERROR0(XPATH_MEMORY_ERROR);
+ }
+ NEXT;
+ return ret;
+ }
+ } else {
+ XP_ERROR0(XPATH_START_LITERAL_ERROR);
+ }
+}
+
+/**
+ * xmlXPathCompLiteral:
+ * @param ctxt the XPath Parser context
+ *
+ * Parse a Literal and push it on the stack.
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ * | "'" [^']* "'"
+ *
+ *
+ */
+static void
+xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
+ const xmlChar *q;
+ xmlChar *ret = NULL;
+
+ if (CUR == '"') {
+ NEXT;
+ q = CUR_PTR;
+ while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
+ NEXT;
+ if (!IS_CHAR_CH(CUR)) {
+ XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
+ } else {
+ ret = xmlStrndup(q, CUR_PTR - q);
+ NEXT;
+ }
+ } else if (CUR == '\'') {
+ NEXT;
+ q = CUR_PTR;
+ while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
+ NEXT;
+ if (!IS_CHAR_CH(CUR)) {
+ XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
+ } else {
+ ret = xmlStrndup(q, CUR_PTR - q);
+ NEXT;
+ }
+ } else {
+ XP_ERROR(XPATH_START_LITERAL_ERROR);
+ }
+ if (ret == NULL) return;
+ PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
+ xmlXPathNewString(ret), NULL);
+ xmlFree(ret);
+}
+
+/**
+ * xmlXPathCompVariableReference:
+ * @param ctxt the XPath Parser context
+ *
+ * Parse a VariableReference, evaluate it and push it on the stack.
+ *
+ * The variable bindings consist of a mapping from variable names
+ * to variable values. The value of a variable is an object, which can be
+ * of any of the types that are possible for the value of an expression,
+ * and may also be of additional types not specified here.
+ *
+ * Early evaluation is possible since:
+ * The variable bindings [...] used to evaluate a subexpression are
+ * always the same as those used to evaluate the containing expression.
+ *
+ * [36] VariableReference ::= '$' QName
+ */
+static void
+xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
+ xmlChar *name;
+ xmlChar *prefix;
+
+ SKIP_BLANKS;
+ if (CUR != '$') {
+ XP_ERROR(XPATH_VARIABLE_REF_ERROR);
+ }
+ NEXT;
+ name = xmlXPathParseQName(ctxt, &prefix); // OOM possible
+ if (!name) {
+ XP_ERROR(XPATH_VARIABLE_REF_ERROR);
+ }
+ ctxt->comp->last = -1;
+ PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, name, prefix);
+ SKIP_BLANKS;
+}
+
+/**
+ * xmlXPathIsNodeType:
+ * @param name a name string
+ *
+ * Is the name given a NodeType one.
+ *
+ * [38] NodeType ::= 'comment'
+ * | 'text'
+ * | 'processing-instruction'
+ * | 'node'
+ *
+ * Returns 1 if true 0 otherwise
+ */
+XMLPUBFUNEXPORT int
+xmlXPathIsNodeType(const xmlChar *name) {
+ if (name == NULL)
+ return(0);
+
+ if (xmlStrEqual(name, BAD_CAST "node"))
+ return(1);
+ if (xmlStrEqual(name, BAD_CAST "text"))
+ return(1);
+ if (xmlStrEqual(name, BAD_CAST "comment"))
+ return(1);
+ if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
+ return(1);
+ return(0);
+}
+
+/**
+ * xmlXPathCompFunctionCall:
+ * @param ctxt the XPath Parser context
+ *
+ * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
+ * [17] Argument ::= Expr
+ *
+ * Compile a function call, the evaluation of all arguments are
+ * pushed on the stack
+ */
+static void
+xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
+ xmlChar *name;
+ xmlChar *prefix; // name and prefix to be freed if abnormal return occurs
+ int nbargs = 0;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ name = xmlXPathParseQName(ctxt, &prefix);
+ if (name == NULL) {
+ XP_ERROR(XPATH_EXPR_ERROR);
+ }
+ SKIP_BLANKS;
+#ifdef DEBUG_EXPR
+ if (prefix == NULL)
+ xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
+ name);
+ else
+ xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
+ prefix, name);
+#endif
+
+ if (CUR != '(')
+ goto EXPR_Err;
+
+ NEXT;
+ SKIP_BLANKS;
+
+ ctxt->comp->last = -1;
+ if (CUR != ')') {
+ while (CUR != 0) {
+ int op1 = ctxt->comp->last;
+ ctxt->comp->last = -1;
+ xmlXPathCompileExpr(ctxt);
+
+ // CHECK_ERROR; replaced with:
+ if(ctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG)
+ goto Err;
+
+ PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
+ nbargs++;
+ if (CUR == ')')
+ break;
+ if (CUR != ',')
+ goto EXPR_Err;
+
+ NEXT;
+ SKIP_BLANKS;
+ }
+ }
+ // Note: now name and prefix are part of compiled expression
+ if ( PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1)
+ goto Err;
+ NEXT;
+ SKIP_BLANKS;
+ return;
+//-------
+EXPR_Err:
+ xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
+Err:
+ if(name)
+ xmlFree(name);
+ if(prefix)
+ xmlFree(prefix);
+}
+
+/**
+ * xmlXPathCompPrimaryExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [15] PrimaryExpr ::= VariableReference
+ * | '(' Expr ')'
+ * | Literal
+ * | Number
+ * | FunctionCall
+ *
+ * Compile a primary expression.
+ */
+static void
+xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt)
+{
+ SKIP_BLANKS;
+ if (CUR == '$')
+ {
+ xmlXPathCompVariableReference(ctxt);
+ }
+ else if (CUR == '(')
+ {
+ NEXT;
+ SKIP_BLANKS;
+ xmlXPathCompileExpr(ctxt);
+ CHECK_ERROR;
+ if (CUR != ')') {
+ XP_ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ SKIP_BLANKS;
+ }
+ else if (IS_DIGIT_CH(CUR) || (CUR == '.' && IS_DIGIT_CH(NXT(1))))
+ {
+ xmlXPathCompNumber(ctxt);
+ }
+ else if ((CUR == '\'') || (CUR == '"'))
+ {
+ xmlXPathCompLiteral(ctxt);
+ }
+ else
+ {
+ xmlXPathCompFunctionCall(ctxt);
+ }
+ SKIP_BLANKS;
+}
+
+/**
+ * xmlXPathCompFilterExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [20] FilterExpr ::= PrimaryExpr
+ * | FilterExpr Predicate
+ *
+ * Compile a filter expression.
+ * Square brackets are used to filter expressions in the same way that
+ * they are used in location paths. It is an error if the expression to
+ * be filtered does not evaluate to a node-set. The context node list
+ * used for evaluating the expression in square brackets is the node-set
+ * to be filtered listed in document order.
+ */
+
+static void
+xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt)
+{
+ xmlXPathCompPrimaryExpr(ctxt);
+ CHECK_ERROR;
+ SKIP_BLANKS;
+
+ while (CUR == '[') {
+ xmlXPathCompPredicate(ctxt, 1);
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathScanName:
+ * @param ctxt the XPath Parser context
+ *
+ * Trickery: parse an XML name but without consuming the input flow
+ * Needed to avoid insanity in the parser state.
+ *
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+ * CombiningChar | Extender
+ *
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * [6] Names ::= Name (S Name)*
+ *
+ * Returns the Name parsed or NULL
+ *
+ * OOM: possible --> always check OOM flag!
+ */
+
+static xmlChar*
+xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+ xmlChar buf[XML_MAX_NAMELEN];
+ int len = 0;
+
+ SKIP_BLANKS;
+ if (!IS_LETTER_CH(CUR) &&
+ (CUR != '_') &&
+ (CUR != ':'))
+ {
+ return(NULL);
+ }
+
+ while ((IS_LETTER_CH(NXT(len))) ||
+ (IS_DIGIT_CH(NXT(len))) ||
+ (NXT(len) == '.') ||
+ (NXT(len) == '-') ||
+ (NXT(len) == '_') ||
+ (NXT(len) == ':') ||
+ (IS_COMBINING_CH(NXT(len))) ||
+ (IS_EXTENDER_CH(NXT(len))))
+ {
+ buf[len] = NXT(len);
+ len++;
+ if (len >= XML_MAX_NAMELEN)
+ {
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlScanName: reached XML_MAX_NAMELEN limit\n"));
+ while (
+ (IS_LETTER_CH(NXT(len))) ||
+ (IS_DIGIT_CH(NXT(len))) ||
+ (NXT(len) == '.') ||
+ (NXT(len) == '-') ||
+ (NXT(len) == '_') ||
+ (NXT(len) == ':') ||
+ (IS_COMBINING_CH(NXT(len))) ||
+ (IS_EXTENDER_CH(NXT(len))))
+ {
+ len++;
+ }
+ break; // while
+ }
+ }
+ return(xmlStrndup(buf, len)); // may cause OOM
+}
+
+/**
+ * xmlXPathCompPathExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [19] PathExpr ::= LocationPath
+ * | FilterExpr
+ * | FilterExpr '/' RelativeLocationPath
+ * | FilterExpr '//' RelativeLocationPath
+ *
+ * Compile a path expression.
+ * The / operator and // operators combine an arbitrary expression
+ * and a relative location path. It is an error if the expression
+ * does not evaluate to a node-set.
+ * The / operator does composition in the same way as when / is
+ * used in a location path. As in location paths, // is short for
+ * /descendant-or-self::node()/.
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
+ int lc = 1; /* Should we branch to LocationPath ? */
+ xmlChar *name = NULL; /* we may have to preparse a name to find out */
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ SKIP_BLANKS;
+ if ((CUR == '$') || (CUR == '(') || (IS_DIGIT_CH(CUR)) ||
+ (CUR == '\'') || (CUR == '"') || (CUR == '.' && IS_DIGIT_CH(NXT(1))))
+ {
+ lc = 0;
+ } else if (CUR == '*') {
+ /* relative or absolute location path */
+ lc = 1;
+ } else if (CUR == '/') {
+ /* relative or absolute location path */
+ lc = 1;
+ } else if (CUR == '@') {
+ /* relative abbreviated attribute location path */
+ lc = 1;
+ } else if (CUR == '.') {
+ /* relative abbreviated attribute location path */
+ lc = 1;
+ } else {
+ /*
+ * Problem is finding if we have a name here whether it's:
+ * - a nodetype
+ * - a function call in which case it's followed by '('
+ * - an axis in which case it's followed by ':'
+ * - a element name
+ * We do an a priori analysis here rather than having to
+ * maintain parsed token content through the recursive function
+ * calls. This looks uglier but makes the code easier to
+ * read/write/debug.
+ */
+ SKIP_BLANKS;
+ name = xmlXPathScanName(ctxt);
+ // Note: if OOM happened then it is handled in the last ELSE branch:
+ if (name && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: Axis\n");
+#endif
+ lc = 1;
+ xmlFree(name);
+ } else if (name != NULL) {
+ int len =xmlStrlen(name);
+
+
+ while (NXT(len) != 0) {
+ if (NXT(len) == '/') {
+ /* element name */
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
+#endif
+ lc = 1;
+ break;
+ } else if (IS_BLANK_CH(NXT(len))) {
+ /* ignore blanks */
+ ;
+ } else if (NXT(len) == ':') {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
+#endif
+ lc = 1;
+ break;
+ } else if ((NXT(len) == '(')) {
+ /* Note Type or Function */
+ if (xmlXPathIsNodeType(name)) {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: Type search\n");
+#endif
+ lc = 1;
+ } else {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: function call\n");
+#endif
+ lc = 0;
+ }
+ break;
+ } else if ((NXT(len) == '[')) {
+ /* element name */
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
+#endif
+ lc = 1;
+ break;
+ } else if ((NXT(len) == '<') ||
+ (NXT(len) == '>') ||
+ (NXT(len) == '='))
+ {
+ lc = 1;
+ break;
+ } else {
+ lc = 1;
+ break;
+ }
+ len++;
+ }
+ if (NXT(len) == 0) {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
+#endif
+ /* element name */
+ lc = 1;
+ }
+ xmlFree(name);
+ } else {
+ // name is NULL -- OOM or bad XPath expression
+ /* make sure all cases are covered explicitly */
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ } else {
+ XP_ERROR(XPATH_EXPR_ERROR);
+ }
+ }
+ }
+
+ if (lc) {
+ if (CUR == '/') {
+ PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
+ } else {
+ PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
+ }
+ xmlXPathCompLocationPath(ctxt);
+
+ if(OOM_FLAG)
+ XP_ERROR(XPATH_MEMORY_ERROR)
+ } else {
+ xmlXPathCompFilterExpr(ctxt);
+ CHECK_ERROR;
+ if ((CUR == '/') && (NXT(1) == '/'))
+ {
+ SKIP(2);
+ SKIP_BLANKS;
+
+ PUSH_LONG_EXPR(
+ XPATH_OP_COLLECT,
+ AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE,
+ NODE_TYPE_NODE,
+ NULL,
+ NULL);
+
+ PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
+
+ xmlXPathCompRelativeLocationPath(ctxt);
+ } else if (CUR == '/') {
+ xmlXPathCompRelativeLocationPath(ctxt);
+ }
+ }
+ SKIP_BLANKS;
+}
+
+/**
+ * xmlXPathCompUnionExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [18] UnionExpr ::= PathExpr
+ * | UnionExpr '|' PathExpr
+ *
+ * Compile an union expression.
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ xmlXPathCompPathExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while (CUR == '|') {
+ int op1 = ctxt->comp->last;
+ PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
+
+ NEXT;
+ SKIP_BLANKS;
+ xmlXPathCompPathExpr(ctxt);
+
+ PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
+
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathCompUnaryExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [27] UnaryExpr ::= UnionExpr
+ * | '-' UnaryExpr
+ *
+ * Compile an unary expression.
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
+ int minus = 0;
+ int found = 0;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ SKIP_BLANKS;
+ while (CUR == '-') {
+ minus = 1 - minus;
+ found = 1;
+ NEXT;
+ SKIP_BLANKS;
+ }
+
+ xmlXPathCompUnionExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ if (found) {
+ if (minus)
+ PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
+ else
+ PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
+ }
+}
+
+/**
+ * xmlXPathCompMultiplicativeExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [26] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr MultiplyOperator UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ * [34] MultiplyOperator ::= '*'
+ *
+ * Compile an Additive expression.
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ xmlXPathCompUnaryExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while ((CUR == '*') ||
+ ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
+ ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
+ int op = -1;
+ int op1 = ctxt->comp->last;
+
+ if (CUR == '*') {
+ op = 0;
+ NEXT;
+ } else if (CUR == 'd') {
+ op = 1;
+ SKIP(3);
+ } else if (CUR == 'm') {
+ op = 2;
+ SKIP(3);
+ }
+ SKIP_BLANKS;
+ xmlXPathCompUnaryExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathCompAdditiveExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [25] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ *
+ * Compile an Additive expression.
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ xmlXPathCompMultiplicativeExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while ((CUR == '+') || (CUR == '-')) {
+ int plus;
+ int op1 = ctxt->comp->last;
+
+ if (CUR == '+')
+ plus = 1;
+ else
+ plus = 0;
+ NEXT;
+ SKIP_BLANKS;
+ xmlXPathCompMultiplicativeExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathCompRelationalExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [24] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ *
+ * A <= B > C is allowed ? Answer from James, yes with
+ * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
+ * which is basically what got implemented.
+ *
+ * Compile a Relational expression, then push the result
+ * on the stack
+ *
+ * OOM:
+ */
+
+static void
+xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt)
+{
+ xmlChar ch;
+ int* last;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ // XML ENGINE: Changes/optimization were applied here
+ xmlXPathCompAdditiveExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ last = &(ctxt->comp->last);
+ // NOTE: this loop allows expressions like A > B <= C < D .. etc.
+ // which will be evaluated in a way that may be unexpected:
+ // e.g. the example above is evaluated as
+ // ((A > B) <= C) < D with boolean results within parentheses
+ // converted to 1 or 0 (from 'true' and 'false')
+
+ for(;;)
+ {
+ ch = *ctxt->cur;
+ if (ch == '<' || ch == '>')
+ {
+ int op1 = *last;
+ int strict = (*(++(ctxt->cur)) == '=') ? 0 : 1; // evaluate 'strict' and NEXT
+ if (!strict)
+ ctxt->cur++; // NEXT again if '='
+
+ SKIP_BLANKS;
+ xmlXPathCompAdditiveExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(
+ XPATH_OP_CMP,
+ op1,
+ *last,
+ (ch == '<') & 1,
+ strict);
+ SKIP_BLANKS;
+ }
+ else
+ return;
+ } // for(;;)
+}
+
+/**
+ * xmlXPathCompEqualityExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [23] EqualityExpr ::= RelationalExpr
+ * | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ *
+ * A != B != C is allowed ? Answer from James, yes with
+ * (RelationalExpr = RelationalExpr) = RelationalExpr
+ * (RelationalExpr != RelationalExpr) != RelationalExpr
+ * which is basically what got implemented.
+ *
+ * Compile an Equality expression.
+ *
+ * OOM:
+ */
+static void
+xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt)
+{
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ xmlXPathCompRelationalExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
+ int eq;
+ int op1 = ctxt->comp->last;
+
+ eq = (CUR == '=') ? 1 : 0;
+ NEXT;
+ if (!eq) NEXT;
+ SKIP_BLANKS;
+ xmlXPathCompRelationalExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathCompAndExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [22] AndExpr ::= EqualityExpr
+ * | AndExpr 'and' EqualityExpr
+ *
+ * Compile an AND expression.
+ *
+ * OOM:
+ */
+static void
+xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ xmlXPathCompEqualityExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
+ int op1 = ctxt->comp->last;
+ SKIP(3);
+ SKIP_BLANKS;
+ xmlXPathCompEqualityExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
+ SKIP_BLANKS;
+ }
+}
+
+#define XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS
+/**
+ * xmlXPathCompileExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * [14] Expr ::= OrExpr
+ * [21] OrExpr ::= AndExpr
+ * | OrExpr 'or' AndExpr
+ *
+ * Parse and compile an expression
+ *
+ * OOM: possible --> check OOM flag ////
+ */
+static void
+xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt)
+{
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ xmlXPathCompAndExpr(ctxt);
+ CHECK_ERROR;
+ if(OOM_FLAG){
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ }
+ SKIP_BLANKS;
+ while ((CUR == 'o') && (NXT(1) == 'r')) {
+ int op1 = ctxt->comp->last;
+ SKIP(2);
+ SKIP_BLANKS;
+ xmlXPathCompAndExpr(ctxt);
+ CHECK_ERROR;
+ PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
+ op1 = ctxt->comp->nbStep;
+ SKIP_BLANKS;
+ }
+ if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
+ /* more ops could be optimized too */
+ PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
+ }
+
+#ifdef XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS
+ // Free unused memory in the tail of ctxt->comp->steps array
+ {
+ xmlXPathStepOp* tmp;
+ // DONE: OPTIMIZE: free the rest of preallocated step table
+ // This is a exeprimental code in XML Engine
+ // DONE: Fix xmlRealloc (Note: OOM should not happen since the size is smaller, but...)
+ tmp = (xmlXPathStepOp*)xmlRealloc(ctxt->comp->steps, sizeof(xmlXPathStepOp) * ctxt->comp->nbStep);
+ if(tmp)
+ {
+ ctxt->comp->steps = tmp;
+ ctxt->comp->maxStep = ctxt->comp->nbStep;
+ }
+ }
+#endif
+ //
+}
+
+/**
+ * xmlXPathCompPredicate:
+ * @param ctxt the XPath Parser context
+ * @param filter act as a filter
+ *
+ * [8] Predicate ::= '[' PredicateExpr ']'
+ * [9] PredicateExpr ::= Expr
+ *
+ * Compile a predicate expression
+ */
+static void
+xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
+ int op1 = ctxt->comp->last;
+
+ SKIP_BLANKS;
+ if (CUR != '[') {
+ XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
+ }
+ NEXT;
+ SKIP_BLANKS;
+
+ ctxt->comp->last = -1;
+ xmlXPathCompileExpr(ctxt);
+ CHECK_ERROR;
+
+ if (CUR != ']') {
+ XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
+ }
+
+ if (filter){
+ PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
+ }else{
+ PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
+ }
+ NEXT;
+ SKIP_BLANKS;
+}
+
+/**
+ * xmlXPathCompNodeTest:
+ * @param ctxt the XPath Parser context
+ * @param test pointer to a xmlXPathTestVal
+ * @param type pointer to a xmlXPathTypeVal
+ * @param prefix placeholder for a possible name prefix
+ *
+ * [7] NodeTest ::= NameTest
+ * | NodeType '(' ')'
+ * | 'processing-instruction' '(' Literal ')'
+ *
+ * [37] NameTest ::= '*'
+ * | NCName ':' '*'
+ * | QName
+ * [38] NodeType ::= 'comment'
+ * | 'text'
+ * | 'processing-instruction'
+ * | 'node'
+ *
+ * Returns the name found and updates test, type and prefix appropriately
+ */
+static xmlChar *
+xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
+ xmlXPathTypeVal *type, const xmlChar **prefix,
+ xmlChar *name)
+{
+ int blanks;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
+ STRANGE;
+ return(NULL);
+ }
+ *type = (xmlXPathTypeVal) 0;
+ *test = (xmlXPathTestVal) 0;
+ *prefix = NULL;
+ SKIP_BLANKS;
+
+ if ((name == NULL) && (CUR == '*')) {
+ /*
+ * All elements
+ */
+ NEXT;
+ *test = NODE_TEST_ALL;
+ return(NULL);
+ }
+
+ if (!name){
+ name = xmlXPathParseNCName(ctxt);
+ if (!name){
+ if(OOM_FLAG){
+ XP_ERROR0(XPATH_MEMORY_ERROR);
+ } else {
+ XP_ERROR0(XPATH_EXPR_ERROR);
+ }
+ }
+ }
+
+ blanks = IS_BLANK_CH(CUR);
+ SKIP_BLANKS;
+ if (CUR == '(')
+ {
+ NEXT;
+ /*
+ * NodeType or PI search
+ */
+ if (xmlStrEqual(name, BAD_CAST "comment"))
+ *type = NODE_TYPE_COMMENT;
+ else if (xmlStrEqual(name, BAD_CAST "node"))
+ *type = NODE_TYPE_NODE;
+ else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
+ *type = NODE_TYPE_PI;
+ else if (xmlStrEqual(name, BAD_CAST "text"))
+ *type = NODE_TYPE_TEXT;
+ else {
+ if (name != NULL)
+ xmlFree(name);
+ XP_ERROR0(XPATH_EXPR_ERROR);
+ }
+
+ *test = NODE_TEST_TYPE;
+
+ SKIP_BLANKS;
+ if (*type == NODE_TYPE_PI) {
+ /*
+ * Specific case: search a PI by name.
+ */
+ if (name)
+ xmlFree(name);
+ name = NULL;
+ if (CUR != ')') {
+ name = xmlXPathParseLiteral(ctxt);
+ CHECK_ERROR 0;
+ *test = NODE_TEST_PI;
+ SKIP_BLANKS;
+ }
+ }
+ if (CUR != ')') {
+ if (name != NULL)
+ xmlFree(name);
+ XP_ERROR0(XPATH_UNCLOSED_ERROR);
+ }
+ NEXT;
+ return(name);
+ }
+ *test = NODE_TEST_NAME;
+ if ((!blanks) && (CUR == ':'))
+ {
+ NEXT;
+
+ /*
+ * Since currently the parser context don't have a
+ * namespace list associated:
+ * The namespace name for this prefix can be computed
+ * only at evaluation time. The compilation is done
+ * outside of any context.
+ */
+#if 0
+ *prefix = xmlXPathNsLookup(ctxt->context, name);
+ if (name != NULL)
+ xmlFree(name);
+ if (*prefix == NULL) {
+ XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
+ }
+#else
+ *prefix = name;
+#endif
+
+ if (CUR == '*') {
+ /*
+ * All elements
+ */
+ NEXT;
+ *test = NODE_TEST_ALL;
+ return(NULL);
+ }
+
+ name = xmlXPathParseNCName(ctxt);
+ if (name == NULL) {
+ XP_ERROR0(XPATH_EXPR_ERROR);
+ }
+ }
+ return(name);
+}
+
+/**
+ * xmlXPathIsAxisName:
+ * @param name a preparsed name token
+ *
+ * [6] AxisName ::= 'ancestor'
+ * | 'ancestor-or-self'
+ * | 'attribute'
+ * | 'child'
+ * | 'descendant'
+ * | 'descendant-or-self'
+ * | 'following'
+ * | 'following-sibling'
+ * | 'namespace'
+ * | 'parent'
+ * | 'preceding'
+ * | 'preceding-sibling'
+ * | 'self'
+ *
+ * Returns the axis or 0
+ *
+ * OOM: never
+ */
+static xmlXPathAxisVal
+xmlXPathIsAxisName(const xmlChar *name)
+{
+ xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
+ switch (name[0]) {
+
+ case 'a':
+ if (xmlStrEqual(name, BAD_CAST "ancestor"))
+ ret = AXIS_ANCESTOR;
+ if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
+ ret = AXIS_ANCESTOR_OR_SELF;
+ if (xmlStrEqual(name, BAD_CAST "attribute"))
+ ret = AXIS_ATTRIBUTE;
+ break;
+ case 'c':
+ if (xmlStrEqual(name, BAD_CAST "child"))
+ ret = AXIS_CHILD;
+ break;
+ case 'd':
+ if (xmlStrEqual(name, BAD_CAST "descendant"))
+ ret = AXIS_DESCENDANT;
+ if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
+ ret = AXIS_DESCENDANT_OR_SELF;
+ break;
+ case 'f':
+ if (xmlStrEqual(name, BAD_CAST "following"))
+ ret = AXIS_FOLLOWING;
+ if (xmlStrEqual(name, BAD_CAST "following-sibling"))
+ ret = AXIS_FOLLOWING_SIBLING;
+ break;
+ case 'n':
+ if (xmlStrEqual(name, BAD_CAST "namespace"))
+ ret = AXIS_NAMESPACE;
+ break;
+ case 'p':
+ if (xmlStrEqual(name, BAD_CAST "parent"))
+ ret = AXIS_PARENT;
+ if (xmlStrEqual(name, BAD_CAST "preceding"))
+ ret = AXIS_PRECEDING;
+ if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
+ ret = AXIS_PRECEDING_SIBLING;
+ break;
+ case 's':
+ if (xmlStrEqual(name, BAD_CAST "self"))
+ ret = AXIS_SELF;
+ break;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathCompStep:
+ * @param ctxt the XPath Parser context
+ *
+ * [4] Step ::= AxisSpecifier NodeTest Predicate*
+ * | AbbreviatedStep
+ *
+ * [12] AbbreviatedStep ::= '.' | '..'
+ *
+ * [5] AxisSpecifier ::= AxisName '::'
+ * | AbbreviatedAxisSpecifier
+ *
+ * [13] AbbreviatedAxisSpecifier ::= '@'?
+ *
+ * Modified for XPtr range support as:
+ *
+ * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
+ * | AbbreviatedStep
+ * | 'range-to' '(' Expr ')' Predicate*
+ *
+ * Compile one step in a Location Path
+ * A location step of . is short for self::node(). This is
+ * particularly useful in conjunction with //. For example, the
+ * location path .//para is short for
+ * self::node()/descendant-or-self::node()/child::para
+ * and so will select all para descendant elements of the context
+ * node.
+ * Similarly, a location step of .. is short for parent::node().
+ * For example, ../title is short for parent::node()/child::title
+ * and so will select the title children of the parent of the context
+ * node.
+ *
+ * OOM: possible --> check OOM flag!
+ */
+static void
+xmlXPathCompStep(xmlXPathParserContextPtr ctxt)
+{
+
+#ifdef LIBXML_XPTR_ENABLED
+ int rangeto = 0;
+ int op2 = -1;
+#endif
+
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ SKIP_BLANKS;
+ if ((CUR == '.') && (NXT(1) == '.'))
+ {
+ SKIP(2);
+ SKIP_BLANKS;
+ PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
+ NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
+ }
+ else if (CUR == '.')
+ {
+ NEXT;
+ SKIP_BLANKS;
+ }
+ else
+ {
+ xmlChar *name = NULL;
+ const xmlChar *prefix = NULL;
+ xmlXPathTestVal test;
+ xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
+ xmlXPathTypeVal type;
+ int op1;
+
+ /*
+ * The modification needed for XPointer change to the production
+ */
+#ifdef LIBXML_XPTR_ENABLED
+ if (ctxt->xptr)
+ {
+ name = xmlXPathParseNCName(ctxt); // OOM is checked later
+ if (name && xmlStrEqual(name, BAD_CAST "range-to"))
+ {
+ op2 = ctxt->comp->last;
+ xmlFree(name);
+ SKIP_BLANKS;
+ if (CUR != '(') {
+ XP_ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ SKIP_BLANKS;
+
+ xmlXPathCompileExpr(ctxt);
+ /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
+ CHECK_ERROR;
+
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ XP_ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ rangeto = 1;
+ goto eval_predicates;
+ }
+ }
+#endif
+ if (CUR == '*') {
+ axis = AXIS_CHILD;
+ } else {
+ if (!name){
+ name = xmlXPathParseNCName(ctxt); // OOM is checked later
+ }
+ if (name) {
+ axis = xmlXPathIsAxisName(name);
+ if (axis != 0) {
+ SKIP_BLANKS;
+ if ((CUR == ':') && (NXT(1) == ':')) {
+ SKIP(2);
+ xmlFree(name);
+ name = NULL;
+ } else {
+ /* an element name can conflict with an axis one :-\ */
+ axis = AXIS_CHILD;
+ }
+ } else {
+ axis = AXIS_CHILD;
+ }
+ } else if (CUR == '@') {
+ NEXT;
+ axis = AXIS_ATTRIBUTE;
+ } else {
+ axis = AXIS_CHILD;
+ }
+ }
+
+ // instead of CHECK_ERROR:
+ if (ctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG){
+ if(name)
+ xmlFree(name);
+ if(OOM_FLAG)
+ XP_ERROR(XPATH_MEMORY_ERROR);
+ return;
+ }
+
+ name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
+ if (test == 0)
+ return;
+
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext,
+ "Basis : computing new set\n");
+
+ xmlGenericError(xmlGenericErrorContext, "Basis : ");
+ if (ctxt->value == NULL)
+ xmlGenericError(xmlGenericErrorContext, "no value\n");
+ else if (ctxt->value->nodesetval == NULL)
+ xmlGenericError(xmlGenericErrorContext, "Empty\n");
+ else
+ xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
+#endif
+
+#ifdef LIBXML_XPTR_ENABLED
+eval_predicates:
+#endif
+ op1 = ctxt->comp->last;
+ ctxt->comp->last = -1;
+
+ SKIP_BLANKS;
+ while (CUR == '[') {
+ xmlXPathCompPredicate(ctxt, 0);
+ }
+
+#ifdef LIBXML_XPTR_ENABLED
+ if (rangeto) {
+ PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
+ } else
+#endif
+ {
+ PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
+ test, type, (void *)prefix, (void *)name);
+ }
+ }
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "Step : ");
+ if (ctxt->value == NULL)
+ xmlGenericError(xmlGenericErrorContext, "no value\n");
+ else if (ctxt->value->nodesetval == NULL)
+ xmlGenericError(xmlGenericErrorContext, "Empty\n");
+ else
+ xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
+ ctxt->value->nodesetval);
+#endif
+}
+
+/**
+ * xmlXPathCompRelativeLocationPath:
+ * @param ctxt the XPath Parser context
+ *
+ * [3] RelativeLocationPath ::= Step
+ * | RelativeLocationPath '/' Step
+ * | AbbreviatedRelativeLocationPath
+ * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
+ *
+ * Compile a relative location path.
+ */
+static void
+xmlXPathCompRelativeLocationPath
+(xmlXPathParserContextPtr ctxt) {
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ SKIP_BLANKS;
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ SKIP(2);
+ SKIP_BLANKS;
+ PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
+ } else if (CUR == '/') {
+ NEXT;
+ SKIP_BLANKS;
+ }
+ xmlXPathCompStep(ctxt);
+ if(OOM_FLAG) return;
+ SKIP_BLANKS;
+ while (CUR == '/') {
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ SKIP(2);
+ SKIP_BLANKS;
+ PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
+ xmlXPathCompStep(ctxt);
+ if(OOM_FLAG) return;
+ } else if (CUR == '/') {
+ NEXT;
+ SKIP_BLANKS;
+ xmlXPathCompStep(ctxt);
+ }
+ SKIP_BLANKS;
+ }
+}
+
+/**
+ * xmlXPathCompLocationPath:
+ * @param ctxt the XPath Parser context
+ *
+ * [1] LocationPath ::= RelativeLocationPath
+ * | AbsoluteLocationPath
+ * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
+ * | AbbreviatedAbsoluteLocationPath
+ * [10] AbbreviatedAbsoluteLocationPath ::=
+ * '//' RelativeLocationPath
+ *
+ * Compile a location path
+ *
+ * // is short for /descendant-or-self::node()/. For example,
+ * //para is short for /descendant-or-self::node()/child::para and
+ * so will select any para element in the document (even a para element
+ * that is a document element will be selected by //para since the
+ * document element node is a child of the root node); div//para is
+ * short for div/descendant-or-self::node()/child::para and so will
+ * select all para descendants of div children.
+ *
+ * OOM: possible -->
+ */
+static void
+xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt)
+{
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ SKIP_BLANKS;
+ if (CUR != '/') {
+ xmlXPathCompRelativeLocationPath(ctxt);
+ } else {
+ while (CUR == '/') {
+ if ((CUR == '/') && (NXT(1) == '/'))
+ {
+ SKIP(2);
+ SKIP_BLANKS;
+ PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
+ xmlXPathCompRelativeLocationPath(ctxt);
+ if(OOM_FLAG) return;
+ }
+ else if (CUR == '/')
+ {
+ NEXT;
+ SKIP_BLANKS;
+ if ((CUR != 0 ) &&
+ ((IS_LETTER_CH(CUR)) || (CUR == '_') || (CUR == '.') ||
+ (CUR == '@') || (CUR == '*')))
+ {
+ xmlXPathCompRelativeLocationPath(ctxt);
+ if(OOM_FLAG) return;
+ }
+ }
+ //
+ CHECK_ERROR;
+ } // end while
+ } // end else
+}
+
+/************************************************************************
+ * *
+ * XPath precompiled expression evaluation *
+ * *
+ ************************************************************************/
+
+static int
+xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
+
+/**
+ * xmlXPathNodeCollectAndTest:
+ * @param ctxt the XPath Parser context
+ * @param op the XPath precompiled step operation
+ * @param first pointer to the first element in document order
+ * @param last pointer to the last element in document order
+ *
+ * This is the function implementing a step: based on the current list
+ * of nodes, it builds up a new list, looking at all nodes under that
+ * axis and selecting them. It also does the predicate filtering
+ *
+ * Pushes the new NodeSet resulting from the search.
+ *
+ * Returns the number of nodes traversed or -1 when OOM
+ */
+static int
+xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op,
+ xmlNodePtr * first, xmlNodePtr * last)
+{
+ xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
+ xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
+ xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
+
+ const xmlChar* prefix = (xmlChar*) op->value4;
+ const xmlChar* name = (xmlChar*) op->value5;
+ const xmlChar* URI = NULL;
+
+#ifdef DEBUG_STEP
+ int n = 0;
+#endif
+
+ int i, t = 0;
+ xmlNodeSetPtr ret, list = NULL;
+ xmlXPathTraversalFunction next = NULL;
+ void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
+ xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr);
+ xmlNodePtr cur = NULL;
+ xmlXPathObjectPtr obj;
+ xmlNodeSetPtr nodelist;
+ xmlNodePtr tmp;
+
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+ CHECK_TYPE0(XPATH_NODESET);
+ obj = valuePop(ctxt);
+ addNode = xmlXPathNodeSetAdd;
+ mergeNodeSet = xmlXPathNodeSetMerge;
+ if (prefix) {
+ URI = xmlXPathNsLookup(ctxt->context, prefix);
+ if (URI == NULL)
+ XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
+ }
+
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "new step : ");
+#endif
+
+ switch (axis)
+ {
+
+ case AXIS_ANCESTOR: {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextAncestor;
+ break;
+ }
+
+ case AXIS_ANCESTOR_OR_SELF: {
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'ancestors-or-self' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextAncestorOrSelf;
+ break;
+ }
+
+ case AXIS_ATTRIBUTE:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
+#endif
+ first = NULL;
+ last = NULL;
+ next = xmlXPathNextAttribute;
+ mergeNodeSet = xmlXPathNodeSetMergeUnique;
+ break;
+ }
+
+ case AXIS_CHILD:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextChild;
+ mergeNodeSet = xmlXPathNodeSetMergeUnique;
+ break;
+ }
+
+ case AXIS_DESCENDANT:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextDescendant;
+ break;
+ }
+
+ case AXIS_DESCENDANT_OR_SELF:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'descendant-or-self' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextDescendantOrSelf;
+ break;
+ }
+
+ case AXIS_FOLLOWING:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextFollowing;
+ break;
+ }
+
+ case AXIS_FOLLOWING_SIBLING:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'following-siblings' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextFollowingSibling;
+ break;
+ }
+
+ case AXIS_NAMESPACE:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
+#endif
+ first = NULL;
+ last = NULL;
+ next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
+ mergeNodeSet = xmlXPathNodeSetMergeUnique;
+ break;
+ }
+
+ case AXIS_PARENT:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextParent;
+ break;
+ }
+
+ case AXIS_PRECEDING:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextPrecedingInternal;
+ break;
+ }
+
+ case AXIS_PRECEDING_SIBLING:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'preceding-sibling' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextPrecedingSibling;
+ break;
+ }
+
+ case AXIS_SELF:{
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
+#endif
+ first = NULL;
+ last = NULL;
+ next = xmlXPathNextSelf;
+ mergeNodeSet = xmlXPathNodeSetMergeUnique;
+ break;
+ }
+
+ } // switch(axis)
+
+ if (next == NULL)
+ return(0);
+
+ nodelist = obj->nodesetval;
+ if (!nodelist) {
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
+ return(0);
+ }
+ addNode = xmlXPathNodeSetAddUnique;
+ ret = NULL;
+
+#ifdef DEBUG_STEP
+{
+ xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr);
+ switch (test) {
+ case NODE_TEST_NONE:
+ xmlGenericError(xmlGenericErrorContext, " searching for none !!!\n");
+ break;
+ case NODE_TEST_TYPE:
+ xmlGenericError(xmlGenericErrorContext, " searching for type %d\n", type);
+ break;
+ case NODE_TEST_PI:
+ xmlGenericError(xmlGenericErrorContext, " searching for PI !!!\n");
+ break;
+ case NODE_TEST_ALL:
+ xmlGenericError(xmlGenericErrorContext, " searching for *\n");
+ break;
+ case NODE_TEST_NS:
+ xmlGenericError(xmlGenericErrorContext, " searching for namespace %s\n", prefix);
+ break;
+ case NODE_TEST_NAME:
+ xmlGenericError(xmlGenericErrorContext, " searching for name %s\n", name);
+ if (prefix != NULL)
+ xmlGenericError(xmlGenericErrorContext, " with namespace %s\n", prefix);
+ break;
+ }
+ xmlGenericError(xmlGenericErrorContext, "Testing : ");
+}
+#endif
+ /*
+ * 2.3 Node Tests
+ * - For the attribute axis, the principal node type is attribute.
+ * - For the namespace axis, the principal node type is namespace.
+ * - For other axes, the principal node type is element.
+ *
+ * A node test * is true for any node of the
+ * principal node type. For example, child::* will
+ * select all element children of the context node
+ */
+ tmp = ctxt->context->node;
+
+ for (i = 0; i < nodelist->nodeNr; i++)
+ {
+ ctxt->context->node = nodelist->nodeTab[i];
+ cur = NULL;
+ list = xmlXPathNodeSetCreate(NULL);
+ if(OOM_FLAG)
+ { // Note: cannot use: goto OOM_obj_list_ret with list!=NULL
+ list = NULL;
+ goto OOM_obj_list_ret;
+ }
+ //
+ do {
+ cur = next(ctxt, cur);
+ if (cur == NULL)
+ break;
+ if (first && (*first == cur))
+ break;
+ if (((t % 256) == 0) &&
+ first && *first &&
+ (xmlXPathCmpNodes(*first, cur) >= 0))
+ break;
+ if (last && (*last == cur))
+ break;
+ if (((t % 256) == 0) &&
+ last && *last &&
+ (xmlXPathCmpNodes(cur, *last) >= 0))
+ break;
+
+ t++;
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
+#endif
+ switch (test)
+ {
+ case NODE_TEST_NONE:{
+ ctxt->context->node = tmp;
+ STRANGE return(t);
+ }
+ case NODE_TEST_TYPE:{
+
+ if ((cur->type == type) ||
+ ((type == NODE_TYPE_NODE) &&
+ ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE) ||
+ (cur->type == XML_ELEMENT_NODE) ||
+ (cur->type == XML_NAMESPACE_DECL) ||
+ (cur->type == XML_ATTRIBUTE_NODE) ||
+ (cur->type == XML_PI_NODE) ||
+ (cur->type == XML_COMMENT_NODE) ||
+ (cur->type == XML_CDATA_SECTION_NODE) ||
+ (cur->type == XML_TEXT_NODE))) ||
+ ((type == NODE_TYPE_TEXT) &&
+ (cur->type == XML_CDATA_SECTION_NODE)))
+ {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ }
+ break;
+ }
+
+ case NODE_TEST_PI: {
+ if (cur->type == XML_PI_NODE)
+ {
+ if ((name != NULL) && (!xmlStrEqual(name, cur->name)))
+ break;
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ }
+ break;
+ }
+
+ case NODE_TEST_ALL: {
+ if (axis == AXIS_ATTRIBUTE) {
+ if (cur->type == XML_ATTRIBUTE_NODE) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ }
+ } else
+ if (axis == AXIS_NAMESPACE) {
+ if (cur->type == XML_NAMESPACE_DECL) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur);
+ }
+ } else {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (prefix == NULL) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ if(OOM_FLAG)
+ {
+ goto OOM_obj_list_ret;
+ }
+ } else
+ if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ }
+ }
+ }
+ break;
+ }
+
+ case NODE_TEST_NS: {
+ TODO;
+ break;
+ }
+ case NODE_TEST_NAME:{
+ switch (cur->type) {
+ case XML_ELEMENT_NODE: {
+ if (xmlStrEqual(name, cur->name)) {
+ if (prefix == NULL) {
+ //if (cur->ns == NULL) { // should we add here || cur->ns->pref == NULL ?
+ if (!cur->ns || !cur->ns->href || !*cur->ns->href) { // XML ENGINE: modified code
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ if(OOM_FLAG)
+ {
+ goto OOM_obj_list;
+ }
+ }
+ } else {
+ if (cur->ns && (xmlStrEqual(URI, cur->ns->href))) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, cur);
+ }
+ }
+ }
+ break;
+ }
+
+ case XML_ATTRIBUTE_NODE:{
+ xmlAttrPtr attr = (xmlAttrPtr) cur;
+
+ if (xmlStrEqual(name, attr->name)) {
+ if (prefix == NULL) {
+ if ((attr->ns == NULL) || (attr->ns->prefix == NULL)) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, (xmlNodePtr) attr);
+ if(OOM_FLAG)
+ {
+ goto OOM_obj_list;
+ }
+ }
+ } else {
+ if ((attr->ns != NULL) && (xmlStrEqual(URI, attr->ns->href))) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ addNode(list, (xmlNodePtr) attr);
+ }
+ }
+ }
+ break;
+ }
+ case XML_NAMESPACE_DECL:{
+ if (cur->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) cur;
+
+ if ((ns->prefix != NULL) && (name != NULL)
+ && (xmlStrEqual(ns->prefix, name)))
+ {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ break;
+ }
+ } // switch(test)
+ } while (cur);
+
+ /*
+ * If there is some predicate filtering do it now
+ */
+ if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0))
+ {
+ xmlXPathObjectPtr obj2;
+
+ valuePush(ctxt, xmlXPathWrapNodeSet(list));
+ if(OOM_FLAG)
+ {
+ goto OOM_obj_list_ret;
+ }
+ xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
+ CHECK_TYPE0(XPATH_NODESET);
+ obj2 = valuePop(ctxt);
+ list = obj2->nodesetval;
+ obj2->nodesetval = NULL;
+ xmlXPathFreeObject(obj2);
+ }
+ if (ret == NULL) {
+ ret = list;
+ } else {
+ ret = mergeNodeSet(ret, list); // either xmlXPathNodeSetMergeUnique or xmlXPathNodeSetMerge called
+ xmlXPathFreeNodeSet(list);
+ }
+ } // for (i = 0; i < nodelist->nodeNr; i++)
+
+ ctxt->context->node = tmp;
+
+#ifdef DEBUG_STEP
+ xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", t, n);
+#endif
+
+ valuePush(ctxt, xmlXPathWrapNodeSet(ret));
+ if(OOM_FLAG)
+ {
+ goto OOM_obj_list;
+ }
+ if ((obj->boolval) && (obj->user != NULL)) {
+ ctxt->value->boolval = 1;
+ ctxt->value->user = obj->user;
+ obj->user = NULL;
+ obj->boolval = 0;
+ }
+ xmlXPathFreeObject(obj);
+ return(t);
+
+//-------- OOM cleanup
+OOM_obj_list_ret:
+ if(ret)
+ xmlXPathFreeNodeSet(ret);
+OOM_obj_list:
+ if(list)
+ xmlXPathFreeNodeSet(list);
+ if(obj)
+ xmlXPathFreeObject(obj);
+ return(-1);
+}
+
+/**
+ * xmlXPathNodeCollectAndTestNth:
+ * @param ctxt the XPath Parser context
+ * @param op the XPath precompiled step operation
+ * @param indx the index to collect
+ * @param first pointer to the first element in document order
+ * @param last pointer to the last element in document order
+ *
+ * This is the function implementing a step: based on the current list
+ * of nodes, it builds up a new list, looking at all nodes under that
+ * axis and selecting them. It also does the predicate filtering
+ *
+ * Pushes the new NodeSet resulting from the search.
+ * Returns the number of node traversed or -1 when OOM
+ */
+static int
+xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op, int indx,
+ xmlNodePtr * first, xmlNodePtr * last)
+{
+ xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
+ xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
+ xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
+ const xmlChar *prefix = (xmlChar*)op->value4;
+ const xmlChar *name = (xmlChar*)op->value5;
+ const xmlChar *URI = NULL;
+ int n = 0, t = 0;
+
+ int i;
+ xmlNodeSetPtr list;
+ xmlXPathTraversalFunction next = NULL;
+ void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
+ xmlNodePtr cur = NULL;
+ xmlXPathObjectPtr obj;
+ xmlNodeSetPtr nodelist;
+ xmlNodePtr tmp;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ CHECK_TYPE0(XPATH_NODESET);
+ obj = valuePop(ctxt);
+ addNode = xmlXPathNodeSetAdd;
+ if (prefix != NULL) {
+ URI = xmlXPathNsLookup(ctxt->context, prefix);
+ if (URI == NULL)
+ XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
+ }
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "new step : ");
+ if (first != NULL) {
+ if (*first != NULL)
+ xmlGenericError(xmlGenericErrorContext, "first = %s ",
+ (*first)->name);
+ else
+ xmlGenericError(xmlGenericErrorContext, "first = NULL ");
+ }
+ if (last != NULL) {
+ if (*last != NULL)
+ xmlGenericError(xmlGenericErrorContext, "last = %s ",
+ (*last)->name);
+ else
+ xmlGenericError(xmlGenericErrorContext, "last = NULL ");
+ }
+#endif
+ switch (axis) {
+ case AXIS_ANCESTOR:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextAncestor;
+ break;
+ case AXIS_ANCESTOR_OR_SELF:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ "axis 'ancestors-or-self' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextAncestorOrSelf;
+ break;
+ case AXIS_ATTRIBUTE:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
+#endif
+ first = NULL;
+ last = NULL;
+ next = xmlXPathNextAttribute;
+ break;
+ case AXIS_CHILD:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextChild;
+ break;
+ case AXIS_DESCENDANT:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextDescendant;
+ break;
+ case AXIS_DESCENDANT_OR_SELF:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ "axis 'descendant-or-self' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextDescendantOrSelf;
+ break;
+ case AXIS_FOLLOWING:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextFollowing;
+ break;
+ case AXIS_FOLLOWING_SIBLING:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ "axis 'following-siblings' ");
+#endif
+ last = NULL;
+ next = xmlXPathNextFollowingSibling;
+ break;
+ case AXIS_NAMESPACE:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
+#endif
+ last = NULL;
+ first = NULL;
+ next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
+ break;
+ case AXIS_PARENT:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextParent;
+ break;
+ case AXIS_PRECEDING:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextPrecedingInternal;
+ break;
+ case AXIS_PRECEDING_SIBLING:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ "axis 'preceding-sibling' ");
+#endif
+ first = NULL;
+ next = xmlXPathNextPrecedingSibling;
+ break;
+ case AXIS_SELF:
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
+#endif
+ first = NULL;
+ last = NULL;
+ next = xmlXPathNextSelf;
+ break;
+ }
+ if (next == NULL)
+ return(0);
+
+ nodelist = obj->nodesetval;
+ if (nodelist == NULL) {
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
+ return(0);
+ }
+ addNode = xmlXPathNodeSetAddUnique;
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ " context contains %d nodes\n", nodelist->nodeNr);
+ switch (test) {
+ case NODE_TEST_NONE:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for none !!!\n");
+ break;
+ case NODE_TEST_TYPE:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for type %d\n", type);
+ break;
+ case NODE_TEST_PI:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for PI !!!\n");
+ break;
+ case NODE_TEST_ALL:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for *\n");
+ break;
+ case NODE_TEST_NS:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for namespace %s\n",
+ prefix);
+ break;
+ case NODE_TEST_NAME:
+ xmlGenericError(xmlGenericErrorContext,
+ " searching for name %s\n", name);
+ if (prefix != NULL)
+ xmlGenericError(xmlGenericErrorContext,
+ " with namespace %s\n", prefix);
+ break;
+ }
+ xmlGenericError(xmlGenericErrorContext, "Testing : ");
+#endif
+ /*
+ * 2.3 Node Tests
+ * - For the attribute axis, the principal node type is attribute.
+ * - For the namespace axis, the principal node type is namespace.
+ * - For other axes, the principal node type is element.
+ *
+ * A node test * is true for any node of the
+ * principal node type. For example, child::* will
+ * select all element children of the context node
+ */
+ tmp = ctxt->context->node;
+ list = xmlXPathNodeSetCreate(NULL);
+ if(!list)
+ goto OOM_obj;
+
+ for (i = 0; i < nodelist->nodeNr; i++) {
+ ctxt->context->node = nodelist->nodeTab[i];
+
+ cur = NULL;
+ n = 0;
+ do {
+ cur = next(ctxt, cur);
+ if (cur == NULL)
+ break;
+ if ((first != NULL) && (*first == cur))
+ break;
+ if (((t % 256) == 0) &&
+ (first != NULL) && (*first != NULL) &&
+ (xmlXPathCmpNodes(*first, cur) >= 0))
+ break;
+ if ((last != NULL) && (*last == cur))
+ break;
+ if (((t % 256) == 0) &&
+ (last != NULL) && (*last != NULL) &&
+ (xmlXPathCmpNodes(cur, *last) >= 0))
+ break;
+ t++;
+ switch (test) {
+ case NODE_TEST_NONE:
+ ctxt->context->node = tmp;
+ STRANGE return(0);
+ case NODE_TEST_TYPE:
+ if ((cur->type == type) ||
+ ((type == NODE_TYPE_NODE) &&
+ ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE) ||
+ (cur->type == XML_ELEMENT_NODE) ||
+ (cur->type == XML_PI_NODE) ||
+ (cur->type == XML_COMMENT_NODE) ||
+ (cur->type == XML_CDATA_SECTION_NODE) ||
+ (cur->type == XML_TEXT_NODE))) ||
+ ((type == NODE_TYPE_TEXT) &&
+ (cur->type == XML_CDATA_SECTION_NODE))) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ break;
+ case NODE_TEST_PI:
+ if (cur->type == XML_PI_NODE) {
+ if ((name != NULL) &&
+ (!xmlStrEqual(name, cur->name)))
+ break;
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ break;
+ case NODE_TEST_ALL:
+ if (axis == AXIS_ATTRIBUTE) {
+ if (cur->type == XML_ATTRIBUTE_NODE) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ } else if (axis == AXIS_NAMESPACE) {
+ if (cur->type == XML_NAMESPACE_DECL) {
+ n++;
+ if (n == indx)
+ xmlXPathNodeSetAddNs(list, ctxt->context->node,
+ (xmlNsPtr) cur);
+ }
+ } else {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (prefix == NULL) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ } else if ((cur->ns != NULL) &&
+ (xmlStrEqual(URI, cur->ns->href))) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ }
+ }
+ break;
+ case NODE_TEST_NS:{
+ TODO;
+ break;
+ }
+ case NODE_TEST_NAME:
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ if (xmlStrEqual(name, cur->name)) {
+ if (prefix == NULL) {
+ if (cur->ns == NULL) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ if(OOM_FLAG)
+ goto OOM_obj_list;
+ }
+ } else {
+ if ((cur->ns != NULL) &&
+ (xmlStrEqual(URI,
+ cur->ns->href))) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ }
+ }
+ break;
+ case XML_ATTRIBUTE_NODE:{
+ xmlAttrPtr attr = (xmlAttrPtr) cur;
+
+ if (xmlStrEqual(name, attr->name)) {
+ if (prefix == NULL) {
+ if ((attr->ns == NULL) ||
+ (attr->ns->prefix == NULL)) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ } else {
+ if ((attr->ns != NULL) &&
+ (xmlStrEqual(URI,
+ attr->ns->
+ href))) {
+ n++;
+ if (n == indx)
+ addNode(list, cur);
+ }
+ }
+ }
+ break;
+ }
+ case XML_NAMESPACE_DECL:
+ if (cur->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) cur;
+
+ if ((ns->prefix != NULL) && (name != NULL)
+ && (xmlStrEqual(ns->prefix, name))) {
+ n++;
+ if (n == indx)
+ xmlXPathNodeSetAddNs(list,
+ ctxt->context->node, (xmlNsPtr) cur);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ } while (n < indx);
+ }
+ ctxt->context->node = tmp;
+#ifdef DEBUG_STEP_NTH
+ xmlGenericError(xmlGenericErrorContext,
+ "\nExamined %d nodes, found %d nodes at that step\n",
+ t, list->nodeNr);
+#endif
+ valuePush(ctxt, xmlXPathWrapNodeSet(list));
+ if(OOM_FLAG)
+ goto OOM_obj_list;
+
+ if ((obj->boolval) && (obj->user != NULL)) {
+ ctxt->value->boolval = 1;
+ ctxt->value->user = obj->user;
+ obj->user = NULL;
+ obj->boolval = 0;
+ }
+ xmlXPathFreeObject(obj);
+ return(t);
+
+//---- OOM
+OOM_obj_list:
+ xmlXPathFreeNodeSet(list);
+OOM_obj:
+ xmlXPathFreeObject(obj);
+ return(-1);
+}
+
+/**
+ * xmlXPathCompOpEvalFirst:
+ * @param ctxt the XPath parser context with the compiled expression
+ * @param op an XPath compiled operation
+ * @param first the first elem found so far
+ *
+ * Evaluate the Precompiled XPath operation searching only the first
+ * element in document order
+ *
+ * Returns the number of examined objects.
+ */
+static int
+xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op, xmlNodePtr * first)
+{
+ int total = 0, cur;
+ xmlXPathCompExprPtr comp;
+ xmlXPathObjectPtr arg1, arg2;
+
+ CHECK_ERROR0;
+ comp = ctxt->comp;
+ switch (op->op) {
+ case XPATH_OP_END:
+ return (0);
+ case XPATH_OP_UNION:
+ total =
+ xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
+ first);
+ CHECK_ERROR0;
+ if ((ctxt->value != NULL)
+ && (ctxt->value->type == XPATH_NODESET)
+ && (ctxt->value->nodesetval != NULL)
+ && (ctxt->value->nodesetval->nodeNr >= 1)) {
+ /*
+ * limit tree traversing to first node in the result
+ */
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ *first = ctxt->value->nodesetval->nodeTab[0];
+ }
+ cur =
+ xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
+ first);
+ CHECK_ERROR0;
+ CHECK_TYPE0(XPATH_NODESET);
+ arg2 = valuePop(ctxt);
+
+ CHECK_TYPE0(XPATH_NODESET);
+ arg1 = valuePop(ctxt);
+
+ arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
+ arg2->nodesetval);
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ /* optimizer */
+ if (total > cur)
+ xmlXPathCompSwap(op);
+ return (total + cur);
+ case XPATH_OP_ROOT:
+ xmlXPathRoot(ctxt);
+ return (0);
+ case XPATH_OP_NODE:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ return (total);
+ case XPATH_OP_RESET:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+ ctxt->context->node = NULL;
+ return (total);
+ case XPATH_OP_COLLECT:{
+ if (op->ch1 == -1)
+ return (total);
+
+ total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ /*
+ * Optimization for [n] selection where n is a number
+ */
+ if ((op->ch2 != -1) &&
+ (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
+ (comp->steps[op->ch2].ch1 == -1) &&
+ (comp->steps[op->ch2].ch2 != -1) &&
+ (comp->steps[comp->steps[op->ch2].ch2].op ==
+ XPATH_OP_VALUE)) {
+ xmlXPathObjectPtr val;
+
+ val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
+ if ((val != NULL) && (val->type == XPATH_NUMBER)) {
+ int indx = (int) val->floatval;
+
+ if (val->floatval == (float) indx) {
+ xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
+ first, NULL);
+ return (total);
+ }
+ }
+ }
+ total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
+ return (total);
+ }
+ case XPATH_OP_VALUE:
+ valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ return (0);
+
+ case XPATH_OP_SORT:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], first);
+ CHECK_ERROR0;
+ if ((ctxt->value != NULL)
+ && (ctxt->value->type == XPATH_NODESET)
+ && (ctxt->value->nodesetval != NULL))
+ {
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ }
+ return (total);
+
+ default:
+ return (xmlXPathCompOpEval(ctxt, op));
+ }
+}
+
+/**
+ * xmlXPathCompOpEvalLast:
+ * @param ctxt the XPath parser context with the compiled expression
+ * @param op an XPath compiled operation
+ * @param last the last elem found so far
+ *
+ * Evaluate the Precompiled XPath operation searching only the last
+ * element in document order
+ *
+ * Returns the number of nodes traversed
+ */
+static int
+xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
+ xmlNodePtr * last)
+{
+ int total = 0, cur;
+ xmlXPathCompExprPtr comp;
+ xmlXPathObjectPtr arg1, arg2;
+ xmlNodePtr bak;
+ xmlDocPtr bakd;
+ int pp;
+ int cs;
+
+ CHECK_ERROR0;
+ comp = ctxt->comp;
+ switch (op->op) {
+ case XPATH_OP_END:
+ return (0);
+ case XPATH_OP_UNION:
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+
+ total = xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
+
+ CHECK_ERROR0;
+ if ((ctxt->value != NULL)
+ && (ctxt->value->type == XPATH_NODESET)
+ && (ctxt->value->nodesetval != NULL)
+ && (ctxt->value->nodesetval->nodeNr >= 1)) {
+ /*
+ * limit tree traversing to first node in the result
+ */
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ *last =
+ ctxt->value->nodesetval->nodeTab[ctxt->value->
+ nodesetval->nodeNr -
+ 1];
+ }
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ cur =
+ xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
+ CHECK_ERROR0;
+ if ((ctxt->value != NULL)
+ && (ctxt->value->type == XPATH_NODESET)
+ && (ctxt->value->nodesetval != NULL)
+ && (ctxt->value->nodesetval->nodeNr >= 1)) {
+ }
+ CHECK_TYPE0(XPATH_NODESET);
+ arg2 = valuePop(ctxt);
+
+ CHECK_TYPE0(XPATH_NODESET);
+ arg1 = valuePop(ctxt);
+
+ arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
+ arg2->nodesetval);
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ /* optimizer */
+ if (total > cur)
+ xmlXPathCompSwap(op);
+ return (total + cur);
+ case XPATH_OP_ROOT:
+ xmlXPathRoot(ctxt);
+ return (0);
+ case XPATH_OP_NODE:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ return (total);
+ case XPATH_OP_RESET:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+ ctxt->context->node = NULL;
+ return (total);
+ case XPATH_OP_COLLECT:{
+ if (op->ch1 == -1)
+ return (0);
+
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ /*
+ * Optimization for [n] selection where n is a number
+ */
+ if ((op->ch2 != -1) &&
+ (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
+ (comp->steps[op->ch2].ch1 == -1) &&
+ (comp->steps[op->ch2].ch2 != -1) &&
+ (comp->steps[comp->steps[op->ch2].ch2].op ==
+ XPATH_OP_VALUE)) {
+ xmlXPathObjectPtr val;
+
+ val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
+ if ((val != NULL) && (val->type == XPATH_NUMBER)) {
+ int indx = (int) val->floatval;
+
+ if (val->floatval == (float) indx) {
+ total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, last);
+ return (total);
+ }
+ }
+ }
+ total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
+ return (total);
+ }
+ case XPATH_OP_VALUE:
+ valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ return (0);
+
+ case XPATH_OP_SORT:
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
+ CHECK_ERROR0;
+ if ((ctxt->value != NULL)
+ && (ctxt->value->type == XPATH_NODESET)
+ && (ctxt->value->nodesetval != NULL))
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ {
+ return (total);
+ }
+
+ default:
+ return (xmlXPathCompOpEval(ctxt, op));
+ }
+}
+
+/**
+ * xmlXPathCompOpEval:
+ * @param ctxt the XPath parser context with the compiled expression
+ * @param op an XPath compiled operation
+ *
+ * Evaluate the Precompiled XPath operation
+ * Returns the number of nodes traversed or -1 when OOM
+ */
+static int
+xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
+{
+ int total = 0;
+ int equal, ret;
+ xmlXPathCompExprPtr comp;
+ xmlXPathObjectPtr arg1, arg2;
+ xmlNodePtr bak;
+ xmlDocPtr bakd;
+ int pp;
+ int cs;
+ LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt)
+
+ CHECK_ERROR0;
+
+ comp = ctxt->comp;
+
+ switch (op->op)
+ {
+ case XPATH_OP_END: return (0);
+ case XPATH_OP_AND: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 1);
+//-- END NEW CODE
+
+ xmlXPathBooleanFunction(ctxt, 1);
+ if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
+ return (total);
+ arg2 = valuePop(ctxt);
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ if (ctxt->error) {
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 1);
+//-- END NEW CODE
+ xmlXPathBooleanFunction(ctxt, 1);
+ arg1 = valuePop(ctxt);
+ arg1->boolval &= arg2->boolval;
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ return (total);
+ }
+
+ case XPATH_OP_OR: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 1);
+//-- END NEW CODE
+ xmlXPathBooleanFunction(ctxt, 1);
+/*
+ // in dependency evaluation mode we must always process both parts
+ // of OR operation in the XPath expression
+ if (!ctxt->context->dependencyList && (ctxt->value || (ctxt->value->boolval == 1)))
+ return (total);
+
+ NOTE: there is still work to do...
+*/
+ if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
+ return (total);
+/*
+end of marked for replacement code
+ */
+ arg2 = valuePop(ctxt);
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ if (ctxt->error) {
+ xmlXPathFreeObject(arg2);
+ return(0);
+ }
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 1);
+//-- END NEW CODE
+ xmlXPathBooleanFunction(ctxt, 1);
+ arg1 = valuePop(ctxt);
+ arg1->boolval |= arg2->boolval;
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ return (total);
+ }
+
+ case XPATH_OP_EQUAL: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 2);
+//-- END NEW CODE
+
+ if (op->value)
+ equal = xmlXPathEqualValues(ctxt);
+ else
+ equal = xmlXPathNotEqualValues(ctxt);
+
+ valuePush(ctxt, xmlXPathNewBoolean(equal));
+ return (total);
+ }
+
+ case XPATH_OP_CMP: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 2);
+//-- END NEW CODE
+
+ ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
+ valuePush(ctxt, xmlXPathNewBoolean(ret));
+ return (total);
+ }
+
+ case XPATH_OP_PLUS: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ if (op->ch2 != -1) {
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ }
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+
+ if(ctxt->context->dependencyList)
+ {
+ if (op->value == 0 || op->value == 1)
+ addNodeSetsFromStackToDependencyList(ctxt, 2);
+ else if (op->value == 2)
+ addNodeSetsFromStackToDependencyList(ctxt, 1);
+ }
+//-- END NEW CODE
+
+
+ if (op->value == 0)
+ xmlXPathSubValues(ctxt);
+ else if (op->value == 1)
+ xmlXPathAddValues(ctxt);
+ else if (op->value == 2)
+ xmlXPathValueFlipSign(ctxt);
+ else if (op->value == 3) {
+ CAST_TO_NUMBER;
+ CHECK_TYPE0(XPATH_NUMBER);
+ }
+ return (total);
+ }
+
+ case XPATH_OP_MULT: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, 2);
+//-- END NEW CODE
+
+ if (op->value == 0)
+ xmlXPathMultValues(ctxt);
+ else if (op->value == 1)
+ xmlXPathDivValues(ctxt);
+ else if (op->value == 2)
+ xmlXPathModValues(ctxt);
+ return (total);
+ }
+
+ case XPATH_OP_UNION: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ pp = ctxt->context->proximityPosition;
+ cs = ctxt->context->contextSize;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ ctxt->context->proximityPosition = pp;
+ ctxt->context->contextSize = cs;
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+ CHECK_TYPE0(XPATH_NODESET);
+ arg2 = valuePop(ctxt);
+
+ CHECK_TYPE0(XPATH_NODESET);
+ arg1 = valuePop(ctxt);
+
+ arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval);
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2); // error for "node-set($var1) | node-set($var2)
+ // -- part of arg1 that was copied from arg2 is destroyed
+ return (total);
+ }
+
+ case XPATH_OP_ROOT: {
+ xmlXPathRoot(ctxt);
+ return (total);
+ }
+
+ case XPATH_OP_NODE: {
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ if(OOM_FLAG) return(-1);
+ return (total);
+ }
+
+ case XPATH_OP_RESET: {
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ CHECK_ERROR0;
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ CHECK_ERROR0;
+
+ ctxt->context->node = NULL;
+ return (total);
+ }
+
+ case XPATH_OP_COLLECT: {
+ if (op->ch1 == -1)
+ return (total);
+
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ if(OOM_FLAG) return(-1);
+ CHECK_ERROR0;
+
+ /*
+ * Optimization for [n] selection where n is a number
+ */
+ if ((op->ch2 != -1) &&
+ (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
+ (comp->steps[op->ch2].ch1 == -1) &&
+ (comp->steps[op->ch2].ch2 != -1) &&
+ (comp->steps[comp->steps[op->ch2].ch2].op == XPATH_OP_VALUE))
+ {
+ xmlXPathObjectPtr val;
+
+ val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
+ if ((val != NULL) && (val->type == XPATH_NUMBER)) {
+ int indx = (int) val->floatval;
+
+ if (val->floatval == (float) indx) {
+ total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, NULL);
+ if(OOM_FLAG) return(-1);
+ return (total);
+ }
+ }
+ }
+
+ total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
+ if(OOM_FLAG) return(-1);
+ return (total);
+ }
+
+ case XPATH_OP_VALUE: {
+ valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ return (total);
+ }
+
+ case XPATH_OP_VARIABLE: {
+ xmlXPathObjectPtr val;
+
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+
+ if (!op->value5) {
+ val = xmlXPathVariableLookup(ctxt->context, (xmlChar*) op->value4);
+ if (!val) {
+ ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
+ return(0);
+ }
+ valuePush(ctxt, val);
+ } else {
+ const xmlChar *URI;
+
+ URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5);
+ if (URI == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n"),
+ op->value4, op->value5);
+ return (total);
+ }
+ val = xmlXPathVariableLookupNS(ctxt->context, (xmlChar*) op->value4, URI);
+ if (val == NULL) {
+ ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
+ return(0);
+ }
+ valuePush(ctxt, val);
+ }
+ return (total);
+ }
+
+ case XPATH_OP_FUNCTION: {
+ xmlXPathFunction func;
+ const xmlChar *oldFunc, *oldFuncURI;
+ int i;
+
+ if (op->ch1 != -1)
+ {
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ if(OOM_FLAG) return(-1);
+ }
+
+ if (ctxt->valueNr < op->value) {
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n"));
+ ctxt->error = XPATH_INVALID_OPERAND;
+ return (total);
+ }
+
+ for (i = 0; i < op->value; i++)
+ if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n"));
+ ctxt->error = XPATH_INVALID_OPERAND;
+ return (total);
+ }
+
+ if (op->cache != NULL){
+ func = (xmlXPathFunction)op->cache;
+ }else {
+ const xmlChar* URI = NULL;
+
+ if (!op->value5){
+ func = xmlXPathFunctionLookup(ctxt->context, (xmlChar*)op->value4);
+ } else {
+ URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5);
+ if (!URI) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathCompOpEval: function %s bound to undefined prefix %s\n"),
+ op->value4, op->value5);
+ return (total);
+ }
+ func = xmlXPathFunctionLookupNS(ctxt->context, (xmlChar*)op->value4, URI);
+ }
+
+ if (!func) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathCompOpEval: function %s not found\n"), op->value4);
+ XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
+ }
+
+ op->cache = (void*) func;
+ op->cacheURI = (void*) URI;
+ } // if (op->cache != NULL)
+
+ oldFunc = ctxt->context->function;
+ oldFuncURI = ctxt->context->functionURI;
+ ctxt->context->function = (xmlChar*) op->value4;
+ ctxt->context->functionURI = (xmlChar*) op->cacheURI;
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ if(ctxt->context->dependencyList)
+ addNodeSetsFromStackToDependencyList(ctxt, op->value);
+//-- END NEW CODE
+
+ func(ctxt, op->value);
+ if(OOM_FLAG) return(-1);
+ ctxt->context->function = oldFunc;
+ ctxt->context->functionURI = oldFuncURI;
+ return (total);
+ }
+
+ case XPATH_OP_ARG: {
+ bakd = ctxt->context->doc;
+ bak = ctxt->context->node;
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ CHECK_ERROR0;
+
+ if (op->ch2 != -1) {
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+ if(OOM_FLAG) return(-1);
+ ctxt->context->doc = bakd;
+ ctxt->context->node = bak;
+ CHECK_ERROR0;
+ }
+ return (total);
+ }
+
+ case XPATH_OP_PREDICATE:
+ case XPATH_OP_FILTER: {
+ xmlXPathObjectPtr res;
+ xmlXPathObjectPtr obj, tmp;
+ xmlNodeSetPtr newset = NULL;
+ xmlNodeSetPtr oldset;
+ xmlNodePtr oldnode;
+ int i;
+
+ /*
+ * Optimization for ()[1] selection i.e. the first elem
+ */
+ if ((op->ch1 != -1) && (op->ch2 != -1) &&
+ (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
+ (comp->steps[op->ch2].op == XPATH_OP_VALUE))
+ {
+ xmlXPathObjectPtr val;
+
+ val = (xmlXPathObjectPtr)comp->steps[op->ch2].value4;
+
+ if ((val != NULL) && (val->type == XPATH_NUMBER) && (val->floatval == 1.0))
+ {
+ xmlNodePtr first = NULL;
+
+ total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], &first);
+ CHECK_ERROR0;
+ /*
+ * The nodeset should be in document order,
+ * Keep only the first value
+ */
+ if ((ctxt->value != NULL) &&
+ (ctxt->value->type == XPATH_NODESET) &&
+ (ctxt->value->nodesetval != NULL) &&
+ (ctxt->value->nodesetval->nodeNr > 1))
+ {
+ ctxt->value->nodesetval->nodeNr = 1;
+ }
+ return (total);
+ }
+ }
+
+ /*
+ * Optimization for ()[last()] selection i.e. the last elem
+ */
+ if ((op->ch1 != -1) && (op->ch2 != -1) &&
+ (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
+ (comp->steps[op->ch2].op == XPATH_OP_SORT))
+ {
+ int f = comp->steps[op->ch2].ch1;
+
+ if ((f != -1) &&
+ (comp->steps[f].op == XPATH_OP_FUNCTION) &&
+ (comp->steps[f].value5 == NULL) &&
+ (comp->steps[f].value == 0) &&
+ (comp->steps[f].value4 != NULL) &&
+ (xmlStrEqual((xmlChar*)comp->steps[f].value4, BAD_CAST "last")))
+ {
+ xmlNodePtr last = NULL;
+
+ total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], &last);
+ CHECK_ERROR0;
+ /*
+ * The nodeset should be in document order,
+ * Keep only the last value
+ */
+ if ((ctxt->value != NULL) &&
+ (ctxt->value->type == XPATH_NODESET) &&
+ (ctxt->value->nodesetval != NULL) &&
+ (ctxt->value->nodesetval->nodeTab != NULL) &&
+ (ctxt->value->nodesetval->nodeNr > 1))
+ {
+ ctxt->value->nodesetval->nodeTab[0] =
+ ctxt->value->nodesetval->nodeTab[ctxt->value->nodesetval->nodeNr - 1];
+ ctxt->value->nodesetval->nodeNr = 1;
+ }
+ return (total);
+ }
+ }
+
+
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+
+ CHECK_ERROR0;
+
+ if (op->ch2 == -1)
+ return (total);
+
+ if (ctxt->value == NULL)
+ return (total);
+
+ oldnode = ctxt->context->node;
+
+#ifdef LIBXML_XPTR_ENABLED
+ /*
+ * Hum are we filtering the result of an XPointer expression
+ */
+ if (ctxt->value->type == XPATH_LOCATIONSET)
+ {
+ xmlLocationSetPtr newlocset = NULL;
+ xmlLocationSetPtr oldlocset;
+
+ /*
+ * Extract the old locset, and then evaluate the result of the
+ * expression for all the element in the locset. use it to grow
+ * up a new locset.
+ */
+ CHECK_TYPE0(XPATH_LOCATIONSET);
+
+ obj = valuePop(ctxt);
+ oldlocset = obj->user;
+ ctxt->context->node = NULL;
+
+ if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
+ ctxt->context->contextSize = 0;
+ ctxt->context->proximityPosition = 0;
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ res = valuePop(ctxt);
+
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+
+ valuePush(ctxt, obj);
+ CHECK_ERROR0;
+
+ return (total);
+ }
+
+ newlocset = xmlXPtrLocationSetCreate(NULL);
+
+ for (i = 0; i < oldlocset->locNr; i++)
+ {
+ /*
+ * Run the evaluation with a node list made of a
+ * single item in the nodelocset.
+ */
+ ctxt->context->node = oldlocset->locTab[i]->user;
+ ctxt->context->contextSize = oldlocset->locNr;
+ ctxt->context->proximityPosition = i + 1;
+ tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ valuePush(ctxt, tmp);
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ CHECK_ERROR0;
+ /*
+ * The result of the evaluation need to be tested to
+ * decided whether the filter succeeded or not
+ */
+ res = valuePop(ctxt);
+ if (xmlXPathEvaluatePredicateResult(ctxt, res))
+ {
+ xmlXPtrLocationSetAdd(newlocset, xmlXPathObjectCopy(oldlocset->locTab[i]));
+ }
+
+ /*
+ * Cleanup
+ */
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ if (ctxt->value == tmp) {
+ res = valuePop(ctxt);
+ xmlXPathFreeObject(res);
+ }
+
+ ctxt->context->node = NULL;
+ } // for (i = 0; i < oldlocset->locNr; i++)
+
+ /*
+ * The result is used as the new evaluation locset.
+ */
+ xmlXPathFreeObject(obj);
+ ctxt->context->node = NULL;
+ ctxt->context->contextSize = -1;
+ ctxt->context->proximityPosition = -1;
+ valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
+ ctxt->context->node = oldnode;
+ return (total);
+ }
+#endif /* LIBXML_XPTR_ENABLED */
+
+ /*
+ * Extract the old set, and then evaluate the result of the
+ * expression for all the element in the set. use it to grow
+ * up a new set.
+ */
+ CHECK_TYPE0(XPATH_NODESET);
+
+ obj = valuePop(ctxt);
+ oldset = obj->nodesetval;
+ oldnode = ctxt->context->node;
+ ctxt->context->node = NULL;
+
+ if ((oldset == NULL) || (oldset->nodeNr == 0))
+ {
+ ctxt->context->contextSize = 0;
+ ctxt->context->proximityPosition = 0;
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ CHECK_ERROR0;
+
+ res = valuePop(ctxt);
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+
+ valuePush(ctxt, obj);
+ ctxt->context->node = oldnode;
+ CHECK_ERROR0;
+ } else {
+ /*
+ * Initialize the new set.
+ */
+ newset = xmlXPathNodeSetCreate(NULL);
+ if(!newset)
+ {
+ XP_ERROR0(XPATH_MEMORY_ERROR); // returns 0
+ }
+ for (i = 0; i < oldset->nodeNr; i++) {
+ /*
+ * Run the evaluation with a node list made of
+ * a single item in the nodeset.
+ */
+ ctxt->context->node = oldset->nodeTab[i];
+ tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ valuePush(ctxt, tmp);
+ ctxt->context->contextSize = oldset->nodeNr;
+ ctxt->context->proximityPosition = i + 1;
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ CHECK_ERROR0;
+
+ /*
+ * The result of the evaluation needs to be tested to
+ * decide whether the filter succeeded or not
+ */
+ res = valuePop(ctxt);
+ if (xmlXPathEvaluatePredicateResult(ctxt, res))
+ {
+ xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
+ }
+
+ /*
+ * Cleanup
+ */
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ if (ctxt->value == tmp) {
+ res = valuePop(ctxt);
+ xmlXPathFreeObject(res);
+ }
+
+ ctxt->context->node = NULL;
+ } // (i = 0; i < oldset->nodeNr; i++)
+
+ /*
+ * The result is used as the new evaluation set.
+ */
+ xmlXPathFreeObject(obj);
+ ctxt->context->node = NULL;
+ ctxt->context->contextSize = -1;
+ ctxt->context->proximityPosition = -1;
+ valuePush(ctxt, xmlXPathWrapNodeSet(newset));
+ }
+ ctxt->context->node = oldnode;
+ return (total);
+ }
+
+ case XPATH_OP_SORT: {
+ if (op->ch1 != -1)
+ {
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ if(OOM_FLAG) return(-1);
+ }
+
+ CHECK_ERROR0;
+
+ if (ctxt->value &&
+ ctxt->value->type == XPATH_NODESET &&
+ ctxt->value->nodesetval)
+ {
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ }
+ return (total);
+ }
+
+#ifdef LIBXML_XPTR_ENABLED
+ case XPATH_OP_RANGETO: {
+ xmlXPathObjectPtr range;
+ xmlXPathObjectPtr res, obj;
+ xmlXPathObjectPtr tmp;
+ xmlLocationSetPtr newlocset = NULL;
+ xmlLocationSetPtr oldlocset;
+ xmlNodeSetPtr oldset;
+ int i, j;
+
+ if (op->ch1 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
+ if (op->ch2 == -1)
+ return (total);
+
+ if (ctxt->value->type == XPATH_LOCATIONSET)
+ {
+ /*
+ * Extract the old locset, and then evaluate the result of the
+ * expression for all the element in the locset. use it to grow
+ * up a new locset.
+ */
+ CHECK_TYPE0(XPATH_LOCATIONSET);
+ obj = valuePop(ctxt);
+ oldlocset = obj->user;
+
+ if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
+ ctxt->context->node = NULL;
+ ctxt->context->contextSize = 0;
+ ctxt->context->proximityPosition = 0;
+
+ total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
+
+ res = valuePop(ctxt);
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ valuePush(ctxt, obj);
+ CHECK_ERROR0;
+ return (total);
+ }
+ newlocset = xmlXPtrLocationSetCreate(NULL);
+
+ for (i = 0; i < oldlocset->locNr; i++) {
+ /*
+ * Run the evaluation with a node list made of a
+ * single item in the nodelocset.
+ */
+ ctxt->context->node = oldlocset->locTab[i]->user;
+ ctxt->context->contextSize = oldlocset->locNr;
+ ctxt->context->proximityPosition = i + 1;
+ tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ valuePush(ctxt, tmp);
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ CHECK_ERROR0;
+
+ res = valuePop(ctxt);
+ if (res->type == XPATH_LOCATIONSET)
+ {
+ xmlLocationSetPtr rloc = (xmlLocationSetPtr)res->user;
+
+ for (j=0; j<rloc->locNr; j++) {
+ range = xmlXPtrNewRange(
+ oldlocset->locTab[i]->user,
+ oldlocset->locTab[i]->index,
+ rloc->locTab[j]->user2,
+ rloc->locTab[j]->index2);
+
+ if (range != NULL) {
+ xmlXPtrLocationSetAdd(newlocset, range);
+ }
+ }
+ } else {
+ range = xmlXPtrNewRangeNodeObject((xmlNodePtr)oldlocset->locTab[i]->user, res);
+ if (range != NULL) {
+ xmlXPtrLocationSetAdd(newlocset,range);
+ }
+ }
+
+ /*
+ * Cleanup
+ */
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ if (ctxt->value == tmp) {
+ res = valuePop(ctxt);
+ xmlXPathFreeObject(res);
+ }
+
+ ctxt->context->node = NULL;
+ }
+
+ }
+ else
+ { /* Not a location set */
+ CHECK_TYPE0(XPATH_NODESET);
+ obj = valuePop(ctxt);
+ oldset = obj->nodesetval;
+ ctxt->context->node = NULL;
+
+ newlocset = xmlXPtrLocationSetCreate(NULL);
+
+ if (oldset != NULL) {
+ for (i = 0; i < oldset->nodeNr; i++) {
+ /*
+ * Run the evaluation with a node list made of a single item
+ * in the nodeset.
+ */
+ ctxt->context->node = oldset->nodeTab[i];
+ tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ valuePush(ctxt, tmp);
+
+ if (op->ch2 != -1)
+ total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
+
+ CHECK_ERROR0;
+
+ res = valuePop(ctxt);
+ range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
+ if (range != NULL) {
+ xmlXPtrLocationSetAdd(newlocset, range);
+ }
+
+ /*
+ * Cleanup
+ */
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ if (ctxt->value == tmp) {
+ res = valuePop(ctxt);
+ xmlXPathFreeObject(res);
+ }
+
+ ctxt->context->node = NULL;
+ } // for
+ }
+ }
+
+ /*
+ * The result is used as the new evaluation set.
+ */
+ xmlXPathFreeObject(obj);
+ ctxt->context->node = NULL;
+ ctxt->context->contextSize = -1;
+ ctxt->context->proximityPosition = -1;
+ valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
+ return (total);
+ }
+#endif /* LIBXML_XPTR_ENABLED */
+ } // switch(op->op)
+
+ // None of options matched
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("XPath: unknown precompiled operation %d\n"), op->op);
+ return (total);
+}
+
+/**
+* xmlXPathRunEval:
+* @param ctxt the XPath parser context with the compiled expression
+*
+* Evaluate the Precompiled XPath expression in the given context.
+*/
+static void
+xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
+ xmlXPathCompExprPtr comp;
+
+ if (!ctxt || !ctxt->comp)
+ return;
+
+ if (!ctxt->valueTab) {
+ /* Allocate the value stack */
+
+ ctxt->valueTab = (xmlXPathObjectPtr *)
+ xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
+ if (!ctxt->valueTab) {
+ xmlXPathPErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n"));
+ xmlFree(ctxt);
+ }
+ ctxt->valueNr = 0;
+ ctxt->valueMax = 10;
+ ctxt->value = NULL;
+ }
+ comp = ctxt->comp;
+ if(comp->last < 0) {
+ xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathRunEval: last is less than zero\n"));
+ return;
+
+ }
+ xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
+}
+
+/************************************************************************
+ * *
+ * Public interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathEvalPredicate:
+ * @param ctxt the XPath context
+ * @param res the Predicate Expression evaluation result
+ *
+ * Evaluate a predicate result for the current node.
+ * A PredicateExpr is evaluated by evaluating the Expr and converting
+ * the result to a boolean. If the result is a number, the result will
+ * be converted to true if the number is equal to the position of the
+ * context node in the context node list (as returned by the position
+ * function) and will be converted to false otherwise; if the result
+ * is not a number, then the result will be converted as if by a call
+ * to the boolean function.
+ *
+ * Returns 1 if predicate is true, 0 otherwise
+ */
+XMLPUBFUNEXPORT int
+xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
+ if (res == NULL) return(0);
+ switch (res->type) {
+ case XPATH_BOOLEAN:
+ return(res->boolval);
+ case XPATH_NUMBER:
+ return(res->floatval == ctxt->proximityPosition);
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ return res->nodesetval
+ ?
+ res->nodesetval->nodeNr != 0
+ : 0;
+ case XPATH_STRING:
+ return
+ res->stringval &&
+ xmlStrlen(res->stringval) != 0;
+ default:
+ STRANGE
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathEvaluatePredicateResult:
+ * @param ctxt the XPath Parser context
+ * @param res the Predicate Expression evaluation result
+ *
+ * Evaluate a predicate result for the current node.
+ * A PredicateExpr is evaluated by evaluating the Expr and converting
+ * the result to a boolean. If the result is a number, the result will
+ * be converted to true if the number is equal to the position of the
+ * context node in the context node list (as returned by the position
+ * function) and will be converted to false otherwise; if the result
+ * is not a number, then the result will be converted as if by a call
+ * to the boolean function.
+ *
+ * Returns 1 if predicate is true, 0 otherwise
+ */
+XMLPUBFUNEXPORT int
+xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
+ xmlXPathObjectPtr res) {
+ if (res == NULL) return(0);
+ switch (res->type) {
+ case XPATH_BOOLEAN:
+ return(res->boolval);
+ case XPATH_NUMBER:
+#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
+ return((res->floatval == ctxt->context->proximityPosition) &&
+ (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
+#else
+ return(res->floatval == ctxt->context->proximityPosition);
+#endif
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ if (res->nodesetval == NULL)
+ return(0);
+ return(res->nodesetval->nodeNr != 0);
+ case XPATH_STRING:
+ return((res->stringval != NULL) &&
+ (xmlStrlen(res->stringval) != 0));
+#ifdef LIBXML_XPTR_ENABLED
+ case XPATH_LOCATIONSET:{
+ xmlLocationSetPtr ptr = res->user;
+ if (ptr == NULL)
+ return(0);
+ return (ptr->locNr != 0);
+ }
+#endif
+ default:
+ STRANGE
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathCtxtCompile:
+ * @param ctxt an XPath context
+ * @param str the XPath expression
+ *
+ * Compile an XPath expression
+ *
+ * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
+ * the caller has to free the object.
+ */
+XMLPUBFUNEXPORT xmlXPathCompExprPtr
+
+xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
+ xmlXPathParserContextPtr pctxt;
+ xmlXPathCompExprPtr comp;
+ LOAD_GS_SAFE_XPATH_CTXT(ctxt)
+
+ xmlXPathInit();
+
+ pctxt = xmlXPathNewParserContext(str, ctxt);
+ if(!pctxt)
+ return NULL; // OOM
+
+ xmlXPathCompileExpr(pctxt);
+
+ if( pctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG)
+ {
+ xmlXPathFreeParserContext(pctxt);
+ return NULL;
+ }
+
+ if (*pctxt->cur) {
+ /*
+ * aleksey: in some cases this line prints *second* error message
+ * (see bug#78858) and probably this should be fixed.
+ * However, we are not sure that all error messages are printed
+ * out in other places. It's not critical so we leave it as-is for now
+ */
+ xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
+ comp = NULL;
+ } else {
+ comp = pctxt->comp;
+ pctxt->comp = NULL;
+ }
+ xmlXPathFreeParserContext(pctxt);
+ if (comp) {
+ comp->expr = xmlStrdup(str);
+#ifdef DEBUG_EVAL_COUNTS
+ comp->string = xmlStrdup(str);
+ comp->nb = 0;
+#endif
+ }
+ return(comp);
+}
+
+/**
+ * xmlXPathCompile:
+ * @param str the XPath expression
+ *
+ * Compile an XPath expression
+ *
+ * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
+ * the caller has to free the object.
+ */
+XMLPUBFUNEXPORT xmlXPathCompExprPtr
+xmlXPathCompile(const xmlChar *str) {
+ return(xmlXPathCtxtCompile(NULL, str));
+}
+
+/**
+ * xmlXPathCompiledEval:
+ * @param comp the compiled XPath expression
+ * @param ctx the XPath context
+ *
+ * Evaluate the Precompiled XPath expression in the given context.
+ *
+ * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
+ * the caller has to free the object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
+ xmlXPathParserContextPtr ctxt;
+ xmlXPathObjectPtr res, tmp, init = NULL;
+ int stack = 0;
+#ifndef LIBXML_THREAD_ENABLED
+ //FIXIT
+ //static
+ int reentance = 0;
+#endif
+ LOAD_GS_SAFE_XPATH_CTXT(ctx)
+
+ if (!comp || !ctx)
+ return(NULL);
+ xmlXPathInit();
+
+ CHECK_CONTEXT(ctx)
+
+#ifndef LIBXML_THREAD_ENABLED
+ reentance++;
+ if (reentance > 1)
+ xmlXPathDisableOptimizer = 1;
+#endif
+
+#ifdef DEBUG_EVAL_COUNTS
+ comp->nb++;
+ if ((comp->string != NULL) && (comp->nb > 100)) {
+ fprintf(stderr, "100 x %s\n", comp->string);
+ comp->nb = 0;
+ }
+#endif
+ ctxt = xmlXPathCompParserContext(comp, ctx);
+ xmlXPathRunEval(ctxt); //might set OOM flag
+ if(OOM_FLAG)
+ {
+ if(ctxt)
+ {
+ ctxt->comp = NULL;
+ xmlXPathFreeParserContext(ctxt);
+ }
+ return(NULL);
+ }
+
+ if (ctxt->value == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathCompiledEval: evaluation failed\n"));
+ res = NULL;
+ } else {
+ res = valuePop(ctxt);
+ }
+
+
+ do {
+ tmp = valuePop(ctxt);
+ if (tmp != NULL) {
+ if (tmp != init)
+ stack++;
+ xmlXPathFreeObject(tmp);
+ }
+ } while (tmp != NULL);
+ if ((stack != 0) && (res != NULL)) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathCompiledEval: %d object left on the stack\n"),
+ stack);
+ }
+ if (ctxt->error != XPATH_EXPRESSION_OK) {
+ xmlXPathFreeObject(res);
+ res = NULL;
+ }
+
+ ctxt->comp = NULL;
+ xmlXPathFreeParserContext(ctxt);
+#ifndef LIBXML_THREAD_ENABLED
+ reentance--;
+#endif
+ return(res);
+}
+
+/**
+ * xmlXPathEvalExpr:
+ * @param ctxt the XPath Parser context
+ *
+ * Parse and evaluate an XPath expression in the given context,
+ * then push the result on the context stack
+ *
+ * OOM: possible --> check OOM flag
+ */
+XMLPUBFUNEXPORT void
+xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathCompileExpr(ctxt);
+ CHECK_ERROR;
+ xmlXPathRunEval(ctxt);
+}
+
+/**
+ * xmlXPathEval:
+ * @param str the XPath expression
+ * @param ctx the XPath context
+ *
+ * Evaluate the XPath Location Path in the given context.
+ *
+ * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
+ * the caller has to free the object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathEval(const xmlChar* str, xmlXPathContextPtr ctx) {
+ xmlXPathParserContextPtr ctxt;
+ xmlXPathObjectPtr res, tmp, init = NULL;
+ int stack = 0;
+
+ xmlXPathInit();
+
+ CHECK_CONTEXT(ctx)
+
+ ctxt = xmlXPathNewParserContext(str, ctx);
+ xmlXPathEvalExpr(ctxt);
+
+ if (ctxt->value == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathEval: evaluation failed\n"));
+ res = NULL;
+ } else if (*ctxt->cur != 0) {
+ xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
+ res = NULL;
+ } else {
+ res = valuePop(ctxt);
+ }
+
+ do {
+ tmp = valuePop(ctxt);
+ if (tmp) {
+ if (tmp != init)
+ stack++;
+ xmlXPathFreeObject(tmp);
+ }
+ } while (tmp);
+
+ if ((stack != 0) && res) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathEval: %d object left on the stack\n"),
+ stack);
+ }
+ if (ctxt->error != XPATH_EXPRESSION_OK) {
+ xmlXPathFreeObject(res);
+ res = NULL;
+ }
+
+ xmlXPathFreeParserContext(ctxt);
+ return(res);
+}
+
+/**
+ * xmlXPathEvalExpression:
+ * @param str the XPath expression
+ * @param ctxt the XPath context
+ *
+ * Evaluate the XPath expression in the given context.
+ *
+ * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
+ * the caller has to free the object.
+ */
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr pctxt;
+ xmlXPathObjectPtr res, tmp;
+ int stack = 0;
+
+ xmlXPathInit();
+
+ CHECK_CONTEXT(ctxt)
+
+ pctxt = xmlXPathNewParserContext(str, ctxt);
+ xmlXPathEvalExpr(pctxt);
+
+ if (*pctxt->cur != 0) {
+ xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
+ res = NULL;
+ } else {
+ res = valuePop(pctxt);
+ }
+
+ do {
+ tmp = valuePop(pctxt);
+ if (tmp != NULL) {
+ xmlXPathFreeObject(tmp);
+ stack++;
+ }
+ } while (tmp != NULL);
+
+ if ((stack != 0) && (res != NULL)) {
+ xmlGenericError(xmlGenericErrorContext,
+ EMBED_ERRTXT("xmlXPathEvalExpression: %d object left on the stack\n"),
+ stack);
+ }
+ xmlXPathFreeParserContext(pctxt);
+ return(res);
+}
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+
+/* First initialises static nodeset variable from deplist (argument).
+* Calls xmlXPathCompiledEval. If the result is a nodeset it is copied
+* to the deplist. Otherwise modifications in xmlXPathCompOpEval have
+* already filled deplist.
+*/
+XMLPUBFUNEXPORT xmlXPathObjectPtr
+xmlXPathCompiledEvalWithDependencies(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx, xmlNodeSetPtr deplist)
+{
+ xmlXPathObjectPtr res = NULL;
+ ctx->dependencyList = deplist;
+ res = xmlXPathCompiledEval(comp, ctx);
+
+ /* if the result is nodeset there are no other dependencies */
+ if (res != NULL && res->type == XPATH_NODESET) {
+ if (!xmlXPathNodeSetIsEmpty(deplist)) {
+ ;/* this should not happen */
+ }
+ xmlXPathNodeSetMerge(deplist, res->nodesetval);
+ // KO: moved out of IF block // xmlXPathNodeSetSort(deplist);
+ }
+ // KO: moved out of IF block //else xmlXPathNodeSetSort(deplist);
+
+ xmlXPathNodeSetSort(deplist); /* dep has been filled with nodes */
+
+ ctx->dependencyList = NULL;
+
+ return res;
+}
+
+/* First pops of nargs values from stack (ctxt) and stores them in temporary
+* array. Adds all values of type nodeset to deplist and pushes everything back
+* on the stack.
+*/
+void addNodeSetsFromStackToDependencyList(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlNodeSetPtr deplist = ctxt->context->dependencyList;
+#define MAXTMPSTACK 10
+ xmlXPathObjectPtr tmpStack[MAXTMPSTACK] = {0,0,0,0,0,0,0,0,0,0};
+ xmlXPathObjectPtr cValue;
+ int i=0;
+
+ if(!deplist) return;
+ cValue = ctxt->value; // To remove when more efficient code is used
+
+ // KO: If to decide number of popped elements first (min(nargs,MAXTMPSTACK))
+ // then no need to prefill tmpStack with zeros and make checks in the second loop
+ /* copy all arguments of type nodeset to deplist */
+ // KO: it is better not to pop and push arguments back, but rather PEEK the values from
+ // the internal stack: PEEK(i) = ctxt->valueTab[ctxt->valueNr - i - 1];
+ // where 'i=1' is the index of the top-most element on the stack
+ for (i=0; (i < nargs) && (i < MAXTMPSTACK); i++) {
+ tmpStack[i] = valuePop(ctxt);
+ }
+
+ for (i= MAXTMPSTACK-1; i>=0 ; i--) {
+ if (tmpStack[i] != NULL) {
+ if (tmpStack[i]->type == XPATH_NODESET)
+ xmlXPathNodeSetMerge(deplist, tmpStack[i]->nodesetval);
+ valuePush(ctxt, tmpStack[i]);
+ }
+ }
+ ctxt->value = cValue;
+//#define __enable_this_test
+#ifdef __enable_this_test
+
+ xmlXPathObjectPtr* stack = ctxt->valueTab;
+ int index = ctxt->valueNr - 1;
+ for (i= 0; i < nargs ; i++) {
+ xmlXPathObjectPtr obj = stack[index--];
+ if (obj->type == XPATH_NODESET)
+ xmlXPathNodeSetMerge(deplist, obj->nodesetval);
+ }
+#endif
+
+#undef __enable_this_test
+}
+
+/*
+ * xmlXFormsInstanceFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the instance() XForms function
+ * nodeset instance(string)
+ */
+void
+xmlXFormsInstanceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur = NULL;
+ xmlXPathObjectPtr ret = NULL;
+ xmlDocPtr doc = NULL;
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ if (!cur)
+ XP_ERROR(XPATH_INVALID_OPERAND);
+
+ /* convert argument to string */
+ cur = xmlXPathConvertString(cur);
+
+
+ if (ctxt->context->instanceDocs)
+ {
+ doc = (xmlDocPtr)xmlHashLookup(ctxt->context->instanceDocs, cur->stringval);
+ }
+
+ /* create a node set and change context, which will be reset by
+ the operations: AND, OR, EQUAL, CMP, MULT, UNION, ARG */
+ if (doc)
+ {
+ ctxt->context->doc = doc;
+ ctxt->context->node = xmlDocGetRootElement(doc);
+ ret = xmlXPathNewNodeSet(ctxt->context->node);
+ }
+ else
+ {
+ ret = xmlXPathNewNodeSet(NULL);
+ }
+
+ /* push the instance on the stack */
+ valuePush(ctxt, ret);
+
+ /* free cur*/
+ xmlXPathFreeObject(cur);
+}
+//
+//
+//-- END NEW CODE // XMLENGINE XFROMS support
+
+/************************************************************************
+ * *
+ * Extra functions not pertaining to the XPath spec *
+ * *
+ ************************************************************************/
+/**
+ * xmlXPathEscapeUriFunction:
+ * @param ctxt the XPath Parser context
+ * @param nargs the number of arguments
+ *
+ * Implement the escape-uri() XPath function
+ * string escape-uri(string $str, bool $escape-reserved)
+ *
+ * This function applies the URI escaping rules defined in section 2 of [RFC
+ * 2396] to the string supplied as $uri-part, which typically represents all
+ * or part of a URI. The effect of the function is to replace any special
+ * character in the string by an escape sequence of the form %xx%yy...,
+ * where xxyy... is the hexadecimal representation of the octets used to
+ * represent the character in UTF-8.
+ *
+ * The set of characters that are escaped depends on the setting of the
+ * boolean argument $escape-reserved.
+ *
+ * If $escape-reserved is true, all characters are escaped other than lower
+ * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
+ * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
+ * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
+ * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
+ * A-F).
+ *
+ * If $escape-reserved is false, the behavior differs in that characters
+ * referred to in [RFC 2396] as reserved characters are not escaped. These
+ * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
+ *
+ * [RFC 2396] does not define whether escaped URIs should use lower case or
+ * upper case for hexadecimal digits. To ensure that escaped URIs can be
+ * compared using string comparison functions, this function must always use
+ * the upper-case letters A-F.
+ *
+ * Generally, $escape-reserved should be set to true when escaping a string
+ * that is to form a single part of a URI, and to false when escaping an
+ * entire URI or URI reference.
+ *
+ * In the case of non-ascii characters, the string is encoded according to
+ * utf-8 and then converted according to RFC 2396.
+ *
+ * Examples
+ * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
+ * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
+ * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
+ * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
+ *
+ */
+static void
+xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str;
+ int escape_reserved;
+ xmlBufferPtr target;
+ xmlChar *cptr;
+ xmlChar escape[4];
+
+ CHECK_ARITY(2);
+
+ escape_reserved = xmlXPathPopBoolean(ctxt);
+
+ CAST_TO_STRING;
+ str = valuePop(ctxt);
+
+ target = xmlBufferCreate();
+
+ escape[0] = '%';
+ escape[3] = 0;
+
+ if (target) {
+ for (cptr = str->stringval; *cptr; cptr++) {
+ if ((*cptr >= 'A' && *cptr <= 'Z') ||
+ (*cptr >= 'a' && *cptr <= 'z') ||
+ (*cptr >= '0' && *cptr <= '9') ||
+ *cptr == '-' || *cptr == '_' || *cptr == '.' ||
+ *cptr == '!' || *cptr == '~' || *cptr == '*' ||
+ *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
+ (*cptr == '%' &&
+ ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
+ (cptr[1] >= 'a' && cptr[1] <= 'f') ||
+ (cptr[1] >= '0' && cptr[1] <= '9')) &&
+ ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
+ (cptr[2] >= 'a' && cptr[2] <= 'f') ||
+ (cptr[2] >= '0' && cptr[2] <= '9'))) ||
+ (!escape_reserved &&
+ (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
+ *cptr == ':' || *cptr == '@' || *cptr == '&' ||
+ *cptr == '=' || *cptr == '+' || *cptr == '$' ||
+ *cptr == ',')))
+ {
+ xmlBufferAdd(target, cptr, 1);
+ } else {
+ if ((*cptr >> 4) < 10)
+ escape[1] = '0' + (*cptr >> 4);
+ else
+ escape[1] = 'A' - 10 + (*cptr >> 4);
+ if ((*cptr & 0xF) < 10)
+ escape[2] = '0' + (*cptr & 0xF);
+ else
+ escape[2] = 'A' - 10 + (*cptr & 0xF);
+
+ xmlBufferAdd(target, &escape[0], 3);
+ }
+ }
+ }
+ valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ xmlBufferFree(target);
+ xmlXPathFreeObject(str);
+}
+
+/**
+ * xmlXPathRegisterAllFunctions:
+ * @param ctxt the XPath context
+ *
+ * Registers all default XPath functions in this context
+ *
+ * OOM: possible --> check OOM flag
+ * Prerequisites: TRUE ( ctxt && !ctxt->funcHash )
+ */
+XMLPUBFUNEXPORT void
+xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
+{
+ LOAD_GS_DIRECT
+ //if (!ctxt || ctxt->funcHash)
+ // return;
+
+ if (!ctxt->funcHash){
+ // Initialize table in this context and then set it for global reuse if needed
+ ctxt->funcHash = xmlHashCreate(40); // Note: for standard XPath functions
+ if(!ctxt->funcHash)
+ return; // OOM
+ }
+
+ // Note: OOM is possible in each of these functions
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"boolean", xmlXPathBooleanFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"ceiling", xmlXPathCeilingFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"count", xmlXPathCountFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"concat", xmlXPathConcatFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"contains",xmlXPathContainsFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"id", xmlXPathIdFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"false", xmlXPathFalseFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"floor", xmlXPathFloorFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"last", xmlXPathLastFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"lang", xmlXPathLangFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"local-name", xmlXPathLocalNameFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"not", xmlXPathNotFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"name", xmlXPathNameFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"namespace-uri", xmlXPathNamespaceURIFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"normalize-space", xmlXPathNormalizeFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"number", xmlXPathNumberFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"position", xmlXPathPositionFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"round", xmlXPathRoundFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string", xmlXPathStringFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string-length", xmlXPathStringLengthFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"starts-with", xmlXPathStartsWithFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring", xmlXPathSubstringFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-before", xmlXPathSubstringBeforeFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-after", xmlXPathSubstringAfterFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"sum", xmlXPathSumFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"true", xmlXPathTrueFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar*)"translate", xmlXPathTranslateFunction);
+
+ xmlXPathRegisterFuncNS(ctxt,(const xmlChar*)"escape-uri",
+ (const xmlChar*)"http://www.w3.org/2002/08/xquery-functions", xmlXPathEscapeUriFunction);
+
+
+
+// XMLENGINE: NEW CODE -- XForms extensions support
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *)"instance", xmlXFormsInstanceFunction);
+//-- END NEW CODE
+
+ if(OOM_FLAG){
+ xmlHashFree(ctxt->funcHash, NULL /* deallocator func */);
+ ctxt->funcHash = NULL;
+ }
+}
+
+#endif /* LIBXML_XPATH_ENABLED */