Coverage Report

Created: 2023-06-07 06:06

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