Coverage Report

Created: 2026-10-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/xpointer.c
Line
Count
Source
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
 * Author: Daniel Veillard
15
 */
16
17
/* To avoid EBCDIC trouble when parsing on zOS */
18
#if defined(__MVS__)
19
#pragma convert("ISO8859-1")
20
#endif
21
22
#define IN_LIBXML
23
#include "libxml.h"
24
25
/*
26
 * TODO: better handling of error cases, the full expression should
27
 *       be parsed beforehand instead of a progressive evaluation
28
 * TODO: Access into entities references are not supported now ...
29
 *       need a start to be able to pop out of entities refs since
30
 *       parent is the entity declaration, not the ref.
31
 */
32
33
#include <string.h>
34
#include <libxml/xpointer.h>
35
#include <libxml/xmlmemory.h>
36
#include <libxml/parserInternals.h>
37
#include <libxml/uri.h>
38
#include <libxml/xpath.h>
39
#include <libxml/xpathInternals.h>
40
#include <libxml/xmlerror.h>
41
42
#ifdef LIBXML_XPTR_ENABLED
43
44
/* Add support of the xmlns() xpointer scheme to initialize the namespaces */
45
#define XPTR_XMLNS_SCHEME
46
47
#include "private/error.h"
48
#include "private/xpath.h"
49
50
/************************************************************************
51
 *                  *
52
 *    Some factorized error routines        *
53
 *                  *
54
 ************************************************************************/
55
56
/**
57
 * Handle an XPointer error
58
 *
59
 * @param ctxt  an XPTR evaluation context
60
 * @param code  error code
61
 * @param msg  error message
62
 * @param extra  extra information
63
 */
64
static void LIBXML_ATTR_FORMAT(3,0)
65
xmlXPtrErr(xmlXPathParserContextPtr ctxt, int code,
66
           const char * msg, const xmlChar *extra)
67
2.25k
{
68
2.25k
    xmlStructuredErrorFunc serror = NULL;
69
2.25k
    void *data = NULL;
70
2.25k
    xmlNodePtr node = NULL;
71
2.25k
    int res;
72
73
2.25k
    if (ctxt == NULL)
74
0
        return;
75
    /* Only report the first error */
76
2.25k
    if (ctxt->error != 0)
77
22
        return;
78
79
2.23k
    ctxt->error = code;
80
81
2.23k
    if (ctxt->context != NULL) {
82
2.23k
        xmlErrorPtr err = &ctxt->context->lastError;
83
84
        /* cleanup current last error */
85
2.23k
        xmlResetError(err);
86
87
2.23k
        err->domain = XML_FROM_XPOINTER;
88
2.23k
        err->code = code;
89
2.23k
        err->level = XML_ERR_ERROR;
90
2.23k
        err->str1 = (char *) xmlStrdup(ctxt->base);
91
2.23k
        if (err->str1 == NULL) {
92
10
            xmlXPathPErrMemory(ctxt);
93
10
            return;
94
10
        }
95
2.22k
        err->int1 = ctxt->cur - ctxt->base;
96
2.22k
        err->node = ctxt->context->debugNode;
97
98
2.22k
        serror = ctxt->context->error;
99
2.22k
        data = ctxt->context->userData;
100
2.22k
        node = ctxt->context->debugNode;
101
2.22k
    }
102
103
2.22k
    res = xmlRaiseError(serror, NULL, data, NULL, node,
104
2.22k
                        XML_FROM_XPOINTER, code, XML_ERR_ERROR, NULL, 0,
105
2.22k
                        (const char *) extra, (const char *) ctxt->base,
106
2.22k
                        NULL, ctxt->cur - ctxt->base, 0,
107
2.22k
                        msg, extra);
108
2.22k
    if (res < 0)
109
10
        xmlXPathPErrMemory(ctxt);
110
2.22k
}
111
112
/************************************************************************
113
 *                  *
114
 *    A few helper functions for child sequences    *
115
 *                  *
116
 ************************************************************************/
