Coverage Report

Created: 2023-09-29 17:40

/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
0
{
82
0
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_XPOINTER,
83
0
        XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
84
0
        NULL, NULL, 0, 0,
85
0
        "Memory allocation failed : %s\n", extra);
86
0
}
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
0
{
99
0
    if (ctxt != NULL)
100
0
        ctxt->error = error;
101
0
    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
0
    xmlResetError(&ctxt->context->lastError);
112
113
0
    ctxt->context->lastError.domain = XML_FROM_XPOINTER;
114
0
    ctxt->context->lastError.code = error;
115
0
    ctxt->context->lastError.level = XML_ERR_ERROR;
116
0
    ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
117
0
    ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
118
0
    ctxt->context->lastError.node = ctxt->context->debugNode;
119
0
    if (ctxt->context->error != NULL) {
120
0
  ctxt->context->error(ctxt->context->userData,
121
0
                       &ctxt->context->lastError);
122
0
    } else {
123
0
  __xmlRaiseError(NULL, NULL, NULL,
124
0
      NULL, ctxt->context->debugNode, XML_FROM_XPOINTER,
125
0
      error, XML_ERR_ERROR, NULL, 0,
126
0
      (const char *) extra, (const char *) ctxt->base, NULL,
127
0
      ctxt->cur - ctxt->base, 0,
128
0
      msg, extra);
129
0
    }
130
0
}
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
0
xmlXPtrGetNthChild(xmlNodePtr cur, int no) {
194
0
    int i;
195
0
    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
196
0
  return(cur);
197
0
    cur = cur->children;
198
0
    for (i = 0;i <= no;cur = cur->next) {
199
0
  if (cur == NULL)
200
0
      return(cur);
201
0
  if ((cur->type == XML_ELEMENT_NODE) ||
202
0
      (cur->type == XML_DOCUMENT_NODE) ||
203
0
      (cur->type == XML_HTML_DOCUMENT_NODE)) {
204
0
      i++;
205
0
      if (i == no)
206
0
    break;
207
0
  }
208
0
    }
209
0
    return(cur);
210
0
}
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
0
#define CUR (*ctxt->cur)
877
#define SKIP(val) ctxt->cur += (val)
878
0
#define NXT(val) ctxt->cur[(val)]
879
880
#define SKIP_BLANKS             \
881
0
    while (IS_BLANK_CH(*(ctxt->cur))) NEXT
882
883
#define CURRENT (*ctxt->cur)
884
0
#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
0
xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int indx) {
896
0
    xmlNodePtr cur = NULL;
897
0
    xmlXPathObjectPtr obj;
898
0
    xmlNodeSetPtr oldset;
899
900
0
    CHECK_TYPE(XPATH_NODESET);
901
0
    obj = valuePop(ctxt);
902
0
    oldset = obj->nodesetval;
903
0
    if ((indx <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) {
904
0
  xmlXPathFreeObject(obj);
905
0
  valuePush(ctxt, xmlXPathNewNodeSet(NULL));
906
0
  return;
907
0
    }
908
0
    cur = xmlXPtrGetNthChild(oldset->nodeTab[0], indx);
909
0
    if (cur == NULL) {
910
0
  xmlXPathFreeObject(obj);
911
0
  valuePush(ctxt, xmlXPathNewNodeSet(NULL));
912
0
  return;
913
0
    }
914
0
    oldset->nodeTab[0] = cur;
915
0
    valuePush(ctxt, obj);
916
0
}
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
0
xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) {
954
0
    xmlChar *buffer, *cur;
955
0
    int len;
956
0
    int level;
957
958
0
    if (name == NULL)
959
0
    name = xmlXPathParseName(ctxt);
960
0
    if (name == NULL)
961
0
  XP_ERROR(XPATH_EXPR_ERROR);
962
963
0
    if (CUR != '(') {
964
0
        xmlFree(name);
965
0
  XP_ERROR(XPATH_EXPR_ERROR);
966
0
    }
967
0
    NEXT;
968
0
    level = 1;
969
970
0
    len = xmlStrlen(ctxt->cur);
971
0
    len++;
972
0
    buffer = (xmlChar *) xmlMallocAtomic(len);
973
0
    if (buffer == NULL) {
974
0
        xmlXPtrErrMemory("allocating buffer");
975
0
        xmlFree(name);
976
0
  return;
977
0
    }
978
979
0
    cur = buffer;
980
0
    while (CUR != 0) {
981
0
  if (CUR == ')') {
982
0
      level--;
983
0
      if (level == 0) {
984
0
    NEXT;
985
0
    break;
986
0
      }
987
0
  } else if (CUR == '(') {
988
0
      level++;
989
0
  } else if (CUR == '^') {
990
0
            if ((NXT(1) == ')') || (NXT(1) == '(') || (NXT(1) == '^')) {
991
0
                NEXT;
992
0
            }
993
0
  }
994
0
        *cur++ = CUR;
995
0
  NEXT;
996
0
    }
997
0
    *cur = 0;
998
999
0
    if ((level != 0) && (CUR == 0)) {
1000
0
        xmlFree(name);
1001
0
  xmlFree(buffer);
1002
0
  XP_ERROR(XPTR_SYNTAX_ERROR);
1003
0
    }
1004
1005
0
    if (xmlStrEqual(name, (xmlChar *) "xpointer") ||
1006
0
        xmlStrEqual(name, (xmlChar *) "xpath1")) {
1007
0
  const xmlChar *oldBase = ctxt->base;
1008
0
  const xmlChar *oldCur = ctxt->cur;
1009
1010
0
  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
0
  ctxt->context->node = (xmlNodePtr)ctxt->context->doc;
1018
0
  ctxt->context->proximityPosition = 1;
1019
0
  ctxt->context->contextSize = 1;
1020
#ifdef LIBXML_XPTR_LOCS_ENABLED
1021
        ctxt->xptr = xmlStrEqual(name, (xmlChar *) "xpointer");
1022
#endif
1023
0
  xmlXPathEvalExpr(ctxt);
1024
0
  ctxt->base = oldBase;
1025
0
        ctxt->cur = oldCur;
1026
0
    } 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
0
    } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) {
1050
0
  const xmlChar *oldBase = ctxt->base;
1051
0
  const xmlChar *oldCur = ctxt->cur;
1052
0
  xmlChar *prefix;
1053
1054
0
  ctxt->cur = ctxt->base = buffer;
1055
0
        prefix = xmlXPathParseNCName(ctxt);
1056
0
  if (prefix == NULL) {
1057
0
            ctxt->base = oldBase;
1058
0
            ctxt->cur = oldCur;
1059
0
      xmlFree(buffer);
1060
0
      xmlFree(name);
1061
0
      XP_ERROR(XPTR_SYNTAX_ERROR);
1062
0
  }
1063
0
  SKIP_BLANKS;
1064
0
  if (CUR != '=') {
1065
0
            ctxt->base = oldBase;
1066
0
            ctxt->cur = oldCur;
1067
0
      xmlFree(prefix);
1068
0
      xmlFree(buffer);
1069
0
      xmlFree(name);
1070
0
      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
0
    } else {
1081
0
        xmlXPtrErr(ctxt, XML_XPTR_UNKNOWN_SCHEME,
1082
0
       "unsupported scheme '%s'\n", name);
1083
0
    }
1084
0
    xmlFree(buffer);
1085
0
    xmlFree(name);
1086
0
}
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
0
xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) {
1117
0
    if (name == NULL)
1118
0
    name = xmlXPathParseName(ctxt);
1119
0
    if (name == NULL)
1120
0
  XP_ERROR(XPATH_EXPR_ERROR);
1121
0
    while (name != NULL) {
1122
0
  ctxt->error = XPATH_EXPRESSION_OK;
1123
0
  xmlXPtrEvalXPtrPart(ctxt, name);
1124
1125
  /* in case of syntax error, break here */
1126
0
  if ((ctxt->error != XPATH_EXPRESSION_OK) &&
1127
0
            (ctxt->error != XML_XPTR_UNKNOWN_SCHEME))
1128
0
      return;
1129
1130
  /*
1131
   * If the returned value is a non-empty nodeset
1132
   * or location set, return here.
1133
   */
1134
0
  if (ctxt->value != NULL) {
1135
0
      xmlXPathObjectPtr obj = ctxt->value;
1136
1137
0
      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
0
    case XPATH_NODESET: {
1147
0
        xmlNodeSetPtr loc = ctxt->value->nodesetval;
1148
0
        if ((loc != NULL) && (loc->nodeNr > 0))
1149
0
      return;
1150
0
        break;
1151
0
    }
1152
0
    default:
1153
0
        break;
1154
0
      }
1155
1156
      /*
1157
       * Evaluating to improper values is equivalent to
1158
       * a sub-resource error, clean-up the stack
1159
       */
1160
0
      do {
1161
0
    obj = valuePop(ctxt);
1162
0
    if (obj != NULL) {
1163
0
        xmlXPathFreeObject(obj);
1164
0
    }
1165
0
      } while (obj != NULL);
1166
0
  }
1167
1168
  /*
1169
   * Is there another XPointer part.
1170
   */
1171
0
  SKIP_BLANKS;
1172
0
  name = xmlXPathParseName(ctxt);
1173
0
    }
