1 /*
2  * xpointer.c : Code to handle XML Pointer
3  *
4  * Base implementation was made accordingly to
5  * W3C Candidate Recommendation 7 June 2000
6  * http://www.w3.org/TR/2000/CR-xptr-20000607
7  *
8  * Added support for the element() scheme described in:
9  * W3C Proposed Recommendation 13 November 2002
10  * http://www.w3.org/TR/2002/PR-xptr-element-20021113/
11  *
12  * See Copyright for the status of this software.
13  *
14  * daniel@veillard.com
15  */
16 
17 #define IN_LIBXML
18 #include "libxml.h"
19 
20 /*
21  * TODO: better handling of error cases, the full expression should
22  *       be parsed beforehand instead of a progressive evaluation
23  * TODO: Access into entities references are not supported now ...
24  *       need a start to be able to pop out of entities refs since
25  *       parent is the endity declaration, not the ref.
26  */
27 
28 #include <string.h>
29 #include <libxml/xpointer.h>
30 #include <libxml/xmlmemory.h>
31 #include <libxml/parserInternals.h>
32 #include <libxml/uri.h>
33 #include <libxml/xpath.h>
34 #include <libxml/xpathInternals.h>
35 #include <libxml/xmlerror.h>
36 #include <libxml/globals.h>
37 
38 #ifdef LIBXML_XPTR_ENABLED
39 
40 /* Add support of the xmlns() xpointer scheme to initialize the namespaces */
41 #define XPTR_XMLNS_SCHEME
42 
43 /* #define DEBUG_RANGES */
44 #ifdef DEBUG_RANGES
45 #ifdef LIBXML_DEBUG_ENABLED
46 #include <libxml/debugXML.h>
47 #endif
48 #endif
49 
50 #define TODO								\
51     xmlGenericError(xmlGenericErrorContext,				\
52 	    "Unimplemented block at %s:%d\n",				\
53             __FILE__, __LINE__);
54 
55 #define STRANGE							\
56     xmlGenericError(xmlGenericErrorContext,				\
57 	    "Internal error at %s:%d\n",				\
58             __FILE__, __LINE__);
59 
60 /************************************************************************
61  *									*
62  *		Some factorized error routines				*
63  *									*
64  ************************************************************************/
65 
66 /**
67  * xmlXPtrErrMemory:
68  * @extra:  extra informations
69  *
70  * Handle a redefinition of attribute error
71  */
72 static void
xmlXPtrErrMemory(const char * extra)73 xmlXPtrErrMemory(const char *extra)
74 {
75     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_XPOINTER,
76 		    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
77 		    NULL, NULL, 0, 0,
78 		    "Memory allocation failed : %s\n", extra);
79 }
80 
81 /**
82  * xmlXPtrErr:
83  * @ctxt:  an XPTR evaluation context
84  * @extra:  extra informations
85  *
86  * Handle a redefinition of attribute error
87  */
88 static void LIBXML_ATTR_FORMAT(3,0)
xmlXPtrErr(xmlXPathParserContextPtr ctxt,int error,const char * msg,const xmlChar * extra)89 xmlXPtrErr(xmlXPathParserContextPtr ctxt, int error,
90            const char * msg, const xmlChar *extra)
91 {
92     if (ctxt != NULL)
93         ctxt->error = error;
94     if ((ctxt == NULL) || (ctxt->context == NULL)) {
95 	__xmlRaiseError(NULL, NULL, NULL,
96 			NULL, NULL, XML_FROM_XPOINTER, error,
97 			XML_ERR_ERROR, NULL, 0,
98 			(const char *) extra, NULL, NULL, 0, 0,
99 			msg, extra);
100 	return;
101     }
102     ctxt->context->lastError.domain = XML_FROM_XPOINTER;
103     ctxt->context->lastError.code = error;
104     ctxt->context->lastError.level = XML_ERR_ERROR;
105     ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
106     ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
107     ctxt->context->lastError.node = ctxt->context->debugNode;
108     if (ctxt->context->error != NULL) {
109 	ctxt->context->error(ctxt->context->userData,
110 	                     &ctxt->context->lastError);
111     } else {
112 	__xmlRaiseError(NULL, NULL, NULL,
113 			NULL, ctxt->context->debugNode, XML_FROM_XPOINTER,
114 			error, XML_ERR_ERROR, NULL, 0,
115 			(const char *) extra, (const char *) ctxt->base, NULL,
116 			ctxt->cur - ctxt->base, 0,
117 			msg, extra);
118     }
119 }
120 
121 /************************************************************************
122  *									*
123  *		A few helper functions for child sequences		*
124  *									*
125  ************************************************************************/
126 /* xmlXPtrAdvanceNode is a private function, but used by xinclude.c */
127 xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level);
128 /**
129  * xmlXPtrGetArity:
130  * @cur:  the node
131  *
132  * Returns the number of child for an element, -1 in case of error
133  */
134 static int
xmlXPtrGetArity(xmlNodePtr cur)135 xmlXPtrGetArity(xmlNodePtr cur) {
136     int i;
137     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
138 	return(-1);
139     cur = cur->children;
140     for (i = 0;cur != NULL;cur = cur->next) {
141 	if ((cur->type == XML_ELEMENT_NODE) ||
142 	    (cur->type == XML_DOCUMENT_NODE) ||
143 	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
144 	    i++;
145 	}
146     }
147     return(i);
148 }
149 
150 /**
151  * xmlXPtrGetIndex:
152  * @cur:  the node
153  *
154  * Returns the index of the node in its parent children list, -1
155  *         in case of error
156  */
157 static int
xmlXPtrGetIndex(xmlNodePtr cur)158 xmlXPtrGetIndex(xmlNodePtr cur) {
159     int i;
160     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
161 	return(-1);
162     for (i = 1;cur != NULL;cur = cur->prev) {
163 	if ((cur->type == XML_ELEMENT_NODE) ||
164 	    (cur->type == XML_DOCUMENT_NODE) ||
165 	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
166 	    i++;
167 	}
168     }
169     return(i);
170 }
171 
172 /**
173  * xmlXPtrGetNthChild:
174  * @cur:  the node
175  * @no:  the child number
176  *
177  * Returns the @no'th element child of @cur or NULL
178  */
179 static xmlNodePtr
xmlXPtrGetNthChild(xmlNodePtr cur,int no)180 xmlXPtrGetNthChild(xmlNodePtr cur, int no) {
181     int i;
182     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
183 	return(cur);
184     cur = cur->children;
185     for (i = 0;i <= no;cur = cur->next) {
186 	if (cur == NULL)
187 	    return(cur);
188 	if ((cur->type == XML_ELEMENT_NODE) ||
189 	    (cur->type == XML_DOCUMENT_NODE) ||
190 	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
191 	    i++;
192 	    if (i == no)
193 		break;
194 	}
195     }
196     return(cur);
197 }
198 
199 /************************************************************************
200  *									*
201  *		Handling of XPointer specific types			*
202  *									*
203  ************************************************************************/
204 
205 /**
206  * xmlXPtrCmpPoints:
207  * @node1:  the first node
208  * @index1:  the first index
209  * @node2:  the second node
210  * @index2:  the second index
211  *
212  * Compare two points w.r.t document order
213  *
214  * Returns -2 in case of error 1 if first point < second point, 0 if
215  *         that's the same point, -1 otherwise
216  */
217 static int
xmlXPtrCmpPoints(xmlNodePtr node1,int index1,xmlNodePtr node2,int index2)218 xmlXPtrCmpPoints(xmlNodePtr node1, int index1, xmlNodePtr node2, int index2) {
219     if ((node1 == NULL) || (node2 == NULL))
220 	return(-2);
221     /*
222      * a couple of optimizations which will avoid computations in most cases
223      */
224     if (node1 == node2) {
225 	if (index1 < index2)
226 	    return(1);
227 	if (index1 > index2)
228 	    return(-1);
229 	return(0);
230     }
231     return(xmlXPathCmpNodes(node1, node2));
232 }
233 
234 /**
235  * xmlXPtrNewPoint:
236  * @node:  the xmlNodePtr
237  * @indx:  the indx within the node
238  *
239  * Create a new xmlXPathObjectPtr of type point
240  *
241  * Returns the newly created object.
242  */
243 static xmlXPathObjectPtr
xmlXPtrNewPoint(xmlNodePtr node,int indx)244 xmlXPtrNewPoint(xmlNodePtr node, int indx) {
245     xmlXPathObjectPtr ret;
246 
247     if (node == NULL)
248 	return(NULL);
249     if (indx < 0)
250 	return(NULL);
251 
252     ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
253     if (ret == NULL) {
254         xmlXPtrErrMemory("allocating point");
255 	return(NULL);
256     }
257     memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
258     ret->type = XPATH_POINT;
259     ret->user = (void *) node;
260     ret->index = indx;
261     return(ret);
262 }
263 
264 /**
265  * xmlXPtrRangeCheckOrder:
266  * @range:  an object range
267  *
268  * Make sure the points in the range are in the right order
269  */
270 static void
xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range)271 xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range) {
272     int tmp;
273     xmlNodePtr tmp2;
274     if (range == NULL)
275 	return;
276     if (range->type != XPATH_RANGE)
277 	return;
278     if (range->user2 == NULL)
279 	return;
280     tmp = xmlXPtrCmpPoints(range->user, range->index,
281 	                     range->user2, range->index2);
282     if (tmp == -1) {
283 	tmp2 = range->user;
284 	range->user = range->user2;
285 	range->user2 = tmp2;
286 	tmp = range->index;
287 	range->index = range->index2;
288 	range->index2 = tmp;
289     }
290 }
291 
292 /**
293  * xmlXPtrRangesEqual:
294  * @range1:  the first range
295  * @range2:  the second range
296  *
297  * Compare two ranges
298  *
299  * Returns 1 if equal, 0 otherwise
300  */
301 static int
xmlXPtrRangesEqual(xmlXPathObjectPtr range1,xmlXPathObjectPtr range2)302 xmlXPtrRangesEqual(xmlXPathObjectPtr range1, xmlXPathObjectPtr range2) {
303     if (range1 == range2)
304 	return(1);
305     if ((range1 == NULL) || (range2 == NULL))
306 	return(0);
307     if (range1->type != range2->type)
308 	return(0);
309     if (range1->type != XPATH_RANGE)
310 	return(0);
311     if (range1->user != range2->user)
312 	return(0);
313     if (range1->index != range2->index)
314 	return(0);
315     if (range1->user2 != range2->user2)
316 	return(0);
317     if (range1->index2 != range2->index2)
318 	return(0);
319     return(1);
320 }
321 
322 /**
323  * xmlXPtrNewRangeInternal:
324  * @start:  the starting node
325  * @startindex:  the start index
326  * @end:  the ending point
327  * @endindex:  the ending index
328  *
329  * Internal function to create a new xmlXPathObjectPtr of type range
330  *
331  * Returns the newly created object.
332  */
333 static xmlXPathObjectPtr
xmlXPtrNewRangeInternal(xmlNodePtr start,int startindex,xmlNodePtr end,int endindex)334 xmlXPtrNewRangeInternal(xmlNodePtr start, int startindex,
335                         xmlNodePtr end, int endindex) {
336     xmlXPathObjectPtr ret;
337 
338     /*
339      * Namespace nodes must be copied (see xmlXPathNodeSetDupNs).
340      * Disallow them for now.
341      */
342     if ((start != NULL) && (start->type == XML_NAMESPACE_DECL))
343 	return(NULL);
344     if ((end != NULL) && (end->type == XML_NAMESPACE_DECL))
345 	return(NULL);
346 
347     ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
348     if (ret == NULL) {
349         xmlXPtrErrMemory("allocating range");
350 	return(NULL);
351     }
352     memset(ret, 0, sizeof(xmlXPathObject));
353     ret->type = XPATH_RANGE;
354     ret->user = start;
355     ret->index = startindex;
356     ret->user2 = end;
357     ret->index2 = endindex;
358     return(ret);
359 }
360 
361 /**
362  * xmlXPtrNewRange:
363  * @start:  the starting node
364  * @startindex:  the start index
365  * @end:  the ending point
366  * @endindex:  the ending index
367  *
368  * Create a new xmlXPathObjectPtr of type range
369  *
370  * Returns the newly created object.
371  */
372 xmlXPathObjectPtr
xmlXPtrNewRange(xmlNodePtr start,int startindex,xmlNodePtr end,int endindex)373 xmlXPtrNewRange(xmlNodePtr start, int startindex,
374 	        xmlNodePtr end, int endindex) {
375     xmlXPathObjectPtr ret;
376 
377     if (start == NULL)
378 	return(NULL);
379     if (end == NULL)
380 	return(NULL);
381     if (startindex < 0)
382 	return(NULL);
383     if (endindex < 0)
384 	return(NULL);
385 
386     ret = xmlXPtrNewRangeInternal(start, startindex, end, endindex);
387     xmlXPtrRangeCheckOrder(ret);
388     return(ret);
389 }
390 
391 /**
392  * xmlXPtrNewRangePoints:
393  * @start:  the starting point
394  * @end:  the ending point
395  *
396  * Create a new xmlXPathObjectPtr of type range using 2 Points
397  *
398  * Returns the newly created object.
399  */
400 xmlXPathObjectPtr
xmlXPtrNewRangePoints(xmlXPathObjectPtr start,xmlXPathObjectPtr end)401 xmlXPtrNewRangePoints(xmlXPathObjectPtr start, xmlXPathObjectPtr end) {
402     xmlXPathObjectPtr ret;
403 
404     if (start == NULL)
405 	return(NULL);
406     if (end == NULL)
407 	return(NULL);
408     if (start->type != XPATH_POINT)
409 	return(NULL);
410     if (end->type != XPATH_POINT)
411 	return(NULL);
412 
413     ret = xmlXPtrNewRangeInternal(start->user, start->index, end->user,
414                                   end->index);
415     xmlXPtrRangeCheckOrder(ret);
416     return(ret);
417 }
418 
419 /**
420  * xmlXPtrNewRangePointNode:
421  * @start:  the starting point
422  * @end:  the ending node
423  *
424  * Create a new xmlXPathObjectPtr of type range from a point to a node
425  *
426  * Returns the newly created object.
427  */
428 xmlXPathObjectPtr
xmlXPtrNewRangePointNode(xmlXPathObjectPtr start,xmlNodePtr end)429 xmlXPtrNewRangePointNode(xmlXPathObjectPtr start, xmlNodePtr end) {
430     xmlXPathObjectPtr ret;
431 
432     if (start == NULL)
433 	return(NULL);
434     if (end == NULL)
435 	return(NULL);
436     if (start->type != XPATH_POINT)
437 	return(NULL);
438 
439     ret = xmlXPtrNewRangeInternal(start->user, start->index, end, -1);
440     xmlXPtrRangeCheckOrder(ret);
441     return(ret);
442 }
443 
444 /**
445  * xmlXPtrNewRangeNodePoint:
446  * @start:  the starting node
447  * @end:  the ending point
448  *
449  * Create a new xmlXPathObjectPtr of type range from a node to a point
450  *
451  * Returns the newly created object.
452  */
453 xmlXPathObjectPtr
xmlXPtrNewRangeNodePoint(xmlNodePtr start,xmlXPathObjectPtr end)454 xmlXPtrNewRangeNodePoint(xmlNodePtr start, xmlXPathObjectPtr end) {
455     xmlXPathObjectPtr ret;
456 
457     if (start == NULL)
458 	return(NULL);
459     if (end == NULL)
460 	return(NULL);
461     if (start->type != XPATH_POINT)
462 	return(NULL);
463     if (end->type != XPATH_POINT)
464 	return(NULL);
465 
466     ret = xmlXPtrNewRangeInternal(start, -1, end->user, end->index);
467     xmlXPtrRangeCheckOrder(ret);
468     return(ret);
469 }
470 
471 /**
472  * xmlXPtrNewRangeNodes:
473  * @start:  the starting node
474  * @end:  the ending node
475  *
476  * Create a new xmlXPathObjectPtr of type range using 2 nodes
477  *
478  * Returns the newly created object.
479  */
480 xmlXPathObjectPtr
xmlXPtrNewRangeNodes(xmlNodePtr start,xmlNodePtr end)481 xmlXPtrNewRangeNodes(xmlNodePtr start, xmlNodePtr end) {
482     xmlXPathObjectPtr ret;
483 
484     if (start == NULL)
485 	return(NULL);
486     if (end == NULL)
487 	return(NULL);
488 
489     ret = xmlXPtrNewRangeInternal(start, -1, end, -1);
490     xmlXPtrRangeCheckOrder(ret);
491     return(ret);
492 }
493 
494 /**
495  * xmlXPtrNewCollapsedRange:
496  * @start:  the starting and ending node
497  *
498  * Create a new xmlXPathObjectPtr of type range using a single nodes
499  *
500  * Returns the newly created object.
501  */
502 xmlXPathObjectPtr
xmlXPtrNewCollapsedRange(xmlNodePtr start)503 xmlXPtrNewCollapsedRange(xmlNodePtr start) {
504     xmlXPathObjectPtr ret;
505 
506     if (start == NULL)
507 	return(NULL);
508 
509     ret = xmlXPtrNewRangeInternal(start, -1, NULL, -1);
510     return(ret);
511 }
512 
513 /**
514  * xmlXPtrNewRangeNodeObject:
515  * @start:  the starting node
516  * @end:  the ending object
517  *
518  * Create a new xmlXPathObjectPtr of type range from a not to an object
519  *
520  * Returns the newly created object.
521  */
522 xmlXPathObjectPtr
xmlXPtrNewRangeNodeObject(xmlNodePtr start,xmlXPathObjectPtr end)523 xmlXPtrNewRangeNodeObject(xmlNodePtr start, xmlXPathObjectPtr end) {
524     xmlNodePtr endNode;
525     int endIndex;
526     xmlXPathObjectPtr ret;
527 
528     if (start == NULL)
529 	return(NULL);
530     if (end == NULL)
531 	return(NULL);
532     switch (end->type) {
533 	case XPATH_POINT:
534 	    endNode = end->user;
535 	    endIndex = end->index;
536 	    break;
537 	case XPATH_RANGE:
538 	    endNode = end->user2;
539 	    endIndex = end->index2;
540 	    break;
541 	case XPATH_NODESET:
542 	    /*
543 	     * Empty set ...
544 	     */
545 	    if (end->nodesetval->nodeNr <= 0)
546 		return(NULL);
547 	    endNode = end->nodesetval->nodeTab[end->nodesetval->nodeNr - 1];
548 	    endIndex = -1;
549 	    break;
550 	default:
551 	    /* TODO */
552 	    return(NULL);
553     }
554 
555     ret = xmlXPtrNewRangeInternal(start, -1, endNode, endIndex);
556     xmlXPtrRangeCheckOrder(ret);
557     return(ret);
558 }
559 
560 #define XML_RANGESET_DEFAULT	10
561 
562 /**
563  * xmlXPtrLocationSetCreate:
564  * @val:  an initial xmlXPathObjectPtr, or NULL
565  *
566  * Create a new xmlLocationSetPtr of type double and of value @val
567  *
568  * Returns the newly created object.
569  */
570 xmlLocationSetPtr
xmlXPtrLocationSetCreate(xmlXPathObjectPtr val)571 xmlXPtrLocationSetCreate(xmlXPathObjectPtr val) {
572     xmlLocationSetPtr ret;
573 
574     ret = (xmlLocationSetPtr) xmlMalloc(sizeof(xmlLocationSet));
575     if (ret == NULL) {
576         xmlXPtrErrMemory("allocating locationset");
577 	return(NULL);
578     }
579     memset(ret, 0 , (size_t) sizeof(xmlLocationSet));
580     if (val != NULL) {
581         ret->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT *
582 					     sizeof(xmlXPathObjectPtr));
583 	if (ret->locTab == NULL) {
584 	    xmlXPtrErrMemory("allocating locationset");
585 	    xmlFree(ret);
586 	    return(NULL);
587 	}
588 	memset(ret->locTab, 0 ,
589 	       XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr));
590         ret->locMax = XML_RANGESET_DEFAULT;
591 	ret->locTab[ret->locNr++] = val;
592     }
593     return(ret);
594 }
595 
596 /**
597  * xmlXPtrLocationSetAdd:
598  * @cur:  the initial range set
599  * @val:  a new xmlXPathObjectPtr
600  *
601  * add a new xmlXPathObjectPtr to an existing LocationSet
602  * If the location already exist in the set @val is freed.
603  */
604 void
xmlXPtrLocationSetAdd(xmlLocationSetPtr cur,xmlXPathObjectPtr val)605 xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) {
606     int i;
607 
608     if ((cur == NULL) || (val == NULL)) return;
609 
610     /*
611      * check against doublons
612      */
613     for (i = 0;i < cur->locNr;i++) {
614 	if (xmlXPtrRangesEqual(cur->locTab[i], val)) {
615 	    xmlXPathFreeObject(val);
616 	    return;
617 	}
618     }
619 
620     /*
621      * grow the locTab if needed
622      */
623     if (cur->locMax == 0) {
624         cur->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT *
625 					     sizeof(xmlXPathObjectPtr));
626 	if (cur->locTab == NULL) {
627 	    xmlXPtrErrMemory("adding location to set");
628 	    return;
629 	}
630 	memset(cur->locTab, 0 ,
631 	       XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr));
632         cur->locMax = XML_RANGESET_DEFAULT;
633     } else if (cur->locNr == cur->locMax) {
634         xmlXPathObjectPtr *temp;
635 
636         cur->locMax *= 2;
637 	temp = (xmlXPathObjectPtr *) xmlRealloc(cur->locTab, cur->locMax *
638 				      sizeof(xmlXPathObjectPtr));
639 	if (temp == NULL) {
640 	    xmlXPtrErrMemory("adding location to set");
641 	    return;
642 	}
643 	cur->locTab = temp;
644     }
645     cur->locTab[cur->locNr++] = val;
646 }
647 
648 /**
649  * xmlXPtrLocationSetMerge:
650  * @val1:  the first LocationSet
651  * @val2:  the second LocationSet
652  *
653  * Merges two rangesets, all ranges from @val2 are added to @val1
654  *
655  * Returns val1 once extended or NULL in case of error.
656  */
657 xmlLocationSetPtr
xmlXPtrLocationSetMerge(xmlLocationSetPtr val1,xmlLocationSetPtr val2)658 xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) {
659     int i;
660 
661     if (val1 == NULL) return(NULL);
662     if (val2 == NULL) return(val1);
663 
664     /*
665      * !!!!! this can be optimized a lot, knowing that both
666      *       val1 and val2 already have unicity of their values.
667      */
668 
669     for (i = 0;i < val2->locNr;i++)
670         xmlXPtrLocationSetAdd(val1, val2->locTab[i]);
671 
672     return(val1);
673 }
674 
675 /**
676  * xmlXPtrLocationSetDel:
677  * @cur:  the initial range set
678  * @val:  an xmlXPathObjectPtr
679  *
680  * Removes an xmlXPathObjectPtr from an existing LocationSet
681  */
682 void
xmlXPtrLocationSetDel(xmlLocationSetPtr cur,xmlXPathObjectPtr val)683 xmlXPtrLocationSetDel(xmlLocationSetPtr cur, xmlXPathObjectPtr val) {
684     int i;
685 
686     if (cur == NULL) return;
687     if (val == NULL) return;
688 
689     /*
690      * check against doublons
691      */
692     for (i = 0;i < cur->locNr;i++)
693         if (cur->locTab[i] == val) break;
694 
695     if (i >= cur->locNr) {
696 #ifdef DEBUG
697         xmlGenericError(xmlGenericErrorContext,
698 	        "xmlXPtrLocationSetDel: Range wasn't found in RangeList\n");
699 #endif
700         return;
701     }
702     cur->locNr--;
703     for (;i < cur->locNr;i++)
704         cur->locTab[i] = cur->locTab[i + 1];
705     cur->locTab[cur->locNr] = NULL;
706 }
707 
708 /**
709  * xmlXPtrLocationSetRemove:
710  * @cur:  the initial range set
711  * @val:  the index to remove
712  *
713  * Removes an entry from an existing LocationSet list.
714  */
715 void
xmlXPtrLocationSetRemove(xmlLocationSetPtr cur,int val)716 xmlXPtrLocationSetRemove(xmlLocationSetPtr cur, int val) {
717     if (cur == NULL) return;
718     if (val >= cur->locNr) return;
719     cur->locNr--;
720     for (;val < cur->locNr;val++)
721         cur->locTab[val] = cur->locTab[val + 1];
722     cur->locTab[cur->locNr] = NULL;
723 }
724 
725 /**
726  * xmlXPtrFreeLocationSet:
727  * @obj:  the xmlLocationSetPtr to free
728  *
729  * Free the LocationSet compound (not the actual ranges !).
730  */
731 void
xmlXPtrFreeLocationSet(xmlLocationSetPtr obj)732 xmlXPtrFreeLocationSet(xmlLocationSetPtr obj) {
733     int i;
734 
735     if (obj == NULL) return;
736     if (obj->locTab != NULL) {
737 	for (i = 0;i < obj->locNr; i++) {
738             xmlXPathFreeObject(obj->locTab[i]);
739 	}
740 	xmlFree(obj->locTab);
741     }
742     xmlFree(obj);
743 }
744 
745 /**
746  * xmlXPtrNewLocationSetNodes:
747  * @start:  the start NodePtr value
748  * @end:  the end NodePtr value or NULL
749  *
750  * Create a new xmlXPathObjectPtr of type LocationSet and initialize
751  * it with the single range made of the two nodes @start and @end
752  *
753  * Returns the newly created object.
754  */
755 xmlXPathObjectPtr
xmlXPtrNewLocationSetNodes(xmlNodePtr start,xmlNodePtr end)756 xmlXPtrNewLocationSetNodes(xmlNodePtr start, xmlNodePtr end) {
757     xmlXPathObjectPtr ret;
758 
759     ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
760     if (ret == NULL) {
761         xmlXPtrErrMemory("allocating locationset");
762 	return(NULL);
763     }
764     memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
765     ret->type = XPATH_LOCATIONSET;
766     if (end == NULL)
767 	ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewCollapsedRange(start));
768     else
769 	ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewRangeNodes(start,end));
770     return(ret);
771 }
772 
773 /**
774  * xmlXPtrNewLocationSetNodeSet:
775  * @set:  a node set
776  *
777  * Create a new xmlXPathObjectPtr of type LocationSet and initialize
778  * it with all the nodes from @set
779  *
780  * Returns the newly created object.
781  */
782 xmlXPathObjectPtr
xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set)783 xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set) {
784     xmlXPathObjectPtr ret;
785 
786     ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
787     if (ret == NULL) {
788         xmlXPtrErrMemory("allocating locationset");
789 	return(NULL);
790     }
791     memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
792     ret->type = XPATH_LOCATIONSET;
793     if (set != NULL) {
794 	int i;
795 	xmlLocationSetPtr newset;
796 
797 	newset = xmlXPtrLocationSetCreate(NULL);
798 	if (newset == NULL)
799 	    return(ret);
800 
801 	for (i = 0;i < set->nodeNr;i++)
802 	    xmlXPtrLocationSetAdd(newset,
803 		        xmlXPtrNewCollapsedRange(set->nodeTab[i]));
804 
805 	ret->user = (void *) newset;
806     }
807     return(ret);
808 }
809 
810 /**
811  * xmlXPtrWrapLocationSet:
812  * @val:  the LocationSet value
813  *
814  * Wrap the LocationSet @val in a new xmlXPathObjectPtr
815  *
816  * Returns the newly created object.
817  */
818 xmlXPathObjectPtr
xmlXPtrWrapLocationSet(xmlLocationSetPtr val)819 xmlXPtrWrapLocationSet(xmlLocationSetPtr val) {
820     xmlXPathObjectPtr ret;
821 
822     ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
823     if (ret == NULL) {
824         xmlXPtrErrMemory("allocating locationset");
825 	return(NULL);
826     }
827     memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
828     ret->type = XPATH_LOCATIONSET;
829     ret->user = (void *) val;
830     return(ret);
831 }
832 
833 /************************************************************************
834  *									*
835  *			The parser					*
836  *									*
837  ************************************************************************/
838 
839 static void xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name);
840 
841 /*
842  * Macros for accessing the content. Those should be used only by the parser,
843  * and not exported.
844  *
845  * Dirty macros, i.e. one need to make assumption on the context to use them
846  *
847  *   CUR_PTR return the current pointer to the xmlChar to be parsed.
848  *   CUR     returns the current xmlChar value, i.e. a 8 bit value
849  *           in ISO-Latin or UTF-8.
850  *           This should be used internally by the parser
851  *           only to compare to ASCII values otherwise it would break when
852  *           running with UTF-8 encoding.
853  *   NXT(n)  returns the n'th next xmlChar. Same as CUR is should be used only
854  *           to compare on ASCII based substring.
855  *   SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
856  *           strings within the parser.
857  *   CURRENT Returns the current char value, with the full decoding of
858  *           UTF-8 if we are using this mode. It returns an int.
859  *   NEXT    Skip to the next character, this does the proper decoding
860  *           in UTF-8 mode. It also pop-up unfinished entities on the fly.
861  *           It returns the pointer to the current xmlChar.
862  */
863 
864 #define CUR (*ctxt->cur)
865 #define SKIP(val) ctxt->cur += (val)
866 #define NXT(val) ctxt->cur[(val)]
867 #define CUR_PTR ctxt->cur
868 
869 #define SKIP_BLANKS							\
870     while (IS_BLANK_CH(*(ctxt->cur))) NEXT
871 
872 #define CURRENT (*ctxt->cur)
873 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
874 
875 /*
876  * xmlXPtrGetChildNo:
877  * @ctxt:  the XPointer Parser context
878  * @index:  the child number
879  *
880  * Move the current node of the nodeset on the stack to the
881  * given child if found
882  */
883 static void
xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt,int indx)884 xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int indx) {
885     xmlNodePtr cur = NULL;
886     xmlXPathObjectPtr obj;
887     xmlNodeSetPtr oldset;
888 
889     CHECK_TYPE(XPATH_NODESET);
890     obj = valuePop(ctxt);
891     oldset = obj->nodesetval;
892     if ((indx <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) {
893 	xmlXPathFreeObject(obj);
894 	valuePush(ctxt, xmlXPathNewNodeSet(NULL));
895 	return;
896     }
897     cur = xmlXPtrGetNthChild(oldset->nodeTab[0], indx);
898     if (cur == NULL) {
899 	xmlXPathFreeObject(obj);
900 	valuePush(ctxt, xmlXPathNewNodeSet(NULL));
901 	return;
902     }
903     oldset->nodeTab[0] = cur;
904     valuePush(ctxt, obj);
905 }
906 
907 /**
908  * xmlXPtrEvalXPtrPart:
909  * @ctxt:  the XPointer Parser context
910  * @name:  the preparsed Scheme for the XPtrPart
911  *
912  * XPtrPart ::= 'xpointer' '(' XPtrExpr ')'
913  *            | Scheme '(' SchemeSpecificExpr ')'
914  *
915  * Scheme   ::=  NCName - 'xpointer' [VC: Non-XPointer schemes]
916  *
917  * SchemeSpecificExpr ::= StringWithBalancedParens
918  *
919  * StringWithBalancedParens ::=
920  *              [^()]* ('(' StringWithBalancedParens ')' [^()]*)*
921  *              [VC: Parenthesis escaping]
922  *
923  * XPtrExpr ::= Expr [VC: Parenthesis escaping]
924  *
925  * VC: Parenthesis escaping:
926  *   The end of an XPointer part is signaled by the right parenthesis ")"
927  *   character that is balanced with the left parenthesis "(" character
928  *   that began the part. Any unbalanced parenthesis character inside the
929  *   expression, even within literals, must be escaped with a circumflex (^)
930  *   character preceding it. If the expression contains any literal
931  *   occurrences of the circumflex, each must be escaped with an additional
932  *   circumflex (that is, ^^). If the unescaped parentheses in the expression
933  *   are not balanced, a syntax error results.
934  *
935  * Parse and evaluate an XPtrPart. Basically it generates the unescaped
936  * string and if the scheme is 'xpointer' it will call the XPath interpreter.
937  *
938  * TODO: there is no new scheme registration mechanism
939  */
940 
941 static void
xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt,xmlChar * name)942 xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) {
943     xmlChar *buffer, *cur;
944     int len;
945     int level;
946 
947     if (name == NULL)
948     name = xmlXPathParseName(ctxt);
949     if (name == NULL)
950 	XP_ERROR(XPATH_EXPR_ERROR);
951 
952     if (CUR != '(')
953 	XP_ERROR(XPATH_EXPR_ERROR);
954     NEXT;
955     level = 1;
956 
957     len = xmlStrlen(ctxt->cur);
958     len++;
959     buffer = (xmlChar *) xmlMallocAtomic(len * sizeof (xmlChar));
960     if (buffer == NULL) {
961         xmlXPtrErrMemory("allocating buffer");
962 	return;
963     }
964 
965     cur = buffer;
966     while (CUR != 0) {
967 	if (CUR == ')') {
968 	    level--;
969 	    if (level == 0) {
970 		NEXT;
971 		break;
972 	    }
973 	} else if (CUR == '(') {
974 	    level++;
975 	} else if (CUR == '^') {
976             if ((NXT(1) == ')') || (NXT(1) == '(') || (NXT(1) == '^')) {
977                 NEXT;
978             }
979 	}
980         *cur++ = CUR;
981 	NEXT;
982     }
983     *cur = 0;
984 
985     if ((level != 0) && (CUR == 0)) {
986 	xmlFree(buffer);
987 	XP_ERROR(XPTR_SYNTAX_ERROR);
988     }
989 
990     if (xmlStrEqual(name, (xmlChar *) "xpointer")) {
991 	const xmlChar *left = CUR_PTR;
992 
993 	CUR_PTR = buffer;
994 	/*
995 	 * To evaluate an xpointer scheme element (4.3) we need:
996 	 *   context initialized to the root
997 	 *   context position initalized to 1
998 	 *   context size initialized to 1
999 	 */
1000 	ctxt->context->node = (xmlNodePtr)ctxt->context->doc;
1001 	ctxt->context->proximityPosition = 1;
1002 	ctxt->context->contextSize = 1;
1003 	xmlXPathEvalExpr(ctxt);
1004 	CUR_PTR=left;
1005     } else if (xmlStrEqual(name, (xmlChar *) "element")) {
1006 	const xmlChar *left = CUR_PTR;
1007 	xmlChar *name2;
1008 
1009 	CUR_PTR = buffer;
1010 	if (buffer[0] == '/') {
1011 	    xmlXPathRoot(ctxt);
1012 	    xmlXPtrEvalChildSeq(ctxt, NULL);
1013 	} else {
1014 	    name2 = xmlXPathParseName(ctxt);
1015 	    if (name2 == NULL) {
1016 		CUR_PTR = left;
1017 		xmlFree(buffer);
1018 		XP_ERROR(XPATH_EXPR_ERROR);
1019 	    }
1020 	    xmlXPtrEvalChildSeq(ctxt, name2);
1021 	}
1022 	CUR_PTR = left;
1023 #ifdef XPTR_XMLNS_SCHEME
1024     } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) {
1025 	const xmlChar *left = CUR_PTR;
1026 	xmlChar *prefix;
1027 	xmlChar *URI;
1028 	xmlURIPtr value;
1029 
1030 	CUR_PTR = buffer;
1031         prefix = xmlXPathParseNCName(ctxt);
1032 	if (prefix == NULL) {
1033 	    xmlFree(buffer);
1034 	    xmlFree(name);
1035 	    XP_ERROR(XPTR_SYNTAX_ERROR);
1036 	}
1037 	SKIP_BLANKS;
1038 	if (CUR != '=') {
1039 	    xmlFree(prefix);
1040 	    xmlFree(buffer);
1041 	    xmlFree(name);
1042 	    XP_ERROR(XPTR_SYNTAX_ERROR);
1043 	}
1044 	NEXT;
1045 	SKIP_BLANKS;
1046 	/* @@ check escaping in the XPointer WD */
1047 
1048 	value = xmlParseURI((const char *)ctxt->cur);
1049 	if (value == NULL) {
1050 	    xmlFree(prefix);
1051 	    xmlFree(buffer);
1052 	    xmlFree(name);
1053 	    XP_ERROR(XPTR_SYNTAX_ERROR);
1054 	}
1055 	URI = xmlSaveUri(value);
1056 	xmlFreeURI(value);
1057 	if (URI == NULL) {
1058 	    xmlFree(prefix);
1059 	    xmlFree(buffer);
1060 	    xmlFree(name);
1061 	    XP_ERROR(XPATH_MEMORY_ERROR);
1062 	}
1063 
1064 	xmlXPathRegisterNs(ctxt->context, prefix, URI);
1065 	CUR_PTR = left;
1066 	xmlFree(URI);
1067 	xmlFree(prefix);
1068 #endif /* XPTR_XMLNS_SCHEME */
1069     } else {
1070         xmlXPtrErr(ctxt, XML_XPTR_UNKNOWN_SCHEME,
1071 		   "unsupported scheme '%s'\n", name);
1072     }
1073     xmlFree(buffer);
1074     xmlFree(name);
1075 }
1076 
1077 /**
1078  * xmlXPtrEvalFullXPtr:
1079  * @ctxt:  the XPointer Parser context
1080  * @name:  the preparsed Scheme for the first XPtrPart
1081  *
1082  * FullXPtr ::= XPtrPart (S? XPtrPart)*
1083  *
1084  * As the specs says:
1085  * -----------
1086  * When multiple XPtrParts are provided, they must be evaluated in
1087  * left-to-right order. If evaluation of one part fails, the nexti
1088  * is evaluated. The following conditions cause XPointer part failure:
1089  *
1090  * - An unknown scheme
1091  * - A scheme that does not locate any sub-resource present in the resource
1092  * - A scheme that is not applicable to the media type of the resource
1093  *
1094  * The XPointer application must consume a failed XPointer part and
1095  * attempt to evaluate the next one, if any. The result of the first
1096  * XPointer part whose evaluation succeeds is taken to be the fragment
1097  * located by the XPointer as a whole. If all the parts fail, the result
1098  * for the XPointer as a whole is a sub-resource error.
1099  * -----------
1100  *
1101  * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based
1102  * expressions or other schemes.
1103  */
1104 static void
xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt,xmlChar * name)1105 xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) {
1106     if (name == NULL)
1107     name = xmlXPathParseName(ctxt);
1108     if (name == NULL)
1109 	XP_ERROR(XPATH_EXPR_ERROR);
1110     while (name != NULL) {
1111 	ctxt->error = XPATH_EXPRESSION_OK;
1112 	xmlXPtrEvalXPtrPart(ctxt, name);
1113 
1114 	/* in case of syntax error, break here */
1115 	if ((ctxt->error != XPATH_EXPRESSION_OK) &&
1116             (ctxt->error != XML_XPTR_UNKNOWN_SCHEME))
1117 	    return;
1118 
1119 	/*
1120 	 * If the returned value is a non-empty nodeset
1121 	 * or location set, return here.
1122 	 */
1123 	if (ctxt->value != NULL) {
1124 	    xmlXPathObjectPtr obj = ctxt->value;
1125 
1126 	    switch (obj->type) {
1127 		case XPATH_LOCATIONSET: {
1128 		    xmlLocationSetPtr loc = ctxt->value->user;
1129 		    if ((loc != NULL) && (loc->locNr > 0))
1130 			return;
1131 		    break;
1132 		}
1133 		case XPATH_NODESET: {
1134 		    xmlNodeSetPtr loc = ctxt->value->nodesetval;
1135 		    if ((loc != NULL) && (loc->nodeNr > 0))
1136 			return;
1137 		    break;
1138 		}
1139 		default:
1140 		    break;
1141 	    }
1142 
1143 	    /*
1144 	     * Evaluating to improper values is equivalent to
1145 	     * a sub-resource error, clean-up the stack
1146 	     */
1147 	    do {
1148 		obj = valuePop(ctxt);
1149 		if (obj != NULL) {
1150 		    xmlXPathFreeObject(obj);
1151 		}
1152 	    } while (obj != NULL);
1153 	}
1154 
1155 	/*
1156 	 * Is there another XPointer part.
1157 	 */
1158 	SKIP_BLANKS;
1159 	name = xmlXPathParseName(ctxt);
1160     }
1161 }
1162 
1163 /**
1164  * xmlXPtrEvalChildSeq:
1165  * @ctxt:  the XPointer Parser context
1166  * @name:  a possible ID name of the child sequence
1167  *
1168  *  ChildSeq ::= '/1' ('/' [0-9]*)*
1169  *             | Name ('/' [0-9]*)+
1170  *
1171  * Parse and evaluate a Child Sequence. This routine also handle the
1172  * case of a Bare Name used to get a document ID.
1173  */
1174 static void
xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt,xmlChar * name)1175 xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) {
1176     /*
1177      * XPointer don't allow by syntax to address in mutirooted trees
1178      * this might prove useful in some cases, warn about it.
1179      */
1180     if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) {
1181         xmlXPtrErr(ctxt, XML_XPTR_CHILDSEQ_START,
1182 		   "warning: ChildSeq not starting by /1\n", NULL);
1183     }
1184 
1185     if (name != NULL) {
1186 	valuePush(ctxt, xmlXPathNewString(name));
1187 	xmlFree(name);
1188 	xmlXPathIdFunction(ctxt, 1);
1189 	CHECK_ERROR;
1190     }
1191 
1192     while (CUR == '/') {
1193 	int child = 0;
1194 	NEXT;
1195 
1196 	while ((CUR >= '0') && (CUR <= '9')) {
1197 	    child = child * 10 + (CUR - '0');
1198 	    NEXT;
1199 	}
1200 	xmlXPtrGetChildNo(ctxt, child);
1201     }
1202 }
1203 
1204 
1205 /**
1206  * xmlXPtrEvalXPointer:
1207  * @ctxt:  the XPointer Parser context
1208  *
1209  *  XPointer ::= Name
1210  *             | ChildSeq
1211  *             | FullXPtr
1212  *
1213  * Parse and evaluate an XPointer
1214  */
1215 static void
xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt)1216 xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) {
1217     if (ctxt->valueTab == NULL) {
1218 	/* Allocate the value stack */
1219 	ctxt->valueTab = (xmlXPathObjectPtr *)
1220 			 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
1221 	if (ctxt->valueTab == NULL) {
1222 	    xmlXPtrErrMemory("allocating evaluation context");
1223 	    return;
1224 	}
1225 	ctxt->valueNr = 0;
1226 	ctxt->valueMax = 10;
1227 	ctxt->value = NULL;
1228 	ctxt->valueFrame = 0;
1229     }
1230     SKIP_BLANKS;
1231     if (CUR == '/') {
1232 	xmlXPathRoot(ctxt);
1233         xmlXPtrEvalChildSeq(ctxt, NULL);
1234     } else {
1235 	xmlChar *name;
1236 
1237 	name = xmlXPathParseName(ctxt);
1238 	if (name == NULL)
1239 	    XP_ERROR(XPATH_EXPR_ERROR);
1240 	if (CUR == '(') {
1241 	    xmlXPtrEvalFullXPtr(ctxt, name);
1242 	    /* Short evaluation */
1243 	    return;
1244 	} else {
1245 	    /* this handle both Bare Names and Child Sequences */
1246 	    xmlXPtrEvalChildSeq(ctxt, name);
1247 	}
1248     }
1249     SKIP_BLANKS;
1250     if (CUR != 0)
1251 	XP_ERROR(XPATH_EXPR_ERROR);
1252 }
1253 
1254 
1255 /************************************************************************
1256  *									*
1257  *			General routines				*
1258  *									*
1259  ************************************************************************/
1260 
1261 static
1262 void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs);
1263 static
1264 void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs);
1265 static
1266 void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs);
1267 static
1268 void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs);
1269 static
1270 void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs);
1271 static
1272 void xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs);
1273 static
1274 void xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs);
1275 
1276 /**
1277  * xmlXPtrNewContext:
1278  * @doc:  the XML document
1279  * @here:  the node that directly contains the XPointer being evaluated or NULL
1280  * @origin:  the element from which a user or program initiated traversal of
1281  *           the link, or NULL.
1282  *
1283  * Create a new XPointer context
1284  *
1285  * Returns the xmlXPathContext just allocated.
1286  */
1287 xmlXPathContextPtr
xmlXPtrNewContext(xmlDocPtr doc,xmlNodePtr here,xmlNodePtr origin)1288 xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) {
1289     xmlXPathContextPtr ret;
1290 
1291     ret = xmlXPathNewContext(doc);
1292     if (ret == NULL)
1293 	return(ret);
1294     ret->xptr = 1;
1295     ret->here = here;
1296     ret->origin = origin;
1297 
1298     xmlXPathRegisterFunc(ret, (xmlChar *)"range",
1299 	                 xmlXPtrRangeFunction);
1300     xmlXPathRegisterFunc(ret, (xmlChar *)"range-inside",
1301 	                 xmlXPtrRangeInsideFunction);
1302     xmlXPathRegisterFunc(ret, (xmlChar *)"string-range",
1303 	                 xmlXPtrStringRangeFunction);
1304     xmlXPathRegisterFunc(ret, (xmlChar *)"start-point",
1305 	                 xmlXPtrStartPointFunction);
1306     xmlXPathRegisterFunc(ret, (xmlChar *)"end-point",
1307 	                 xmlXPtrEndPointFunction);
1308     xmlXPathRegisterFunc(ret, (xmlChar *)"here",
1309 	                 xmlXPtrHereFunction);
1310     xmlXPathRegisterFunc(ret, (xmlChar *)" origin",
1311 	                 xmlXPtrOriginFunction);
1312 
1313     return(ret);
1314 }
1315 
1316 /**
1317  * xmlXPtrEval:
1318  * @str:  the XPointer expression
1319  * @ctx:  the XPointer context
1320  *
1321  * Evaluate the XPath Location Path in the given context.
1322  *
1323  * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
1324  *         the caller has to free the object.
1325  */
1326 xmlXPathObjectPtr
xmlXPtrEval(const xmlChar * str,xmlXPathContextPtr ctx)1327 xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) {
1328     xmlXPathParserContextPtr ctxt;
1329     xmlXPathObjectPtr res = NULL, tmp;
1330     xmlXPathObjectPtr init = NULL;
1331     int stack = 0;
1332 
1333     xmlXPathInit();
1334 
1335     if ((ctx == NULL) || (str == NULL))
1336 	return(NULL);
1337 
1338     ctxt = xmlXPathNewParserContext(str, ctx);
1339     if (ctxt == NULL)
1340 	return(NULL);
1341     ctxt->xptr = 1;
1342     xmlXPtrEvalXPointer(ctxt);
1343 
1344     if ((ctxt->value != NULL) &&
1345 	(ctxt->value->type != XPATH_NODESET) &&
1346 	(ctxt->value->type != XPATH_LOCATIONSET)) {
1347         xmlXPtrErr(ctxt, XML_XPTR_EVAL_FAILED,
1348 		"xmlXPtrEval: evaluation failed to return a node set\n",
1349 		   NULL);
1350     } else {
1351 	res = valuePop(ctxt);
1352     }
1353 
1354     do {
1355         tmp = valuePop(ctxt);
1356 	if (tmp != NULL) {
1357 	    if (tmp != init) {
1358 		if (tmp->type == XPATH_NODESET) {
1359 		    /*
1360 		     * Evaluation may push a root nodeset which is unused
1361 		     */
1362 		    xmlNodeSetPtr set;
1363 		    set = tmp->nodesetval;
1364 		    if ((set->nodeNr != 1) ||
1365 			(set->nodeTab[0] != (xmlNodePtr) ctx->doc))
1366 			stack++;
1367 		} else
1368 		    stack++;
1369 	    }
1370 	    xmlXPathFreeObject(tmp);
1371         }
1372     } while (tmp != NULL);
1373     if (stack != 0) {
1374         xmlXPtrErr(ctxt, XML_XPTR_EXTRA_OBJECTS,
1375 		   "xmlXPtrEval: object(s) left on the eval stack\n",
1376 		   NULL);
1377     }
1378     if (ctxt->error != XPATH_EXPRESSION_OK) {
1379 	xmlXPathFreeObject(res);
1380 	res = NULL;
1381     }
1382 
1383     xmlXPathFreeParserContext(ctxt);
1384     return(res);
1385 }
1386 
1387 /**
1388  * xmlXPtrBuildRangeNodeList:
1389  * @range:  a range object
1390  *
1391  * Build a node list tree copy of the range
1392  *
1393  * Returns an xmlNodePtr list or NULL.
1394  *         the caller has to free the node tree.
1395  */
1396 static xmlNodePtr
xmlXPtrBuildRangeNodeList(xmlXPathObjectPtr range)1397 xmlXPtrBuildRangeNodeList(xmlXPathObjectPtr range) {
1398     /* pointers to generated nodes */
1399     xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp;
1400     /* pointers to traversal nodes */
1401     xmlNodePtr start, cur, end;
1402     int index1, index2;
1403 
1404     if (range == NULL)
1405 	return(NULL);
1406     if (range->type != XPATH_RANGE)
1407 	return(NULL);
1408     start = (xmlNodePtr) range->user;
1409 
1410     if ((start == NULL) || (start->type == XML_NAMESPACE_DECL))
1411 	return(NULL);
1412     end = range->user2;
1413     if (end == NULL)
1414 	return(xmlCopyNode(start, 1));
1415     if (end->type == XML_NAMESPACE_DECL)
1416         return(NULL);
1417 
1418     cur = start;
1419     index1 = range->index;
1420     index2 = range->index2;
1421     while (cur != NULL) {
1422 	if (cur == end) {
1423 	    if (cur->type == XML_TEXT_NODE) {
1424 		const xmlChar *content = cur->content;
1425 		int len;
1426 
1427 		if (content == NULL) {
1428 		    tmp = xmlNewTextLen(NULL, 0);
1429 		} else {
1430 		    len = index2;
1431 		    if ((cur == start) && (index1 > 1)) {
1432 			content += (index1 - 1);
1433 			len -= (index1 - 1);
1434 			index1 = 0;
1435 		    } else {
1436 			len = index2;
1437 		    }
1438 		    tmp = xmlNewTextLen(content, len);
1439 		}
1440 		/* single sub text node selection */
1441 		if (list == NULL)
1442 		    return(tmp);
1443 		/* prune and return full set */
1444 		if (last != NULL)
1445 		    xmlAddNextSibling(last, tmp);
1446 		else
1447 		    xmlAddChild(parent, tmp);
1448 		return(list);
1449 	    } else {
1450 		tmp = xmlCopyNode(cur, 0);
1451 		if (list == NULL)
1452 		    list = tmp;
1453 		else {
1454 		    if (last != NULL)
1455 			xmlAddNextSibling(last, tmp);
1456 		    else
1457 			xmlAddChild(parent, tmp);
1458 		}
1459 		last = NULL;
1460 		parent = tmp;
1461 
1462 		if (index2 > 1) {
1463 		    end = xmlXPtrGetNthChild(cur, index2 - 1);
1464 		    index2 = 0;
1465 		}
1466 		if ((cur == start) && (index1 > 1)) {
1467 		    cur = xmlXPtrGetNthChild(cur, index1 - 1);
1468 		    index1 = 0;
1469 		} else {
1470 		    cur = cur->children;
1471 		}
1472 		/*
1473 		 * Now gather the remaining nodes from cur to end
1474 		 */
1475 		continue; /* while */
1476 	    }
1477 	} else if ((cur == start) &&
1478 		   (list == NULL) /* looks superfluous but ... */ ) {
1479 	    if ((cur->type == XML_TEXT_NODE) ||
1480 		(cur->type == XML_CDATA_SECTION_NODE)) {
1481 		const xmlChar *content = cur->content;
1482 
1483 		if (content == NULL) {
1484 		    tmp = xmlNewTextLen(NULL, 0);
1485 		} else {
1486 		    if (index1 > 1) {
1487 			content += (index1 - 1);
1488 		    }
1489 		    tmp = xmlNewText(content);
1490 		}
1491 		last = list = tmp;
1492 	    } else {
1493 		if ((cur == start) && (index1 > 1)) {
1494 		    tmp = xmlCopyNode(cur, 0);
1495 		    list = tmp;
1496 		    parent = tmp;
1497 		    last = NULL;
1498 		    cur = xmlXPtrGetNthChild(cur, index1 - 1);
1499 		    index1 = 0;
1500 		    /*
1501 		     * Now gather the remaining nodes from cur to end
1502 		     */
1503 		    continue; /* while */
1504 		}
1505 		tmp = xmlCopyNode(cur, 1);
1506 		list = tmp;
1507 		parent = NULL;
1508 		last = tmp;
1509 	    }
1510 	} else {
1511 	    tmp = NULL;
1512 	    switch (cur->type) {
1513 		case XML_DTD_NODE:
1514 		case XML_ELEMENT_DECL:
1515 		case XML_ATTRIBUTE_DECL:
1516 		case XML_ENTITY_NODE:
1517 		    /* Do not copy DTD informations */
1518 		    break;
1519 		case XML_ENTITY_DECL:
1520 		    TODO /* handle crossing entities -> stack needed */
1521 		    break;
1522 		case XML_XINCLUDE_START:
1523 		case XML_XINCLUDE_END:
1524 		    /* don't consider it part of the tree content */
1525 		    break;
1526 		case XML_ATTRIBUTE_NODE:
1527 		    /* Humm, should not happen ! */
1528 		    STRANGE
1529 		    break;
1530 		default:
1531 		    tmp = xmlCopyNode(cur, 1);
1532 		    break;
1533 	    }
1534 	    if (tmp != NULL) {
1535 		if ((list == NULL) || ((last == NULL) && (parent == NULL)))  {
1536 		    STRANGE
1537 		    return(NULL);
1538 		}
1539 		if (last != NULL)
1540 		    xmlAddNextSibling(last, tmp);
1541 		else {
1542 		    xmlAddChild(parent, tmp);
1543 		    last = tmp;
1544 		}
1545 	    }
1546 	}
1547 	/*
1548 	 * Skip to next node in document order
1549 	 */
1550 	if ((list == NULL) || ((last == NULL) && (parent == NULL)))  {
1551 	    STRANGE
1552 	    return(NULL);
1553 	}
1554 	cur = xmlXPtrAdvanceNode(cur, NULL);
1555     }
1556     return(list);
1557 }
1558 
1559 /**
1560  * xmlXPtrBuildNodeList:
1561  * @obj:  the XPointer result from the evaluation.
1562  *
1563  * Build a node list tree copy of the XPointer result.
1564  * This will drop Attributes and Namespace declarations.
1565  *
1566  * Returns an xmlNodePtr list or NULL.
1567  *         the caller has to free the node tree.
1568  */
1569 xmlNodePtr
xmlXPtrBuildNodeList(xmlXPathObjectPtr obj)1570 xmlXPtrBuildNodeList(xmlXPathObjectPtr obj) {
1571     xmlNodePtr list = NULL, last = NULL;
1572     int i;
1573 
1574     if (obj == NULL)
1575 	return(NULL);
1576     switch (obj->type) {
1577         case XPATH_NODESET: {
1578 	    xmlNodeSetPtr set = obj->nodesetval;
1579 	    if (set == NULL)
1580 		return(NULL);
1581 	    for (i = 0;i < set->nodeNr;i++) {
1582 		if (set->nodeTab[i] == NULL)
1583 		    continue;
1584 		switch (set->nodeTab[i]->type) {
1585 		    case XML_TEXT_NODE:
1586 		    case XML_CDATA_SECTION_NODE:
1587 		    case XML_ELEMENT_NODE:
1588 		    case XML_ENTITY_REF_NODE:
1589 		    case XML_ENTITY_NODE:
1590 		    case XML_PI_NODE:
1591 		    case XML_COMMENT_NODE:
1592 		    case XML_DOCUMENT_NODE:
1593 		    case XML_HTML_DOCUMENT_NODE:
1594 #ifdef LIBXML_DOCB_ENABLED
1595 		    case XML_DOCB_DOCUMENT_NODE:
1596 #endif
1597 		    case XML_XINCLUDE_START:
1598 		    case XML_XINCLUDE_END:
1599 			break;
1600 		    case XML_ATTRIBUTE_NODE:
1601 		    case XML_NAMESPACE_DECL:
1602 		    case XML_DOCUMENT_TYPE_NODE:
1603 		    case XML_DOCUMENT_FRAG_NODE:
1604 		    case XML_NOTATION_NODE:
1605 		    case XML_DTD_NODE:
1606 		    case XML_ELEMENT_DECL:
1607 		    case XML_ATTRIBUTE_DECL:
1608 		    case XML_ENTITY_DECL:
1609 			continue; /* for */
1610 		}
1611 		if (last == NULL)
1612 		    list = last = xmlCopyNode(set->nodeTab[i], 1);
1613 		else {
1614 		    xmlAddNextSibling(last, xmlCopyNode(set->nodeTab[i], 1));
1615 		    if (last->next != NULL)
1616 			last = last->next;
1617 		}
1618 	    }
1619 	    break;
1620 	}
1621 	case XPATH_LOCATIONSET: {
1622 	    xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user;
1623 	    if (set == NULL)
1624 		return(NULL);
1625 	    for (i = 0;i < set->locNr;i++) {
1626 		if (last == NULL)
1627 		    list = last = xmlXPtrBuildNodeList(set->locTab[i]);
1628 		else
1629 		    xmlAddNextSibling(last,
1630 			    xmlXPtrBuildNodeList(set->locTab[i]));
1631 		if (last != NULL) {
1632 		    while (last->next != NULL)
1633 			last = last->next;
1634 		}
1635 	    }
1636 	    break;
1637 	}
1638 	case XPATH_RANGE:
1639 	    return(xmlXPtrBuildRangeNodeList(obj));
1640 	case XPATH_POINT:
1641 	    return(xmlCopyNode(obj->user, 0));
1642 	default:
1643 	    break;
1644     }
1645     return(list);
1646 }
1647 
1648 /************************************************************************
1649  *									*
1650  *			XPointer functions				*
1651  *									*
1652  ************************************************************************/
1653 
1654 /**
1655  * xmlXPtrNbLocChildren:
1656  * @node:  an xmlNodePtr
1657  *
1658  * Count the number of location children of @node or the length of the
1659  * string value in case of text/PI/Comments nodes
1660  *
1661  * Returns the number of location children
1662  */
1663 static int
xmlXPtrNbLocChildren(xmlNodePtr node)1664 xmlXPtrNbLocChildren(xmlNodePtr node) {
1665     int ret = 0;
1666     if (node == NULL)
1667 	return(-1);
1668     switch (node->type) {
1669         case XML_HTML_DOCUMENT_NODE:
1670         case XML_DOCUMENT_NODE:
1671         case XML_ELEMENT_NODE:
1672 	    node = node->children;
1673 	    while (node != NULL) {
1674 		if (node->type == XML_ELEMENT_NODE)
1675 		    ret++;
1676 		node = node->next;
1677 	    }
1678 	    break;
1679         case XML_ATTRIBUTE_NODE:
1680 	    return(-1);
1681 
1682         case XML_PI_NODE:
1683         case XML_COMMENT_NODE:
1684         case XML_TEXT_NODE:
1685         case XML_CDATA_SECTION_NODE:
1686         case XML_ENTITY_REF_NODE:
1687 	    ret = xmlStrlen(node->content);
1688 	    break;
1689 	default:
1690 	    return(-1);
1691     }
1692     return(ret);
1693 }
1694 
1695 /**
1696  * xmlXPtrHereFunction:
1697  * @ctxt:  the XPointer Parser context
1698  * @nargs:  the number of args
1699  *
1700  * Function implementing here() operation
1701  * as described in 5.4.3
1702  */
1703 static void
xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt,int nargs)1704 xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1705     CHECK_ARITY(0);
1706 
1707     if (ctxt->context->here == NULL)
1708 	XP_ERROR(XPTR_SYNTAX_ERROR);
1709 
1710     valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL));
1711 }
1712 
1713 /**
1714  * xmlXPtrOriginFunction:
1715  * @ctxt:  the XPointer Parser context
1716  * @nargs:  the number of args
1717  *
1718  * Function implementing origin() operation
1719  * as described in 5.4.3
1720  */
1721 static void
xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt,int nargs)1722 xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1723     CHECK_ARITY(0);
1724 
1725     if (ctxt->context->origin == NULL)
1726 	XP_ERROR(XPTR_SYNTAX_ERROR);
1727 
1728     valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL));
1729 }
1730 
1731 /**
1732  * xmlXPtrStartPointFunction:
1733  * @ctxt:  the XPointer Parser context
1734  * @nargs:  the number of args
1735  *
1736  * Function implementing start-point() operation
1737  * as described in 5.4.3
1738  * ----------------
1739  * location-set start-point(location-set)
1740  *
1741  * For each location x in the argument location-set, start-point adds a
1742  * location of type point to the result location-set. That point represents
1743  * the start point of location x and is determined by the following rules:
1744  *
1745  * - If x is of type point, the start point is x.
1746  * - If x is of type range, the start point is the start point of x.
1747  * - If x is of type root, element, text, comment, or processing instruction,
1748  * - the container node of the start point is x and the index is 0.
1749  * - If x is of type attribute or namespace, the function must signal a
1750  *   syntax error.
1751  * ----------------
1752  *
1753  */
1754 static void
xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt,int nargs)1755 xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1756     xmlXPathObjectPtr tmp, obj, point;
1757     xmlLocationSetPtr newset = NULL;
1758     xmlLocationSetPtr oldset = NULL;
1759 
1760     CHECK_ARITY(1);
1761     if ((ctxt->value == NULL) ||
1762 	((ctxt->value->type != XPATH_LOCATIONSET) &&
1763 	 (ctxt->value->type != XPATH_NODESET)))
1764         XP_ERROR(XPATH_INVALID_TYPE)
1765 
1766     obj = valuePop(ctxt);
1767     if (obj->type == XPATH_NODESET) {
1768 	/*
1769 	 * First convert to a location set
1770 	 */
1771 	tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval);
1772 	xmlXPathFreeObject(obj);
1773 	if (tmp == NULL)
1774             XP_ERROR(XPATH_MEMORY_ERROR)
1775 	obj = tmp;
1776     }
1777 
1778     newset = xmlXPtrLocationSetCreate(NULL);
1779     if (newset == NULL) {
1780 	xmlXPathFreeObject(obj);
1781         XP_ERROR(XPATH_MEMORY_ERROR);
1782     }
1783     oldset = (xmlLocationSetPtr) obj->user;
1784     if (oldset != NULL) {
1785 	int i;
1786 
1787 	for (i = 0; i < oldset->locNr; i++) {
1788 	    tmp = oldset->locTab[i];
1789 	    if (tmp == NULL)
1790 		continue;
1791 	    point = NULL;
1792 	    switch (tmp->type) {
1793 		case XPATH_POINT:
1794 		    point = xmlXPtrNewPoint(tmp->user, tmp->index);
1795 		    break;
1796 		case XPATH_RANGE: {
1797 		    xmlNodePtr node = tmp->user;
1798 		    if (node != NULL) {
1799 			if (node->type == XML_ATTRIBUTE_NODE) {
1800 			    /* TODO: Namespace Nodes ??? */
1801 			    xmlXPathFreeObject(obj);
1802 			    xmlXPtrFreeLocationSet(newset);
1803 			    XP_ERROR(XPTR_SYNTAX_ERROR);
1804 			}
1805 			point = xmlXPtrNewPoint(node, tmp->index);
1806 		    }
1807 		    break;
1808 	        }
1809 		default:
1810 		    /*** Should we raise an error ?
1811 		    xmlXPathFreeObject(obj);
1812 		    xmlXPathFreeObject(newset);
1813 		    XP_ERROR(XPATH_INVALID_TYPE)
1814 		    ***/
1815 		    break;
1816 	    }
1817             if (point != NULL)
1818 		xmlXPtrLocationSetAdd(newset, point);
1819 	}
1820     }
1821     xmlXPathFreeObject(obj);
1822     valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
1823 }
1824 
1825 /**
1826  * xmlXPtrEndPointFunction:
1827  * @ctxt:  the XPointer Parser context
1828  * @nargs:  the number of args
1829  *
1830  * Function implementing end-point() operation
1831  * as described in 5.4.3
1832  * ----------------------------
1833  * location-set end-point(location-set)
1834  *
1835  * For each location x in the argument location-set, end-point adds a
1836  * location of type point to the result location-set. That point represents
1837  * the end point of location x and is determined by the following rules:
1838  *
1839  * - If x is of type point, the resulting point is x.
1840  * - If x is of type range, the resulting point is the end point of x.
1841  * - If x is of type root or element, the container node of the resulting
1842  *   point is x and the index is the number of location children of x.
1843  * - If x is of type text, comment, or processing instruction, the container
1844  *   node of the resulting point is x and the index is the length of the
1845  *   string-value of x.
1846  * - If x is of type attribute or namespace, the function must signal a
1847  *   syntax error.
1848  * ----------------------------
1849  */
1850 static void
xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt,int nargs)1851 xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1852     xmlXPathObjectPtr tmp, obj, point;
1853     xmlLocationSetPtr newset = NULL;
1854     xmlLocationSetPtr oldset = NULL;
1855 
1856     CHECK_ARITY(1);
1857     if ((ctxt->value == NULL) ||
1858 	((ctxt->value->type != XPATH_LOCATIONSET) &&
1859 	 (ctxt->value->type != XPATH_NODESET)))
1860         XP_ERROR(XPATH_INVALID_TYPE)
1861 
1862     obj = valuePop(ctxt);
1863     if (obj->type == XPATH_NODESET) {
1864 	/*
1865 	 * First convert to a location set
1866 	 */
1867 	tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval);
1868 	xmlXPathFreeObject(obj);
1869 	if (tmp == NULL)
1870             XP_ERROR(XPATH_MEMORY_ERROR)
1871 	obj = tmp;
1872     }
1873 
1874     newset = xmlXPtrLocationSetCreate(NULL);
1875     if (newset == NULL) {
1876 	xmlXPathFreeObject(obj);
1877         XP_ERROR(XPATH_MEMORY_ERROR);
1878     }
1879     oldset = (xmlLocationSetPtr) obj->user;
1880     if (oldset != NULL) {
1881 	int i;
1882 
1883 	for (i = 0; i < oldset->locNr; i++) {
1884 	    tmp = oldset->locTab[i];
1885 	    if (tmp == NULL)
1886 		continue;
1887 	    point = NULL;
1888 	    switch (tmp->type) {
1889 		case XPATH_POINT:
1890 		    point = xmlXPtrNewPoint(tmp->user, tmp->index);
1891 		    break;
1892 		case XPATH_RANGE: {
1893 		    xmlNodePtr node = tmp->user2;
1894 		    if (node != NULL) {
1895 			if (node->type == XML_ATTRIBUTE_NODE) {
1896 			    /* TODO: Namespace Nodes ??? */
1897 			    xmlXPathFreeObject(obj);
1898 			    xmlXPtrFreeLocationSet(newset);
1899 			    XP_ERROR(XPTR_SYNTAX_ERROR);
1900 			}
1901 			point = xmlXPtrNewPoint(node, tmp->index2);
1902 		    } else if (tmp->user == NULL) {
1903 			point = xmlXPtrNewPoint(node,
1904 				       xmlXPtrNbLocChildren(node));
1905 		    }
1906 		    break;
1907 	        }
1908 		default:
1909 		    /*** Should we raise an error ?
1910 		    xmlXPathFreeObject(obj);
1911 		    xmlXPathFreeObject(newset);
1912 		    XP_ERROR(XPATH_INVALID_TYPE)
1913 		    ***/
1914 		    break;
1915 	    }
1916             if (point != NULL)
1917 		xmlXPtrLocationSetAdd(newset, point);
1918 	}
1919     }
1920     xmlXPathFreeObject(obj);
1921     valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
1922 }
1923 
1924 
1925 /**
1926  * xmlXPtrCoveringRange:
1927  * @ctxt:  the XPointer Parser context
1928  * @loc:  the location for which the covering range must be computed
1929  *
1930  * A covering range is a range that wholly encompasses a location
1931  * Section 5.3.3. Covering Ranges for All Location Types
1932  *        http://www.w3.org/TR/xptr#N2267
1933  *
1934  * Returns a new location or NULL in case of error
1935  */
1936 static xmlXPathObjectPtr
xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt,xmlXPathObjectPtr loc)1937 xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) {
1938     if (loc == NULL)
1939 	return(NULL);
1940     if ((ctxt == NULL) || (ctxt->context == NULL) ||
1941 	(ctxt->context->doc == NULL))
1942 	return(NULL);
1943     switch (loc->type) {
1944         case XPATH_POINT:
1945 	    return(xmlXPtrNewRange(loc->user, loc->index,
1946 			           loc->user, loc->index));
1947         case XPATH_RANGE:
1948 	    if (loc->user2 != NULL) {
1949 		return(xmlXPtrNewRange(loc->user, loc->index,
1950 			              loc->user2, loc->index2));
1951 	    } else {
1952 		xmlNodePtr node = (xmlNodePtr) loc->user;
1953 		if (node == (xmlNodePtr) ctxt->context->doc) {
1954 		    return(xmlXPtrNewRange(node, 0, node,
1955 					   xmlXPtrGetArity(node)));
1956 		} else {
1957 		    switch (node->type) {
1958 			case XML_ATTRIBUTE_NODE:
1959 			/* !!! our model is slightly different than XPath */
1960 			    return(xmlXPtrNewRange(node, 0, node,
1961 					           xmlXPtrGetArity(node)));
1962 			case XML_ELEMENT_NODE:
1963 			case XML_TEXT_NODE:
1964 			case XML_CDATA_SECTION_NODE:
1965 			case XML_ENTITY_REF_NODE:
1966 			case XML_PI_NODE:
1967 			case XML_COMMENT_NODE:
1968 			case XML_DOCUMENT_NODE:
1969 			case XML_NOTATION_NODE:
1970 			case XML_HTML_DOCUMENT_NODE: {
1971 			    int indx = xmlXPtrGetIndex(node);
1972 
1973 			    node = node->parent;
1974 			    return(xmlXPtrNewRange(node, indx - 1,
1975 					           node, indx + 1));
1976 			}
1977 			default:
1978 			    return(NULL);
1979 		    }
1980 		}
1981 	    }
1982 	default:
1983 	    TODO /* missed one case ??? */
1984     }
1985     return(NULL);
1986 }
1987 
1988 /**
1989  * xmlXPtrRangeFunction:
1990  * @ctxt:  the XPointer Parser context
1991  * @nargs:  the number of args
1992  *
1993  * Function implementing the range() function 5.4.3
1994  *  location-set range(location-set )
1995  *
1996  *  The range function returns ranges covering the locations in
1997  *  the argument location-set. For each location x in the argument
1998  *  location-set, a range location representing the covering range of
1999  *  x is added to the result location-set.
2000  */
2001 static void
xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt,int nargs)2002 xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2003     int i;
2004     xmlXPathObjectPtr set;
2005     xmlLocationSetPtr oldset;
2006     xmlLocationSetPtr newset;
2007 
2008     CHECK_ARITY(1);
2009     if ((ctxt->value == NULL) ||
2010 	((ctxt->value->type != XPATH_LOCATIONSET) &&
2011 	 (ctxt->value->type != XPATH_NODESET)))
2012         XP_ERROR(XPATH_INVALID_TYPE)
2013 
2014     set = valuePop(ctxt);
2015     if (set->type == XPATH_NODESET) {
2016 	xmlXPathObjectPtr tmp;
2017 
2018 	/*
2019 	 * First convert to a location set
2020 	 */
2021 	tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2022 	xmlXPathFreeObject(set);
2023 	if (tmp == NULL)
2024             XP_ERROR(XPATH_MEMORY_ERROR)
2025 	set = tmp;
2026     }
2027     oldset = (xmlLocationSetPtr) set->user;
2028 
2029     /*
2030      * The loop is to compute the covering range for each item and add it
2031      */
2032     newset = xmlXPtrLocationSetCreate(NULL);
2033     if (newset == NULL) {
2034 	xmlXPathFreeObject(set);
2035         XP_ERROR(XPATH_MEMORY_ERROR);
2036     }
2037     for (i = 0;i < oldset->locNr;i++) {
2038 	xmlXPtrLocationSetAdd(newset,
2039 		xmlXPtrCoveringRange(ctxt, oldset->locTab[i]));
2040     }
2041 
2042     /*
2043      * Save the new value and cleanup
2044      */
2045     valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2046     xmlXPathFreeObject(set);
2047 }
2048 
2049 /**
2050  * xmlXPtrInsideRange:
2051  * @ctxt:  the XPointer Parser context
2052  * @loc:  the location for which the inside range must be computed
2053  *
2054  * A inside range is a range described in the range-inside() description
2055  *
2056  * Returns a new location or NULL in case of error
2057  */
2058 static xmlXPathObjectPtr
xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt,xmlXPathObjectPtr loc)2059 xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) {
2060     if (loc == NULL)
2061 	return(NULL);
2062     if ((ctxt == NULL) || (ctxt->context == NULL) ||
2063 	(ctxt->context->doc == NULL))
2064 	return(NULL);
2065     switch (loc->type) {
2066         case XPATH_POINT: {
2067 	    xmlNodePtr node = (xmlNodePtr) loc->user;
2068 	    switch (node->type) {
2069 		case XML_PI_NODE:
2070 		case XML_COMMENT_NODE:
2071 		case XML_TEXT_NODE:
2072 		case XML_CDATA_SECTION_NODE: {
2073 		    if (node->content == NULL) {
2074 			return(xmlXPtrNewRange(node, 0, node, 0));
2075 		    } else {
2076 			return(xmlXPtrNewRange(node, 0, node,
2077 					       xmlStrlen(node->content)));
2078 		    }
2079 		}
2080 		case XML_ATTRIBUTE_NODE:
2081 		case XML_ELEMENT_NODE:
2082 		case XML_ENTITY_REF_NODE:
2083 		case XML_DOCUMENT_NODE:
2084 		case XML_NOTATION_NODE:
2085 		case XML_HTML_DOCUMENT_NODE: {
2086 		    return(xmlXPtrNewRange(node, 0, node,
2087 					   xmlXPtrGetArity(node)));
2088 		}
2089 		default:
2090 		    break;
2091 	    }
2092 	    return(NULL);
2093 	}
2094         case XPATH_RANGE: {
2095 	    xmlNodePtr node = (xmlNodePtr) loc->user;
2096 	    if (loc->user2 != NULL) {
2097 		return(xmlXPtrNewRange(node, loc->index,
2098 			               loc->user2, loc->index2));
2099 	    } else {
2100 		switch (node->type) {
2101 		    case XML_PI_NODE:
2102 		    case XML_COMMENT_NODE:
2103 		    case XML_TEXT_NODE:
2104 		    case XML_CDATA_SECTION_NODE: {
2105 			if (node->content == NULL) {
2106 			    return(xmlXPtrNewRange(node, 0, node, 0));
2107 			} else {
2108 			    return(xmlXPtrNewRange(node, 0, node,
2109 						   xmlStrlen(node->content)));
2110 			}
2111 		    }
2112 		    case XML_ATTRIBUTE_NODE:
2113 		    case XML_ELEMENT_NODE:
2114 		    case XML_ENTITY_REF_NODE:
2115 		    case XML_DOCUMENT_NODE:
2116 		    case XML_NOTATION_NODE:
2117 		    case XML_HTML_DOCUMENT_NODE: {
2118 			return(xmlXPtrNewRange(node, 0, node,
2119 					       xmlXPtrGetArity(node)));
2120 		    }
2121 		    default:
2122 			break;
2123 		}
2124 		return(NULL);
2125 	    }
2126         }
2127 	default:
2128 	    TODO /* missed one case ??? */
2129     }
2130     return(NULL);
2131 }
2132 
2133 /**
2134  * xmlXPtrRangeInsideFunction:
2135  * @ctxt:  the XPointer Parser context
2136  * @nargs:  the number of args
2137  *
2138  * Function implementing the range-inside() function 5.4.3
2139  *  location-set range-inside(location-set )
2140  *
2141  *  The range-inside function returns ranges covering the contents of
2142  *  the locations in the argument location-set. For each location x in
2143  *  the argument location-set, a range location is added to the result
2144  *  location-set. If x is a range location, then x is added to the
2145  *  result location-set. If x is not a range location, then x is used
2146  *  as the container location of the start and end points of the range
2147  *  location to be added; the index of the start point of the range is
2148  *  zero; if the end point is a character point then its index is the
2149  *  length of the string-value of x, and otherwise is the number of
2150  *  location children of x.
2151  *
2152  */
2153 static void
xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt,int nargs)2154 xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2155     int i;
2156     xmlXPathObjectPtr set;
2157     xmlLocationSetPtr oldset;
2158     xmlLocationSetPtr newset;
2159 
2160     CHECK_ARITY(1);
2161     if ((ctxt->value == NULL) ||
2162 	((ctxt->value->type != XPATH_LOCATIONSET) &&
2163 	 (ctxt->value->type != XPATH_NODESET)))
2164         XP_ERROR(XPATH_INVALID_TYPE)
2165 
2166     set = valuePop(ctxt);
2167     if (set->type == XPATH_NODESET) {
2168 	xmlXPathObjectPtr tmp;
2169 
2170 	/*
2171 	 * First convert to a location set
2172 	 */
2173 	tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2174 	xmlXPathFreeObject(set);
2175 	if (tmp == NULL)
2176 	     XP_ERROR(XPATH_MEMORY_ERROR)
2177 	set = tmp;
2178     }
2179     oldset = (xmlLocationSetPtr) set->user;
2180 
2181     /*
2182      * The loop is to compute the covering range for each item and add it
2183      */
2184     newset = xmlXPtrLocationSetCreate(NULL);
2185     if (newset == NULL) {
2186 	xmlXPathFreeObject(set);
2187         XP_ERROR(XPATH_MEMORY_ERROR);
2188     }
2189     for (i = 0;i < oldset->locNr;i++) {
2190 	xmlXPtrLocationSetAdd(newset,
2191 		xmlXPtrInsideRange(ctxt, oldset->locTab[i]));
2192     }
2193 
2194     /*
2195      * Save the new value and cleanup
2196      */
2197     valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2198     xmlXPathFreeObject(set);
2199 }
2200 
2201 /**
2202  * xmlXPtrRangeToFunction:
2203  * @ctxt:  the XPointer Parser context
2204  * @nargs:  the number of args
2205  *
2206  * Implement the range-to() XPointer function
2207  *
2208  * Obsolete. range-to is not a real function but a special type of location
2209  * step which is handled in xpath.c.
2210  */
2211 void
xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt,int nargs ATTRIBUTE_UNUSED)2212 xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt,
2213                        int nargs ATTRIBUTE_UNUSED) {
2214     XP_ERROR(XPATH_EXPR_ERROR);
2215 }
2216 
2217 /**
2218  * xmlXPtrAdvanceNode:
2219  * @cur:  the node
2220  * @level: incremented/decremented to show level in tree
2221  *
2222  * Advance to the next element or text node in document order
2223  * TODO: add a stack for entering/exiting entities
2224  *
2225  * Returns -1 in case of failure, 0 otherwise
2226  */
2227 xmlNodePtr
xmlXPtrAdvanceNode(xmlNodePtr cur,int * level)2228 xmlXPtrAdvanceNode(xmlNodePtr cur, int *level) {
2229 next:
2230     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
2231 	return(NULL);
2232     if (cur->children != NULL) {
2233         cur = cur->children ;
2234 	if (level != NULL)
2235 	    (*level)++;
2236 	goto found;
2237     }
2238 skip:		/* This label should only be needed if something is wrong! */
2239     if (cur->next != NULL) {
2240 	cur = cur->next;
2241 	goto found;
2242     }
2243     do {
2244         cur = cur->parent;
2245 	if (level != NULL)
2246 	    (*level)--;
2247         if (cur == NULL) return(NULL);
2248         if (cur->next != NULL) {
2249 	    cur = cur->next;
2250 	    goto found;
2251 	}
2252     } while (cur != NULL);
2253 
2254 found:
2255     if ((cur->type != XML_ELEMENT_NODE) &&
2256 	(cur->type != XML_TEXT_NODE) &&
2257 	(cur->type != XML_DOCUMENT_NODE) &&
2258 	(cur->type != XML_HTML_DOCUMENT_NODE) &&
2259 	(cur->type != XML_CDATA_SECTION_NODE)) {
2260 	    if (cur->type == XML_ENTITY_REF_NODE) {	/* Shouldn't happen */
2261 		TODO
2262 		goto skip;
2263 	    }
2264 	    goto next;
2265 	}
2266     return(cur);
2267 }
2268 
2269 /**
2270  * xmlXPtrAdvanceChar:
2271  * @node:  the node
2272  * @indx:  the indx
2273  * @bytes:  the number of bytes
2274  *
2275  * Advance a point of the associated number of bytes (not UTF8 chars)
2276  *
2277  * Returns -1 in case of failure, 0 otherwise
2278  */
2279 static int
xmlXPtrAdvanceChar(xmlNodePtr * node,int * indx,int bytes)2280 xmlXPtrAdvanceChar(xmlNodePtr *node, int *indx, int bytes) {
2281     xmlNodePtr cur;
2282     int pos;
2283     int len;
2284 
2285     if ((node == NULL) || (indx == NULL))
2286 	return(-1);
2287     cur = *node;
2288     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
2289 	return(-1);
2290     pos = *indx;
2291 
2292     while (bytes >= 0) {
2293 	/*
2294 	 * First position to the beginning of the first text node
2295 	 * corresponding to this point
2296 	 */
2297 	while ((cur != NULL) &&
2298 	       ((cur->type == XML_ELEMENT_NODE) ||
2299 	        (cur->type == XML_DOCUMENT_NODE) ||
2300 	        (cur->type == XML_HTML_DOCUMENT_NODE))) {
2301 	    if (pos > 0) {
2302 		cur = xmlXPtrGetNthChild(cur, pos);
2303 		pos = 0;
2304 	    } else {
2305 		cur = xmlXPtrAdvanceNode(cur, NULL);
2306 		pos = 0;
2307 	    }
2308 	}
2309 
2310 	if (cur == NULL) {
2311 	    *node = NULL;
2312 	    *indx = 0;
2313 	    return(-1);
2314 	}
2315 
2316 	/*
2317 	 * if there is no move needed return the current value.
2318 	 */
2319 	if (pos == 0) pos = 1;
2320 	if (bytes == 0) {
2321 	    *node = cur;
2322 	    *indx = pos;
2323 	    return(0);
2324 	}
2325 	/*
2326 	 * We should have a text (or cdata) node ...
2327 	 */
2328 	len = 0;
2329 	if ((cur->type != XML_ELEMENT_NODE) &&
2330             (cur->content != NULL)) {
2331 	    len = xmlStrlen(cur->content);
2332 	}
2333 	if (pos > len) {
2334 	    /* Strange, the indx in the text node is greater than it's len */
2335 	    STRANGE
2336 	    pos = len;
2337 	}
2338 	if (pos + bytes >= len) {
2339 	    bytes -= (len - pos);
2340 	    cur = xmlXPtrAdvanceNode(cur, NULL);
2341 	    pos = 0;
2342 	} else if (pos + bytes < len) {
2343 	    pos += bytes;
2344 	    *node = cur;
2345 	    *indx = pos;
2346 	    return(0);
2347 	}
2348     }
2349     return(-1);
2350 }
2351 
2352 /**
2353  * xmlXPtrMatchString:
2354  * @string:  the string to search
2355  * @start:  the start textnode
2356  * @startindex:  the start index
2357  * @end:  the end textnode IN/OUT
2358  * @endindex:  the end index IN/OUT
2359  *
2360  * Check whether the document contains @string at the position
2361  * (@start, @startindex) and limited by the (@end, @endindex) point
2362  *
2363  * Returns -1 in case of failure, 0 if not found, 1 if found in which case
2364  *            (@start, @startindex) will indicate the position of the beginning
2365  *            of the range and (@end, @endindex) will indicate the end
2366  *            of the range
2367  */
2368 static int
xmlXPtrMatchString(const xmlChar * string,xmlNodePtr start,int startindex,xmlNodePtr * end,int * endindex)2369 xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex,
2370 	            xmlNodePtr *end, int *endindex) {
2371     xmlNodePtr cur;
2372     int pos; /* 0 based */
2373     int len; /* in bytes */
2374     int stringlen; /* in bytes */
2375     int match;
2376 
2377     if (string == NULL)
2378 	return(-1);
2379     if ((start == NULL) || (start->type == XML_NAMESPACE_DECL))
2380 	return(-1);
2381     if ((end == NULL) || (*end == NULL) ||
2382         ((*end)->type == XML_NAMESPACE_DECL) || (endindex == NULL))
2383 	return(-1);
2384     cur = start;
2385     pos = startindex - 1;
2386     stringlen = xmlStrlen(string);
2387 
2388     while (stringlen > 0) {
2389 	if ((cur == *end) && (pos + stringlen > *endindex))
2390 	    return(0);
2391 
2392 	if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
2393 	    len = xmlStrlen(cur->content);
2394 	    if (len >= pos + stringlen) {
2395 		match = (!xmlStrncmp(&cur->content[pos], string, stringlen));
2396 		if (match) {
2397 #ifdef DEBUG_RANGES
2398 		    xmlGenericError(xmlGenericErrorContext,
2399 			    "found range %d bytes at index %d of ->",
2400 			    stringlen, pos + 1);
2401 		    xmlDebugDumpString(stdout, cur->content);
2402 		    xmlGenericError(xmlGenericErrorContext, "\n");
2403 #endif
2404 		    *end = cur;
2405 		    *endindex = pos + stringlen;
2406 		    return(1);
2407 		} else {
2408 		    return(0);
2409 		}
2410 	    } else {
2411                 int sub = len - pos;
2412 		match = (!xmlStrncmp(&cur->content[pos], string, sub));
2413 		if (match) {
2414 #ifdef DEBUG_RANGES
2415 		    xmlGenericError(xmlGenericErrorContext,
2416 			    "found subrange %d bytes at index %d of ->",
2417 			    sub, pos + 1);
2418 		    xmlDebugDumpString(stdout, cur->content);
2419 		    xmlGenericError(xmlGenericErrorContext, "\n");
2420 #endif
2421                     string = &string[sub];
2422 		    stringlen -= sub;
2423 		} else {
2424 		    return(0);
2425 		}
2426 	    }
2427 	}
2428 	cur = xmlXPtrAdvanceNode(cur, NULL);
2429 	if (cur == NULL)
2430 	    return(0);
2431 	pos = 0;
2432     }
2433     return(1);
2434 }
2435 
2436 /**
2437  * xmlXPtrSearchString:
2438  * @string:  the string to search
2439  * @start:  the start textnode IN/OUT
2440  * @startindex:  the start index IN/OUT
2441  * @end:  the end textnode
2442  * @endindex:  the end index
2443  *
2444  * Search the next occurrence of @string within the document content
2445  * until the (@end, @endindex) point is reached
2446  *
2447  * Returns -1 in case of failure, 0 if not found, 1 if found in which case
2448  *            (@start, @startindex) will indicate the position of the beginning
2449  *            of the range and (@end, @endindex) will indicate the end
2450  *            of the range
2451  */
2452 static int
xmlXPtrSearchString(const xmlChar * string,xmlNodePtr * start,int * startindex,xmlNodePtr * end,int * endindex)2453 xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex,
2454 	            xmlNodePtr *end, int *endindex) {
2455     xmlNodePtr cur;
2456     const xmlChar *str;
2457     int pos; /* 0 based */
2458     int len; /* in bytes */
2459     xmlChar first;
2460 
2461     if (string == NULL)
2462 	return(-1);
2463     if ((start == NULL) || (*start == NULL) ||
2464         ((*start)->type == XML_NAMESPACE_DECL) || (startindex == NULL))
2465 	return(-1);
2466     if ((end == NULL) || (endindex == NULL))
2467 	return(-1);
2468     cur = *start;
2469     pos = *startindex - 1;
2470     first = string[0];
2471 
2472     while (cur != NULL) {
2473 	if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
2474 	    len = xmlStrlen(cur->content);
2475 	    while (pos <= len) {
2476 		if (first != 0) {
2477 		    str = xmlStrchr(&cur->content[pos], first);
2478 		    if (str != NULL) {
2479 			pos = (str - (xmlChar *)(cur->content));
2480 #ifdef DEBUG_RANGES
2481 			xmlGenericError(xmlGenericErrorContext,
2482 				"found '%c' at index %d of ->",
2483 				first, pos + 1);
2484 			xmlDebugDumpString(stdout, cur->content);
2485 			xmlGenericError(xmlGenericErrorContext, "\n");
2486 #endif
2487 			if (xmlXPtrMatchString(string, cur, pos + 1,
2488 					       end, endindex)) {
2489 			    *start = cur;
2490 			    *startindex = pos + 1;
2491 			    return(1);
2492 			}
2493 			pos++;
2494 		    } else {
2495 			pos = len + 1;
2496 		    }
2497 		} else {
2498 		    /*
2499 		     * An empty string is considered to match before each
2500 		     * character of the string-value and after the final
2501 		     * character.
2502 		     */
2503 #ifdef DEBUG_RANGES
2504 		    xmlGenericError(xmlGenericErrorContext,
2505 			    "found '' at index %d of ->",
2506 			    pos + 1);
2507 		    xmlDebugDumpString(stdout, cur->content);
2508 		    xmlGenericError(xmlGenericErrorContext, "\n");
2509 #endif
2510 		    *start = cur;
2511 		    *startindex = pos + 1;
2512 		    *end = cur;
2513 		    *endindex = pos + 1;
2514 		    return(1);
2515 		}
2516 	    }
2517 	}
2518 	if ((cur == *end) && (pos >= *endindex))
2519 	    return(0);
2520 	cur = xmlXPtrAdvanceNode(cur, NULL);
2521 	if (cur == NULL)
2522 	    return(0);
2523 	pos = 1;
2524     }
2525     return(0);
2526 }
2527 
2528 /**
2529  * xmlXPtrGetLastChar:
2530  * @node:  the node
2531  * @index:  the index
2532  *
2533  * Computes the point coordinates of the last char of this point
2534  *
2535  * Returns -1 in case of failure, 0 otherwise
2536  */
2537 static int
xmlXPtrGetLastChar(xmlNodePtr * node,int * indx)2538 xmlXPtrGetLastChar(xmlNodePtr *node, int *indx) {
2539     xmlNodePtr cur;
2540     int pos, len = 0;
2541 
2542     if ((node == NULL) || (*node == NULL) ||
2543         ((*node)->type == XML_NAMESPACE_DECL) || (indx == NULL))
2544 	return(-1);
2545     cur = *node;
2546     pos = *indx;
2547 
2548     if ((cur->type == XML_ELEMENT_NODE) ||
2549 	(cur->type == XML_DOCUMENT_NODE) ||
2550 	(cur->type == XML_HTML_DOCUMENT_NODE)) {
2551 	if (pos > 0) {
2552 	    cur = xmlXPtrGetNthChild(cur, pos);
2553 	}
2554     }
2555     while (cur != NULL) {
2556 	if (cur->last != NULL)
2557 	    cur = cur->last;
2558 	else if ((cur->type != XML_ELEMENT_NODE) &&
2559 	         (cur->content != NULL)) {
2560 	    len = xmlStrlen(cur->content);
2561 	    break;
2562 	} else {
2563 	    return(-1);
2564 	}
2565     }
2566     if (cur == NULL)
2567 	return(-1);
2568     *node = cur;
2569     *indx = len;
2570     return(0);
2571 }
2572 
2573 /**
2574  * xmlXPtrGetStartPoint:
2575  * @obj:  an range
2576  * @node:  the resulting node
2577  * @indx:  the resulting index
2578  *
2579  * read the object and return the start point coordinates.
2580  *
2581  * Returns -1 in case of failure, 0 otherwise
2582  */
2583 static int
xmlXPtrGetStartPoint(xmlXPathObjectPtr obj,xmlNodePtr * node,int * indx)2584 xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) {
2585     if ((obj == NULL) || (node == NULL) || (indx == NULL))
2586 	return(-1);
2587 
2588     switch (obj->type) {
2589         case XPATH_POINT:
2590 	    *node = obj->user;
2591 	    if (obj->index <= 0)
2592 		*indx = 0;
2593 	    else
2594 		*indx = obj->index;
2595 	    return(0);
2596         case XPATH_RANGE:
2597 	    *node = obj->user;
2598 	    if (obj->index <= 0)
2599 		*indx = 0;
2600 	    else
2601 		*indx = obj->index;
2602 	    return(0);
2603 	default:
2604 	    break;
2605     }
2606     return(-1);
2607 }
2608 
2609 /**
2610  * xmlXPtrGetEndPoint:
2611  * @obj:  an range
2612  * @node:  the resulting node
2613  * @indx:  the resulting indx
2614  *
2615  * read the object and return the end point coordinates.
2616  *
2617  * Returns -1 in case of failure, 0 otherwise
2618  */
2619 static int
xmlXPtrGetEndPoint(xmlXPathObjectPtr obj,xmlNodePtr * node,int * indx)2620 xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) {
2621     if ((obj == NULL) || (node == NULL) || (indx == NULL))
2622 	return(-1);
2623 
2624     switch (obj->type) {
2625         case XPATH_POINT:
2626 	    *node = obj->user;
2627 	    if (obj->index <= 0)
2628 		*indx = 0;
2629 	    else
2630 		*indx = obj->index;
2631 	    return(0);
2632         case XPATH_RANGE:
2633 	    *node = obj->user;
2634 	    if (obj->index <= 0)
2635 		*indx = 0;
2636 	    else
2637 		*indx = obj->index;
2638 	    return(0);
2639 	default:
2640 	    break;
2641     }
2642     return(-1);
2643 }
2644 
2645 /**
2646  * xmlXPtrStringRangeFunction:
2647  * @ctxt:  the XPointer Parser context
2648  * @nargs:  the number of args
2649  *
2650  * Function implementing the string-range() function
2651  * range as described in 5.4.2
2652  *
2653  * ------------------------------
2654  * [Definition: For each location in the location-set argument,
2655  * string-range returns a set of string ranges, a set of substrings in a
2656  * string. Specifically, the string-value of the location is searched for
2657  * substrings that match the string argument, and the resulting location-set
2658  * will contain a range location for each non-overlapping match.]
2659  * An empty string is considered to match before each character of the
2660  * string-value and after the final character. Whitespace in a string
2661  * is matched literally, with no normalization except that provided by
2662  * XML for line ends. The third argument gives the position of the first
2663  * character to be in the resulting range, relative to the start of the
2664  * match. The default value is 1, which makes the range start immediately
2665  * before the first character of the matched string. The fourth argument
2666  * gives the number of characters in the range; the default is that the
2667  * range extends to the end of the matched string.
2668  *
2669  * Element boundaries, as well as entire embedded nodes such as processing
2670  * instructions and comments, are ignored as defined in [XPath].
2671  *
2672  * If the string in the second argument is not found in the string-value
2673  * of the location, or if a value in the third or fourth argument indicates
2674  * a string that is beyond the beginning or end of the document, the
2675  * expression fails.
2676  *
2677  * The points of the range-locations in the returned location-set will
2678  * all be character points.
2679  * ------------------------------
2680  */
2681 static void
xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt,int nargs)2682 xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2683     int i, startindex, endindex = 0, fendindex;
2684     xmlNodePtr start, end = 0, fend;
2685     xmlXPathObjectPtr set;
2686     xmlLocationSetPtr oldset;
2687     xmlLocationSetPtr newset;
2688     xmlXPathObjectPtr string;
2689     xmlXPathObjectPtr position = NULL;
2690     xmlXPathObjectPtr number = NULL;
2691     int found, pos = 0, num = 0;
2692 
2693     /*
2694      * Grab the arguments
2695      */
2696     if ((nargs < 2) || (nargs > 4))
2697 	XP_ERROR(XPATH_INVALID_ARITY);
2698 
2699     if (nargs >= 4) {
2700 	CHECK_TYPE(XPATH_NUMBER);
2701 	number = valuePop(ctxt);
2702 	if (number != NULL)
2703 	    num = (int) number->floatval;
2704     }
2705     if (nargs >= 3) {
2706 	CHECK_TYPE(XPATH_NUMBER);
2707 	position = valuePop(ctxt);
2708 	if (position != NULL)
2709 	    pos = (int) position->floatval;
2710     }
2711     CHECK_TYPE(XPATH_STRING);
2712     string = valuePop(ctxt);
2713     if ((ctxt->value == NULL) ||
2714 	((ctxt->value->type != XPATH_LOCATIONSET) &&
2715 	 (ctxt->value->type != XPATH_NODESET)))
2716         XP_ERROR(XPATH_INVALID_TYPE)
2717 
2718     set = valuePop(ctxt);
2719     newset = xmlXPtrLocationSetCreate(NULL);
2720     if (newset == NULL) {
2721 	xmlXPathFreeObject(set);
2722         XP_ERROR(XPATH_MEMORY_ERROR);
2723     }
2724     if (set->nodesetval == NULL) {
2725         goto error;
2726     }
2727     if (set->type == XPATH_NODESET) {
2728 	xmlXPathObjectPtr tmp;
2729 
2730 	/*
2731 	 * First convert to a location set
2732 	 */
2733 	tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2734 	xmlXPathFreeObject(set);
2735 	if (tmp == NULL)
2736 	     XP_ERROR(XPATH_MEMORY_ERROR)
2737 	set = tmp;
2738     }
2739     oldset = (xmlLocationSetPtr) set->user;
2740 
2741     /*
2742      * The loop is to search for each element in the location set
2743      * the list of location set corresponding to that search
2744      */
2745     for (i = 0;i < oldset->locNr;i++) {
2746 #ifdef DEBUG_RANGES
2747 	xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0);
2748 #endif
2749 
2750 	xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex);
2751 	xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex);
2752 	xmlXPtrAdvanceChar(&start, &startindex, 0);
2753 	xmlXPtrGetLastChar(&end, &endindex);
2754 
2755 #ifdef DEBUG_RANGES
2756 	xmlGenericError(xmlGenericErrorContext,
2757 		"from index %d of ->", startindex);
2758 	xmlDebugDumpString(stdout, start->content);
2759 	xmlGenericError(xmlGenericErrorContext, "\n");
2760 	xmlGenericError(xmlGenericErrorContext,
2761 		"to index %d of ->", endindex);
2762 	xmlDebugDumpString(stdout, end->content);
2763 	xmlGenericError(xmlGenericErrorContext, "\n");
2764 #endif
2765 	do {
2766             fend = end;
2767             fendindex = endindex;
2768 	    found = xmlXPtrSearchString(string->stringval, &start, &startindex,
2769 		                        &fend, &fendindex);
2770 	    if (found == 1) {
2771 		if (position == NULL) {
2772 		    xmlXPtrLocationSetAdd(newset,
2773 			 xmlXPtrNewRange(start, startindex, fend, fendindex));
2774 		} else if (xmlXPtrAdvanceChar(&start, &startindex,
2775 			                      pos - 1) == 0) {
2776 		    if ((number != NULL) && (num > 0)) {
2777 			int rindx;
2778 			xmlNodePtr rend;
2779 			rend = start;
2780 			rindx = startindex - 1;
2781 			if (xmlXPtrAdvanceChar(&rend, &rindx,
2782 				               num) == 0) {
2783 			    xmlXPtrLocationSetAdd(newset,
2784 					xmlXPtrNewRange(start, startindex,
2785 							rend, rindx));
2786 			}
2787 		    } else if ((number != NULL) && (num <= 0)) {
2788 			xmlXPtrLocationSetAdd(newset,
2789 				    xmlXPtrNewRange(start, startindex,
2790 						    start, startindex));
2791 		    } else {
2792 			xmlXPtrLocationSetAdd(newset,
2793 				    xmlXPtrNewRange(start, startindex,
2794 						    fend, fendindex));
2795 		    }
2796 		}
2797 		start = fend;
2798 		startindex = fendindex;
2799 		if (string->stringval[0] == 0)
2800 		    startindex++;
2801 	    }
2802 	} while (found == 1);
2803     }
2804 
2805     /*
2806      * Save the new value and cleanup
2807      */
2808 error:
2809     valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2810     xmlXPathFreeObject(set);
2811     xmlXPathFreeObject(string);
2812     if (position) xmlXPathFreeObject(position);
2813     if (number) xmlXPathFreeObject(number);
2814 }
2815 
2816 /**
2817  * xmlXPtrEvalRangePredicate:
2818  * @ctxt:  the XPointer Parser context
2819  *
2820  *  [8]   Predicate ::=   '[' PredicateExpr ']'
2821  *  [9]   PredicateExpr ::=   Expr
2822  *
2823  * Evaluate a predicate as in xmlXPathEvalPredicate() but for
2824  * a Location Set instead of a node set
2825  */
2826 void
xmlXPtrEvalRangePredicate(xmlXPathParserContextPtr ctxt)2827 xmlXPtrEvalRangePredicate(xmlXPathParserContextPtr ctxt) {
2828     const xmlChar *cur;
2829     xmlXPathObjectPtr res;
2830     xmlXPathObjectPtr obj, tmp;
2831     xmlLocationSetPtr newset = NULL;
2832     xmlLocationSetPtr oldset;
2833     int i;
2834 
2835     if (ctxt == NULL) return;
2836 
2837     SKIP_BLANKS;
2838     if (CUR != '[') {
2839 	XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
2840     }
2841     NEXT;
2842     SKIP_BLANKS;
2843 
2844     /*
2845      * Extract the old set, and then evaluate the result of the
2846      * expression for all the element in the set. use it to grow
2847      * up a new set.
2848      */
2849     CHECK_TYPE(XPATH_LOCATIONSET);
2850     obj = valuePop(ctxt);
2851     oldset = obj->user;
2852     ctxt->context->node = NULL;
2853 
2854     if ((oldset == NULL) || (oldset->locNr == 0)) {
2855 	ctxt->context->contextSize = 0;
2856 	ctxt->context->proximityPosition = 0;
2857 	xmlXPathEvalExpr(ctxt);
2858 	res = valuePop(ctxt);
2859 	if (res != NULL)
2860 	    xmlXPathFreeObject(res);
2861 	valuePush(ctxt, obj);
2862 	CHECK_ERROR;
2863     } else {
2864 	/*
2865 	 * Save the expression pointer since we will have to evaluate
2866 	 * it multiple times. Initialize the new set.
2867 	 */
2868         cur = ctxt->cur;
2869 	newset = xmlXPtrLocationSetCreate(NULL);
2870 
2871         for (i = 0; i < oldset->locNr; i++) {
2872 	    ctxt->cur = cur;
2873 
2874 	    /*
2875 	     * Run the evaluation with a node list made of a single item
2876 	     * in the nodeset.
2877 	     */
2878 	    ctxt->context->node = oldset->locTab[i]->user;
2879 	    tmp = xmlXPathNewNodeSet(ctxt->context->node);
2880 	    valuePush(ctxt, tmp);
2881 	    ctxt->context->contextSize = oldset->locNr;
2882 	    ctxt->context->proximityPosition = i + 1;
2883 
2884 	    xmlXPathEvalExpr(ctxt);
2885 	    CHECK_ERROR;
2886 
2887 	    /*
2888 	     * The result of the evaluation need to be tested to
2889 	     * decided whether the filter succeeded or not
2890 	     */
2891 	    res = valuePop(ctxt);
2892 	    if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
2893 	        xmlXPtrLocationSetAdd(newset,
2894 			xmlXPathObjectCopy(oldset->locTab[i]));
2895 	    }
2896 
2897 	    /*
2898 	     * Cleanup
2899 	     */
2900 	    if (res != NULL)
2901 		xmlXPathFreeObject(res);
2902 	    if (ctxt->value == tmp) {
2903 		res = valuePop(ctxt);
2904 		xmlXPathFreeObject(res);
2905 	    }
2906 
2907 	    ctxt->context->node = NULL;
2908 	}
2909 
2910 	/*
2911 	 * The result is used as the new evaluation set.
2912 	 */
2913 	xmlXPathFreeObject(obj);
2914 	ctxt->context->node = NULL;
2915 	ctxt->context->contextSize = -1;
2916 	ctxt->context->proximityPosition = -1;
2917 	valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2918     }
2919     if (CUR != ']') {
2920 	XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
2921     }
2922 
2923     NEXT;
2924     SKIP_BLANKS;
2925 }
2926 
2927 #define bottom_xpointer
2928 #include "elfgcchack.h"
2929 #endif
2930 
2931