117
118
/**
119
 * @param cur  the node
120
 * @param no  the child number
121
 * @returns the `no`'th element child of `cur` or NULL
122
 */
123
static xmlNodePtr
124
1.02k
xmlXPtrGetNthChild(xmlNodePtr cur, int no) {
125
1.02k
    int i;
126
1.02k
    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
127
0
  return(cur);
128
1.02k
    cur = cur->children;
129
5.33k
    for (i = 0;i <= no;cur = cur->next) {
130
5.33k
  if (cur == NULL)
131
447
      return(cur);
132
4.88k
  if ((cur->type == XML_ELEMENT_NODE) ||
133
2.22k
      (cur->type == XML_DOCUMENT_NODE) ||
134
2.66k
      (cur->type == XML_HTML_DOCUMENT_NODE)) {
135
2.66k
      i++;
136
2.66k
      if (i == no)
137
580
    break;
138
2.66k
  }
139
4.88k
    }
140
580
    return(cur);
141
1.02k
}
142
143
/************************************************************************
144
 *                  *
145
 *      The parser          *
146
 *                  *
147
 ************************************************************************/
148
149
static void xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name);
150
151
/*
152
 * Macros for accessing the content. Those should be used only by the parser,
153
 * and not exported.
154
 *
155
 * Dirty macros, i.e. one need to make assumption on the context to use them
156
 *
157
 *   CUR     returns the current xmlChar value, i.e. a 8 bit value
158
 *           in ISO-Latin or UTF-8.
159
 *           This should be used internally by the parser
160
 *           only to compare to ASCII values otherwise it would break when
161
 *           running with UTF-8 encoding.
162
 *   NXT(n)  returns the n'th next xmlChar. Same as CUR is should be used only
163
 *           to compare on ASCII based substring.
164
 *   SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
165
 *           strings within the parser.
166
 *   CURRENT Returns the current char value, with the full decoding of
167
 *           UTF-8 if we are using this mode. It returns an int.
168
 *   NEXT    Skip to the next character, this does the proper decoding
169
 *           in UTF-8 mode. It also pop-up unfinished entities on the fly.
170
 *           It returns the pointer to the current xmlChar.
171
 */
172
173
72.7M
#define CUR (*ctxt->cur)
174
#define SKIP(val) ctxt->cur += (val)
175
17.3k
#define NXT(val) ctxt->cur[(val)]
176
177
#define SKIP_BLANKS             \
178
86.9k
    while (IS_BLANK_CH(*(ctxt->cur))) NEXT
179
180
#define CURRENT (*ctxt->cur)
181
14.8M
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
182
183
/*
184
 * @param ctxt  the XPointer Parser context
185
 * @param index  the child number
186
 *
187
 * Move the current node of the nodeset on the stack to the
188
 * given child if found
189
 */