1174
0
}
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
0
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
0
    if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) {
1194
0
        xmlXPtrErr(ctxt, XML_XPTR_CHILDSEQ_START,
1195
0
       "warning: ChildSeq not starting by /1\n", NULL);
1196
0
    }
1197
1198
0
    if (name != NULL) {
1199
0
  valuePush(ctxt, xmlXPathNewString(name));
1200
0
  xmlFree(name);
1201
0
  xmlXPathIdFunction(ctxt, 1);
1202
0
  CHECK_ERROR;
1203
0
    }
1204
1205
0
    while (CUR == '/') {
1206
0
  int child = 0, overflow = 0;
1207
0
  NEXT;
1208
1209
0
  while ((CUR >= '0') && (CUR <= '9')) {
1210
0
            int d = CUR - '0';
1211
0
            if (child > INT_MAX / 10)
1212
0
                overflow = 1;
1213
0
            else
1214
0
                child *= 10;
1215
0
            if (child > INT_MAX - d)
1216
0
                overflow = 1;
1217
0
            else
1218
0
                child += d;
1219
0
      NEXT;
1220
0
  }
1221
0
        if (overflow)
1222
0
            child = 0;
1223
0
  xmlXPtrGetChildNo(ctxt, child);
1224
0
    }
1225
0
}
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
0
xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) {
1240
0
    if (ctxt->valueTab == NULL) {
1241
  /* Allocate the value stack */
1242
0
  ctxt->valueTab = (xmlXPathObjectPtr *)
1243
0
       xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
1244
0
  if (ctxt->valueTab == NULL) {
1245
0
      xmlXPtrErrMemory("allocating evaluation context");
1246
0
      return;
1247
0
  }
1248
0
  ctxt->valueNr = 0;
1249
0
  ctxt->valueMax = 10;
1250
0
  ctxt->value = NULL;
1251
0
  ctxt->valueFrame = 0;
1252
0
    }
1253
0
    SKIP_BLANKS;
1254
0
    if (CUR == '/') {
1255
0
  xmlXPathRoot(ctxt);
1256
0
        xmlXPtrEvalChildSeq(ctxt, NULL);
1257
0
    } else {
1258
0
  xmlChar *name;
1259
1260
0
  name = xmlXPathParseName(ctxt);
1261
0
  if (name == NULL)
1262
0
      XP_ERROR(XPATH_EXPR_ERROR);
1263
0
  if (CUR == '(') {
1264
0
      xmlXPtrEvalFullXPtr(ctxt, name);
1265
      /* Short evaluation */
1266
0
      return;
1267
0
  } else {
1268
      /* this handle both Bare Names and Child Sequences */
1269
0
      xmlXPtrEvalChildSeq(ctxt, name);
1270
0
  }
1271
0
    }
1272
0
    SKIP_BLANKS;
1273
0
    if (CUR != 0)
1274
0
  XP_ERROR(XPATH_EXPR_ERROR);
1275
0
}
1276
1277
1278
/************************************************************************
1279
 *                  *
1280
 *      General routines        *
1281
 *                  *
1282
 ************************************************************************/
1283
1284
#ifdef LIBXML_XPTR_LOCS_ENABLED
1285
static
1286
void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs);
1287
static
1288
void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs);
1289
static
1290
void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs);
1291
static
1292
void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs);
1293
static
1294
void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs);
1295
static
1296
void xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs);
1297
static
1298
void xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs);
1299
#endif /* LIBXML_XPTR_LOCS_ENABLED */
1300
1301
/**
1302
 * xmlXPtrNewContext:
1303
 * @doc:  the XML document
1304
 * @here:  the node that directly contains the XPointer being evaluated or NULL
1305
 * @origin:  the element from which a user or program initiated traversal of
1306
 *           the link, or NULL.
1307
 *
1308
 * Create a new XPointer context
1309
 *
1310
 * Returns the xmlXPathContext just allocated.
1311
 */
1312
xmlXPathContextPtr
1313
0
xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) {
1314
0
    xmlXPathContextPtr ret;
1315
0
    (void) here;
1316
0
    (void) origin;
1317
1318
0
    ret = xmlXPathNewContext(doc);
1319
0
    if (ret == NULL)
1320
0
  return(ret);
1321
#ifdef LIBXML_XPTR_LOCS_ENABLED
1322
    ret->xptr = 1;
1323
    ret->here = here;
1324
    ret->origin = origin;
1325
1326
    xmlXPathRegisterFunc(ret, (xmlChar *)"range",
1327
                   xmlXPtrRangeFunction);
1328
    xmlXPathRegisterFunc(ret, (xmlChar *)"range-inside",
1329
                   xmlXPtrRangeInsideFunction);
1330
    xmlXPathRegisterFunc(ret, (xmlChar *)"string-range",
1331
                   xmlXPtrStringRangeFunction);
1332
    xmlXPathRegisterFunc(ret, (xmlChar *)"start-point",
1333
                   xmlXPtrStartPointFunction);
1334
    xmlXPathRegisterFunc(ret, (xmlChar *)"end-point",
1335
                   xmlXPtrEndPointFunction);
1336
    xmlXPathRegisterFunc(ret, (xmlChar *)"here",
1337
                   xmlXPtrHereFunction);
1338
    xmlXPathRegisterFunc(ret, (xmlChar *)" origin",
1339
                   xmlXPtrOriginFunction);
1340
#endif /* LIBXML_XPTR_LOCS_ENABLED */
1341
1342
0
    return(ret);
1343
0
}
1344
1345
/**
1346
 * xmlXPtrEval:
1347
 * @str:  the XPointer expression
1348
 * @ctx:  the XPointer context
1349
 *
1350
 * Evaluate the XPath Location Path in the given context.
1351
 *
1352
 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
1353
 *         the caller has to free the object.
1354
 */
1355
xmlXPathObjectPtr
1356
0
xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) {
1357
0
    xmlXPathParserContextPtr ctxt;
1358
0
    xmlXPathObjectPtr res = NULL, tmp;
1359
0
    xmlXPathObjectPtr init = NULL;
1360
0
    int stack = 0;
1361
1362
0
    xmlInitParser();
1363
1364
0
    if ((ctx == NULL) || (str == NULL))
1365
0
  return(NULL);
1366
1367
0
    ctxt = xmlXPathNewParserContext(str, ctx);
1368
0
    if (ctxt == NULL)
1369
0
  return(NULL);
1370
0
    xmlXPtrEvalXPointer(ctxt);
1371
1372
0
    if ((ctxt->value != NULL) &&
1373
#ifdef LIBXML_XPTR_LOCS_ENABLED
1374
  (ctxt->value->type != XPATH_LOCATIONSET) &&
1375
#endif
1376
0
  (ctxt->value->type != XPATH_NODESET)) {
1377
0
        xmlXPtrErr(ctxt, XML_XPTR_EVAL_FAILED,
1378
0
    "xmlXPtrEval: evaluation failed to return a node set\n",
1379
0
       NULL);
1380
0
    } else {
1381
0
  res = valuePop(ctxt);
1382
0
    }
1383
1384
0
    do {
1385
0
        tmp = valuePop(ctxt);
1386
0
  if (tmp != NULL) {
1387
0
      if (tmp != init) {
1388
0
    if (tmp->type == XPATH_NODESET) {
1389
        /*
1390
         * Evaluation may push a root nodeset which is unused
1391
         */
1392
0
        xmlNodeSetPtr set;
1393
0
        set = tmp->nodesetval;
1394
0
        if ((set == NULL) || (set->nodeNr != 1) ||
1395
0
      (set->nodeTab[0] != (xmlNodePtr) ctx->doc))
1396
0
      stack++;
1397
0
    } else
1398
0
        stack++;
1399
0
      }
1400
0
      xmlXPathFreeObject(tmp);
1401
0
        }
1402
0
    } while (tmp != NULL);
1403
0
    if (stack != 0) {
1404
0
        xmlXPtrErr(ctxt, XML_XPTR_EXTRA_OBJECTS,
1405
0
       "xmlXPtrEval: object(s) left on the eval stack\n",
1406
0
       NULL);
1407
0
    }
1408
0
    if (ctxt->error != XPATH_EXPRESSION_OK) {
1409
0
  xmlXPathFreeObject(res);
1410
0
  res = NULL;
1411
0
    }
1412
1413
0
    xmlXPathFreeParserContext(ctxt);
1414
0
    return(res);
1415
0
}
1416
1417
#ifdef LIBXML_XPTR_LOCS_ENABLED
1418
/**
1419
 * xmlXPtrBuildRangeNodeList:
1420
 * @range:  a range object
1421
 *
1422
 * Build a node list tree copy of the range
1423
 *
1424
 * Returns an xmlNodePtr list or NULL.
1425
 *         the caller has to free the node tree.
1426
 */
