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