190
static void
191
4.98k
xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int indx) {
192
4.98k
    xmlNodePtr cur = NULL;
193
4.98k
    xmlXPathObjectPtr obj;
194
4.98k
    xmlNodeSetPtr oldset;
195
196
4.98k
    CHECK_TYPE(XPATH_NODESET);
197
4.12k
    obj = xmlXPathValuePop(ctxt);
198
4.12k
    oldset = obj->nodesetval;
199
4.12k
    if ((indx <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) {
200
3.09k
  xmlXPathFreeObject(obj);
201
3.09k
  xmlXPathValuePush(ctxt, xmlXPathNewNodeSet(NULL));
202
3.09k
  return;
203
3.09k
    }
204
1.02k
    cur = xmlXPtrGetNthChild(oldset->nodeTab[0], indx);
205
1.02k
    if (cur == NULL) {
206
447
  xmlXPathFreeObject(obj);
207
447
  xmlXPathValuePush(ctxt, xmlXPathNewNodeSet(NULL));
208
447
  return;
209
447
    }
210
580
    oldset->nodeTab[0] = cur;
211
580
    xmlXPathValuePush(ctxt, obj);
212
580
}
213
214
/**
215
 * XPtrPart ::= 'xpointer' '(' XPtrExpr ')'
216
 *            | Scheme '(' SchemeSpecificExpr ')'
217
 *
218
 * Scheme   ::=  NCName - 'xpointer' [VC: Non-XPointer schemes]
219
 *
220
 * SchemeSpecificExpr ::= StringWithBalancedParens
221
 *
222
 * StringWithBalancedParens ::=
223
 *              [^()]* ('(' StringWithBalancedParens ')' [^()]*)*
224
 *              [VC: Parenthesis escaping]
225
 *
226
 * XPtrExpr ::= Expr [VC: Parenthesis escaping]
227
 *
228
 * VC: Parenthesis escaping:
229
 *   The end of an XPointer part is signaled by the right parenthesis ")"
230
 *   character that is balanced with the left parenthesis "(" character
231
 *   that began the part. Any unbalanced parenthesis character inside the
232
 *   expression, even within literals, must be escaped with a circumflex (^)
233
 *   character preceding it. If the expression contains any literal
234
 *   occurrences of the circumflex, each must be escaped with an additional
235
 *   circumflex (that is, ^^). If the unescaped parentheses in the expression
236
 *   are not balanced, a syntax error results.
237
 *
238
 * Parse and evaluate an XPtrPart. Basically it generates the unescaped
239
 * string and if the scheme is 'xpointer' it will call the XPath interpreter.
240
 *
241
 * TODO: there is no new scheme registration mechanism
242
 *
243
 * @param ctxt  the XPointer Parser context
244
 * @param name  the preparsed Scheme for the XPtrPart
245
 */
246
247
static void
248
50.3k
xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) {
249
50.3k
    xmlChar *buffer, *cur;
250
50.3k
    int len;
251
50.3k
    int level;
252
253
50.3k
    if (name == NULL)
254
0
    name = xmlXPathParseName(ctxt);
255
50.3k
    if (name == NULL)
256
50.3k
  XP_ERROR(XPATH_EXPR_ERROR);
257
258
50.3k
    if (CUR != '(') {
259
2.84k
        xmlFree(name);
260
2.84k
  XP_ERROR(XPATH_EXPR_ERROR);
261
0
    }
262
47.4k
    NEXT;
263
47.4k
    level = 1;
264
265
47.4k
    len = xmlStrlen(ctxt->cur);
266
    /* Overflow in xmlStrlen */
267
47.4k
    if (len == 0 && ctxt->cur != NULL && *ctxt->cur != 0) {
268
0
        xmlXPathPErrMemory(ctxt);
269
0
        xmlFree(name);
270
0
        return;
271
0
    }
272
273
47.4k
    len++;
274
47.4k
    buffer = xmlMalloc(len);
275
47.4k
    if (buffer == NULL) {
276
41
        xmlXPathPErrMemory(ctxt);
277
41
        xmlFree(name);
278
41
  return;
279
41
    }
280
281
47.4k
    cur = buffer;
282
14.5M
    while (CUR != 0) {
283
14.5M
  if (CUR == ')') {
284
101k
      level--;
285
101k
      if (level == 0) {
286
46.7k
    NEXT;
287
46.7k
    break;
288
46.7k
      }
289
14.4M
  } else if (CUR == '(') {
290
57.5k
      level++;
291
14.4M
  } else if (CUR == '^') {
292
6.11k
            if ((NXT(1) == ')') || (NXT(1) == '(') || (NXT(1) == '^')) {
293
5.28k
                NEXT;
294
5.28k
            }
295
6.11k
  }
296
14.5M
        *cur++ = CUR;
297
14.5M
  NEXT;
298
14.5M
    }
299
47.4k
    *cur = 0;
300
301
47.4k
    if ((level != 0) && (CUR == 0)) {
302
728
        xmlFree(name);
303
728
  xmlFree(buffer);
304
728
  XP_ERROR(XPTR_SYNTAX_ERROR);
305
0
    }
306
307
46.7k
    if (xmlStrEqual(name, (xmlChar *) "xpointer") ||
308
42.5k
        xmlStrEqual(name, (xmlChar *) "xpath1")) {
309
42.5k
  const xmlChar *oldBase = ctxt->base;
310
42.5k
  const xmlChar *oldCur = ctxt->cur;
311
312
42.5k
  ctxt->cur = ctxt->base = buffer;
313
  /*
314
   * To evaluate an xpointer scheme element (4.3) we need:
315
   *   context initialized to the root
316
   *   context position initialized to 1
317
   *   context size initialized to 1
318
   */
319
42.5k
  ctxt->context->node = (xmlNodePtr)ctxt->context->doc;
320
42.5k
  ctxt->context->proximityPosition = 1;
321
42.5k
  ctxt->context->contextSize = 1;
322
42.5k
  xmlXPathEvalExpr(ctxt);
323
42.5k
  ctxt->base = oldBase;
324
42.5k
        ctxt->cur = oldCur;
325
42.5k
    } else if (xmlStrEqual(name, (xmlChar *) "element")) {
326
597
  const xmlChar *oldBase = ctxt->base;
327
597
  const xmlChar *oldCur = ctxt->cur;
328
597
  xmlChar *name2;
329
330
597
  ctxt->cur = ctxt->base = buffer;
331
597
  if (buffer[0] == '/') {
332
243
      xmlXPathRoot(ctxt);
333
243
      xmlXPtrEvalChildSeq(ctxt, NULL);
334
354
  } else {
335
354
      name2 = xmlXPathParseName(ctxt);
336
354
      if (name2 == NULL) {
337
40
                ctxt->base = oldBase;
338
40
                ctxt->cur = oldCur;
339
40
    xmlFree(buffer);
340
40
                xmlFree(name);
341
40
    XP_ERROR(XPATH_EXPR_ERROR);
342
0
      }
343
314
      xmlXPtrEvalChildSeq(ctxt, name2);
344
314
  }
345
557
  ctxt->base = oldBase;
346
557
        ctxt->cur = oldCur;
347
557
#ifdef XPTR_XMLNS_SCHEME
348
3.59k
    } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) {
349
1.90k
  const xmlChar *oldBase = ctxt->base;
350
1.90k
  const xmlChar *oldCur = ctxt->cur;
351
1.90k
  xmlChar *prefix;
352
353
1.90k
  ctxt->cur = ctxt->base = buffer;
354
1.90k
        prefix = xmlXPathParseNCName(ctxt);
355
1.90k
  if (prefix == NULL) {
356
28
            ctxt->base = oldBase;
357
28
            ctxt->cur = oldCur;
358
28
      xmlFree(buffer);
359
28
      xmlFree(name);
360
28
      XP_ERROR(XPTR_SYNTAX_ERROR);
361
0
  }
362
1.87k
  SKIP_BLANKS;
363
1.87k
  if (CUR != '=') {
364
93
            ctxt->base = oldBase;
365
93
            ctxt->cur = oldCur;
366
93
      xmlFree(prefix);
367
93
      xmlFree(buffer);
368
93
      xmlFree(name);
369
93
      XP_ERROR(XPTR_SYNTAX_ERROR);
370
0
  }
371
1.78k
  NEXT;
372
1.78k
  SKIP_BLANKS;
373
374
1.78k
  if (xmlXPathRegisterNs(ctxt->context, prefix, ctxt->cur) < 0)
375
18
            xmlXPathPErrMemory(ctxt);
376
1.78k
        ctxt->base = oldBase;
377
1.78k
        ctxt->cur = oldCur;
378
1.78k
  xmlFree(prefix);
379
1.78k
#endif /* XPTR_XMLNS_SCHEME */
380
1.78k
    } else {
381
1.69k
        xmlXPtrErr(ctxt, XML_XPTR_UNKNOWN_SCHEME,
382
1.69k
       "unsupported scheme '%s'\n", name);
383
1.69k
    }