1427
static xmlNodePtr
1428
xmlXPtrBuildRangeNodeList(xmlXPathObjectPtr range) {
1429
    /* pointers to generated nodes */
1430
    xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp;
1431
    /* pointers to traversal nodes */
1432
    xmlNodePtr start, cur, end;
1433
    int index1, index2;
1434
1435
    if (range == NULL)
1436
  return(NULL);
1437
    if (range->type != XPATH_RANGE)
1438
  return(NULL);
1439
    start = (xmlNodePtr) range->user;
1440
1441
    if ((start == NULL) || (start->type == XML_NAMESPACE_DECL))
1442
  return(NULL);
1443
    end = range->user2;
1444
    if (end == NULL)
1445
  return(xmlCopyNode(start, 1));
1446
    if (end->type == XML_NAMESPACE_DECL)
1447
        return(NULL);
1448
1449
    cur = start;
1450
    index1 = range->index;
1451
    index2 = range->index2;
1452
    while (cur != NULL) {
1453
  if (cur == end) {
1454
      if (cur->type == XML_TEXT_NODE) {
1455
    const xmlChar *content = cur->content;
1456
    int len;
1457
1458
    if (content == NULL) {
1459
        tmp = xmlNewTextLen(NULL, 0);
1460
    } else {
1461
        len = index2;
1462
        if ((cur == start) && (index1 > 1)) {
1463
      content += (index1 - 1);
1464
      len -= (index1 - 1);
1465
      index1 = 0;
1466
        } else {
1467
      len = index2;
1468
        }
1469
        tmp = xmlNewTextLen(content, len);
1470
    }
1471
    /* single sub text node selection */
1472
    if (list == NULL)
1473
        return(tmp);
1474
    /* prune and return full set */
1475
    if (last != NULL)
1476
        xmlAddNextSibling(last, tmp);
1477
    else
1478
        xmlAddChild(parent, tmp);
1479
    return(list);
1480
      } else {
1481
    tmp = xmlCopyNode(cur, 0);
1482
    if (list == NULL) {
1483
        list = tmp;
1484
        parent = tmp;
1485
    } else {
1486
        if (last != NULL)
1487
      parent = xmlAddNextSibling(last, tmp);
1488
        else
1489
      parent = xmlAddChild(parent, tmp);
1490
    }
1491
    last = NULL;
1492
1493
    if (index2 > 1) {
1494
        end = xmlXPtrGetNthChild(cur, index2 - 1);
1495
        index2 = 0;
1496
    }
1497
    if ((cur == start) && (index1 > 1)) {
1498
        cur = xmlXPtrGetNthChild(cur, index1 - 1);
1499
        index1 = 0;
1500
    } else {
1501
        cur = cur->children;
1502
    }
1503
    /*
1504
     * Now gather the remaining nodes from cur to end
1505
     */
1506
    continue; /* while */
1507
      }
1508
  } else if ((cur == start) &&
1509
       (list == NULL) /* looks superfluous but ... */ ) {
1510
      if ((cur->type == XML_TEXT_NODE) ||
1511
    (cur->type == XML_CDATA_SECTION_NODE)) {
1512
    const xmlChar *content = cur->content;
1513
1514
    if (content == NULL) {
1515
        tmp = xmlNewTextLen(NULL, 0);
1516
    } else {
1517
        if (index1 > 1) {
1518
      content += (index1 - 1);
1519
        }
1520
        tmp = xmlNewText(content);
1521
    }
1522
    last = list = tmp;
1523
      } else {
1524
    if ((cur == start) && (index1 > 1)) {
1525
        tmp = xmlCopyNode(cur, 0);
1526
        list = tmp;
1527
        parent = tmp;
1528
        last = NULL;
1529
        cur = xmlXPtrGetNthChild(cur, index1 - 1);
1530
        index1 = 0;
1531
        /*
1532
         * Now gather the remaining nodes from cur to end
1533
         */
1534
        continue; /* while */
1535
    }
1536
    tmp = xmlCopyNode(cur, 1);
1537
    list = tmp;
1538
    parent = NULL;
1539
    last = tmp;
1540
      }
1541
  } else {
1542
      tmp = NULL;
1543
      switch (cur->type) {
1544
    case XML_DTD_NODE:
1545
    case XML_ELEMENT_DECL:
1546
    case XML_ATTRIBUTE_DECL:
1547
    case XML_ENTITY_NODE:
1548
        /* Do not copy DTD information */
1549
        break;
1550
    case XML_ENTITY_DECL:
1551
        TODO /* handle crossing entities -> stack needed */
1552
        break;
1553
    case XML_XINCLUDE_START:
1554
    case XML_XINCLUDE_END:
1555
        /* don't consider it part of the tree content */
1556
        break;
1557
    case XML_ATTRIBUTE_NODE:
1558
        /* Humm, should not happen ! */
1559
        STRANGE
1560
        break;
1561
    default:
1562
        tmp = xmlCopyNode(cur, 1);
1563
        break;
1564
      }
1565
      if (tmp != NULL) {
1566
    if ((list == NULL) || ((last == NULL) && (parent == NULL)))  {
1567
        STRANGE
1568
        return(NULL);
1569
    }
1570
    if (last != NULL)
1571
        xmlAddNextSibling(last, tmp);
1572
    else {
1573
        last = xmlAddChild(parent, tmp);
1574
    }
1575
      }
1576
  }
1577
  /*
1578
   * Skip to next node in document order
1579
   */
1580
  if ((list == NULL) || ((last == NULL) && (parent == NULL)))  {
1581
      STRANGE
1582
      return(NULL);
1583
  }
1584
  cur = xmlXPtrAdvanceNode(cur, NULL);
1585
    }
1586
    return(list);
1587
}
1588
1589
/**
1590
 * xmlXPtrBuildNodeList:
1591
 * @obj:  the XPointer result from the evaluation.
1592
 *
1593
 * Build a node list tree copy of the XPointer result.
1594
 * This will drop Attributes and Namespace declarations.
1595
 *
1596
 * Returns an xmlNodePtr list or NULL.
1597
 *         the caller has to free the node tree.
1598
 */
1599
xmlNodePtr
1600
xmlXPtrBuildNodeList(xmlXPathObjectPtr obj) {
1601
    xmlNodePtr list = NULL, last = NULL;
1602
    int i;
1603
1604
    if (obj == NULL)
1605
  return(NULL);
1606
    switch (obj->type) {
1607
        case XPATH_NODESET: {
1608
      xmlNodeSetPtr set = obj->nodesetval;
1609
      if (set == NULL)
1610
    return(NULL);
1611
      for (i = 0;i < set->nodeNr;i++) {
1612
    if (set->nodeTab[i] == NULL)
1613
        continue;
1614
    switch (set->nodeTab[i]->type) {
1615
        case XML_TEXT_NODE:
1616
        case XML_CDATA_SECTION_NODE:
1617
        case XML_ELEMENT_NODE:
1618
        case XML_ENTITY_REF_NODE:
1619
        case XML_ENTITY_NODE:
1620
        case XML_PI_NODE:
1621
        case XML_COMMENT_NODE:
1622
        case XML_DOCUMENT_NODE:
1623
        case XML_HTML_DOCUMENT_NODE:
1624
        case XML_XINCLUDE_START:
1625
        case XML_XINCLUDE_END:
1626
      break;
1627
        case XML_ATTRIBUTE_NODE:
1628
        case XML_NAMESPACE_DECL:
1629
        case XML_DOCUMENT_TYPE_NODE:
1630
        case XML_DOCUMENT_FRAG_NODE:
1631
        case XML_NOTATION_NODE:
1632
        case XML_DTD_NODE:
1633
        case XML_ELEMENT_DECL:
1634
        case XML_ATTRIBUTE_DECL:
1635
        case XML_ENTITY_DECL:
1636
      continue; /* for */
1637
    }
1638
    if (last == NULL)
1639
        list = last = xmlCopyNode(set->nodeTab[i], 1);
1640
    else {
1641
        xmlAddNextSibling(last, xmlCopyNode(set->nodeTab[i], 1));
1642
        if (last->next != NULL)
1643
      last = last->next;
1644
    }
1645
      }
1646
      break;
1647
  }
1648
  case XPATH_LOCATIONSET: {
1649
      xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user;
1650
      if (set == NULL)
1651
    return(NULL);
1652
      for (i = 0;i < set->locNr;i++) {
1653
    if (last == NULL)
1654
        list = last = xmlXPtrBuildNodeList(set->locTab[i]);
1655
    else
1656
        xmlAddNextSibling(last,
1657
          xmlXPtrBuildNodeList(set->locTab[i]));
1658
    if (last != NULL) {
1659
        while (last->next != NULL)
1660
      last = last->next;
1661
    }
1662
      }
1663
      break;
1664
  }
1665
  case XPATH_RANGE:
1666
      return(xmlXPtrBuildRangeNodeList(obj));
1667
  case XPATH_POINT:
1668
      return(xmlCopyNode(obj->user, 0));
1669
  default:
1670
      break;
1671
    }
1672
    return(list);
1673
}
1674
1675
/************************************************************************
1676
 *                  *
1677
 *      XPointer functions        *
1678
 *                  *
1679
 ************************************************************************/
1680
1681
/**
1682
 * xmlXPtrNbLocChildren:
1683
 * @node:  an xmlNodePtr
1684
 *
1685
 * Count the number of location children of @node or the length of the
1686
 * string value in case of text/PI/Comments nodes
1687
 *
1688
 * Returns the number of location children
1689
 */
