diff --git a/ChangeLog b/ChangeLog index f2ea765b..e8c3356b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Fri Oct 13 02:54:37 CEST 2000 Daniel Veillard + + * testXPath.c xpath.[ch]: moved some debug functions to xpath core + * xpointer.c: implemented string-range() at least a good first version + * test/XPath/docs/str test/XPath/xptr/strrange + result/XPath/xptr/strrange: the string-range() tests + Thu Oct 12 10:02:59 CEST 2000 Daniel Veillard * Makefile.am include/Makefile.am include/win32config.h diff --git a/Makefile.am b/Makefile.am index bff30d7c..e837a143 100644 --- a/Makefile.am +++ b/Makefile.am @@ -345,6 +345,9 @@ dist-hook: libxml.spec -cp libxml.spec $(distdir) (cd $(srcdir) ; tar -cf - --exclude CVS test result SAXresult ) | (cd $(distdir); tar xf -) +rpm: $(distdir).tar.gz + rpm -ta $(distdir).tar.gz + ## We create xmlConf.sh here and not from configure because we want ## to get the paths expanded correctly. Macros like srcdir are given ## the value NONE in configure if the user doesn't specify them (this diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h index 533ca3fc..81a11f62 100644 --- a/include/libxml/xpath.h +++ b/include/libxml/xpath.h @@ -178,19 +178,19 @@ struct _xmlXPathContext { int nb_variables; /* number of defined variables */ int max_variables; /* max number of variables */ - xmlXPathVariablePtr *variables; /* Array of defined variables */ + xmlXPathVariablePtr variables; /* Array of defined variables */ int nb_types; /* number of defined types */ int max_types; /* max number of types */ - xmlXPathTypePtr *types; /* Array of defined types */ + xmlXPathTypePtr types; /* Array of defined types */ int nb_funcs; /* number of defined funcs */ int max_funcs; /* max number of funcs */ - xmlXPathFuncPtr *funcs; /* Array of defined funcs */ + xmlXPathFuncPtr funcs; /* Array of defined funcs */ int nb_axis; /* number of defined axis */ int max_axis; /* max number of axis */ - xmlXPathAxisPtr *axis; /* Array of defined axis */ + xmlXPathAxisPtr axis; /* Array of defined axis */ /* Namespace traversal should be implemented with user */ xmlNsPtr *namespaces; /* The namespaces lookup */ @@ -266,6 +266,10 @@ void xmlXPatherror (xmlXPathParserContextPtr ctxt, int line, int no); +void xmlXPathDebugDumpObject (FILE *output, + xmlXPathObjectPtr cur, + int depth); + /** * Utilities to extend XPath (XPointer) */ diff --git a/result/XPath/xptr/strrange b/result/XPath/xptr/strrange new file mode 100644 index 00000000..c48ba43b --- /dev/null +++ b/result/XPath/xptr/strrange @@ -0,0 +1,76 @@ + +======================== +Expression: xpointer(string-range(//p, 'simple')) +Object is a Location Set: +1 : Object is a range : + From index 3 in node + TEXT + content=a simple test + To index 8 in node + TEXT + content=a simple test + + +======================== +Expression: xpointer(string-range(//p, 'test')) +Object is a Location Set: +1 : Object is a range : + From index 10 in node + TEXT + content=a simple test + To index 13 in node + TEXT + content=a simple test + +2 : Object is a range : + From index 10 in node + TEXT + content=multiple tests + To index 13 in node + TEXT + content=multiple tests + +3 : Object is a range : + From index 7 in node + TEXT + content=anced test + To index 10 in node + TEXT + content=anced test + + +======================== +Expression: xpointer(string-range(//p, 'difficult')) +Object is a Location Set: +1 : Object is a range : + From index 3 in node + TEXT + content=a diff + To index 4 in node + TEXT + content=cult one + + +======================== +Expression: xpointer(string-range(//p, 'spanning')) +Object is a Location Set: +1 : Object is a range : + From index 3 in node + TEXT + content=a span + To index 3 in node + TEXT + content=ing one + + +======================== +Expression: xpointer(string-range(//p, 'unbalanced')) +Object is a Location Set: +1 : Object is a range : + From index 8 in node + TEXT + content=and an unbal + To index 5 in node + TEXT + content=anced test + diff --git a/test/XPath/docs/str b/test/XPath/docs/str new file mode 100644 index 00000000..7127ca2a --- /dev/null +++ b/test/XPath/docs/str @@ -0,0 +1,8 @@ + + +

a simple test

+

multiple tests

+

a difficult one

+

a span

n

ing one

+

and an unbal

anced test