384
46.5k
    xmlFree(buffer);
385
46.5k
    xmlFree(name);
386
46.5k
}
387
388
/**
389
 * FullXPtr ::= XPtrPart (S? XPtrPart)*
390
 *
391
 * As the specs says:
392
 * -----------
393
 * When multiple XPtrParts are provided, they must be evaluated in
394
 * left-to-right order. If evaluation of one part fails, the nexti
395
 * is evaluated. The following conditions cause XPointer part failure:
396
 *
397
 * - An unknown scheme
398
 * - A scheme that does not locate any sub-resource present in the resource
399
 * - A scheme that is not applicable to the media type of the resource
400
 *
401
 * The XPointer application must consume a failed XPointer part and
402
 * attempt to evaluate the next one, if any. The result of the first
403
 * XPointer part whose evaluation succeeds is taken to be the fragment
404
 * located by the XPointer as a whole. If all the parts fail, the result
405
 * for the XPointer as a whole is a sub-resource error.
406
 * -----------
407
 *
408
 * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based
409
 * expressions or other schemes.
410
 *
411
 * @param ctxt  the XPointer Parser context
412
 * @param name  the preparsed Scheme for the first XPtrPart
413
 */
414
static void
415
36.5k
xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) {
416
36.5k
    if (name == NULL)
417
0
    name = xmlXPathParseName(ctxt);
418
36.5k
    if (name == NULL)
419
36.5k
  XP_ERROR(XPATH_EXPR_ERROR);
420
62.1k
    while (name != NULL) {
421
50.3k
  ctxt->error = XPATH_EXPRESSION_OK;
422
50.3k
  xmlXPtrEvalXPtrPart(ctxt, name);
423
424
  /* in case of syntax error, break here */
425
50.3k
  if ((ctxt->error != XPATH_EXPRESSION_OK) &&
426
22.4k
            (ctxt->error != XML_XPTR_UNKNOWN_SCHEME))
427
20.7k
      return;
428
429
  /*
430
   * If the returned value is a non-empty nodeset
431
   * or location set, return here.
432
   */
433
29.5k
  if (ctxt->value != NULL) {
434
25.5k
      xmlXPathObjectPtr obj = ctxt->value;
435
436
25.5k
      switch (obj->type) {
437
12.4k
    case XPATH_NODESET: {
438
12.4k
        xmlNodeSetPtr loc = ctxt->value->nodesetval;
439
12.4k
        if ((loc != NULL) && (loc->nodeNr > 0))
440
3.91k
      return;
441
8.52k
        break;
442
12.4k
    }
443
13.1k
    default:
444
13.1k
        break;
445
25.5k
      }
446
447
      /*
448
       * Evaluating to improper values is equivalent to
449
       * a sub-resource error, clean-up the stack
450
       */
451
43.2k
      do {
452
43.2k
    obj = xmlXPathValuePop(ctxt);
453
43.2k
    if (obj != NULL) {
454
21.6k
        xmlXPathFreeObject(obj);
455
21.6k
    }
456
43.2k
      } while (obj != NULL);
457
21.6k
  }
458
459
  /*
460
   * Is there another XPointer part.
461
   */
462
25.6k
  SKIP_BLANKS;
463
25.6k
  name = xmlXPathParseName(ctxt);
464
25.6k
    }
465
36.5k
}
466
467
/**
468
 *  ChildSeq ::= '/1' ('/' [0-9]*)*
469
 *             | Name ('/' [0-9]*)+
470
 *
471
 * Parse and evaluate a Child Sequence. This routine also handle the
472
 * case of a Bare Name used to get a document ID.
473
 *
474
 * @param ctxt  the XPointer Parser context
475
 * @param name  a possible ID name of the child sequence
476
 */