1690
static int
1691
xmlXPtrNbLocChildren(xmlNodePtr node) {
1692
    int ret = 0;
1693
    if (node == NULL)
1694
  return(-1);
1695
    switch (node->type) {
1696
        case XML_HTML_DOCUMENT_NODE:
1697
        case XML_DOCUMENT_NODE:
1698
        case XML_ELEMENT_NODE:
1699
      node = node->children;
1700
      while (node != NULL) {
1701
    if (node->type == XML_ELEMENT_NODE)
1702
        ret++;
1703
    node = node->next;
1704
      }
1705
      break;
1706
        case XML_ATTRIBUTE_NODE:
1707
      return(-1);
1708
1709
        case XML_PI_NODE:
1710
        case XML_COMMENT_NODE:
1711
        case XML_TEXT_NODE:
1712
        case XML_CDATA_SECTION_NODE:
1713
        case XML_ENTITY_REF_NODE:
1714
      ret = xmlStrlen(node->content);
1715
      break;
1716
  default:
1717
      return(-1);
1718
    }
1719
    return(ret);
1720
}
1721
1722
/**
1723
 * xmlXPtrHereFunction:
1724
 * @ctxt:  the XPointer Parser context
1725
 * @nargs:  the number of args
1726
 *
1727
 * Function implementing here() operation
1728
 * as described in 5.4.3
1729
 */
1730
static void
1731
xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1732
    CHECK_ARITY(0);
1733
1734
    if (ctxt->context->here == NULL)
1735
  XP_ERROR(XPTR_SYNTAX_ERROR);
1736
1737
    valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL));
1738
}
1739
1740
/**
1741
 * xmlXPtrOriginFunction:
1742
 * @ctxt:  the XPointer Parser context
1743
 * @nargs:  the number of args
1744
 *
1745
 * Function implementing origin() operation
1746
 * as described in 5.4.3
1747
 */
1748
static void
1749
xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1750
    CHECK_ARITY(0);
1751
1752
    if (ctxt->context->origin == NULL)
1753
  XP_ERROR(XPTR_SYNTAX_ERROR);
1754
1755
    valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL));
1756
}
1757
1758
/**
1759
 * xmlXPtrStartPointFunction:
1760
 * @ctxt:  the XPointer Parser context
1761
 * @nargs:  the number of args
1762
 *
1763
 * Function implementing start-point() operation
1764
 * as described in 5.4.3
1765
 * ----------------
1766
 * location-set start-point(location-set)
1767
 *
1768
 * For each location x in the argument location-set, start-point adds a
1769
 * location of type point to the result location-set. That point represents
1770
 * the start point of location x and is determined by the following rules:
1771
 *
1772
 * - If x is of type point, the start point is x.
1773
 * - If x is of type range, the start point is the start point of x.
1774
 * - If x is of type root, element, text, comment, or processing instruction,
1775
 * - the container node of the start point is x and the index is 0.
1776
 * - If x is of type attribute or namespace, the function must signal a
1777
 *   syntax error.
1778
 * ----------------
1779
 *
1780
 */
1781
static void
1782
xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1783
    xmlXPathObjectPtr tmp, obj, point;
1784
    xmlLocationSetPtr newset = NULL;
1785
    xmlLocationSetPtr oldset = NULL;
1786
1787
    CHECK_ARITY(1);
1788
    if ((ctxt->value == NULL) ||
1789
  ((ctxt->value->type != XPATH_LOCATIONSET) &&
1790
   (ctxt->value->type != XPATH_NODESET)))
1791
        XP_ERROR(XPATH_INVALID_TYPE)
1792
1793
    obj = valuePop(ctxt);
1794
    if (obj->type == XPATH_NODESET) {
1795
  /*
1796
   * First convert to a location set
1797
   */
1798
  tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval);
1799
  xmlXPathFreeObject(obj);
1800
  if (tmp == NULL)
1801
            XP_ERROR(XPATH_MEMORY_ERROR)
1802
  obj = tmp;
1803
    }
1804
1805
    newset = xmlXPtrLocationSetCreate(NULL);
1806
    if (newset == NULL) {
1807
  xmlXPathFreeObject(obj);
1808
        XP_ERROR(XPATH_MEMORY_ERROR);
1809
    }
1810
    oldset = (xmlLocationSetPtr) obj->user;
1811
    if (oldset != NULL) {
1812
  int i;
1813
1814
  for (i = 0; i < oldset->locNr; i++) {
1815
      tmp = oldset->locTab[i];
1816
      if (tmp == NULL)
1817
    continue;
1818
      point = NULL;
1819
      switch (tmp->type) {
1820
    case XPATH_POINT:
1821
        point = xmlXPtrNewPoint(tmp->user, tmp->index);
1822
        break;
1823
    case XPATH_RANGE: {
1824
        xmlNodePtr node = tmp->user;
1825
        if (node != NULL) {
1826
      if ((node->type == XML_ATTRIBUTE_NODE) ||
1827
                            (node->type == XML_NAMESPACE_DECL)) {
1828
          xmlXPathFreeObject(obj);
1829
          xmlXPtrFreeLocationSet(newset);
1830
          XP_ERROR(XPTR_SYNTAX_ERROR);
1831
      }
1832
      point = xmlXPtrNewPoint(node, tmp->index);
1833
        }
1834
        break;
1835
          }
1836
    default:
1837
        /*** Should we raise an error ?
1838
        xmlXPathFreeObject(obj);
1839
        xmlXPathFreeObject(newset);
1840
        XP_ERROR(XPATH_INVALID_TYPE)
1841
        ***/
1842
        break;
1843
      }
1844
            if (point != NULL)
1845
    xmlXPtrLocationSetAdd(newset, point);
1846
  }
1847
    }
1848
    xmlXPathFreeObject(obj);
1849
    valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
1850
}
1851
1852
/**
1853
 * xmlXPtrEndPointFunction:
1854
 * @ctxt:  the XPointer Parser context
1855
 * @nargs:  the number of args
1856
 *
1857
 * Function implementing end-point() operation
1858
 * as described in 5.4.3
1859
 * ----------------------------
1860
 * location-set end-point(location-set)
1861
 *
1862
 * For each location x in the argument location-set, end-point adds a
1863
 * location of type point to the result location-set. That point represents
1864
 * the end point of location x and is determined by the following rules:
1865
 *
1866
 * - If x is of type point, the resulting point is x.
1867
 * - If x is of type range, the resulting point is the end point of x.
1868
 * - If x is of type root or element, the container node of the resulting
1869
 *   point is x and the index is the number of location children of x.
1870
 * - If x is of type text, comment, or processing instruction, the container
1871
 *   node of the resulting point is x and the index is the length of the
1872
 *   string-value of x.
1873
 * - If x is of type attribute or namespace, the function must signal a
1874
 *   syntax error.
1875
 * ----------------------------
1876
 */
1877
static void
1878
xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1879
    xmlXPathObjectPtr tmp, obj, point;
1880
    xmlLocationSetPtr newset = NULL;
1881
    xmlLocationSetPtr oldset = NULL;
1882
1883
    CHECK_ARITY(1);
1884
    if ((ctxt->value == NULL) ||
1885
  ((ctxt->value->type != XPATH_LOCATIONSET) &&
1886
   (ctxt->value->type != XPATH_NODESET)))
1887
        XP_ERROR(XPATH_INVALID_TYPE)
1888
1889
    obj = valuePop(ctxt);
1890
    if (obj->type == XPATH_NODESET) {
1891
  /*
1892
   * First convert to a location set
1893
   */
1894
  tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval);
1895
  xmlXPathFreeObject(obj);
1896
  if (tmp == NULL)
1897
            XP_ERROR(XPATH_MEMORY_ERROR)
1898
  obj = tmp;
1899
    }
1900
1901
    newset = xmlXPtrLocationSetCreate(NULL);
1902
    if (newset == NULL) {
1903
  xmlXPathFreeObject(obj);
1904
        XP_ERROR(XPATH_MEMORY_ERROR);
1905
    }
1906
    oldset = (xmlLocationSetPtr) obj->user;
1907
    if (oldset != NULL) {
1908
  int i;
1909
1910
  for (i = 0; i < oldset->locNr; i++) {
1911
      tmp = oldset->locTab[i];
1912
      if (tmp == NULL)
1913
    continue;
1914
      point = NULL;
1915
      switch (tmp->type) {
1916
    case XPATH_POINT:
1917
        point = xmlXPtrNewPoint(tmp->user, tmp->index);
1918
        break;
1919
    case XPATH_RANGE: {
1920
        xmlNodePtr node = tmp->user2;
1921
        if (node != NULL) {
1922
      if ((node->type == XML_ATTRIBUTE_NODE) ||
1923
                            (node->type == XML_NAMESPACE_DECL)) {
1924
          xmlXPathFreeObject(obj);
1925
          xmlXPtrFreeLocationSet(newset);
1926
          XP_ERROR(XPTR_SYNTAX_ERROR);
1927
      }
1928
      point = xmlXPtrNewPoint(node, tmp->index2);
1929
        } else if (tmp->user == NULL) {
1930
      point = xmlXPtrNewPoint(node,
1931
               xmlXPtrNbLocChildren(node));
1932
        }
1933
        break;
1934
          }
1935
    default:
1936
        /*** Should we raise an error ?
1937
        xmlXPathFreeObject(obj);
1938
        xmlXPathFreeObject(newset);
1939
        XP_ERROR(XPATH_INVALID_TYPE)
1940
        ***/
1941
        break;
1942
      }
1943
            if (point != NULL)
1944
    xmlXPtrLocationSetAdd(newset, point);
1945
  }
1946
    }
1947
    xmlXPathFreeObject(obj);
1948
    valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
