/* * xpointer.c : Code to handle XML Pointer * * World Wide Web Consortium Working Draft 03-March-1998 * http://www.w3.org/TR/2000/CR-xptr-20000607 * * See Copyright for the status of this software. * * Daniel.Veillard@w3.org */ #ifdef WIN32 #include "win32config.h" #else #include "config.h" #endif /** * 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 #include #include #include #include #ifdef LIBXML_DEBUG_ENABLED #include #endif #ifdef LIBXML_XPTR_ENABLED /* #define DEBUG_RANGES */ extern FILE *xmlXPathDebug; #define TODO \ fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); #define STRANGE \ fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \ __FILE__, __LINE__); /************************************************************************ * * * Handling of XPointer specific types * * * ************************************************************************/ /** * xmlXPtrCmpPoints: * @node1: the first node * @index1: the first index * @node2: the second node * @index2: the second index * * Compare two points w.r.t document order * * Returns -2 in case of error 1 if first point < second point, 0 if * that's the same point, -1 otherwise */ int xmlXPtrCmpPoints(xmlNodePtr node1, int index1, xmlNodePtr node2, int index2) { int depth1, depth2; xmlNodePtr cur, root; if ((node1 == NULL) || (node2 == NULL)) return(-2); /* * a couple of optimizations which will avoid computations in most cases */ if (node1 == node2) { if (index1 < index2) return(1); if (index1 > index2) return(-1); return(0); } if (node1 == node2->prev) return(1); if (node1 == node2->next) return(-1); /* * compute depth to root */ for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { if (cur == node1) return(1); depth2++; } root = cur; for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { if (cur == node2) return(-1); depth1++; } /* * Distinct document (or distinct entities :-( ) case. */ if (root != cur) { return(-2); } /* * get the nearest common ancestor. */ while (depth1 > depth2) { depth1--; node1 = node1->parent; } while (depth2 > depth1) { depth2--; node2 = node2->parent; } while (node1->parent != node2->parent) { node1 = node1->parent; node2 = node2->parent; /* should not happen but just in case ... */ if ((node1 == NULL) || (node2 == NULL)) return(-2); } /* * Find who's first. */ if (node1 == node2->next) return(-1); for (cur = node1->next;cur != NULL;cur = cur->next) if (cur == node2) return(1); return(-1); /* assume there is no sibling list corruption */ } /** * xmlXPtrNewPoint: * @node: the xmlNodePtr * @index: the index within the node * * Create a new xmlXPathObjectPtr of type point * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewPoint(xmlNodePtr node, int index) { xmlXPathObjectPtr ret; if (node == NULL) return(NULL); if (index < 0) return(NULL); ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewPoint: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_POINT; ret->user = (void *) node; ret->index = index; return(ret); } /** * xmlXPtrRangeCheckOrder: * @range: an object range * * Make sure the points in the range are in the right order */ void xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range) { int tmp; xmlNodePtr tmp2; if (range == NULL) return; if (range->type != XPATH_RANGE) return; if (range->user2 == NULL) return; tmp = xmlXPtrCmpPoints(range->user, range->index, range->user2, range->index2); if (tmp == -1) { tmp2 = range->user; range->user = range->user2; range->user2 = tmp2; tmp = range->index; range->index = range->index2; range->index2 = tmp; } } /** * 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; xmlXPtrRangeCheckOrder(ret); return(ret); } /** * xmlXPtrNewRangePoints: * @start: the starting point * @end: the ending point * * Create a new xmlXPathObjectPtr of type range using 2 Points * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewRangePoints(xmlXPathObjectPtr start, xmlXPathObjectPtr end) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); if (end == NULL) return(NULL); if (start->type != XPATH_POINT) return(NULL); if (end->type != XPATH_POINT) 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->user; ret->index = start->index; ret->user2 = end->user; ret->index2 = end->index; xmlXPtrRangeCheckOrder(ret); return(ret); } /** * xmlXPtrNewRangePointNode: * @start: the starting point * @end: the ending node * * Create a new xmlXPathObjectPtr of type range from a point to a node * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewRangePointNode(xmlXPathObjectPtr start, xmlNodePtr end) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); if (end == NULL) return(NULL); if (start->type != XPATH_POINT) return(NULL); ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewRangePointNode: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_RANGE; ret->user = start->user; ret->index = start->index; ret->user2 = end; ret->index2 = -1; xmlXPtrRangeCheckOrder(ret); return(ret); } /** * xmlXPtrNewRangeNodePoint: * @start: the starting node * @end: the ending point * * Create a new xmlXPathObjectPtr of type range from a node to a point * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewRangeNodePoint(xmlNodePtr start, xmlXPathObjectPtr end) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); if (end == NULL) return(NULL); if (start->type != XPATH_POINT) return(NULL); if (end->type != XPATH_POINT) return(NULL); ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewRangeNodePoint: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_RANGE; ret->user = start; ret->index = -1; ret->user2 = end->user; ret->index2 = end->index; xmlXPtrRangeCheckOrder(ret); return(ret); } /** * xmlXPtrNewRangeNodes: * @start: the starting node * @end: the ending node * * Create a new xmlXPathObjectPtr of type range using 2 nodes * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewRangeNodes(xmlNodePtr start, xmlNodePtr end) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); if (end == NULL) return(NULL); ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewRangeNodes: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_RANGE; ret->user = start; ret->index = -1; ret->user2 = end; ret->index2 = -1; xmlXPtrRangeCheckOrder(ret); return(ret); } /** * xmlXPtrNewCollapsedRange: * @start: the starting and ending node * * Create a new xmlXPathObjectPtr of type range using a single nodes * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewCollapsedRange(xmlNodePtr start) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewRangeNodes: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_RANGE; ret->user = start; ret->index = -1; ret->user2 = NULL; ret->index2 = -1; return(ret); } /** * xmlXPtrNewRangeNodeObject: * @start: the starting node * @end: the ending object * * Create a new xmlXPathObjectPtr of type range from a not to an object * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewRangeNodeObject(xmlNodePtr start, xmlXPathObjectPtr end) { xmlXPathObjectPtr ret; if (start == NULL) return(NULL); if (end == NULL) return(NULL); switch (end->type) { case XPATH_POINT: break; case XPATH_NODESET: /* * Empty set ... */ if (end->nodesetval->nodeNr <= 0) return(NULL); break; default: TODO return(NULL); } ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewRangeNodeObject: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_RANGE; ret->user = start; ret->index = -1; switch (end->type) { case XPATH_POINT: ret->user2 = end->user; ret->index2 = end->index; case XPATH_NODESET: { ret->user2 = end->nodesetval->nodeTab[end->nodesetval->nodeNr - 1]; ret->index2 = -1; break; } default: STRANGE return(NULL); } xmlXPtrRangeCheckOrder(ret); return(ret); } #define XML_RANGESET_DEFAULT 10 /** * xmlXPtrLocationSetCreate: * @val: an initial xmlXPathObjectPtr, or NULL * * Create a new xmlLocationSetPtr of type double and of value @val * * Returns the newly created object. */ xmlLocationSetPtr xmlXPtrLocationSetCreate(xmlXPathObjectPtr val) { xmlLocationSetPtr ret; ret = (xmlLocationSetPtr) xmlMalloc(sizeof(xmlLocationSet)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrLocationSetCreate: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlLocationSet)); if (val != NULL) { ret->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * sizeof(xmlXPathObjectPtr)); if (ret->locTab == NULL) { fprintf(xmlXPathDebug, "xmlXPtrLocationSetCreate: out of memory\n"); return(NULL); } memset(ret->locTab, 0 , XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); ret->locMax = XML_RANGESET_DEFAULT; ret->locTab[ret->locNr++] = val; } return(ret); } /** * xmlXPtrLocationSetAdd: * @cur: the initial range set * @val: a new xmlXPathObjectPtr * * add a new xmlXPathObjectPtr ot an existing LocationSet */ void xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { int i; if (val == NULL) return; /* * check against doublons */ for (i = 0;i < cur->locNr;i++) if (cur->locTab[i] == val) return; /* * grow the locTab if needed */ if (cur->locMax == 0) { cur->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * sizeof(xmlXPathObjectPtr)); if (cur->locTab == NULL) { fprintf(xmlXPathDebug, "xmlXPtrLocationSetAdd: out of memory\n"); return; } memset(cur->locTab, 0 , XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); cur->locMax = XML_RANGESET_DEFAULT; } else if (cur->locNr == cur->locMax) { xmlXPathObjectPtr *temp; cur->locMax *= 2; temp = (xmlXPathObjectPtr *) xmlRealloc(cur->locTab, cur->locMax * sizeof(xmlXPathObjectPtr)); if (temp == NULL) { fprintf(xmlXPathDebug, "xmlXPtrLocationSetAdd: out of memory\n"); return; } cur->locTab = temp; } cur->locTab[cur->locNr++] = val; } /** * xmlXPtrLocationSetMerge: * @val1: the first LocationSet * @val2: the second LocationSet * * Merges two rangesets, all ranges from @val2 are added to @val1 * * Returns val1 once extended or NULL in case of error. */ xmlLocationSetPtr xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr 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->locNr;i++) xmlXPtrLocationSetAdd(val1, val2->locTab[i]); return(val1); } /** * xmlXPtrLocationSetDel: * @cur: the initial range set * @val: an xmlXPathObjectPtr * * Removes an xmlXPathObjectPtr from an existing LocationSet */ void xmlXPtrLocationSetDel(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { int i; if (cur == NULL) return; if (val == NULL) return; /* * check against doublons */ for (i = 0;i < cur->locNr;i++) if (cur->locTab[i] == val) break; if (i >= cur->locNr) { #ifdef DEBUG fprintf(xmlXPathDebug, "xmlXPtrLocationSetDel: Range %s wasn't found in RangeList\n", val->name); #endif return; } cur->locNr--; for (;i < cur->locNr;i++) cur->locTab[i] = cur->locTab[i + 1]; cur->locTab[cur->locNr] = NULL; } /** * xmlXPtrLocationSetRemove: * @cur: the initial range set * @val: the index to remove * * Removes an entry from an existing LocationSet list. */ void xmlXPtrLocationSetRemove(xmlLocationSetPtr cur, int val) { if (cur == NULL) return; if (val >= cur->locNr) return; cur->locNr--; for (;val < cur->locNr;val++) cur->locTab[val] = cur->locTab[val + 1]; cur->locTab[cur->locNr] = NULL; } /** * xmlXPtrFreeLocationSet: * @obj: the xmlLocationSetPtr to free * * Free the LocationSet compound (not the actual ranges !). */ void xmlXPtrFreeLocationSet(xmlLocationSetPtr obj) { int i; if (obj == NULL) return; if (obj->locTab != NULL) { for (i = 0;i < obj->locNr; i++) { xmlXPathFreeObject(obj->locTab[i]); } #ifdef DEBUG memset(obj->locTab, 0xB , (size_t) sizeof(xmlXPathObjectPtr) * obj->locMax); #endif xmlFree(obj->locTab); } #ifdef DEBUG memset(obj, 0xB , (size_t) sizeof(xmlLocationSet)); #endif xmlFree(obj); } /** * xmlXPtrNewLocationSetNodes: * @start: the start NodePtr value * @end: the end NodePtr value or NULL * * Create a new xmlXPathObjectPtr of type LocationSet and initialize * it with the single range made of the two nodes @start and @end * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewLocationSetNodes(xmlNodePtr start, xmlNodePtr end) { xmlXPathObjectPtr ret; ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewLocationSetNodes: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_LOCATIONSET; if (end == NULL) ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewCollapsedRange(start)); else ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewRangeNodes(start,end)); return(ret); } /** * xmlXPtrNewLocationSetNodeSet: * @set: a node set * * Create a new xmlXPathObjectPtr of type LocationSet and initialize * it with all the nodes from @set * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set) { xmlXPathObjectPtr ret; ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrNewLocationSetNodes: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_LOCATIONSET; if (set != NULL) { int i; xmlLocationSetPtr newset; newset = xmlXPtrLocationSetCreate(NULL); if (newset == NULL) return(ret); for (i = 0;i < set->nodeNr;i++) xmlXPtrLocationSetAdd(newset, xmlXPtrNewCollapsedRange(set->nodeTab[i])); ret->user = (void *) newset; } return(ret); } /** * xmlXPtrWrapLocationSet: * @val: the LocationSet value * * Wrap the LocationSet @val in a new xmlXPathObjectPtr * * Returns the newly created object. */ xmlXPathObjectPtr xmlXPtrWrapLocationSet(xmlLocationSetPtr val) { xmlXPathObjectPtr ret; ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); if (ret == NULL) { fprintf(xmlXPathDebug, "xmlXPtrWrapLocationSet: out of memory\n"); return(NULL); } memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_LOCATIONSET; ret->user = (void *) val; return(ret); } /************************************************************************ * * * The parser * * * ************************************************************************/ /* * Macros for accessing the content. Those should be used only by the parser, * and not exported. * * Dirty macros, i.e. one need to make assumption on the context to use them * * CUR_PTR return the current pointer to the xmlChar to be parsed. * CUR returns the current xmlChar value, i.e. a 8 bit value * in ISO-Latin or UTF-8. * This should be used internally by the parser * only to compare to ASCII values otherwise it would break when * running with UTF-8 encoding. * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only * to compare on ASCII based substring. * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined * strings within the parser. * CURRENT Returns the current char value, with the full decoding of * UTF-8 if we are using this mode. It returns an int. * NEXT Skip to the next character, this does the proper decoding * in UTF-8 mode. It also pop-up unfinished entities on the fly. * It returns the pointer to the current xmlChar. */ #define CUR (*ctxt->cur) #define SKIP(val) ctxt->cur += (val) #define NXT(val) ctxt->cur[(val)] #define CUR_PTR ctxt->cur #define SKIP_BLANKS \ while (IS_BLANK(*(ctxt->cur))) NEXT #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 * @index: the child number * * Move the current node of the nodeset on the stack to the * given child if found */ void xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int index) { xmlNodePtr cur = NULL; xmlXPathObjectPtr obj; xmlNodeSetPtr oldset; CHECK_TYPE(XPATH_NODESET); obj = valuePop(ctxt); oldset = obj->nodesetval; if ((index <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) { xmlXPathFreeObject(obj); valuePush(ctxt, xmlXPathNewNodeSet(NULL)); return; } cur = xmlXPtrGetNthChild(oldset->nodeTab[0], index); if (cur == NULL) { xmlXPathFreeObject(obj); valuePush(ctxt, xmlXPathNewNodeSet(NULL)); return; } oldset->nodeTab[0] = cur; valuePush(ctxt, obj); } /** * xmlXPtrEvalXPtrPart: * @ctxt: the XPointer Parser context * @name: the preparsed Scheme for the XPtrPart * * XPtrPart ::= 'xpointer' '(' XPtrExpr ')' * | Scheme '(' SchemeSpecificExpr ')' * * Scheme ::= NCName - 'xpointer' [VC: Non-XPointer schemes] * * SchemeSpecificExpr ::= StringWithBalancedParens * * StringWithBalancedParens ::= * [^()]* ('(' StringWithBalancedParens ')' [^()]*)* * [VC: Parenthesis escaping] * * XPtrExpr ::= Expr [VC: Parenthesis escaping] * * VC: Parenthesis escaping: * The end of an XPointer part is signaled by the right parenthesis ")" * character that is balanced with the left parenthesis "(" character * that began the part. Any unbalanced parenthesis character inside the * expression, even within literals, must be escaped with a circumflex (^) * character preceding it. If the expression contains any literal * occurrences of the circumflex, each must be escaped with an additional * circumflex (that is, ^^). If the unescaped parentheses in the expression * are not balanced, a syntax error results. * * Parse and evaluate an XPtrPart. Basically it generates the unescaped * string and if the scheme is 'xpointer' it will call the XPath interprter. * * TODO: there is no new scheme registration mechanism */ void xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { xmlChar *buffer, *cur; int len; int level; if (name == NULL) name = xmlXPathParseName(ctxt); if (name == NULL) XP_ERROR(XPATH_EXPR_ERROR); if (CUR != '(') XP_ERROR(XPATH_EXPR_ERROR); NEXT; level = 1; len = xmlStrlen(ctxt->cur); len++; buffer = (xmlChar *) xmlMalloc(len * sizeof (xmlChar)); if (buffer == NULL) { fprintf(xmlXPathDebug, "xmlXPtrEvalXPtrPart: out of memory\n"); return; } cur = buffer; while (CUR != '0') { if (CUR == ')') { level--; if (level == 0) { NEXT; break; } *cur++ = CUR; } else if (CUR == '(') { level++; *cur++ = CUR; } else if (CUR == '^') { NEXT; if ((CUR == ')') || (CUR == '(') || (CUR == '^')) { *cur++ = CUR; } else { *cur++ = '^'; *cur++ = CUR; } } else { *cur++ = CUR; } NEXT; } *cur = 0; if ((level != 0) && (CUR == 0)) { xmlFree(buffer); XP_ERROR(XPTR_SYNTAX_ERROR); } if (xmlStrEqual(name, (xmlChar *) "xpointer")) { const xmlChar *left = CUR_PTR; CUR_PTR = buffer; xmlXPathRoot(ctxt); xmlXPathEvalExpr(ctxt); CUR_PTR=left; } else { fprintf(xmlXPathDebug, "unsupported scheme '%s'\n", name); } xmlFree(buffer); xmlFree(name); } /** * xmlXPtrEvalFullXPtr: * @ctxt: the XPointer Parser context * @name: the preparsed Scheme for the first XPtrPart * * FullXPtr ::= XPtrPart (S? XPtrPart)* * * As the specs says: * ----------- * When multiple XPtrParts are provided, they must be evaluated in * left-to-right order. If evaluation of one part fails, the nexti * is evaluated. The following conditions cause XPointer part failure: * * - An unknown scheme * - A scheme that does not locate any sub-resource present in the resource * - A scheme that is not applicable to the media type of the resource * * The XPointer application must consume a failed XPointer part and * attempt to evaluate the next one, if any. The result of the first * XPointer part whose evaluation succeeds is taken to be the fragment * located by the XPointer as a whole. If all the parts fail, the result * for the XPointer as a whole is a sub-resource error. * ----------- * * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based * expressions or other shemes. */ void xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) { if (name == NULL) name = xmlXPathParseName(ctxt); if (name == NULL) XP_ERROR(XPATH_EXPR_ERROR); while (name != NULL) { xmlXPtrEvalXPtrPart(ctxt, name); /* in case of syntax error, break here */ if (ctxt->error != XPATH_EXPRESSION_OK) return; /* * If the returned value is a non-empty nodeset * or location set, return here. */ if (ctxt->value != NULL) { xmlXPathObjectPtr obj = ctxt->value; switch (obj->type) { case XPATH_LOCATIONSET: { xmlLocationSetPtr loc = ctxt->value->user; if ((loc != NULL) && (loc->locNr > 0)) return; break; } case XPATH_NODESET: { xmlNodeSetPtr loc = ctxt->value->nodesetval; if ((loc != NULL) && (loc->nodeNr > 0)) return; break; } default: break; } /* * Evaluating to improper values is equivalent to * a sub-resource error, clean-up the stack */ do { obj = valuePop(ctxt); if (obj != NULL) { xmlXPathFreeObject(obj); } } while (obj != NULL); } /* * Is there another XPoointer part. */ SKIP_BLANKS; name = xmlXPathParseName(ctxt); } } /** * xmlXPtrEvalChildSeq: * @ctxt: the XPointer Parser context * @name: a possible ID name of the child sequence * * ChildSeq ::= '/1' ('/' [0-9]*)* * | Name ('/' [0-9]*)+ * * Parse and evaluate a Child Sequence. This routine also handle the * case of a Bare Name used to get a document ID. */ void xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) { /* * XPointer don't allow by syntax to adress in mutirooted trees * this might prove useful in some cases, warn about it. */ if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) { fprintf(xmlXPathDebug, "warning: ChildSeq not starting by /1\n"); } if (name != NULL) { valuePush(ctxt, xmlXPathNewString(name)); xmlFree(name); xmlXPathIdFunction(ctxt, 1); CHECK_ERROR; } while (CUR == '/') { int child = 0; NEXT; while ((CUR >= '0') && (CUR <= '9')) { child = child * 10 + (CUR - '0'); NEXT; } xmlXPtrGetChildNo(ctxt, child); } } /** * xmlXPtrEvalXPointer: * @ctxt: the XPointer Parser context * * XPointer ::= Name * | ChildSeq * | FullXPtr * * Parse and evaluate an XPointer */ void xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) { SKIP_BLANKS; if (CUR == '/') { xmlXPathRoot(ctxt); xmlXPtrEvalChildSeq(ctxt, NULL); } else { xmlChar *name; name = xmlXPathParseName(ctxt); if (name == NULL) XP_ERROR(XPATH_EXPR_ERROR); if (CUR == '(') { xmlXPtrEvalFullXPtr(ctxt, name); /* Short evaluation */ return; } else { /* this handle both Bare Names and Child Sequences */ xmlXPtrEvalChildSeq(ctxt, name); } } SKIP_BLANKS; if (CUR != 0) XP_ERROR(XPATH_EXPR_ERROR); } /************************************************************************ * * * General routines * * * ************************************************************************/ 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 * @here: the node that directly contains the XPointer being evaluated or NULL * @origin: the element from which a user or program initiated traversal of * the link, or NULL. * * Create a new XPointer context * * Returns the xmlXPathContext just allocated. */ xmlXPathContextPtr xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) { xmlXPathContextPtr ret; ret = xmlXPathNewContext(doc); if (ret == NULL) return(ret); ret->xptr = 1; 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); } /** * xmlXPtrEval: * @str: the XPointer expression * @ctx: the XPointer 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 xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) { xmlXPathParserContextPtr ctxt; xmlXPathObjectPtr res = NULL, tmp; xmlXPathObjectPtr init = NULL; int stack = 0; xmlXPathInit(); if ((ctx == NULL) || (str == NULL)) return(NULL); if (xmlXPathDebug == NULL) xmlXPathDebug = stderr; ctxt = xmlXPathNewParserContext(str, ctx); if (ctx->node != NULL) { init = xmlXPathNewNodeSet(ctx->node); valuePush(ctxt, init); } xmlXPtrEvalXPointer(ctxt); if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_NODESET) && (ctxt->value->type != XPATH_LOCATIONSET)) { fprintf(xmlXPathDebug, "xmlXPtrEval: evaluation failed to return a node set\n"); } else { res = valuePop(ctxt); } 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); } } while (tmp != NULL); if (stack != 0) { fprintf(xmlXPathDebug, "xmlXPtrEval: %d object left on the stack\n", stack); } if (ctxt->error != XPATH_EXPRESSION_OK) { xmlXPathFreeObject(res); res = NULL; } xmlXPathFreeParserContext(ctxt); return(res); } /************************************************************************ * * * XPointer functions * * * ************************************************************************/ /** * xmlXPtrNbLocChildren: * @node: an xmlNodePtr * * Count the number of location children of @node or the lenght of the * string value in case of text/PI/Comments nodes * * Returns the number of location children */ int xmlXPtrNbLocChildren(xmlNodePtr node) { int ret = 0; if (node == NULL) return(-1); switch (node->type) { case XML_HTML_DOCUMENT_NODE: case XML_DOCUMENT_NODE: case XML_ELEMENT_NODE: node = node->children; while (node != NULL) { if (node->type == XML_ELEMENT_NODE) ret++; node = node->next; } break; case XML_ATTRIBUTE_NODE: return(-1); case XML_PI_NODE: case XML_COMMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: #ifndef XML_USE_BUFFER_CONTENT ret = xmlStrlen(node->content); #else ret = xmlBufferLength(node->content); #endif break; default: return(-1); } return(ret); } /** * xmlXPtrHereFunction: * @ctxt: the XPointer Parser context * * Function implementing here() operation * as described in 5.4.3 */ void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt->context->here == NULL) XP_ERROR(XPTR_SYNTAX_ERROR); valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL)); } /** * xmlXPtrOriginFunction: * @ctxt: the XPointer Parser context * * Function implementing origin() operation * as described in 5.4.3 */ void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt->context->origin == NULL) XP_ERROR(XPTR_SYNTAX_ERROR); valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL)); } /** * xmlXPtrStartPointFunction: * @ctxt: the XPointer Parser context * * Function implementing start-point() operation * as described in 5.4.3 * ---------------- * location-set start-point(location-set) * * For each location x in the argument location-set, start-point adds a * location of type point to the result location-set. That point represents * the start point of location x and is determined by the following rules: * * - If x is of type point, the start point is x. * - If x is of type range, the start point is the start point of x. * - If x is of type root, element, text, comment, or processing instruction, * - the container node of the start point is x and the index is 0. * - If x is of type attribute or namespace, the function must signal a * syntax error. * ---------------- * */ void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr tmp, obj, point; xmlLocationSetPtr newset = NULL; xmlLocationSetPtr oldset = NULL; CHECK_ARITY(1); if ((ctxt->value == NULL) || ((ctxt->value->type != XPATH_LOCATIONSET) && (ctxt->value->type != XPATH_NODESET))) XP_ERROR(XPATH_INVALID_TYPE) obj = valuePop(ctxt); if (obj->type == XPATH_NODESET) { /* * First convert to a location set */ tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); xmlXPathFreeObject(obj); obj = tmp; } newset = xmlXPtrLocationSetCreate(NULL); oldset = (xmlLocationSetPtr) obj->user; if (oldset != NULL) { int i; for (i = 0; i < oldset->locNr; i++) { tmp = oldset->locTab[i]; if (tmp == NULL) continue; point = NULL; switch (tmp->type) { case XPATH_POINT: point = xmlXPtrNewPoint(tmp->user, tmp->index); break; case XPATH_RANGE: { xmlNodePtr node = tmp->user; if (node != NULL) { if (node->type == XML_ATTRIBUTE_NODE) { /* TODO: Namespace Nodes ??? */ xmlXPathFreeObject(obj); xmlXPtrFreeLocationSet(newset); XP_ERROR(XPTR_SYNTAX_ERROR); } point = xmlXPtrNewPoint(node, tmp->index); } if (tmp->user2 == NULL) { point = xmlXPtrNewPoint(node, 0); } else point = xmlXPtrNewPoint(node, tmp->index); break; } default: /*** Should we raise an error ? xmlXPathFreeObject(obj); xmlXPathFreeObject(newset); XP_ERROR(XPATH_INVALID_TYPE) ***/ break; } if (point != NULL) xmlXPtrLocationSetAdd(newset, point); } } xmlXPathFreeObject(obj); } /** * xmlXPtrEndPointFunction: * @ctxt: the XPointer Parser context * * Function implementing end-point() operation * as described in 5.4.3 * ---------------------------- * location-set end-point(location-set) * * For each location x in the argument location-set, end-point adds a * location of type point to the result location-set. That point representsi * the end point of location x and is determined by the following rules: * * - If x is of type point, the resulting point is x. * - If x is of type range, the resulting point is the end point of x. * - If x is of type root or element, the container node of the resulting * point is x and the index is the number of location children of x. * - If x is of type text, comment, or processing instruction, the container * node of the resulting point is x and the index is the length of thei * string-value of x. * - If x is of type attribute or namespace, the function must signal a * syntax error. * ---------------------------- */ void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr tmp, obj, point; xmlLocationSetPtr newset = NULL; xmlLocationSetPtr oldset = NULL; CHECK_ARITY(1); if ((ctxt->value == NULL) || ((ctxt->value->type != XPATH_LOCATIONSET) && (ctxt->value->type != XPATH_NODESET))) XP_ERROR(XPATH_INVALID_TYPE) obj = valuePop(ctxt); if (obj->type == XPATH_NODESET) { /* * First convert to a location set */ tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); xmlXPathFreeObject(obj); obj = tmp; } newset = xmlXPtrLocationSetCreate(NULL); oldset = (xmlLocationSetPtr) obj->user; if (oldset != NULL) { int i; for (i = 0; i < oldset->locNr; i++) { tmp = oldset->locTab[i]; if (tmp == NULL) continue; point = NULL; switch (tmp->type) { case XPATH_POINT: point = xmlXPtrNewPoint(tmp->user, tmp->index); break; case XPATH_RANGE: { xmlNodePtr node = tmp->user; if (node != NULL) { if (node->type == XML_ATTRIBUTE_NODE) { /* TODO: Namespace Nodes ??? */ xmlXPathFreeObject(obj); xmlXPtrFreeLocationSet(newset); XP_ERROR(XPTR_SYNTAX_ERROR); } point = xmlXPtrNewPoint(node, tmp->index); } if (tmp->user2 == NULL) { point = xmlXPtrNewPoint(node, xmlXPtrNbLocChildren(node)); } else point = xmlXPtrNewPoint(node, tmp->index); break; } default: /*** Should we raise an error ? xmlXPathFreeObject(obj); xmlXPathFreeObject(newset); XP_ERROR(XPATH_INVALID_TYPE) ***/ break; } if (point != NULL) xmlXPtrLocationSetAdd(newset, point); } } xmlXPathFreeObject(obj); } /** * xmlXPtrCoveringRange: * @ctxt: the XPointer Parser context * * Function implementing the range() operation of computing a covering * range as described in 5.3.3 Covering Ranges for All Location Types. */ void xmlXPtrRange(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); TODO } /** * xmlXPtrRangeToFunction: * @ctxt: the XPointer Parser context * * Implement the range-to() XPointer function */ void xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr range; const xmlChar *cur; xmlXPathObjectPtr res, obj; xmlXPathObjectPtr tmp; xmlLocationSetPtr newset = NULL; xmlNodeSetPtr oldset; int i; CHECK_ARITY(1); /* * Save the expression pointer since we will have to evaluate * it multiple times. Initialize the new set. */ CHECK_TYPE(XPATH_NODESET); obj = valuePop(ctxt); oldset = obj->nodesetval; ctxt->context->node = NULL; cur = ctxt->cur; newset = xmlXPtrLocationSetCreate(NULL); for (i = 0; i < oldset->nodeNr; i++) { ctxt->cur = cur; /* * Run the evaluation with a node list made of a single item * in the nodeset. */ ctxt->context->node = oldset->nodeTab[i]; tmp = xmlXPathNewNodeSet(ctxt->context->node); valuePush(ctxt, tmp); xmlXPathEvalExpr(ctxt); CHECK_ERROR; /* * The result of the evaluation need to be tested to * decided whether the filter succeeded or not */ res = valuePop(ctxt); range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); if (range != NULL) { xmlXPtrLocationSetAdd(newset, range); } /* * Cleanup */ if (res != NULL) xmlXPathFreeObject(res); if (ctxt->value == tmp) { res = valuePop(ctxt); xmlXPathFreeObject(res); } ctxt->context->node = NULL; } /* * The result is used as the new evaluation set. */ xmlXPathFreeObject(obj); ctxt->context->node = NULL; ctxt->context->contextSize = -1; ctxt->context->proximityPosition = -1; valuePush(ctxt, xmlXPtrWrapLocationSet(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); while (cur != NULL) { if (cur->content != NULL) { len = xmlStrlen(cur->content); while (pos <= len) { if (first != 0) { 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; } } else { /* * An empty string is considered to match before each * character of the string-value and after the final * character. */ #ifdef DEBUG_RANGES fprintf(stdout, "found '' at index %d of ->", pos + 1); xmlDebugDumpString(stdout, cur->content); fprintf(stdout, "\n"); #endif *start = cur; *startindex = pos + 1; *end = cur; *endindex = pos + 1; return(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; if (string->stringval[0] == 0) startindex++; } } 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