477
static void
478
3.73k
xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) {
479
    /*
480
     * XPointer don't allow by syntax to address in multirooted trees
481
     * this might prove useful in some cases, warn about it.
482
     */
483
3.73k
    if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) {
484
563
        xmlXPtrErr(ctxt, XML_XPTR_CHILDSEQ_START,
485
563
       "warning: ChildSeq not starting by /1\n", NULL);
486
563
    }
487
488
3.73k
    if (name != NULL) {
489
2.69k
  xmlXPathValuePush(ctxt, xmlXPathNewString(name));
490
2.69k
  xmlFree(name);
491
2.69k
  xmlXPathIdFunction(ctxt, 1);
492
2.69k
  CHECK_ERROR;
493
2.69k
    }
494
495
8.65k
    while (CUR == '/') {
496
4.98k
  int child = 0, overflow = 0;
497
4.98k
  NEXT;
498
499
22.6k
  while ((CUR >= '0') && (CUR <= '9')) {
500
17.6k
            int d = CUR - '0';
501
17.6k
            if (child > INT_MAX / 10)
502
5.06k
                overflow = 1;
503
12.6k
            else
504
12.6k
                child *= 10;
505
17.6k
            if (child > INT_MAX - d)
506
1.42k
                overflow = 1;
507
16.2k
            else
508
16.2k
                child += d;
509
17.6k
      NEXT;
510
17.6k
  }
511
4.98k
        if (overflow)
512
770
            child = 0;
513
4.98k
  xmlXPtrGetChildNo(ctxt, child);
514
4.98k
    }