1949
}
1950
1951
1952
/**
1953
 * xmlXPtrCoveringRange:
1954
 * @ctxt:  the XPointer Parser context
1955
 * @loc:  the location for which the covering range must be computed
1956
 *
1957
 * A covering range is a range that wholly encompasses a location
1958
 * Section 5.3.3. Covering Ranges for All Location Types
1959
 *        http://www.w3.org/TR/xptr#N2267
1960
 *
1961
 * Returns a new location or NULL in case of error
1962
 */
1963
static xmlXPathObjectPtr
1964
xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) {
1965
    if (loc == NULL)
1966
  return(NULL);
1967
    if ((ctxt == NULL) || (ctxt->context == NULL) ||
1968
  (ctxt->context->doc == NULL))
1969
  return(NULL);
1970
    switch (loc->type) {
1971
        case XPATH_POINT:
1972
      return(xmlXPtrNewRange(loc->user, loc->index,
1973
                 loc->user, loc->index));
1974
        case XPATH_RANGE:
1975
      if (loc->user2 != NULL) {
1976
    return(xmlXPtrNewRange(loc->user, loc->index,
1977
                    loc->user2, loc->index2));
1978
      } else {
1979
    xmlNodePtr node = (xmlNodePtr) loc->user;
1980
    if (node == (xmlNodePtr) ctxt->context->doc) {
1981
        return(xmlXPtrNewRange(node, 0, node,
1982
             xmlXPtrGetArity(node)));
1983
    } else {
1984
        switch (node->type) {
1985
      case XML_ATTRIBUTE_NODE:
1986
      /* !!! our model is slightly different than XPath */
1987
          return(xmlXPtrNewRange(node, 0, node,
1988
                     xmlXPtrGetArity(node)));
1989
      case XML_ELEMENT_NODE:
1990
      case XML_TEXT_NODE:
1991
      case XML_CDATA_SECTION_NODE:
1992
      case XML_ENTITY_REF_NODE:
1993
      case XML_PI_NODE:
1994
      case XML_COMMENT_NODE:
1995
      case XML_DOCUMENT_NODE:
1996
      case XML_NOTATION_NODE:
1997
      case XML_HTML_DOCUMENT_NODE: {
1998
          int indx = xmlXPtrGetIndex(node);
1999
2000
          node = node->parent;
2001
          return(xmlXPtrNewRange(node, indx - 1,
2002
                     node, indx + 1));
2003
      }
2004
      default:
2005
          return(NULL);
2006
        }
2007
    }
2008
      }
2009
  default:
2010
      TODO /* missed one case ??? */
2011
    }
2012
    return(NULL);
2013
}
2014
2015
/**
2016
 * xmlXPtrRangeFunction:
2017
 * @ctxt:  the XPointer Parser context
2018
 * @nargs:  the number of args
2019
 *
2020
 * Function implementing the range() function 5.4.3
2021
 *  location-set range(location-set )
2022
 *
2023
 *  The range function returns ranges covering the locations in
2024
 *  the argument location-set. For each location x in the argument
2025
 *  location-set, a range location representing the covering range of
2026
 *  x is added to the result location-set.
2027
 */
2028
static void
2029
xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2030
    int i;
2031
    xmlXPathObjectPtr set;
2032
    xmlLocationSetPtr oldset;
2033
    xmlLocationSetPtr newset;
2034
2035
    CHECK_ARITY(1);
2036
    if ((ctxt->value == NULL) ||
2037
  ((ctxt->value->type != XPATH_LOCATIONSET) &&
2038
   (ctxt->value->type != XPATH_NODESET)))
2039
        XP_ERROR(XPATH_INVALID_TYPE)
2040
2041
    set = valuePop(ctxt);
2042
    if (set->type == XPATH_NODESET) {
2043
  xmlXPathObjectPtr tmp;
2044
2045
  /*
2046
   * First convert to a location set
2047
   */
2048
  tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2049
  xmlXPathFreeObject(set);
2050
  if (tmp == NULL)
2051
            XP_ERROR(XPATH_MEMORY_ERROR)
2052
  set = tmp;
2053
    }
2054
    oldset = (xmlLocationSetPtr) set->user;
2055
2056
    /*
2057
     * The loop is to compute the covering range for each item and add it
2058
     */
2059
    newset = xmlXPtrLocationSetCreate(NULL);
2060
    if (newset == NULL) {
2061
  xmlXPathFreeObject(set);
2062
        XP_ERROR(XPATH_MEMORY_ERROR);
2063
    }
2064
    if (oldset != NULL) {
2065
        for (i = 0;i < oldset->locNr;i++) {
2066
            xmlXPtrLocationSetAdd(newset,
2067
                    xmlXPtrCoveringRange(ctxt, oldset->locTab[i]));
2068
        }
2069
    }
2070
2071
    /*
2072
     * Save the new value and cleanup
2073
     */
2074
    valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2075
    xmlXPathFreeObject(set);
2076
}
2077
2078
/**
2079
 * xmlXPtrInsideRange:
2080
 * @ctxt:  the XPointer Parser context
2081
 * @loc:  the location for which the inside range must be computed
2082
 *
2083
 * A inside range is a range described in the range-inside() description
2084
 *
2085
 * Returns a new location or NULL in case of error
2086
 */
2087
static xmlXPathObjectPtr
2088
xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) {
2089
    if (loc == NULL)
2090
  return(NULL);
2091
    if ((ctxt == NULL) || (ctxt->context == NULL) ||
2092
  (ctxt->context->doc == NULL))
2093
  return(NULL);
2094
    switch (loc->type) {
2095
        case XPATH_POINT: {
2096
      xmlNodePtr node = (xmlNodePtr) loc->user;
2097
      switch (node->type) {
2098
    case XML_PI_NODE:
2099
    case XML_COMMENT_NODE:
2100
    case XML_TEXT_NODE:
2101
    case XML_CDATA_SECTION_NODE: {
2102
        if (node->content == NULL) {
2103
      return(xmlXPtrNewRange(node, 0, node, 0));
2104
        } else {
2105
      return(xmlXPtrNewRange(node, 0, node,
2106
                 xmlStrlen(node->content)));
2107
        }
2108
    }
2109
    case XML_ATTRIBUTE_NODE:
2110
    case XML_ELEMENT_NODE:
2111
    case XML_ENTITY_REF_NODE:
2112
    case XML_DOCUMENT_NODE:
2113
    case XML_NOTATION_NODE:
2114
    case XML_HTML_DOCUMENT_NODE: {
2115
        return(xmlXPtrNewRange(node, 0, node,
2116
             xmlXPtrGetArity(node)));
2117
    }
2118
    default:
2119
        break;
2120
      }
2121
      return(NULL);
2122
  }
2123
        case XPATH_RANGE: {
2124
      xmlNodePtr node = (xmlNodePtr) loc->user;
2125
      if (loc->user2 != NULL) {
2126
    return(xmlXPtrNewRange(node, loc->index,
2127
                     loc->user2, loc->index2));
2128
      } else {
2129
    switch (node->type) {
2130
        case XML_PI_NODE:
2131
        case XML_COMMENT_NODE:
2132
        case XML_TEXT_NODE:
2133
        case XML_CDATA_SECTION_NODE: {
2134
      if (node->content == NULL) {
2135
          return(xmlXPtrNewRange(node, 0, node, 0));
2136
      } else {
2137
          return(xmlXPtrNewRange(node, 0, node,
2138
               xmlStrlen(node->content)));
2139
      }
2140
        }
2141
        case XML_ATTRIBUTE_NODE:
2142
        case XML_ELEMENT_NODE:
2143
        case XML_ENTITY_REF_NODE:
2144
        case XML_DOCUMENT_NODE:
2145
        case XML_NOTATION_NODE:
2146
        case XML_HTML_DOCUMENT_NODE: {
2147
      return(xmlXPtrNewRange(node, 0, node,
2148
                 xmlXPtrGetArity(node)));
2149
        }
2150
        default:
2151
      break;
2152
    }
2153
    return(NULL);
2154
      }
2155
        }
2156
  default:
2157
      TODO /* missed one case ??? */
2158
    }
2159
    return(NULL);
2160
}
2161
2162
/**
2163
 * xmlXPtrRangeInsideFunction:
2164
 * @ctxt:  the XPointer Parser context
2165
 * @nargs:  the number of args
2166
 *
2167
 * Function implementing the range-inside() function 5.4.3
2168
 *  location-set range-inside(location-set )
2169
 *
2170
 *  The range-inside function returns ranges covering the contents of
2171
 *  the locations in the argument location-set. For each location x in
2172
 *  the argument location-set, a range location is added to the result
2173
 *  location-set. If x is a range location, then x is added to the
2174
 *  result location-set. If x is not a range location, then x is used
2175
 *  as the container location of the start and end points of the range
2176
 *  location to be added; the index of the start point of the range is
2177
 *  zero; if the end point is a character point then its index is the
2178
 *  length of the string-value of x, and otherwise is the number of
2179
 *  location children of x.
2180
 *
2181
 */
2182
static void
2183
xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2184
    int i;
2185
    xmlXPathObjectPtr set;
2186
    xmlLocationSetPtr oldset;
2187
    xmlLocationSetPtr newset;
2188
2189
    CHECK_ARITY(1);
2190
    if ((ctxt->value == NULL) ||
2191
  ((ctxt->value->type != XPATH_LOCATIONSET) &&
2192
   (ctxt->value->type != XPATH_NODESET)))
