/* * 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 Working Draft internal 5 July 1999 * http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html * Public reference: * http://www.w3.org/TR/WD-xpath/ * * See COPYRIGHT for the status of this software * * Author: Daniel.Veillard@w3.org */ #ifdef WIN32 #include "win32config.h" #else #include "config.h" #endif #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_MATH_H #include #endif #ifdef HAVE_MATH_H #include #endif #ifdef HAVE_IEEEFP_H #include #endif #ifdef HAVE_NAN_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #include "xmlmemory.h" #include "tree.h" #include "valid.h" #include "xpath.h" #include "parserInternals.h" /* #define DEBUG */ /* #define DEBUG_STEP */ /* #define DEBUG_EXPR */ /* * Setup stuff for floating point * The lack of portability of this section of the libc is annoying ! */ double xmlXPathNAN = 0; double xmlXPathPINF = 1; double xmlXPathMINF = -1; #ifndef isinf #ifndef HAVE_ISINF #if HAVE_FPCLASS int isinf(double d) { fpclass_t type = fpclass(d); switch (type) { case FP_NINF: return(-1); case FP_PINF: return(1); default: return(0); } return(0); } #elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D) #if HAVE_FP_CLASS_H #include #endif int isinf(double d) { #if HAVE_FP_CLASS int fpclass = fp_class(d); #else int fpclass = fp_class_d(d); #endif if (fpclass == FP_POS_INF) return(1); if (fpclass == FP_NEG_INF) return(-1); return(0); } #elif defined(HAVE_CLASS) int isinf(double d) { int fpclass = class(d); if (fpclass == FP_PLUS_INF) return(1); if (fpclass == FP_MINUS_INF) return(-1); return(0); } #elif defined(finite) || defined(HAVE_FINITE) int isinf(double x) { return !finite(x) && x==x; } #elif defined(HUGE_VAL) static int isinf(double x) { if (x == HUGE_VAL) return(1); if (x == -HUGE_VAL) return(-1); return(0); } #endif #endif /* ! HAVE_ISINF */ #endif /* ! defined(isinf) */ #ifndef isnan #ifndef HAVE_ISNAN #ifdef HAVE_ISNAND #define isnan(f) isnand(f) #endif /* HAVE_iSNAND */ #endif /* ! HAVE_iSNAN */ #endif /* ! defined(isnan) */ /** * xmlXPathInit: * * Initialize the XPath environment */ void xmlXPathInit(void) { static int initialized = 0; if (initialized) return; xmlXPathNAN = 0; xmlXPathNAN /= 0; xmlXPathPINF = 1; xmlXPathPINF /= 0; xmlXPathMINF = -1; xmlXPathMINF /= 0; initialized = 1; } FILE *xmlXPathDebug = NULL; #define TODO \ fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); #define STRANGE \ fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \ __FILE__, __LINE__); double xmlXPathStringEvalNumber(const xmlChar *str); void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); /************************************************************************ * * * Parser stacks related functions and macros * * * ************************************************************************/ /* * Generic function for accessing stacks in the Parser Context */ #define PUSH_AND_POP(type, name) \ extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \ if (ctxt->name##Nr >= ctxt->name##Max) { \ ctxt->name##Max *= 2; \ ctxt->name##Tab = (void *) xmlRealloc(ctxt->name##Tab, \ ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \ if (ctxt->name##Tab == NULL) { \ fprintf(xmlXPathDebug, "realloc failed !\n"); \ return(0); \ } \ } \ ctxt->name##Tab[ctxt->name##Nr] = value; \ ctxt->name = value; \ return(ctxt->name##Nr++); \ } \ extern type name##Pop(xmlXPathParserContextPtr ctxt) { \ type ret; \ if (ctxt->name##Nr <= 0) return(0); \ ctxt->name##Nr--; \ if (ctxt->name##Nr > 0) \ ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \ else \ ctxt->name = NULL; \ ret = ctxt->name##Tab[ctxt->name##Nr]; \ ctxt->name##Tab[ctxt->name##Nr] = 0; \ return(ret); \ } \ PUSH_AND_POP(xmlXPathObjectPtr, value) /* * 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 if compiled * in ISO-Latin or UTF-8, and the current 16 bit value if compiled * in UNICODE mode. 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 SKIP_BLANKS \ while (IS_BLANK(*(ctxt->cur))) NEXT #ifndef USE_UTF_8 #define CURRENT (*ctxt->cur) #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) #else #endif /************************************************************************ * * * Error handling routines * * * ************************************************************************/ #define XPATH_EXPRESSION_OK 0 #define XPATH_NUMBER_ERROR 1 #define XPATH_UNFINISHED_LITERAL_ERROR 2 #define XPATH_START_LITERAL_ERROR 3 #define XPATH_VARIABLE_REF_ERROR 4 #define XPATH_UNDEF_VARIABLE_ERROR 5 #define XPATH_INVALID_PREDICATE_ERROR 6 #define XPATH_EXPR_ERROR 7 #define XPATH_UNCLOSED_ERROR 8 #define XPATH_UNKNOWN_FUNC_ERROR 9 #define XPATH_INVALID_OPERAND 10 #define XPATH_INVALID_TYPE 11 #define XPATH_INVALID_ARITY 12 const char *xmlXPathErrorMessages[] = { "Ok", "Number encoding", "Unfinished litteral", "Start of litteral", "Expected $ for variable reference", "Undefined variable", "Invalid predicate", "Invalid expression", "Missing closing curly brace", "Unregistered function", "Invalid operand", "Invalid type", "Invalid number of arguments", }; /** * xmlXPathError: * @ctxt: the XPath Parser context * @file: the file name * @line: the line number * @no: the error number * * Create a new xmlNodeSetPtr of type double and of value @val * * Returns the newly created object. */ void xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file, int line, int no) { int n; const xmlChar *cur; const xmlChar *base; fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line, xmlXPathErrorMessages[no]); cur = ctxt->cur; base = ctxt->base; while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) { cur--; } n = 0; while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r')) cur--; if ((*cur == '\n') || (*cur == '\r')) cur++; base = cur; n = 0; while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) { fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++); n++; } fprintf(xmlXPathDebug, "\n"); cur = ctxt->cur; while ((*cur == '\n') || (*cur == '\r')) cur--; n = 0; while ((cur != base) && (n++ < 80)) { fprintf(xmlXPathDebug, " "); base++; } fprintf(xmlXPathDebug,"^\n"); } #define CHECK_ERROR \ if (ctxt->error != XPATH_EXPRESSION_OK) return #define ERROR(X) \ { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \ ctxt->error = (X); return; } #define ERROR0(X) \ { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \ ctxt->error = (X); return(0); } #define CHECK_TYPE(typeval) \ if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \ ERROR(XPATH_INVALID_TYPE) \ /************************************************************************ * * * Routines to handle NodeSets * * * ************************************************************************/ #define XML_NODESET_DEFAULT 10 /** * 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) { fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\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) { fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n"); return(NULL); } memset(ret->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); ret->nodeMax = XML_NODESET_DEFAULT; ret->nodeTab[ret->nodeNr++] = val; } return(ret); } /** * xmlXPathNodeSetAdd: * @cur: the initial node set * @val: a new xmlNodePtr * * add a new xmlNodePtr ot an existing NodeSet */ void xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { int i; if (val == NULL) return; /* * 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) { fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\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) { fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n"); return; } cur->nodeTab = temp; } cur->nodeTab[cur->nodeNr++] = val; } /** * xmlXPathNodeSetMerge: * @val1: the first NodeSet * @val2: the second NodeSet * * Merges two nodesets, all nodes from @val2 are added to @val1 * * Returns val1 once extended or NULL in case of error. */ xmlNodeSetPtr xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { int i; if (val1 == NULL) return(NULL); if (val2 == NULL) return(val1); /* * !!!!! this can be optimized a lot, knowing that both * val1 and val2 already have unicity of their values. */ for (i = 0;i < val2->nodeNr;i++) xmlXPathNodeSetAdd(val1, 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 fprintf(xmlXPathDebug, "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n", val->name); #endif return; } 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; 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) { #ifdef DEBUG memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax); #endif xmlFree(obj->nodeTab); } #ifdef DEBUG memset(obj, 0xB , (size_t) sizeof(xmlNodeSet)); #endif xmlFree(obj); } #if defined(DEBUG) || defined(DEBUG_STEP) /** * xmlXPathDebugNodeSet: * @output: a FILE * for the output * @obj: the xmlNodeSetPtr to free * * Quick display of a NodeSet */ void xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) { int i; if (output == NULL) output = xmlXPathDebug; 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 /************************************************************************ * * * Routines to handle Variable * * * * UNIMPLEMENTED CURRENTLY * * * ************************************************************************/ /** * xmlXPathVariablelookup: * @ctxt: the XPath Parser context * @prefix: the variable name namespace if any * @name: the variable name * * Search in the Variable array of the context for the given * variable value. * * UNIMPLEMENTED: always return NULL. * * Returns the value or NULL if not found */ xmlXPathObjectPtr xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt, const xmlChar *prefix, const xmlChar *name) { return(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) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\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) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\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) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_STRING; ret->stringval = xmlStrdup(val); return(ret); } /** * xmlXPathNewCString: * @val: the char * value * * Create a new xmlXPathObjectPtr of type string and of value @val * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPathNewCString(const char *val) { xmlXPathObjectPtr ret; ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_STRING; ret->stringval = xmlStrdup(BAD_CAST val); return(ret); } /** * 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) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_NODESET; 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; ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\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; #ifdef DEBUG memset(obj, 0xB , (size_t) sizeof(xmlXPathObject)); #endif xmlFree(obj); } /** * xmlXPathFreeObject: * @obj: the object to free * * Free up an xmlXPathObjectPtr object. */ void xmlXPathFreeObject(xmlXPathObjectPtr obj) { if (obj == NULL) return; if (obj->nodesetval != NULL) xmlXPathFreeNodeSet(obj->nodesetval); if (obj->stringval != NULL) xmlFree(obj->stringval); #ifdef DEBUG memset(obj, 0xB , (size_t) sizeof(xmlXPathObject)); #endif xmlFree(obj); } /************************************************************************ * * * Routines to handle XPath contexts * * * ************************************************************************/ /** * xmlXPathNewContext: * @doc: the XML document * * Create a new xmlXPathContext * * Returns the xmlXPathContext just allocated. */ xmlXPathContextPtr xmlXPathNewContext(xmlDocPtr doc) { xmlXPathContextPtr ret; ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathContext)); ret->doc = doc; /*********** ret->node = (xmlNodePtr) doc; ret->nodelist = xmlXPathNodeSetCreate(ret->node); ***********/ ret->node = NULL; ret->nodelist = NULL; ret->nb_variables = 0; ret->max_variables = 0; ret->variables = NULL; ret->nb_types = 0; ret->max_types = 0; ret->types = NULL; ret->nb_funcs = 0; ret->max_funcs = 0; ret->funcs = NULL; ret->nb_axis = 0; ret->max_axis = 0; ret->axis = NULL; ret->namespaces = NULL; ret->user = NULL; ret->nsNr = 0; return(ret); } /** * xmlXPathFreeContext: * @ctxt: the context to free * * Free up an xmlXPathContext */ void xmlXPathFreeContext(xmlXPathContextPtr ctxt) { if (ctxt->namespaces != NULL) xmlFree(ctxt->namespaces); /*********** if (ctxt->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->nodelist); ***********/ #ifdef DEBUG memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext)); #endif xmlFree(ctxt); } /************************************************************************ * * * Routines to handle XPath parser contexts * * * ************************************************************************/ #define CHECK_CTXT \ if (ctxt == NULL) { \ fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \ __FILE__, __LINE__); \ } \ #define CHECK_CONTEXT \ if (ctxt == NULL) { \ fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \ __FILE__, __LINE__); \ } \ if (ctxt->doc == NULL) { \ fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \ __FILE__, __LINE__); \ } \ if (ctxt->doc->root == NULL) { \ fprintf(xmlXPathDebug, \ "%s:%d Internal error: document without root\n", \ __FILE__, __LINE__); \ } \ /** * xmlXPathNewParserContext: * @str: the XPath expression * @ctxt: the XPath context * * Create a new xmlXPathParserContext * * Returns the xmlXPathParserContext just allocated. */ xmlXPathParserContextPtr xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) { xmlXPathParserContextPtr ret; ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); ret->cur = ret->base = str; ret->context = ctxt; /* Allocate the value stack */ ret->valueTab = (xmlXPathObjectPtr *) xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); ret->valueNr = 0; ret->valueMax = 10; ret->value = NULL; return(ret); } /** * xmlXPathFreeParserContext: * @ctxt: the context to free * * Free up an xmlXPathParserContext */ void xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) { if (ctxt->valueTab != NULL) { #ifdef DEBUG memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr)); #endif xmlFree(ctxt->valueTab); } #ifdef DEBUG memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext)); #endif xmlFree(ctxt); } /************************************************************************ * * * The implicit core function library * * * ************************************************************************/ /* * Auto-pop and cast to a number */ void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs); #define CHECK_ARITY(x) \ if (nargs != (x)) { \ ERROR(XPATH_INVALID_ARITY); \ } \ #define POP_FLOAT \ arg = valuePop(ctxt); \ if (arg == NULL) { \ ERROR(XPATH_INVALID_OPERAND); \ } \ if (arg->type != XPATH_NUMBER) { \ valuePush(ctxt, arg); \ xmlXPathNumberFunction(ctxt, 1); \ arg = valuePop(ctxt); \ } /** * xmlXPathEqualNodeSetString * @arg: the nodeset object argument * @str: the string to compare to. * * 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. */ int xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) { int i; xmlNodeSetPtr ns; xmlChar *str2; if ((str == NULL) || (arg == NULL) || (arg->type != XPATH_NODESET)) return(0); ns = arg->nodesetval; for (i = 0;i < ns->nodeNr;i++) { str2 = xmlNodeGetContent(ns->nodeTab[i]); if ((str2 != NULL) && (!xmlStrcmp(str, str2))) { xmlFree(str2); return(1); } xmlFree(str2); } return(0); } /** * xmlXPathEqualNodeSetFloat * @arg: the nodeset object argument * @f: the float to compare to * * 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. */ int xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) { char buf[100] = ""; if ((arg == NULL) || (arg->type != XPATH_NODESET)) return(0); if (isnan(f)) sprintf(buf, "NaN"); else if (isinf(f) > 0) sprintf(buf, "+Infinity"); else if (isinf(f) < 0) sprintf(buf, "-Infinity"); else sprintf(buf, "%0g", f); return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf)); } /** * xmlXPathEqualNodeSets * @arg1: first nodeset object argument * @arg2: second nodeset object argument * * Implement the equal operation on XPath nodesets: @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. */ int xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { int i; xmlNodeSetPtr ns; xmlChar *str; if ((arg1 == NULL) || (arg1->type != XPATH_NODESET)) return(0); if ((arg2 == NULL) || (arg2->type != XPATH_NODESET)) return(0); ns = arg1->nodesetval; for (i = 0;i < ns->nodeNr;i++) { str = xmlNodeGetContent(ns->nodeTab[i]); if ((str != NULL) && (xmlXPathEqualNodeSetString(arg2, str))) { xmlFree(str); return(1); } xmlFree(str); } return(0); } /** * xmlXPathEqualValues: * @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. */ int xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg1, arg2; int ret = 0; arg1 = valuePop(ctxt); if (arg1 == NULL) ERROR0(XPATH_INVALID_OPERAND); arg2 = valuePop(ctxt); if (arg2 == NULL) { xmlXPathFreeObject(arg1); ERROR0(XPATH_INVALID_OPERAND); } if (arg1 == arg2) { #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: by pointer\n"); #endif return(1); } switch (arg1->type) { case XPATH_UNDEFINED: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: undefined\n"); #endif break; case XPATH_NODESET: switch (arg2->type) { case XPATH_UNDEFINED: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: undefined\n"); #endif break; case XPATH_NODESET: ret = xmlXPathEqualNodeSets(arg1, arg2); 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(arg1, arg2->floatval); break; case XPATH_STRING: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval); break; } break; case XPATH_BOOLEAN: switch (arg2->type) { case XPATH_UNDEFINED: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: undefined\n"); #endif break; case XPATH_NODESET: if ((arg2->nodesetval == NULL) || (arg2->nodesetval->nodeNr == 0)) ret = 0; else ret = 1; break; case XPATH_BOOLEAN: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: %d boolean %d \n", arg1->boolval, arg2->boolval); #endif ret = (arg1->boolval == arg2->boolval); break; case XPATH_NUMBER: if (arg2->floatval) ret = 1; else ret = 0; ret = (arg1->boolval == ret); break; case XPATH_STRING: if ((arg2->stringval == NULL) || (arg2->stringval[0] == 0)) ret = 0; else ret = 1; ret = (arg1->boolval == ret); break; } break; case XPATH_NUMBER: switch (arg2->type) { case XPATH_UNDEFINED: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: undefined\n"); #endif break; case XPATH_NODESET: ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval); break; case XPATH_BOOLEAN: if (arg1->floatval) ret = 1; else ret = 0; ret = (arg2->boolval == ret); break; case XPATH_STRING: valuePush(ctxt, arg2); xmlXPathNumberFunction(ctxt, 1); arg2 = valuePop(ctxt); /* no break on purpose */ case XPATH_NUMBER: ret = (arg1->floatval == arg2->floatval); break; } break; case XPATH_STRING: switch (arg2->type) { case XPATH_UNDEFINED: #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Equal: undefined\n"); #endif break; case XPATH_NODESET: ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval); 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 = !xmlStrcmp(arg1->stringval, arg2->stringval); break; case XPATH_NUMBER: valuePush(ctxt, arg1); xmlXPathNumberFunction(ctxt, 1); arg1 = valuePop(ctxt); ret = (arg1->floatval == arg2->floatval); break; } break; } xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); return(ret); } /** * xmlXPathCompareValues: * @ctxt: the XPath Parser context * @inf: less than (1) or greater than (2) * @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. */ int xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { int ret = 0; xmlXPathObjectPtr arg1, arg2; arg2 = valuePop(ctxt); if ((arg2 == NULL) || (arg2->type == XPATH_NODESET)) { if (arg2 != NULL) xmlXPathFreeObject(arg2); ERROR0(XPATH_INVALID_OPERAND); } arg1 = valuePop(ctxt); if ((arg1 == NULL) || (arg1->type == XPATH_NODESET)) { if (arg1 != NULL) xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); ERROR0(XPATH_INVALID_OPERAND); } if (arg1->type != XPATH_NUMBER) { valuePush(ctxt, arg1); xmlXPathNumberFunction(ctxt, 1); arg1 = valuePop(ctxt); } if (arg1->type != XPATH_NUMBER) { xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); 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); ERROR0(XPATH_INVALID_OPERAND); } /* * Add tests for infinity and nan * => feedback on 3.4 for Inf and NaN */ if (inf && strict) ret = (arg1->floatval < arg2->floatval); else if (inf && !strict) ret = (arg1->floatval <= arg2->floatval); else if (!inf && strict) ret = (arg1->floatval > arg2->floatval); else if (!inf && !strict) ret = (arg1->floatval >= arg2->floatval); xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); return(ret); } /** * xmlXPathValueFlipSign: * @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. */ void xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; POP_FLOAT arg->floatval = -arg->floatval; valuePush(ctxt, arg); } /** * xmlXPathAddValues: * @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. */ void xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; double val; POP_FLOAT val = arg->floatval; xmlXPathFreeObject(arg); POP_FLOAT arg->floatval += val; valuePush(ctxt, arg); } /** * xmlXPathSubValues: * @ctxt: the XPath Parser context * * Implement the substraction operation on XPath objects: * The numeric operators convert their operands to numbers as if * by calling the number function. */ void xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; double val; POP_FLOAT val = arg->floatval; xmlXPathFreeObject(arg); POP_FLOAT arg->floatval -= val; valuePush(ctxt, arg); } /** * xmlXPathMultValues: * @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. */ void xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; double val; POP_FLOAT val = arg->floatval; xmlXPathFreeObject(arg); POP_FLOAT arg->floatval *= val; valuePush(ctxt, arg); } /** * xmlXPathDivValues: * @ctxt: the XPath Parser context * * Implement the div operation on XPath objects: * The numeric operators convert their operands to numbers as if * by calling the number function. */ void xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; double val; POP_FLOAT val = arg->floatval; xmlXPathFreeObject(arg); POP_FLOAT arg->floatval /= val; valuePush(ctxt, arg); } /** * xmlXPathModValues: * @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. */ void xmlXPathModValues(xmlXPathParserContextPtr ctxt) { xmlXPathObjectPtr arg; double val; POP_FLOAT val = arg->floatval; xmlXPathFreeObject(arg); POP_FLOAT arg->floatval /= val; valuePush(ctxt, arg); } /************************************************************************ * * * The traversal functions * * * ************************************************************************/ #define AXIS_ANCESTOR 1 #define AXIS_ANCESTOR_OR_SELF 2 #define AXIS_ATTRIBUTE 3 #define AXIS_CHILD 4 #define AXIS_DESCENDANT 5 #define AXIS_DESCENDANT_OR_SELF 6 #define AXIS_FOLLOWING 7 #define AXIS_FOLLOWING_SIBLING 8 #define AXIS_NAMESPACE 9 #define AXIS_PARENT 10 #define AXIS_PRECEDING 11 #define AXIS_PRECEDING_SIBLING 12 #define AXIS_SELF 13 /* * 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); /** * mlXPathNextSelf: * @ctxt: the XPath Parser context * @cur: the current node in the traversal * * Traversal function for the "self" direction * he self axis contains just the context node itself * * Returns the next element following that axis */ xmlNodePtr xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == NULL) return(ctxt->context->node); return(NULL); } /** * mlXPathNextChild: * @ctxt: the XPath Parser context * @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 */ 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: return(ctxt->context->node->childs); case XML_ATTRIBUTE_NODE: return(NULL); case XML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: return(((xmlDocPtr) ctxt->context->node)->root); } return(NULL); } if ((cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) return(NULL); return(cur->next); } /** * mlXPathNextDescendant: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == NULL) { if (ctxt->context->node == NULL) return(NULL); if (ctxt->context->node->type == XML_ATTRIBUTE_NODE) return(NULL); if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) return(ctxt->context->doc->root); return(ctxt->context->node->childs); } if (cur->childs != NULL) return(cur->childs); if (cur->next != NULL) return(cur->next); 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); } /** * mlXPathNextDescendantOrSelf: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == NULL) { if (ctxt->context->node == NULL) return(NULL); if (ctxt->context->node->type == XML_ATTRIBUTE_NODE) return(NULL); return(ctxt->context->node); } return(xmlXPathNextDescendant(ctxt, cur)); } /** * xmlXPathNextParent: * @ctxt: the XPath Parser context * @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 */ 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: if (ctxt->context->node->parent == NULL) return((xmlNodePtr) ctxt->context->doc); return(ctxt->context->node->parent); case XML_ATTRIBUTE_NODE: { xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; return(att->node); } case XML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: return(NULL); } } return(NULL); } /** * xmlXPathNextAncestor: * @ctxt: the XPath Parser context * @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 */ 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_NOTATION_NODE: if (ctxt->context->node->parent == NULL) return((xmlNodePtr) ctxt->context->doc); return(ctxt->context->node->parent); case XML_ATTRIBUTE_NODE: { xmlAttrPtr cur = (xmlAttrPtr) ctxt->context->node; return(cur->node); } case XML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: return(NULL); } return(NULL); } if (cur == ctxt->context->doc->root) 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: return(cur->parent); case XML_ATTRIBUTE_NODE: { xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; return(att->node); } case XML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: return(NULL); } return(NULL); } /** * xmlXPathNextAncestorOrSelf: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == NULL) return(ctxt->context->node); return(xmlXPathNextAncestor(ctxt, cur)); } /** * xmlXPathNextFollowingSibling: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); if (cur == NULL) return(ctxt->context->node->next); return(cur->next); } /** * xmlXPathNextPrecedingSibling: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); if (cur == NULL) return(ctxt->context->node->prev); return(cur->prev); } /** * xmlXPathNextFollowing: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); if (cur == NULL) return(ctxt->context->node->next);; /* !!!!!!!!! */ if (cur->childs != NULL) return(cur->childs); if (cur->next != NULL) return(cur->next); do { cur = cur->parent; if (cur == NULL) return(NULL); if (cur == ctxt->context->doc->root) return(NULL); if (cur->next != NULL) { cur = cur->next; return(cur); } } while (cur != NULL); return(cur); } /** * xmlXPathNextPreceding: * @ctxt: the XPath Parser context * @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 */ xmlNodePtr xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); if (cur == NULL) return(ctxt->context->node->prev); /* !!!!!!!!! */ if (cur->last != NULL) return(cur->last); if (cur->prev != NULL) return(cur->prev); do { cur = cur->parent; if (cur == NULL) return(NULL); if (cur == ctxt->context->doc->root) return(NULL); if (cur->prev != NULL) { cur = cur->prev; return(cur); } } while (cur != NULL); return(cur); } /** * xmlXPathNextNamespace: * @ctxt: the XPath Parser context * @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 * * Returns the next element following that axis */ xmlNsPtr xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) { if ((cur == NULL) || (ctxt->context->namespaces == NULL)) { if (ctxt->context->namespaces != NULL) xmlFree(ctxt->context->namespaces); ctxt->context->namespaces = xmlGetNsList(ctxt->context->doc, ctxt->context->node); if (ctxt->context->namespaces == NULL) return(NULL); ctxt->context->nsNr = 0; } return(ctxt->context->namespaces[ctxt->context->nsNr++]); } /** * xmlXPathNextAttribute: * @ctxt: the XPath Parser context * @cur: the current attribute in the traversal * * Traversal function for the "attribute" direction * TODO: support DTD inherited default attributes * * Returns the next element following that axis */ xmlAttrPtr xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) { if (cur == NULL) { if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) return(NULL); return(ctxt->context->node->properties); } return(cur->next); } /************************************************************************ * * * NodeTest Functions * * * ************************************************************************/ #define NODE_TEST_NONE 0 #define NODE_TEST_TYPE 1 #define NODE_TEST_PI 2 #define NODE_TEST_ALL 3 #define NODE_TEST_NS 4 #define NODE_TEST_NAME 5 #define NODE_TYPE_COMMENT 50 #define NODE_TYPE_TEXT 51 #define NODE_TYPE_PI 52 #define NODE_TYPE_NODE 53 #define IS_FUNCTION 200 /** * xmlXPathNodeCollectAndTest: * @ctxt: the XPath Parser context * @cur: the current node to test * * 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. * * Returns the new NodeSet resulting from the search. */ xmlNodeSetPtr xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis, int test, int type, const xmlChar *prefix, const xmlChar *name) { #ifdef DEBUG_STEP int n = 0, t = 0; #endif int i; xmlNodeSetPtr ret; xmlXPathTraversalFunction next = NULL; xmlNodePtr cur = NULL; if (ctxt->context->nodelist == NULL) { if (ctxt->context->node == NULL) { fprintf(xmlXPathDebug, "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n", __FILE__, __LINE__); return(NULL); } STRANGE return(NULL); } #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "new step : "); #endif switch (axis) { case AXIS_ANCESTOR: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'ancestors' "); #endif next = xmlXPathNextAncestor; break; case AXIS_ANCESTOR_OR_SELF: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'ancestors-or-self' "); #endif next = xmlXPathNextAncestorOrSelf; break; case AXIS_ATTRIBUTE: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'attributes' "); #endif next = (xmlXPathTraversalFunction) xmlXPathNextAttribute; break; break; case AXIS_CHILD: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'child' "); #endif next = xmlXPathNextChild; break; case AXIS_DESCENDANT: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'descendant' "); #endif next = xmlXPathNextDescendant; break; case AXIS_DESCENDANT_OR_SELF: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'descendant-or-self' "); #endif next = xmlXPathNextDescendantOrSelf; break; case AXIS_FOLLOWING: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'following' "); #endif next = xmlXPathNextFollowing; break; case AXIS_FOLLOWING_SIBLING: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'following-siblings' "); #endif next = xmlXPathNextFollowingSibling; break; case AXIS_NAMESPACE: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'namespace' "); #endif next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break; break; case AXIS_PARENT: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'parent' "); #endif next = xmlXPathNextParent; break; case AXIS_PRECEDING: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'preceding' "); #endif next = xmlXPathNextPreceding; break; case AXIS_PRECEDING_SIBLING: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'preceding-sibling' "); #endif next = xmlXPathNextPrecedingSibling; break; case AXIS_SELF: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "axis 'self' "); #endif next = xmlXPathNextSelf; break; } if (next == NULL) return(NULL); ret = xmlXPathNodeSetCreate(NULL); #ifdef DEBUG_STEP fprintf(xmlXPathDebug, " context contains %d nodes\n", ctxt->context->nodelist->nodeNr); switch (test) { case NODE_TEST_NONE: fprintf(xmlXPathDebug, " searching for none !!!\n"); break; case NODE_TEST_TYPE: fprintf(xmlXPathDebug, " searching for type %d\n", type); break; case NODE_TEST_PI: fprintf(xmlXPathDebug, " searching for PI !!!\n"); break; case NODE_TEST_ALL: fprintf(xmlXPathDebug, " searching for *\n"); break; case NODE_TEST_NS: fprintf(xmlXPathDebug, " searching for namespace %s\n", prefix); break; case NODE_TEST_NAME: fprintf(xmlXPathDebug, " searching for name %s\n", name); if (prefix != NULL) fprintf(xmlXPathDebug, " with namespace %s\n", prefix); break; } fprintf(xmlXPathDebug, "Testing : "); #endif for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) { ctxt->context->node = ctxt->context->nodelist->nodeTab[i]; cur = NULL; do { cur = next(ctxt, cur); if (cur == NULL) break; #ifdef DEBUG_STEP t++; fprintf(xmlXPathDebug, " %s", cur->name); #endif switch (test) { case NODE_TEST_NONE: STRANGE return(NULL); case NODE_TEST_TYPE: if ((cur->type == type) || ((type == XML_ELEMENT_NODE) && ((cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)))) { #ifdef DEBUG_STEP n++; #endif xmlXPathNodeSetAdd(ret, cur); } break; case NODE_TEST_PI: if (cur->type == XML_PI_NODE) { if ((name != NULL) && (xmlStrcmp(name, cur->name))) break; #ifdef DEBUG_STEP n++; #endif xmlXPathNodeSetAdd(ret, cur); } break; case NODE_TEST_ALL: if ((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_ATTRIBUTE_NODE)) { /* !!! || (cur->type == XML_TEXT_NODE)) { */ #ifdef DEBUG_STEP n++; #endif xmlXPathNodeSetAdd(ret, cur); } break; case NODE_TEST_NS: { TODO /* namespace search */ break; } case NODE_TEST_NAME: switch (cur->type) { case XML_ELEMENT_NODE: if (!xmlStrcmp(name, cur->name) && (((prefix == NULL) || ((cur->ns != NULL) && (!xmlStrcmp(prefix, cur->ns->href)))))) { #ifdef DEBUG_STEP n++; #endif xmlXPathNodeSetAdd(ret, cur); } break; case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) cur; if (!xmlStrcmp(name, attr->name)) { #ifdef DEBUG_STEP n++; #endif xmlXPathNodeSetAdd(ret, cur); } break; } default: break; } break; } } while (cur != NULL); } #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "\nExamined %d nodes, found %d nodes at that step\n", t, n); #endif return(ret); } /************************************************************************ * * * Implicit tree core function library * * * ************************************************************************/ /** * xmlXPathRoot: * @ctxt: the XPath Parser context * * Initialize the context to the root of the document */ void xmlXPathRoot(xmlXPathParserContextPtr ctxt) { if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->node = (xmlNodePtr) ctxt->context->doc; ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node); } /************************************************************************ * * * The explicit core function library * *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib * * * ************************************************************************/ /** * xmlXPathLastFunction: * @ctxt: the XPath Parser context * * Implement the last() XPath function * The last function returns the number of nodes in the context node list. */ void xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); if ((ctxt->context->nodelist == NULL) || (ctxt->context->node == NULL) || (ctxt->context->nodelist->nodeNr == 0)) { valuePush(ctxt, xmlXPathNewFloat((double) 0)); } else { valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr)); } } /** * xmlXPathPositionFunction: * @ctxt: the XPath Parser context * * Implement the position() XPath function * The position function returns the position of the context node in the * context node list. The first position is 1, and so the last positionr * will be equal to last(). */ void xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { int i; CHECK_ARITY(0); if ((ctxt->context->nodelist == NULL) || (ctxt->context->node == NULL) || (ctxt->context->nodelist->nodeNr == 0)) { valuePush(ctxt, xmlXPathNewFloat((double) 0)); } for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) { if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) { valuePush(ctxt, xmlXPathNewFloat((double) i + 1)); return; } } valuePush(ctxt, xmlXPathNewFloat((double) 0)); } /** * xmlXPathCountFunction: * @ctxt: the XPath Parser context * * Implement the count() XPath function */ void xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; CHECK_ARITY(1); CHECK_TYPE(XPATH_NODESET); cur = valuePop(ctxt); valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr)); xmlXPathFreeObject(cur); } /** * xmlXPathIdFunction: * @ctxt: the XPath Parser context * * Implement the id() XPath function * 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. */ void xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { const xmlChar *tokens; const xmlChar *cur; xmlChar *ID; xmlAttrPtr attr; xmlNodePtr elem = NULL; xmlXPathObjectPtr ret, obj; CHECK_ARITY(1); obj = valuePop(ctxt); if (obj == NULL) ERROR(XPATH_INVALID_OPERAND); if (obj->type == XPATH_NODESET) { TODO /* ID function in case of NodeSet */ } if (obj->type != XPATH_STRING) { valuePush(ctxt, obj); xmlXPathStringFunction(ctxt, 1); obj = valuePop(ctxt); if (obj->type != XPATH_STRING) { xmlXPathFreeObject(obj); return; } } tokens = obj->stringval; ret = xmlXPathNewNodeSet(NULL); valuePush(ctxt, ret); if (tokens == NULL) { xmlXPathFreeObject(obj); return; } cur = tokens; while (IS_BLANK(*cur)) cur++; while (*cur != 0) { while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) || (*cur == '.') || (*cur == '-') || (*cur == '_') || (*cur == ':') || (IS_COMBINING(*cur)) || (IS_EXTENDER(*cur))) cur++; if ((!IS_BLANK(*cur)) && (*cur != 0)) break; ID = xmlStrndup(tokens, cur - tokens); attr = xmlGetID(ctxt->context->doc, ID); if (attr != NULL) { elem = attr->node; xmlXPathNodeSetAdd(ret->nodesetval, elem); } if (ID != NULL) xmlFree(ID); while (IS_BLANK(*cur)) cur++; tokens = cur; } xmlXPathFreeObject(obj); return; } /** * xmlXPathLocalPartFunction: * @ctxt: the XPath Parser context * * Implement the local-part() XPath function * The local-part 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. */ void xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; CHECK_ARITY(1); CHECK_TYPE(XPATH_NODESET); cur = valuePop(ctxt); if (cur->nodesetval->nodeNr == 0) { valuePush(ctxt, xmlXPathNewCString("")); } else { int i = 0; /* Should be first in document order !!!!! */ valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); } xmlXPathFreeObject(cur); } /** * xmlXPathNamespaceFunction: * @ctxt: the XPath Parser context * * Implement the namespace() XPath function * The namespace 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. */ void xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; if (nargs == 0) { valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); nargs = 1; } CHECK_ARITY(1); CHECK_TYPE(XPATH_NODESET); cur = valuePop(ctxt); if (cur->nodesetval->nodeNr == 0) { valuePush(ctxt, xmlXPathNewCString("")); } else { int i = 0; /* Should be first in document order !!!!! */ if (cur->nodesetval->nodeTab[i]->ns == NULL) valuePush(ctxt, xmlXPathNewCString("")); else valuePush(ctxt, xmlXPathNewString( cur->nodesetval->nodeTab[i]->ns->href)); } xmlXPathFreeObject(cur); } /** * xmlXPathNameFunction: * @ctxt: the XPath Parser context * * Implement the name() XPath function * The name function returns a string containing a QName representing * the name of the node in the argument node-set that is first in documenti * 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. */ void xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; CHECK_ARITY(1); CHECK_TYPE(XPATH_NODESET); cur = valuePop(ctxt); if (cur->nodesetval->nodeNr == 0) { valuePush(ctxt, xmlXPathNewCString("")); } else { int i = 0; /* Should be first in document order !!!!! */ if (cur->nodesetval->nodeTab[i]->ns == NULL) valuePush(ctxt, xmlXPathNewString( cur->nodesetval->nodeTab[i]->name)); else { char name[2000]; sprintf(name, "%s:%s", (char *) cur->nodesetval->nodeTab[i]->ns->prefix, (char *) cur->nodesetval->nodeTab[i]->name); valuePush(ctxt, xmlXPathNewCString(name)); } } xmlXPathFreeObject(cur); } /** * xmlXPathStringFunction: * @ctxt: the XPath Parser context * * Implement the string() XPath function * he 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 immediatelyi * 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. */ void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; CHECK_ARITY(1); cur = valuePop(ctxt); if (cur == NULL) ERROR(XPATH_INVALID_OPERAND); switch (cur->type) { case XPATH_NODESET: if (cur->nodesetval->nodeNr == 0) { valuePush(ctxt, xmlXPathNewCString("")); } else { xmlChar *res; int i = 0; /* Should be first in document order !!!!! */ res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]); valuePush(ctxt, xmlXPathNewString(res)); xmlFree(res); } xmlXPathFreeObject(cur); return; case XPATH_STRING: valuePush(ctxt, cur); return; case XPATH_BOOLEAN: if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true")); else valuePush(ctxt, xmlXPathNewCString("false")); xmlXPathFreeObject(cur); return; case XPATH_NUMBER: { char buf[100]; if (isnan(cur->floatval)) sprintf(buf, "NaN"); else if (isinf(cur->floatval) > 0) sprintf(buf, "+Infinity"); else if (isinf(cur->floatval) < 0) sprintf(buf, "-Infinity"); else sprintf(buf, "%0g", cur->floatval); valuePush(ctxt, xmlXPathNewCString(buf)); xmlXPathFreeObject(cur); return; } } STRANGE } /** * xmlXPathStringLengthFunction: * @ctxt: the XPath Parser context * * Implement the string-length() XPath function * 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. */ void xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; if (nargs == 0) { if (ctxt->context->node == NULL) { valuePush(ctxt, xmlXPathNewFloat(0)); } else { xmlChar *content; content = xmlNodeGetContent(ctxt->context->node); valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content))); xmlFree(content); } return; } CHECK_ARITY(1); CHECK_TYPE(XPATH_STRING); cur = valuePop(ctxt); valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval))); xmlXPathFreeObject(cur); } /** * xmlXPathConcatFunction: * @ctxt: the XPath Parser context * * Implement the concat() XPath function * The concat function returns the concatenation of its arguments. */ void xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur, new; xmlChar *tmp; if (nargs < 2) { CHECK_ARITY(2); } cur = valuePop(ctxt); if ((cur == NULL) || (cur->type != XPATH_STRING)) { xmlXPathFreeObject(cur); return; } nargs--; while (nargs > 0) { new = valuePop(ctxt); if ((new == NULL) || (new->type != XPATH_STRING)) { xmlXPathFreeObject(new); xmlXPathFreeObject(cur); ERROR(XPATH_INVALID_TYPE); } tmp = xmlStrcat(new->stringval, cur->stringval); new->stringval = cur->stringval; cur->stringval = tmp; xmlXPathFreeObject(new); nargs--; } valuePush(ctxt, cur); } /** * xmlXPathContainsFunction: * @ctxt: the XPath Parser context * * Implement the contains() XPath function * The contains function returns true if the first argument string * contains the second argument string, and otherwise returns false. */ void xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr hay, needle; CHECK_ARITY(2); CHECK_TYPE(XPATH_STRING); needle = valuePop(ctxt); hay = valuePop(ctxt); if ((hay == NULL) || (hay->type != XPATH_STRING)) { xmlXPathFreeObject(hay); xmlXPathFreeObject(needle); ERROR(XPATH_INVALID_TYPE); } if (xmlStrstr(hay->stringval, needle->stringval)) valuePush(ctxt, xmlXPathNewBoolean(1)); else valuePush(ctxt, xmlXPathNewBoolean(0)); xmlXPathFreeObject(hay); xmlXPathFreeObject(needle); } /** * xmlXPathStartsWithFunction: * @ctxt: the XPath Parser context * * Implement the starts-with() XPath function * The starts-with function returns true if the first argument string * starts with the second argument string, and otherwise returns false. */ void xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr hay, needle; int n; CHECK_ARITY(2); CHECK_TYPE(XPATH_STRING); needle = valuePop(ctxt); hay = valuePop(ctxt); if ((hay == NULL) || (hay->type != XPATH_STRING)) { xmlXPathFreeObject(hay); xmlXPathFreeObject(needle); 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: * @ctxt: the XPath Parser context * * Implement the substring() XPath function * 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 "" */ void xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr str, start, len; double le, in; int i, l; xmlChar *ret; /* * Conformance needs to be checked !!!!! */ if (nargs < 2) { CHECK_ARITY(2); } if (nargs > 3) { CHECK_ARITY(3); } if (nargs == 3) { CHECK_TYPE(XPATH_NUMBER); len = valuePop(ctxt); le = len->floatval; xmlXPathFreeObject(len); } else { le = 2000000000; } CHECK_TYPE(XPATH_NUMBER); start = valuePop(ctxt); in = start->floatval; xmlXPathFreeObject(start); CHECK_TYPE(XPATH_STRING); str = valuePop(ctxt); le += in; /* integer index of the first char */ i = in; if (((double)i) != in) i++; /* integer index of the last char */ l = le; if (((double)l) != le) l++; /* back to a zero based len */ i--; l--; /* check against the string len */ if (l > 1024) { l = xmlStrlen(str->stringval); } if (i < 0) { i = 0; } /* number of chars to copy */ l -= i; ret = xmlStrsub(str->stringval, i, l); if (ret == NULL) valuePush(ctxt, xmlXPathNewCString("")); else { valuePush(ctxt, xmlXPathNewString(ret)); xmlFree(ret); } xmlXPathFreeObject(str); } /** * xmlXPathSubstringBeforeFunction: * @ctxt: the XPath Parser context * * Implement the substring-before() XPath function * 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. */ void xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(2); TODO /* substring before */ } /** * xmlXPathSubstringAfterFunction: * @ctxt: the XPath Parser context * * Implement the substring-after() XPath function * 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. */ void xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(2); TODO /* substring after */ } /** * xmlXPathNormalizeFunction: * @ctxt: the XPath Parser context * * Implement the normalize() XPath function * The normalize 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. */ void xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); TODO /* normalize isn't as boring as translate, but pretty much */ } /** * xmlXPathTranslateFunction: * @ctxt: the XPath Parser context * * Implement the translate() XPath function * 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. */ void xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(3); TODO /* translate is boring, waiting for UTF-8 representation too */ } /** * xmlXPathBooleanFunction: * @ctxt: the XPath Parser context * * Implement the boolean() XPath function * he 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 */ void xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; int res = 0; CHECK_ARITY(1); cur = valuePop(ctxt); if (cur == NULL) ERROR(XPATH_INVALID_OPERAND); switch (cur->type) { case XPATH_NODESET: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) res = 0; else res = 1; break; case XPATH_STRING: if ((cur->stringval == NULL) || (cur->stringval[0] == 0)) res = 0; else res = 1; break; case XPATH_BOOLEAN: valuePush(ctxt, cur); return; case XPATH_NUMBER: if (cur->floatval) res = 1; break; default: STRANGE } xmlXPathFreeObject(cur); valuePush(ctxt, xmlXPathNewBoolean(res)); } /** * xmlXPathNotFunction: * @ctxt: the XPath Parser context * * Implement the not() XPath function * The not function returns true if its argument is false, * and false otherwise. */ void xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); CHECK_TYPE(XPATH_BOOLEAN); ctxt->value->boolval = ! ctxt->value->boolval; } /** * xmlXPathTrueFunction: * @ctxt: the XPath Parser context * * Implement the true() XPath function */ void xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); valuePush(ctxt, xmlXPathNewBoolean(1)); } /** * xmlXPathFalseFunction: * @ctxt: the XPath Parser context * * Implement the false() XPath function */ void xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); valuePush(ctxt, xmlXPathNewBoolean(0)); } /** * xmlXPathLangFunction: * @ctxt: the XPath Parser context * * Implement the lang() XPath function * 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. */ void xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr val; const xmlChar *theLang; const xmlChar *lang; int ret = 0; int i; CHECK_ARITY(1); 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: xmlXPathFreeObject(val); valuePush(ctxt, xmlXPathNewBoolean(ret)); } /** * xmlXPathNumberFunction: * @ctxt: the XPath Parser context * * Implement the number() XPath function */ void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr cur; double res; CHECK_ARITY(1); cur = valuePop(ctxt); switch (cur->type) { case XPATH_NODESET: valuePush(ctxt, cur); xmlXPathStringFunction(ctxt, 1); cur = valuePop(ctxt); case XPATH_STRING: res = xmlXPathStringEvalNumber(cur->stringval); valuePush(ctxt, xmlXPathNewFloat(res)); xmlXPathFreeObject(cur); return; case XPATH_BOOLEAN: if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0)); else valuePush(ctxt, xmlXPathNewFloat(0.0)); xmlXPathFreeObject(cur); return; case XPATH_NUMBER: valuePush(ctxt, cur); return; } STRANGE } /** * xmlXPathSumFunction: * @ctxt: the XPath Parser context * * Implement the sum() XPath function * The sum function returns the sum of the values of the nodes in * the argument node-set. */ void xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); TODO /* BUG Sum : don't understand the definition */ } /** * xmlXPathFloorFunction: * @ctxt: the XPath Parser context * * Implement the floor() XPath function * The floor function returns the largest (closest to positive infinity) * number that is not greater than the argument and that is an integer. */ void xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); CHECK_TYPE(XPATH_NUMBER); /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */ ctxt->value->floatval = (double)((int) ctxt->value->floatval); } /** * xmlXPathCeilingFunction: * @ctxt: the XPath Parser context * * Implement the ceiling() XPath function * The ceiling function returns the smallest (closest to negative infinity) * number that is not less than the argument and that is an integer. */ void xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { double f; CHECK_ARITY(1); CHECK_TYPE(XPATH_NUMBER); f = (double)((int) ctxt->value->floatval); if (f != ctxt->value->floatval) ctxt->value->floatval = f + 1; } /** * xmlXPathRoundFunction: * @ctxt: the XPath Parser context * * Implement the round() XPath function * 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. */ void xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { double f; CHECK_ARITY(1); CHECK_TYPE(XPATH_NUMBER); /* round(0.50000001) => 0 !!!!! */ f = (double)((int) ctxt->value->floatval); if (ctxt->value->floatval < f + 0.5) ctxt->value->floatval = f; else if (ctxt->value->floatval == f + 0.5) ctxt->value->floatval = f; /* !!!! Not following the spec here */ else ctxt->value->floatval = f + 1; } /************************************************************************ * * * The Parser * * * ************************************************************************/ /* * a couple of forward declarations since we use a recursive call based * implementation. */ void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt); void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt); void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt); void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt); /** * xmlXPathParseNCName: * @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 */ xmlChar * xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) { const xmlChar *q; xmlChar *ret = NULL; if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL); q = NEXT; while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) || (CUR == '.') || (CUR == '-') || (CUR == '_') || (IS_COMBINING(CUR)) || (IS_EXTENDER(CUR))) NEXT; ret = xmlStrndup(q, CUR_PTR - q); return(ret); } /** * xmlXPathParseQName: * @ctxt: the XPath Parser context * @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. */ xmlChar * xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) { xmlChar *ret = NULL; *prefix = NULL; ret = xmlXPathParseNCName(ctxt); if (CUR == ':') { *prefix = ret; NEXT; ret = xmlXPathParseNCName(ctxt); } return(ret); } /** * xmlXPathStringEvalNumber: * @str: A string to scan * * [30] Number ::= Digits ('.' Digits)? * | '.' Digits * [31] Digits ::= [0-9]+ * * Parse and evaluate a Number in the string * * BUG: "1.' is not valid ... James promised correction * as Digits ('.' Digits?)? * * Returns the double value. */ double xmlXPathStringEvalNumber(const xmlChar *str) { const xmlChar *cur = str; double ret = 0.0; double mult = 1; int ok = 0; while (*cur == ' ') cur++; if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) { return(xmlXPathNAN); } while ((*cur >= '0') && (*cur <= '9')) { ret = ret * 10 + (*cur - '0'); ok = 1; cur++; } if (*cur == '.') { cur++; if (((*cur < '0') || (*cur > '9')) && (!ok)) { return(xmlXPathNAN); } while ((*cur >= '0') && (*cur <= '9')) { mult /= 10; ret = ret + (*cur - '0') * mult; cur++; } } while (*cur == ' ') cur++; if (*cur != 0) return(xmlXPathNAN); return(ret); } /** * xmlXPathEvalNumber: * @ctxt: the XPath Parser context * * [30] Number ::= Digits ('.' Digits)? * | '.' Digits * [31] Digits ::= [0-9]+ * * Parse and evaluate a Number, then push it on the stack * * BUG: "1.' is not valid ... James promised correction * as Digits ('.' Digits?)? */ void xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) { double ret = 0.0; double mult = 1; int ok = 0; CHECK_ERROR; if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) { ERROR(XPATH_NUMBER_ERROR); } while ((CUR >= '0') && (CUR <= '9')) { ret = ret * 10 + (CUR - '0'); ok = 1; NEXT; } if (CUR == '.') { NEXT; if (((CUR < '0') || (CUR > '9')) && (!ok)) { ERROR(XPATH_NUMBER_ERROR); } while ((CUR >= '0') && (CUR <= '9')) { mult /= 10; ret = ret + (CUR - '0') * mult; NEXT; } } valuePush(ctxt, xmlXPathNewFloat(ret)); } /** * xmlXPathEvalLiteral: * @ctxt: the XPath Parser context * * Parse a Literal and push it on the stack. * * [29] Literal ::= '"' [^"]* '"' * | "'" [^']* "'" * * TODO: xmlXPathEvalLiteral memory allocation could be improved. */ void xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) { const xmlChar *q; xmlChar *ret = NULL; if (CUR == '"') { NEXT; q = CUR_PTR; while ((IS_CHAR(CUR)) && (CUR != '"')) NEXT; if (!IS_CHAR(CUR)) { ERROR(XPATH_UNFINISHED_LITERAL_ERROR); } else { ret = xmlStrndup(q, CUR_PTR - q); NEXT; } } else if (CUR == '\'') { NEXT; q = CUR_PTR; while ((IS_CHAR(CUR)) && (CUR != '\'')) NEXT; if (!IS_CHAR(CUR)) { ERROR(XPATH_UNFINISHED_LITERAL_ERROR); } else { ret = xmlStrndup(q, CUR_PTR - q); NEXT; } } else { ERROR(XPATH_START_LITERAL_ERROR); } if (ret == NULL) return; valuePush(ctxt, xmlXPathNewString(ret)); xmlFree(ret); } /** * xmlXPathEvalVariableReference: * @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 * 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 */ void xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) { xmlChar *name; xmlChar *prefix; xmlXPathObjectPtr value; if (CUR != '$') { ERROR(XPATH_VARIABLE_REF_ERROR); } name = xmlXPathParseQName(ctxt, &prefix); if (name == NULL) { ERROR(XPATH_VARIABLE_REF_ERROR); } value = xmlXPathVariablelookup(ctxt, prefix, name); if (value == NULL) { ERROR(XPATH_UNDEF_VARIABLE_ERROR); } valuePush(ctxt, value); if (prefix != NULL) xmlFree(prefix); xmlFree(name); } /** * xmlXPathFunctionLookup: * @ctxt: the XPath Parser context * @name: a name string * * Search for a function of the given name * * [35] FunctionName ::= QName - NodeType * * TODO: for the moment the function list is hardcoded from the spec !!!! * * Returns the xmlXPathFunction if found, or NULL otherwise */ xmlXPathFunction xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, const xmlChar *name) { switch (name[0]) { case 'b': if (!xmlStrcmp(name, BAD_CAST "boolean")) return(xmlXPathBooleanFunction); break; case 'c': if (!xmlStrcmp(name, BAD_CAST "ceiling")) return(xmlXPathCeilingFunction); if (!xmlStrcmp(name, BAD_CAST "count")) return(xmlXPathCountFunction); if (!xmlStrcmp(name, BAD_CAST "concat")) return(xmlXPathConcatFunction); if (!xmlStrcmp(name, BAD_CAST "contains")) return(xmlXPathContainsFunction); break; case 'i': if (!xmlStrcmp(name, BAD_CAST "id")) return(xmlXPathIdFunction); break; case 'f': if (!xmlStrcmp(name, BAD_CAST "false")) return(xmlXPathFalseFunction); if (!xmlStrcmp(name, BAD_CAST "floor")) return(xmlXPathFloorFunction); break; case 'l': if (!xmlStrcmp(name, BAD_CAST "last")) return(xmlXPathLastFunction); if (!xmlStrcmp(name, BAD_CAST "lang")) return(xmlXPathLangFunction); if (!xmlStrcmp(name, BAD_CAST "local-part")) return(xmlXPathLocalPartFunction); break; case 'n': if (!xmlStrcmp(name, BAD_CAST "not")) return(xmlXPathNotFunction); if (!xmlStrcmp(name, BAD_CAST "name")) return(xmlXPathNameFunction); if (!xmlStrcmp(name, BAD_CAST "namespace")) return(xmlXPathNamespaceFunction); if (!xmlStrcmp(name, BAD_CAST "normalize-space")) return(xmlXPathNormalizeFunction); if (!xmlStrcmp(name, BAD_CAST "normalize")) return(xmlXPathNormalizeFunction); if (!xmlStrcmp(name, BAD_CAST "number")) return(xmlXPathNumberFunction); break; case 'p': if (!xmlStrcmp(name, BAD_CAST "position")) return(xmlXPathPositionFunction); break; case 'r': if (!xmlStrcmp(name, BAD_CAST "round")) return(xmlXPathRoundFunction); break; case 's': if (!xmlStrcmp(name, BAD_CAST "string")) return(xmlXPathStringFunction); if (!xmlStrcmp(name, BAD_CAST "string-length")) return(xmlXPathStringLengthFunction); if (!xmlStrcmp(name, BAD_CAST "starts-with")) return(xmlXPathStartsWithFunction); if (!xmlStrcmp(name, BAD_CAST "substring")) return(xmlXPathSubstringFunction); if (!xmlStrcmp(name, BAD_CAST "substring-before")) return(xmlXPathSubstringBeforeFunction); if (!xmlStrcmp(name, BAD_CAST "substring-after")) return(xmlXPathSubstringAfterFunction); if (!xmlStrcmp(name, BAD_CAST "sum")) return(xmlXPathSumFunction); break; case 't': if (!xmlStrcmp(name, BAD_CAST "true")) return(xmlXPathTrueFunction); if (!xmlStrcmp(name, BAD_CAST "translate")) return(xmlXPathTranslateFunction); break; } return(NULL); } /** * xmlXPathEvalLocationPathName: * @ctxt: the XPath Parser context * @name: a name string * * Various names in the beginning of a LocationPath expression * indicate whether that's an Axis, a node type, * * [6] AxisName ::= 'ancestor' * | 'ancestor-or-self' * | 'attribute' * | 'child' * | 'descendant' * | 'descendant-or-self' * | 'following' * | 'following-sibling' * | 'namespace' * | 'parent' * | 'preceding' * | 'preceding-sibling' * | 'self' * [38] NodeType ::= 'comment' * | 'text' * | 'processing-instruction' * | 'node' */ int xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, const xmlChar *name) { switch (name[0]) { case 'a': if (!xmlStrcmp(name, BAD_CAST "ancestor")) return(AXIS_ANCESTOR); if (!xmlStrcmp(name, BAD_CAST "ancestor-or-self")) return(AXIS_ANCESTOR_OR_SELF); if (!xmlStrcmp(name, BAD_CAST "attribute")) return(AXIS_ATTRIBUTE); break; case 'c': if (!xmlStrcmp(name, BAD_CAST "child")) return(AXIS_CHILD); if (!xmlStrcmp(name, BAD_CAST "comment")) return(NODE_TYPE_COMMENT); break; case 'd': if (!xmlStrcmp(name, BAD_CAST "descendant")) return(AXIS_DESCENDANT); if (!xmlStrcmp(name, BAD_CAST "descendant-or-self")) return(AXIS_DESCENDANT_OR_SELF); break; case 'f': if (!xmlStrcmp(name, BAD_CAST "following")) return(AXIS_FOLLOWING); if (!xmlStrcmp(name, BAD_CAST "following-sibling")) return(AXIS_FOLLOWING_SIBLING); break; case 'n': if (!xmlStrcmp(name, BAD_CAST "namespace")) return(AXIS_NAMESPACE); if (!xmlStrcmp(name, BAD_CAST "node")) return(NODE_TYPE_NODE); break; case 'p': if (!xmlStrcmp(name, BAD_CAST "parent")) return(AXIS_PARENT); if (!xmlStrcmp(name, BAD_CAST "preceding")) return(AXIS_PRECEDING); if (!xmlStrcmp(name, BAD_CAST "preceding-sibling")) return(AXIS_PRECEDING_SIBLING); if (!xmlStrcmp(name, BAD_CAST "processing-instruction")) return(NODE_TYPE_PI); break; case 's': if (!xmlStrcmp(name, BAD_CAST "self")) return(AXIS_SELF); break; case 't': if (!xmlStrcmp(name, BAD_CAST "text")) return(NODE_TYPE_TEXT); break; } if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION); return(0); } /** * xmlXPathEvalFunctionCall: * @ctxt: the XPath Parser context * * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' * [17] Argument ::= Expr * * Parse and evaluate a function call, the evaluation of all arguments are * pushed on the stack */ void xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) { xmlChar *name; xmlChar *prefix; xmlXPathFunction func; int nbargs = 0; name = xmlXPathParseQName(ctxt, &prefix); if (name == NULL) { ERROR(XPATH_EXPR_ERROR); } SKIP_BLANKS; func = xmlXPathIsFunction(ctxt, name); if (func == NULL) { xmlFree(name); ERROR(XPATH_UNKNOWN_FUNC_ERROR); } #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Calling function %s\n", name); #endif if (CUR != '(') { xmlFree(name); ERROR(XPATH_EXPR_ERROR); } NEXT; SKIP_BLANKS; while (CUR != ')') { xmlXPathEvalExpr(ctxt); nbargs++; if (CUR == ')') break; if (CUR != ',') { xmlFree(name); ERROR(XPATH_EXPR_ERROR); } NEXT; SKIP_BLANKS; } NEXT; SKIP_BLANKS; xmlFree(name); func(ctxt, nbargs); } /** * xmlXPathEvalPrimaryExpr: * @ctxt: the XPath Parser context * * [15] PrimaryExpr ::= VariableReference * | '(' Expr ')' * | Literal * | Number * | FunctionCall * * Parse and evaluate a primary expression, then push the result on the stack */ void xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) { SKIP_BLANKS; if (CUR == '$') xmlXPathEvalVariableReference(ctxt); else if (CUR == '(') { NEXT; SKIP_BLANKS; xmlXPathEvalExpr(ctxt); if (CUR != ')') { ERROR(XPATH_EXPR_ERROR); } NEXT; SKIP_BLANKS; } else if (IS_DIGIT(CUR)) { xmlXPathEvalNumber(ctxt); } else if ((CUR == '\'') || (CUR == '"')) { xmlXPathEvalLiteral(ctxt); } else { xmlXPathEvalFunctionCall(ctxt); } } /** * xmlXPathEvalFilterExpr: * @ctxt: the XPath Parser context * * [20] FilterExpr ::= PrimaryExpr * | FilterExpr Predicate * * Parse and evaluate a filter expression, then push the result on the stack * 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. */ void xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) { /**** xmlNodeSetPtr oldset = NULL; xmlXPathObjectPtr arg; ****/ xmlXPathEvalPrimaryExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; if (CUR != '[') return; CHECK_TYPE(XPATH_NODESET); while (CUR == '[') { xmlXPathEvalPredicate(ctxt); SKIP_BLANKS; } } /** * xmlXPathScanName: * @ctxt: the XPath Parser context * * Trickery: parse an XML name but without consuming the input flow * Needed for rollback cases. * * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | * CombiningChar | Extender * * [5] Name ::= (Letter | '_' | ':') (NameChar)* * * [6] Names ::= Name (S Name)* * * Returns the Name parsed or NULL */ xmlChar * xmlXPathScanName(xmlXPathParserContextPtr ctxt) { xmlChar buf[XML_MAX_NAMELEN]; int len = 0; SKIP_BLANKS; if (!IS_LETTER(CUR) && (CUR != '_') && (CUR != ':')) { return(NULL); } while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) || (NXT(len) == '.') || (NXT(len) == '-') || (NXT(len) == '_') || (NXT(len) == ':') || (IS_COMBINING(NXT(len))) || (IS_EXTENDER(NXT(len)))) { buf[len] = NXT(len); len++; if (len >= XML_MAX_NAMELEN) { fprintf(stderr, "xmlScanName: reached XML_MAX_NAMELEN limit\n"); while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) || (NXT(len) == '.') || (NXT(len) == '-') || (NXT(len) == '_') || (NXT(len) == ':') || (IS_COMBINING(NXT(len))) || (IS_EXTENDER(NXT(len)))) len++; break; } } return(xmlStrndup(buf, len)); } /** * xmlXPathEvalPathExpr: * @ctxt: the XPath Parser context * * [19] PathExpr ::= LocationPath * | FilterExpr * | FilterExpr '/' RelativeLocationPath * | FilterExpr '//' RelativeLocationPath * * Parse and evaluate a path expression, then push the result on the stack * 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()/. */ void xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) { xmlNodeSetPtr newset = NULL; SKIP_BLANKS; if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) || (CUR == '\'') || (CUR == '"')) { xmlXPathEvalFilterExpr(ctxt); CHECK_ERROR; if ((CUR == '/') && (NXT(1) == '/')) { SKIP(2); SKIP_BLANKS; if (ctxt->context->nodelist == NULL) { STRANGE xmlXPathRoot(ctxt); } newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; xmlXPathEvalRelativeLocationPath(ctxt); } else if (CUR == '/') { xmlXPathEvalRelativeLocationPath(ctxt); } } else { /******* !!!!!!!!!! @attname */ xmlChar *name; name = xmlXPathScanName(ctxt); if ((name == NULL) || (!xmlXPathIsFunction(ctxt, name))) xmlXPathEvalLocationPath(ctxt); else xmlXPathEvalFilterExpr(ctxt); if (name != NULL) xmlFree(name); } } /** * xmlXPathEvalUnionExpr: * @ctxt: the XPath Parser context * * [18] UnionExpr ::= PathExpr * | UnionExpr '|' PathExpr * * Parse and evaluate an union expression, then push the result on the stack */ void xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalPathExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; if (CUR == '|') { xmlNodeSetPtr old = ctxt->context->nodelist; NEXT; SKIP_BLANKS; xmlXPathEvalPathExpr(ctxt); if (ctxt->context->nodelist == NULL) ctxt->context->nodelist = old; else { ctxt->context->nodelist = xmlXPathNodeSetMerge(ctxt->context->nodelist, old); xmlXPathFreeNodeSet(old); } } } /** * xmlXPathEvalUnaryExpr: * @ctxt: the XPath Parser context * * [27] UnaryExpr ::= UnionExpr * | '-' UnaryExpr * * Parse and evaluate an unary expression, then push the result on the stack */ void xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) { int minus = 0; SKIP_BLANKS; if (CUR == '-') { minus = 1; NEXT; SKIP_BLANKS; } xmlXPathEvalUnionExpr(ctxt); CHECK_ERROR; if (minus) { xmlXPathValueFlipSign(ctxt); } } /** * xmlXPathEvalMultiplicativeExpr: * @ctxt: the XPath Parser context * * [26] MultiplicativeExpr ::= UnaryExpr * | MultiplicativeExpr MultiplyOperator UnaryExpr * | MultiplicativeExpr 'div' UnaryExpr * | MultiplicativeExpr 'mod' UnaryExpr * [34] MultiplyOperator ::= '*' * * Parse and evaluate an Additive expression, then push the result on the stack */ void xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalUnaryExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == '*') || ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) || ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) { int op = -1; if (CUR == '*') { op = 0; NEXT; } else if (CUR == 'd') { op = 1; SKIP(3); } else if (CUR == 'm') { op = 2; SKIP(3); } SKIP_BLANKS; xmlXPathEvalUnaryExpr(ctxt); CHECK_ERROR; switch (op) { case 0: xmlXPathMultValues(ctxt); break; case 1: xmlXPathDivValues(ctxt); break; case 2: xmlXPathModValues(ctxt); break; } } } /** * xmlXPathEvalAdditiveExpr: * @ctxt: the XPath Parser context * * [25] AdditiveExpr ::= MultiplicativeExpr * | AdditiveExpr '+' MultiplicativeExpr * | AdditiveExpr '-' MultiplicativeExpr * * Parse and evaluate an Additive expression, then push the result on the stack */ void xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalMultiplicativeExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == '+') || (CUR == '-')) { int plus; if (CUR == '+') plus = 1; else plus = 0; NEXT; SKIP_BLANKS; xmlXPathEvalMultiplicativeExpr(ctxt); CHECK_ERROR; if (plus) xmlXPathAddValues(ctxt); else xmlXPathSubValues(ctxt); } } /** * xmlXPathEvalRelationalExpr: * @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. * * Parse and evaluate a Relational expression, then push the result * on the stack */ void xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalAdditiveExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == '<') || (CUR == '>') || ((CUR == '<') && (NXT(1) == '=')) || ((CUR == '>') && (NXT(1) == '='))) { int inf, strict, ret; if (CUR == '<') inf = 1; else inf = 0; if (NXT(1) == '=') strict = 0; else strict = 1; NEXT; if (!strict) NEXT; SKIP_BLANKS; xmlXPathEvalAdditiveExpr(ctxt); CHECK_ERROR; ret = xmlXPathCompareValues(ctxt, inf, strict); valuePush(ctxt, xmlXPathNewBoolean(ret)); } } /** * xmlXPathEvalEqualityExpr: * @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. * * Parse and evaluate an Equality expression, then push the result on the stack * */ void xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalRelationalExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) { xmlXPathObjectPtr res; int eq, equal; if (CUR == '=') eq = 1; else eq = 0; NEXT; if (!eq) NEXT; SKIP_BLANKS; xmlXPathEvalRelationalExpr(ctxt); CHECK_ERROR; equal = xmlXPathEqualValues(ctxt); if (eq) res = xmlXPathNewBoolean(equal); else res = xmlXPathNewBoolean(!equal); valuePush(ctxt, res); } } /** * xmlXPathEvalAndExpr: * @ctxt: the XPath Parser context * * [22] AndExpr ::= EqualityExpr * | AndExpr 'and' EqualityExpr * * Parse and evaluate an AND expression, then push the result on the stack * */ void xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalEqualityExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) { xmlXPathObjectPtr arg1, arg2; SKIP(3); SKIP_BLANKS; xmlXPathEvalEqualityExpr(ctxt); CHECK_ERROR; arg2 = valuePop(ctxt); arg1 = valuePop(ctxt); arg1->boolval &= arg2->boolval; valuePush(ctxt, arg1); xmlXPathFreeObject(arg2); } } /** * xmlXPathEvalExpr: * @ctxt: the XPath Parser context * * [14] Expr ::= OrExpr * [21] OrExpr ::= AndExpr * | OrExpr 'or' AndExpr * * Parse and evaluate an expression, then push the result on the stack * */ void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { xmlXPathEvalAndExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; while ((CUR == 'o') && (NXT(1) == 'r')) { xmlXPathObjectPtr arg1, arg2; SKIP(2); SKIP_BLANKS; xmlXPathEvalAndExpr(ctxt); CHECK_ERROR; arg2 = valuePop(ctxt); arg1 = valuePop(ctxt); arg1->boolval |= arg2->boolval; valuePush(ctxt, arg1); xmlXPathFreeObject(arg2); } } /** * xmlXPathEvaluatePredicateResult: * @ctxt: the XPath Parser context * @res: the Predicate Expression evaluation result * @index: index of the current node in the current list * * 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. */ int xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr res, int index) { if (res == NULL) return(0); switch (res->type) { case XPATH_BOOLEAN: return(res->boolval); case XPATH_NUMBER: return(res->floatval == index); case XPATH_NODESET: return(res->nodesetval->nodeNr != 0); case XPATH_STRING: return((res->stringval != NULL) && (xmlStrlen(res->stringval) != 0)); default: STRANGE } return(0); } /** * xmlXPathEvalPredicate: * @ctxt: the XPath Parser context * * [8] Predicate ::= '[' PredicateExpr ']' * [9] PredicateExpr ::= Expr * * Parse and evaluate a predicate for all the elements of the * current node list. Then refine the list by removing all * nodes where the predicate is false. */ void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) { const xmlChar *cur; xmlXPathObjectPtr res; xmlNodeSetPtr newset = NULL; int i; SKIP_BLANKS; if (CUR != '[') { ERROR(XPATH_INVALID_PREDICATE_ERROR); } NEXT; SKIP_BLANKS; if ((ctxt->context->nodelist == NULL) || (ctxt->context->nodelist->nodeNr == 0)) { ctxt->context->node = NULL; xmlXPathEvalExpr(ctxt); CHECK_ERROR; res = valuePop(ctxt); if (res != NULL) xmlXPathFreeObject(res); } else { cur = ctxt->cur; newset = xmlXPathNodeSetCreate(NULL); for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) { ctxt->cur = cur; ctxt->context->node = ctxt->context->nodelist->nodeTab[i]; xmlXPathEvalExpr(ctxt); CHECK_ERROR; res = valuePop(ctxt); if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1)) xmlXPathNodeSetAdd(newset, ctxt->context->nodelist->nodeTab[i]); if (res != NULL) xmlXPathFreeObject(res); } if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; } if (CUR != ']') { ERROR(XPATH_INVALID_PREDICATE_ERROR); } NEXT; SKIP_BLANKS; #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "After predicate : "); xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist); #endif } /** * xmlXPathEvalBasis: * @ctxt: the XPath Parser context * * [5] Basis ::= AxisName '::' NodeTest * | AbbreviatedBasis * [13] AbbreviatedBasis ::= NodeTest * | '@' NodeTest * [7] NodeTest ::= WildcardName * | NodeType '(' ')' * | 'processing-instruction' '(' Literal ')' * [37] WildcardName ::= '*' * | NCName ':' '*' * | QName * * Evaluate one step in a Location Path */ void xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) { xmlChar *name = NULL; xmlChar *prefix = NULL; int type = 0; int axis = AXIS_CHILD; /* the default on abbreviated syntax */ int nodetest = NODE_TEST_NONE; int nodetype = 0; xmlNodeSetPtr newset = NULL; if (CUR == '@') { NEXT; axis = AXIS_ATTRIBUTE; goto parse_NodeTest; } else if (CUR == '*') { NEXT; nodetest = NODE_TEST_ALL; } else { name = xmlXPathParseNCName(ctxt); if (name == NULL) { ERROR(XPATH_EXPR_ERROR); } type = xmlXPathGetNameType(ctxt, name); switch (type) { case IS_FUNCTION: { xmlXPathFunction func; int nbargs = 0; xmlXPathObjectPtr top; top = ctxt->value; func = xmlXPathIsFunction(ctxt, name); if (func == NULL) { xmlFree(name); ERROR(XPATH_UNKNOWN_FUNC_ERROR); } #ifdef DEBUG_EXPR fprintf(xmlXPathDebug, "Calling function %s\n", name); #endif if (CUR != '(') { xmlFree(name); ERROR(XPATH_EXPR_ERROR); } NEXT; while (CUR != ')') { xmlXPathEvalExpr(ctxt); nbargs++; if (CUR == ')') break; if (CUR != ',') { xmlFree(name); ERROR(XPATH_EXPR_ERROR); } NEXT; } NEXT; xmlFree(name); func(ctxt, nbargs); if ((ctxt->value != top) && (ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET)) { xmlXPathObjectPtr cur; cur = valuePop(ctxt); ctxt->context->nodelist = cur->nodesetval; ctxt->context->node = NULL; cur->nodesetval = NULL; xmlXPathFreeObject(cur); } return; } /* * Simple case: no axis seach all given node types. */ case NODE_TYPE_COMMENT: if ((CUR != '(') || (NXT(1) != ')')) break; SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_COMMENT_NODE; goto search_nodes; case NODE_TYPE_TEXT: if ((CUR != '(') || (NXT(1) != ')')) break; SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_TEXT_NODE; goto search_nodes; case NODE_TYPE_NODE: if ((CUR != '(') || (NXT(1) != ')')) { nodetest = NODE_TEST_NAME; break; } SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_ELEMENT_NODE; goto search_nodes; case NODE_TYPE_PI: if (CUR != '(') break; if (name != NULL) xmlFree(name); name = NULL; if (NXT(1) != ')') { xmlXPathObjectPtr cur; /* * Specific case: search a PI by name. */ NEXT; nodetest = NODE_TEST_PI; xmlXPathEvalLiteral(ctxt); CHECK_ERROR; if (CUR != ')') ERROR(XPATH_UNCLOSED_ERROR); NEXT; xmlXPathStringFunction(ctxt, 1); CHECK_ERROR; cur = valuePop(ctxt); name = xmlStrdup(cur->stringval); xmlXPathFreeObject(cur); } else SKIP(2); nodetest = NODE_TEST_PI; goto search_nodes; /* * Handling of the compund form: got the axis. */ case AXIS_ANCESTOR: case AXIS_ANCESTOR_OR_SELF: case AXIS_ATTRIBUTE: case AXIS_CHILD: case AXIS_DESCENDANT: case AXIS_DESCENDANT_OR_SELF: case AXIS_FOLLOWING: case AXIS_FOLLOWING_SIBLING: case AXIS_NAMESPACE: case AXIS_PARENT: case AXIS_PRECEDING: case AXIS_PRECEDING_SIBLING: case AXIS_SELF: if ((CUR != ':') || (NXT(1) != ':')) { nodetest = NODE_TEST_NAME; break; } SKIP(2); axis = type; break; /* * Default: abbreviated syntax the axis is AXIS_CHILD */ default: nodetest = NODE_TEST_NAME; } parse_NodeTest: if (nodetest == NODE_TEST_NONE) { if (CUR == '*') { NEXT; nodetest = NODE_TEST_ALL; } else { if (name != NULL) xmlFree(name); name = xmlXPathParseQName(ctxt, &prefix); if (name == NULL) { ERROR(XPATH_EXPR_ERROR); } type = xmlXPathGetNameType(ctxt, name); switch (type) { /* * Simple case: no axis seach all given node types. */ case NODE_TYPE_COMMENT: if ((CUR != '(') || (NXT(1) != ')')) break; SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_COMMENT_NODE; goto search_nodes; case NODE_TYPE_TEXT: if ((CUR != '(') || (NXT(1) != ')')) break; SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_TEXT_NODE; goto search_nodes; case NODE_TYPE_NODE: if ((CUR != '(') || (NXT(1) != ')')) { nodetest = NODE_TEST_NAME; break; } SKIP(2); nodetest = NODE_TEST_TYPE; nodetype = XML_ELEMENT_NODE; goto search_nodes; case NODE_TYPE_PI: if (CUR != '(') break; if (name != NULL) xmlFree(name); name = NULL; if (NXT(1) != ')') { xmlXPathObjectPtr cur; /* * Specific case: search a PI by name. */ NEXT; nodetest = NODE_TEST_PI; xmlXPathEvalLiteral(ctxt); CHECK_ERROR; if (CUR != ')') ERROR(XPATH_UNCLOSED_ERROR); NEXT; xmlXPathStringFunction(ctxt, 1); CHECK_ERROR; cur = valuePop(ctxt); name = xmlStrdup(cur->stringval); xmlXPathFreeObject(cur); } else SKIP(2); nodetest = NODE_TEST_PI; goto search_nodes; } nodetest = NODE_TEST_NAME; } } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) { NEXT; prefix = name; if (CUR == '*') { NEXT; nodetest = NODE_TEST_ALL; } else name = xmlXPathParseNCName(ctxt); } else if (name == NULL) ERROR(XPATH_EXPR_ERROR); } search_nodes: #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "Basis : computing new set\n"); #endif newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype, prefix, name); if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "Basis : "); xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist); #endif if (name != NULL) xmlFree(name); if (prefix != NULL) xmlFree(prefix); } /** * xmlXPathEvalStep: * @ctxt: the XPath Parser context * * [4] Step ::= Basis Predicate* * | AbbreviatedStep * [12] AbbreviatedStep ::= '.' * | '..' * * Evaluate 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. */ void xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) { xmlNodeSetPtr newset = NULL; SKIP_BLANKS; if ((CUR == '.') && (NXT(1) == '.')) { SKIP(2); SKIP_BLANKS; if (ctxt->context->nodelist == NULL) { STRANGE xmlXPathRoot(ctxt); } newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT, NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; } else if (CUR == '.') { NEXT; SKIP_BLANKS; } else { xmlXPathEvalBasis(ctxt); SKIP_BLANKS; while (CUR == '[') { xmlXPathEvalPredicate(ctxt); } } #ifdef DEBUG_STEP fprintf(xmlXPathDebug, "Step : "); xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist); #endif } /** * xmlXPathEvalRelativeLocationPath: * @ctxt: the XPath Parser context * * [3] RelativeLocationPath ::= Step * | RelativeLocationPath '/' Step * | AbbreviatedRelativeLocationPath * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step * */ void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) { xmlNodeSetPtr newset = NULL; SKIP_BLANKS; xmlXPathEvalStep(ctxt); SKIP_BLANKS; while (CUR == '/') { if ((CUR == '/') && (NXT(1) == '/')) { SKIP(2); SKIP_BLANKS; if (ctxt->context->nodelist == NULL) { STRANGE xmlXPathRoot(ctxt); } newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; xmlXPathEvalStep(ctxt); } else if (CUR == '/') { NEXT; SKIP_BLANKS; xmlXPathEvalStep(ctxt); } SKIP_BLANKS; } } /** * xmlXPathEvalLocationPath: * @ctxt: the XPath Parser context * * [1] LocationPath ::= RelativeLocationPath * | AbsoluteLocationPath * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? * | AbbreviatedAbsoluteLocationPath * [10] AbbreviatedAbsoluteLocationPath ::= * '//' RelativeLocationPath * * // 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. */ void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) { xmlNodeSetPtr newset = NULL; SKIP_BLANKS; if (CUR != '/') { xmlXPathEvalRelativeLocationPath(ctxt); } else { while (CUR == '/') { if ((CUR == '/') && (NXT(1) == '/')) { SKIP(2); SKIP_BLANKS; if (ctxt->context->nodelist == NULL) xmlXPathRoot(ctxt); newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL); if (ctxt->context->nodelist != NULL) xmlXPathFreeNodeSet(ctxt->context->nodelist); ctxt->context->nodelist = newset; ctxt->context->node = NULL; xmlXPathEvalRelativeLocationPath(ctxt); } else if (CUR == '/') { NEXT; SKIP_BLANKS; xmlXPathRoot(ctxt); if (CUR != 0) xmlXPathEvalRelativeLocationPath(ctxt); } else { xmlXPathEvalRelativeLocationPath(ctxt); } } } } /** * xmlXPathEval: * @str: the XPath expression * @ctxt: the XPath context * * Evaluate the XPath Location Path in the given context. * * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL. * the caller has to free the object. */ xmlXPathObjectPtr xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt) { xmlXPathParserContextPtr pctxt; xmlXPathObjectPtr res = NULL, tmp; int stack = 0; xmlXPathInit(); CHECK_CONTEXT if (xmlXPathDebug == NULL) xmlXPathDebug = stderr; pctxt = xmlXPathNewParserContext(str, ctxt); if (str[0] == '/') xmlXPathRoot(pctxt); xmlXPathEvalLocationPath(pctxt); /* TODO: cleanup nodelist, res = valuePop(pctxt); */ do { tmp = valuePop(pctxt); if (tmp != NULL) { xmlXPathFreeObject(tmp); stack++; } } while (tmp != NULL); if (stack != 0) { fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n", stack); } if (pctxt->error == XPATH_EXPRESSION_OK) res = xmlXPathNewNodeSetList(pctxt->context->nodelist); else res = NULL; xmlXPathFreeParserContext(pctxt); return(res); } /** * xmlXPathEvalExpression: * @str: the XPath expression * @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. */ xmlXPathObjectPtr xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { xmlXPathParserContextPtr pctxt; xmlXPathObjectPtr res, tmp; int stack = 0; xmlXPathInit(); CHECK_CONTEXT if (xmlXPathDebug == NULL) xmlXPathDebug = stderr; pctxt = xmlXPathNewParserContext(str, ctxt); xmlXPathEvalExpr(pctxt); res = valuePop(pctxt); do { tmp = valuePop(pctxt); if (tmp != NULL) { xmlXPathFreeObject(tmp); stack++; } } while (tmp != NULL); if (stack != 0) { fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n", stack); } xmlXPathFreeParserContext(pctxt); return(res); }