515
3.66k
}
516
517
518
/**
519
 *  XPointer ::= Name
520
 *             | ChildSeq
521
 *             | FullXPtr
522
 *
523
 * Parse and evaluate an XPointer
524
 *
525
 * @param ctxt  the XPointer Parser context
526
 */
527
static void
528
54.5k
xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) {
529
54.5k
    if (ctxt->valueTab == NULL) {
530
  /* Allocate the value stack */
531
54.5k
  ctxt->valueTab = (xmlXPathObjectPtr *)
532
54.5k
       xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
533
54.5k
  if (ctxt->valueTab == NULL) {
534
109
      xmlXPathPErrMemory(ctxt);
535
109
      return;
536
109
  }
537
54.4k
  ctxt->valueNr = 0;
538
54.4k
  ctxt->valueMax = 10;
539
54.4k
  ctxt->value = NULL;
540
54.4k
    }
541
54.4k
    SKIP_BLANKS;
542
54.4k
    if (CUR == '/') {
543
793
  xmlXPathRoot(ctxt);
544
793
        xmlXPtrEvalChildSeq(ctxt, NULL);
545
53.6k
    } else {
546
53.6k
  xmlChar *name;
547
548
53.6k
  name = xmlXPathParseName(ctxt);
549
53.6k
  if (name == NULL)
550
38.8k
      XP_ERROR(XPATH_EXPR_ERROR);
551
38.8k
  if (CUR == '(') {
552
36.5k
      xmlXPtrEvalFullXPtr(ctxt, name);
553
      /* Short evaluation */
554
36.5k
      return;
555
36.5k
  } else {
556
      /* this handle both Bare Names and Child Sequences */
557
2.38k
      xmlXPtrEvalChildSeq(ctxt, name);
558
2.38k
  }
559
38.8k
    }
560
3.17k
    SKIP_BLANKS;
561
3.17k
    if (CUR != 0)
562
1.59k
  XP_ERROR(XPATH_EXPR_ERROR);
563
1.59k
}
564
565
566
/************************************************************************
567
 *                  *
568
 *      General routines        *
569
 *                  *
570
 ************************************************************************/