2193
        XP_ERROR(XPATH_INVALID_TYPE)
2194
2195
    set = valuePop(ctxt);
2196
    if (set->type == XPATH_NODESET) {
2197
  xmlXPathObjectPtr tmp;
2198
2199
  /*
2200
   * First convert to a location set
2201
   */
2202
  tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2203
  xmlXPathFreeObject(set);
2204
  if (tmp == NULL)
2205
       XP_ERROR(XPATH_MEMORY_ERROR)
2206
  set = tmp;
2207
    }
2208
2209
    /*
2210
     * The loop is to compute the covering range for each item and add it
2211
     */
2212
    newset = xmlXPtrLocationSetCreate(NULL);
2213
    if (newset == NULL) {
2214
  xmlXPathFreeObject(set);
2215
        XP_ERROR(XPATH_MEMORY_ERROR);
2216
    }
2217
    oldset = (xmlLocationSetPtr) set->user;
2218
    if (oldset != NULL) {
2219
        for (i = 0;i < oldset->locNr;i++) {
2220
            xmlXPtrLocationSetAdd(newset,
2221
                    xmlXPtrInsideRange(ctxt, oldset->locTab[i]));
2222
        }
2223
    }
2224
2225
    /*
2226
     * Save the new value and cleanup
2227
     */
2228
    valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2229
    xmlXPathFreeObject(set);
2230
}
2231
2232
/**
2233
 * xmlXPtrRangeToFunction:
2234
 * @ctxt:  the XPointer Parser context
2235
 * @nargs:  the number of args
2236
 *
2237
 * Implement the range-to() XPointer function
2238
 *
2239
 * Obsolete. range-to is not a real function but a special type of location
2240
 * step which is handled in xpath.c.
2241
 */
2242
void
2243
xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt,
2244
                       int nargs ATTRIBUTE_UNUSED) {
2245
    XP_ERROR(XPATH_EXPR_ERROR);
2246
}
2247
2248
/**
2249
 * xmlXPtrAdvanceNode:
2250
 * @cur:  the node
2251
 * @level: incremented/decremented to show level in tree
2252
 *
2253
 * Advance to the next element or text node in document order
2254
 * TODO: add a stack for entering/exiting entities
2255
 *
2256
 * Returns -1 in case of failure, 0 otherwise
2257
 */
2258
xmlNodePtr
2259
xmlXPtrAdvanceNode(xmlNodePtr cur, int *level) {
2260
next:
2261
    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
2262
  return(NULL);
2263
    if (cur->children != NULL) {
2264
        cur = cur->children ;
2265
  if (level != NULL)
2266
      (*level)++;
2267
  goto found;
2268
    }
2269
skip:   /* This label should only be needed if something is wrong! */
2270
    if (cur->next != NULL) {
2271
  cur = cur->next;
2272
  goto found;
2273
    }
2274
    do {
2275
        cur = cur->parent;
2276
  if (level != NULL)
2277
      (*level)--;
2278
        if (cur == NULL) return(NULL);
2279
        if (cur->next != NULL) {
2280
      cur = cur->next;
2281
      goto found;
2282
  }
2283
    } while (cur != NULL);
2284
2285
found:
2286
    if ((cur->type != XML_ELEMENT_NODE) &&
2287
  (cur->type != XML_TEXT_NODE) &&
2288
  (cur->type != XML_DOCUMENT_NODE) &&
2289
  (cur->type != XML_HTML_DOCUMENT_NODE) &&
2290
  (cur->type != XML_CDATA_SECTION_NODE)) {
2291
      if (cur->type == XML_ENTITY_REF_NODE) { /* Shouldn't happen */
2292
    TODO
2293
    goto skip;
2294
      }
2295
      goto next;
2296
  }
2297
    return(cur);
2298
}
2299
2300
/**
2301
 * xmlXPtrAdvanceChar:
2302
 * @node:  the node
2303
 * @indx:  the indx
2304
 * @bytes:  the number of bytes
2305
 *
2306
 * Advance a point of the associated number of bytes (not UTF8 chars)
2307
 *
2308
 * Returns -1 in case of failure, 0 otherwise
2309
 */
2310
static int
2311
xmlXPtrAdvanceChar(xmlNodePtr *node, int *indx, int bytes) {
2312
    xmlNodePtr cur;
2313
    int pos;
2314
    int len;
2315
2316
    if ((node == NULL) || (indx == NULL))
2317
  return(-1);
2318
    cur = *node;
2319
    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
2320
  return(-1);
2321
    pos = *indx;
2322
2323
    while (bytes >= 0) {
2324
  /*
2325
   * First position to the beginning of the first text node
2326
   * corresponding to this point
2327
   */
2328
  while ((cur != NULL) &&
2329
         ((cur->type == XML_ELEMENT_NODE) ||
2330
          (cur->type == XML_DOCUMENT_NODE) ||
2331
          (cur->type == XML_HTML_DOCUMENT_NODE))) {
2332
      if (pos > 0) {
2333
    cur = xmlXPtrGetNthChild(cur, pos);
2334
    pos = 0;
2335
      } else {
2336
    cur = xmlXPtrAdvanceNode(cur, NULL);
2337
    pos = 0;
2338
      }
2339
  }
2340
2341
  if (cur == NULL) {
2342
      *node = NULL;
2343
      *indx = 0;
2344
      return(-1);
2345
  }
2346
2347
  /*
2348
   * if there is no move needed return the current value.
2349
   */
2350
  if (pos == 0) pos = 1;
2351
  if (bytes == 0) {
2352
      *node = cur;
2353
      *indx = pos;
2354
      return(0);
2355
  }
2356
  /*
2357
   * We should have a text (or cdata) node ...
2358
   */
2359
  len = 0;
2360
  if ((cur->type != XML_ELEMENT_NODE) &&
2361
            (cur->content != NULL)) {
2362
      len = xmlStrlen(cur->content);
2363
  }
2364
  if (pos > len) {
2365
      /* Strange, the indx in the text node is greater than it's len */
2366
      STRANGE
2367
      pos = len;
2368
  }
2369
  if (pos + bytes >= len) {
2370
      bytes -= (len - pos);
2371
      cur = xmlXPtrAdvanceNode(cur, NULL);
2372
      pos = 0;
2373
  } else if (pos + bytes < len) {
2374
      pos += bytes;
2375
      *node = cur;
2376
      *indx = pos;
2377
      return(0);
2378
  }
2379
    }
2380
    return(-1);
2381
}
2382
2383
/**
2384
 * xmlXPtrMatchString:
2385
 * @string:  the string to search
2386
 * @start:  the start textnode
2387
 * @startindex:  the start index
2388
 * @end:  the end textnode IN/OUT
2389
 * @endindex:  the end index IN/OUT
2390
 *
2391
 * Check whether the document contains @string at the position
2392
 * (@start, @startindex) and limited by the (@end, @endindex) point
2393
 *
2394
 * Returns -1 in case of failure, 0 if not found, 1 if found in which case
2395
 *            (@start, @startindex) will indicate the position of the beginning
2396
 *            of the range and (@end, @endindex) will indicate the end
2397
 *            of the range
2398
 */
2399
static int
2400
xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex,
2401
              xmlNodePtr *end, int *endindex) {
2402
    xmlNodePtr cur;
2403
    int pos; /* 0 based */
2404
    int len; /* in bytes */
2405
    int stringlen; /* in bytes */
2406
    int match;
2407
2408
    if (string == NULL)
2409
  return(-1);
2410
    if ((start == NULL) || (start->type == XML_NAMESPACE_DECL))
2411
  return(-1);
2412
    if ((end == NULL) || (*end == NULL) ||
2413
        ((*end)->type == XML_NAMESPACE_DECL) || (endindex == NULL))
2414
  return(-1);
2415
    cur = start;
2416
    pos = startindex - 1;
2417
    stringlen = xmlStrlen(string);
2418
2419
    while (stringlen > 0) {
2420
  if ((cur == *end) && (pos + stringlen > *endindex))
2421
      return(0);
2422
2423
  if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
2424
      len = xmlStrlen(cur->content);
2425
      if (len >= pos + stringlen) {
2426
    match = (!xmlStrncmp(&cur->content[pos], string, stringlen));
2427
    if (match) {
2428
#ifdef DEBUG_RANGES
2429
        xmlGenericError(xmlGenericErrorContext,
2430
          "found range %d bytes at index %d of ->",
2431
          stringlen, pos + 1);
2432
        xmlDebugDumpString(stdout, cur->content);
2433
        xmlGenericError(xmlGenericErrorContext, "\n");
2434
#endif
2435
        *end = cur;
2436
        *endindex = pos + stringlen;
2437
        return(1);
2438
    } else {
2439
        return(0);
2440
    }
2441
      } else {
2442
                int sub = len - pos;
2443
    match = (!xmlStrncmp(&cur->content[pos], string, sub));
2444
    if (match) {
2445
#ifdef DEBUG_RANGES
2446
        xmlGenericError(xmlGenericErrorContext,
2447
          "found subrange %d bytes at index %d of ->",
2448
          sub, pos + 1);
2449
        xmlDebugDumpString(stdout, cur->content);
2450
        xmlGenericError(xmlGenericErrorContext, "\n");
2451
#endif
2452
                    string = &string[sub];
2453
        stringlen -= sub;
2454
    } else {
2455
        return(0);
2456
    }
2457
      }
2458
  }
2459
  cur = xmlXPtrAdvanceNode(cur, NULL);
2460
  if (cur == NULL)
2461
      return(0);
2462
  pos = 0;
2463
    }
