Coverage Report

Created: 2025-08-04 07:15

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