Coverage Report

Created: 2025-08-26 06:56

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