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