+
diff --git a/test/XPath/xptr/strrange b/test/XPath/xptr/strrange new file mode 100644 index 00000000..3c2aafce --- /dev/null +++ b/test/XPath/xptr/strrange @@ -0,0 +1,5 @@ +xpointer(string-range(//p, 'simple')) +xpointer(string-range(//p, 'test')) +xpointer(string-range(//p, 'difficult')) +xpointer(string-range(//p, 'spanning')) +xpointer(string-range(//p, 'unbalanced')) diff --git a/testXPath.c b/testXPath.c index 1133d4cf..c226cf0f 100644 --- a/testXPath.c +++ b/testXPath.c @@ -84,146 +84,6 @@ static xmlChar buffer[] = \n\ "; -void xmlXPAthDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { - int i; - char shift[100]; - - for (i = 0;((i < depth) && (i < 25));i++) - shift[2 * i] = shift[2 * i + 1] = ' '; - shift[2 * i] = shift[2 * i + 1] = 0; - if (cur == NULL) { - fprintf(output, shift); - fprintf(output, "Node is NULL !\n"); - return; - - } - - if ((cur->type == XML_DOCUMENT_NODE) || - (cur->type == XML_HTML_DOCUMENT_NODE)) { - fprintf(output, shift); - fprintf(output, " /\n"); - } else if (cur->type == XML_ATTRIBUTE_NODE) - xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); - else - xmlDebugDumpOneNode(output, cur, depth); -} - -void xmlXPAthDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { - int i; - char shift[100]; - - for (i = 0;((i < depth) && (i < 25));i++) - shift[2 * i] = shift[2 * i + 1] = ' '; - shift[2 * i] = shift[2 * i + 1] = 0; - - if (cur == NULL) { - fprintf(output, shift); - fprintf(output, "NodeSet is NULL !\n"); - return; - - } - - fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); - for (i = 0;i < cur->nodeNr;i++) { - fprintf(output, shift); - fprintf(output, "%d", i + 1); - xmlXPAthDebugDumpNode(output, cur->nodeTab[i], depth + 1); - } -} - -#if defined(LIBXML_XPTR_ENABLED) -void xmlXPAthDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth); -void xmlXPAthDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { - int i; - char shift[100]; - - for (i = 0;((i < depth) && (i < 25));i++) - shift[2 * i] = shift[2 * i + 1] = ' '; - shift[2 * i] = shift[2 * i + 1] = 0; - - if (cur == NULL) { - fprintf(output, shift); - fprintf(output, "LocationSet is NULL !\n"); - return; - - } - - for (i = 0;i < cur->locNr;i++) { - fprintf(output, shift); - fprintf(output, "%d : ", i + 1); - xmlXPAthDebugDumpObject(output, cur->locTab[i], depth + 1); - } -} -#endif - -void xmlXPAthDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { - int i; - char shift[100]; - - for (i = 0;((i < depth) && (i < 25));i++) - shift[2 * i] = shift[2 * i + 1] = ' '; - shift[2 * i] = shift[2 * i + 1] = 0; - - fprintf(output, shift); - - if (cur == NULL) { - fprintf(output, "Object is empty (NULL)\n"); - return; - } - switch(cur->type) { - case XPATH_UNDEFINED: - fprintf(output, "Object is uninitialized\n"); - break; - case XPATH_NODESET: - fprintf(output, "Object is a Node Set :\n"); - xmlXPAthDebugDumpNodeSet(output, cur->nodesetval, depth); - break; - case XPATH_BOOLEAN: - fprintf(output, "Object is a Boolean : "); - if (cur->boolval) fprintf(output, "true\n"); - else fprintf(output, "false\n"); - break; - case XPATH_NUMBER: - fprintf(output, "Object is a number : %0g\n", cur->floatval); - break; - case XPATH_STRING: - fprintf(output, "Object is a string : "); - xmlDebugDumpString(output, cur->stringval); - fprintf(output, "\n"); - break; - case XPATH_POINT: - fprintf(output, "Object is a point : index %d in node", cur->index); - xmlXPAthDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); - fprintf(output, "\n"); - break; - case XPATH_RANGE: - fprintf(output, "Object is a range :\n"); - fprintf(output, shift); - fprintf(output, "From "); - if (cur->index >= 0) - fprintf(output, "index %d in ", cur->index); - fprintf(output, "node\n"); - xmlXPAthDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); - fprintf(output, shift); - fprintf(output, "To "); - if (cur->index2 >= 0) - fprintf(output, "index %d in ", cur->index2); - fprintf(output, "node\n"); - xmlXPAthDebugDumpNode(output, (xmlNodePtr) cur->user2, depth + 1); - fprintf(output, "\n"); - break; - case XPATH_LOCATIONSET: -#if defined(LIBXML_XPTR_ENABLED) - fprintf(output, "Object is a Location Set:\n"); - xmlXPAthDebugDumpLocationSet(output, - (xmlLocationSetPtr) cur->user, depth); -#endif - break; - case XPATH_USERS: - fprintf(output, "Object is user defined\n"); - break; - } -} void testXPath(const char *str) { xmlXPathObjectPtr res; @@ -243,7 +103,7 @@ void testXPath(const char *str) { #if defined(LIBXML_XPTR_ENABLED) } #endif - xmlXPAthDebugDumpObject(stdout, res, 0); + xmlXPathDebugDumpObject(stdout, res, 0); xmlXPathFreeObject(res); xmlXPathFreeContext(ctxt); } diff --git a/xpath.c b/xpath.c index 83b70875..371ecbbc 100644 --- a/xpath.c +++ b/xpath.c @@ -52,11 +52,17 @@ #ifdef LIBXML_XPTR_ENABLED #include #endif +#ifdef LIBXML_DEBUG_ENABLED +#include +#endif /* #define DEBUG */ /* #define DEBUG_STEP */ /* #define DEBUG_EXPR */ +void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); +double xmlXPathStringEvalNumber(const xmlChar *str); + /* * Setup stuff for floating point * The lack of portability of this section of the libc is annoying ! @@ -159,17 +165,14 @@ xmlXPathInit(void) { initialized = 1; } -FILE *xmlXPathDebug = NULL; - -double xmlXPathStringEvalNumber(const xmlChar *str); -void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); - /************************************************************************ * * - * Parser stacks related functions and macros * + * Debugging related functions * * * ************************************************************************/ +FILE *xmlXPathDebug = NULL; + #define TODO \ fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); @@ -178,6 +181,171 @@ void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \ __FILE__, __LINE__); +#ifdef LIBXML_DEBUG_ENABLED +double xmlXPathStringEvalNumber(const xmlChar *str); +void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs); + +void xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "Node is NULL !\n"); + return; + + } + + if ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + fprintf(output, shift); + fprintf(output, " /\n"); + } else if (cur->type == XML_ATTRIBUTE_NODE) + xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); + else + xmlDebugDumpOneNode(output, cur, depth); +} + +void xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "NodeSet is NULL !\n"); + return; + + } + + fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); + for (i = 0;i < cur->nodeNr;i++) { + fprintf(output, shift); + fprintf(output, "%d", i + 1); + xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1); + } +} + +#if defined(LIBXML_XPTR_ENABLED) +void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth); +void xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "LocationSet is NULL !\n"); + return; + + } + + for (i = 0;i < cur->locNr;i++) { + fprintf(output, shift); + fprintf(output, "%d : ", i + 1); + xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1); + } +} +#endif + +void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + fprintf(output, shift); + + if (cur == NULL) { + fprintf(output, "Object is empty (NULL)\n"); + return; + } + switch(cur->type) { + case XPATH_UNDEFINED: + fprintf(output, "Object is uninitialized\n"); + break; + case XPATH_NODESET: + fprintf(output, "Object is a Node Set :\n"); + xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth); + break; + case XPATH_BOOLEAN: + fprintf(output, "Object is a Boolean : "); + if (cur->boolval) fprintf(output, "true\n"); + else fprintf(output, "false\n"); + break; + case XPATH_NUMBER: + fprintf(output, "Object is a number : %0g\n", cur->floatval); + break; + case XPATH_STRING: + fprintf(output, "Object is a string : "); + xmlDebugDumpString(output, cur->stringval); + fprintf(output, "\n"); + break; + case XPATH_POINT: + fprintf(output, "Object is a point : index %d in node", cur->index); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); + fprintf(output, "\n"); + break; + case XPATH_RANGE: + if ((cur->user2 == NULL) || + ((cur->user2 == cur->user) && (cur->index == cur->index2))) { + fprintf(output, "Object is a collapsed range :\n"); + fprintf(output, shift); + if (cur->index >= 0) + fprintf(output, "index %d in ", cur->index); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, + depth + 1); + } else { + fprintf(output, "Object is a range :\n"); + fprintf(output, shift); + fprintf(output, "From "); + if (cur->index >= 0) + fprintf(output, "index %d in ", cur->index); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, + depth + 1); + fprintf(output, shift); + fprintf(output, "To "); + if (cur->index2 >= 0) + fprintf(output, "index %d in ", cur->index2); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2, + depth + 1); + fprintf(output, "\n"); + } + break; + case XPATH_LOCATIONSET: +#if defined(LIBXML_XPTR_ENABLED) + fprintf(output, "Object is a Location Set:\n"); + xmlXPathDebugDumpLocationSet(output, + (xmlLocationSetPtr) cur->user, depth); +#endif + break; + case XPATH_USERS: + fprintf(output, "Object is user defined\n"); + break; + } +} +#endif + +/************************************************************************ + * * + * Parser stacks related functions and macros * + * * + ************************************************************************/ + /* * Generic function for accessing stacks in the Parser Context */ @@ -672,30 +840,31 @@ xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, return(-1); for (i = 0;i < ctxt->nb_funcs;i++) { - if (xmlStrEqual(ctxt->funcs[i]->name, name)) { + if (xmlStrEqual(ctxt->funcs[i].name, name)) { /* * It's just an update or a removal */ - ctxt->funcs[i]->func = f; + ctxt->funcs[i].func = f; return(0); } } if (ctxt->max_funcs <= 0) { ctxt->max_funcs = 10; ctxt->nb_funcs = 0; - ctxt->funcs = (xmlXPathFuncPtr *) xmlMalloc(ctxt->max_funcs * - sizeof(xmlXPathFuncPtr)); + ctxt->funcs = (xmlXPathFuncPtr) xmlMalloc(ctxt->max_funcs * + sizeof(xmlXPathFunct)); } else if (ctxt->max_funcs <= ctxt->nb_funcs) { ctxt->max_funcs *= 2; - ctxt->funcs = (xmlXPathFuncPtr *) xmlRealloc(ctxt->funcs, - ctxt->max_funcs * sizeof(xmlXPathFuncPtr)); + ctxt->funcs = (xmlXPathFuncPtr) xmlRealloc(ctxt->funcs, + ctxt->max_funcs * sizeof(xmlXPathFunct)); } if (ctxt->funcs == NULL) { fprintf(xmlXPathDebug, "xmlXPathRegisterFunc: out of memory\n"); return(-1); } - ctxt->funcs[i]->name = xmlStrdup(name); - ctxt->funcs[i]->func = f; + ctxt->funcs[ctxt->nb_funcs].name = xmlStrdup(name); + ctxt->funcs[ctxt->nb_funcs].func = f; + ctxt->nb_funcs++; return(0); } @@ -719,8 +888,8 @@ xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { return(NULL); for (i = 0;i < ctxt->nb_funcs;i++) { - if (xmlStrEqual(ctxt->funcs[i]->name, name)) { - return(ctxt->funcs[i]->func); + if (xmlStrEqual(ctxt->funcs[i].name, name)) { + return(ctxt->funcs[i].func); } } return(NULL); @@ -740,7 +909,7 @@ xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) { return; for (i = 0;i < ctxt->nb_funcs;i++) { - xmlFree((xmlChar *) ctxt->funcs[i]->name); + xmlFree((xmlChar *) ctxt->funcs[i].name); } ctxt->nb_funcs = -1; ctxt->max_funcs = -1; @@ -777,34 +946,35 @@ xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name, return(-1); for (i = 0;i < ctxt->nb_variables;i++) { - if (xmlStrEqual(ctxt->variables[i]->name, name)) { + if (xmlStrEqual(ctxt->variables[i].name, name)) { /* * It's just an update or a removal */ - if (ctxt->variables[i]->value != NULL) { - xmlXPathFreeObject(ctxt->variables[i]->value); + if (ctxt->variables[i].value != NULL) { + xmlXPathFreeObject(ctxt->variables[i].value); } - ctxt->variables[i]->value = xmlXPathObjectCopy(value); + ctxt->variables[i].value = xmlXPathObjectCopy(value); return(0); } } if (ctxt->max_variables <= 0) { ctxt->max_variables = 10; ctxt->nb_variables = 0; - ctxt->variables = (xmlXPathVariablePtr *) - xmlMalloc(ctxt->max_variables * sizeof(xmlXPathVariablePtr)); + ctxt->variables = (xmlXPathVariablePtr) + xmlMalloc(ctxt->max_variables * sizeof(xmlXPathVariable)); } else if (ctxt->max_variables <= ctxt->nb_variables) { ctxt->max_variables *= 2; - ctxt->variables = (xmlXPathVariablePtr *) + ctxt->variables = (xmlXPathVariablePtr) xmlRealloc(ctxt->variables, - ctxt->max_variables * sizeof(xmlXPathVariablePtr)); + ctxt->max_variables * sizeof(xmlXPathVariable)); } if (ctxt->variables == NULL) { fprintf(xmlXPathDebug, "xmlXPathRegisterVariable: out of memory\n"); return(-1); } - ctxt->variables[i]->name = xmlStrdup(name); - ctxt->variables[i]->value = xmlXPathObjectCopy(value); + ctxt->variables[ctxt->nb_variables].name = xmlStrdup(name); + ctxt->variables[ctxt->nb_variables].value = xmlXPathObjectCopy(value); + ctxt->nb_variables++; return(0); } @@ -828,8 +998,8 @@ xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { return(NULL); for (i = 0;i < ctxt->nb_variables;i++) { - if (xmlStrEqual(ctxt->variables[i]->name, name)) { - return(xmlXPathObjectCopy(ctxt->variables[i]->value)); + if (xmlStrEqual(ctxt->variables[i].name, name)) { + return(xmlXPathObjectCopy(ctxt->variables[i].value)); } } return(NULL); @@ -849,8 +1019,8 @@ xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) { return; for (i = 0;i < ctxt->nb_variables;i++) { - xmlFree((xmlChar *) ctxt->variables[i]->name); - xmlXPathFreeObject(ctxt->variables[i]->value); + xmlFree((xmlChar *) ctxt->variables[i].name); + xmlXPathFreeObject(ctxt->variables[i].value); } ctxt->nb_variables = -1; ctxt->max_variables = -1; diff --git a/xpath.h b/xpath.h index 533ca3fc..81a11f62 100644 --- a/xpath.h +++ b/xpath.h @@ -178,19 +178,19 @@ struct _xmlXPathContext { int nb_variables; /* number of defined variables */ int max_variables; /* max number of variables */ - xmlXPathVariablePtr *variables; /* Array of defined variables */ + xmlXPathVariablePtr variables; /* Array of defined variables */ int nb_types; /* number of defined types */ int max_types; /* max number of types */ - xmlXPathTypePtr *types; /* Array of defined types */ + xmlXPathTypePtr types; /* Array of defined types */ int nb_funcs; /* number of defined funcs */ int max_funcs; /* max number of funcs */ - xmlXPathFuncPtr *funcs; /* Array of defined funcs */ + xmlXPathFuncPtr funcs; /* Array of defined funcs */ int nb_axis; /* number of defined axis */ int max_axis; /* max number of axis */ - xmlXPathAxisPtr *axis; /* Array of defined axis */ + xmlXPathAxisPtr axis; /* Array of defined axis */ /* Namespace traversal should be implemented with user */ xmlNsPtr *namespaces; /* The namespaces lookup */ @@ -266,6 +266,10 @@ void xmlXPatherror (xmlXPathParserContextPtr ctxt, int line, int no); +void xmlXPathDebugDumpObject (FILE *output, + xmlXPathObjectPtr cur, + int depth); + /** * Utilities to extend XPath (XPointer) */ diff --git a/xpointer.c b/xpointer.c index 2756057d..3403bc3f 100644 --- a/xpointer.c +++ b/xpointer.c @@ -18,6 +18,10 @@ /** * TODO: better handling of error cases, the full expression should * be parsed beforehand instead of a progressive evaluation + * TODO: Access into entities references are not supported now ... + * need a start to be able to pop out of entities refs since + * parent is the endity declaration, not the ref. + * TODO: some functions are still missing ! */ #include @@ -25,8 +29,15 @@ #include #include #include +#ifdef LIBXML_DEBUG_ENABLED +#include +#endif #ifdef LIBXML_XPTR_ENABLED + +/* #define DEBUG_RANGES */ + + extern FILE *xmlXPathDebug; #define TODO \ @@ -73,6 +84,45 @@ xmlXPtrNewPoint(xmlNodePtr node, int index) { return(ret); } +/** + * xmlXPtrNewRange: + * @start: the starting node + * @startindex: the start index + * @end: the ending point + * @endindex: the ending index + * + * Create a new xmlXPathObjectPtr of type range + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRange(xmlNodePtr start, int startindex, + xmlNodePtr end, int endindex) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + if (startindex < 0) + return(NULL); + if (endindex < 0) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + fprintf(xmlXPathDebug, "xmlXPtrNewRangePoints: out of memory\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = startindex; + ret->user2 = end; + ret->index2 = endindex; + return(ret); +} + /** * xmlXPtrNewRangePoints: * @start: the starting point @@ -381,11 +431,10 @@ xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { /** * xmlXPtrLocationSetMerge: - * @val1: the first LocationSet or NULL + * @val1: the first LocationSet * @val2: the second LocationSet * * Merges two rangesets, all ranges from @val2 are added to @val1 - * if @val1 is NULL, a new set is created and copied from @val2 * * Returns val1 once extended or NULL in case of error. */ @@ -393,12 +442,8 @@ xmlLocationSetPtr xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) { int i; + if (val1 == NULL) return(NULL); if (val2 == NULL) return(val1); - if (val1 == NULL) { - val1 = xmlXPtrLocationSetCreate(NULL); - if (val1 == NULL) - return(NULL); - } /* * !!!!! this can be optimized a lot, knowing that both @@ -661,6 +706,33 @@ xmlXPtrWrapLocationSet(xmlLocationSetPtr val) { #define CURRENT (*ctxt->cur) #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) +/** + * xmlXPtrGetNthChild: + * @cur: the node + * @no: the child number + * + * Returns the @no'th element child of @cur or NULL + */ +xmlNodePtr +xmlXPtrGetNthChild(xmlNodePtr cur, int no) { + int i; + if (cur == NULL) + return(cur); + cur = cur->children; + for (i = 0;i <= no;cur = cur->next) { + if (cur == NULL) + return(cur); + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + i++; + if (i == no) + break; + } + } + return(cur); +} + /* * xmlXPtrGetChildNo: * @ctxt: the XPointer Parser context @@ -674,7 +746,6 @@ xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int index) { xmlNodePtr cur = NULL; xmlXPathObjectPtr obj; xmlNodeSetPtr oldset; - int i; CHECK_TYPE(XPATH_NODESET); obj = valuePop(ctxt); @@ -684,23 +755,7 @@ xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int index) { valuePush(ctxt, xmlXPathNewNodeSet(NULL)); return; } - cur = oldset->nodeTab[0]; - if (cur == NULL) - goto done; - cur = cur->children; - for (i = 0;i <= index;cur = cur->next) { - if (cur == NULL) - goto done; - if ((cur->type == XML_ELEMENT_NODE) || - (cur->type == XML_DOCUMENT_NODE) || - (cur->type == XML_HTML_DOCUMENT_NODE)) { - i++; - if (i == index) - break; - } - } - -done: + cur = xmlXPtrGetNthChild(oldset->nodeTab[0], index); if (cur == NULL) { xmlXPathFreeObject(obj); valuePush(ctxt, xmlXPathNewNodeSet(NULL)); @@ -802,13 +857,9 @@ xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { if (xmlStrEqual(name, (xmlChar *) "xpointer")) { const xmlChar *left = CUR_PTR; - xmlXPathObjectPtr root = NULL; CUR_PTR = buffer; - if (buffer[0] == '/') { - xmlXPathRoot(ctxt); - root = ctxt->value; - } + xmlXPathRoot(ctxt); xmlXPathEvalExpr(ctxt); CUR_PTR=left; } else { @@ -986,6 +1037,13 @@ xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) { * * ************************************************************************/ +void xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs); + /** * xmlXPtrNewContext: * @doc: the XML document @@ -1008,6 +1066,19 @@ xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) { ret->here = here; ret->origin = origin; + xmlXPathRegisterFunc(ret, (xmlChar *)"range-to", + xmlXPtrRangeToFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"string-range", + xmlXPtrStringRangeFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"start-point", + xmlXPtrStartPointFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"end-point", + xmlXPtrEndPointFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"here", + xmlXPtrHereFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)" origin", + xmlXPtrOriginFunction); + return(ret); } @@ -1054,9 +1125,20 @@ xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) { do { tmp = valuePop(ctxt); if (tmp != NULL) { + if (tmp != init) { + if (tmp->type == XPATH_NODESET) { + /* + * Evaluation may push a root nodeset which is unused + */ + xmlNodeSetPtr set; + set = tmp->nodesetval; + if ((set->nodeNr != 1) || + (set->nodeTab[0] != (xmlNodePtr) ctx->doc)) + stack++; + } else + stack++; + } xmlXPathFreeObject(tmp); - if (tmp != init) - stack++; } } while (tmp != NULL); if (stack != 0) { @@ -1125,14 +1207,14 @@ xmlXPtrNbLocChildren(xmlNodePtr node) { } /** - * xmlXPtrHere: + * xmlXPtrHereFunction: * @ctxt: the XPointer Parser context * * Function implementing here() operation * as described in 5.4.3 */ void -xmlXPtrHere(xmlXPathParserContextPtr ctxt, int nargs) { +xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt->context->here == NULL) XP_ERROR(XPTR_SYNTAX_ERROR); @@ -1140,14 +1222,14 @@ xmlXPtrHere(xmlXPathParserContextPtr ctxt, int nargs) { } /** - * xmlXPtrOrigin: + * xmlXPtrOriginFunction: * @ctxt: the XPointer Parser context * * Function implementing origin() operation * as described in 5.4.3 */ void -xmlXPtrOrigin(xmlXPathParserContextPtr ctxt, int nargs) { +xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt->context->origin == NULL) XP_ERROR(XPTR_SYNTAX_ERROR); @@ -1155,7 +1237,7 @@ xmlXPtrOrigin(xmlXPathParserContextPtr ctxt, int nargs) { } /** - * xmlXPtrStartPoint: + * xmlXPtrStartPointFunction: * @ctxt: the XPointer Parser context * * Function implementing start-point() operation @@ -1177,7 +1259,7 @@ xmlXPtrOrigin(xmlXPathParserContextPtr ctxt, int nargs) { * */ void -xmlXPtrStartPoint(xmlXPathParserContextPtr ctxt, int nargs) { +xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr tmp, obj, point; xmlLocationSetPtr newset = NULL; xmlLocationSetPtr oldset = NULL; @@ -1245,7 +1327,7 @@ xmlXPtrStartPoint(xmlXPathParserContextPtr ctxt, int nargs) { } /** - * xmlXPtrEndPoint: + * xmlXPtrEndPointFunction: * @ctxt: the XPointer Parser context * * Function implementing end-point() operation @@ -1269,7 +1351,7 @@ xmlXPtrStartPoint(xmlXPathParserContextPtr ctxt, int nargs) { * ---------------------------- */ void -xmlXPtrEndPoint(xmlXPathParserContextPtr ctxt, int nargs) { +xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr tmp, obj, point; xmlLocationSetPtr newset = NULL; xmlLocationSetPtr oldset = NULL; @@ -1426,6 +1508,528 @@ xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs) { valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); } +/** + * xmlXPtrAdvanceNode: + * @cur: the node + * + * Advance to the next element or text node in document order + * TODO: add a stack for entering/exiting entities + * + * Returns -1 in case of failure, 0 otherwise + */ +xmlNodePtr +xmlXPtrAdvanceNode(xmlNodePtr cur) { +next: + if (cur == NULL) + return(NULL); + if (cur->children != NULL) { + cur = cur->children ; + goto found; + } + if (cur->next != NULL) { + cur = cur->next; + goto found; + } + do { + cur = cur->parent; + if (cur == NULL) return(NULL); + if (cur->next != NULL) { + cur = cur->next; + goto found; + } + } while (cur != NULL); + +found: + if ((cur->type != XML_ELEMENT_NODE) && + (cur->type != XML_TEXT_NODE) && + (cur->type != XML_DOCUMENT_NODE) && + (cur->type != XML_HTML_DOCUMENT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) + goto next; + if (cur->type == XML_ENTITY_REF_NODE) { + TODO + } + return(cur); +} + +/** + * xmlXPtrAdvanceChar: + * @node: the node + * @index: the index + * @bytes: the number of bytes + * + * Advance a point of the associated number of bytes (not UTF8 chars) + * + * Returns -1 in case of failure, 0 otherwise + */ +int +xmlXPtrAdvanceChar(xmlNodePtr *node, int *index, int bytes) { + xmlNodePtr cur; + int pos; + int len; + + if ((node == NULL) || (index == NULL)) + return(-1); + cur = *node; + if (cur == NULL) + return(-1); + pos = *index; + + while (bytes >= 0) { + /* + * First position to the beginning of the first text node + * corresponding to this point + */ + while ((cur != NULL) && + ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE))) { + if (pos > 0) { + cur = xmlXPtrGetNthChild(cur, pos); + pos = 0; + } else { + cur = xmlXPtrAdvanceNode(cur); + pos = 0; + } + } + if (cur == NULL) { + *node = NULL; + *index = 0; + return(-1); + } + + /* + * if there is no move needed return the current value. + */ + if (pos == 0) pos = 1; + if (bytes == 0) { + *node = cur; + *index = pos; + return(0); + } + /* + * We should have a text (or cdata) node ... + */ + len = 0; + if (cur->content != NULL) { + len = xmlStrlen(cur->content); + } + if (pos > len) { + /* Strange, the index in the text node is greater than it's len */ + STRANGE + pos = len; + } + if (pos + bytes >= len) { + bytes -= (len - pos); + cur = xmlXPtrAdvanceNode(cur); + cur = 0; + } + } + return(-1); +} + +/** + * xmlXPtrMatchString: + * @string: the string to search + * @start: the start textnode + * @startindex: the start index + * @end: the end textnode IN/OUT + * @endindex: the end index IN/OUT + * + * Check whether the document contains @string at the position + * (@start, @startindex) and limited by the (@end, @endindex) point + * + * Returns -1 in case of failure, 0 if not found, 1 if found in which case + * (@start, @startindex) will indicate the position of the beginning + * of the range and (@end, @endindex) will endicate the end + * of the range + */ +int +xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex, + xmlNodePtr *end, int *endindex) { + xmlNodePtr cur; + int pos; /* 0 based */ + int len; /* in bytes */ + int stringlen; /* in bytes */ + int match; + + if (string == NULL) + return(-1); + if (start == NULL) + return(-1); + if ((end == NULL) || (endindex == NULL)) + return(-1); + cur = start; + if (cur == NULL) + return(-1); + pos = startindex - 1; + stringlen = xmlStrlen(string); + + while (stringlen > 0) { + if ((cur == *end) && (pos + stringlen > *endindex)) + return(0); + if (cur->content != NULL) { + len = xmlStrlen(cur->content); + if (len >= pos + stringlen) { + match = (!xmlStrncmp(&cur->content[pos], string, stringlen)); + if (match) { +#ifdef DEBUG_RANGES + fprintf(stdout, "found range %d bytes at index %d of ->", + stringlen, pos + 1); + xmlDebugDumpString(stdout, cur->content); + fprintf(stdout, "\n"); +#endif + *end = cur; + *endindex = pos + stringlen; + return(1); + } else { + return(0); + } + } else { + int sub = len - pos; + match = (!xmlStrncmp(&cur->content[pos], string, sub)); + if (match) { +#ifdef DEBUG_RANGES + fprintf(stdout, "found subrange %d bytes at index %d of ->", + sub, pos + 1); + xmlDebugDumpString(stdout, cur->content); + fprintf(stdout, "\n"); +#endif + string = &string[sub]; + stringlen -= sub; + } else { + return(0); + } + } + } + cur = xmlXPtrAdvanceNode(cur); + if (cur == NULL) + return(0); + pos = 0; + } + return(1); +} + +/** + * xmlXPtrSearchString: + * @string: the string to search + * @start: the start textnode IN/OUT + * @startindex: the start index IN/OUT + * @end: the end textnode + * @endindex: the end index + * + * Search the next occurence of @string within the document content + * until the (@end, @endindex) point is reached + * + * Returns -1 in case of failure, 0 if not found, 1 if found in which case + * (@start, @startindex) will indicate the position of the beginning + * of the range and (@end, @endindex) will endicate the end + * of the range + */ +int +xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex, + xmlNodePtr *end, int *endindex) { + xmlNodePtr cur; + const xmlChar *str; + int pos; /* 0 based */ + int len; /* in bytes */ + int stringlen; /* in bytes */ + xmlChar first; + + if (string == NULL) + return(-1); + if ((start == NULL) || (startindex == NULL)) + return(-1); + if ((end == NULL) || (endindex == NULL)) + return(-1); + cur = *start; + if (cur == NULL) + return(-1); + pos = *startindex - 1; + first = string[0]; + stringlen = xmlStrlen(string); + + /* TODO: first = 0 */ + while (cur != NULL) { + if (cur->content != NULL) { + len = xmlStrlen(cur->content); + while (pos <= len) { + str = xmlStrchr(&cur->content[pos], first); + if (str != NULL) { + pos = (str - cur->content); +#ifdef DEBUG_RANGES + fprintf(stdout, "found '%c' at index %d of ->", + first, pos + 1); + xmlDebugDumpString(stdout, cur->content); + fprintf(stdout, "\n"); +#endif + if (xmlXPtrMatchString(string, cur, pos + 1, + end, endindex)) { + *start = cur; + *startindex = pos + 1; + return(1); + } + pos++; + } else { + pos = len + 1; + } + } + } + if ((cur == *end) && (pos >= *endindex)) + return(0); + cur = xmlXPtrAdvanceNode(cur); + if (cur == NULL) + return(0); + pos = 1; + } + return(0); +} + +/** + * xmlXPtrGetLastChar: + * @node: the node + * @index: the index + * + * Computes the point coordinates of the last char of this point + * + * Returns -1 in case of failure, 0 otherwise + */ +int +xmlXPtrGetLastChar(xmlNodePtr *node, int *index) { + xmlNodePtr cur; + int pos, len = 0; + + if ((node == NULL) || (index == NULL)) + return(-1); + cur = *node; + pos = *index; + + if (cur == NULL) + return(-1); + + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + if (pos > 0) { + cur = xmlXPtrGetNthChild(cur, pos); + pos = 0; + } + } + while (cur != NULL) { + if (cur->last != NULL) + cur = cur->last; + else if (cur->content != NULL) { + len = xmlStrlen(cur->content); + break; + } + } + if (cur == NULL) + return(-1); + *node = cur; + *index = len; + return(0); +} + +/** + * xmlXPtrGetStartPoint: + * @obj: an range + * @node: the resulting node + * @index: the resulting index + * + * read the object and return the start point coordinates. + * + * Returns -1 in case of failure, 0 otherwise + */ +int +xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *index) { + if ((obj == NULL) || (node == NULL) || (index == NULL)) + return(-1); + + switch (obj->type) { + case XPATH_POINT: + *node = obj->user; + if (obj->index <= 0) + *index = 0; + else + *index = obj->index; + return(0); + case XPATH_RANGE: + *node = obj->user; + if (obj->index <= 0) + *index = 0; + else + *index = obj->index; + return(0); + default: + return(-1); + } + return(-1); +} + +/** + * xmlXPtrGetEndPoint: + * @obj: an range + * @node: the resulting node + * @index: the resulting index + * + * read the object and return the end point coordinates. + * + * Returns -1 in case of failure, 0 otherwise + */ +int +xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *index) { + if ((obj == NULL) || (node == NULL) || (index == NULL)) + return(-1); + + switch (obj->type) { + case XPATH_POINT: + *node = obj->user; + if (obj->index <= 0) + *index = 0; + else + *index = obj->index; + return(0); + case XPATH_RANGE: + *node = obj->user; + if (obj->index <= 0) + *index = 0; + else + *index = obj->index; + return(0); + default: + return(-1); + } + return(-1); +} + +/** + * xmlXPtrStringRangeFunction: + * @ctxt: the XPointer Parser context + * + * Function implementing the string-range() function + * range as described in 5.4.2 + * + * ------------------------------ + * [Definition: For each location in the location-set argument, + * string-range returns a set of string ranges, a set of substrings in a + * string. Specifically, the string-value of the location is searched for + * substrings that match the string argument, and the resulting location-set + * will contain a range location for each non-overlapping match.] + * An empty string is considered to match before each character of the + * string-value and after the final character. Whitespace in a string + * is matched literally, with no normalization except that provided by + * XML for line ends. The third argument gives the position of the first + * character to be in the resulting range, relative to the start of the + * match. The default value is 1, which makes the range start immediately + * before the first character of the matched string. The fourth argument + * gives the number of characters in the range; the default is that the + * range extends to the end of the matched string. + * + * Element boundaries, as well as entire embedded nodes such as processing + * instructions and comments, are ignored as defined in [XPath]. + * + * If the string in the second argument is not found in the string-value + * of the location, or if a value in the third or fourth argument indicates + * a string that is beyond the beginning or end of the document, the + * expression fails. + * + * The points of the range-locations in the returned location-set will + * all be character points. + * ------------------------------ + */ +void +xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { + int i, startindex, endindex, fendindex; + xmlNodePtr start, end, fend; + xmlXPathObjectPtr set; + xmlLocationSetPtr oldset; + xmlLocationSetPtr newset; + xmlXPathObjectPtr string; + xmlXPathObjectPtr position = NULL; + xmlXPathObjectPtr number = NULL; + int found; + + /* + * Grab the arguments + */ + if ((nargs < 2) || (nargs > 4)) + XP_ERROR(XPATH_INVALID_ARITY); + + if (nargs >= 4) { + CHECK_TYPE(XPATH_NUMBER); + number = valuePop(ctxt); + } + if (nargs >= 3) { + CHECK_TYPE(XPATH_NUMBER); + position = valuePop(ctxt); + } + CHECK_TYPE(XPATH_STRING); + string = valuePop(ctxt); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + set = valuePop(ctxt); + if (set->type == XPATH_NODESET) { + xmlXPathObjectPtr tmp; + + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); + xmlXPathFreeObject(set); + set = tmp; + } + oldset = (xmlLocationSetPtr) set->user; + + /* + * The loop is to search for each element in the location set + * the list of location set corresponding to that search + */ + newset = xmlXPtrLocationSetCreate(NULL); + for (i = 0;i < oldset->locNr;i++) { +#ifdef DEBUG_RANGES + xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0); +#endif + + xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex); + xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex); + xmlXPtrAdvanceChar(&start, &startindex, 0); + xmlXPtrGetLastChar(&end, &endindex); + +#ifdef DEBUG_RANGES + fprintf(stdout, "from index %d of ->", startindex); + xmlDebugDumpString(stdout, start->content); + fprintf(stdout, "\n"); + fprintf(stdout, "to index %d of ->", endindex); + xmlDebugDumpString(stdout, end->content); + fprintf(stdout, "\n"); +#endif + do { + fend = end; + fendindex = endindex; + found = xmlXPtrSearchString(string->stringval, &start, &startindex, + &fend, &fendindex); + if (found == 1) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewRange(start, startindex, fend, fendindex)); + start = fend; + startindex = fendindex; + } + } while (found == 1); + } + + /* + * Save the new value and cleanup + */ + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); + xmlXPathFreeObject(set); + xmlXPathFreeObject(string); + if (position) xmlXPathFreeObject(position); + if (number) xmlXPathFreeObject(number); +} + #else #endif