| /* |
| * 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 |
| * |
| */ |
| |
| #define IN_LIBXML |
| #include "libxml.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 |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| #ifdef HAVE_SIGNAL_H |
| #include <signal.h> |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/valid.h> |
| #include <libxml/xpath.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/hash.h> |
| #ifdef LIBXML_XPTR_ENABLED |
| #include <libxml/xpointer.h> |
| #endif |
| #ifdef LIBXML_DEBUG_ENABLED |
| #include <libxml/debugXML.h> |
| #endif |
| #include <libxml/xmlerror.h> |
| #include <libxml/threads.h> |
| #include <libxml/globals.h> |
| |
| #define TODO \ |
| xmlGenericError(xmlGenericErrorContext, \ |
| "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 "trionan.c" |
| |
| /* |
| * The lack of portability of this section of the libc is annoying ! |
| */ |
| double xmlXPathNAN = 0; |
| double xmlXPathPINF = 1; |
| double xmlXPathNINF = -1; |
| double xmlXPathNZERO = 0; |
| static int xmlXPathInitialized = 0; |
| |
| /** |
| * xmlXPathInit: |
| * |
| * Initialize the XPath environment |
| */ |
| void |
| xmlXPathInit(void) { |
| if (xmlXPathInitialized) return; |
| |
| xmlXPathPINF = trio_pinf(); |
| xmlXPathNINF = trio_ninf(); |
| xmlXPathNAN = trio_nan(); |
| xmlXPathNZERO = trio_nzero(); |
| |
| xmlXPathInitialized = 1; |
| } |
| |
| /** |
| * xmlXPathIsNaN: |
| * @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 |
| */ |
| int |
| xmlXPathIsNaN(double val) { |
| return(trio_isnan(val)); |
| } |
| |
| /** |
| * xmlXPathIsInf: |
| * @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 |
| */ |
| int |
| xmlXPathIsInf(double val) { |
| return(trio_isinf(val)); |
| } |
| |
| #endif /* SCHEMAS or XPATH */ |
| #ifdef LIBXML_XPATH_ENABLED |
| /** |
| * xmlXPathGetSign: |
| * @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 |
| */ |
| static int |
| xmlXPathGetSign(double val) { |
| return(trio_signbit(val)); |
| } |
| |
| |
| /* |
| * TODO: when compatibility allows remove all "fake node libxslt" strings |
| * the test should just be name[0] = ' ' |
| */ |
| /* #define DEBUG */ |
| /* #define DEBUG_STEP */ |
| /* #define DEBUG_STEP_NTH */ |
| /* #define DEBUG_EXPR */ |
| /* #define DEBUG_EVAL_COUNTS */ |
| |
| static xmlNs xmlXPathXMLNamespaceStruct = { |
| NULL, |
| XML_NAMESPACE_DECL, |
| XML_XML_NAMESPACE, |
| BAD_CAST "xml", |
| NULL |
| }; |
| static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct; |
| #ifndef LIBXML_THREAD_ENABLED |
| /* |
| * Optimizer is disabled only when threaded apps are detected while |
| * the library ain't compiled for thread safety. |
| */ |
| static int xmlXPathDisableOptimizer = 0; |
| #endif |
| |
| /************************************************************************ |
| * * |
| * Error handling routines * |
| * * |
| ************************************************************************/ |
| |
| |
| static const char *xmlXPathErrorMessages[] = { |
| "Ok\n", |
| "Number encoding\n", |
| "Unfinished literal\n", |
| "Start of literal\n", |
| "Expected $ for variable reference\n", |
| "Undefined variable\n", |
| "Invalid predicate\n", |
| "Invalid expression\n", |
| "Missing closing curly brace\n", |
| "Unregistered function\n", |
| "Invalid operand\n", |
| "Invalid type\n", |
| "Invalid number of arguments\n", |
| "Invalid context size\n", |
| "Invalid context position\n", |
| "Memory allocation error\n", |
| "Syntax error\n", |
| "Resource error\n", |
| "Sub resource error\n", |
| "Undefined namespace prefix\n", |
| "Encoding error\n", |
| "Char out of XML range\n" |
| }; |
| |
| |
| /** |
| * xmlXPathErrMemory: |
| * @ctxt: an XPath context |
| * @extra: extra informations |
| * |
| * Handle a redefinition of attribute error |
| */ |
| static void |
| xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
| { |
| if (ctxt != NULL) { |
| if (extra) { |
| xmlChar buf[200]; |
| |
| xmlStrPrintf(buf, 200, |
| BAD_CAST "Memory allocation failed : %s\n", |
| extra); |
| ctxt->lastError.message = (char *) xmlStrdup(buf); |
| } else { |
| ctxt->lastError.message = (char *) |
| xmlStrdup(BAD_CAST "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, |
| "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, |
| "Memory allocation failed\n"); |
| } |
| } |
| |
| /** |
| * xmlXPathErrMemory: |
| * @ctxt: an XPath parser context |
| * @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: |
| * @ctxt: a XPath parser context |
| * @error: the error code |
| * |
| * Handle a Relax NG Parsing error |
| */ |
| 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; |
| 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]); |
| } |
| |
| } |
| |
| /** |
| * xmlXPatherror: |
| * @ctxt: the XPath Parser context |
| * @file: the file name |
| * @line: the line number |
| * @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 { |
| XPATH_OP_END=0, |
| XPATH_OP_AND, |
| XPATH_OP_OR, |
| XPATH_OP_EQUAL, |
| XPATH_OP_CMP, |
| XPATH_OP_PLUS, |
| XPATH_OP_MULT, |
| XPATH_OP_UNION, |
| XPATH_OP_ROOT, |
| XPATH_OP_NODE, |
| XPATH_OP_RESET, |
| XPATH_OP_COLLECT, |
| XPATH_OP_VALUE, |
| XPATH_OP_VARIABLE, |
| XPATH_OP_FUNCTION, |
| XPATH_OP_ARG, |
| XPATH_OP_PREDICATE, |
| XPATH_OP_FILTER, |
| XPATH_OP_SORT |
| #ifdef LIBXML_XPTR_ENABLED |
| ,XPATH_OP_RANGETO |
| #endif |
| } xmlXPathOp; |
| |
| 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; |
| |
| |
| typedef struct _xmlXPathStepOp xmlXPathStepOp; |
| typedef xmlXPathStepOp *xmlXPathStepOpPtr; |
| struct _xmlXPathStepOp { |
| xmlXPathOp op; |
| int ch1; |
| int ch2; |
| int value; |
| int value2; |
| int value3; |
| void *value4; |
| void *value5; |
| void *cache; |
| void *cacheURI; |
| }; |
| |
| struct _xmlXPathCompExpr { |
| int nbStep; |
| int maxStep; |
| xmlXPathStepOp *steps; /* ops for computation */ |
| int last; |
| xmlChar *expr; |
| #ifdef DEBUG_EVAL_COUNTS |
| int nb; |
| xmlChar *string; |
| #endif |
| }; |
| |
| /************************************************************************ |
| * * |
| * 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, "allocating component\n"); |
| return(NULL); |
| } |
| memset(cur, 0, sizeof(xmlXPathCompExpr)); |
| cur->maxStep = 10; |
| cur->nbStep = 0; |
| cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * |
| sizeof(xmlXPathStepOp)); |
| if (cur->steps == NULL) { |
| xmlXPathErrMemory(NULL, "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: |
| * @comp: an XPATH comp |
| * |
| * Free up the memory allocated by @comp |
| */ |
| void |
| xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) |
| { |
| xmlXPathStepOpPtr op; |
| int i; |
| |
| if (comp == NULL) |
| return; |
| for (i = 0; i < comp->nbStep; i++) { |
| op = &comp->steps[i]; |
| if (op->value4 != NULL) { |
| if (op->op == XPATH_OP_VALUE) |
| xmlXPathFreeObject(op->value4); |
| else |
| xmlFree(op->value4); |
| } |
| if (op->value5 != NULL) |
| xmlFree(op->value5); |
| } |
| 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: |
| * @comp: the compiled expression |
| * @ch1: first child index |
| * @ch2: second child index |
| * @op: an op |
| * @value: the first int value |
| * @value2: the second int value |
| * @value3: the third int value |
| * @value4: the first string value |
| * @value5: the second string value |
| * |
| * Add an 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) { |
| 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, "adding step\n"); |
| return(-1); |
| } |
| comp->steps = real; |
| } |
| comp->last = comp->nbStep; |
| comp->steps[comp->nbStep].ch1 = ch1; |
| comp->steps[comp->nbStep].ch2 = ch2; |
| comp->steps[comp->nbStep].op = op; |
| comp->steps[comp->nbStep].value = value; |
| comp->steps[comp->nbStep].value2 = value2; |
| comp->steps[comp->nbStep].value3 = value3; |
| comp->steps[comp->nbStep].value4 = value4; |
| comp->steps[comp->nbStep].value5 = value5; |
| comp->steps[comp->nbStep].cache = NULL; |
| return(comp->nbStep++); |
| } |
| |
| /** |
| * xmlXPathCompSwap: |
| * @comp: the compiled expression |
| * @op: operation index |
| * |
| * Swaps 2 operations in the compiled expression |
| */ |
| static void |
| xmlXPathCompSwap(xmlXPathStepOpPtr op) { |
| int tmp; |
| |
| #ifndef LIBXML_THREAD_ENABLED |
| /* |
| * Since this manipulates possibly shared variables, this is |
| * disable 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, \ |
| "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: |
| * @output: the FILE * to dump the output |
| * @cur: the object to inspect |
| * @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: |
| * @output: the FILE * for the output |
| * @comp: the precompiled XPath expression |
| * @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: |
| * @ctxt: an XPath evaluation context |
| * |
| * Pops the top XPath object from the value stack |
| * |
| * Returns the XPath object just removed |
| */ |
| 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: |
| * @ctxt: an XPath evaluation context |
| * @value: the XPath object |
| * |
| * Pushes a new XPath object on top of the value stack |
| * |
| * returns the number of items on the value stack |
| */ |
| extern int |
| valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) |
| { |
| if (ctxt->valueNr >= ctxt->valueMax) { |
| ctxt->valueMax *= 2; |
| ctxt->valueTab = |
| (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab, |
| ctxt->valueMax * |
| sizeof(ctxt->valueTab[0])); |
| if (ctxt->valueTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); |
| return (0); |
| } |
| } |
| ctxt->valueTab[ctxt->valueNr] = value; |
| ctxt->value = value; |
| return (ctxt->valueNr++); |
| } |
| |
| /** |
| * xmlXPathPopBoolean: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a boolean from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the boolean |
| */ |
| int |
| xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| int ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(0); |
| } |
| ret = xmlXPathCastToBoolean(obj); |
| xmlXPathFreeObject(obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopNumber: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a number from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the number |
| */ |
| double |
| xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| double ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(0); |
| } |
| ret = xmlXPathCastToNumber(obj); |
| xmlXPathFreeObject(obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopString: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a string from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the string |
| */ |
| xmlChar * |
| xmlXPathPopString (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| xmlChar * ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(NULL); |
| } |
| ret = xmlXPathCastToString(obj); |
| /* TODO: needs refactoring somewhere else */ |
| if (obj->stringval == ret) |
| obj->stringval = NULL; |
| xmlXPathFreeObject(obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopNodeSet: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a node-set from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the node-set |
| */ |
| xmlNodeSetPtr |
| xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| xmlNodeSetPtr ret; |
| |
| if (ctxt->value == NULL) { |
| 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 != NULL) |
| xmlFreeNodeList((xmlNodePtr) obj->user); |
| xmlXPathFreeNodeSetList(obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopExternal: |
| * @ctxt: an XPath parser context |
| * |
| * Pops an external object from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the object |
| */ |
| void * |
| xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| void * ret; |
| |
| if (ctxt->value == NULL) { |
| 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(l,b,i,v) \ |
| if (l == 1) b[i++] = (xmlChar) v; \ |
| else i += xmlCopyChar(l,&b[i],v) |
| |
| #define NEXTL(l) ctxt->cur += l |
| |
| #define SKIP_BLANKS \ |
| while (IS_BLANK_CH(*(ctxt->cur))) NEXT |
| |
| #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) |
| |
| /** |
| * xmlXPathFormatNumber: |
| * @number: number to format |
| * @buffer: output buffer |
| * @buffersize: size of output buffer |
| * |
| * Convert the number into a string representation. |
| */ |
| 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') |
| ; |
| if (*ptr != '.') |
| ptr++; |
| while ((*ptr++ = *after_fraction++) != 0); |
| |
| /* 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: |
| * @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 (startting at -1) |
| * to be able to differenciate from line numbers. |
| * |
| * Returns the number of element found in the document or -1 in case |
| * of error. |
| */ |
| long |
| xmlXPathOrderDocElems(xmlDocPtr doc) { |
| long count = 0; |
| xmlNodePtr cur; |
| |
| if (doc == NULL) |
| return(-1); |
| cur = doc->children; |
| while (cur != NULL) { |
| if (cur->type == XML_ELEMENT_NODE) { |
| cur->content = (void *) (-(++count)); |
| if (cur->children != NULL) { |
| cur = cur->children; |
| continue; |
| } |
| } |
| if (cur->next != NULL) { |
| cur = cur->next; |
| continue; |
| } |
| do { |
| cur = cur->parent; |
| if (cur == NULL) |
| break; |
| if (cur == (xmlNodePtr) doc) { |
| cur = NULL; |
| break; |
| } |
| if (cur->next != NULL) { |
| cur = cur->next; |
| break; |
| } |
| } while (cur != NULL); |
| } |
| return(count); |
| } |
| |
| /** |
| * xmlXPathCmpNodes: |
| * @node1: the first node |
| * @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 |
| * that's the same node, -1 otherwise |
| */ |
| 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->next) |
| 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: |
| * @set: the node set |
| * |
| * Sort the node set in document order |
| */ |
| 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: |
| * @node: the parent node of the namespace XPath node |
| * @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. |
| */ |
| static xmlNodePtr |
| xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) { |
| xmlNsPtr cur; |
| |
| if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) |
| return(NULL); |
| if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) |
| return((xmlNodePtr) ns); |
| |
| /* |
| * Allocate a new Namespace and fill the fields. |
| */ |
| cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); |
| if (cur == NULL) { |
| xmlXPathErrMemory(NULL, "duplicating namespace\n"); |
| return(NULL); |
| } |
| memset(cur, 0, sizeof(xmlNs)); |
| cur->type = XML_NAMESPACE_DECL; |
| if (ns->href != NULL) |
| cur->href = xmlStrdup(ns->href); |
| if (ns->prefix != NULL) |
| cur->prefix = xmlStrdup(ns->prefix); |
| cur->next = (xmlNsPtr) node; |
| return((xmlNodePtr) cur); |
| } |
| |
| /** |
| * xmlXPathNodeSetFreeNs: |
| * @ns: the XPath namespace node found in a nodeset. |
| * |
| * 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. Check if such a node need to be freed |
| */ |
| void |
| xmlXPathNodeSetFreeNs(xmlNsPtr ns) { |
| if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) |
| return; |
| |
| if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) { |
| if (ns->href != NULL) |
| xmlFree((xmlChar *)ns->href); |
| if (ns->prefix != NULL) |
| xmlFree((xmlChar *)ns->prefix); |
| xmlFree(ns); |
| } |
| } |
| |
| /** |
| * xmlXPathNodeSetCreate: |
| * @val: an initial xmlNodePtr, or NULL |
| * |
| * Create a new xmlNodeSetPtr of type double and of value @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlNodeSetPtr |
| xmlXPathNodeSetCreate(xmlNodePtr val) { |
| xmlNodeSetPtr ret; |
| |
| ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating nodeset\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlNodeSet)); |
| if (val != NULL) { |
| ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
| sizeof(xmlNodePtr)); |
| if (ret->nodeTab == NULL) { |
| xmlXPathErrMemory(NULL, "creating nodeset\n"); |
| xmlFree(ret); |
| return(NULL); |
| } |
| 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; |
| |
| ret->nodeTab[ret->nodeNr++] = |
| xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
| } else |
| ret->nodeTab[ret->nodeNr++] = val; |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathNodeSetContains: |
| * @cur: the node-set |
| * @val: the node |
| * |
| * checks whether @cur contains @val |
| * |
| * Returns true (1) if @cur contains @val, false (0) otherwise |
| */ |
| 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); |
| } |
| |
| /** |
| * xmlXPathNodeSetAddNs: |
| * @cur: the initial node set |
| * @node: the hosting node |
| * @ns: a the namespace node |
| * |
| * add a new namespace node to an existing NodeSet |
| */ |
| void |
| xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
| int i; |
| |
| if ((ns == NULL) || (node == NULL) || (ns->type != XML_NAMESPACE_DECL) || |
| (node->type != XML_ELEMENT_NODE)) |
| return; |
| |
| /* @@ with_ns to check wether namespace nodes should be looked at @@ */ |
| /* |
| * check against doublons |
| */ |
| for (i = 0;i < cur->nodeNr;i++) { |
| if ((cur->nodeTab[i] != NULL) && |
| (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 == NULL) { |
| xmlXPathErrMemory(NULL, "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, "growing nodeset\n"); |
| return; |
| } |
| cur->nodeTab = temp; |
| } |
| cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); |
| } |
| |
| /** |
| * xmlXPathNodeSetAdd: |
| * @cur: the initial node set |
| * @val: a new xmlNodePtr |
| * |
| * add a new xmlNodePtr to an existing NodeSet |
| */ |
| void |
| xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
| int i; |
| |
| 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 wether namespace nodes should be looked at @@ */ |
| /* |
| * check against doublons |
| */ |
| 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 == NULL) { |
| xmlXPathErrMemory(NULL, "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, "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: |
| * @cur: the initial node set |
| * @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. |
| */ |
| 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 wether 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, "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, "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: |
| * @val1: the first NodeSet or NULL |
| * @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. |
| */ |
| xmlNodeSetPtr |
| xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
| int i, j, initNr, skip; |
| |
| if (val2 == NULL) return(val1); |
| if (val1 == NULL) { |
| val1 = xmlXPathNodeSetCreate(NULL); |
| } |
| |
| /* @@ with_ns to check wether namespace nodes should be looked at @@ */ |
| initNr = val1->nodeNr; |
| |
| for (i = 0;i < val2->nodeNr;i++) { |
| /* |
| * check against doublons |
| */ |
| 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, "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, "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: |
| * @val1: the first NodeSet or NULL |
| * @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 wether 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, "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, "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: |
| * @cur: the initial node set |
| * @val: an xmlNodePtr |
| * |
| * Removes an xmlNodePtr from an existing NodeSet |
| */ |
| void |
| xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { |
| int i; |
| |
| if (cur == NULL) return; |
| if (val == NULL) return; |
| |
| /* |
| * check against doublons |
| */ |
| for (i = 0;i < cur->nodeNr;i++) |
| if (cur->nodeTab[i] == val) break; |
| |
| if (i >= cur->nodeNr) { |
| #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: |
| * @cur: the initial node set |
| * @val: the index to remove |
| * |
| * Removes an entry from an existing NodeSet list. |
| */ |
| void |
| xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { |
| if (cur == NULL) return; |
| if (val >= cur->nodeNr) return; |
| if ((cur->nodeTab[val] != NULL) && |
| (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: |
| * @obj: the xmlNodeSetPtr to free |
| * |
| * Free the NodeSet compound (not the actual nodes !). |
| */ |
| void |
| xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { |
| if (obj == NULL) return; |
| if (obj->nodeTab != NULL) { |
| int i; |
| |
| /* @@ with_ns to check wether namespace nodes should be looked at @@ */ |
| for (i = 0;i < obj->nodeNr;i++) |
| if ((obj->nodeTab[i] != NULL) && |
| (obj->nodeTab[i]->type == XML_NAMESPACE_DECL)) |
| xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); |
| xmlFree(obj->nodeTab); |
| } |
| xmlFree(obj); |
| } |
| |
| /** |
| * xmlXPathFreeValueTree: |
| * @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 == NULL) return; |
| |
| if (obj->nodeTab != NULL) { |
| for (i = 0;i < obj->nodeNr;i++) { |
| if (obj->nodeTab[i] != NULL) { |
| 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: |
| * @output: a FILE * for the output |
| * @obj: the xmlNodeSetPtr to free |
| * |
| * 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: |
| * @val: the NodePtr value |
| * |
| * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
| * it with the single Node @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewNodeSet(xmlNodePtr val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating nodeset\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_NODESET; |
| ret->boolval = 0; |
| ret->nodesetval = xmlXPathNodeSetCreate(val); |
| /* @@ with_ns to check wether namespace nodes should be looked at @@ */ |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathNewValueTree: |
| * @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. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewValueTree(xmlNodePtr val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "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); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathNewNodeSetList: |
| * @val: an existing NodeSet |
| * |
| * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
| * it with the Nodeset @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewNodeSetList(xmlNodeSetPtr val) |
| { |
| xmlXPathObjectPtr ret; |
| int i; |
| |
| if (val == NULL) |
| ret = NULL; |
| else if (val->nodeTab == NULL) |
| 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: |
| * @val: the NodePtr value |
| * |
| * Wrap the Nodeset @val in a new xmlXPathObjectPtr |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathWrapNodeSet(xmlNodeSetPtr val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating node set object\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_NODESET; |
| ret->nodesetval = val; |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathFreeNodeSetList: |
| * @obj: an existing NodeSetList object |
| * |
| * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in |
| * the list contrary to xmlXPathFreeObject(). |
| */ |
| void |
| xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { |
| if (obj == NULL) return; |
| xmlFree(obj); |
| } |
| |
| /** |
| * xmlXPathDifference: |
| * @nodes1: a node-set |
| * @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 |
| */ |
| 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: |
| * @nodes1: a node-set |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
| xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL); |
| int i, l1; |
| xmlNodePtr cur; |
| |
| if (xmlXPathNodeSetIsEmpty(nodes1)) |
| return(ret); |
| if (xmlXPathNodeSetIsEmpty(nodes2)) |
| return(ret); |
| |
| l1 = xmlXPathNodeSetGetLength(nodes1); |
| |
| for (i = 0; i < l1; i++) { |
| cur = xmlXPathNodeSetItem(nodes1, i); |
| if (xmlXPathNodeSetContains(nodes2, cur)) |
| xmlXPathNodeSetAddUnique(ret, cur); |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathDistinctSorted: |
| * @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 |
| */ |
| 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: |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathDistinct (xmlNodeSetPtr nodes) { |
| if (xmlXPathNodeSetIsEmpty(nodes)) |
| return(nodes); |
| |
| xmlXPathNodeSetSort(nodes); |
| return(xmlXPathDistinctSorted(nodes)); |
| } |
| |
| /** |
| * xmlXPathHasSameNodes: |
| * @nodes1: a node-set |
| * @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 |
| */ |
| 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: |
| * @nodes: a node-set, sorted by document order |
| * @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 |
| */ |
| 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: |
| * @nodes: a node-set |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) { |
| xmlXPathNodeSetSort(nodes); |
| return(xmlXPathNodeLeadingSorted(nodes, node)); |
| } |
| |
| /** |
| * xmlXPathLeadingSorted: |
| * @nodes1: a node-set, sorted by document order |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
| if (xmlXPathNodeSetIsEmpty(nodes2)) |
| return(nodes1); |
| return(xmlXPathNodeLeadingSorted(nodes1, |
| xmlXPathNodeSetItem(nodes2, 1))); |
| } |
| |
| /** |
| * xmlXPathLeading: |
| * @nodes1: a node-set |
| * @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 |
| */ |
| 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: |
| * @nodes: a node-set, sorted by document order |
| * @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 |
| */ |
| 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: |
| * @nodes: a node-set |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) { |
| xmlXPathNodeSetSort(nodes); |
| return(xmlXPathNodeTrailingSorted(nodes, node)); |
| } |
| |
| /** |
| * xmlXPathTrailingSorted: |
| * @nodes1: a node-set, sorted by document order |
| * @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 |
| */ |
| xmlNodeSetPtr |
| xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
| if (xmlXPathNodeSetIsEmpty(nodes2)) |
| return(nodes1); |
| return(xmlXPathNodeTrailingSorted(nodes1, |
| xmlXPathNodeSetItem(nodes2, 0))); |
| } |
| |
| /** |
| * xmlXPathTrailing: |
| * @nodes1: a node-set |
| * @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 |
| */ |
| 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: |
| * @ctxt: the XPath context |
| * @name: the function name |
| * @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 |
| */ |
| int |
| xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, |
| xmlXPathFunction f) { |
| return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f)); |
| } |
| |
| /** |
| * xmlXPathRegisterFuncNS: |
| * @ctxt: the XPath context |
| * @name: the function name |
| * @ns_uri: the function namespace URI |
| * @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 |
| */ |
| int |
| xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
| const xmlChar *ns_uri, xmlXPathFunction f) { |
| if (ctxt == NULL) |
| return(-1); |
| if (name == NULL) |
| return(-1); |
| |
| if (ctxt->funcHash == NULL) |
| ctxt->funcHash = xmlHashCreate(0); |
| if (ctxt->funcHash == NULL) |
| return(-1); |
| if (f == NULL) |
| return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL)); |
| return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f)); |
| } |
| |
| /** |
| * xmlXPathRegisterFuncLookup: |
| * @ctxt: the XPath context |
| * @f: the lookup function |
| * @funcCtxt: the lookup data |
| * |
| * Registers an external mechanism to do function lookup. |
| */ |
| void |
| xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt, |
| xmlXPathFuncLookupFunc f, |
| void *funcCtxt) { |
| if (ctxt == NULL) |
| return; |
| ctxt->funcLookupFunc = (void *) f; |
| ctxt->funcLookupData = funcCtxt; |
| } |
| |
| /** |
| * xmlXPathFunctionLookup: |
| * @ctxt: the XPath context |
| * @name: the function name |
| * |
| * Search in the Function array of the context for the given |
| * function. |
| * |
| * Returns the xmlXPathFunction or NULL if not found |
| */ |
| xmlXPathFunction |
| xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { |
| if (ctxt == NULL) |
| return (NULL); |
| |
| if (ctxt->funcLookupFunc != NULL) { |
| xmlXPathFunction ret; |
| xmlXPathFuncLookupFunc f; |
| |
| f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
| ret = f(ctxt->funcLookupData, name, NULL); |
| if (ret != NULL) |
| return(ret); |
| } |
| return(xmlXPathFunctionLookupNS(ctxt, name, NULL)); |
| } |
| |
| /** |
| * xmlXPathFunctionLookupNS: |
| * @ctxt: the XPath context |
| * @name: the function name |
| * @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 |
| */ |
| xmlXPathFunction |
| xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
| const xmlChar *ns_uri) { |
| if (ctxt == NULL) |
| return(NULL); |
| if (name == NULL) |
| return(NULL); |
| |
| if (ctxt->funcLookupFunc != NULL) { |
| xmlXPathFunction ret; |
| xmlXPathFuncLookupFunc f; |
| |
| f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
| ret = f(ctxt->funcLookupData, name, ns_uri); |
| if (ret != NULL) |
| return(ret); |
| } |
| |
| if (ctxt->funcHash == NULL) |
| return(NULL); |
| |
| return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri)); |
| } |
| |
| /** |
| * xmlXPathRegisteredFuncsCleanup: |
| * @ctxt: the XPath context |
| * |
| * Cleanup the XPath context data associated to registered functions |
| */ |
| void |
| xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) { |
| if (ctxt == NULL) |
| return; |
| |
| xmlHashFree(ctxt->funcHash, NULL); |
| ctxt->funcHash = NULL; |
| } |
| |
| /************************************************************************ |
| * * |
| * Routines to handle Variable * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXPathRegisterVariable: |
| * @ctxt: the XPath context |
| * @name: the variable name |
| * @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 |
| */ |
| int |
| xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name, |
| xmlXPathObjectPtr value) { |
| return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value)); |
| } |
| |
| /** |
| * xmlXPathRegisterVariableNS: |
| * @ctxt: the XPath context |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * @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 |
| */ |
| 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: |
| * @ctxt: the XPath context |
| * @f: the lookup function |
| * @data: the lookup data |
| * |
| * register an external mechanism to do variable lookup |
| */ |
| void |
| xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt, |
| xmlXPathVariableLookupFunc f, void *data) { |
| if (ctxt == NULL) |
| return; |
| ctxt->varLookupFunc = (void *) f; |
| ctxt->varLookupData = data; |
| } |
| |
| /** |
| * xmlXPathVariableLookup: |
| * @ctxt: the XPath context |
| * @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 |
| */ |
| xmlXPathObjectPtr |
| xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { |
| if (ctxt == NULL) |
| return(NULL); |
| |
| if (ctxt->varLookupFunc != NULL) { |
| xmlXPathObjectPtr ret; |
| |
| ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc) |
| (ctxt->varLookupData, name, NULL); |
| return(ret); |
| } |
| return(xmlXPathVariableLookupNS(ctxt, name, NULL)); |
| } |
| |
| /** |
| * xmlXPathVariableLookupNS: |
| * @ctxt: the XPath context |
| * @name: the variable name |
| * @ns_uri: the variable namespace URI |
| * |
| * Search in the Variable array of the context for the given |
| * variable value. |
| * |
| * Returns the a copy of the value or NULL if not found |
| */ |
| xmlXPathObjectPtr |
| xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
| const xmlChar *ns_uri) { |
| if (ctxt == NULL) |
| return(NULL); |
| |
| if (ctxt->varLookupFunc != NULL) { |
| xmlXPathObjectPtr ret; |
| |
| ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc) |
| (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: |
| * @ctxt: the XPath context |
| * |
| * Cleanup the XPath context data associated to registered variables |
| */ |
| void |
| xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) { |
| if (ctxt == NULL) |
| return; |
| |
| xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject); |
| ctxt->varHash = NULL; |
| } |
| |
| /** |
| * xmlXPathRegisterNs: |
| * @ctxt: the XPath context |
| * @prefix: the namespace prefix |
| * @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 |
| */ |
| int |
| xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix, |
| const xmlChar *ns_uri) { |
| if (ctxt == NULL) |
| return(-1); |
| if (prefix == NULL) |
| return(-1); |
| |
| if (ctxt->nsHash == NULL) |
| ctxt->nsHash = xmlHashCreate(10); |
| if (ctxt->nsHash == NULL) |
| return(-1); |
| if (ns_uri == NULL) |
| return(xmlHashRemoveEntry(ctxt->nsHash, prefix, |
| (xmlHashDeallocator)xmlFree)); |
| return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri), |
| (xmlHashDeallocator)xmlFree)); |
| } |
| |
| /** |
| * xmlXPathNsLookup: |
| * @ctxt: the XPath context |
| * @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 |
| */ |
| const xmlChar * |
| xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) { |
| if (ctxt == NULL) |
| return(NULL); |
| if (prefix == NULL) |
| return(NULL); |
| |
| #ifdef XML_XML_NAMESPACE |
| if (xmlStrEqual(prefix, (const xmlChar *) "xml")) |
| return(XML_XML_NAMESPACE); |
| #endif |
| |
| if (ctxt->namespaces != NULL) { |
| int i; |
| |
| for (i = 0;i < ctxt->nsNr;i++) { |
| if ((ctxt->namespaces[i] != NULL) && |
| (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix))) |
| return(ctxt->namespaces[i]->href); |
| } |
| } |
| |
| return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix)); |
| } |
| |
| /** |
| * xmlXPathRegisteredNsCleanup: |
| * @ctxt: the XPath context |
| * |
| * Cleanup the XPath context data associated to registered variables |
| */ |
| void |
| xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) { |
| if (ctxt == NULL) |
| return; |
| |
| xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree); |
| ctxt->nsHash = NULL; |
| } |
| |
| /************************************************************************ |
| * * |
| * Routines to handle Values * |
| * * |
| ************************************************************************/ |
| |
| /* Allocations are terrible, one need to optimize all this !!! */ |
| |
| /** |
| * xmlXPathNewFloat: |
| * @val: the double value |
| * |
| * Create a new xmlXPathObjectPtr of type double and of value @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewFloat(double val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating float object\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_NUMBER; |
| ret->floatval = val; |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathNewBoolean: |
| * @val: the boolean value |
| * |
| * Create a new xmlXPathObjectPtr of type boolean and of value @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewBoolean(int val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating boolean object\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_BOOLEAN; |
| ret->boolval = (val != 0); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathNewString: |
| * @val: the xmlChar * value |
| * |
| * Create a new xmlXPathObjectPtr of type string and of value @val |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathNewString(const xmlChar *val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating string object\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_STRING; |
| if (val != NULL) |
| ret->stringval = xmlStrdup(val); |
| else |
| ret->stringval = xmlStrdup((const xmlChar *)""); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathWrapString: |
| * @val: the xmlChar * value |
| * |
| * Wraps the @val string into an XPath object. |
| * |
| * Returns the newly created object. |
| */ |
| xmlXPathObjectPtr |
| xmlXPathWrapString (xmlChar *val) { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating string object\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
| ret->type = XPATH_STRING; |
| ret->stringval = val; |
| |