2464
    return(1);
2465
}
2466
2467
/**
2468
 * xmlXPtrSearchString:
2469
 * @string:  the string to search
2470
 * @start:  the start textnode IN/OUT
2471
 * @startindex:  the start index IN/OUT
2472
 * @end:  the end textnode
2473
 * @endindex:  the end index
2474
 *
2475
 * Search the next occurrence of @string within the document content
2476
 * until the (@end, @endindex) point is reached
2477
 *
2478
 * Returns -1 in case of failure, 0 if not found, 1 if found in which case
2479
 *            (@start, @startindex) will indicate the position of the beginning
2480
 *            of the range and (@end, @endindex) will indicate the end
2481
 *            of the range
2482
 */
2483
static int
2484
xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex,
2485
              xmlNodePtr *end, int *endindex) {
2486
    xmlNodePtr cur;
2487
    const xmlChar *str;
2488
    int pos; /* 0 based */
2489
    int len; /* in bytes */
2490
    xmlChar first;
2491
2492
    if (string == NULL)
2493
  return(-1);
2494
    if ((start == NULL) || (*start == NULL) ||
2495
        ((*start)->type == XML_NAMESPACE_DECL) || (startindex == NULL))
2496
  return(-1);
2497
    if ((end == NULL) || (endindex == NULL))
2498
  return(-1);
2499
    cur = *start;
2500
    pos = *startindex - 1;
2501
    first = string[0];
2502
2503
    while (cur != NULL) {
2504
  if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
2505
      len = xmlStrlen(cur->content);
2506
      while (pos <= len) {
2507
    if (first != 0) {
2508
        str = xmlStrchr(&cur->content[pos], first);
2509
        if (str != NULL) {
2510
      pos = (str - (xmlChar *)(cur->content));
2511
#ifdef DEBUG_RANGES
2512
      xmlGenericError(xmlGenericErrorContext,
2513
        "found '%c' at index %d of ->",
2514
        first, pos + 1);
2515
      xmlDebugDumpString(stdout, cur->content);
2516
      xmlGenericError(xmlGenericErrorContext, "\n");
2517
#endif
2518
      if (xmlXPtrMatchString(string, cur, pos + 1,
2519
                 end, endindex)) {
2520
          *start = cur;
2521
          *startindex = pos + 1;
2522
          return(1);
2523
      }
2524
      pos++;
2525
        } else {
2526
      pos = len + 1;
2527
        }
2528
    } else {
2529
        /*
2530
         * An empty string is considered to match before each
2531
         * character of the string-value and after the final
2532
         * character.
2533
         */
2534
#ifdef DEBUG_RANGES
2535
        xmlGenericError(xmlGenericErrorContext,
2536
          "found '' at index %d of ->",
2537
          pos + 1);
2538
        xmlDebugDumpString(stdout, cur->content);
2539
        xmlGenericError(xmlGenericErrorContext, "\n");
2540
#endif
2541
        *start = cur;
2542
        *startindex = pos + 1;
2543
        *end = cur;
2544
        *endindex = pos + 1;
2545
        return(1);
2546
    }
2547
      }
2548
  }
2549
  if ((cur == *end) && (pos >= *endindex))
2550
      return(0);
2551
  cur = xmlXPtrAdvanceNode(cur, NULL);
2552
  if (cur == NULL)
2553
      return(0);
2554
  pos = 1;
2555
    }
2556
    return(0);
2557
}
2558
2559
/**
2560
 * xmlXPtrGetLastChar:
2561
 * @node:  the node
2562
 * @index:  the index
2563
 *
2564
 * Computes the point coordinates of the last char of this point
2565
 *
2566
 * Returns -1 in case of failure, 0 otherwise
2567
 */
2568
static int
2569
xmlXPtrGetLastChar(xmlNodePtr *node, int *indx) {
2570
    xmlNodePtr cur;
2571
    int pos, len = 0;
2572
2573
    if ((node == NULL) || (*node == NULL) ||
2574
        ((*node)->type == XML_NAMESPACE_DECL) || (indx == NULL))
2575
  return(-1);
2576
    cur = *node;
2577
    pos = *indx;
2578
2579
    if ((cur->type == XML_ELEMENT_NODE) ||
2580
  (cur->type == XML_DOCUMENT_NODE) ||
2581
  (cur->type == XML_HTML_DOCUMENT_NODE)) {
2582
  if (pos > 0) {
2583
      cur = xmlXPtrGetNthChild(cur, pos);
2584
  }
2585
    }
2586
    while (cur != NULL) {
2587
  if (cur->last != NULL)
2588
      cur = cur->last;
2589
  else if ((cur->type != XML_ELEMENT_NODE) &&
2590
           (cur->content != NULL)) {
2591
      len = xmlStrlen(cur->content);
2592
      break;
2593
  } else {
2594
      return(-1);
2595
  }
2596
    }
2597
    if (cur == NULL)
2598
  return(-1);
2599
    *node = cur;
2600
    *indx = len;
2601
    return(0);
2602
}
2603
2604
/**
2605
 * xmlXPtrGetStartPoint:
2606
 * @obj:  an range
2607
 * @node:  the resulting node
2608
 * @indx:  the resulting index
2609
 *
2610
 * read the object and return the start point coordinates.
2611
 *
2612
 * Returns -1 in case of failure, 0 otherwise
2613
 */
2614
static int
2615
xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) {
2616
    if ((obj == NULL) || (node == NULL) || (indx == NULL))
2617
  return(-1);
2618
2619
    switch (obj->type) {
2620
        case XPATH_POINT:
2621
      *node = obj->user;
2622
      if (obj->index <= 0)
2623
    *indx = 0;
2624
      else
2625
    *indx = obj->index;
2626
      return(0);
2627
        case XPATH_RANGE:
2628
      *node = obj->user;
2629
      if (obj->index <= 0)
2630
    *indx = 0;
2631
      else
2632
    *indx = obj->index;
2633
      return(0);
2634
  default:
2635
      break;
2636
    }
2637
    return(-1);
2638
}
2639
2640
/**
2641
 * xmlXPtrGetEndPoint:
2642
 * @obj:  an range
2643
 * @node:  the resulting node
2644
 * @indx:  the resulting indx
2645
 *
2646
 * read the object and return the end point coordinates.
2647
 *
2648
 * Returns -1 in case of failure, 0 otherwise
2649
 */
2650
static int
2651
xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) {
2652
    if ((obj == NULL) || (node == NULL) || (indx == NULL))
2653
  return(-1);
2654
2655
    switch (obj->type) {
2656
        case XPATH_POINT:
2657
      *node = obj->user;
2658
      if (obj->index <= 0)
2659
    *indx = 0;
2660
      else
2661
    *indx = obj->index;
2662
      return(0);
2663
        case XPATH_RANGE:
2664
      *node = obj->user;
2665
      if (obj->index <= 0)
2666
    *indx = 0;
2667
      else
2668
    *indx = obj->index;
2669
      return(0);
2670
  default:
2671
      break;
2672
    }
2673
    return(-1);
2674
}
2675
2676
/**
2677
 * xmlXPtrStringRangeFunction:
2678
 * @ctxt:  the XPointer Parser context
2679
 * @nargs:  the number of args
2680
 *
2681
 * Function implementing the string-range() function
2682
 * range as described in 5.4.2
2683
 *
2684
 * ------------------------------
2685
 * [Definition: For each location in the location-set argument,
2686
 * string-range returns a set of string ranges, a set of substrings in a
2687
 * string. Specifically, the string-value of the location is searched for
2688
 * substrings that match the string argument, and the resulting location-set
2689
 * will contain a range location for each non-overlapping match.]
2690
 * An empty string is considered to match before each character of the
2691
 * string-value and after the final character. Whitespace in a string
2692
 * is matched literally, with no normalization except that provided by
2693
 * XML for line ends. The third argument gives the position of the first
2694
 * character to be in the resulting range, relative to the start of the
2695
 * match. The default value is 1, which makes the range start immediately
2696
 * before the first character of the matched string. The fourth argument
2697
 * gives the number of characters in the range; the default is that the
2698
 * range extends to the end of the matched string.
2699
 *
2700
 * Element boundaries, as well as entire embedded nodes such as processing
2701
 * instructions and comments, are ignored as defined in [XPath].
2702
 *
2703
 * If the string in the second argument is not found in the string-value
2704
 * of the location, or if a value in the third or fourth argument indicates
2705
 * a string that is beyond the beginning or end of the document, the
2706
 * expression fails.
2707
 *
2708
 * The points of the range-locations in the returned location-set will
2709
 * all be character points.
2710
 * ------------------------------
2711
 */
2712
static void
2713
xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2714
    int i, startindex, endindex = 0, fendindex;
2715
    xmlNodePtr start, end = 0, fend;
2716
    xmlXPathObjectPtr set = NULL;
2717
    xmlLocationSetPtr oldset;
2718
    xmlLocationSetPtr newset = NULL;
2719
    xmlXPathObjectPtr string = NULL;
2720
    xmlXPathObjectPtr position = NULL;
2721
    xmlXPathObjectPtr number = NULL;
2722
    int found, pos = 0, num = 0;
