Coverage Report

Created: 2025-07-23 08:13

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