571
572
/**
573
 * Create a new XPointer context
574
 *
575
 * @deprecated Same as xmlXPathNewContext.
576
 *
577
 * @param doc  the XML document
578
 * @param here  unused
579
 * @param origin  unused
580
 * @returns the xmlXPathContext just allocated.
581
 */
582
xmlXPathContext *
583
0
xmlXPtrNewContext(xmlDoc *doc, xmlNode *here, xmlNode *origin) {
584
0
    xmlXPathContextPtr ret;
585
0
    (void) here;
586
0
    (void) origin;
587
588
0
    ret = xmlXPathNewContext(doc);
589
0
    if (ret == NULL)
590
0
  return(ret);
591
592
0
    return(ret);
593
0
}
594
595
/**
596
 * Evaluate an XPointer expression.
597
 *
598
 * This function can only return nodesets. The caller has to
599
 * free the object.
600
 *
601
 * @param str  an XPointer expression
602
 * @param ctx  an XPath context
603
 * @returns the xmlXPathObject resulting from the evaluation or NULL
604
 * in case of error.
605
 */
606
xmlXPathObject *
607
55.0k
xmlXPtrEval(const xmlChar *str, xmlXPathContext *ctx) {
608
55.0k
    xmlXPathParserContextPtr ctxt;
609
55.0k
    xmlXPathObjectPtr res = NULL, tmp;
610
55.0k
    xmlXPathObjectPtr init = NULL;
611
55.0k
    int stack = 0;
612
613
55.0k
    xmlInitParser();
614
615
55.0k
    if ((ctx == NULL) || (str == NULL))
616
0
  return(NULL);
617
618
55.0k
    xmlResetError(&ctx->lastError);
619
620
55.0k
    ctxt = xmlXPathNewParserContext(str, ctx);
621
55.0k
    if (ctxt == NULL) {
622
469
        xmlXPathErrMemory(ctx);
623
469
  return(NULL);
624
469
    }
625
54.5k
    xmlXPtrEvalXPointer(ctxt);
626
54.5k
    if (ctx->lastError.code != XML_ERR_OK)
627
38.0k
        goto error;
628
629
16.5k
    if ((ctxt->value != NULL) &&
630
5.23k
  (ctxt->value->type != XPATH_NODESET)) {
631
0
        xmlXPtrErr(ctxt, XML_XPTR_EVAL_FAILED,
632
0
    "xmlXPtrEval: evaluation failed to return a node set\n",
633
0
       NULL);
634
16.5k
    } else {
635
16.5k
  res = xmlXPathValuePop(ctxt);
636
16.5k
    }
637
638
16.5k
    do {
639
16.5k
        tmp = xmlXPathValuePop(ctxt);
640
16.5k
  if (tmp != NULL) {
641
0
      if (tmp != init) {
642
0
    if (tmp->type == XPATH_NODESET) {
643
        /*
644
         * Evaluation may push a root nodeset which is unused
645
         */
646
0
        xmlNodeSetPtr set;
647
0
        set = tmp->nodesetval;
648
0
        if ((set == NULL) || (set->nodeNr != 1) ||
649
0
      (set->nodeTab[0] != (xmlNodePtr) ctx->doc))
650
0
      stack++;
651
0
    } else
652
0
        stack++;
653
0
      }
654
0
      xmlXPathFreeObject(tmp);
655
0
        }
656
16.5k
    } while (tmp != NULL);
657
16.5k
    if (stack != 0) {
658
0
        xmlXPtrErr(ctxt, XML_XPTR_EXTRA_OBJECTS,
659
0
       "xmlXPtrEval: object(s) left on the eval stack\n",
660
0
       NULL);
661
0
    }
662
16.5k
    if (ctx->lastError.code != XML_ERR_OK) {
663
0
  xmlXPathFreeObject(res);
664
0
  res = NULL;
665
0
    }
666
667
54.5k
error:
668
54.5k
    xmlXPathFreeParserContext(ctxt);
669
54.5k
    return(res);
670
16.5k
}
671
672
#endif
673