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