Coverage Report

Created: 2023-06-07 06:14

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