2723
2724
    /*
2725
     * Grab the arguments
2726
     */
2727
    if ((nargs < 2) || (nargs > 4))
2728
  XP_ERROR(XPATH_INVALID_ARITY);
2729
2730
    if (nargs >= 4) {
2731
        if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NUMBER)) {
2732
            xmlXPathErr(ctxt, XPATH_INVALID_TYPE);
2733
            goto error;
2734
        }
2735
  number = valuePop(ctxt);
2736
  if (number != NULL)
2737
      num = (int) number->floatval;
2738
    }
2739
    if (nargs >= 3) {
2740
        if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NUMBER)) {
2741
            xmlXPathErr(ctxt, XPATH_INVALID_TYPE);
2742
            goto error;
2743
        }
2744
  position = valuePop(ctxt);
2745
  if (position != NULL)
2746
      pos = (int) position->floatval;
2747
    }
2748
    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
2749
        xmlXPathErr(ctxt, XPATH_INVALID_TYPE);
2750
        goto error;
2751
    }
2752
    string = valuePop(ctxt);
2753
    if ((ctxt->value == NULL) ||
2754
  ((ctxt->value->type != XPATH_LOCATIONSET) &&
2755
   (ctxt->value->type != XPATH_NODESET))) {
2756
        xmlXPathErr(ctxt, XPATH_INVALID_TYPE);
2757
        goto error;
2758
    }
2759
    set = valuePop(ctxt);
2760
    newset = xmlXPtrLocationSetCreate(NULL);
2761
    if (newset == NULL) {
2762
        xmlXPathErr(ctxt, XPATH_MEMORY_ERROR);
2763
        goto error;
2764
    }
2765
    if (set->nodesetval == NULL) {
2766
        goto error;
2767
    }
2768
    if (set->type == XPATH_NODESET) {
2769
  xmlXPathObjectPtr tmp;
2770
2771
  /*
2772
   * First convert to a location set
2773
   */
2774
  tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
2775
  xmlXPathFreeObject(set);
2776
        set = NULL;
2777
  if (tmp == NULL) {
2778
            xmlXPathErr(ctxt, XPATH_MEMORY_ERROR);
2779
            goto error;
2780
        }
2781
  set = tmp;
2782
    }
2783
    oldset = (xmlLocationSetPtr) set->user;
2784
2785
    /*
2786
     * The loop is to search for each element in the location set
2787
     * the list of location set corresponding to that search
2788
     */
2789
    for (i = 0;i < oldset->locNr;i++) {
2790
#ifdef DEBUG_RANGES
2791
  xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0);
2792
#endif
2793
2794
  xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex);
2795
  xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex);
2796
  xmlXPtrAdvanceChar(&start, &startindex, 0);
2797
  xmlXPtrGetLastChar(&end, &endindex);
2798
2799
#ifdef DEBUG_RANGES
2800
  xmlGenericError(xmlGenericErrorContext,
2801
    "from index %d of ->", startindex);
2802
  xmlDebugDumpString(stdout, start->content);
2803
  xmlGenericError(xmlGenericErrorContext, "\n");
2804
  xmlGenericError(xmlGenericErrorContext,
2805
    "to index %d of ->", endindex);
2806
  xmlDebugDumpString(stdout, end->content);
2807
  xmlGenericError(xmlGenericErrorContext, "\n");
2808
#endif
2809
  do {
2810
            fend = end;
2811
            fendindex = endindex;
2812
      found = xmlXPtrSearchString(string->stringval, &start, &startindex,
2813
                            &fend, &fendindex);
2814
      if (found == 1) {
2815
    if (position == NULL) {
2816
        xmlXPtrLocationSetAdd(newset,
2817
       xmlXPtrNewRange(start, startindex, fend, fendindex));
2818
    } else if (xmlXPtrAdvanceChar(&start, &startindex,
2819
                            pos - 1) == 0) {
2820
        if ((number != NULL) && (num > 0)) {
2821
      int rindx;
2822
      xmlNodePtr rend;
2823
      rend = start;
2824
      rindx = startindex - 1;
2825
      if (xmlXPtrAdvanceChar(&rend, &rindx,
2826
                       num) == 0) {
2827
          xmlXPtrLocationSetAdd(newset,
2828
          xmlXPtrNewRange(start, startindex,
2829
              rend, rindx));
2830
      }
2831
        } else if ((number != NULL) && (num <= 0)) {
2832
      xmlXPtrLocationSetAdd(newset,
2833
            xmlXPtrNewRange(start, startindex,
2834
                start, startindex));
2835
        } else {
2836
      xmlXPtrLocationSetAdd(newset,
2837
            xmlXPtrNewRange(start, startindex,
2838
                fend, fendindex));
2839
        }
2840
    }
2841
    start = fend;
2842
    startindex = fendindex;
2843
    if (string->stringval[0] == 0)
2844
        startindex++;
2845
      }
2846
  } while (found == 1);
2847
    }
2848
2849
    /*
2850
     * Save the new value and cleanup
2851
     */
2852
error:
2853
    if (newset != NULL)
2854
        valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2855
    xmlXPathFreeObject(set);
2856
    xmlXPathFreeObject(string);
2857
    if (position) xmlXPathFreeObject(position);
2858
    if (number) xmlXPathFreeObject(number);
2859
}
2860
2861
/**
2862
 * xmlXPtrEvalRangePredicate:
2863
 * @ctxt:  the XPointer Parser context
2864
 *
2865
 *  [8]   Predicate ::=   '[' PredicateExpr ']'
2866
 *  [9]   PredicateExpr ::=   Expr
2867
 *
2868
 * Evaluate a predicate as in xmlXPathEvalPredicate() but for
2869
 * a Location Set instead of a node set
2870
 */
2871
void
2872
xmlXPtrEvalRangePredicate(xmlXPathParserContextPtr ctxt) {
2873
    const xmlChar *cur;
2874
    xmlXPathObjectPtr res;
2875
    xmlXPathObjectPtr obj, tmp;
2876
    xmlLocationSetPtr newset = NULL;
2877
    xmlLocationSetPtr oldset;
2878
    int i;
2879
2880
    if (ctxt == NULL) return;
2881
2882
    SKIP_BLANKS;
2883
    if (CUR != '[') {
2884
  XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
2885
    }
2886
    NEXT;
2887
    SKIP_BLANKS;
2888
2889
    /*
2890
     * Extract the old set, and then evaluate the result of the
2891
     * expression for all the element in the set. use it to grow
2892
     * up a new set.
2893
     */
2894
    CHECK_TYPE(XPATH_LOCATIONSET);
2895
    obj = valuePop(ctxt);
2896
    oldset = obj->user;
2897
    ctxt->context->node = NULL;
2898
2899
    if ((oldset == NULL) || (oldset->locNr == 0)) {
2900
  ctxt->context->contextSize = 0;
2901
  ctxt->context->proximityPosition = 0;
2902
  xmlXPathEvalExpr(ctxt);
2903
  res = valuePop(ctxt);
2904
  if (res != NULL)
2905
      xmlXPathFreeObject(res);
2906
  valuePush(ctxt, obj);
2907
  CHECK_ERROR;
2908
    } else {
2909
  /*
2910
   * Save the expression pointer since we will have to evaluate
2911
   * it multiple times. Initialize the new set.
2912
   */
2913
        cur = ctxt->cur;
2914
  newset = xmlXPtrLocationSetCreate(NULL);
2915
2916
        for (i = 0; i < oldset->locNr; i++) {
2917
      ctxt->cur = cur;
2918
2919
      /*
2920
       * Run the evaluation with a node list made of a single item
2921
       * in the nodeset.
2922
       */
2923
      ctxt->context->node = oldset->locTab[i]->user;
2924
      tmp = xmlXPathNewNodeSet(ctxt->context->node);
2925
      valuePush(ctxt, tmp);
2926
      ctxt->context->contextSize = oldset->locNr;
2927
      ctxt->context->proximityPosition = i + 1;
2928
2929
      xmlXPathEvalExpr(ctxt);
2930
      CHECK_ERROR;
2931
2932
      /*
2933
       * The result of the evaluation need to be tested to
2934
       * decided whether the filter succeeded or not
2935
       */
2936
      res = valuePop(ctxt);
2937
      if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
2938
          xmlXPtrLocationSetAdd(newset,
2939
      xmlXPathObjectCopy(oldset->locTab[i]));
2940
      }
2941
2942
      /*
2943
       * Cleanup
2944
       */
2945
      if (res != NULL)
2946
    xmlXPathFreeObject(res);
2947
      if (ctxt->value == tmp) {
2948
    res = valuePop(ctxt);
2949
    xmlXPathFreeObject(res);
2950
      }
2951
2952
      ctxt->context->node = NULL;
2953
  }
2954
2955
  /*
2956
   * The result is used as the new evaluation set.
2957
   */
2958
  xmlXPathFreeObject(obj);
2959
  ctxt->context->node = NULL;
2960
  ctxt->context->contextSize = -1;
2961
  ctxt->context->proximityPosition = -1;
2962
  valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
2963
    }
2964
    if (CUR != ']') {
2965
  XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
2966
    }
2967
2968
    NEXT;
2969
    SKIP_BLANKS;
2970
}
2971
#endif /* LIBXML_XPTR_LOCS_ENABLED */
2972
2973
#endif
2974