winealsa: Use mmdevapi's AudioClient's GetService.
[wine.git] / libs / xml2 / xpath.c
blob8df7974f1462fd92366e9aae82df9006771e0191
1 /*
2 * xpath.c: XML Path Language implementation
3 * XPath is a language for addressing parts of an XML document,
4 * designed to be used by both XSLT and XPointer
6 * Reference: W3C Recommendation 16 November 1999
7 * http://www.w3.org/TR/1999/REC-xpath-19991116
8 * Public reference:
9 * http://www.w3.org/TR/xpath
11 * See Copyright for the status of this software
13 * Author: daniel@veillard.com
17 /* To avoid EBCDIC trouble when parsing on zOS */
18 #if defined(__MVS__)
19 #pragma convert("ISO8859-1")
20 #endif
22 #define IN_LIBXML
23 #include "libxml.h"
25 #include <limits.h>
26 #include <string.h>
27 #include <stddef.h>
28 #include <math.h>
29 #include <float.h>
30 #include <ctype.h>
32 #include <libxml/xmlmemory.h>
33 #include <libxml/tree.h>
34 #include <libxml/valid.h>
35 #include <libxml/xpath.h>
36 #include <libxml/xpathInternals.h>
37 #include <libxml/parserInternals.h>
38 #include <libxml/hash.h>
39 #ifdef LIBXML_XPTR_LOCS_ENABLED
40 #include <libxml/xpointer.h>
41 #endif
42 #ifdef LIBXML_DEBUG_ENABLED
43 #include <libxml/debugXML.h>
44 #endif
45 #include <libxml/xmlerror.h>
46 #include <libxml/threads.h>
47 #include <libxml/globals.h>
48 #ifdef LIBXML_PATTERN_ENABLED
49 #include <libxml/pattern.h>
50 #endif
52 #include "private/buf.h"
53 #include "private/error.h"
54 #include "private/xpath.h"
56 #ifdef LIBXML_PATTERN_ENABLED
57 #define XPATH_STREAMING
58 #endif
60 #define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
65 /**
66 * WITH_TIM_SORT:
68 * Use the Timsort algorithm provided in timsort.h to sort
69 * nodeset as this is a great improvement over the old Shell sort
70 * used in xmlXPathNodeSetSort()
72 #define WITH_TIM_SORT
75 * XP_OPTIMIZED_NON_ELEM_COMPARISON:
76 * If defined, this will use xmlXPathCmpNodesExt() instead of
77 * xmlXPathCmpNodes(). The new function is optimized comparison of
78 * non-element nodes; actually it will speed up comparison only if
79 * xmlXPathOrderDocElems() was called in order to index the elements of
80 * a tree in document order; Libxslt does such an indexing, thus it will
81 * benefit from this optimization.
83 #define XP_OPTIMIZED_NON_ELEM_COMPARISON
86 * XP_OPTIMIZED_FILTER_FIRST:
87 * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
88 * in a way, that it stop evaluation at the first node.
90 #define XP_OPTIMIZED_FILTER_FIRST
93 * XP_DEBUG_OBJ_USAGE:
94 * Internal flag to enable tracking of how much XPath objects have been
95 * created.
97 /* #define XP_DEBUG_OBJ_USAGE */
100 * XPATH_MAX_STEPS:
101 * when compiling an XPath expression we arbitrary limit the maximum
102 * number of step operation in the compiled expression. 1000000 is
103 * an insanely large value which should never be reached under normal
104 * circumstances
106 #define XPATH_MAX_STEPS 1000000
109 * XPATH_MAX_STACK_DEPTH:
110 * when evaluating an XPath expression we arbitrary limit the maximum
111 * number of object allowed to be pushed on the stack. 1000000 is
112 * an insanely large value which should never be reached under normal
113 * circumstances
115 #define XPATH_MAX_STACK_DEPTH 1000000
118 * XPATH_MAX_NODESET_LENGTH:
119 * when evaluating an XPath expression nodesets are created and we
120 * arbitrary limit the maximum length of those node set. 10000000 is
121 * an insanely large value which should never be reached under normal
122 * circumstances, one would first need to construct an in memory tree
123 * with more than 10 millions nodes.
125 #define XPATH_MAX_NODESET_LENGTH 10000000
128 * XPATH_MAX_RECRUSION_DEPTH:
129 * Maximum amount of nested functions calls when parsing or evaluating
130 * expressions
132 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
133 #define XPATH_MAX_RECURSION_DEPTH 500
134 #elif defined(_WIN32)
135 /* Windows typically limits stack size to 1MB. */
136 #define XPATH_MAX_RECURSION_DEPTH 1000
137 #else
138 #define XPATH_MAX_RECURSION_DEPTH 5000
139 #endif
142 * TODO:
143 * There are a few spots where some tests are done which depend upon ascii
144 * data. These should be enhanced for full UTF8 support (see particularly
145 * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
148 #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
150 /************************************************************************
152 * Floating point stuff *
154 ************************************************************************/
156 double xmlXPathNAN = 0.0;
157 double xmlXPathPINF = 0.0;
158 double xmlXPathNINF = 0.0;
161 * xmlXPathInit:
163 * DEPRECATED: Alias for xmlInitParser.
165 void
166 xmlXPathInit(void) {
167 xmlInitParser();
171 * xmlInitXPathInternal:
173 * Initialize the XPath environment
175 ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
176 void
177 xmlInitXPathInternal(void) {
178 #if defined(NAN) && defined(INFINITY)
179 xmlXPathNAN = NAN;
180 xmlXPathPINF = INFINITY;
181 xmlXPathNINF = -INFINITY;
182 #else
183 /* MSVC doesn't allow division by zero in constant expressions. */
184 double zero = 0.0;
185 xmlXPathNAN = 0.0 / zero;
186 xmlXPathPINF = 1.0 / zero;
187 xmlXPathNINF = -xmlXPathPINF;
188 #endif
192 * xmlXPathIsNaN:
193 * @val: a double value
195 * Returns 1 if the value is a NaN, 0 otherwise
198 xmlXPathIsNaN(double val) {
199 #ifdef isnan
200 return isnan(val);
201 #else
202 return !(val == val);
203 #endif
207 * xmlXPathIsInf:
208 * @val: a double value
210 * Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise
213 xmlXPathIsInf(double val) {
214 #ifdef isinf
215 return isinf(val) ? (val > 0 ? 1 : -1) : 0;
216 #else
217 if (val >= xmlXPathPINF)
218 return 1;
219 if (val <= -xmlXPathPINF)
220 return -1;
221 return 0;
222 #endif
225 #endif /* SCHEMAS or XPATH */
227 #ifdef LIBXML_XPATH_ENABLED
230 * TODO: when compatibility allows remove all "fake node libxslt" strings
231 * the test should just be name[0] = ' '
233 #ifdef DEBUG_XPATH_EXPRESSION
234 #define DEBUG_STEP
235 #define DEBUG_EXPR
236 #define DEBUG_EVAL_COUNTS
237 #endif
239 static xmlNs xmlXPathXMLNamespaceStruct = {
240 NULL,
241 XML_NAMESPACE_DECL,
242 XML_XML_NAMESPACE,
243 BAD_CAST "xml",
244 NULL,
245 NULL
247 static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
248 #ifndef LIBXML_THREAD_ENABLED
250 * Optimizer is disabled only when threaded apps are detected while
251 * the library ain't compiled for thread safety.
253 static int xmlXPathDisableOptimizer = 0;
254 #endif
256 static void
257 xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes);
259 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
261 * xmlXPathCmpNodesExt:
262 * @node1: the first node
263 * @node2: the second node
265 * Compare two nodes w.r.t document order.
266 * This one is optimized for handling of non-element nodes.
268 * Returns -2 in case of error 1 if first point < second point, 0 if
269 * it's the same node, -1 otherwise
271 static int
272 xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
273 int depth1, depth2;
274 int misc = 0, precedence1 = 0, precedence2 = 0;
275 xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
276 xmlNodePtr cur, root;
277 ptrdiff_t l1, l2;
279 if ((node1 == NULL) || (node2 == NULL))
280 return(-2);
282 if (node1 == node2)
283 return(0);
286 * a couple of optimizations which will avoid computations in most cases
288 switch (node1->type) {
289 case XML_ELEMENT_NODE:
290 if (node2->type == XML_ELEMENT_NODE) {
291 if ((0 > (ptrdiff_t) node1->content) &&
292 (0 > (ptrdiff_t) node2->content) &&
293 (node1->doc == node2->doc))
295 l1 = -((ptrdiff_t) node1->content);
296 l2 = -((ptrdiff_t) node2->content);
297 if (l1 < l2)
298 return(1);
299 if (l1 > l2)
300 return(-1);
301 } else
302 goto turtle_comparison;
304 break;
305 case XML_ATTRIBUTE_NODE:
306 precedence1 = 1; /* element is owner */
307 miscNode1 = node1;
308 node1 = node1->parent;
309 misc = 1;
310 break;
311 case XML_TEXT_NODE:
312 case XML_CDATA_SECTION_NODE:
313 case XML_COMMENT_NODE:
314 case XML_PI_NODE: {
315 miscNode1 = node1;
317 * Find nearest element node.
319 if (node1->prev != NULL) {
320 do {
321 node1 = node1->prev;
322 if (node1->type == XML_ELEMENT_NODE) {
323 precedence1 = 3; /* element in prev-sibl axis */
324 break;
326 if (node1->prev == NULL) {
327 precedence1 = 2; /* element is parent */
329 * URGENT TODO: Are there any cases, where the
330 * parent of such a node is not an element node?
332 node1 = node1->parent;
333 break;
335 } while (1);
336 } else {
337 precedence1 = 2; /* element is parent */
338 node1 = node1->parent;
340 if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
341 (0 <= (ptrdiff_t) node1->content)) {
343 * Fallback for whatever case.
345 node1 = miscNode1;
346 precedence1 = 0;
347 } else
348 misc = 1;
350 break;
351 case XML_NAMESPACE_DECL:
353 * TODO: why do we return 1 for namespace nodes?
355 return(1);
356 default:
357 break;
359 switch (node2->type) {
360 case XML_ELEMENT_NODE:
361 break;
362 case XML_ATTRIBUTE_NODE:
363 precedence2 = 1; /* element is owner */
364 miscNode2 = node2;
365 node2 = node2->parent;
366 misc = 1;
367 break;
368 case XML_TEXT_NODE:
369 case XML_CDATA_SECTION_NODE:
370 case XML_COMMENT_NODE:
371 case XML_PI_NODE: {
372 miscNode2 = node2;
373 if (node2->prev != NULL) {
374 do {
375 node2 = node2->prev;
376 if (node2->type == XML_ELEMENT_NODE) {
377 precedence2 = 3; /* element in prev-sibl axis */
378 break;
380 if (node2->prev == NULL) {
381 precedence2 = 2; /* element is parent */
382 node2 = node2->parent;
383 break;
385 } while (1);
386 } else {
387 precedence2 = 2; /* element is parent */
388 node2 = node2->parent;
390 if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
391 (0 <= (ptrdiff_t) node2->content))
393 node2 = miscNode2;
394 precedence2 = 0;
395 } else
396 misc = 1;
398 break;
399 case XML_NAMESPACE_DECL:
400 return(1);
401 default:
402 break;
404 if (misc) {
405 if (node1 == node2) {
406 if (precedence1 == precedence2) {
408 * The ugly case; but normally there aren't many
409 * adjacent non-element nodes around.
411 cur = miscNode2->prev;
412 while (cur != NULL) {
413 if (cur == miscNode1)
414 return(1);
415 if (cur->type == XML_ELEMENT_NODE)
416 return(-1);
417 cur = cur->prev;
419 return (-1);
420 } else {
422 * Evaluate based on higher precedence wrt to the element.
423 * TODO: This assumes attributes are sorted before content.
424 * Is this 100% correct?
426 if (precedence1 < precedence2)
427 return(1);
428 else
429 return(-1);
433 * Special case: One of the helper-elements is contained by the other.
434 * <foo>
435 * <node2>
436 * <node1>Text-1(precedence1 == 2)</node1>
437 * </node2>
438 * Text-6(precedence2 == 3)
439 * </foo>
441 if ((precedence2 == 3) && (precedence1 > 1)) {
442 cur = node1->parent;
443 while (cur) {
444 if (cur == node2)
445 return(1);
446 cur = cur->parent;
449 if ((precedence1 == 3) && (precedence2 > 1)) {
450 cur = node2->parent;
451 while (cur) {
452 if (cur == node1)
453 return(-1);
454 cur = cur->parent;
460 * Speedup using document order if available.
462 if ((node1->type == XML_ELEMENT_NODE) &&
463 (node2->type == XML_ELEMENT_NODE) &&
464 (0 > (ptrdiff_t) node1->content) &&
465 (0 > (ptrdiff_t) node2->content) &&
466 (node1->doc == node2->doc)) {
468 l1 = -((ptrdiff_t) node1->content);
469 l2 = -((ptrdiff_t) node2->content);
470 if (l1 < l2)
471 return(1);
472 if (l1 > l2)
473 return(-1);
476 turtle_comparison:
478 if (node1 == node2->prev)
479 return(1);
480 if (node1 == node2->next)
481 return(-1);
483 * compute depth to root
485 for (depth2 = 0, cur = node2; cur->parent != NULL; cur = cur->parent) {
486 if (cur->parent == node1)
487 return(1);
488 depth2++;
490 root = cur;
491 for (depth1 = 0, cur = node1; cur->parent != NULL; cur = cur->parent) {
492 if (cur->parent == node2)
493 return(-1);
494 depth1++;
497 * Distinct document (or distinct entities :-( ) case.
499 if (root != cur) {
500 return(-2);
503 * get the nearest common ancestor.
505 while (depth1 > depth2) {
506 depth1--;
507 node1 = node1->parent;
509 while (depth2 > depth1) {
510 depth2--;
511 node2 = node2->parent;
513 while (node1->parent != node2->parent) {
514 node1 = node1->parent;
515 node2 = node2->parent;
516 /* should not happen but just in case ... */
517 if ((node1 == NULL) || (node2 == NULL))
518 return(-2);
521 * Find who's first.
523 if (node1 == node2->prev)
524 return(1);
525 if (node1 == node2->next)
526 return(-1);
528 * Speedup using document order if available.
530 if ((node1->type == XML_ELEMENT_NODE) &&
531 (node2->type == XML_ELEMENT_NODE) &&
532 (0 > (ptrdiff_t) node1->content) &&
533 (0 > (ptrdiff_t) node2->content) &&
534 (node1->doc == node2->doc)) {
536 l1 = -((ptrdiff_t) node1->content);
537 l2 = -((ptrdiff_t) node2->content);
538 if (l1 < l2)
539 return(1);
540 if (l1 > l2)
541 return(-1);
544 for (cur = node1->next;cur != NULL;cur = cur->next)
545 if (cur == node2)
546 return(1);
547 return(-1); /* assume there is no sibling list corruption */
549 #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
552 * Wrapper for the Timsort algorithm from timsort.h
554 #ifdef WITH_TIM_SORT
555 #define SORT_NAME libxml_domnode
556 #define SORT_TYPE xmlNodePtr
558 * wrap_cmp:
559 * @x: a node
560 * @y: another node
562 * Comparison function for the Timsort implementation
564 * Returns -2 in case of error -1 if first point < second point, 0 if
565 * it's the same node, +1 otherwise
567 static
568 int wrap_cmp( xmlNodePtr x, xmlNodePtr y );
569 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
570 static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
572 int res = xmlXPathCmpNodesExt(x, y);
573 return res == -2 ? res : -res;
575 #else
576 static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
578 int res = xmlXPathCmpNodes(x, y);
579 return res == -2 ? res : -res;
581 #endif
582 #define SORT_CMP(x, y) (wrap_cmp(x, y))
583 #include "timsort.h"
584 #endif /* WITH_TIM_SORT */
586 /************************************************************************
588 * Error handling routines *
590 ************************************************************************/
593 * XP_ERRORNULL:
594 * @X: the error code
596 * Macro to raise an XPath error and return NULL.
598 #define XP_ERRORNULL(X) \
599 { xmlXPathErr(ctxt, X); return(NULL); }
602 * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
604 static const char* const xmlXPathErrorMessages[] = {
605 "Ok\n",
606 "Number encoding\n",
607 "Unfinished literal\n",
608 "Start of literal\n",
609 "Expected $ for variable reference\n",
610 "Undefined variable\n",
611 "Invalid predicate\n",
612 "Invalid expression\n",
613 "Missing closing curly brace\n",
614 "Unregistered function\n",
615 "Invalid operand\n",
616 "Invalid type\n",
617 "Invalid number of arguments\n",
618 "Invalid context size\n",
619 "Invalid context position\n",
620 "Memory allocation error\n",
621 "Syntax error\n",
622 "Resource error\n",
623 "Sub resource error\n",
624 "Undefined namespace prefix\n",
625 "Encoding error\n",
626 "Char out of XML range\n",
627 "Invalid or incomplete context\n",
628 "Stack usage error\n",
629 "Forbidden variable\n",
630 "Operation limit exceeded\n",
631 "Recursion limit exceeded\n",
632 "?? Unknown error ??\n" /* Must be last in the list! */
634 #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
635 sizeof(xmlXPathErrorMessages[0])) - 1)
637 * xmlXPathErrMemory:
638 * @ctxt: an XPath context
639 * @extra: extra information
641 * Handle a redefinition of attribute error
643 static void
644 xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
646 if (ctxt != NULL) {
647 xmlResetError(&ctxt->lastError);
648 if (extra) {
649 xmlChar buf[200];
651 xmlStrPrintf(buf, 200,
652 "Memory allocation failed : %s\n",
653 extra);
654 ctxt->lastError.message = (char *) xmlStrdup(buf);
655 } else {
656 ctxt->lastError.message = (char *)
657 xmlStrdup(BAD_CAST "Memory allocation failed\n");
659 ctxt->lastError.domain = XML_FROM_XPATH;
660 ctxt->lastError.code = XML_ERR_NO_MEMORY;
661 if (ctxt->error != NULL)
662 ctxt->error(ctxt->userData, &ctxt->lastError);
663 } else {
664 if (extra)
665 __xmlRaiseError(NULL, NULL, NULL,
666 NULL, NULL, XML_FROM_XPATH,
667 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
668 extra, NULL, NULL, 0, 0,
669 "Memory allocation failed : %s\n", extra);
670 else
671 __xmlRaiseError(NULL, NULL, NULL,
672 NULL, NULL, XML_FROM_XPATH,
673 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
674 NULL, NULL, NULL, 0, 0,
675 "Memory allocation failed\n");
680 * xmlXPathPErrMemory:
681 * @ctxt: an XPath parser context
682 * @extra: extra information
684 * Handle a redefinition of attribute error
686 static void
687 xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
689 if (ctxt == NULL)
690 xmlXPathErrMemory(NULL, extra);
691 else {
692 ctxt->error = XPATH_MEMORY_ERROR;
693 xmlXPathErrMemory(ctxt->context, extra);
698 * xmlXPathErr:
699 * @ctxt: a XPath parser context
700 * @error: the error code
702 * Handle an XPath error
704 void
705 xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
707 if ((error < 0) || (error > MAXERRNO))
708 error = MAXERRNO;
709 if (ctxt == NULL) {
710 __xmlRaiseError(NULL, NULL, NULL,
711 NULL, NULL, XML_FROM_XPATH,
712 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
713 XML_ERR_ERROR, NULL, 0,
714 NULL, NULL, NULL, 0, 0,
715 "%s", xmlXPathErrorMessages[error]);
716 return;
718 /* Only report the first error */
719 if (ctxt->error != 0)
720 return;
721 ctxt->error = error;
722 if (ctxt->context == NULL) {
723 __xmlRaiseError(NULL, NULL, NULL,
724 NULL, NULL, XML_FROM_XPATH,
725 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
726 XML_ERR_ERROR, NULL, 0,
727 (const char *) ctxt->base, NULL, NULL,
728 ctxt->cur - ctxt->base, 0,
729 "%s", xmlXPathErrorMessages[error]);
730 return;
733 /* cleanup current last error */
734 xmlResetError(&ctxt->context->lastError);
736 ctxt->context->lastError.domain = XML_FROM_XPATH;
737 ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
738 XPATH_EXPRESSION_OK;
739 ctxt->context->lastError.level = XML_ERR_ERROR;
740 ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
741 ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
742 ctxt->context->lastError.node = ctxt->context->debugNode;
743 if (ctxt->context->error != NULL) {
744 ctxt->context->error(ctxt->context->userData,
745 &ctxt->context->lastError);
746 } else {
747 __xmlRaiseError(NULL, NULL, NULL,
748 NULL, ctxt->context->debugNode, XML_FROM_XPATH,
749 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
750 XML_ERR_ERROR, NULL, 0,
751 (const char *) ctxt->base, NULL, NULL,
752 ctxt->cur - ctxt->base, 0,
753 "%s", xmlXPathErrorMessages[error]);
759 * xmlXPatherror:
760 * @ctxt: the XPath Parser context
761 * @file: the file name
762 * @line: the line number
763 * @no: the error number
765 * Formats an error message.
767 void
768 xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
769 int line ATTRIBUTE_UNUSED, int no) {
770 xmlXPathErr(ctxt, no);
774 * xmlXPathCheckOpLimit:
775 * @ctxt: the XPath Parser context
776 * @opCount: the number of operations to be added
778 * Adds opCount to the running total of operations and returns -1 if the
779 * operation limit is exceeded. Returns 0 otherwise.
781 static int
782 xmlXPathCheckOpLimit(xmlXPathParserContextPtr ctxt, unsigned long opCount) {
783 xmlXPathContextPtr xpctxt = ctxt->context;
785 if ((opCount > xpctxt->opLimit) ||
786 (xpctxt->opCount > xpctxt->opLimit - opCount)) {
787 xpctxt->opCount = xpctxt->opLimit;
788 xmlXPathErr(ctxt, XPATH_OP_LIMIT_EXCEEDED);
789 return(-1);
792 xpctxt->opCount += opCount;
793 return(0);
796 #define OP_LIMIT_EXCEEDED(ctxt, n) \
797 ((ctxt->context->opLimit != 0) && (xmlXPathCheckOpLimit(ctxt, n) < 0))
799 /************************************************************************
801 * Utilities *
803 ************************************************************************/
806 * xsltPointerList:
808 * Pointer-list for various purposes.
810 typedef struct _xmlPointerList xmlPointerList;
811 typedef xmlPointerList *xmlPointerListPtr;
812 struct _xmlPointerList {
813 void **items;
814 int number;
815 int size;
818 * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
819 * and here, we should make the functions public.
821 static int
822 xmlPointerListAddSize(xmlPointerListPtr list,
823 void *item,
824 int initialSize)
826 if (list->size <= list->number) {
827 void **tmp;
828 size_t newSize;
830 if (list->size == 0) {
831 if (initialSize <= 0)
832 initialSize = 1;
833 newSize = initialSize;
834 } else {
835 if (list->size > 50000000) {
836 xmlXPathErrMemory(NULL,
837 "xmlPointerListAddSize: re-allocating item\n");
838 return(-1);
840 newSize = list->size * 2;
842 tmp = (void **) xmlRealloc(list->items, newSize * sizeof(void *));
843 if (tmp == NULL) {
844 xmlXPathErrMemory(NULL,
845 "xmlPointerListAddSize: re-allocating item\n");
846 return(-1);
848 list->items = tmp;
849 list->size = newSize;
851 list->items[list->number++] = item;
852 return(0);
856 * xsltPointerListCreate:
858 * Creates an xsltPointerList structure.
860 * Returns a xsltPointerList structure or NULL in case of an error.
862 static xmlPointerListPtr
863 xmlPointerListCreate(int initialSize)
865 xmlPointerListPtr ret;
867 ret = xmlMalloc(sizeof(xmlPointerList));
868 if (ret == NULL) {
869 xmlXPathErrMemory(NULL,
870 "xmlPointerListCreate: allocating item\n");
871 return (NULL);
873 memset(ret, 0, sizeof(xmlPointerList));
874 if (initialSize > 0) {
875 xmlPointerListAddSize(ret, NULL, initialSize);
876 ret->number = 0;
878 return (ret);
882 * xsltPointerListFree:
884 * Frees the xsltPointerList structure. This does not free
885 * the content of the list.
887 static void
888 xmlPointerListFree(xmlPointerListPtr list)
890 if (list == NULL)
891 return;
892 if (list->items != NULL)
893 xmlFree(list->items);
894 xmlFree(list);
897 /************************************************************************
899 * Parser Types *
901 ************************************************************************/
904 * Types are private:
907 typedef enum {
908 XPATH_OP_END=0,
909 XPATH_OP_AND,
910 XPATH_OP_OR,
911 XPATH_OP_EQUAL,
912 XPATH_OP_CMP,
913 XPATH_OP_PLUS,
914 XPATH_OP_MULT,
915 XPATH_OP_UNION,
916 XPATH_OP_ROOT,
917 XPATH_OP_NODE,
918 XPATH_OP_COLLECT,
919 XPATH_OP_VALUE, /* 11 */
920 XPATH_OP_VARIABLE,
921 XPATH_OP_FUNCTION,
922 XPATH_OP_ARG,
923 XPATH_OP_PREDICATE,
924 XPATH_OP_FILTER, /* 16 */
925 XPATH_OP_SORT /* 17 */
926 #ifdef LIBXML_XPTR_LOCS_ENABLED
927 ,XPATH_OP_RANGETO
928 #endif
929 } xmlXPathOp;
931 typedef enum {
932 AXIS_ANCESTOR = 1,
933 AXIS_ANCESTOR_OR_SELF,
934 AXIS_ATTRIBUTE,
935 AXIS_CHILD,
936 AXIS_DESCENDANT,
937 AXIS_DESCENDANT_OR_SELF,
938 AXIS_FOLLOWING,
939 AXIS_FOLLOWING_SIBLING,
940 AXIS_NAMESPACE,
941 AXIS_PARENT,
942 AXIS_PRECEDING,
943 AXIS_PRECEDING_SIBLING,
944 AXIS_SELF
945 } xmlXPathAxisVal;
947 typedef enum {
948 NODE_TEST_NONE = 0,
949 NODE_TEST_TYPE = 1,
950 NODE_TEST_PI = 2,
951 NODE_TEST_ALL = 3,
952 NODE_TEST_NS = 4,
953 NODE_TEST_NAME = 5
954 } xmlXPathTestVal;
956 typedef enum {
957 NODE_TYPE_NODE = 0,
958 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
959 NODE_TYPE_TEXT = XML_TEXT_NODE,
960 NODE_TYPE_PI = XML_PI_NODE
961 } xmlXPathTypeVal;
963 typedef struct _xmlXPathStepOp xmlXPathStepOp;
964 typedef xmlXPathStepOp *xmlXPathStepOpPtr;
965 struct _xmlXPathStepOp {
966 xmlXPathOp op; /* The identifier of the operation */
967 int ch1; /* First child */
968 int ch2; /* Second child */
969 int value;
970 int value2;
971 int value3;
972 void *value4;
973 void *value5;
974 xmlXPathFunction cache;
975 void *cacheURI;
978 struct _xmlXPathCompExpr {
979 int nbStep; /* Number of steps in this expression */
980 int maxStep; /* Maximum number of steps allocated */
981 xmlXPathStepOp *steps; /* ops for computation of this expression */
982 int last; /* index of last step in expression */
983 xmlChar *expr; /* the expression being computed */
984 xmlDictPtr dict; /* the dictionary to use if any */
985 #ifdef DEBUG_EVAL_COUNTS
986 int nb;
987 xmlChar *string;
988 #endif
989 #ifdef XPATH_STREAMING
990 xmlPatternPtr stream;
991 #endif
994 /************************************************************************
996 * Forward declarations *
998 ************************************************************************/
999 static void
1000 xmlXPathFreeValueTree(xmlNodeSetPtr obj);
1001 static void
1002 xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
1003 static int
1004 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
1005 xmlXPathStepOpPtr op, xmlNodePtr *first);
1006 static int
1007 xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
1008 xmlXPathStepOpPtr op,
1009 int isPredicate);
1010 static void
1011 xmlXPathFreeObjectEntry(void *obj, const xmlChar *name);
1013 /************************************************************************
1015 * Parser Type functions *
1017 ************************************************************************/
1020 * xmlXPathNewCompExpr:
1022 * Create a new Xpath component
1024 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
1026 static xmlXPathCompExprPtr
1027 xmlXPathNewCompExpr(void) {
1028 xmlXPathCompExprPtr cur;
1030 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
1031 if (cur == NULL) {
1032 xmlXPathErrMemory(NULL, "allocating component\n");
1033 return(NULL);
1035 memset(cur, 0, sizeof(xmlXPathCompExpr));
1036 cur->maxStep = 10;
1037 cur->nbStep = 0;
1038 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
1039 sizeof(xmlXPathStepOp));
1040 if (cur->steps == NULL) {
1041 xmlXPathErrMemory(NULL, "allocating steps\n");
1042 xmlFree(cur);
1043 return(NULL);
1045 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
1046 cur->last = -1;
1047 #ifdef DEBUG_EVAL_COUNTS
1048 cur->nb = 0;
1049 #endif
1050 return(cur);
1054 * xmlXPathFreeCompExpr:
1055 * @comp: an XPATH comp
1057 * Free up the memory allocated by @comp
1059 void
1060 xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
1062 xmlXPathStepOpPtr op;
1063 int i;
1065 if (comp == NULL)
1066 return;
1067 if (comp->dict == NULL) {
1068 for (i = 0; i < comp->nbStep; i++) {
1069 op = &comp->steps[i];
1070 if (op->value4 != NULL) {
1071 if (op->op == XPATH_OP_VALUE)
1072 xmlXPathFreeObject(op->value4);
1073 else
1074 xmlFree(op->value4);
1076 if (op->value5 != NULL)
1077 xmlFree(op->value5);
1079 } else {
1080 for (i = 0; i < comp->nbStep; i++) {
1081 op = &comp->steps[i];
1082 if (op->value4 != NULL) {
1083 if (op->op == XPATH_OP_VALUE)
1084 xmlXPathFreeObject(op->value4);
1087 xmlDictFree(comp->dict);
1089 if (comp->steps != NULL) {
1090 xmlFree(comp->steps);
1092 #ifdef DEBUG_EVAL_COUNTS
1093 if (comp->string != NULL) {
1094 xmlFree(comp->string);
1096 #endif
1097 #ifdef XPATH_STREAMING
1098 if (comp->stream != NULL) {
1099 xmlFreePatternList(comp->stream);
1101 #endif
1102 if (comp->expr != NULL) {
1103 xmlFree(comp->expr);
1106 xmlFree(comp);
1110 * xmlXPathCompExprAdd:
1111 * @comp: the compiled expression
1112 * @ch1: first child index
1113 * @ch2: second child index
1114 * @op: an op
1115 * @value: the first int value
1116 * @value2: the second int value
1117 * @value3: the third int value
1118 * @value4: the first string value
1119 * @value5: the second string value
1121 * Add a step to an XPath Compiled Expression
1123 * Returns -1 in case of failure, the index otherwise
1125 static int
1126 xmlXPathCompExprAdd(xmlXPathParserContextPtr ctxt, int ch1, int ch2,
1127 xmlXPathOp op, int value,
1128 int value2, int value3, void *value4, void *value5) {
1129 xmlXPathCompExprPtr comp = ctxt->comp;
1130 if (comp->nbStep >= comp->maxStep) {
1131 xmlXPathStepOp *real;
1133 if (comp->maxStep >= XPATH_MAX_STEPS) {
1134 xmlXPathPErrMemory(ctxt, "adding step\n");
1135 return(-1);
1137 comp->maxStep *= 2;
1138 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
1139 comp->maxStep * sizeof(xmlXPathStepOp));
1140 if (real == NULL) {
1141 comp->maxStep /= 2;
1142 xmlXPathPErrMemory(ctxt, "adding step\n");
1143 return(-1);
1145 comp->steps = real;
1147 comp->last = comp->nbStep;
1148 comp->steps[comp->nbStep].ch1 = ch1;
1149 comp->steps[comp->nbStep].ch2 = ch2;
1150 comp->steps[comp->nbStep].op = op;
1151 comp->steps[comp->nbStep].value = value;
1152 comp->steps[comp->nbStep].value2 = value2;
1153 comp->steps[comp->nbStep].value3 = value3;
1154 if ((comp->dict != NULL) &&
1155 ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
1156 (op == XPATH_OP_COLLECT))) {
1157 if (value4 != NULL) {
1158 comp->steps[comp->nbStep].value4 = (xmlChar *)
1159 (void *)xmlDictLookup(comp->dict, value4, -1);
1160 xmlFree(value4);
1161 } else
1162 comp->steps[comp->nbStep].value4 = NULL;
1163 if (value5 != NULL) {
1164 comp->steps[comp->nbStep].value5 = (xmlChar *)
1165 (void *)xmlDictLookup(comp->dict, value5, -1);
1166 xmlFree(value5);
1167 } else
1168 comp->steps[comp->nbStep].value5 = NULL;
1169 } else {
1170 comp->steps[comp->nbStep].value4 = value4;
1171 comp->steps[comp->nbStep].value5 = value5;
1173 comp->steps[comp->nbStep].cache = NULL;
1174 return(comp->nbStep++);
1178 * xmlXPathCompSwap:
1179 * @comp: the compiled expression
1180 * @op: operation index
1182 * Swaps 2 operations in the compiled expression
1184 static void
1185 xmlXPathCompSwap(xmlXPathStepOpPtr op) {
1186 int tmp;
1188 #ifndef LIBXML_THREAD_ENABLED
1190 * Since this manipulates possibly shared variables, this is
1191 * disabled if one detects that the library is used in a multithreaded
1192 * application
1194 if (xmlXPathDisableOptimizer)
1195 return;
1196 #endif
1198 tmp = op->ch1;
1199 op->ch1 = op->ch2;
1200 op->ch2 = tmp;
1203 #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
1204 xmlXPathCompExprAdd(ctxt, (op1), (op2), \
1205 (op), (val), (val2), (val3), (val4), (val5))
1206 #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
1207 xmlXPathCompExprAdd(ctxt, ctxt->comp->last, -1, \
1208 (op), (val), (val2), (val3), (val4), (val5))
1210 #define PUSH_LEAVE_EXPR(op, val, val2) \
1211 xmlXPathCompExprAdd(ctxt, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
1213 #define PUSH_UNARY_EXPR(op, ch, val, val2) \
1214 xmlXPathCompExprAdd(ctxt, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
1216 #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
1217 xmlXPathCompExprAdd(ctxt, (ch1), (ch2), (op), \
1218 (val), (val2), 0 ,NULL ,NULL)
1220 /************************************************************************
1222 * XPath object cache structures *
1224 ************************************************************************/
1226 /* #define XP_DEFAULT_CACHE_ON */
1228 #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
1230 typedef struct _xmlXPathContextCache xmlXPathContextCache;
1231 typedef xmlXPathContextCache *xmlXPathContextCachePtr;
1232 struct _xmlXPathContextCache {
1233 xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
1234 xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
1235 xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
1236 xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
1237 xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
1238 int maxNodeset;
1239 int maxString;
1240 int maxBoolean;
1241 int maxNumber;
1242 int maxMisc;
1243 #ifdef XP_DEBUG_OBJ_USAGE
1244 int dbgCachedAll;
1245 int dbgCachedNodeset;
1246 int dbgCachedString;
1247 int dbgCachedBool;
1248 int dbgCachedNumber;
1249 int dbgCachedPoint;
1250 int dbgCachedRange;
1251 int dbgCachedLocset;
1252 int dbgCachedUsers;
1253 int dbgCachedXSLTTree;
1254 int dbgCachedUndefined;
1257 int dbgReusedAll;
1258 int dbgReusedNodeset;
1259 int dbgReusedString;
1260 int dbgReusedBool;
1261 int dbgReusedNumber;
1262 int dbgReusedPoint;
1263 int dbgReusedRange;
1264 int dbgReusedLocset;
1265 int dbgReusedUsers;
1266 int dbgReusedXSLTTree;
1267 int dbgReusedUndefined;
1269 #endif
1272 /************************************************************************
1274 * Debugging related functions *
1276 ************************************************************************/
1278 #define STRANGE \
1279 xmlGenericError(xmlGenericErrorContext, \
1280 "Internal error at %s:%d\n", \
1281 __FILE__, __LINE__);
1283 #ifdef LIBXML_DEBUG_ENABLED
1284 static void
1285 xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
1286 int i;
1287 char shift[100];
1289 for (i = 0;((i < depth) && (i < 25));i++)
1290 shift[2 * i] = shift[2 * i + 1] = ' ';
1291 shift[2 * i] = shift[2 * i + 1] = 0;
1292 if (cur == NULL) {
1293 fprintf(output, "%s", shift);
1294 fprintf(output, "Node is NULL !\n");
1295 return;
1299 if ((cur->type == XML_DOCUMENT_NODE) ||
1300 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1301 fprintf(output, "%s", shift);
1302 fprintf(output, " /\n");
1303 } else if (cur->type == XML_ATTRIBUTE_NODE)
1304 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
1305 else
1306 xmlDebugDumpOneNode(output, cur, depth);
1308 static void
1309 xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
1310 xmlNodePtr tmp;
1311 int i;
1312 char shift[100];
1314 for (i = 0;((i < depth) && (i < 25));i++)
1315 shift[2 * i] = shift[2 * i + 1] = ' ';
1316 shift[2 * i] = shift[2 * i + 1] = 0;
1317 if (cur == NULL) {
1318 fprintf(output, "%s", shift);
1319 fprintf(output, "Node is NULL !\n");
1320 return;
1324 while (cur != NULL) {
1325 tmp = cur;
1326 cur = cur->next;
1327 xmlDebugDumpOneNode(output, tmp, depth);
1331 static void
1332 xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
1333 int i;
1334 char shift[100];
1336 for (i = 0;((i < depth) && (i < 25));i++)
1337 shift[2 * i] = shift[2 * i + 1] = ' ';
1338 shift[2 * i] = shift[2 * i + 1] = 0;
1340 if (cur == NULL) {
1341 fprintf(output, "%s", shift);
1342 fprintf(output, "NodeSet is NULL !\n");
1343 return;
1347 if (cur != NULL) {
1348 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
1349 for (i = 0;i < cur->nodeNr;i++) {
1350 fprintf(output, "%s", shift);
1351 fprintf(output, "%d", i + 1);
1352 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
1357 static void
1358 xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
1359 int i;
1360 char shift[100];
1362 for (i = 0;((i < depth) && (i < 25));i++)
1363 shift[2 * i] = shift[2 * i + 1] = ' ';
1364 shift[2 * i] = shift[2 * i + 1] = 0;
1366 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
1367 fprintf(output, "%s", shift);
1368 fprintf(output, "Value Tree is NULL !\n");
1369 return;
1373 fprintf(output, "%s", shift);
1374 fprintf(output, "%d", i + 1);
1375 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
1377 #if defined(LIBXML_XPTR_LOCS_ENABLED)
1378 static void
1379 xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
1380 int i;
1381 char shift[100];
1383 for (i = 0;((i < depth) && (i < 25));i++)
1384 shift[2 * i] = shift[2 * i + 1] = ' ';
1385 shift[2 * i] = shift[2 * i + 1] = 0;
1387 if (cur == NULL) {
1388 fprintf(output, "%s", shift);
1389 fprintf(output, "LocationSet is NULL !\n");
1390 return;
1394 for (i = 0;i < cur->locNr;i++) {
1395 fprintf(output, "%s", shift);
1396 fprintf(output, "%d : ", i + 1);
1397 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
1400 #endif /* LIBXML_XPTR_LOCS_ENABLED */
1403 * xmlXPathDebugDumpObject:
1404 * @output: the FILE * to dump the output
1405 * @cur: the object to inspect
1406 * @depth: indentation level
1408 * Dump the content of the object for debugging purposes
1410 void
1411 xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
1412 int i;
1413 char shift[100];
1415 if (output == NULL) return;
1417 for (i = 0;((i < depth) && (i < 25));i++)
1418 shift[2 * i] = shift[2 * i + 1] = ' ';
1419 shift[2 * i] = shift[2 * i + 1] = 0;
1422 fprintf(output, "%s", shift);
1424 if (cur == NULL) {
1425 fprintf(output, "Object is empty (NULL)\n");
1426 return;
1428 switch(cur->type) {
1429 case XPATH_UNDEFINED:
1430 fprintf(output, "Object is uninitialized\n");
1431 break;
1432 case XPATH_NODESET:
1433 fprintf(output, "Object is a Node Set :\n");
1434 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
1435 break;
1436 case XPATH_XSLT_TREE:
1437 fprintf(output, "Object is an XSLT value tree :\n");
1438 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
1439 break;
1440 case XPATH_BOOLEAN:
1441 fprintf(output, "Object is a Boolean : ");
1442 if (cur->boolval) fprintf(output, "true\n");
1443 else fprintf(output, "false\n");
1444 break;
1445 case XPATH_NUMBER:
1446 switch (xmlXPathIsInf(cur->floatval)) {
1447 case 1:
1448 fprintf(output, "Object is a number : Infinity\n");
1449 break;
1450 case -1:
1451 fprintf(output, "Object is a number : -Infinity\n");
1452 break;
1453 default:
1454 if (xmlXPathIsNaN(cur->floatval)) {
1455 fprintf(output, "Object is a number : NaN\n");
1456 } else if (cur->floatval == 0) {
1457 /* Omit sign for negative zero. */
1458 fprintf(output, "Object is a number : 0\n");
1459 } else {
1460 fprintf(output, "Object is a number : %0g\n", cur->floatval);
1463 break;
1464 case XPATH_STRING:
1465 fprintf(output, "Object is a string : ");
1466 xmlDebugDumpString(output, cur->stringval);
1467 fprintf(output, "\n");
1468 break;
1469 #ifdef LIBXML_XPTR_LOCS_ENABLED
1470 case XPATH_POINT:
1471 fprintf(output, "Object is a point : index %d in node", cur->index);
1472 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
1473 fprintf(output, "\n");
1474 break;
1475 case XPATH_RANGE:
1476 if ((cur->user2 == NULL) ||
1477 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
1478 fprintf(output, "Object is a collapsed range :\n");
1479 fprintf(output, "%s", shift);
1480 if (cur->index >= 0)
1481 fprintf(output, "index %d in ", cur->index);
1482 fprintf(output, "node\n");
1483 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1484 depth + 1);
1485 } else {
1486 fprintf(output, "Object is a range :\n");
1487 fprintf(output, "%s", shift);
1488 fprintf(output, "From ");
1489 if (cur->index >= 0)
1490 fprintf(output, "index %d in ", cur->index);
1491 fprintf(output, "node\n");
1492 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1493 depth + 1);
1494 fprintf(output, "%s", shift);
1495 fprintf(output, "To ");
1496 if (cur->index2 >= 0)
1497 fprintf(output, "index %d in ", cur->index2);
1498 fprintf(output, "node\n");
1499 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
1500 depth + 1);
1501 fprintf(output, "\n");
1503 break;
1504 case XPATH_LOCATIONSET:
1505 fprintf(output, "Object is a Location Set:\n");
1506 xmlXPathDebugDumpLocationSet(output,
1507 (xmlLocationSetPtr) cur->user, depth);
1508 break;
1509 #endif /* LIBXML_XPTR_LOCS_ENABLED */
1510 case XPATH_USERS:
1511 fprintf(output, "Object is user defined\n");
1512 break;
1516 static void
1517 xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
1518 xmlXPathStepOpPtr op, int depth) {
1519 int i;
1520 char shift[100];
1522 for (i = 0;((i < depth) && (i < 25));i++)
1523 shift[2 * i] = shift[2 * i + 1] = ' ';
1524 shift[2 * i] = shift[2 * i + 1] = 0;
1526 fprintf(output, "%s", shift);
1527 if (op == NULL) {
1528 fprintf(output, "Step is NULL\n");
1529 return;
1531 switch (op->op) {
1532 case XPATH_OP_END:
1533 fprintf(output, "END"); break;
1534 case XPATH_OP_AND:
1535 fprintf(output, "AND"); break;
1536 case XPATH_OP_OR:
1537 fprintf(output, "OR"); break;
1538 case XPATH_OP_EQUAL:
1539 if (op->value)
1540 fprintf(output, "EQUAL =");
1541 else
1542 fprintf(output, "EQUAL !=");
1543 break;
1544 case XPATH_OP_CMP:
1545 if (op->value)
1546 fprintf(output, "CMP <");
1547 else
1548 fprintf(output, "CMP >");
1549 if (!op->value2)
1550 fprintf(output, "=");
1551 break;
1552 case XPATH_OP_PLUS:
1553 if (op->value == 0)
1554 fprintf(output, "PLUS -");
1555 else if (op->value == 1)
1556 fprintf(output, "PLUS +");
1557 else if (op->value == 2)
1558 fprintf(output, "PLUS unary -");
1559 else if (op->value == 3)
1560 fprintf(output, "PLUS unary - -");
1561 break;
1562 case XPATH_OP_MULT:
1563 if (op->value == 0)
1564 fprintf(output, "MULT *");
1565 else if (op->value == 1)
1566 fprintf(output, "MULT div");
1567 else
1568 fprintf(output, "MULT mod");
1569 break;
1570 case XPATH_OP_UNION:
1571 fprintf(output, "UNION"); break;
1572 case XPATH_OP_ROOT:
1573 fprintf(output, "ROOT"); break;
1574 case XPATH_OP_NODE:
1575 fprintf(output, "NODE"); break;
1576 case XPATH_OP_SORT:
1577 fprintf(output, "SORT"); break;
1578 case XPATH_OP_COLLECT: {
1579 xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
1580 xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
1581 xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
1582 const xmlChar *prefix = op->value4;
1583 const xmlChar *name = op->value5;
1585 fprintf(output, "COLLECT ");
1586 switch (axis) {
1587 case AXIS_ANCESTOR:
1588 fprintf(output, " 'ancestors' "); break;
1589 case AXIS_ANCESTOR_OR_SELF:
1590 fprintf(output, " 'ancestors-or-self' "); break;
1591 case AXIS_ATTRIBUTE:
1592 fprintf(output, " 'attributes' "); break;
1593 case AXIS_CHILD:
1594 fprintf(output, " 'child' "); break;
1595 case AXIS_DESCENDANT:
1596 fprintf(output, " 'descendant' "); break;
1597 case AXIS_DESCENDANT_OR_SELF:
1598 fprintf(output, " 'descendant-or-self' "); break;
1599 case AXIS_FOLLOWING:
1600 fprintf(output, " 'following' "); break;
1601 case AXIS_FOLLOWING_SIBLING:
1602 fprintf(output, " 'following-siblings' "); break;
1603 case AXIS_NAMESPACE:
1604 fprintf(output, " 'namespace' "); break;
1605 case AXIS_PARENT:
1606 fprintf(output, " 'parent' "); break;
1607 case AXIS_PRECEDING:
1608 fprintf(output, " 'preceding' "); break;
1609 case AXIS_PRECEDING_SIBLING:
1610 fprintf(output, " 'preceding-sibling' "); break;
1611 case AXIS_SELF:
1612 fprintf(output, " 'self' "); break;
1614 switch (test) {
1615 case NODE_TEST_NONE:
1616 fprintf(output, "'none' "); break;
1617 case NODE_TEST_TYPE:
1618 fprintf(output, "'type' "); break;
1619 case NODE_TEST_PI:
1620 fprintf(output, "'PI' "); break;
1621 case NODE_TEST_ALL:
1622 fprintf(output, "'all' "); break;
1623 case NODE_TEST_NS:
1624 fprintf(output, "'namespace' "); break;
1625 case NODE_TEST_NAME:
1626 fprintf(output, "'name' "); break;
1628 switch (type) {
1629 case NODE_TYPE_NODE:
1630 fprintf(output, "'node' "); break;
1631 case NODE_TYPE_COMMENT:
1632 fprintf(output, "'comment' "); break;
1633 case NODE_TYPE_TEXT:
1634 fprintf(output, "'text' "); break;
1635 case NODE_TYPE_PI:
1636 fprintf(output, "'PI' "); break;
1638 if (prefix != NULL)
1639 fprintf(output, "%s:", prefix);
1640 if (name != NULL)
1641 fprintf(output, "%s", (const char *) name);
1642 break;
1645 case XPATH_OP_VALUE: {
1646 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
1648 fprintf(output, "ELEM ");
1649 xmlXPathDebugDumpObject(output, object, 0);
1650 goto finish;
1652 case XPATH_OP_VARIABLE: {
1653 const xmlChar *prefix = op->value5;
1654 const xmlChar *name = op->value4;
1656 if (prefix != NULL)
1657 fprintf(output, "VARIABLE %s:%s", prefix, name);
1658 else
1659 fprintf(output, "VARIABLE %s", name);
1660 break;
1662 case XPATH_OP_FUNCTION: {
1663 int nbargs = op->value;
1664 const xmlChar *prefix = op->value5;
1665 const xmlChar *name = op->value4;
1667 if (prefix != NULL)
1668 fprintf(output, "FUNCTION %s:%s(%d args)",
1669 prefix, name, nbargs);
1670 else
1671 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
1672 break;
1674 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
1675 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
1676 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
1677 #ifdef LIBXML_XPTR_LOCS_ENABLED
1678 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
1679 #endif
1680 default:
1681 fprintf(output, "UNKNOWN %d\n", op->op); return;
1683 fprintf(output, "\n");
1684 finish:
1685 if (op->ch1 >= 0)
1686 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
1687 if (op->ch2 >= 0)
1688 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
1692 * xmlXPathDebugDumpCompExpr:
1693 * @output: the FILE * for the output
1694 * @comp: the precompiled XPath expression
1695 * @depth: the indentation level.
1697 * Dumps the tree of the compiled XPath expression.
1699 void
1700 xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
1701 int depth) {
1702 int i;
1703 char shift[100];
1705 if ((output == NULL) || (comp == NULL)) return;
1707 for (i = 0;((i < depth) && (i < 25));i++)
1708 shift[2 * i] = shift[2 * i + 1] = ' ';
1709 shift[2 * i] = shift[2 * i + 1] = 0;
1711 fprintf(output, "%s", shift);
1713 #ifdef XPATH_STREAMING
1714 if (comp->stream) {
1715 fprintf(output, "Streaming Expression\n");
1716 } else
1717 #endif
1719 fprintf(output, "Compiled Expression : %d elements\n",
1720 comp->nbStep);
1721 i = comp->last;
1722 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
1726 #ifdef XP_DEBUG_OBJ_USAGE
1729 * XPath object usage related debugging variables.
1731 static int xmlXPathDebugObjCounterUndefined = 0;
1732 static int xmlXPathDebugObjCounterNodeset = 0;
1733 static int xmlXPathDebugObjCounterBool = 0;
1734 static int xmlXPathDebugObjCounterNumber = 0;
1735 static int xmlXPathDebugObjCounterString = 0;
1736 static int xmlXPathDebugObjCounterPoint = 0;
1737 static int xmlXPathDebugObjCounterRange = 0;
1738 static int xmlXPathDebugObjCounterLocset = 0;
1739 static int xmlXPathDebugObjCounterUsers = 0;
1740 static int xmlXPathDebugObjCounterXSLTTree = 0;
1741 static int xmlXPathDebugObjCounterAll = 0;
1743 static int xmlXPathDebugObjTotalUndefined = 0;
1744 static int xmlXPathDebugObjTotalNodeset = 0;
1745 static int xmlXPathDebugObjTotalBool = 0;
1746 static int xmlXPathDebugObjTotalNumber = 0;
1747 static int xmlXPathDebugObjTotalString = 0;
1748 static int xmlXPathDebugObjTotalPoint = 0;
1749 static int xmlXPathDebugObjTotalRange = 0;
1750 static int xmlXPathDebugObjTotalLocset = 0;
1751 static int xmlXPathDebugObjTotalUsers = 0;
1752 static int xmlXPathDebugObjTotalXSLTTree = 0;
1753 static int xmlXPathDebugObjTotalAll = 0;
1755 static int xmlXPathDebugObjMaxUndefined = 0;
1756 static int xmlXPathDebugObjMaxNodeset = 0;
1757 static int xmlXPathDebugObjMaxBool = 0;
1758 static int xmlXPathDebugObjMaxNumber = 0;
1759 static int xmlXPathDebugObjMaxString = 0;
1760 static int xmlXPathDebugObjMaxPoint = 0;
1761 static int xmlXPathDebugObjMaxRange = 0;
1762 static int xmlXPathDebugObjMaxLocset = 0;
1763 static int xmlXPathDebugObjMaxUsers = 0;
1764 static int xmlXPathDebugObjMaxXSLTTree = 0;
1765 static int xmlXPathDebugObjMaxAll = 0;
1767 static void
1768 xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
1770 if (ctxt != NULL) {
1771 if (ctxt->cache != NULL) {
1772 xmlXPathContextCachePtr cache =
1773 (xmlXPathContextCachePtr) ctxt->cache;
1775 cache->dbgCachedAll = 0;
1776 cache->dbgCachedNodeset = 0;
1777 cache->dbgCachedString = 0;
1778 cache->dbgCachedBool = 0;
1779 cache->dbgCachedNumber = 0;
1780 cache->dbgCachedPoint = 0;
1781 cache->dbgCachedRange = 0;
1782 cache->dbgCachedLocset = 0;
1783 cache->dbgCachedUsers = 0;
1784 cache->dbgCachedXSLTTree = 0;
1785 cache->dbgCachedUndefined = 0;
1787 cache->dbgReusedAll = 0;
1788 cache->dbgReusedNodeset = 0;
1789 cache->dbgReusedString = 0;
1790 cache->dbgReusedBool = 0;
1791 cache->dbgReusedNumber = 0;
1792 cache->dbgReusedPoint = 0;
1793 cache->dbgReusedRange = 0;
1794 cache->dbgReusedLocset = 0;
1795 cache->dbgReusedUsers = 0;
1796 cache->dbgReusedXSLTTree = 0;
1797 cache->dbgReusedUndefined = 0;
1801 xmlXPathDebugObjCounterUndefined = 0;
1802 xmlXPathDebugObjCounterNodeset = 0;
1803 xmlXPathDebugObjCounterBool = 0;
1804 xmlXPathDebugObjCounterNumber = 0;
1805 xmlXPathDebugObjCounterString = 0;
1806 xmlXPathDebugObjCounterPoint = 0;
1807 xmlXPathDebugObjCounterRange = 0;
1808 xmlXPathDebugObjCounterLocset = 0;
1809 xmlXPathDebugObjCounterUsers = 0;
1810 xmlXPathDebugObjCounterXSLTTree = 0;
1811 xmlXPathDebugObjCounterAll = 0;
1813 xmlXPathDebugObjTotalUndefined = 0;
1814 xmlXPathDebugObjTotalNodeset = 0;
1815 xmlXPathDebugObjTotalBool = 0;
1816 xmlXPathDebugObjTotalNumber = 0;
1817 xmlXPathDebugObjTotalString = 0;
1818 xmlXPathDebugObjTotalPoint = 0;
1819 xmlXPathDebugObjTotalRange = 0;
1820 xmlXPathDebugObjTotalLocset = 0;
1821 xmlXPathDebugObjTotalUsers = 0;
1822 xmlXPathDebugObjTotalXSLTTree = 0;
1823 xmlXPathDebugObjTotalAll = 0;
1825 xmlXPathDebugObjMaxUndefined = 0;
1826 xmlXPathDebugObjMaxNodeset = 0;
1827 xmlXPathDebugObjMaxBool = 0;
1828 xmlXPathDebugObjMaxNumber = 0;
1829 xmlXPathDebugObjMaxString = 0;
1830 xmlXPathDebugObjMaxPoint = 0;
1831 xmlXPathDebugObjMaxRange = 0;
1832 xmlXPathDebugObjMaxLocset = 0;
1833 xmlXPathDebugObjMaxUsers = 0;
1834 xmlXPathDebugObjMaxXSLTTree = 0;
1835 xmlXPathDebugObjMaxAll = 0;
1839 static void
1840 xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
1841 xmlXPathObjectType objType)
1843 int isCached = 0;
1845 if (ctxt != NULL) {
1846 if (ctxt->cache != NULL) {
1847 xmlXPathContextCachePtr cache =
1848 (xmlXPathContextCachePtr) ctxt->cache;
1850 isCached = 1;
1852 cache->dbgReusedAll++;
1853 switch (objType) {
1854 case XPATH_UNDEFINED:
1855 cache->dbgReusedUndefined++;
1856 break;
1857 case XPATH_NODESET:
1858 cache->dbgReusedNodeset++;
1859 break;
1860 case XPATH_BOOLEAN:
1861 cache->dbgReusedBool++;
1862 break;
1863 case XPATH_NUMBER:
1864 cache->dbgReusedNumber++;
1865 break;
1866 case XPATH_STRING:
1867 cache->dbgReusedString++;
1868 break;
1869 #ifdef LIBXML_XPTR_LOCS_ENABLED
1870 case XPATH_POINT:
1871 cache->dbgReusedPoint++;
1872 break;
1873 case XPATH_RANGE:
1874 cache->dbgReusedRange++;
1875 break;
1876 case XPATH_LOCATIONSET:
1877 cache->dbgReusedLocset++;
1878 break;
1879 #endif /* LIBXML_XPTR_LOCS_ENABLED */
1880 case XPATH_USERS:
1881 cache->dbgReusedUsers++;
1882 break;
1883 case XPATH_XSLT_TREE:
1884 cache->dbgReusedXSLTTree++;
1885 break;
1886 default:
1887 break;
1892 switch (objType) {
1893 case XPATH_UNDEFINED:
1894 if (! isCached)
1895 xmlXPathDebugObjTotalUndefined++;
1896 xmlXPathDebugObjCounterUndefined++;
1897 if (xmlXPathDebugObjCounterUndefined >
1898 xmlXPathDebugObjMaxUndefined)
1899 xmlXPathDebugObjMaxUndefined =
1900 xmlXPathDebugObjCounterUndefined;
1901 break;
1902 case XPATH_NODESET:
1903 if (! isCached)
1904 xmlXPathDebugObjTotalNodeset++;
1905 xmlXPathDebugObjCounterNodeset++;
1906 if (xmlXPathDebugObjCounterNodeset >
1907 xmlXPathDebugObjMaxNodeset)
1908 xmlXPathDebugObjMaxNodeset =
1909 xmlXPathDebugObjCounterNodeset;
1910 break;
1911 case XPATH_BOOLEAN:
1912 if (! isCached)
1913 xmlXPathDebugObjTotalBool++;
1914 xmlXPathDebugObjCounterBool++;
1915 if (xmlXPathDebugObjCounterBool >
1916 xmlXPathDebugObjMaxBool)
1917 xmlXPathDebugObjMaxBool =
1918 xmlXPathDebugObjCounterBool;
1919 break;
1920 case XPATH_NUMBER:
1921 if (! isCached)
1922 xmlXPathDebugObjTotalNumber++;
1923 xmlXPathDebugObjCounterNumber++;
1924 if (xmlXPathDebugObjCounterNumber >
1925 xmlXPathDebugObjMaxNumber)
1926 xmlXPathDebugObjMaxNumber =
1927 xmlXPathDebugObjCounterNumber;
1928 break;
1929 case XPATH_STRING:
1930 if (! isCached)
1931 xmlXPathDebugObjTotalString++;
1932 xmlXPathDebugObjCounterString++;
1933 if (xmlXPathDebugObjCounterString >
1934 xmlXPathDebugObjMaxString)
1935 xmlXPathDebugObjMaxString =
1936 xmlXPathDebugObjCounterString;
1937 break;
1938 #ifdef LIBXML_XPTR_LOCS_ENABLED
1939 case XPATH_POINT:
1940 if (! isCached)
1941 xmlXPathDebugObjTotalPoint++;
1942 xmlXPathDebugObjCounterPoint++;
1943 if (xmlXPathDebugObjCounterPoint >
1944 xmlXPathDebugObjMaxPoint)
1945 xmlXPathDebugObjMaxPoint =
1946 xmlXPathDebugObjCounterPoint;
1947 break;
1948 case XPATH_RANGE:
1949 if (! isCached)
1950 xmlXPathDebugObjTotalRange++;
1951 xmlXPathDebugObjCounterRange++;
1952 if (xmlXPathDebugObjCounterRange >
1953 xmlXPathDebugObjMaxRange)
1954 xmlXPathDebugObjMaxRange =
1955 xmlXPathDebugObjCounterRange;
1956 break;
1957 case XPATH_LOCATIONSET:
1958 if (! isCached)
1959 xmlXPathDebugObjTotalLocset++;
1960 xmlXPathDebugObjCounterLocset++;
1961 if (xmlXPathDebugObjCounterLocset >
1962 xmlXPathDebugObjMaxLocset)
1963 xmlXPathDebugObjMaxLocset =
1964 xmlXPathDebugObjCounterLocset;
1965 break;
1966 #endif /* LIBXML_XPTR_LOCS_ENABLED */
1967 case XPATH_USERS:
1968 if (! isCached)
1969 xmlXPathDebugObjTotalUsers++;
1970 xmlXPathDebugObjCounterUsers++;
1971 if (xmlXPathDebugObjCounterUsers >
1972 xmlXPathDebugObjMaxUsers)
1973 xmlXPathDebugObjMaxUsers =
1974 xmlXPathDebugObjCounterUsers;
1975 break;
1976 case XPATH_XSLT_TREE:
1977 if (! isCached)
1978 xmlXPathDebugObjTotalXSLTTree++;
1979 xmlXPathDebugObjCounterXSLTTree++;
1980 if (xmlXPathDebugObjCounterXSLTTree >
1981 xmlXPathDebugObjMaxXSLTTree)
1982 xmlXPathDebugObjMaxXSLTTree =
1983 xmlXPathDebugObjCounterXSLTTree;
1984 break;
1985 default:
1986 break;
1988 if (! isCached)
1989 xmlXPathDebugObjTotalAll++;
1990 xmlXPathDebugObjCounterAll++;
1991 if (xmlXPathDebugObjCounterAll >
1992 xmlXPathDebugObjMaxAll)
1993 xmlXPathDebugObjMaxAll =
1994 xmlXPathDebugObjCounterAll;
1997 static void
1998 xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
1999 xmlXPathObjectType objType)
2001 int isCached = 0;
2003 if (ctxt != NULL) {
2004 if (ctxt->cache != NULL) {
2005 xmlXPathContextCachePtr cache =
2006 (xmlXPathContextCachePtr) ctxt->cache;
2008 isCached = 1;
2010 cache->dbgCachedAll++;
2011 switch (objType) {
2012 case XPATH_UNDEFINED:
2013 cache->dbgCachedUndefined++;
2014 break;
2015 case XPATH_NODESET:
2016 cache->dbgCachedNodeset++;
2017 break;
2018 case XPATH_BOOLEAN:
2019 cache->dbgCachedBool++;
2020 break;
2021 case XPATH_NUMBER:
2022 cache->dbgCachedNumber++;
2023 break;
2024 case XPATH_STRING:
2025 cache->dbgCachedString++;
2026 break;
2027 #ifdef LIBXML_XPTR_LOCS_ENABLED
2028 case XPATH_POINT:
2029 cache->dbgCachedPoint++;
2030 break;
2031 case XPATH_RANGE:
2032 cache->dbgCachedRange++;
2033 break;
2034 case XPATH_LOCATIONSET:
2035 cache->dbgCachedLocset++;
2036 break;
2037 #endif /* LIBXML_XPTR_LOCS_ENABLED */
2038 case XPATH_USERS:
2039 cache->dbgCachedUsers++;
2040 break;
2041 case XPATH_XSLT_TREE:
2042 cache->dbgCachedXSLTTree++;
2043 break;
2044 default:
2045 break;
2050 switch (objType) {
2051 case XPATH_UNDEFINED:
2052 xmlXPathDebugObjCounterUndefined--;
2053 break;
2054 case XPATH_NODESET:
2055 xmlXPathDebugObjCounterNodeset--;
2056 break;
2057 case XPATH_BOOLEAN:
2058 xmlXPathDebugObjCounterBool--;
2059 break;
2060 case XPATH_NUMBER:
2061 xmlXPathDebugObjCounterNumber--;
2062 break;
2063 case XPATH_STRING:
2064 xmlXPathDebugObjCounterString--;
2065 break;
2066 #ifdef LIBXML_XPTR_LOCS_ENABLED
2067 case XPATH_POINT:
2068 xmlXPathDebugObjCounterPoint--;
2069 break;
2070 case XPATH_RANGE:
2071 xmlXPathDebugObjCounterRange--;
2072 break;
2073 case XPATH_LOCATIONSET:
2074 xmlXPathDebugObjCounterLocset--;
2075 break;
2076 #endif /* LIBXML_XPTR_LOCS_ENABLED */
2077 case XPATH_USERS:
2078 xmlXPathDebugObjCounterUsers--;
2079 break;
2080 case XPATH_XSLT_TREE:
2081 xmlXPathDebugObjCounterXSLTTree--;
2082 break;
2083 default:
2084 break;
2086 xmlXPathDebugObjCounterAll--;
2089 static void
2090 xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
2092 int reqAll, reqNodeset, reqString, reqBool, reqNumber,
2093 reqXSLTTree, reqUndefined;
2094 int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
2095 caNumber = 0, caXSLTTree = 0, caUndefined = 0;
2096 int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
2097 reNumber = 0, reXSLTTree = 0, reUndefined = 0;
2098 int leftObjs = xmlXPathDebugObjCounterAll;
2100 reqAll = xmlXPathDebugObjTotalAll;
2101 reqNodeset = xmlXPathDebugObjTotalNodeset;
2102 reqString = xmlXPathDebugObjTotalString;
2103 reqBool = xmlXPathDebugObjTotalBool;
2104 reqNumber = xmlXPathDebugObjTotalNumber;
2105 reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
2106 reqUndefined = xmlXPathDebugObjTotalUndefined;
2108 printf("# XPath object usage:\n");
2110 if (ctxt != NULL) {
2111 if (ctxt->cache != NULL) {
2112 xmlXPathContextCachePtr cache =
2113 (xmlXPathContextCachePtr) ctxt->cache;
2115 reAll = cache->dbgReusedAll;
2116 reqAll += reAll;
2117 reNodeset = cache->dbgReusedNodeset;
2118 reqNodeset += reNodeset;
2119 reString = cache->dbgReusedString;
2120 reqString += reString;
2121 reBool = cache->dbgReusedBool;
2122 reqBool += reBool;
2123 reNumber = cache->dbgReusedNumber;
2124 reqNumber += reNumber;
2125 reXSLTTree = cache->dbgReusedXSLTTree;
2126 reqXSLTTree += reXSLTTree;
2127 reUndefined = cache->dbgReusedUndefined;
2128 reqUndefined += reUndefined;
2130 caAll = cache->dbgCachedAll;
2131 caBool = cache->dbgCachedBool;
2132 caNodeset = cache->dbgCachedNodeset;
2133 caString = cache->dbgCachedString;
2134 caNumber = cache->dbgCachedNumber;
2135 caXSLTTree = cache->dbgCachedXSLTTree;
2136 caUndefined = cache->dbgCachedUndefined;
2138 if (cache->nodesetObjs)
2139 leftObjs -= cache->nodesetObjs->number;
2140 if (cache->stringObjs)
2141 leftObjs -= cache->stringObjs->number;
2142 if (cache->booleanObjs)
2143 leftObjs -= cache->booleanObjs->number;
2144 if (cache->numberObjs)
2145 leftObjs -= cache->numberObjs->number;
2146 if (cache->miscObjs)
2147 leftObjs -= cache->miscObjs->number;
2151 printf("# all\n");
2152 printf("# total : %d\n", reqAll);
2153 printf("# left : %d\n", leftObjs);
2154 printf("# created: %d\n", xmlXPathDebugObjTotalAll);
2155 printf("# reused : %d\n", reAll);
2156 printf("# max : %d\n", xmlXPathDebugObjMaxAll);
2158 printf("# node-sets\n");
2159 printf("# total : %d\n", reqNodeset);
2160 printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
2161 printf("# reused : %d\n", reNodeset);
2162 printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
2164 printf("# strings\n");
2165 printf("# total : %d\n", reqString);
2166 printf("# created: %d\n", xmlXPathDebugObjTotalString);
2167 printf("# reused : %d\n", reString);
2168 printf("# max : %d\n", xmlXPathDebugObjMaxString);
2170 printf("# booleans\n");
2171 printf("# total : %d\n", reqBool);
2172 printf("# created: %d\n", xmlXPathDebugObjTotalBool);
2173 printf("# reused : %d\n", reBool);
2174 printf("# max : %d\n", xmlXPathDebugObjMaxBool);
2176 printf("# numbers\n");
2177 printf("# total : %d\n", reqNumber);
2178 printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
2179 printf("# reused : %d\n", reNumber);
2180 printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
2182 printf("# XSLT result tree fragments\n");
2183 printf("# total : %d\n", reqXSLTTree);
2184 printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
2185 printf("# reused : %d\n", reXSLTTree);
2186 printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
2188 printf("# undefined\n");
2189 printf("# total : %d\n", reqUndefined);
2190 printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
2191 printf("# reused : %d\n", reUndefined);
2192 printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
2196 #endif /* XP_DEBUG_OBJ_USAGE */
2198 #endif /* LIBXML_DEBUG_ENABLED */
2200 /************************************************************************
2202 * XPath object caching *
2204 ************************************************************************/
2207 * xmlXPathNewCache:
2209 * Create a new object cache
2211 * Returns the xmlXPathCache just allocated.
2213 static xmlXPathContextCachePtr
2214 xmlXPathNewCache(void)
2216 xmlXPathContextCachePtr ret;
2218 ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
2219 if (ret == NULL) {
2220 xmlXPathErrMemory(NULL, "creating object cache\n");
2221 return(NULL);
2223 memset(ret, 0 , sizeof(xmlXPathContextCache));
2224 ret->maxNodeset = 100;
2225 ret->maxString = 100;
2226 ret->maxBoolean = 100;
2227 ret->maxNumber = 100;
2228 ret->maxMisc = 100;
2229 return(ret);
2232 static void
2233 xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
2235 int i;
2236 xmlXPathObjectPtr obj;
2238 if (list == NULL)
2239 return;
2241 for (i = 0; i < list->number; i++) {
2242 obj = list->items[i];
2244 * Note that it is already assured that we don't need to
2245 * look out for namespace nodes in the node-set.
2247 if (obj->nodesetval != NULL) {
2248 if (obj->nodesetval->nodeTab != NULL)
2249 xmlFree(obj->nodesetval->nodeTab);
2250 xmlFree(obj->nodesetval);
2252 xmlFree(obj);
2253 #ifdef XP_DEBUG_OBJ_USAGE
2254 xmlXPathDebugObjCounterAll--;
2255 #endif
2257 xmlPointerListFree(list);
2260 static void
2261 xmlXPathFreeCache(xmlXPathContextCachePtr cache)
2263 if (cache == NULL)
2264 return;
2265 if (cache->nodesetObjs)
2266 xmlXPathCacheFreeObjectList(cache->nodesetObjs);
2267 if (cache->stringObjs)
2268 xmlXPathCacheFreeObjectList(cache->stringObjs);
2269 if (cache->booleanObjs)
2270 xmlXPathCacheFreeObjectList(cache->booleanObjs);
2271 if (cache->numberObjs)
2272 xmlXPathCacheFreeObjectList(cache->numberObjs);
2273 if (cache->miscObjs)
2274 xmlXPathCacheFreeObjectList(cache->miscObjs);
2275 xmlFree(cache);
2279 * xmlXPathContextSetCache:
2281 * @ctxt: the XPath context
2282 * @active: enables/disables (creates/frees) the cache
2283 * @value: a value with semantics dependent on @options
2284 * @options: options (currently only the value 0 is used)
2286 * Creates/frees an object cache on the XPath context.
2287 * If activates XPath objects (xmlXPathObject) will be cached internally
2288 * to be reused.
2289 * @options:
2290 * 0: This will set the XPath object caching:
2291 * @value:
2292 * This will set the maximum number of XPath objects
2293 * to be cached per slot
2294 * There are 5 slots for: node-set, string, number, boolean, and
2295 * misc objects. Use <0 for the default number (100).
2296 * Other values for @options have currently no effect.
2298 * Returns 0 if the setting succeeded, and -1 on API or internal errors.
2301 xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
2302 int active,
2303 int value,
2304 int options)
2306 if (ctxt == NULL)
2307 return(-1);
2308 if (active) {
2309 xmlXPathContextCachePtr cache;
2311 if (ctxt->cache == NULL) {
2312 ctxt->cache = xmlXPathNewCache();
2313 if (ctxt->cache == NULL)
2314 return(-1);
2316 cache = (xmlXPathContextCachePtr) ctxt->cache;
2317 if (options == 0) {
2318 if (value < 0)
2319 value = 100;
2320 cache->maxNodeset = value;
2321 cache->maxString = value;
2322 cache->maxNumber = value;
2323 cache->maxBoolean = value;
2324 cache->maxMisc = value;
2326 } else if (ctxt->cache != NULL) {
2327 xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
2328 ctxt->cache = NULL;
2330 return(0);
2334 * xmlXPathCacheWrapNodeSet:
2335 * @ctxt: the XPath context
2336 * @val: the NodePtr value
2338 * This is the cached version of xmlXPathWrapNodeSet().
2339 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
2341 * Returns the created or reused object.
2343 * In case of error the node set is destroyed and NULL is returned.
2345 static xmlXPathObjectPtr
2346 xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
2348 if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2349 xmlXPathContextCachePtr cache =
2350 (xmlXPathContextCachePtr) ctxt->cache;
2352 if ((cache->miscObjs != NULL) &&
2353 (cache->miscObjs->number != 0))
2355 xmlXPathObjectPtr ret;
2357 ret = (xmlXPathObjectPtr)
2358 cache->miscObjs->items[--cache->miscObjs->number];
2359 ret->type = XPATH_NODESET;
2360 ret->nodesetval = val;
2361 #ifdef XP_DEBUG_OBJ_USAGE
2362 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2363 #endif
2364 return(ret);
2368 return(xmlXPathWrapNodeSet(val));
2373 * xmlXPathCacheWrapString:
2374 * @ctxt: the XPath context
2375 * @val: the xmlChar * value
2377 * This is the cached version of xmlXPathWrapString().
2378 * Wraps the @val string into an XPath object.
2380 * Returns the created or reused object.
2382 static xmlXPathObjectPtr
2383 xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
2385 if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2386 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2388 if ((cache->stringObjs != NULL) &&
2389 (cache->stringObjs->number != 0))
2392 xmlXPathObjectPtr ret;
2394 ret = (xmlXPathObjectPtr)
2395 cache->stringObjs->items[--cache->stringObjs->number];
2396 ret->type = XPATH_STRING;
2397 ret->stringval = val;
2398 #ifdef XP_DEBUG_OBJ_USAGE
2399 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2400 #endif
2401 return(ret);
2402 } else if ((cache->miscObjs != NULL) &&
2403 (cache->miscObjs->number != 0))
2405 xmlXPathObjectPtr ret;
2407 * Fallback to misc-cache.
2409 ret = (xmlXPathObjectPtr)
2410 cache->miscObjs->items[--cache->miscObjs->number];
2412 ret->type = XPATH_STRING;
2413 ret->stringval = val;
2414 #ifdef XP_DEBUG_OBJ_USAGE
2415 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2416 #endif
2417 return(ret);
2420 return(xmlXPathWrapString(val));
2424 * xmlXPathCacheNewNodeSet:
2425 * @ctxt: the XPath context
2426 * @val: the NodePtr value
2428 * This is the cached version of xmlXPathNewNodeSet().
2429 * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
2430 * it with the single Node @val
2432 * Returns the created or reused object.
2434 static xmlXPathObjectPtr
2435 xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
2437 if ((ctxt != NULL) && (ctxt->cache)) {
2438 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2440 if ((cache->nodesetObjs != NULL) &&
2441 (cache->nodesetObjs->number != 0))
2443 xmlXPathObjectPtr ret;
2445 * Use the nodeset-cache.
2447 ret = (xmlXPathObjectPtr)
2448 cache->nodesetObjs->items[--cache->nodesetObjs->number];
2449 ret->type = XPATH_NODESET;
2450 ret->boolval = 0;
2451 if (val) {
2452 if ((ret->nodesetval->nodeMax == 0) ||
2453 (val->type == XML_NAMESPACE_DECL))
2455 /* TODO: Check memory error. */
2456 xmlXPathNodeSetAddUnique(ret->nodesetval, val);
2457 } else {
2458 ret->nodesetval->nodeTab[0] = val;
2459 ret->nodesetval->nodeNr = 1;
2462 #ifdef XP_DEBUG_OBJ_USAGE
2463 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2464 #endif
2465 return(ret);
2466 } else if ((cache->miscObjs != NULL) &&
2467 (cache->miscObjs->number != 0))
2469 xmlXPathObjectPtr ret;
2470 xmlNodeSetPtr set;
2472 * Fallback to misc-cache.
2475 set = xmlXPathNodeSetCreate(val);
2476 if (set == NULL) {
2477 ctxt->lastError.domain = XML_FROM_XPATH;
2478 ctxt->lastError.code = XML_ERR_NO_MEMORY;
2479 return(NULL);
2482 ret = (xmlXPathObjectPtr)
2483 cache->miscObjs->items[--cache->miscObjs->number];
2485 ret->type = XPATH_NODESET;
2486 ret->boolval = 0;
2487 ret->nodesetval = set;
2488 #ifdef XP_DEBUG_OBJ_USAGE
2489 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2490 #endif
2491 return(ret);
2494 return(xmlXPathNewNodeSet(val));
2498 * xmlXPathCacheNewString:
2499 * @ctxt: the XPath context
2500 * @val: the xmlChar * value
2502 * This is the cached version of xmlXPathNewString().
2503 * Acquire an xmlXPathObjectPtr of type string and of value @val
2505 * Returns the created or reused object.
2507 static xmlXPathObjectPtr
2508 xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
2510 if ((ctxt != NULL) && (ctxt->cache)) {
2511 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2513 if ((cache->stringObjs != NULL) &&
2514 (cache->stringObjs->number != 0))
2516 xmlXPathObjectPtr ret;
2517 xmlChar *copy;
2519 if (val == NULL)
2520 val = BAD_CAST "";
2521 copy = xmlStrdup(val);
2522 if (copy == NULL) {
2523 xmlXPathErrMemory(ctxt, NULL);
2524 return(NULL);
2527 ret = (xmlXPathObjectPtr)
2528 cache->stringObjs->items[--cache->stringObjs->number];
2529 ret->type = XPATH_STRING;
2530 ret->stringval = copy;
2531 #ifdef XP_DEBUG_OBJ_USAGE
2532 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2533 #endif
2534 return(ret);
2535 } else if ((cache->miscObjs != NULL) &&
2536 (cache->miscObjs->number != 0))
2538 xmlXPathObjectPtr ret;
2539 xmlChar *copy;
2541 if (val == NULL)
2542 val = BAD_CAST "";
2543 copy = xmlStrdup(val);
2544 if (copy == NULL) {
2545 xmlXPathErrMemory(ctxt, NULL);
2546 return(NULL);
2549 ret = (xmlXPathObjectPtr)
2550 cache->miscObjs->items[--cache->miscObjs->number];
2552 ret->type = XPATH_STRING;
2553 ret->stringval = copy;
2554 #ifdef XP_DEBUG_OBJ_USAGE
2555 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2556 #endif
2557 return(ret);
2560 return(xmlXPathNewString(val));
2564 * xmlXPathCacheNewCString:
2565 * @ctxt: the XPath context
2566 * @val: the char * value
2568 * This is the cached version of xmlXPathNewCString().
2569 * Acquire an xmlXPathObjectPtr of type string and of value @val
2571 * Returns the created or reused object.
2573 static xmlXPathObjectPtr
2574 xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
2576 return xmlXPathCacheNewString(ctxt, BAD_CAST val);
2580 * xmlXPathCacheNewBoolean:
2581 * @ctxt: the XPath context
2582 * @val: the boolean value
2584 * This is the cached version of xmlXPathNewBoolean().
2585 * Acquires an xmlXPathObjectPtr of type boolean and of value @val
2587 * Returns the created or reused object.
2589 static xmlXPathObjectPtr
2590 xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
2592 if ((ctxt != NULL) && (ctxt->cache)) {
2593 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2595 if ((cache->booleanObjs != NULL) &&
2596 (cache->booleanObjs->number != 0))
2598 xmlXPathObjectPtr ret;
2600 ret = (xmlXPathObjectPtr)
2601 cache->booleanObjs->items[--cache->booleanObjs->number];
2602 ret->type = XPATH_BOOLEAN;
2603 ret->boolval = (val != 0);
2604 #ifdef XP_DEBUG_OBJ_USAGE
2605 xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2606 #endif
2607 return(ret);
2608 } else if ((cache->miscObjs != NULL) &&
2609 (cache->miscObjs->number != 0))
2611 xmlXPathObjectPtr ret;
2613 ret = (xmlXPathObjectPtr)
2614 cache->miscObjs->items[--cache->miscObjs->number];
2616 ret->type = XPATH_BOOLEAN;
2617 ret->boolval = (val != 0);
2618 #ifdef XP_DEBUG_OBJ_USAGE
2619 xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2620 #endif
2621 return(ret);
2624 return(xmlXPathNewBoolean(val));
2628 * xmlXPathCacheNewFloat:
2629 * @ctxt: the XPath context
2630 * @val: the double value
2632 * This is the cached version of xmlXPathNewFloat().
2633 * Acquires an xmlXPathObjectPtr of type double and of value @val
2635 * Returns the created or reused object.
2637 static xmlXPathObjectPtr
2638 xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
2640 if ((ctxt != NULL) && (ctxt->cache)) {
2641 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2643 if ((cache->numberObjs != NULL) &&
2644 (cache->numberObjs->number != 0))
2646 xmlXPathObjectPtr ret;
2648 ret = (xmlXPathObjectPtr)
2649 cache->numberObjs->items[--cache->numberObjs->number];
2650 ret->type = XPATH_NUMBER;
2651 ret->floatval = val;
2652 #ifdef XP_DEBUG_OBJ_USAGE
2653 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2654 #endif
2655 return(ret);
2656 } else if ((cache->miscObjs != NULL) &&
2657 (cache->miscObjs->number != 0))
2659 xmlXPathObjectPtr ret;
2661 ret = (xmlXPathObjectPtr)
2662 cache->miscObjs->items[--cache->miscObjs->number];
2664 ret->type = XPATH_NUMBER;
2665 ret->floatval = val;
2666 #ifdef XP_DEBUG_OBJ_USAGE
2667 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2668 #endif
2669 return(ret);
2672 return(xmlXPathNewFloat(val));
2676 * xmlXPathCacheConvertString:
2677 * @ctxt: the XPath context
2678 * @val: an XPath object
2680 * This is the cached version of xmlXPathConvertString().
2681 * Converts an existing object to its string() equivalent
2683 * Returns a created or reused object, the old one is freed (cached)
2684 * (or the operation is done directly on @val)
2687 static xmlXPathObjectPtr
2688 xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2689 xmlChar *res = NULL;
2691 if (val == NULL)
2692 return(xmlXPathCacheNewCString(ctxt, ""));
2694 switch (val->type) {
2695 case XPATH_UNDEFINED:
2696 #ifdef DEBUG_EXPR
2697 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2698 #endif
2699 break;
2700 case XPATH_NODESET:
2701 case XPATH_XSLT_TREE:
2702 res = xmlXPathCastNodeSetToString(val->nodesetval);
2703 break;
2704 case XPATH_STRING:
2705 return(val);
2706 case XPATH_BOOLEAN:
2707 res = xmlXPathCastBooleanToString(val->boolval);
2708 break;
2709 case XPATH_NUMBER:
2710 res = xmlXPathCastNumberToString(val->floatval);
2711 break;
2712 case XPATH_USERS:
2713 #ifdef LIBXML_XPTR_LOCS_ENABLED
2714 case XPATH_POINT:
2715 case XPATH_RANGE:
2716 case XPATH_LOCATIONSET:
2717 #endif /* LIBXML_XPTR_LOCS_ENABLED */
2718 TODO;
2719 break;
2721 xmlXPathReleaseObject(ctxt, val);
2722 if (res == NULL)
2723 return(xmlXPathCacheNewCString(ctxt, ""));
2724 return(xmlXPathCacheWrapString(ctxt, res));
2728 * xmlXPathCacheObjectCopy:
2729 * @ctxt: the XPath context
2730 * @val: the original object
2732 * This is the cached version of xmlXPathObjectCopy().
2733 * Acquire a copy of a given object
2735 * Returns a created or reused created object.
2737 static xmlXPathObjectPtr
2738 xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
2740 if (val == NULL)
2741 return(NULL);
2743 if (XP_HAS_CACHE(ctxt)) {
2744 switch (val->type) {
2745 case XPATH_NODESET:
2746 return(xmlXPathCacheWrapNodeSet(ctxt,
2747 xmlXPathNodeSetMerge(NULL, val->nodesetval)));
2748 case XPATH_STRING:
2749 return(xmlXPathCacheNewString(ctxt, val->stringval));
2750 case XPATH_BOOLEAN:
2751 return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
2752 case XPATH_NUMBER:
2753 return(xmlXPathCacheNewFloat(ctxt, val->floatval));
2754 default:
2755 break;
2758 return(xmlXPathObjectCopy(val));
2762 * xmlXPathCacheConvertBoolean:
2763 * @ctxt: the XPath context
2764 * @val: an XPath object
2766 * This is the cached version of xmlXPathConvertBoolean().
2767 * Converts an existing object to its boolean() equivalent
2769 * Returns a created or reused object, the old one is freed (or the operation
2770 * is done directly on @val)
2772 static xmlXPathObjectPtr
2773 xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2774 xmlXPathObjectPtr ret;
2776 if (val == NULL)
2777 return(xmlXPathCacheNewBoolean(ctxt, 0));
2778 if (val->type == XPATH_BOOLEAN)
2779 return(val);
2780 ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
2781 xmlXPathReleaseObject(ctxt, val);
2782 return(ret);
2786 * xmlXPathCacheConvertNumber:
2787 * @ctxt: the XPath context
2788 * @val: an XPath object
2790 * This is the cached version of xmlXPathConvertNumber().
2791 * Converts an existing object to its number() equivalent
2793 * Returns a created or reused object, the old one is freed (or the operation
2794 * is done directly on @val)
2796 static xmlXPathObjectPtr
2797 xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2798 xmlXPathObjectPtr ret;
2800 if (val == NULL)
2801 return(xmlXPathCacheNewFloat(ctxt, 0.0));
2802 if (val->type == XPATH_NUMBER)
2803 return(val);
2804 ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
2805 xmlXPathReleaseObject(ctxt, val);
2806 return(ret);
2809 /************************************************************************
2811 * Parser stacks related functions and macros *
2813 ************************************************************************/
2816 * valuePop:
2817 * @ctxt: an XPath evaluation context
2819 * Pops the top XPath object from the value stack
2821 * Returns the XPath object just removed
2823 xmlXPathObjectPtr
2824 valuePop(xmlXPathParserContextPtr ctxt)
2826 xmlXPathObjectPtr ret;
2828 if ((ctxt == NULL) || (ctxt->valueNr <= 0))
2829 return (NULL);
2831 ctxt->valueNr--;
2832 if (ctxt->valueNr > 0)
2833 ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
2834 else
2835 ctxt->value = NULL;
2836 ret = ctxt->valueTab[ctxt->valueNr];
2837 ctxt->valueTab[ctxt->valueNr] = NULL;
2838 return (ret);
2841 * valuePush:
2842 * @ctxt: an XPath evaluation context
2843 * @value: the XPath object
2845 * Pushes a new XPath object on top of the value stack. If value is NULL,
2846 * a memory error is recorded in the parser context.
2848 * Returns the number of items on the value stack, or -1 in case of error.
2850 * The object is destroyed in case of error.
2853 valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
2855 if (ctxt == NULL) return(-1);
2856 if (value == NULL) {
2858 * A NULL value typically indicates that a memory allocation failed,
2859 * so we set ctxt->error here to propagate the error.
2861 ctxt->error = XPATH_MEMORY_ERROR;
2862 return(-1);
2864 if (ctxt->valueNr >= ctxt->valueMax) {
2865 xmlXPathObjectPtr *tmp;
2867 if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) {
2868 xmlXPathPErrMemory(ctxt, "XPath stack depth limit reached\n");
2869 xmlXPathFreeObject(value);
2870 return (-1);
2872 tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
2873 2 * ctxt->valueMax *
2874 sizeof(ctxt->valueTab[0]));
2875 if (tmp == NULL) {
2876 xmlXPathPErrMemory(ctxt, "pushing value\n");
2877 xmlXPathFreeObject(value);
2878 return (-1);
2880 ctxt->valueMax *= 2;
2881 ctxt->valueTab = tmp;
2883 ctxt->valueTab[ctxt->valueNr] = value;
2884 ctxt->value = value;
2885 return (ctxt->valueNr++);
2889 * xmlXPathPopBoolean:
2890 * @ctxt: an XPath parser context
2892 * Pops a boolean from the stack, handling conversion if needed.
2893 * Check error with #xmlXPathCheckError.
2895 * Returns the boolean
2898 xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
2899 xmlXPathObjectPtr obj;
2900 int ret;
2902 obj = valuePop(ctxt);
2903 if (obj == NULL) {
2904 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2905 return(0);
2907 if (obj->type != XPATH_BOOLEAN)
2908 ret = xmlXPathCastToBoolean(obj);
2909 else
2910 ret = obj->boolval;
2911 xmlXPathReleaseObject(ctxt->context, obj);
2912 return(ret);
2916 * xmlXPathPopNumber:
2917 * @ctxt: an XPath parser context
2919 * Pops a number from the stack, handling conversion if needed.
2920 * Check error with #xmlXPathCheckError.
2922 * Returns the number
2924 double
2925 xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
2926 xmlXPathObjectPtr obj;
2927 double ret;
2929 obj = valuePop(ctxt);
2930 if (obj == NULL) {
2931 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2932 return(0);
2934 if (obj->type != XPATH_NUMBER)
2935 ret = xmlXPathCastToNumber(obj);
2936 else
2937 ret = obj->floatval;
2938 xmlXPathReleaseObject(ctxt->context, obj);
2939 return(ret);
2943 * xmlXPathPopString:
2944 * @ctxt: an XPath parser context
2946 * Pops a string from the stack, handling conversion if needed.
2947 * Check error with #xmlXPathCheckError.
2949 * Returns the string
2951 xmlChar *
2952 xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
2953 xmlXPathObjectPtr obj;
2954 xmlChar * ret;
2956 obj = valuePop(ctxt);
2957 if (obj == NULL) {
2958 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2959 return(NULL);
2961 ret = xmlXPathCastToString(obj); /* this does required strdup */
2962 /* TODO: needs refactoring somewhere else */
2963 if (obj->stringval == ret)
2964 obj->stringval = NULL;
2965 xmlXPathReleaseObject(ctxt->context, obj);
2966 return(ret);
2970 * xmlXPathPopNodeSet:
2971 * @ctxt: an XPath parser context
2973 * Pops a node-set from the stack, handling conversion if needed.
2974 * Check error with #xmlXPathCheckError.
2976 * Returns the node-set
2978 xmlNodeSetPtr
2979 xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
2980 xmlXPathObjectPtr obj;
2981 xmlNodeSetPtr ret;
2983 if (ctxt == NULL) return(NULL);
2984 if (ctxt->value == NULL) {
2985 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2986 return(NULL);
2988 if (!xmlXPathStackIsNodeSet(ctxt)) {
2989 xmlXPathSetTypeError(ctxt);
2990 return(NULL);
2992 obj = valuePop(ctxt);
2993 ret = obj->nodesetval;
2994 #if 0
2995 /* to fix memory leak of not clearing obj->user */
2996 if (obj->boolval && obj->user != NULL)
2997 xmlFreeNodeList((xmlNodePtr) obj->user);
2998 #endif
2999 obj->nodesetval = NULL;
3000 xmlXPathReleaseObject(ctxt->context, obj);
3001 return(ret);
3005 * xmlXPathPopExternal:
3006 * @ctxt: an XPath parser context
3008 * Pops an external object from the stack, handling conversion if needed.
3009 * Check error with #xmlXPathCheckError.
3011 * Returns the object
3013 void *
3014 xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
3015 xmlXPathObjectPtr obj;
3016 void * ret;
3018 if ((ctxt == NULL) || (ctxt->value == NULL)) {
3019 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
3020 return(NULL);
3022 if (ctxt->value->type != XPATH_USERS) {
3023 xmlXPathSetTypeError(ctxt);
3024 return(NULL);
3026 obj = valuePop(ctxt);
3027 ret = obj->user;
3028 obj->user = NULL;
3029 xmlXPathReleaseObject(ctxt->context, obj);
3030 return(ret);
3034 * Macros for accessing the content. Those should be used only by the parser,
3035 * and not exported.
3037 * Dirty macros, i.e. one need to make assumption on the context to use them
3039 * CUR_PTR return the current pointer to the xmlChar to be parsed.
3040 * CUR returns the current xmlChar value, i.e. a 8 bit value
3041 * in ISO-Latin or UTF-8.
3042 * This should be used internally by the parser
3043 * only to compare to ASCII values otherwise it would break when
3044 * running with UTF-8 encoding.
3045 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
3046 * to compare on ASCII based substring.
3047 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
3048 * strings within the parser.
3049 * CURRENT Returns the current char value, with the full decoding of
3050 * UTF-8 if we are using this mode. It returns an int.
3051 * NEXT Skip to the next character, this does the proper decoding
3052 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
3053 * It returns the pointer to the current xmlChar.
3056 #define CUR (*ctxt->cur)
3057 #define SKIP(val) ctxt->cur += (val)
3058 #define NXT(val) ctxt->cur[(val)]
3059 #define CUR_PTR ctxt->cur
3060 #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
3062 #define COPY_BUF(l,b,i,v) \
3063 if (l == 1) b[i++] = v; \
3064 else i += xmlCopyChar(l,&b[i],v)
3066 #define NEXTL(l) ctxt->cur += l
3068 #define SKIP_BLANKS \
3069 while (IS_BLANK_CH(*(ctxt->cur))) NEXT
3071 #define CURRENT (*ctxt->cur)
3072 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
3075 #ifndef DBL_DIG
3076 #define DBL_DIG 16
3077 #endif
3078 #ifndef DBL_EPSILON
3079 #define DBL_EPSILON 1E-9
3080 #endif
3082 #define UPPER_DOUBLE 1E9
3083 #define LOWER_DOUBLE 1E-5
3084 #define LOWER_DOUBLE_EXP 5
3086 #define INTEGER_DIGITS DBL_DIG
3087 #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
3088 #define EXPONENT_DIGITS (3 + 2)
3091 * xmlXPathFormatNumber:
3092 * @number: number to format
3093 * @buffer: output buffer
3094 * @buffersize: size of output buffer
3096 * Convert the number into a string representation.
3098 static void
3099 xmlXPathFormatNumber(double number, char buffer[], int buffersize)
3101 switch (xmlXPathIsInf(number)) {
3102 case 1:
3103 if (buffersize > (int)sizeof("Infinity"))
3104 snprintf(buffer, buffersize, "Infinity");
3105 break;
3106 case -1:
3107 if (buffersize > (int)sizeof("-Infinity"))
3108 snprintf(buffer, buffersize, "-Infinity");
3109 break;
3110 default:
3111 if (xmlXPathIsNaN(number)) {
3112 if (buffersize > (int)sizeof("NaN"))
3113 snprintf(buffer, buffersize, "NaN");
3114 } else if (number == 0) {
3115 /* Omit sign for negative zero. */
3116 snprintf(buffer, buffersize, "0");
3117 } else if ((number > INT_MIN) && (number < INT_MAX) &&
3118 (number == (int) number)) {
3119 char work[30];
3120 char *ptr, *cur;
3121 int value = (int) number;
3123 ptr = &buffer[0];
3124 if (value == 0) {
3125 *ptr++ = '0';
3126 } else {
3127 snprintf(work, 29, "%d", value);
3128 cur = &work[0];
3129 while ((*cur) && (ptr - buffer < buffersize)) {
3130 *ptr++ = *cur++;
3133 if (ptr - buffer < buffersize) {
3134 *ptr = 0;
3135 } else if (buffersize > 0) {
3136 ptr--;
3137 *ptr = 0;
3139 } else {
3141 For the dimension of work,
3142 DBL_DIG is number of significant digits
3143 EXPONENT is only needed for "scientific notation"
3144 3 is sign, decimal point, and terminating zero
3145 LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
3146 Note that this dimension is slightly (a few characters)
3147 larger than actually necessary.
3149 char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
3150 int integer_place, fraction_place;
3151 char *ptr;
3152 char *after_fraction;
3153 double absolute_value;
3154 int size;
3156 absolute_value = fabs(number);
3159 * First choose format - scientific or regular floating point.
3160 * In either case, result is in work, and after_fraction points
3161 * just past the fractional part.
3163 if ( ((absolute_value > UPPER_DOUBLE) ||
3164 (absolute_value < LOWER_DOUBLE)) &&
3165 (absolute_value != 0.0) ) {
3166 /* Use scientific notation */
3167 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
3168 fraction_place = DBL_DIG - 1;
3169 size = snprintf(work, sizeof(work),"%*.*e",
3170 integer_place, fraction_place, number);
3171 while ((size > 0) && (work[size] != 'e')) size--;
3174 else {
3175 /* Use regular notation */
3176 if (absolute_value > 0.0) {
3177 integer_place = (int)log10(absolute_value);
3178 if (integer_place > 0)
3179 fraction_place = DBL_DIG - integer_place - 1;
3180 else
3181 fraction_place = DBL_DIG - integer_place;
3182 } else {
3183 fraction_place = 1;
3185 size = snprintf(work, sizeof(work), "%0.*f",
3186 fraction_place, number);
3189 /* Remove leading spaces sometimes inserted by snprintf */
3190 while (work[0] == ' ') {
3191 for (ptr = &work[0];(ptr[0] = ptr[1]);ptr++);
3192 size--;
3195 /* Remove fractional trailing zeroes */
3196 after_fraction = work + size;
3197 ptr = after_fraction;
3198 while (*(--ptr) == '0')
3200 if (*ptr != '.')
3201 ptr++;
3202 while ((*ptr++ = *after_fraction++) != 0);
3204 /* Finally copy result back to caller */
3205 size = strlen(work) + 1;
3206 if (size > buffersize) {
3207 work[buffersize - 1] = 0;
3208 size = buffersize;
3210 memmove(buffer, work, size);
3212 break;
3217 /************************************************************************
3219 * Routines to handle NodeSets *
3221 ************************************************************************/
3224 * xmlXPathOrderDocElems:
3225 * @doc: an input document
3227 * Call this routine to speed up XPath computation on static documents.
3228 * This stamps all the element nodes with the document order
3229 * Like for line information, the order is kept in the element->content
3230 * field, the value stored is actually - the node number (starting at -1)
3231 * to be able to differentiate from line numbers.
3233 * Returns the number of elements found in the document or -1 in case
3234 * of error.
3236 long
3237 xmlXPathOrderDocElems(xmlDocPtr doc) {
3238 ptrdiff_t count = 0;
3239 xmlNodePtr cur;
3241 if (doc == NULL)
3242 return(-1);
3243 cur = doc->children;
3244 while (cur != NULL) {
3245 if (cur->type == XML_ELEMENT_NODE) {
3246 cur->content = (void *) (-(++count));
3247 if (cur->children != NULL) {
3248 cur = cur->children;
3249 continue;
3252 if (cur->next != NULL) {
3253 cur = cur->next;
3254 continue;
3256 do {
3257 cur = cur->parent;
3258 if (cur == NULL)
3259 break;
3260 if (cur == (xmlNodePtr) doc) {
3261 cur = NULL;
3262 break;
3264 if (cur->next != NULL) {
3265 cur = cur->next;
3266 break;
3268 } while (cur != NULL);
3270 return(count);
3274 * xmlXPathCmpNodes:
3275 * @node1: the first node
3276 * @node2: the second node
3278 * Compare two nodes w.r.t document order
3280 * Returns -2 in case of error 1 if first point < second point, 0 if
3281 * it's the same node, -1 otherwise
3284 xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
3285 int depth1, depth2;
3286 int attr1 = 0, attr2 = 0;
3287 xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
3288 xmlNodePtr cur, root;
3290 if ((node1 == NULL) || (node2 == NULL))
3291 return(-2);
3293 * a couple of optimizations which will avoid computations in most cases
3295 if (node1 == node2) /* trivial case */
3296 return(0);
3297 if (node1->type == XML_ATTRIBUTE_NODE) {
3298 attr1 = 1;
3299 attrNode1 = node1;
3300 node1 = node1->parent;
3302 if (node2->type == XML_ATTRIBUTE_NODE) {
3303 attr2 = 1;
3304 attrNode2 = node2;
3305 node2 = node2->parent;
3307 if (node1 == node2) {
3308 if (attr1 == attr2) {
3309 /* not required, but we keep attributes in order */
3310 if (attr1 != 0) {
3311 cur = attrNode2->prev;
3312 while (cur != NULL) {
3313 if (cur == attrNode1)
3314 return (1);
3315 cur = cur->prev;
3317 return (-1);
3319 return(0);
3321 if (attr2 == 1)
3322 return(1);
3323 return(-1);
3325 if ((node1->type == XML_NAMESPACE_DECL) ||
3326 (node2->type == XML_NAMESPACE_DECL))
3327 return(1);
3328 if (node1 == node2->prev)
3329 return(1);
3330 if (node1 == node2->next)
3331 return(-1);
3334 * Speedup using document order if available.
3336 if ((node1->type == XML_ELEMENT_NODE) &&
3337 (node2->type == XML_ELEMENT_NODE) &&
3338 (0 > (ptrdiff_t) node1->content) &&
3339 (0 > (ptrdiff_t) node2->content) &&
3340 (node1->doc == node2->doc)) {
3341 ptrdiff_t l1, l2;
3343 l1 = -((ptrdiff_t) node1->content);
3344 l2 = -((ptrdiff_t) node2->content);
3345 if (l1 < l2)
3346 return(1);
3347 if (l1 > l2)
3348 return(-1);
3352 * compute depth to root
3354 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
3355 if (cur->parent == node1)
3356 return(1);
3357 depth2++;
3359 root = cur;
3360 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
3361 if (cur->parent == node2)
3362 return(-1);
3363 depth1++;
3366 * Distinct document (or distinct entities :-( ) case.
3368 if (root != cur) {
3369 return(-2);
3372 * get the nearest common ancestor.
3374 while (depth1 > depth2) {
3375 depth1--;
3376 node1 = node1->parent;
3378 while (depth2 > depth1) {
3379 depth2--;
3380 node2 = node2->parent;
3382 while (node1->parent != node2->parent) {
3383 node1 = node1->parent;
3384 node2 = node2->parent;
3385 /* should not happen but just in case ... */
3386 if ((node1 == NULL) || (node2 == NULL))
3387 return(-2);
3390 * Find who's first.
3392 if (node1 == node2->prev)
3393 return(1);
3394 if (node1 == node2->next)
3395 return(-1);
3397 * Speedup using document order if available.
3399 if ((node1->type == XML_ELEMENT_NODE) &&
3400 (node2->type == XML_ELEMENT_NODE) &&
3401 (0 > (ptrdiff_t) node1->content) &&
3402 (0 > (ptrdiff_t) node2->content) &&
3403 (node1->doc == node2->doc)) {
3404 ptrdiff_t l1, l2;
3406 l1 = -((ptrdiff_t) node1->content);
3407 l2 = -((ptrdiff_t) node2->content);
3408 if (l1 < l2)
3409 return(1);
3410 if (l1 > l2)
3411 return(-1);
3414 for (cur = node1->next;cur != NULL;cur = cur->next)
3415 if (cur == node2)
3416 return(1);
3417 return(-1); /* assume there is no sibling list corruption */
3421 * xmlXPathNodeSetSort:
3422 * @set: the node set
3424 * Sort the node set in document order
3426 void
3427 xmlXPathNodeSetSort(xmlNodeSetPtr set) {
3428 #ifndef WITH_TIM_SORT
3429 int i, j, incr, len;
3430 xmlNodePtr tmp;
3431 #endif
3433 if (set == NULL)
3434 return;
3436 #ifndef WITH_TIM_SORT
3438 * Use the old Shell's sort implementation to sort the node-set
3439 * Timsort ought to be quite faster
3441 len = set->nodeNr;
3442 for (incr = len / 2; incr > 0; incr /= 2) {
3443 for (i = incr; i < len; i++) {
3444 j = i - incr;
3445 while (j >= 0) {
3446 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
3447 if (xmlXPathCmpNodesExt(set->nodeTab[j],
3448 set->nodeTab[j + incr]) == -1)
3449 #else
3450 if (xmlXPathCmpNodes(set->nodeTab[j],
3451 set->nodeTab[j + incr]) == -1)
3452 #endif
3454 tmp = set->nodeTab[j];
3455 set->nodeTab[j] = set->nodeTab[j + incr];
3456 set->nodeTab[j + incr] = tmp;
3457 j -= incr;
3458 } else
3459 break;
3463 #else /* WITH_TIM_SORT */
3464 libxml_domnode_tim_sort(set->nodeTab, set->nodeNr);
3465 #endif /* WITH_TIM_SORT */
3468 #define XML_NODESET_DEFAULT 10
3470 * xmlXPathNodeSetDupNs:
3471 * @node: the parent node of the namespace XPath node
3472 * @ns: the libxml namespace declaration node.
3474 * Namespace node in libxml don't match the XPath semantic. In a node set
3475 * the namespace nodes are duplicated and the next pointer is set to the
3476 * parent node in the XPath semantic.
3478 * Returns the newly created object.
3480 static xmlNodePtr
3481 xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
3482 xmlNsPtr cur;
3484 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3485 return(NULL);
3486 if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
3487 return((xmlNodePtr) ns);
3490 * Allocate a new Namespace and fill the fields.
3492 cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
3493 if (cur == NULL) {
3494 xmlXPathErrMemory(NULL, "duplicating namespace\n");
3495 return(NULL);
3497 memset(cur, 0, sizeof(xmlNs));
3498 cur->type = XML_NAMESPACE_DECL;
3499 if (ns->href != NULL)
3500 cur->href = xmlStrdup(ns->href);
3501 if (ns->prefix != NULL)
3502 cur->prefix = xmlStrdup(ns->prefix);
3503 cur->next = (xmlNsPtr) node;
3504 return((xmlNodePtr) cur);
3508 * xmlXPathNodeSetFreeNs:
3509 * @ns: the XPath namespace node found in a nodeset.
3511 * Namespace nodes in libxml don't match the XPath semantic. In a node set
3512 * the namespace nodes are duplicated and the next pointer is set to the
3513 * parent node in the XPath semantic. Check if such a node needs to be freed
3515 void
3516 xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
3517 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3518 return;
3520 if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
3521 if (ns->href != NULL)
3522 xmlFree((xmlChar *)ns->href);
3523 if (ns->prefix != NULL)
3524 xmlFree((xmlChar *)ns->prefix);
3525 xmlFree(ns);
3530 * xmlXPathNodeSetCreate:
3531 * @val: an initial xmlNodePtr, or NULL
3533 * Create a new xmlNodeSetPtr of type double and of value @val
3535 * Returns the newly created object.
3537 xmlNodeSetPtr
3538 xmlXPathNodeSetCreate(xmlNodePtr val) {
3539 xmlNodeSetPtr ret;
3541 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3542 if (ret == NULL) {
3543 xmlXPathErrMemory(NULL, "creating nodeset\n");
3544 return(NULL);
3546 memset(ret, 0 , sizeof(xmlNodeSet));
3547 if (val != NULL) {
3548 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3549 sizeof(xmlNodePtr));
3550 if (ret->nodeTab == NULL) {
3551 xmlXPathErrMemory(NULL, "creating nodeset\n");
3552 xmlFree(ret);
3553 return(NULL);
3555 memset(ret->nodeTab, 0 ,
3556 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3557 ret->nodeMax = XML_NODESET_DEFAULT;
3558 if (val->type == XML_NAMESPACE_DECL) {
3559 xmlNsPtr ns = (xmlNsPtr) val;
3560 xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3562 if (nsNode == NULL) {
3563 xmlXPathFreeNodeSet(ret);
3564 return(NULL);
3566 ret->nodeTab[ret->nodeNr++] = nsNode;
3567 } else
3568 ret->nodeTab[ret->nodeNr++] = val;
3570 return(ret);
3574 * xmlXPathNodeSetContains:
3575 * @cur: the node-set
3576 * @val: the node
3578 * checks whether @cur contains @val
3580 * Returns true (1) if @cur contains @val, false (0) otherwise
3583 xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
3584 int i;
3586 if ((cur == NULL) || (val == NULL)) return(0);
3587 if (val->type == XML_NAMESPACE_DECL) {
3588 for (i = 0; i < cur->nodeNr; i++) {
3589 if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
3590 xmlNsPtr ns1, ns2;
3592 ns1 = (xmlNsPtr) val;
3593 ns2 = (xmlNsPtr) cur->nodeTab[i];
3594 if (ns1 == ns2)
3595 return(1);
3596 if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
3597 (xmlStrEqual(ns1->prefix, ns2->prefix)))
3598 return(1);
3601 } else {
3602 for (i = 0; i < cur->nodeNr; i++) {
3603 if (cur->nodeTab[i] == val)
3604 return(1);
3607 return(0);
3611 * xmlXPathNodeSetAddNs:
3612 * @cur: the initial node set
3613 * @node: the hosting node
3614 * @ns: a the namespace node
3616 * add a new namespace node to an existing NodeSet
3618 * Returns 0 in case of success and -1 in case of error
3621 xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
3622 int i;
3623 xmlNodePtr nsNode;
3625 if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
3626 (ns->type != XML_NAMESPACE_DECL) ||
3627 (node->type != XML_ELEMENT_NODE))
3628 return(-1);
3630 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3632 * prevent duplicates
3634 for (i = 0;i < cur->nodeNr;i++) {
3635 if ((cur->nodeTab[i] != NULL) &&
3636 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
3637 (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
3638 (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
3639 return(0);
3643 * grow the nodeTab if needed
3645 if (cur->nodeMax == 0) {
3646 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3647 sizeof(xmlNodePtr));
3648 if (cur->nodeTab == NULL) {
3649 xmlXPathErrMemory(NULL, "growing nodeset\n");
3650 return(-1);
3652 memset(cur->nodeTab, 0 ,
3653 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3654 cur->nodeMax = XML_NODESET_DEFAULT;
3655 } else if (cur->nodeNr == cur->nodeMax) {
3656 xmlNodePtr *temp;
3658 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3659 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3660 return(-1);
3662 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3663 sizeof(xmlNodePtr));
3664 if (temp == NULL) {
3665 xmlXPathErrMemory(NULL, "growing nodeset\n");
3666 return(-1);
3668 cur->nodeMax *= 2;
3669 cur->nodeTab = temp;
3671 nsNode = xmlXPathNodeSetDupNs(node, ns);
3672 if(nsNode == NULL)
3673 return(-1);
3674 cur->nodeTab[cur->nodeNr++] = nsNode;
3675 return(0);
3679 * xmlXPathNodeSetAdd:
3680 * @cur: the initial node set
3681 * @val: a new xmlNodePtr
3683 * add a new xmlNodePtr to an existing NodeSet
3685 * Returns 0 in case of success, and -1 in case of error
3688 xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
3689 int i;
3691 if ((cur == NULL) || (val == NULL)) return(-1);
3693 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3695 * prevent duplicates
3697 for (i = 0;i < cur->nodeNr;i++)
3698 if (cur->nodeTab[i] == val) return(0);
3701 * grow the nodeTab if needed
3703 if (cur->nodeMax == 0) {
3704 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3705 sizeof(xmlNodePtr));
3706 if (cur->nodeTab == NULL) {
3707 xmlXPathErrMemory(NULL, "growing nodeset\n");
3708 return(-1);
3710 memset(cur->nodeTab, 0 ,
3711 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3712 cur->nodeMax = XML_NODESET_DEFAULT;
3713 } else if (cur->nodeNr == cur->nodeMax) {
3714 xmlNodePtr *temp;
3716 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3717 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3718 return(-1);
3720 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3721 sizeof(xmlNodePtr));
3722 if (temp == NULL) {
3723 xmlXPathErrMemory(NULL, "growing nodeset\n");
3724 return(-1);
3726 cur->nodeMax *= 2;
3727 cur->nodeTab = temp;
3729 if (val->type == XML_NAMESPACE_DECL) {
3730 xmlNsPtr ns = (xmlNsPtr) val;
3731 xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3733 if (nsNode == NULL)
3734 return(-1);
3735 cur->nodeTab[cur->nodeNr++] = nsNode;
3736 } else
3737 cur->nodeTab[cur->nodeNr++] = val;
3738 return(0);
3742 * xmlXPathNodeSetAddUnique:
3743 * @cur: the initial node set
3744 * @val: a new xmlNodePtr
3746 * add a new xmlNodePtr to an existing NodeSet, optimized version
3747 * when we are sure the node is not already in the set.
3749 * Returns 0 in case of success and -1 in case of failure
3752 xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
3753 if ((cur == NULL) || (val == NULL)) return(-1);
3755 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3757 * grow the nodeTab if needed
3759 if (cur->nodeMax == 0) {
3760 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3761 sizeof(xmlNodePtr));
3762 if (cur->nodeTab == NULL) {
3763 xmlXPathErrMemory(NULL, "growing nodeset\n");
3764 return(-1);
3766 memset(cur->nodeTab, 0 ,
3767 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3768 cur->nodeMax = XML_NODESET_DEFAULT;
3769 } else if (cur->nodeNr == cur->nodeMax) {
3770 xmlNodePtr *temp;
3772 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3773 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3774 return(-1);
3776 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3777 sizeof(xmlNodePtr));
3778 if (temp == NULL) {
3779 xmlXPathErrMemory(NULL, "growing nodeset\n");
3780 return(-1);
3782 cur->nodeTab = temp;
3783 cur->nodeMax *= 2;
3785 if (val->type == XML_NAMESPACE_DECL) {
3786 xmlNsPtr ns = (xmlNsPtr) val;
3787 xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3789 if (nsNode == NULL)
3790 return(-1);
3791 cur->nodeTab[cur->nodeNr++] = nsNode;
3792 } else
3793 cur->nodeTab[cur->nodeNr++] = val;
3794 return(0);
3798 * xmlXPathNodeSetMerge:
3799 * @val1: the first NodeSet or NULL
3800 * @val2: the second NodeSet
3802 * Merges two nodesets, all nodes from @val2 are added to @val1
3803 * if @val1 is NULL, a new set is created and copied from @val2
3805 * Returns @val1 once extended or NULL in case of error.
3807 * Frees @val1 in case of error.
3809 xmlNodeSetPtr
3810 xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
3811 int i, j, initNr, skip;
3812 xmlNodePtr n1, n2;
3814 if (val2 == NULL) return(val1);
3815 if (val1 == NULL) {
3816 val1 = xmlXPathNodeSetCreate(NULL);
3817 if (val1 == NULL)
3818 return (NULL);
3821 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3822 initNr = val1->nodeNr;
3824 for (i = 0;i < val2->nodeNr;i++) {
3825 n2 = val2->nodeTab[i];
3827 * check against duplicates
3829 skip = 0;
3830 for (j = 0; j < initNr; j++) {
3831 n1 = val1->nodeTab[j];
3832 if (n1 == n2) {
3833 skip = 1;
3834 break;
3835 } else if ((n1->type == XML_NAMESPACE_DECL) &&
3836 (n2->type == XML_NAMESPACE_DECL)) {
3837 if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3838 (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3839 ((xmlNsPtr) n2)->prefix)))
3841 skip = 1;
3842 break;
3846 if (skip)
3847 continue;
3850 * grow the nodeTab if needed
3852 if (val1->nodeMax == 0) {
3853 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3854 sizeof(xmlNodePtr));
3855 if (val1->nodeTab == NULL) {
3856 xmlXPathErrMemory(NULL, "merging nodeset\n");
3857 goto error;
3859 memset(val1->nodeTab, 0 ,
3860 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3861 val1->nodeMax = XML_NODESET_DEFAULT;
3862 } else if (val1->nodeNr == val1->nodeMax) {
3863 xmlNodePtr *temp;
3865 if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3866 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
3867 goto error;
3869 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
3870 sizeof(xmlNodePtr));
3871 if (temp == NULL) {
3872 xmlXPathErrMemory(NULL, "merging nodeset\n");
3873 goto error;
3875 val1->nodeTab = temp;
3876 val1->nodeMax *= 2;
3878 if (n2->type == XML_NAMESPACE_DECL) {
3879 xmlNsPtr ns = (xmlNsPtr) n2;
3880 xmlNodePtr nsNode = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3882 if (nsNode == NULL)
3883 goto error;
3884 val1->nodeTab[val1->nodeNr++] = nsNode;
3885 } else
3886 val1->nodeTab[val1->nodeNr++] = n2;
3889 return(val1);
3891 error:
3892 xmlXPathFreeNodeSet(val1);
3893 return(NULL);
3898 * xmlXPathNodeSetMergeAndClear:
3899 * @set1: the first NodeSet or NULL
3900 * @set2: the second NodeSet
3902 * Merges two nodesets, all nodes from @set2 are added to @set1.
3903 * Checks for duplicate nodes. Clears set2.
3905 * Returns @set1 once extended or NULL in case of error.
3907 * Frees @set1 in case of error.
3909 static xmlNodeSetPtr
3910 xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
3913 int i, j, initNbSet1;
3914 xmlNodePtr n1, n2;
3916 initNbSet1 = set1->nodeNr;
3917 for (i = 0;i < set2->nodeNr;i++) {
3918 n2 = set2->nodeTab[i];
3920 * Skip duplicates.
3922 for (j = 0; j < initNbSet1; j++) {
3923 n1 = set1->nodeTab[j];
3924 if (n1 == n2) {
3925 goto skip_node;
3926 } else if ((n1->type == XML_NAMESPACE_DECL) &&
3927 (n2->type == XML_NAMESPACE_DECL))
3929 if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3930 (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3931 ((xmlNsPtr) n2)->prefix)))
3934 * Free the namespace node.
3936 xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
3937 goto skip_node;
3942 * grow the nodeTab if needed
3944 if (set1->nodeMax == 0) {
3945 set1->nodeTab = (xmlNodePtr *) xmlMalloc(
3946 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3947 if (set1->nodeTab == NULL) {
3948 xmlXPathErrMemory(NULL, "merging nodeset\n");
3949 goto error;
3951 memset(set1->nodeTab, 0,
3952 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3953 set1->nodeMax = XML_NODESET_DEFAULT;
3954 } else if (set1->nodeNr >= set1->nodeMax) {
3955 xmlNodePtr *temp;
3957 if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3958 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
3959 goto error;
3961 temp = (xmlNodePtr *) xmlRealloc(
3962 set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
3963 if (temp == NULL) {
3964 xmlXPathErrMemory(NULL, "merging nodeset\n");
3965 goto error;
3967 set1->nodeTab = temp;
3968 set1->nodeMax *= 2;
3970 set1->nodeTab[set1->nodeNr++] = n2;
3971 skip_node:
3972 set2->nodeTab[i] = NULL;
3975 set2->nodeNr = 0;
3976 return(set1);
3978 error:
3979 xmlXPathFreeNodeSet(set1);
3980 xmlXPathNodeSetClear(set2, 1);
3981 return(NULL);
3985 * xmlXPathNodeSetMergeAndClearNoDupls:
3986 * @set1: the first NodeSet or NULL
3987 * @set2: the second NodeSet
3989 * Merges two nodesets, all nodes from @set2 are added to @set1.
3990 * Doesn't check for duplicate nodes. Clears set2.
3992 * Returns @set1 once extended or NULL in case of error.
3994 * Frees @set1 in case of error.
3996 static xmlNodeSetPtr
3997 xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
4000 int i;
4001 xmlNodePtr n2;
4003 for (i = 0;i < set2->nodeNr;i++) {
4004 n2 = set2->nodeTab[i];
4005 if (set1->nodeMax == 0) {
4006 set1->nodeTab = (xmlNodePtr *) xmlMalloc(
4007 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
4008 if (set1->nodeTab == NULL) {
4009 xmlXPathErrMemory(NULL, "merging nodeset\n");
4010 goto error;
4012 memset(set1->nodeTab, 0,
4013 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
4014 set1->nodeMax = XML_NODESET_DEFAULT;
4015 } else if (set1->nodeNr >= set1->nodeMax) {
4016 xmlNodePtr *temp;
4018 if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
4019 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
4020 goto error;
4022 temp = (xmlNodePtr *) xmlRealloc(
4023 set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
4024 if (temp == NULL) {
4025 xmlXPathErrMemory(NULL, "merging nodeset\n");
4026 goto error;
4028 set1->nodeTab = temp;
4029 set1->nodeMax *= 2;
4031 set1->nodeTab[set1->nodeNr++] = n2;
4032 set2->nodeTab[i] = NULL;
4035 set2->nodeNr = 0;
4036 return(set1);
4038 error:
4039 xmlXPathFreeNodeSet(set1);
4040 xmlXPathNodeSetClear(set2, 1);
4041 return(NULL);
4045 * xmlXPathNodeSetDel:
4046 * @cur: the initial node set
4047 * @val: an xmlNodePtr
4049 * Removes an xmlNodePtr from an existing NodeSet
4051 void
4052 xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
4053 int i;
4055 if (cur == NULL) return;
4056 if (val == NULL) return;
4059 * find node in nodeTab
4061 for (i = 0;i < cur->nodeNr;i++)
4062 if (cur->nodeTab[i] == val) break;
4064 if (i >= cur->nodeNr) { /* not found */
4065 #ifdef DEBUG
4066 xmlGenericError(xmlGenericErrorContext,
4067 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
4068 val->name);
4069 #endif
4070 return;
4072 if ((cur->nodeTab[i] != NULL) &&
4073 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
4074 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
4075 cur->nodeNr--;
4076 for (;i < cur->nodeNr;i++)
4077 cur->nodeTab[i] = cur->nodeTab[i + 1];
4078 cur->nodeTab[cur->nodeNr] = NULL;
4082 * xmlXPathNodeSetRemove:
4083 * @cur: the initial node set
4084 * @val: the index to remove
4086 * Removes an entry from an existing NodeSet list.
4088 void
4089 xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
4090 if (cur == NULL) return;
4091 if (val >= cur->nodeNr) return;
4092 if ((cur->nodeTab[val] != NULL) &&
4093 (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
4094 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
4095 cur->nodeNr--;
4096 for (;val < cur->nodeNr;val++)
4097 cur->nodeTab[val] = cur->nodeTab[val + 1];
4098 cur->nodeTab[cur->nodeNr] = NULL;
4102 * xmlXPathFreeNodeSet:
4103 * @obj: the xmlNodeSetPtr to free
4105 * Free the NodeSet compound (not the actual nodes !).
4107 void
4108 xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
4109 if (obj == NULL) return;
4110 if (obj->nodeTab != NULL) {
4111 int i;
4113 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4114 for (i = 0;i < obj->nodeNr;i++)
4115 if ((obj->nodeTab[i] != NULL) &&
4116 (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
4117 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4118 xmlFree(obj->nodeTab);
4120 xmlFree(obj);
4124 * xmlXPathNodeSetClearFromPos:
4125 * @set: the node set to be cleared
4126 * @pos: the start position to clear from
4128 * Clears the list from temporary XPath objects (e.g. namespace nodes
4129 * are feed) starting with the entry at @pos, but does *not* free the list
4130 * itself. Sets the length of the list to @pos.
4132 static void
4133 xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
4135 if ((set == NULL) || (pos >= set->nodeNr))
4136 return;
4137 else if ((hasNsNodes)) {
4138 int i;
4139 xmlNodePtr node;
4141 for (i = pos; i < set->nodeNr; i++) {
4142 node = set->nodeTab[i];
4143 if ((node != NULL) &&
4144 (node->type == XML_NAMESPACE_DECL))
4145 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4148 set->nodeNr = pos;
4152 * xmlXPathNodeSetClear:
4153 * @set: the node set to clear
4155 * Clears the list from all temporary XPath objects (e.g. namespace nodes
4156 * are feed), but does *not* free the list itself. Sets the length of the
4157 * list to 0.
4159 static void
4160 xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
4162 xmlXPathNodeSetClearFromPos(set, 0, hasNsNodes);
4166 * xmlXPathNodeSetKeepLast:
4167 * @set: the node set to be cleared
4169 * Move the last node to the first position and clear temporary XPath objects
4170 * (e.g. namespace nodes) from all other nodes. Sets the length of the list
4171 * to 1.
4173 static void
4174 xmlXPathNodeSetKeepLast(xmlNodeSetPtr set)
4176 int i;
4177 xmlNodePtr node;
4179 if ((set == NULL) || (set->nodeNr <= 1))
4180 return;
4181 for (i = 0; i < set->nodeNr - 1; i++) {
4182 node = set->nodeTab[i];
4183 if ((node != NULL) &&
4184 (node->type == XML_NAMESPACE_DECL))
4185 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4187 set->nodeTab[0] = set->nodeTab[set->nodeNr-1];
4188 set->nodeNr = 1;
4192 * xmlXPathFreeValueTree:
4193 * @obj: the xmlNodeSetPtr to free
4195 * Free the NodeSet compound and the actual tree, this is different
4196 * from xmlXPathFreeNodeSet()
4198 static void
4199 xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
4200 int i;
4202 if (obj == NULL) return;
4204 if (obj->nodeTab != NULL) {
4205 for (i = 0;i < obj->nodeNr;i++) {
4206 if (obj->nodeTab[i] != NULL) {
4207 if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
4208 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4209 } else {
4210 xmlFreeNodeList(obj->nodeTab[i]);
4214 xmlFree(obj->nodeTab);
4216 xmlFree(obj);
4219 #if defined(DEBUG) || defined(DEBUG_STEP)
4221 * xmlGenericErrorContextNodeSet:
4222 * @output: a FILE * for the output
4223 * @obj: the xmlNodeSetPtr to display
4225 * Quick display of a NodeSet
4227 void
4228 xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
4229 int i;
4231 if (output == NULL) output = xmlGenericErrorContext;
4232 if (obj == NULL) {
4233 fprintf(output, "NodeSet == NULL !\n");
4234 return;
4236 if (obj->nodeNr == 0) {
4237 fprintf(output, "NodeSet is empty\n");
4238 return;
4240 if (obj->nodeTab == NULL) {
4241 fprintf(output, " nodeTab == NULL !\n");
4242 return;
4244 for (i = 0; i < obj->nodeNr; i++) {
4245 if (obj->nodeTab[i] == NULL) {
4246 fprintf(output, " NULL !\n");
4247 return;
4249 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
4250 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
4251 fprintf(output, " /");
4252 else if (obj->nodeTab[i]->name == NULL)
4253 fprintf(output, " noname!");
4254 else fprintf(output, " %s", obj->nodeTab[i]->name);
4256 fprintf(output, "\n");
4258 #endif
4261 * xmlXPathNewNodeSet:
4262 * @val: the NodePtr value
4264 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4265 * it with the single Node @val
4267 * Returns the newly created object.
4269 xmlXPathObjectPtr
4270 xmlXPathNewNodeSet(xmlNodePtr val) {
4271 xmlXPathObjectPtr ret;
4273 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4274 if (ret == NULL) {
4275 xmlXPathErrMemory(NULL, "creating nodeset\n");
4276 return(NULL);
4278 memset(ret, 0 , sizeof(xmlXPathObject));
4279 ret->type = XPATH_NODESET;
4280 ret->boolval = 0;
4281 /* TODO: Check memory error. */
4282 ret->nodesetval = xmlXPathNodeSetCreate(val);
4283 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4284 #ifdef XP_DEBUG_OBJ_USAGE
4285 xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4286 #endif
4287 return(ret);
4291 * xmlXPathNewValueTree:
4292 * @val: the NodePtr value
4294 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
4295 * it with the tree root @val
4297 * Returns the newly created object.
4299 xmlXPathObjectPtr
4300 xmlXPathNewValueTree(xmlNodePtr val) {
4301 xmlXPathObjectPtr ret;
4303 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4304 if (ret == NULL) {
4305 xmlXPathErrMemory(NULL, "creating result value tree\n");
4306 return(NULL);
4308 memset(ret, 0 , sizeof(xmlXPathObject));
4309 ret->type = XPATH_XSLT_TREE;
4310 ret->boolval = 1;
4311 ret->user = (void *) val;
4312 ret->nodesetval = xmlXPathNodeSetCreate(val);
4313 #ifdef XP_DEBUG_OBJ_USAGE
4314 xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
4315 #endif
4316 return(ret);
4320 * xmlXPathNewNodeSetList:
4321 * @val: an existing NodeSet
4323 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4324 * it with the Nodeset @val
4326 * Returns the newly created object.
4328 xmlXPathObjectPtr
4329 xmlXPathNewNodeSetList(xmlNodeSetPtr val)
4331 xmlXPathObjectPtr ret;
4332 int i;
4334 if (val == NULL)
4335 ret = NULL;
4336 else if (val->nodeTab == NULL)
4337 ret = xmlXPathNewNodeSet(NULL);
4338 else {
4339 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
4340 if (ret) {
4341 for (i = 1; i < val->nodeNr; ++i) {
4342 /* TODO: Propagate memory error. */
4343 if (xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i])
4344 < 0) break;
4349 return (ret);
4353 * xmlXPathWrapNodeSet:
4354 * @val: the NodePtr value
4356 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
4358 * Returns the newly created object.
4360 * In case of error the node set is destroyed and NULL is returned.
4362 xmlXPathObjectPtr
4363 xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
4364 xmlXPathObjectPtr ret;
4366 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4367 if (ret == NULL) {
4368 xmlXPathErrMemory(NULL, "creating node set object\n");
4369 xmlXPathFreeNodeSet(val);
4370 return(NULL);
4372 memset(ret, 0 , sizeof(xmlXPathObject));
4373 ret->type = XPATH_NODESET;
4374 ret->nodesetval = val;
4375 #ifdef XP_DEBUG_OBJ_USAGE
4376 xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4377 #endif
4378 return(ret);
4382 * xmlXPathFreeNodeSetList:
4383 * @obj: an existing NodeSetList object
4385 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
4386 * the list contrary to xmlXPathFreeObject().
4388 void
4389 xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
4390 if (obj == NULL) return;
4391 #ifdef XP_DEBUG_OBJ_USAGE
4392 xmlXPathDebugObjUsageReleased(NULL, obj->type);
4393 #endif
4394 xmlFree(obj);
4398 * xmlXPathDifference:
4399 * @nodes1: a node-set
4400 * @nodes2: a node-set
4402 * Implements the EXSLT - Sets difference() function:
4403 * node-set set:difference (node-set, node-set)
4405 * Returns the difference between the two node sets, or nodes1 if
4406 * nodes2 is empty
4408 xmlNodeSetPtr
4409 xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4410 xmlNodeSetPtr ret;
4411 int i, l1;
4412 xmlNodePtr cur;
4414 if (xmlXPathNodeSetIsEmpty(nodes2))
4415 return(nodes1);
4417 /* TODO: Check memory error. */
4418 ret = xmlXPathNodeSetCreate(NULL);
4419 if (xmlXPathNodeSetIsEmpty(nodes1))
4420 return(ret);
4422 l1 = xmlXPathNodeSetGetLength(nodes1);
4424 for (i = 0; i < l1; i++) {
4425 cur = xmlXPathNodeSetItem(nodes1, i);
4426 if (!xmlXPathNodeSetContains(nodes2, cur)) {
4427 /* TODO: Propagate memory error. */
4428 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4429 break;
4432 return(ret);
4436 * xmlXPathIntersection:
4437 * @nodes1: a node-set
4438 * @nodes2: a node-set
4440 * Implements the EXSLT - Sets intersection() function:
4441 * node-set set:intersection (node-set, node-set)
4443 * Returns a node set comprising the nodes that are within both the
4444 * node sets passed as arguments
4446 xmlNodeSetPtr
4447 xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4448 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
4449 int i, l1;
4450 xmlNodePtr cur;
4452 if (ret == NULL)
4453 return(ret);
4454 if (xmlXPathNodeSetIsEmpty(nodes1))
4455 return(ret);
4456 if (xmlXPathNodeSetIsEmpty(nodes2))
4457 return(ret);
4459 l1 = xmlXPathNodeSetGetLength(nodes1);
4461 for (i = 0; i < l1; i++) {
4462 cur = xmlXPathNodeSetItem(nodes1, i);
4463 if (xmlXPathNodeSetContains(nodes2, cur)) {
4464 /* TODO: Propagate memory error. */
4465 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4466 break;
4469 return(ret);
4473 * xmlXPathDistinctSorted:
4474 * @nodes: a node-set, sorted by document order
4476 * Implements the EXSLT - Sets distinct() function:
4477 * node-set set:distinct (node-set)
4479 * Returns a subset of the nodes contained in @nodes, or @nodes if
4480 * it is empty
4482 xmlNodeSetPtr
4483 xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
4484 xmlNodeSetPtr ret;
4485 xmlHashTablePtr hash;
4486 int i, l;
4487 xmlChar * strval;
4488 xmlNodePtr cur;
4490 if (xmlXPathNodeSetIsEmpty(nodes))
4491 return(nodes);
4493 ret = xmlXPathNodeSetCreate(NULL);
4494 if (ret == NULL)
4495 return(ret);
4496 l = xmlXPathNodeSetGetLength(nodes);
4497 hash = xmlHashCreate (l);
4498 for (i = 0; i < l; i++) {
4499 cur = xmlXPathNodeSetItem(nodes, i);
4500 strval = xmlXPathCastNodeToString(cur);
4501 if (xmlHashLookup(hash, strval) == NULL) {
4502 if (xmlHashAddEntry(hash, strval, strval) < 0) {
4503 xmlFree(strval);
4504 goto error;
4506 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4507 goto error;
4508 } else {
4509 xmlFree(strval);
4512 xmlHashFree(hash, xmlHashDefaultDeallocator);
4513 return(ret);
4515 error:
4516 xmlHashFree(hash, xmlHashDefaultDeallocator);
4517 xmlXPathFreeNodeSet(ret);
4518 return(NULL);
4522 * xmlXPathDistinct:
4523 * @nodes: a node-set
4525 * Implements the EXSLT - Sets distinct() function:
4526 * node-set set:distinct (node-set)
4527 * @nodes is sorted by document order, then #exslSetsDistinctSorted
4528 * is called with the sorted node-set
4530 * Returns a subset of the nodes contained in @nodes, or @nodes if
4531 * it is empty
4533 xmlNodeSetPtr
4534 xmlXPathDistinct (xmlNodeSetPtr nodes) {
4535 if (xmlXPathNodeSetIsEmpty(nodes))
4536 return(nodes);
4538 xmlXPathNodeSetSort(nodes);
4539 return(xmlXPathDistinctSorted(nodes));
4543 * xmlXPathHasSameNodes:
4544 * @nodes1: a node-set
4545 * @nodes2: a node-set
4547 * Implements the EXSLT - Sets has-same-nodes function:
4548 * boolean set:has-same-node(node-set, node-set)
4550 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
4551 * otherwise
4554 xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4555 int i, l;
4556 xmlNodePtr cur;
4558 if (xmlXPathNodeSetIsEmpty(nodes1) ||
4559 xmlXPathNodeSetIsEmpty(nodes2))
4560 return(0);
4562 l = xmlXPathNodeSetGetLength(nodes1);
4563 for (i = 0; i < l; i++) {
4564 cur = xmlXPathNodeSetItem(nodes1, i);
4565 if (xmlXPathNodeSetContains(nodes2, cur))
4566 return(1);
4568 return(0);
4572 * xmlXPathNodeLeadingSorted:
4573 * @nodes: a node-set, sorted by document order
4574 * @node: a node
4576 * Implements the EXSLT - Sets leading() function:
4577 * node-set set:leading (node-set, node-set)
4579 * Returns the nodes in @nodes that precede @node in document order,
4580 * @nodes if @node is NULL or an empty node-set if @nodes
4581 * doesn't contain @node
4583 xmlNodeSetPtr
4584 xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4585 int i, l;
4586 xmlNodePtr cur;
4587 xmlNodeSetPtr ret;
4589 if (node == NULL)
4590 return(nodes);
4592 ret = xmlXPathNodeSetCreate(NULL);
4593 if (ret == NULL)
4594 return(ret);
4595 if (xmlXPathNodeSetIsEmpty(nodes) ||
4596 (!xmlXPathNodeSetContains(nodes, node)))
4597 return(ret);
4599 l = xmlXPathNodeSetGetLength(nodes);
4600 for (i = 0; i < l; i++) {
4601 cur = xmlXPathNodeSetItem(nodes, i);
4602 if (cur == node)
4603 break;
4604 /* TODO: Propagate memory error. */
4605 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4606 break;
4608 return(ret);
4612 * xmlXPathNodeLeading:
4613 * @nodes: a node-set
4614 * @node: a node
4616 * Implements the EXSLT - Sets leading() function:
4617 * node-set set:leading (node-set, node-set)
4618 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
4619 * is called.
4621 * Returns the nodes in @nodes that precede @node in document order,
4622 * @nodes if @node is NULL or an empty node-set if @nodes
4623 * doesn't contain @node
4625 xmlNodeSetPtr
4626 xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
4627 xmlXPathNodeSetSort(nodes);
4628 return(xmlXPathNodeLeadingSorted(nodes, node));
4632 * xmlXPathLeadingSorted:
4633 * @nodes1: a node-set, sorted by document order
4634 * @nodes2: a node-set, sorted by document order
4636 * Implements the EXSLT - Sets leading() function:
4637 * node-set set:leading (node-set, node-set)
4639 * Returns the nodes in @nodes1 that precede the first node in @nodes2
4640 * in document order, @nodes1 if @nodes2 is NULL or empty or
4641 * an empty node-set if @nodes1 doesn't contain @nodes2
4643 xmlNodeSetPtr
4644 xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4645 if (xmlXPathNodeSetIsEmpty(nodes2))
4646 return(nodes1);
4647 return(xmlXPathNodeLeadingSorted(nodes1,
4648 xmlXPathNodeSetItem(nodes2, 1)));
4652 * xmlXPathLeading:
4653 * @nodes1: a node-set
4654 * @nodes2: a node-set
4656 * Implements the EXSLT - Sets leading() function:
4657 * node-set set:leading (node-set, node-set)
4658 * @nodes1 and @nodes2 are sorted by document order, then
4659 * #exslSetsLeadingSorted is called.
4661 * Returns the nodes in @nodes1 that precede the first node in @nodes2
4662 * in document order, @nodes1 if @nodes2 is NULL or empty or
4663 * an empty node-set if @nodes1 doesn't contain @nodes2
4665 xmlNodeSetPtr
4666 xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4667 if (xmlXPathNodeSetIsEmpty(nodes2))
4668 return(nodes1);
4669 if (xmlXPathNodeSetIsEmpty(nodes1))
4670 return(xmlXPathNodeSetCreate(NULL));
4671 xmlXPathNodeSetSort(nodes1);
4672 xmlXPathNodeSetSort(nodes2);
4673 return(xmlXPathNodeLeadingSorted(nodes1,
4674 xmlXPathNodeSetItem(nodes2, 1)));
4678 * xmlXPathNodeTrailingSorted:
4679 * @nodes: a node-set, sorted by document order
4680 * @node: a node
4682 * Implements the EXSLT - Sets trailing() function:
4683 * node-set set:trailing (node-set, node-set)
4685 * Returns the nodes in @nodes that follow @node in document order,
4686 * @nodes if @node is NULL or an empty node-set if @nodes
4687 * doesn't contain @node
4689 xmlNodeSetPtr
4690 xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4691 int i, l;
4692 xmlNodePtr cur;
4693 xmlNodeSetPtr ret;
4695 if (node == NULL)
4696 return(nodes);
4698 ret = xmlXPathNodeSetCreate(NULL);
4699 if (ret == NULL)
4700 return(ret);
4701 if (xmlXPathNodeSetIsEmpty(nodes) ||
4702 (!xmlXPathNodeSetContains(nodes, node)))
4703 return(ret);
4705 l = xmlXPathNodeSetGetLength(nodes);
4706 for (i = l - 1; i >= 0; i--) {
4707 cur = xmlXPathNodeSetItem(nodes, i);
4708 if (cur == node)
4709 break;
4710 /* TODO: Propagate memory error. */
4711 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4712 break;
4714 xmlXPathNodeSetSort(ret); /* bug 413451 */
4715 return(ret);
4719 * xmlXPathNodeTrailing:
4720 * @nodes: a node-set
4721 * @node: a node
4723 * Implements the EXSLT - Sets trailing() function:
4724 * node-set set:trailing (node-set, node-set)
4725 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
4726 * is called.
4728 * Returns the nodes in @nodes that follow @node in document order,
4729 * @nodes if @node is NULL or an empty node-set if @nodes
4730 * doesn't contain @node
4732 xmlNodeSetPtr
4733 xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
4734 xmlXPathNodeSetSort(nodes);
4735 return(xmlXPathNodeTrailingSorted(nodes, node));
4739 * xmlXPathTrailingSorted:
4740 * @nodes1: a node-set, sorted by document order
4741 * @nodes2: a node-set, sorted by document order
4743 * Implements the EXSLT - Sets trailing() function:
4744 * node-set set:trailing (node-set, node-set)
4746 * Returns the nodes in @nodes1 that follow the first node in @nodes2
4747 * in document order, @nodes1 if @nodes2 is NULL or empty or
4748 * an empty node-set if @nodes1 doesn't contain @nodes2
4750 xmlNodeSetPtr
4751 xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4752 if (xmlXPathNodeSetIsEmpty(nodes2))
4753 return(nodes1);
4754 return(xmlXPathNodeTrailingSorted(nodes1,
4755 xmlXPathNodeSetItem(nodes2, 0)));
4759 * xmlXPathTrailing:
4760 * @nodes1: a node-set
4761 * @nodes2: a node-set
4763 * Implements the EXSLT - Sets trailing() function:
4764 * node-set set:trailing (node-set, node-set)
4765 * @nodes1 and @nodes2 are sorted by document order, then
4766 * #xmlXPathTrailingSorted is called.
4768 * Returns the nodes in @nodes1 that follow the first node in @nodes2
4769 * in document order, @nodes1 if @nodes2 is NULL or empty or
4770 * an empty node-set if @nodes1 doesn't contain @nodes2
4772 xmlNodeSetPtr
4773 xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4774 if (xmlXPathNodeSetIsEmpty(nodes2))
4775 return(nodes1);
4776 if (xmlXPathNodeSetIsEmpty(nodes1))
4777 return(xmlXPathNodeSetCreate(NULL));
4778 xmlXPathNodeSetSort(nodes1);
4779 xmlXPathNodeSetSort(nodes2);
4780 return(xmlXPathNodeTrailingSorted(nodes1,
4781 xmlXPathNodeSetItem(nodes2, 0)));
4784 /************************************************************************
4786 * Routines to handle extra functions *
4788 ************************************************************************/
4791 * xmlXPathRegisterFunc:
4792 * @ctxt: the XPath context
4793 * @name: the function name
4794 * @f: the function implementation or NULL
4796 * Register a new function. If @f is NULL it unregisters the function
4798 * Returns 0 in case of success, -1 in case of error
4801 xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
4802 xmlXPathFunction f) {
4803 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
4807 * xmlXPathRegisterFuncNS:
4808 * @ctxt: the XPath context
4809 * @name: the function name
4810 * @ns_uri: the function namespace URI
4811 * @f: the function implementation or NULL
4813 * Register a new function. If @f is NULL it unregisters the function
4815 * Returns 0 in case of success, -1 in case of error
4818 xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4819 const xmlChar *ns_uri, xmlXPathFunction f) {
4820 if (ctxt == NULL)
4821 return(-1);
4822 if (name == NULL)
4823 return(-1);
4825 if (ctxt->funcHash == NULL)
4826 ctxt->funcHash = xmlHashCreate(0);
4827 if (ctxt->funcHash == NULL)
4828 return(-1);
4829 if (f == NULL)
4830 return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
4831 XML_IGNORE_FPTR_CAST_WARNINGS
4832 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
4833 XML_POP_WARNINGS
4837 * xmlXPathRegisterFuncLookup:
4838 * @ctxt: the XPath context
4839 * @f: the lookup function
4840 * @funcCtxt: the lookup data
4842 * Registers an external mechanism to do function lookup.
4844 void
4845 xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
4846 xmlXPathFuncLookupFunc f,
4847 void *funcCtxt) {
4848 if (ctxt == NULL)
4849 return;
4850 ctxt->funcLookupFunc = f;
4851 ctxt->funcLookupData = funcCtxt;
4855 * xmlXPathFunctionLookup:
4856 * @ctxt: the XPath context
4857 * @name: the function name
4859 * Search in the Function array of the context for the given
4860 * function.
4862 * Returns the xmlXPathFunction or NULL if not found
4864 xmlXPathFunction
4865 xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
4866 if (ctxt == NULL)
4867 return (NULL);
4869 if (ctxt->funcLookupFunc != NULL) {
4870 xmlXPathFunction ret;
4871 xmlXPathFuncLookupFunc f;
4873 f = ctxt->funcLookupFunc;
4874 ret = f(ctxt->funcLookupData, name, NULL);
4875 if (ret != NULL)
4876 return(ret);
4878 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
4882 * xmlXPathFunctionLookupNS:
4883 * @ctxt: the XPath context
4884 * @name: the function name
4885 * @ns_uri: the function namespace URI
4887 * Search in the Function array of the context for the given
4888 * function.
4890 * Returns the xmlXPathFunction or NULL if not found
4892 xmlXPathFunction
4893 xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4894 const xmlChar *ns_uri) {
4895 xmlXPathFunction ret;
4897 if (ctxt == NULL)
4898 return(NULL);
4899 if (name == NULL)
4900 return(NULL);
4902 if (ctxt->funcLookupFunc != NULL) {
4903 xmlXPathFuncLookupFunc f;
4905 f = ctxt->funcLookupFunc;
4906 ret = f(ctxt->funcLookupData, name, ns_uri);
4907 if (ret != NULL)
4908 return(ret);
4911 if (ctxt->funcHash == NULL)
4912 return(NULL);
4914 XML_IGNORE_FPTR_CAST_WARNINGS
4915 ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
4916 XML_POP_WARNINGS
4917 return(ret);
4921 * xmlXPathRegisteredFuncsCleanup:
4922 * @ctxt: the XPath context
4924 * Cleanup the XPath context data associated to registered functions
4926 void
4927 xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
4928 if (ctxt == NULL)
4929 return;
4931 xmlHashFree(ctxt->funcHash, NULL);
4932 ctxt->funcHash = NULL;
4935 /************************************************************************
4937 * Routines to handle Variables *
4939 ************************************************************************/
4942 * xmlXPathRegisterVariable:
4943 * @ctxt: the XPath context
4944 * @name: the variable name
4945 * @value: the variable value or NULL
4947 * Register a new variable value. If @value is NULL it unregisters
4948 * the variable
4950 * Returns 0 in case of success, -1 in case of error
4953 xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
4954 xmlXPathObjectPtr value) {
4955 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
4959 * xmlXPathRegisterVariableNS:
4960 * @ctxt: the XPath context
4961 * @name: the variable name
4962 * @ns_uri: the variable namespace URI
4963 * @value: the variable value or NULL
4965 * Register a new variable value. If @value is NULL it unregisters
4966 * the variable
4968 * Returns 0 in case of success, -1 in case of error
4971 xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4972 const xmlChar *ns_uri,
4973 xmlXPathObjectPtr value) {
4974 if (ctxt == NULL)
4975 return(-1);
4976 if (name == NULL)
4977 return(-1);
4979 if (ctxt->varHash == NULL)
4980 ctxt->varHash = xmlHashCreate(0);
4981 if (ctxt->varHash == NULL)
4982 return(-1);
4983 if (value == NULL)
4984 return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
4985 xmlXPathFreeObjectEntry));
4986 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
4987 (void *) value, xmlXPathFreeObjectEntry));
4991 * xmlXPathRegisterVariableLookup:
4992 * @ctxt: the XPath context
4993 * @f: the lookup function
4994 * @data: the lookup data
4996 * register an external mechanism to do variable lookup
4998 void
4999 xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
5000 xmlXPathVariableLookupFunc f, void *data) {
5001 if (ctxt == NULL)
5002 return;
5003 ctxt->varLookupFunc = f;
5004 ctxt->varLookupData = data;
5008 * xmlXPathVariableLookup:
5009 * @ctxt: the XPath context
5010 * @name: the variable name
5012 * Search in the Variable array of the context for the given
5013 * variable value.
5015 * Returns a copy of the value or NULL if not found
5017 xmlXPathObjectPtr
5018 xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
5019 if (ctxt == NULL)
5020 return(NULL);
5022 if (ctxt->varLookupFunc != NULL) {
5023 xmlXPathObjectPtr ret;
5025 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5026 (ctxt->varLookupData, name, NULL);
5027 return(ret);
5029 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
5033 * xmlXPathVariableLookupNS:
5034 * @ctxt: the XPath context
5035 * @name: the variable name
5036 * @ns_uri: the variable namespace URI
5038 * Search in the Variable array of the context for the given
5039 * variable value.
5041 * Returns the a copy of the value or NULL if not found
5043 xmlXPathObjectPtr
5044 xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
5045 const xmlChar *ns_uri) {
5046 if (ctxt == NULL)
5047 return(NULL);
5049 if (ctxt->varLookupFunc != NULL) {
5050 xmlXPathObjectPtr ret;
5052 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5053 (ctxt->varLookupData, name, ns_uri);
5054 if (ret != NULL) return(ret);
5057 if (ctxt->varHash == NULL)
5058 return(NULL);
5059 if (name == NULL)
5060 return(NULL);
5062 return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
5063 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
5067 * xmlXPathRegisteredVariablesCleanup:
5068 * @ctxt: the XPath context
5070 * Cleanup the XPath context data associated to registered variables
5072 void
5073 xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
5074 if (ctxt == NULL)
5075 return;
5077 xmlHashFree(ctxt->varHash, xmlXPathFreeObjectEntry);
5078 ctxt->varHash = NULL;
5082 * xmlXPathRegisterNs:
5083 * @ctxt: the XPath context
5084 * @prefix: the namespace prefix cannot be NULL or empty string
5085 * @ns_uri: the namespace name
5087 * Register a new namespace. If @ns_uri is NULL it unregisters
5088 * the namespace
5090 * Returns 0 in case of success, -1 in case of error
5093 xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
5094 const xmlChar *ns_uri) {
5095 xmlChar *copy;
5097 if (ctxt == NULL)
5098 return(-1);
5099 if (prefix == NULL)
5100 return(-1);
5101 if (prefix[0] == 0)
5102 return(-1);
5104 if (ctxt->nsHash == NULL)
5105 ctxt->nsHash = xmlHashCreate(10);
5106 if (ctxt->nsHash == NULL)
5107 return(-1);
5108 if (ns_uri == NULL)
5109 return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
5110 xmlHashDefaultDeallocator));
5112 copy = xmlStrdup(ns_uri);
5113 if (copy == NULL)
5114 return(-1);
5115 if (xmlHashUpdateEntry(ctxt->nsHash, prefix, copy,
5116 xmlHashDefaultDeallocator) < 0) {
5117 xmlFree(copy);
5118 return(-1);
5121 return(0);
5125 * xmlXPathNsLookup:
5126 * @ctxt: the XPath context
5127 * @prefix: the namespace prefix value
5129 * Search in the namespace declaration array of the context for the given
5130 * namespace name associated to the given prefix
5132 * Returns the value or NULL if not found
5134 const xmlChar *
5135 xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
5136 if (ctxt == NULL)
5137 return(NULL);
5138 if (prefix == NULL)
5139 return(NULL);
5141 #ifdef XML_XML_NAMESPACE
5142 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
5143 return(XML_XML_NAMESPACE);
5144 #endif
5146 if (ctxt->namespaces != NULL) {
5147 int i;
5149 for (i = 0;i < ctxt->nsNr;i++) {
5150 if ((ctxt->namespaces[i] != NULL) &&
5151 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
5152 return(ctxt->namespaces[i]->href);
5156 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
5160 * xmlXPathRegisteredNsCleanup:
5161 * @ctxt: the XPath context
5163 * Cleanup the XPath context data associated to registered variables
5165 void
5166 xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
5167 if (ctxt == NULL)
5168 return;
5170 xmlHashFree(ctxt->nsHash, xmlHashDefaultDeallocator);
5171 ctxt->nsHash = NULL;
5174 /************************************************************************
5176 * Routines to handle Values *
5178 ************************************************************************/
5180 /* Allocations are terrible, one needs to optimize all this !!! */
5183 * xmlXPathNewFloat:
5184 * @val: the double value
5186 * Create a new xmlXPathObjectPtr of type double and of value @val
5188 * Returns the newly created object.
5190 xmlXPathObjectPtr
5191 xmlXPathNewFloat(double val) {
5192 xmlXPathObjectPtr ret;
5194 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5195 if (ret == NULL) {
5196 xmlXPathErrMemory(NULL, "creating float object\n");
5197 return(NULL);
5199 memset(ret, 0 , sizeof(xmlXPathObject));
5200 ret->type = XPATH_NUMBER;
5201 ret->floatval = val;
5202 #ifdef XP_DEBUG_OBJ_USAGE
5203 xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
5204 #endif
5205 return(ret);
5209 * xmlXPathNewBoolean:
5210 * @val: the boolean value
5212 * Create a new xmlXPathObjectPtr of type boolean and of value @val
5214 * Returns the newly created object.
5216 xmlXPathObjectPtr
5217 xmlXPathNewBoolean(int val) {
5218 xmlXPathObjectPtr ret;
5220 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5221 if (ret == NULL) {
5222 xmlXPathErrMemory(NULL, "creating boolean object\n");
5223 return(NULL);
5225 memset(ret, 0 , sizeof(xmlXPathObject));
5226 ret->type = XPATH_BOOLEAN;
5227 ret->boolval = (val != 0);
5228 #ifdef XP_DEBUG_OBJ_USAGE
5229 xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
5230 #endif
5231 return(ret);
5235 * xmlXPathNewString:
5236 * @val: the xmlChar * value
5238 * Create a new xmlXPathObjectPtr of type string and of value @val
5240 * Returns the newly created object.
5242 xmlXPathObjectPtr
5243 xmlXPathNewString(const xmlChar *val) {
5244 xmlXPathObjectPtr ret;
5246 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5247 if (ret == NULL) {
5248 xmlXPathErrMemory(NULL, "creating string object\n");
5249 return(NULL);
5251 memset(ret, 0 , sizeof(xmlXPathObject));
5252 ret->type = XPATH_STRING;
5253 if (val == NULL)
5254 val = BAD_CAST "";
5255 ret->stringval = xmlStrdup(val);
5256 if (ret->stringval == NULL) {
5257 xmlFree(ret);
5258 return(NULL);
5260 #ifdef XP_DEBUG_OBJ_USAGE
5261 xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5262 #endif
5263 return(ret);
5267 * xmlXPathWrapString:
5268 * @val: the xmlChar * value
5270 * Wraps the @val string into an XPath object.
5272 * Returns the newly created object.
5274 * Frees @val in case of error.
5276 xmlXPathObjectPtr
5277 xmlXPathWrapString (xmlChar *val) {
5278 xmlXPathObjectPtr ret;
5280 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5281 if (ret == NULL) {
5282 xmlXPathErrMemory(NULL, "creating string object\n");
5283 xmlFree(val);
5284 return(NULL);
5286 memset(ret, 0 , sizeof(xmlXPathObject));
5287 ret->type = XPATH_STRING;
5288 ret->stringval = val;
5289 #ifdef XP_DEBUG_OBJ_USAGE
5290 xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5291 #endif
5292 return(ret);
5296 * xmlXPathNewCString:
5297 * @val: the char * value
5299 * Create a new xmlXPathObjectPtr of type string and of value @val
5301 * Returns the newly created object.
5303 xmlXPathObjectPtr
5304 xmlXPathNewCString(const char *val) {
5305 return(xmlXPathNewString(BAD_CAST val));
5309 * xmlXPathWrapCString:
5310 * @val: the char * value
5312 * Wraps a string into an XPath object.
5314 * Returns the newly created object.
5316 xmlXPathObjectPtr
5317 xmlXPathWrapCString (char * val) {
5318 return(xmlXPathWrapString((xmlChar *)(val)));
5322 * xmlXPathWrapExternal:
5323 * @val: the user data
5325 * Wraps the @val data into an XPath object.
5327 * Returns the newly created object.
5329 xmlXPathObjectPtr
5330 xmlXPathWrapExternal (void *val) {
5331 xmlXPathObjectPtr ret;
5333 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5334 if (ret == NULL) {
5335 xmlXPathErrMemory(NULL, "creating user object\n");
5336 return(NULL);
5338 memset(ret, 0 , sizeof(xmlXPathObject));
5339 ret->type = XPATH_USERS;
5340 ret->user = val;
5341 #ifdef XP_DEBUG_OBJ_USAGE
5342 xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
5343 #endif
5344 return(ret);
5348 * xmlXPathObjectCopy:
5349 * @val: the original object
5351 * allocate a new copy of a given object
5353 * Returns the newly created object.
5355 xmlXPathObjectPtr
5356 xmlXPathObjectCopy(xmlXPathObjectPtr val) {
5357 xmlXPathObjectPtr ret;
5359 if (val == NULL)
5360 return(NULL);
5362 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5363 if (ret == NULL) {
5364 xmlXPathErrMemory(NULL, "copying object\n");
5365 return(NULL);
5367 memcpy(ret, val , sizeof(xmlXPathObject));
5368 #ifdef XP_DEBUG_OBJ_USAGE
5369 xmlXPathDebugObjUsageRequested(NULL, val->type);
5370 #endif
5371 switch (val->type) {
5372 case XPATH_BOOLEAN:
5373 case XPATH_NUMBER:
5374 #ifdef LIBXML_XPTR_LOCS_ENABLED
5375 case XPATH_POINT:
5376 case XPATH_RANGE:
5377 #endif /* LIBXML_XPTR_LOCS_ENABLED */
5378 break;
5379 case XPATH_STRING:
5380 ret->stringval = xmlStrdup(val->stringval);
5381 if (ret->stringval == NULL) {
5382 xmlFree(ret);
5383 return(NULL);
5385 break;
5386 case XPATH_XSLT_TREE:
5387 #if 0
5389 Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
5390 this previous handling is no longer correct, and can cause some serious
5391 problems (ref. bug 145547)
5393 if ((val->nodesetval != NULL) &&
5394 (val->nodesetval->nodeTab != NULL)) {
5395 xmlNodePtr cur, tmp;
5396 xmlDocPtr top;
5398 ret->boolval = 1;
5399 top = xmlNewDoc(NULL);
5400 top->name = (char *)
5401 xmlStrdup(val->nodesetval->nodeTab[0]->name);
5402 ret->user = top;
5403 if (top != NULL) {
5404 top->doc = top;
5405 cur = val->nodesetval->nodeTab[0]->children;
5406 while (cur != NULL) {
5407 tmp = xmlDocCopyNode(cur, top, 1);
5408 xmlAddChild((xmlNodePtr) top, tmp);
5409 cur = cur->next;
5413 ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
5414 } else
5415 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
5416 /* Deallocate the copied tree value */
5417 break;
5418 #endif
5419 case XPATH_NODESET:
5420 /* TODO: Check memory error. */
5421 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
5422 /* Do not deallocate the copied tree value */
5423 ret->boolval = 0;
5424 break;
5425 #ifdef LIBXML_XPTR_LOCS_ENABLED
5426 case XPATH_LOCATIONSET:
5428 xmlLocationSetPtr loc = val->user;
5429 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
5430 break;
5432 #endif
5433 case XPATH_USERS:
5434 ret->user = val->user;
5435 break;
5436 case XPATH_UNDEFINED:
5437 xmlGenericError(xmlGenericErrorContext,
5438 "xmlXPathObjectCopy: unsupported type %d\n",
5439 val->type);
5440 break;
5442 return(ret);
5446 * xmlXPathFreeObject:
5447 * @obj: the object to free
5449 * Free up an xmlXPathObjectPtr object.
5451 void
5452 xmlXPathFreeObject(xmlXPathObjectPtr obj) {
5453 if (obj == NULL) return;
5454 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
5455 if (obj->boolval) {
5456 #if 0
5457 if (obj->user != NULL) {
5458 xmlXPathFreeNodeSet(obj->nodesetval);
5459 xmlFreeNodeList((xmlNodePtr) obj->user);
5460 } else
5461 #endif
5462 obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
5463 if (obj->nodesetval != NULL)
5464 xmlXPathFreeValueTree(obj->nodesetval);
5465 } else {
5466 if (obj->nodesetval != NULL)
5467 xmlXPathFreeNodeSet(obj->nodesetval);
5469 #ifdef LIBXML_XPTR_LOCS_ENABLED
5470 } else if (obj->type == XPATH_LOCATIONSET) {
5471 if (obj->user != NULL)
5472 xmlXPtrFreeLocationSet(obj->user);
5473 #endif
5474 } else if (obj->type == XPATH_STRING) {
5475 if (obj->stringval != NULL)
5476 xmlFree(obj->stringval);
5478 #ifdef XP_DEBUG_OBJ_USAGE
5479 xmlXPathDebugObjUsageReleased(NULL, obj->type);
5480 #endif
5481 xmlFree(obj);
5484 static void
5485 xmlXPathFreeObjectEntry(void *obj, const xmlChar *name ATTRIBUTE_UNUSED) {
5486 xmlXPathFreeObject((xmlXPathObjectPtr) obj);
5490 * xmlXPathReleaseObject:
5491 * @obj: the xmlXPathObjectPtr to free or to cache
5493 * Depending on the state of the cache this frees the given
5494 * XPath object or stores it in the cache.
5496 static void
5497 xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
5499 #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
5500 sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
5501 if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
5503 #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
5505 if (obj == NULL)
5506 return;
5507 if ((ctxt == NULL) || (ctxt->cache == NULL)) {
5508 xmlXPathFreeObject(obj);
5509 } else {
5510 xmlXPathContextCachePtr cache =
5511 (xmlXPathContextCachePtr) ctxt->cache;
5513 switch (obj->type) {
5514 case XPATH_NODESET:
5515 case XPATH_XSLT_TREE:
5516 if (obj->nodesetval != NULL) {
5517 if (obj->boolval) {
5519 * It looks like the @boolval is used for
5520 * evaluation if this an XSLT Result Tree Fragment.
5521 * TODO: Check if this assumption is correct.
5523 obj->type = XPATH_XSLT_TREE; /* just for debugging */
5524 xmlXPathFreeValueTree(obj->nodesetval);
5525 obj->nodesetval = NULL;
5526 } else if ((obj->nodesetval->nodeMax <= 40) &&
5527 (XP_CACHE_WANTS(cache->nodesetObjs,
5528 cache->maxNodeset)))
5530 XP_CACHE_ADD(cache->nodesetObjs, obj);
5531 goto obj_cached;
5532 } else {
5533 xmlXPathFreeNodeSet(obj->nodesetval);
5534 obj->nodesetval = NULL;
5537 break;
5538 case XPATH_STRING:
5539 if (obj->stringval != NULL)
5540 xmlFree(obj->stringval);
5542 if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
5543 XP_CACHE_ADD(cache->stringObjs, obj);
5544 goto obj_cached;
5546 break;
5547 case XPATH_BOOLEAN:
5548 if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
5549 XP_CACHE_ADD(cache->booleanObjs, obj);
5550 goto obj_cached;
5552 break;
5553 case XPATH_NUMBER:
5554 if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
5555 XP_CACHE_ADD(cache->numberObjs, obj);
5556 goto obj_cached;
5558 break;
5559 #ifdef LIBXML_XPTR_LOCS_ENABLED
5560 case XPATH_LOCATIONSET:
5561 if (obj->user != NULL) {
5562 xmlXPtrFreeLocationSet(obj->user);
5564 goto free_obj;
5565 #endif
5566 default:
5567 goto free_obj;
5571 * Fallback to adding to the misc-objects slot.
5573 if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
5574 XP_CACHE_ADD(cache->miscObjs, obj);
5575 } else
5576 goto free_obj;
5578 obj_cached:
5580 #ifdef XP_DEBUG_OBJ_USAGE
5581 xmlXPathDebugObjUsageReleased(ctxt, obj->type);
5582 #endif
5584 if (obj->nodesetval != NULL) {
5585 xmlNodeSetPtr tmpset = obj->nodesetval;
5588 * TODO: Due to those nasty ns-nodes, we need to traverse
5589 * the list and free the ns-nodes.
5590 * URGENT TODO: Check if it's actually slowing things down.
5591 * Maybe we shouldn't try to preserve the list.
5593 if (tmpset->nodeNr > 1) {
5594 int i;
5595 xmlNodePtr node;
5597 for (i = 0; i < tmpset->nodeNr; i++) {
5598 node = tmpset->nodeTab[i];
5599 if ((node != NULL) &&
5600 (node->type == XML_NAMESPACE_DECL))
5602 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
5605 } else if (tmpset->nodeNr == 1) {
5606 if ((tmpset->nodeTab[0] != NULL) &&
5607 (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
5608 xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
5610 tmpset->nodeNr = 0;
5611 memset(obj, 0, sizeof(xmlXPathObject));
5612 obj->nodesetval = tmpset;
5613 } else
5614 memset(obj, 0, sizeof(xmlXPathObject));
5616 return;
5618 free_obj:
5620 * Cache is full; free the object.
5622 if (obj->nodesetval != NULL)
5623 xmlXPathFreeNodeSet(obj->nodesetval);
5624 #ifdef XP_DEBUG_OBJ_USAGE
5625 xmlXPathDebugObjUsageReleased(NULL, obj->type);
5626 #endif
5627 xmlFree(obj);
5629 return;
5633 /************************************************************************
5635 * Type Casting Routines *
5637 ************************************************************************/
5640 * xmlXPathCastBooleanToString:
5641 * @val: a boolean
5643 * Converts a boolean to its string value.
5645 * Returns a newly allocated string.
5647 xmlChar *
5648 xmlXPathCastBooleanToString (int val) {
5649 xmlChar *ret;
5650 if (val)
5651 ret = xmlStrdup((const xmlChar *) "true");
5652 else
5653 ret = xmlStrdup((const xmlChar *) "false");
5654 return(ret);
5658 * xmlXPathCastNumberToString:
5659 * @val: a number
5661 * Converts a number to its string value.
5663 * Returns a newly allocated string.
5665 xmlChar *
5666 xmlXPathCastNumberToString (double val) {
5667 xmlChar *ret;
5668 switch (xmlXPathIsInf(val)) {
5669 case 1:
5670 ret = xmlStrdup((const xmlChar *) "Infinity");
5671 break;
5672 case -1:
5673 ret = xmlStrdup((const xmlChar *) "-Infinity");
5674 break;
5675 default:
5676 if (xmlXPathIsNaN(val)) {
5677 ret = xmlStrdup((const xmlChar *) "NaN");
5678 } else if (val == 0) {
5679 /* Omit sign for negative zero. */
5680 ret = xmlStrdup((const xmlChar *) "0");
5681 } else {
5682 /* could be improved */
5683 char buf[100];
5684 xmlXPathFormatNumber(val, buf, 99);
5685 buf[99] = 0;
5686 ret = xmlStrdup((const xmlChar *) buf);
5689 return(ret);
5693 * xmlXPathCastNodeToString:
5694 * @node: a node
5696 * Converts a node to its string value.
5698 * Returns a newly allocated string.
5700 xmlChar *
5701 xmlXPathCastNodeToString (xmlNodePtr node) {
5702 xmlChar *ret;
5703 if ((ret = xmlNodeGetContent(node)) == NULL)
5704 ret = xmlStrdup((const xmlChar *) "");
5705 return(ret);
5709 * xmlXPathCastNodeSetToString:
5710 * @ns: a node-set
5712 * Converts a node-set to its string value.
5714 * Returns a newly allocated string.
5716 xmlChar *
5717 xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
5718 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
5719 return(xmlStrdup((const xmlChar *) ""));
5721 if (ns->nodeNr > 1)
5722 xmlXPathNodeSetSort(ns);
5723 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
5727 * xmlXPathCastToString:
5728 * @val: an XPath object
5730 * Converts an existing object to its string() equivalent
5732 * Returns the allocated string value of the object, NULL in case of error.
5733 * It's up to the caller to free the string memory with xmlFree().
5735 xmlChar *
5736 xmlXPathCastToString(xmlXPathObjectPtr val) {
5737 xmlChar *ret = NULL;
5739 if (val == NULL)
5740 return(xmlStrdup((const xmlChar *) ""));
5741 switch (val->type) {
5742 case XPATH_UNDEFINED:
5743 #ifdef DEBUG_EXPR
5744 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
5745 #endif
5746 ret = xmlStrdup((const xmlChar *) "");
5747 break;
5748 case XPATH_NODESET:
5749 case XPATH_XSLT_TREE:
5750 ret = xmlXPathCastNodeSetToString(val->nodesetval);
5751 break;
5752 case XPATH_STRING:
5753 return(xmlStrdup(val->stringval));
5754 case XPATH_BOOLEAN:
5755 ret = xmlXPathCastBooleanToString(val->boolval);
5756 break;
5757 case XPATH_NUMBER: {
5758 ret = xmlXPathCastNumberToString(val->floatval);
5759 break;
5761 case XPATH_USERS:
5762 #ifdef LIBXML_XPTR_LOCS_ENABLED
5763 case XPATH_POINT:
5764 case XPATH_RANGE:
5765 case XPATH_LOCATIONSET:
5766 #endif /* LIBXML_XPTR_LOCS_ENABLED */
5767 TODO
5768 ret = xmlStrdup((const xmlChar *) "");
5769 break;
5771 return(ret);
5775 * xmlXPathConvertString:
5776 * @val: an XPath object
5778 * Converts an existing object to its string() equivalent
5780 * Returns the new object, the old one is freed (or the operation
5781 * is done directly on @val)
5783 xmlXPathObjectPtr
5784 xmlXPathConvertString(xmlXPathObjectPtr val) {
5785 xmlChar *res = NULL;
5787 if (val == NULL)
5788 return(xmlXPathNewCString(""));
5790 switch (val->type) {
5791 case XPATH_UNDEFINED:
5792 #ifdef DEBUG_EXPR
5793 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
5794 #endif
5795 break;
5796 case XPATH_NODESET:
5797 case XPATH_XSLT_TREE:
5798 res = xmlXPathCastNodeSetToString(val->nodesetval);
5799 break;
5800 case XPATH_STRING:
5801 return(val);
5802 case XPATH_BOOLEAN:
5803 res = xmlXPathCastBooleanToString(val->boolval);
5804 break;
5805 case XPATH_NUMBER:
5806 res = xmlXPathCastNumberToString(val->floatval);
5807 break;
5808 case XPATH_USERS:
5809 #ifdef LIBXML_XPTR_LOCS_ENABLED
5810 case XPATH_POINT:
5811 case XPATH_RANGE:
5812 case XPATH_LOCATIONSET:
5813 #endif /* LIBXML_XPTR_LOCS_ENABLED */
5814 TODO;
5815 break;
5817 xmlXPathFreeObject(val);
5818 if (res == NULL)
5819 return(xmlXPathNewCString(""));
5820 return(xmlXPathWrapString(res));
5824 * xmlXPathCastBooleanToNumber:
5825 * @val: a boolean
5827 * Converts a boolean to its number value
5829 * Returns the number value
5831 double
5832 xmlXPathCastBooleanToNumber(int val) {
5833 if (val)
5834 return(1.0);
5835 return(0.0);
5839 * xmlXPathCastStringToNumber:
5840 * @val: a string
5842 * Converts a string to its number value
5844 * Returns the number value
5846 double
5847 xmlXPathCastStringToNumber(const xmlChar * val) {
5848 return(xmlXPathStringEvalNumber(val));
5852 * xmlXPathCastNodeToNumber:
5853 * @node: a node
5855 * Converts a node to its number value
5857 * Returns the number value
5859 double
5860 xmlXPathCastNodeToNumber (xmlNodePtr node) {
5861 xmlChar *strval;
5862 double ret;
5864 if (node == NULL)
5865 return(xmlXPathNAN);
5866 strval = xmlXPathCastNodeToString(node);
5867 if (strval == NULL)
5868 return(xmlXPathNAN);
5869 ret = xmlXPathCastStringToNumber(strval);
5870 xmlFree(strval);
5872 return(ret);
5876 * xmlXPathCastNodeSetToNumber:
5877 * @ns: a node-set
5879 * Converts a node-set to its number value
5881 * Returns the number value
5883 double
5884 xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
5885 xmlChar *str;
5886 double ret;
5888 if (ns == NULL)
5889 return(xmlXPathNAN);
5890 str = xmlXPathCastNodeSetToString(ns);
5891 ret = xmlXPathCastStringToNumber(str);
5892 xmlFree(str);
5893 return(ret);
5897 * xmlXPathCastToNumber:
5898 * @val: an XPath object
5900 * Converts an XPath object to its number value
5902 * Returns the number value
5904 double
5905 xmlXPathCastToNumber(xmlXPathObjectPtr val) {
5906 double ret = 0.0;
5908 if (val == NULL)
5909 return(xmlXPathNAN);
5910 switch (val->type) {
5911 case XPATH_UNDEFINED:
5912 #ifdef DEBUG_EXPR
5913 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5914 #endif
5915 ret = xmlXPathNAN;
5916 break;
5917 case XPATH_NODESET:
5918 case XPATH_XSLT_TREE:
5919 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
5920 break;
5921 case XPATH_STRING:
5922 ret = xmlXPathCastStringToNumber(val->stringval);
5923 break;
5924 case XPATH_NUMBER:
5925 ret = val->floatval;
5926 break;
5927 case XPATH_BOOLEAN:
5928 ret = xmlXPathCastBooleanToNumber(val->boolval);
5929 break;
5930 case XPATH_USERS:
5931 #ifdef LIBXML_XPTR_LOCS_ENABLED
5932 case XPATH_POINT:
5933 case XPATH_RANGE:
5934 case XPATH_LOCATIONSET:
5935 #endif /* LIBXML_XPTR_LOCS_ENABLED */
5936 TODO;
5937 ret = xmlXPathNAN;
5938 break;
5940 return(ret);
5944 * xmlXPathConvertNumber:
5945 * @val: an XPath object
5947 * Converts an existing object to its number() equivalent
5949 * Returns the new object, the old one is freed (or the operation
5950 * is done directly on @val)
5952 xmlXPathObjectPtr
5953 xmlXPathConvertNumber(xmlXPathObjectPtr val) {
5954 xmlXPathObjectPtr ret;
5956 if (val == NULL)
5957 return(xmlXPathNewFloat(0.0));
5958 if (val->type == XPATH_NUMBER)
5959 return(val);
5960 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
5961 xmlXPathFreeObject(val);
5962 return(ret);
5966 * xmlXPathCastNumberToBoolean:
5967 * @val: a number
5969 * Converts a number to its boolean value
5971 * Returns the boolean value
5974 xmlXPathCastNumberToBoolean (double val) {
5975 if (xmlXPathIsNaN(val) || (val == 0.0))
5976 return(0);
5977 return(1);
5981 * xmlXPathCastStringToBoolean:
5982 * @val: a string
5984 * Converts a string to its boolean value
5986 * Returns the boolean value
5989 xmlXPathCastStringToBoolean (const xmlChar *val) {
5990 if ((val == NULL) || (xmlStrlen(val) == 0))
5991 return(0);
5992 return(1);
5996 * xmlXPathCastNodeSetToBoolean:
5997 * @ns: a node-set
5999 * Converts a node-set to its boolean value
6001 * Returns the boolean value
6004 xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
6005 if ((ns == NULL) || (ns->nodeNr == 0))
6006 return(0);
6007 return(1);
6011 * xmlXPathCastToBoolean:
6012 * @val: an XPath object
6014 * Converts an XPath object to its boolean value
6016 * Returns the boolean value
6019 xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
6020 int ret = 0;
6022 if (val == NULL)
6023 return(0);
6024 switch (val->type) {
6025 case XPATH_UNDEFINED:
6026 #ifdef DEBUG_EXPR
6027 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
6028 #endif
6029 ret = 0;
6030 break;
6031 case XPATH_NODESET:
6032 case XPATH_XSLT_TREE:
6033 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
6034 break;
6035 case XPATH_STRING:
6036 ret = xmlXPathCastStringToBoolean(val->stringval);
6037 break;
6038 case XPATH_NUMBER:
6039 ret = xmlXPathCastNumberToBoolean(val->floatval);
6040 break;
6041 case XPATH_BOOLEAN:
6042 ret = val->boolval;
6043 break;
6044 case XPATH_USERS:
6045 #ifdef LIBXML_XPTR_LOCS_ENABLED
6046 case XPATH_POINT:
6047 case XPATH_RANGE:
6048 case XPATH_LOCATIONSET:
6049 #endif /* LIBXML_XPTR_LOCS_ENABLED */
6050 TODO;
6051 ret = 0;
6052 break;
6054 return(ret);
6059 * xmlXPathConvertBoolean:
6060 * @val: an XPath object
6062 * Converts an existing object to its boolean() equivalent
6064 * Returns the new object, the old one is freed (or the operation
6065 * is done directly on @val)
6067 xmlXPathObjectPtr
6068 xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
6069 xmlXPathObjectPtr ret;
6071 if (val == NULL)
6072 return(xmlXPathNewBoolean(0));
6073 if (val->type == XPATH_BOOLEAN)
6074 return(val);
6075 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
6076 xmlXPathFreeObject(val);
6077 return(ret);
6080 /************************************************************************
6082 * Routines to handle XPath contexts *
6084 ************************************************************************/
6087 * xmlXPathNewContext:
6088 * @doc: the XML document
6090 * Create a new xmlXPathContext
6092 * Returns the xmlXPathContext just allocated. The caller will need to free it.
6094 xmlXPathContextPtr
6095 xmlXPathNewContext(xmlDocPtr doc) {
6096 xmlXPathContextPtr ret;
6098 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
6099 if (ret == NULL) {
6100 xmlXPathErrMemory(NULL, "creating context\n");
6101 return(NULL);
6103 memset(ret, 0 , sizeof(xmlXPathContext));
6104 ret->doc = doc;
6105 ret->node = NULL;
6107 ret->varHash = NULL;
6109 ret->nb_types = 0;
6110 ret->max_types = 0;
6111 ret->types = NULL;
6113 ret->funcHash = xmlHashCreate(0);
6115 ret->nb_axis = 0;
6116 ret->max_axis = 0;
6117 ret->axis = NULL;
6119 ret->nsHash = NULL;
6120 ret->user = NULL;
6122 ret->contextSize = -1;
6123 ret->proximityPosition = -1;
6125 #ifdef XP_DEFAULT_CACHE_ON
6126 if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
6127 xmlXPathFreeContext(ret);
6128 return(NULL);
6130 #endif
6132 xmlXPathRegisterAllFunctions(ret);
6134 return(ret);
6138 * xmlXPathFreeContext:
6139 * @ctxt: the context to free
6141 * Free up an xmlXPathContext
6143 void
6144 xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
6145 if (ctxt == NULL) return;
6147 if (ctxt->cache != NULL)
6148 xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
6149 xmlXPathRegisteredNsCleanup(ctxt);
6150 xmlXPathRegisteredFuncsCleanup(ctxt);
6151 xmlXPathRegisteredVariablesCleanup(ctxt);
6152 xmlResetError(&ctxt->lastError);
6153 xmlFree(ctxt);
6156 /************************************************************************
6158 * Routines to handle XPath parser contexts *
6160 ************************************************************************/
6162 #define CHECK_CTXT(ctxt) \
6163 if (ctxt == NULL) { \
6164 __xmlRaiseError(NULL, NULL, NULL, \
6165 NULL, NULL, XML_FROM_XPATH, \
6166 XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
6167 __FILE__, __LINE__, \
6168 NULL, NULL, NULL, 0, 0, \
6169 "NULL context pointer\n"); \
6170 return(NULL); \
6173 #define CHECK_CTXT_NEG(ctxt) \
6174 if (ctxt == NULL) { \
6175 __xmlRaiseError(NULL, NULL, NULL, \
6176 NULL, NULL, XML_FROM_XPATH, \
6177 XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
6178 __FILE__, __LINE__, \
6179 NULL, NULL, NULL, 0, 0, \
6180 "NULL context pointer\n"); \
6181 return(-1); \
6185 #define CHECK_CONTEXT(ctxt) \
6186 if ((ctxt == NULL) || (ctxt->doc == NULL) || \
6187 (ctxt->doc->children == NULL)) { \
6188 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \
6189 return(NULL); \
6194 * xmlXPathNewParserContext:
6195 * @str: the XPath expression
6196 * @ctxt: the XPath context
6198 * Create a new xmlXPathParserContext
6200 * Returns the xmlXPathParserContext just allocated.
6202 xmlXPathParserContextPtr
6203 xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
6204 xmlXPathParserContextPtr ret;
6206 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6207 if (ret == NULL) {
6208 xmlXPathErrMemory(ctxt, "creating parser context\n");
6209 return(NULL);
6211 memset(ret, 0 , sizeof(xmlXPathParserContext));
6212 ret->cur = ret->base = str;
6213 ret->context = ctxt;
6215 ret->comp = xmlXPathNewCompExpr();
6216 if (ret->comp == NULL) {
6217 xmlFree(ret->valueTab);
6218 xmlFree(ret);
6219 return(NULL);
6221 if ((ctxt != NULL) && (ctxt->dict != NULL)) {
6222 ret->comp->dict = ctxt->dict;
6223 xmlDictReference(ret->comp->dict);
6226 return(ret);
6230 * xmlXPathCompParserContext:
6231 * @comp: the XPath compiled expression
6232 * @ctxt: the XPath context
6234 * Create a new xmlXPathParserContext when processing a compiled expression
6236 * Returns the xmlXPathParserContext just allocated.
6238 static xmlXPathParserContextPtr
6239 xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
6240 xmlXPathParserContextPtr ret;
6242 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6243 if (ret == NULL) {
6244 xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6245 return(NULL);
6247 memset(ret, 0 , sizeof(xmlXPathParserContext));
6249 /* Allocate the value stack */
6250 ret->valueTab = (xmlXPathObjectPtr *)
6251 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
6252 if (ret->valueTab == NULL) {
6253 xmlFree(ret);
6254 xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6255 return(NULL);
6257 ret->valueNr = 0;
6258 ret->valueMax = 10;
6259 ret->value = NULL;
6261 ret->context = ctxt;
6262 ret->comp = comp;
6264 return(ret);
6268 * xmlXPathFreeParserContext:
6269 * @ctxt: the context to free
6271 * Free up an xmlXPathParserContext
6273 void
6274 xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
6275 int i;
6277 if (ctxt->valueTab != NULL) {
6278 for (i = 0; i < ctxt->valueNr; i++) {
6279 if (ctxt->context)
6280 xmlXPathReleaseObject(ctxt->context, ctxt->valueTab[i]);
6281 else
6282 xmlXPathFreeObject(ctxt->valueTab[i]);
6284 xmlFree(ctxt->valueTab);
6286 if (ctxt->comp != NULL) {
6287 #ifdef XPATH_STREAMING
6288 if (ctxt->comp->stream != NULL) {
6289 xmlFreePatternList(ctxt->comp->stream);
6290 ctxt->comp->stream = NULL;
6292 #endif
6293 xmlXPathFreeCompExpr(ctxt->comp);
6295 xmlFree(ctxt);
6298 /************************************************************************
6300 * The implicit core function library *
6302 ************************************************************************/
6305 * xmlXPathNodeValHash:
6306 * @node: a node pointer
6308 * Function computing the beginning of the string value of the node,
6309 * used to speed up comparisons
6311 * Returns an int usable as a hash
6313 static unsigned int
6314 xmlXPathNodeValHash(xmlNodePtr node) {
6315 int len = 2;
6316 const xmlChar * string = NULL;
6317 xmlNodePtr tmp = NULL;
6318 unsigned int ret = 0;
6320 if (node == NULL)
6321 return(0);
6323 if (node->type == XML_DOCUMENT_NODE) {
6324 tmp = xmlDocGetRootElement((xmlDocPtr) node);
6325 if (tmp == NULL)
6326 node = node->children;
6327 else
6328 node = tmp;
6330 if (node == NULL)
6331 return(0);
6334 switch (node->type) {
6335 case XML_COMMENT_NODE:
6336 case XML_PI_NODE:
6337 case XML_CDATA_SECTION_NODE:
6338 case XML_TEXT_NODE:
6339 string = node->content;
6340 if (string == NULL)
6341 return(0);
6342 if (string[0] == 0)
6343 return(0);
6344 return(string[0] + (string[1] << 8));
6345 case XML_NAMESPACE_DECL:
6346 string = ((xmlNsPtr)node)->href;
6347 if (string == NULL)
6348 return(0);
6349 if (string[0] == 0)
6350 return(0);
6351 return(string[0] + (string[1] << 8));
6352 case XML_ATTRIBUTE_NODE:
6353 tmp = ((xmlAttrPtr) node)->children;
6354 break;
6355 case XML_ELEMENT_NODE:
6356 tmp = node->children;
6357 break;
6358 default:
6359 return(0);
6361 while (tmp != NULL) {
6362 switch (tmp->type) {
6363 case XML_CDATA_SECTION_NODE:
6364 case XML_TEXT_NODE:
6365 string = tmp->content;
6366 break;
6367 default:
6368 string = NULL;
6369 break;
6371 if ((string != NULL) && (string[0] != 0)) {
6372 if (len == 1) {
6373 return(ret + (string[0] << 8));
6375 if (string[1] == 0) {
6376 len = 1;
6377 ret = string[0];
6378 } else {
6379 return(string[0] + (string[1] << 8));
6383 * Skip to next node
6385 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
6386 if (tmp->children->type != XML_ENTITY_DECL) {
6387 tmp = tmp->children;
6388 continue;
6391 if (tmp == node)
6392 break;
6394 if (tmp->next != NULL) {
6395 tmp = tmp->next;
6396 continue;
6399 do {
6400 tmp = tmp->parent;
6401 if (tmp == NULL)
6402 break;
6403 if (tmp == node) {
6404 tmp = NULL;
6405 break;
6407 if (tmp->next != NULL) {
6408 tmp = tmp->next;
6409 break;
6411 } while (tmp != NULL);
6413 return(ret);
6417 * xmlXPathStringHash:
6418 * @string: a string
6420 * Function computing the beginning of the string value of the node,
6421 * used to speed up comparisons
6423 * Returns an int usable as a hash
6425 static unsigned int
6426 xmlXPathStringHash(const xmlChar * string) {
6427 if (string == NULL)
6428 return(0);
6429 if (string[0] == 0)
6430 return(0);
6431 return(string[0] + (string[1] << 8));
6435 * xmlXPathCompareNodeSetFloat:
6436 * @ctxt: the XPath Parser context
6437 * @inf: less than (1) or greater than (0)
6438 * @strict: is the comparison strict
6439 * @arg: the node set
6440 * @f: the value
6442 * Implement the compare operation between a nodeset and a number
6443 * @ns < @val (1, 1, ...
6444 * @ns <= @val (1, 0, ...
6445 * @ns > @val (0, 1, ...
6446 * @ns >= @val (0, 0, ...
6448 * If one object to be compared is a node-set and the other is a number,
6449 * then the comparison will be true if and only if there is a node in the
6450 * node-set such that the result of performing the comparison on the number
6451 * to be compared and on the result of converting the string-value of that
6452 * node to a number using the number function is true.
6454 * Returns 0 or 1 depending on the results of the test.
6456 static int
6457 xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
6458 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
6459 int i, ret = 0;
6460 xmlNodeSetPtr ns;
6461 xmlChar *str2;
6463 if ((f == NULL) || (arg == NULL) ||
6464 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6465 xmlXPathReleaseObject(ctxt->context, arg);
6466 xmlXPathReleaseObject(ctxt->context, f);
6467 return(0);
6469 ns = arg->nodesetval;
6470 if (ns != NULL) {
6471 for (i = 0;i < ns->nodeNr;i++) {
6472 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6473 if (str2 != NULL) {
6474 valuePush(ctxt,
6475 xmlXPathCacheNewString(ctxt->context, str2));
6476 xmlFree(str2);
6477 xmlXPathNumberFunction(ctxt, 1);
6478 valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
6479 ret = xmlXPathCompareValues(ctxt, inf, strict);
6480 if (ret)
6481 break;
6485 xmlXPathReleaseObject(ctxt->context, arg);
6486 xmlXPathReleaseObject(ctxt->context, f);
6487 return(ret);
6491 * xmlXPathCompareNodeSetString:
6492 * @ctxt: the XPath Parser context
6493 * @inf: less than (1) or greater than (0)
6494 * @strict: is the comparison strict
6495 * @arg: the node set
6496 * @s: the value
6498 * Implement the compare operation between a nodeset and a string
6499 * @ns < @val (1, 1, ...
6500 * @ns <= @val (1, 0, ...
6501 * @ns > @val (0, 1, ...
6502 * @ns >= @val (0, 0, ...
6504 * If one object to be compared is a node-set and the other is a string,
6505 * then the comparison will be true if and only if there is a node in
6506 * the node-set such that the result of performing the comparison on the
6507 * string-value of the node and the other string is true.
6509 * Returns 0 or 1 depending on the results of the test.
6511 static int
6512 xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
6513 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
6514 int i, ret = 0;
6515 xmlNodeSetPtr ns;
6516 xmlChar *str2;
6518 if ((s == NULL) || (arg == NULL) ||
6519 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6520 xmlXPathReleaseObject(ctxt->context, arg);
6521 xmlXPathReleaseObject(ctxt->context, s);
6522 return(0);
6524 ns = arg->nodesetval;
6525 if (ns != NULL) {
6526 for (i = 0;i < ns->nodeNr;i++) {
6527 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6528 if (str2 != NULL) {
6529 valuePush(ctxt,
6530 xmlXPathCacheNewString(ctxt->context, str2));
6531 xmlFree(str2);
6532 valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
6533 ret = xmlXPathCompareValues(ctxt, inf, strict);
6534 if (ret)
6535 break;
6539 xmlXPathReleaseObject(ctxt->context, arg);
6540 xmlXPathReleaseObject(ctxt->context, s);
6541 return(ret);
6545 * xmlXPathCompareNodeSets:
6546 * @inf: less than (1) or greater than (0)
6547 * @strict: is the comparison strict
6548 * @arg1: the first node set object
6549 * @arg2: the second node set object
6551 * Implement the compare operation on nodesets:
6553 * If both objects to be compared are node-sets, then the comparison
6554 * will be true if and only if there is a node in the first node-set
6555 * and a node in the second node-set such that the result of performing
6556 * the comparison on the string-values of the two nodes is true.
6557 * ....
6558 * When neither object to be compared is a node-set and the operator
6559 * is <=, <, >= or >, then the objects are compared by converting both
6560 * objects to numbers and comparing the numbers according to IEEE 754.
6561 * ....
6562 * The number function converts its argument to a number as follows:
6563 * - a string that consists of optional whitespace followed by an
6564 * optional minus sign followed by a Number followed by whitespace
6565 * is converted to the IEEE 754 number that is nearest (according
6566 * to the IEEE 754 round-to-nearest rule) to the mathematical value
6567 * represented by the string; any other string is converted to NaN
6569 * Conclusion all nodes need to be converted first to their string value
6570 * and then the comparison must be done when possible
6572 static int
6573 xmlXPathCompareNodeSets(int inf, int strict,
6574 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6575 int i, j, init = 0;
6576 double val1;
6577 double *values2;
6578 int ret = 0;
6579 xmlNodeSetPtr ns1;
6580 xmlNodeSetPtr ns2;
6582 if ((arg1 == NULL) ||
6583 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
6584 xmlXPathFreeObject(arg2);
6585 return(0);
6587 if ((arg2 == NULL) ||
6588 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
6589 xmlXPathFreeObject(arg1);
6590 xmlXPathFreeObject(arg2);
6591 return(0);
6594 ns1 = arg1->nodesetval;
6595 ns2 = arg2->nodesetval;
6597 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
6598 xmlXPathFreeObject(arg1);
6599 xmlXPathFreeObject(arg2);
6600 return(0);
6602 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
6603 xmlXPathFreeObject(arg1);
6604 xmlXPathFreeObject(arg2);
6605 return(0);
6608 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
6609 if (values2 == NULL) {
6610 /* TODO: Propagate memory error. */
6611 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6612 xmlXPathFreeObject(arg1);
6613 xmlXPathFreeObject(arg2);
6614 return(0);
6616 for (i = 0;i < ns1->nodeNr;i++) {
6617 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
6618 if (xmlXPathIsNaN(val1))
6619 continue;
6620 for (j = 0;j < ns2->nodeNr;j++) {
6621 if (init == 0) {
6622 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
6624 if (xmlXPathIsNaN(values2[j]))
6625 continue;
6626 if (inf && strict)
6627 ret = (val1 < values2[j]);
6628 else if (inf && !strict)
6629 ret = (val1 <= values2[j]);
6630 else if (!inf && strict)
6631 ret = (val1 > values2[j]);
6632 else if (!inf && !strict)
6633 ret = (val1 >= values2[j]);
6634 if (ret)
6635 break;
6637 if (ret)
6638 break;
6639 init = 1;
6641 xmlFree(values2);
6642 xmlXPathFreeObject(arg1);
6643 xmlXPathFreeObject(arg2);
6644 return(ret);
6648 * xmlXPathCompareNodeSetValue:
6649 * @ctxt: the XPath Parser context
6650 * @inf: less than (1) or greater than (0)
6651 * @strict: is the comparison strict
6652 * @arg: the node set
6653 * @val: the value
6655 * Implement the compare operation between a nodeset and a value
6656 * @ns < @val (1, 1, ...
6657 * @ns <= @val (1, 0, ...
6658 * @ns > @val (0, 1, ...
6659 * @ns >= @val (0, 0, ...
6661 * If one object to be compared is a node-set and the other is a boolean,
6662 * then the comparison will be true if and only if the result of performing
6663 * the comparison on the boolean and on the result of converting
6664 * the node-set to a boolean using the boolean function is true.
6666 * Returns 0 or 1 depending on the results of the test.
6668 static int
6669 xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
6670 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
6671 if ((val == NULL) || (arg == NULL) ||
6672 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6673 return(0);
6675 switch(val->type) {
6676 case XPATH_NUMBER:
6677 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
6678 case XPATH_NODESET:
6679 case XPATH_XSLT_TREE:
6680 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
6681 case XPATH_STRING:
6682 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
6683 case XPATH_BOOLEAN:
6684 valuePush(ctxt, arg);
6685 xmlXPathBooleanFunction(ctxt, 1);
6686 valuePush(ctxt, val);
6687 return(xmlXPathCompareValues(ctxt, inf, strict));
6688 default:
6689 xmlGenericError(xmlGenericErrorContext,
6690 "xmlXPathCompareNodeSetValue: Can't compare node set "
6691 "and object of type %d\n",
6692 val->type);
6693 xmlXPathReleaseObject(ctxt->context, arg);
6694 xmlXPathReleaseObject(ctxt->context, val);
6695 XP_ERROR0(XPATH_INVALID_TYPE);
6697 return(0);
6701 * xmlXPathEqualNodeSetString:
6702 * @arg: the nodeset object argument
6703 * @str: the string to compare to.
6704 * @neq: flag to show whether for '=' (0) or '!=' (1)
6706 * Implement the equal operation on XPath objects content: @arg1 == @arg2
6707 * If one object to be compared is a node-set and the other is a string,
6708 * then the comparison will be true if and only if there is a node in
6709 * the node-set such that the result of performing the comparison on the
6710 * string-value of the node and the other string is true.
6712 * Returns 0 or 1 depending on the results of the test.
6714 static int
6715 xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
6717 int i;
6718 xmlNodeSetPtr ns;
6719 xmlChar *str2;
6720 unsigned int hash;
6722 if ((str == NULL) || (arg == NULL) ||
6723 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6724 return (0);
6725 ns = arg->nodesetval;
6727 * A NULL nodeset compared with a string is always false
6728 * (since there is no node equal, and no node not equal)
6730 if ((ns == NULL) || (ns->nodeNr <= 0) )
6731 return (0);
6732 hash = xmlXPathStringHash(str);
6733 for (i = 0; i < ns->nodeNr; i++) {
6734 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
6735 str2 = xmlNodeGetContent(ns->nodeTab[i]);
6736 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
6737 xmlFree(str2);
6738 if (neq)
6739 continue;
6740 return (1);
6741 } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
6742 if (neq)
6743 continue;
6744 return (1);
6745 } else if (neq) {
6746 if (str2 != NULL)
6747 xmlFree(str2);
6748 return (1);
6750 if (str2 != NULL)
6751 xmlFree(str2);
6752 } else if (neq)
6753 return (1);
6755 return (0);
6759 * xmlXPathEqualNodeSetFloat:
6760 * @arg: the nodeset object argument
6761 * @f: the float to compare to
6762 * @neq: flag to show whether to compare '=' (0) or '!=' (1)
6764 * Implement the equal operation on XPath objects content: @arg1 == @arg2
6765 * If one object to be compared is a node-set and the other is a number,
6766 * then the comparison will be true if and only if there is a node in
6767 * the node-set such that the result of performing the comparison on the
6768 * number to be compared and on the result of converting the string-value
6769 * of that node to a number using the number function is true.
6771 * Returns 0 or 1 depending on the results of the test.
6773 static int
6774 xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
6775 xmlXPathObjectPtr arg, double f, int neq) {
6776 int i, ret=0;
6777 xmlNodeSetPtr ns;
6778 xmlChar *str2;
6779 xmlXPathObjectPtr val;
6780 double v;
6782 if ((arg == NULL) ||
6783 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6784 return(0);
6786 ns = arg->nodesetval;
6787 if (ns != NULL) {
6788 for (i=0;i<ns->nodeNr;i++) {
6789 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6790 if (str2 != NULL) {
6791 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
6792 xmlFree(str2);
6793 xmlXPathNumberFunction(ctxt, 1);
6794 CHECK_ERROR0;
6795 val = valuePop(ctxt);
6796 v = val->floatval;
6797 xmlXPathReleaseObject(ctxt->context, val);
6798 if (!xmlXPathIsNaN(v)) {
6799 if ((!neq) && (v==f)) {
6800 ret = 1;
6801 break;
6802 } else if ((neq) && (v!=f)) {
6803 ret = 1;
6804 break;
6806 } else { /* NaN is unequal to any value */
6807 if (neq)
6808 ret = 1;
6814 return(ret);
6819 * xmlXPathEqualNodeSets:
6820 * @arg1: first nodeset object argument
6821 * @arg2: second nodeset object argument
6822 * @neq: flag to show whether to test '=' (0) or '!=' (1)
6824 * Implement the equal / not equal operation on XPath nodesets:
6825 * @arg1 == @arg2 or @arg1 != @arg2
6826 * If both objects to be compared are node-sets, then the comparison
6827 * will be true if and only if there is a node in the first node-set and
6828 * a node in the second node-set such that the result of performing the
6829 * comparison on the string-values of the two nodes is true.
6831 * (needless to say, this is a costly operation)
6833 * Returns 0 or 1 depending on the results of the test.
6835 static int
6836 xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
6837 int i, j;
6838 unsigned int *hashs1;
6839 unsigned int *hashs2;
6840 xmlChar **values1;
6841 xmlChar **values2;
6842 int ret = 0;
6843 xmlNodeSetPtr ns1;
6844 xmlNodeSetPtr ns2;
6846 if ((arg1 == NULL) ||
6847 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
6848 return(0);
6849 if ((arg2 == NULL) ||
6850 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
6851 return(0);
6853 ns1 = arg1->nodesetval;
6854 ns2 = arg2->nodesetval;
6856 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
6857 return(0);
6858 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
6859 return(0);
6862 * for equal, check if there is a node pertaining to both sets
6864 if (neq == 0)
6865 for (i = 0;i < ns1->nodeNr;i++)
6866 for (j = 0;j < ns2->nodeNr;j++)
6867 if (ns1->nodeTab[i] == ns2->nodeTab[j])
6868 return(1);
6870 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
6871 if (values1 == NULL) {
6872 /* TODO: Propagate memory error. */
6873 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6874 return(0);
6876 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
6877 if (hashs1 == NULL) {
6878 /* TODO: Propagate memory error. */
6879 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6880 xmlFree(values1);
6881 return(0);
6883 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
6884 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
6885 if (values2 == NULL) {
6886 /* TODO: Propagate memory error. */
6887 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6888 xmlFree(hashs1);
6889 xmlFree(values1);
6890 return(0);
6892 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
6893 if (hashs2 == NULL) {
6894 /* TODO: Propagate memory error. */
6895 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6896 xmlFree(hashs1);
6897 xmlFree(values1);
6898 xmlFree(values2);
6899 return(0);
6901 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
6902 for (i = 0;i < ns1->nodeNr;i++) {
6903 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
6904 for (j = 0;j < ns2->nodeNr;j++) {
6905 if (i == 0)
6906 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
6907 if (hashs1[i] != hashs2[j]) {
6908 if (neq) {
6909 ret = 1;
6910 break;
6913 else {
6914 if (values1[i] == NULL)
6915 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
6916 if (values2[j] == NULL)
6917 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
6918 ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
6919 if (ret)
6920 break;
6923 if (ret)
6924 break;
6926 for (i = 0;i < ns1->nodeNr;i++)
6927 if (values1[i] != NULL)
6928 xmlFree(values1[i]);
6929 for (j = 0;j < ns2->nodeNr;j++)
6930 if (values2[j] != NULL)
6931 xmlFree(values2[j]);
6932 xmlFree(values1);
6933 xmlFree(values2);
6934 xmlFree(hashs1);
6935 xmlFree(hashs2);
6936 return(ret);
6939 static int
6940 xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
6941 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6942 int ret = 0;
6944 *At this point we are assured neither arg1 nor arg2
6945 *is a nodeset, so we can just pick the appropriate routine.
6947 switch (arg1->type) {
6948 case XPATH_UNDEFINED:
6949 #ifdef DEBUG_EXPR
6950 xmlGenericError(xmlGenericErrorContext,
6951 "Equal: undefined\n");
6952 #endif
6953 break;
6954 case XPATH_BOOLEAN:
6955 switch (arg2->type) {
6956 case XPATH_UNDEFINED:
6957 #ifdef DEBUG_EXPR
6958 xmlGenericError(xmlGenericErrorContext,
6959 "Equal: undefined\n");
6960 #endif
6961 break;
6962 case XPATH_BOOLEAN:
6963 #ifdef DEBUG_EXPR
6964 xmlGenericError(xmlGenericErrorContext,
6965 "Equal: %d boolean %d \n",
6966 arg1->boolval, arg2->boolval);
6967 #endif
6968 ret = (arg1->boolval == arg2->boolval);
6969 break;
6970 case XPATH_NUMBER:
6971 ret = (arg1->boolval ==
6972 xmlXPathCastNumberToBoolean(arg2->floatval));
6973 break;
6974 case XPATH_STRING:
6975 if ((arg2->stringval == NULL) ||
6976 (arg2->stringval[0] == 0)) ret = 0;
6977 else
6978 ret = 1;
6979 ret = (arg1->boolval == ret);
6980 break;
6981 case XPATH_USERS:
6982 #ifdef LIBXML_XPTR_LOCS_ENABLED
6983 case XPATH_POINT:
6984 case XPATH_RANGE:
6985 case XPATH_LOCATIONSET:
6986 #endif /* LIBXML_XPTR_LOCS_ENABLED */
6987 TODO
6988 break;
6989 case XPATH_NODESET:
6990 case XPATH_XSLT_TREE:
6991 break;
6993 break;
6994 case XPATH_NUMBER:
6995 switch (arg2->type) {
6996 case XPATH_UNDEFINED:
6997 #ifdef DEBUG_EXPR
6998 xmlGenericError(xmlGenericErrorContext,
6999 "Equal: undefined\n");
7000 #endif
7001 break;
7002 case XPATH_BOOLEAN:
7003 ret = (arg2->boolval==
7004 xmlXPathCastNumberToBoolean(arg1->floatval));
7005 break;
7006 case XPATH_STRING:
7007 valuePush(ctxt, arg2);
7008 xmlXPathNumberFunction(ctxt, 1);
7009 arg2 = valuePop(ctxt);
7010 if (ctxt->error)
7011 break;
7012 /* Falls through. */
7013 case XPATH_NUMBER:
7014 /* Hand check NaN and Infinity equalities */
7015 if (xmlXPathIsNaN(arg1->floatval) ||
7016 xmlXPathIsNaN(arg2->floatval)) {
7017 ret = 0;
7018 } else if (xmlXPathIsInf(arg1->floatval) == 1) {
7019 if (xmlXPathIsInf(arg2->floatval) == 1)
7020 ret = 1;
7021 else
7022 ret = 0;
7023 } else if (xmlXPathIsInf(arg1->floatval) == -1) {
7024 if (xmlXPathIsInf(arg2->floatval) == -1)
7025 ret = 1;
7026 else
7027 ret = 0;
7028 } else if (xmlXPathIsInf(arg2->floatval) == 1) {
7029 if (xmlXPathIsInf(arg1->floatval) == 1)
7030 ret = 1;
7031 else
7032 ret = 0;
7033 } else if (xmlXPathIsInf(arg2->floatval) == -1) {
7034 if (xmlXPathIsInf(arg1->floatval) == -1)
7035 ret = 1;
7036 else
7037 ret = 0;
7038 } else {
7039 ret = (arg1->floatval == arg2->floatval);
7041 break;
7042 case XPATH_USERS:
7043 #ifdef LIBXML_XPTR_LOCS_ENABLED
7044 case XPATH_POINT:
7045 case XPATH_RANGE:
7046 case XPATH_LOCATIONSET:
7047 #endif /* LIBXML_XPTR_LOCS_ENABLED */
7048 TODO
7049 break;
7050 case XPATH_NODESET:
7051 case XPATH_XSLT_TREE:
7052 break;
7054 break;
7055 case XPATH_STRING:
7056 switch (arg2->type) {
7057 case XPATH_UNDEFINED:
7058 #ifdef DEBUG_EXPR
7059 xmlGenericError(xmlGenericErrorContext,
7060 "Equal: undefined\n");
7061 #endif
7062 break;
7063 case XPATH_BOOLEAN:
7064 if ((arg1->stringval == NULL) ||
7065 (arg1->stringval[0] == 0)) ret = 0;
7066 else
7067 ret = 1;
7068 ret = (arg2->boolval == ret);
7069 break;
7070 case XPATH_STRING:
7071 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
7072 break;
7073 case XPATH_NUMBER:
7074 valuePush(ctxt, arg1);
7075 xmlXPathNumberFunction(ctxt, 1);
7076 arg1 = valuePop(ctxt);
7077 if (ctxt->error)
7078 break;
7079 /* Hand check NaN and Infinity equalities */
7080 if (xmlXPathIsNaN(arg1->floatval) ||
7081 xmlXPathIsNaN(arg2->floatval)) {
7082 ret = 0;
7083 } else if (xmlXPathIsInf(arg1->floatval) == 1) {
7084 if (xmlXPathIsInf(arg2->floatval) == 1)
7085 ret = 1;
7086 else
7087 ret = 0;
7088 } else if (xmlXPathIsInf(arg1->floatval) == -1) {
7089 if (xmlXPathIsInf(arg2->floatval) == -1)
7090 ret = 1;
7091 else
7092 ret = 0;
7093 } else if (xmlXPathIsInf(arg2->floatval) == 1) {
7094 if (xmlXPathIsInf(arg1->floatval) == 1)
7095 ret = 1;
7096 else
7097 ret = 0;
7098 } else if (xmlXPathIsInf(arg2->floatval) == -1) {
7099 if (xmlXPathIsInf(arg1->floatval) == -1)
7100 ret = 1;
7101 else
7102 ret = 0;
7103 } else {
7104 ret = (arg1->floatval == arg2->floatval);
7106 break;
7107 case XPATH_USERS:
7108 #ifdef LIBXML_XPTR_LOCS_ENABLED
7109 case XPATH_POINT:
7110 case XPATH_RANGE:
7111 case XPATH_LOCATIONSET:
7112 #endif /* LIBXML_XPTR_LOCS_ENABLED */
7113 TODO
7114 break;
7115 case XPATH_NODESET:
7116 case XPATH_XSLT_TREE:
7117 break;
7119 break;
7120 case XPATH_USERS:
7121 #ifdef LIBXML_XPTR_LOCS_ENABLED
7122 case XPATH_POINT:
7123 case XPATH_RANGE:
7124 case XPATH_LOCATIONSET:
7125 #endif /* LIBXML_XPTR_LOCS_ENABLED */
7126 TODO
7127 break;
7128 case XPATH_NODESET:
7129 case XPATH_XSLT_TREE:
7130 break;
7132 xmlXPathReleaseObject(ctxt->context, arg1);
7133 xmlXPathReleaseObject(ctxt->context, arg2);
7134 return(ret);
7138 * xmlXPathEqualValues:
7139 * @ctxt: the XPath Parser context
7141 * Implement the equal operation on XPath objects content: @arg1 == @arg2
7143 * Returns 0 or 1 depending on the results of the test.
7146 xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
7147 xmlXPathObjectPtr arg1, arg2, argtmp;
7148 int ret = 0;
7150 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7151 arg2 = valuePop(ctxt);
7152 arg1 = valuePop(ctxt);
7153 if ((arg1 == NULL) || (arg2 == NULL)) {
7154 if (arg1 != NULL)
7155 xmlXPathReleaseObject(ctxt->context, arg1);
7156 else
7157 xmlXPathReleaseObject(ctxt->context, arg2);
7158 XP_ERROR0(XPATH_INVALID_OPERAND);
7161 if (arg1 == arg2) {
7162 #ifdef DEBUG_EXPR
7163 xmlGenericError(xmlGenericErrorContext,
7164 "Equal: by pointer\n");
7165 #endif
7166 xmlXPathFreeObject(arg1);
7167 return(1);
7171 *If either argument is a nodeset, it's a 'special case'
7173 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7174 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7176 *Hack it to assure arg1 is the nodeset
7178 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7179 argtmp = arg2;
7180 arg2 = arg1;
7181 arg1 = argtmp;
7183 switch (arg2->type) {
7184 case XPATH_UNDEFINED:
7185 #ifdef DEBUG_EXPR
7186 xmlGenericError(xmlGenericErrorContext,
7187 "Equal: undefined\n");
7188 #endif
7189 break;
7190 case XPATH_NODESET:
7191 case XPATH_XSLT_TREE:
7192 ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
7193 break;
7194 case XPATH_BOOLEAN:
7195 if ((arg1->nodesetval == NULL) ||
7196 (arg1->nodesetval->nodeNr == 0)) ret = 0;
7197 else
7198 ret = 1;
7199 ret = (ret == arg2->boolval);
7200 break;
7201 case XPATH_NUMBER:
7202 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
7203 break;
7204 case XPATH_STRING:
7205 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
7206 break;
7207 case XPATH_USERS:
7208 #ifdef LIBXML_XPTR_LOCS_ENABLED
7209 case XPATH_POINT:
7210 case XPATH_RANGE:
7211 case XPATH_LOCATIONSET:
7212 #endif /* LIBXML_XPTR_LOCS_ENABLED */
7213 TODO
7214 break;
7216 xmlXPathReleaseObject(ctxt->context, arg1);
7217 xmlXPathReleaseObject(ctxt->context, arg2);
7218 return(ret);
7221 return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7225 * xmlXPathNotEqualValues:
7226 * @ctxt: the XPath Parser context
7228 * Implement the equal operation on XPath objects content: @arg1 == @arg2
7230 * Returns 0 or 1 depending on the results of the test.
7233 xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
7234 xmlXPathObjectPtr arg1, arg2, argtmp;
7235 int ret = 0;
7237 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7238 arg2 = valuePop(ctxt);
7239 arg1 = valuePop(ctxt);
7240 if ((arg1 == NULL) || (arg2 == NULL)) {
7241 if (arg1 != NULL)
7242 xmlXPathReleaseObject(ctxt->context, arg1);
7243 else
7244 xmlXPathReleaseObject(ctxt->context, arg2);
7245 XP_ERROR0(XPATH_INVALID_OPERAND);
7248 if (arg1 == arg2) {
7249 #ifdef DEBUG_EXPR
7250 xmlGenericError(xmlGenericErrorContext,
7251 "NotEqual: by pointer\n");
7252 #endif
7253 xmlXPathReleaseObject(ctxt->context, arg1);
7254 return(0);
7258 *If either argument is a nodeset, it's a 'special case'
7260 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7261 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7263 *Hack it to assure arg1 is the nodeset
7265 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7266 argtmp = arg2;
7267 arg2 = arg1;
7268 arg1 = argtmp;
7270 switch (arg2->type) {
7271 case XPATH_UNDEFINED:
7272 #ifdef DEBUG_EXPR
7273 xmlGenericError(xmlGenericErrorContext,
7274 "NotEqual: undefined\n");
7275 #endif
7276 break;
7277 case XPATH_NODESET:
7278 case XPATH_XSLT_TREE:
7279 ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
7280 break;
7281 case XPATH_BOOLEAN:
7282 if ((arg1->nodesetval == NULL) ||
7283 (arg1->nodesetval->nodeNr == 0)) ret = 0;
7284 else
7285 ret = 1;
7286 ret = (ret != arg2->boolval);
7287 break;
7288 case XPATH_NUMBER:
7289 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
7290 break;
7291 case XPATH_STRING:
7292 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
7293 break;
7294 case XPATH_USERS:
7295 #ifdef LIBXML_XPTR_LOCS_ENABLED
7296 case XPATH_POINT:
7297 case XPATH_RANGE:
7298 case XPATH_LOCATIONSET:
7299 #endif /* LIBXML_XPTR_LOCS_ENABLED */
7300 TODO
7301 break;
7303 xmlXPathReleaseObject(ctxt->context, arg1);
7304 xmlXPathReleaseObject(ctxt->context, arg2);
7305 return(ret);
7308 return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7312 * xmlXPathCompareValues:
7313 * @ctxt: the XPath Parser context
7314 * @inf: less than (1) or greater than (0)
7315 * @strict: is the comparison strict
7317 * Implement the compare operation on XPath objects:
7318 * @arg1 < @arg2 (1, 1, ...
7319 * @arg1 <= @arg2 (1, 0, ...
7320 * @arg1 > @arg2 (0, 1, ...
7321 * @arg1 >= @arg2 (0, 0, ...
7323 * When neither object to be compared is a node-set and the operator is
7324 * <=, <, >=, >, then the objects are compared by converted both objects
7325 * to numbers and comparing the numbers according to IEEE 754. The <
7326 * comparison will be true if and only if the first number is less than the
7327 * second number. The <= comparison will be true if and only if the first
7328 * number is less than or equal to the second number. The > comparison
7329 * will be true if and only if the first number is greater than the second
7330 * number. The >= comparison will be true if and only if the first number
7331 * is greater than or equal to the second number.
7333 * Returns 1 if the comparison succeeded, 0 if it failed
7336 xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
7337 int ret = 0, arg1i = 0, arg2i = 0;
7338 xmlXPathObjectPtr arg1, arg2;
7340 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7341 arg2 = valuePop(ctxt);
7342 arg1 = valuePop(ctxt);
7343 if ((arg1 == NULL) || (arg2 == NULL)) {
7344 if (arg1 != NULL)
7345 xmlXPathReleaseObject(ctxt->context, arg1);
7346 else
7347 xmlXPathReleaseObject(ctxt->context, arg2);
7348 XP_ERROR0(XPATH_INVALID_OPERAND);
7351 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7352 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7354 * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
7355 * are not freed from within this routine; they will be freed from the
7356 * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
7358 if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
7359 ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
7360 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
7361 } else {
7362 if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7363 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
7364 arg1, arg2);
7365 } else {
7366 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
7367 arg2, arg1);
7370 return(ret);
7373 if (arg1->type != XPATH_NUMBER) {
7374 valuePush(ctxt, arg1);
7375 xmlXPathNumberFunction(ctxt, 1);
7376 arg1 = valuePop(ctxt);
7378 if (arg2->type != XPATH_NUMBER) {
7379 valuePush(ctxt, arg2);
7380 xmlXPathNumberFunction(ctxt, 1);
7381 arg2 = valuePop(ctxt);
7383 if (ctxt->error)
7384 goto error;
7386 * Add tests for infinity and nan
7387 * => feedback on 3.4 for Inf and NaN
7389 /* Hand check NaN and Infinity comparisons */
7390 if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
7391 ret=0;
7392 } else {
7393 arg1i=xmlXPathIsInf(arg1->floatval);
7394 arg2i=xmlXPathIsInf(arg2->floatval);
7395 if (inf && strict) {
7396 if ((arg1i == -1 && arg2i != -1) ||
7397 (arg2i == 1 && arg1i != 1)) {
7398 ret = 1;
7399 } else if (arg1i == 0 && arg2i == 0) {
7400 ret = (arg1->floatval < arg2->floatval);
7401 } else {
7402 ret = 0;
7405 else if (inf && !strict) {
7406 if (arg1i == -1 || arg2i == 1) {
7407 ret = 1;
7408 } else if (arg1i == 0 && arg2i == 0) {
7409 ret = (arg1->floatval <= arg2->floatval);
7410 } else {
7411 ret = 0;
7414 else if (!inf && strict) {
7415 if ((arg1i == 1 && arg2i != 1) ||
7416 (arg2i == -1 && arg1i != -1)) {
7417 ret = 1;
7418 } else if (arg1i == 0 && arg2i == 0) {
7419 ret = (arg1->floatval > arg2->floatval);
7420 } else {
7421 ret = 0;
7424 else if (!inf && !strict) {
7425 if (arg1i == 1 || arg2i == -1) {
7426 ret = 1;
7427 } else if (arg1i == 0 && arg2i == 0) {
7428 ret = (arg1->floatval >= arg2->floatval);
7429 } else {
7430 ret = 0;
7434 error:
7435 xmlXPathReleaseObject(ctxt->context, arg1);
7436 xmlXPathReleaseObject(ctxt->context, arg2);
7437 return(ret);
7441 * xmlXPathValueFlipSign:
7442 * @ctxt: the XPath Parser context
7444 * Implement the unary - operation on an XPath object
7445 * The numeric operators convert their operands to numbers as if
7446 * by calling the number function.
7448 void
7449 xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
7450 if ((ctxt == NULL) || (ctxt->context == NULL)) return;
7451 CAST_TO_NUMBER;
7452 CHECK_TYPE(XPATH_NUMBER);
7453 ctxt->value->floatval = -ctxt->value->floatval;
7457 * xmlXPathAddValues:
7458 * @ctxt: the XPath Parser context
7460 * Implement the add operation on XPath objects:
7461 * The numeric operators convert their operands to numbers as if
7462 * by calling the number function.
7464 void
7465 xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
7466 xmlXPathObjectPtr arg;
7467 double val;
7469 arg = valuePop(ctxt);
7470 if (arg == NULL)
7471 XP_ERROR(XPATH_INVALID_OPERAND);
7472 val = xmlXPathCastToNumber(arg);
7473 xmlXPathReleaseObject(ctxt->context, arg);
7474 CAST_TO_NUMBER;
7475 CHECK_TYPE(XPATH_NUMBER);
7476 ctxt->value->floatval += val;
7480 * xmlXPathSubValues:
7481 * @ctxt: the XPath Parser context
7483 * Implement the subtraction operation on XPath objects:
7484 * The numeric operators convert their operands to numbers as if
7485 * by calling the number function.
7487 void
7488 xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
7489 xmlXPathObjectPtr arg;
7490 double val;
7492 arg = valuePop(ctxt);
7493 if (arg == NULL)
7494 XP_ERROR(XPATH_INVALID_OPERAND);
7495 val = xmlXPathCastToNumber(arg);
7496 xmlXPathReleaseObject(ctxt->context, arg);
7497 CAST_TO_NUMBER;
7498 CHECK_TYPE(XPATH_NUMBER);
7499 ctxt->value->floatval -= val;
7503 * xmlXPathMultValues:
7504 * @ctxt: the XPath Parser context
7506 * Implement the multiply operation on XPath objects:
7507 * The numeric operators convert their operands to numbers as if
7508 * by calling the number function.
7510 void
7511 xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
7512 xmlXPathObjectPtr arg;
7513 double val;
7515 arg = valuePop(ctxt);
7516 if (arg == NULL)
7517 XP_ERROR(XPATH_INVALID_OPERAND);
7518 val = xmlXPathCastToNumber(arg);
7519 xmlXPathReleaseObject(ctxt->context, arg);
7520 CAST_TO_NUMBER;
7521 CHECK_TYPE(XPATH_NUMBER);
7522 ctxt->value->floatval *= val;
7526 * xmlXPathDivValues:
7527 * @ctxt: the XPath Parser context
7529 * Implement the div operation on XPath objects @arg1 / @arg2:
7530 * The numeric operators convert their operands to numbers as if
7531 * by calling the number function.
7533 ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
7534 void
7535 xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
7536 xmlXPathObjectPtr arg;
7537 double val;
7539 arg = valuePop(ctxt);
7540 if (arg == NULL)
7541 XP_ERROR(XPATH_INVALID_OPERAND);
7542 val = xmlXPathCastToNumber(arg);
7543 xmlXPathReleaseObject(ctxt->context, arg);
7544 CAST_TO_NUMBER;
7545 CHECK_TYPE(XPATH_NUMBER);
7546 ctxt->value->floatval /= val;
7550 * xmlXPathModValues:
7551 * @ctxt: the XPath Parser context
7553 * Implement the mod operation on XPath objects: @arg1 / @arg2
7554 * The numeric operators convert their operands to numbers as if
7555 * by calling the number function.
7557 void
7558 xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
7559 xmlXPathObjectPtr arg;
7560 double arg1, arg2;
7562 arg = valuePop(ctxt);
7563 if (arg == NULL)
7564 XP_ERROR(XPATH_INVALID_OPERAND);
7565 arg2 = xmlXPathCastToNumber(arg);
7566 xmlXPathReleaseObject(ctxt->context, arg);
7567 CAST_TO_NUMBER;
7568 CHECK_TYPE(XPATH_NUMBER);
7569 arg1 = ctxt->value->floatval;
7570 if (arg2 == 0)
7571 ctxt->value->floatval = xmlXPathNAN;
7572 else {
7573 ctxt->value->floatval = fmod(arg1, arg2);
7577 /************************************************************************
7579 * The traversal functions *
7581 ************************************************************************/
7584 * A traversal function enumerates nodes along an axis.
7585 * Initially it must be called with NULL, and it indicates
7586 * termination on the axis by returning NULL.
7588 typedef xmlNodePtr (*xmlXPathTraversalFunction)
7589 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
7592 * xmlXPathTraversalFunctionExt:
7593 * A traversal function enumerates nodes along an axis.
7594 * Initially it must be called with NULL, and it indicates
7595 * termination on the axis by returning NULL.
7596 * The context node of the traversal is specified via @contextNode.
7598 typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
7599 (xmlNodePtr cur, xmlNodePtr contextNode);
7602 * xmlXPathNodeSetMergeFunction:
7603 * Used for merging node sets in xmlXPathCollectAndTest().
7605 typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
7606 (xmlNodeSetPtr, xmlNodeSetPtr);
7610 * xmlXPathNextSelf:
7611 * @ctxt: the XPath Parser context
7612 * @cur: the current node in the traversal
7614 * Traversal function for the "self" direction
7615 * The self axis contains just the context node itself
7617 * Returns the next element following that axis
7619 xmlNodePtr
7620 xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7621 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7622 if (cur == NULL)
7623 return(ctxt->context->node);
7624 return(NULL);
7628 * xmlXPathNextChild:
7629 * @ctxt: the XPath Parser context
7630 * @cur: the current node in the traversal
7632 * Traversal function for the "child" direction
7633 * The child axis contains the children of the context node in document order.
7635 * Returns the next element following that axis
7637 xmlNodePtr
7638 xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7639 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7640 if (cur == NULL) {
7641 if (ctxt->context->node == NULL) return(NULL);
7642 switch (ctxt->context->node->type) {
7643 case XML_ELEMENT_NODE:
7644 case XML_TEXT_NODE:
7645 case XML_CDATA_SECTION_NODE:
7646 case XML_ENTITY_REF_NODE:
7647 case XML_ENTITY_NODE:
7648 case XML_PI_NODE:
7649 case XML_COMMENT_NODE:
7650 case XML_NOTATION_NODE:
7651 case XML_DTD_NODE:
7652 return(ctxt->context->node->children);
7653 case XML_DOCUMENT_NODE:
7654 case XML_DOCUMENT_TYPE_NODE:
7655 case XML_DOCUMENT_FRAG_NODE:
7656 case XML_HTML_DOCUMENT_NODE:
7657 return(((xmlDocPtr) ctxt->context->node)->children);
7658 case XML_ELEMENT_DECL:
7659 case XML_ATTRIBUTE_DECL:
7660 case XML_ENTITY_DECL:
7661 case XML_ATTRIBUTE_NODE:
7662 case XML_NAMESPACE_DECL:
7663 case XML_XINCLUDE_START:
7664 case XML_XINCLUDE_END:
7665 return(NULL);
7667 return(NULL);
7669 if ((cur->type == XML_DOCUMENT_NODE) ||
7670 (cur->type == XML_HTML_DOCUMENT_NODE))
7671 return(NULL);
7672 return(cur->next);
7676 * xmlXPathNextChildElement:
7677 * @ctxt: the XPath Parser context
7678 * @cur: the current node in the traversal
7680 * Traversal function for the "child" direction and nodes of type element.
7681 * The child axis contains the children of the context node in document order.
7683 * Returns the next element following that axis
7685 static xmlNodePtr
7686 xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7687 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7688 if (cur == NULL) {
7689 cur = ctxt->context->node;
7690 if (cur == NULL) return(NULL);
7692 * Get the first element child.
7694 switch (cur->type) {
7695 case XML_ELEMENT_NODE:
7696 case XML_DOCUMENT_FRAG_NODE:
7697 case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
7698 case XML_ENTITY_NODE:
7699 cur = cur->children;
7700 if (cur != NULL) {
7701 if (cur->type == XML_ELEMENT_NODE)
7702 return(cur);
7703 do {
7704 cur = cur->next;
7705 } while ((cur != NULL) &&
7706 (cur->type != XML_ELEMENT_NODE));
7707 return(cur);
7709 return(NULL);
7710 case XML_DOCUMENT_NODE:
7711 case XML_HTML_DOCUMENT_NODE:
7712 return(xmlDocGetRootElement((xmlDocPtr) cur));
7713 default:
7714 return(NULL);
7716 return(NULL);
7719 * Get the next sibling element node.
7721 switch (cur->type) {
7722 case XML_ELEMENT_NODE:
7723 case XML_TEXT_NODE:
7724 case XML_ENTITY_REF_NODE:
7725 case XML_ENTITY_NODE:
7726 case XML_CDATA_SECTION_NODE:
7727 case XML_PI_NODE:
7728 case XML_COMMENT_NODE:
7729 case XML_XINCLUDE_END:
7730 break;
7731 /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
7732 default:
7733 return(NULL);
7735 if (cur->next != NULL) {
7736 if (cur->next->type == XML_ELEMENT_NODE)
7737 return(cur->next);
7738 cur = cur->next;
7739 do {
7740 cur = cur->next;
7741 } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
7742 return(cur);
7744 return(NULL);
7747 #if 0
7749 * xmlXPathNextDescendantOrSelfElemParent:
7750 * @ctxt: the XPath Parser context
7751 * @cur: the current node in the traversal
7753 * Traversal function for the "descendant-or-self" axis.
7754 * Additionally it returns only nodes which can be parents of
7755 * element nodes.
7758 * Returns the next element following that axis
7760 static xmlNodePtr
7761 xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
7762 xmlNodePtr contextNode)
7764 if (cur == NULL) {
7765 if (contextNode == NULL)
7766 return(NULL);
7767 switch (contextNode->type) {
7768 case XML_ELEMENT_NODE:
7769 case XML_XINCLUDE_START:
7770 case XML_DOCUMENT_FRAG_NODE:
7771 case XML_DOCUMENT_NODE:
7772 case XML_HTML_DOCUMENT_NODE:
7773 return(contextNode);
7774 default:
7775 return(NULL);
7777 return(NULL);
7778 } else {
7779 xmlNodePtr start = cur;
7781 while (cur != NULL) {
7782 switch (cur->type) {
7783 case XML_ELEMENT_NODE:
7784 /* TODO: OK to have XInclude here? */
7785 case XML_XINCLUDE_START:
7786 case XML_DOCUMENT_FRAG_NODE:
7787 if (cur != start)
7788 return(cur);
7789 if (cur->children != NULL) {
7790 cur = cur->children;
7791 continue;
7793 break;
7794 /* Not sure if we need those here. */
7795 case XML_DOCUMENT_NODE:
7796 case XML_HTML_DOCUMENT_NODE:
7797 if (cur != start)
7798 return(cur);
7799 return(xmlDocGetRootElement((xmlDocPtr) cur));
7800 default:
7801 break;
7804 next_sibling:
7805 if ((cur == NULL) || (cur == contextNode))
7806 return(NULL);
7807 if (cur->next != NULL) {
7808 cur = cur->next;
7809 } else {
7810 cur = cur->parent;
7811 goto next_sibling;
7815 return(NULL);
7817 #endif
7820 * xmlXPathNextDescendant:
7821 * @ctxt: the XPath Parser context
7822 * @cur: the current node in the traversal
7824 * Traversal function for the "descendant" direction
7825 * the descendant axis contains the descendants of the context node in document
7826 * order; a descendant is a child or a child of a child and so on.
7828 * Returns the next element following that axis
7830 xmlNodePtr
7831 xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7832 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7833 if (cur == NULL) {
7834 if (ctxt->context->node == NULL)
7835 return(NULL);
7836 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7837 (ctxt->context->node->type == XML_NAMESPACE_DECL))
7838 return(NULL);
7840 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
7841 return(ctxt->context->doc->children);
7842 return(ctxt->context->node->children);
7845 if (cur->type == XML_NAMESPACE_DECL)
7846 return(NULL);
7847 if (cur->children != NULL) {
7849 * Do not descend on entities declarations
7851 if (cur->children->type != XML_ENTITY_DECL) {
7852 cur = cur->children;
7854 * Skip DTDs
7856 if (cur->type != XML_DTD_NODE)
7857 return(cur);
7861 if (cur == ctxt->context->node) return(NULL);
7863 while (cur->next != NULL) {
7864 cur = cur->next;
7865 if ((cur->type != XML_ENTITY_DECL) &&
7866 (cur->type != XML_DTD_NODE))
7867 return(cur);
7870 do {
7871 cur = cur->parent;
7872 if (cur == NULL) break;
7873 if (cur == ctxt->context->node) return(NULL);
7874 if (cur->next != NULL) {
7875 cur = cur->next;
7876 return(cur);
7878 } while (cur != NULL);
7879 return(cur);
7883 * xmlXPathNextDescendantOrSelf:
7884 * @ctxt: the XPath Parser context
7885 * @cur: the current node in the traversal
7887 * Traversal function for the "descendant-or-self" direction
7888 * the descendant-or-self axis contains the context node and the descendants
7889 * of the context node in document order; thus the context node is the first
7890 * node on the axis, and the first child of the context node is the second node
7891 * on the axis
7893 * Returns the next element following that axis
7895 xmlNodePtr
7896 xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7897 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7898 if (cur == NULL)
7899 return(ctxt->context->node);
7901 if (ctxt->context->node == NULL)
7902 return(NULL);
7903 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7904 (ctxt->context->node->type == XML_NAMESPACE_DECL))
7905 return(NULL);
7907 return(xmlXPathNextDescendant(ctxt, cur));
7911 * xmlXPathNextParent:
7912 * @ctxt: the XPath Parser context
7913 * @cur: the current node in the traversal
7915 * Traversal function for the "parent" direction
7916 * The parent axis contains the parent of the context node, if there is one.
7918 * Returns the next element following that axis
7920 xmlNodePtr
7921 xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7922 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7924 * the parent of an attribute or namespace node is the element
7925 * to which the attribute or namespace node is attached
7926 * Namespace handling !!!
7928 if (cur == NULL) {
7929 if (ctxt->context->node == NULL) return(NULL);
7930 switch (ctxt->context->node->type) {
7931 case XML_ELEMENT_NODE:
7932 case XML_TEXT_NODE:
7933 case XML_CDATA_SECTION_NODE:
7934 case XML_ENTITY_REF_NODE:
7935 case XML_ENTITY_NODE:
7936 case XML_PI_NODE:
7937 case XML_COMMENT_NODE:
7938 case XML_NOTATION_NODE:
7939 case XML_DTD_NODE:
7940 case XML_ELEMENT_DECL:
7941 case XML_ATTRIBUTE_DECL:
7942 case XML_XINCLUDE_START:
7943 case XML_XINCLUDE_END:
7944 case XML_ENTITY_DECL:
7945 if (ctxt->context->node->parent == NULL)
7946 return((xmlNodePtr) ctxt->context->doc);
7947 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
7948 ((ctxt->context->node->parent->name[0] == ' ') ||
7949 (xmlStrEqual(ctxt->context->node->parent->name,
7950 BAD_CAST "fake node libxslt"))))
7951 return(NULL);
7952 return(ctxt->context->node->parent);
7953 case XML_ATTRIBUTE_NODE: {
7954 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
7956 return(att->parent);
7958 case XML_DOCUMENT_NODE:
7959 case XML_DOCUMENT_TYPE_NODE:
7960 case XML_DOCUMENT_FRAG_NODE:
7961 case XML_HTML_DOCUMENT_NODE:
7962 return(NULL);
7963 case XML_NAMESPACE_DECL: {
7964 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
7966 if ((ns->next != NULL) &&
7967 (ns->next->type != XML_NAMESPACE_DECL))
7968 return((xmlNodePtr) ns->next);
7969 return(NULL);
7973 return(NULL);
7977 * xmlXPathNextAncestor:
7978 * @ctxt: the XPath Parser context
7979 * @cur: the current node in the traversal
7981 * Traversal function for the "ancestor" direction
7982 * the ancestor axis contains the ancestors of the context node; the ancestors
7983 * of the context node consist of the parent of context node and the parent's
7984 * parent and so on; the nodes are ordered in reverse document order; thus the
7985 * parent is the first node on the axis, and the parent's parent is the second
7986 * node on the axis
7988 * Returns the next element following that axis
7990 xmlNodePtr
7991 xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7992 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7994 * the parent of an attribute or namespace node is the element
7995 * to which the attribute or namespace node is attached
7996 * !!!!!!!!!!!!!
7998 if (cur == NULL) {
7999 if (ctxt->context->node == NULL) return(NULL);
8000 switch (ctxt->context->node->type) {
8001 case XML_ELEMENT_NODE:
8002 case XML_TEXT_NODE:
8003 case XML_CDATA_SECTION_NODE:
8004 case XML_ENTITY_REF_NODE:
8005 case XML_ENTITY_NODE:
8006 case XML_PI_NODE:
8007 case XML_COMMENT_NODE:
8008 case XML_DTD_NODE:
8009 case XML_ELEMENT_DECL:
8010 case XML_ATTRIBUTE_DECL:
8011 case XML_ENTITY_DECL:
8012 case XML_NOTATION_NODE:
8013 case XML_XINCLUDE_START:
8014 case XML_XINCLUDE_END:
8015 if (ctxt->context->node->parent == NULL)
8016 return((xmlNodePtr) ctxt->context->doc);
8017 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
8018 ((ctxt->context->node->parent->name[0] == ' ') ||
8019 (xmlStrEqual(ctxt->context->node->parent->name,
8020 BAD_CAST "fake node libxslt"))))
8021 return(NULL);
8022 return(ctxt->context->node->parent);
8023 case XML_ATTRIBUTE_NODE: {
8024 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
8026 return(tmp->parent);
8028 case XML_DOCUMENT_NODE:
8029 case XML_DOCUMENT_TYPE_NODE:
8030 case XML_DOCUMENT_FRAG_NODE:
8031 case XML_HTML_DOCUMENT_NODE:
8032 return(NULL);
8033 case XML_NAMESPACE_DECL: {
8034 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
8036 if ((ns->next != NULL) &&
8037 (ns->next->type != XML_NAMESPACE_DECL))
8038 return((xmlNodePtr) ns->next);
8039 /* Bad, how did that namespace end up here ? */
8040 return(NULL);
8043 return(NULL);
8045 if (cur == ctxt->context->doc->children)
8046 return((xmlNodePtr) ctxt->context->doc);
8047 if (cur == (xmlNodePtr) ctxt->context->doc)
8048 return(NULL);
8049 switch (cur->type) {
8050 case XML_ELEMENT_NODE:
8051 case XML_TEXT_NODE:
8052 case XML_CDATA_SECTION_NODE:
8053 case XML_ENTITY_REF_NODE:
8054 case XML_ENTITY_NODE:
8055 case XML_PI_NODE:
8056 case XML_COMMENT_NODE:
8057 case XML_NOTATION_NODE:
8058 case XML_DTD_NODE:
8059 case XML_ELEMENT_DECL:
8060 case XML_ATTRIBUTE_DECL:
8061 case XML_ENTITY_DECL:
8062 case XML_XINCLUDE_START:
8063 case XML_XINCLUDE_END:
8064 if (cur->parent == NULL)
8065 return(NULL);
8066 if ((cur->parent->type == XML_ELEMENT_NODE) &&
8067 ((cur->parent->name[0] == ' ') ||
8068 (xmlStrEqual(cur->parent->name,
8069 BAD_CAST "fake node libxslt"))))
8070 return(NULL);
8071 return(cur->parent);
8072 case XML_ATTRIBUTE_NODE: {
8073 xmlAttrPtr att = (xmlAttrPtr) cur;
8075 return(att->parent);
8077 case XML_NAMESPACE_DECL: {
8078 xmlNsPtr ns = (xmlNsPtr) cur;
8080 if ((ns->next != NULL) &&
8081 (ns->next->type != XML_NAMESPACE_DECL))
8082 return((xmlNodePtr) ns->next);
8083 /* Bad, how did that namespace end up here ? */
8084 return(NULL);
8086 case XML_DOCUMENT_NODE:
8087 case XML_DOCUMENT_TYPE_NODE:
8088 case XML_DOCUMENT_FRAG_NODE:
8089 case XML_HTML_DOCUMENT_NODE:
8090 return(NULL);
8092 return(NULL);
8096 * xmlXPathNextAncestorOrSelf:
8097 * @ctxt: the XPath Parser context
8098 * @cur: the current node in the traversal
8100 * Traversal function for the "ancestor-or-self" direction
8101 * he ancestor-or-self axis contains the context node and ancestors of
8102 * the context node in reverse document order; thus the context node is
8103 * the first node on the axis, and the context node's parent the second;
8104 * parent here is defined the same as with the parent axis.
8106 * Returns the next element following that axis
8108 xmlNodePtr
8109 xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8110 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8111 if (cur == NULL)
8112 return(ctxt->context->node);
8113 return(xmlXPathNextAncestor(ctxt, cur));
8117 * xmlXPathNextFollowingSibling:
8118 * @ctxt: the XPath Parser context
8119 * @cur: the current node in the traversal
8121 * Traversal function for the "following-sibling" direction
8122 * The following-sibling axis contains the following siblings of the context
8123 * node in document order.
8125 * Returns the next element following that axis
8127 xmlNodePtr
8128 xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8129 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8130 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8131 (ctxt->context->node->type == XML_NAMESPACE_DECL))
8132 return(NULL);
8133 if (cur == (xmlNodePtr) ctxt->context->doc)
8134 return(NULL);
8135 if (cur == NULL)
8136 return(ctxt->context->node->next);
8137 return(cur->next);
8141 * xmlXPathNextPrecedingSibling:
8142 * @ctxt: the XPath Parser context
8143 * @cur: the current node in the traversal
8145 * Traversal function for the "preceding-sibling" direction
8146 * The preceding-sibling axis contains the preceding siblings of the context
8147 * node in reverse document order; the first preceding sibling is first on the
8148 * axis; the sibling preceding that node is the second on the axis and so on.
8150 * Returns the next element following that axis
8152 xmlNodePtr
8153 xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8154 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8155 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8156 (ctxt->context->node->type == XML_NAMESPACE_DECL))
8157 return(NULL);
8158 if (cur == (xmlNodePtr) ctxt->context->doc)
8159 return(NULL);
8160 if (cur == NULL)
8161 return(ctxt->context->node->prev);
8162 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
8163 cur = cur->prev;
8164 if (cur == NULL)
8165 return(ctxt->context->node->prev);
8167 return(cur->prev);
8171 * xmlXPathNextFollowing:
8172 * @ctxt: the XPath Parser context
8173 * @cur: the current node in the traversal
8175 * Traversal function for the "following" direction
8176 * The following axis contains all nodes in the same document as the context
8177 * node that are after the context node in document order, excluding any
8178 * descendants and excluding attribute nodes and namespace nodes; the nodes
8179 * are ordered in document order
8181 * Returns the next element following that axis
8183 xmlNodePtr
8184 xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8185 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8186 if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
8187 (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
8188 return(cur->children);
8190 if (cur == NULL) {
8191 cur = ctxt->context->node;
8192 if (cur->type == XML_ATTRIBUTE_NODE) {
8193 cur = cur->parent;
8194 } else if (cur->type == XML_NAMESPACE_DECL) {
8195 xmlNsPtr ns = (xmlNsPtr) cur;
8197 if ((ns->next == NULL) ||
8198 (ns->next->type == XML_NAMESPACE_DECL))
8199 return (NULL);
8200 cur = (xmlNodePtr) ns->next;
8203 if (cur == NULL) return(NULL) ; /* ERROR */
8204 if (cur->next != NULL) return(cur->next) ;
8205 do {
8206 cur = cur->parent;
8207 if (cur == NULL) break;
8208 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
8209 if (cur->next != NULL) return(cur->next);
8210 } while (cur != NULL);
8211 return(cur);
8215 * xmlXPathIsAncestor:
8216 * @ancestor: the ancestor node
8217 * @node: the current node
8219 * Check that @ancestor is a @node's ancestor
8221 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
8223 static int
8224 xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
8225 if ((ancestor == NULL) || (node == NULL)) return(0);
8226 if (node->type == XML_NAMESPACE_DECL)
8227 return(0);
8228 if (ancestor->type == XML_NAMESPACE_DECL)
8229 return(0);
8230 /* nodes need to be in the same document */
8231 if (ancestor->doc != node->doc) return(0);
8232 /* avoid searching if ancestor or node is the root node */
8233 if (ancestor == (xmlNodePtr) node->doc) return(1);
8234 if (node == (xmlNodePtr) ancestor->doc) return(0);
8235 while (node->parent != NULL) {
8236 if (node->parent == ancestor)
8237 return(1);
8238 node = node->parent;
8240 return(0);
8244 * xmlXPathNextPreceding:
8245 * @ctxt: the XPath Parser context
8246 * @cur: the current node in the traversal
8248 * Traversal function for the "preceding" direction
8249 * the preceding axis contains all nodes in the same document as the context
8250 * node that are before the context node in document order, excluding any
8251 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8252 * ordered in reverse document order
8254 * Returns the next element following that axis
8256 xmlNodePtr
8257 xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
8259 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8260 if (cur == NULL) {
8261 cur = ctxt->context->node;
8262 if (cur->type == XML_ATTRIBUTE_NODE) {
8263 cur = cur->parent;
8264 } else if (cur->type == XML_NAMESPACE_DECL) {
8265 xmlNsPtr ns = (xmlNsPtr) cur;
8267 if ((ns->next == NULL) ||
8268 (ns->next->type == XML_NAMESPACE_DECL))
8269 return (NULL);
8270 cur = (xmlNodePtr) ns->next;
8273 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
8274 return (NULL);
8275 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8276 cur = cur->prev;
8277 do {
8278 if (cur->prev != NULL) {
8279 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
8280 return (cur);
8283 cur = cur->parent;
8284 if (cur == NULL)
8285 return (NULL);
8286 if (cur == ctxt->context->doc->children)
8287 return (NULL);
8288 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
8289 return (cur);
8293 * xmlXPathNextPrecedingInternal:
8294 * @ctxt: the XPath Parser context
8295 * @cur: the current node in the traversal
8297 * Traversal function for the "preceding" direction
8298 * the preceding axis contains all nodes in the same document as the context
8299 * node that are before the context node in document order, excluding any
8300 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8301 * ordered in reverse document order
8302 * This is a faster implementation but internal only since it requires a
8303 * state kept in the parser context: ctxt->ancestor.
8305 * Returns the next element following that axis
8307 static xmlNodePtr
8308 xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
8309 xmlNodePtr cur)
8311 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8312 if (cur == NULL) {
8313 cur = ctxt->context->node;
8314 if (cur == NULL)
8315 return (NULL);
8316 if (cur->type == XML_ATTRIBUTE_NODE) {
8317 cur = cur->parent;
8318 } else if (cur->type == XML_NAMESPACE_DECL) {
8319 xmlNsPtr ns = (xmlNsPtr) cur;
8321 if ((ns->next == NULL) ||
8322 (ns->next->type == XML_NAMESPACE_DECL))
8323 return (NULL);
8324 cur = (xmlNodePtr) ns->next;
8326 ctxt->ancestor = cur->parent;
8328 if (cur->type == XML_NAMESPACE_DECL)
8329 return(NULL);
8330 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8331 cur = cur->prev;
8332 while (cur->prev == NULL) {
8333 cur = cur->parent;
8334 if (cur == NULL)
8335 return (NULL);
8336 if (cur == ctxt->context->doc->children)
8337 return (NULL);
8338 if (cur != ctxt->ancestor)
8339 return (cur);
8340 ctxt->ancestor = cur->parent;
8342 cur = cur->prev;
8343 while (cur->last != NULL)
8344 cur = cur->last;
8345 return (cur);
8349 * xmlXPathNextNamespace:
8350 * @ctxt: the XPath Parser context
8351 * @cur: the current attribute in the traversal
8353 * Traversal function for the "namespace" direction
8354 * the namespace axis contains the namespace nodes of the context node;
8355 * the order of nodes on this axis is implementation-defined; the axis will
8356 * be empty unless the context node is an element
8358 * We keep the XML namespace node at the end of the list.
8360 * Returns the next element following that axis
8362 xmlNodePtr
8363 xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8364 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8365 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
8366 if (cur == NULL) {
8367 if (ctxt->context->tmpNsList != NULL)
8368 xmlFree(ctxt->context->tmpNsList);
8369 ctxt->context->tmpNsList =
8370 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
8371 ctxt->context->tmpNsNr = 0;
8372 if (ctxt->context->tmpNsList != NULL) {
8373 while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
8374 ctxt->context->tmpNsNr++;
8377 return((xmlNodePtr) xmlXPathXMLNamespace);
8379 if (ctxt->context->tmpNsNr > 0) {
8380 return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
8381 } else {
8382 if (ctxt->context->tmpNsList != NULL)
8383 xmlFree(ctxt->context->tmpNsList);
8384 ctxt->context->tmpNsList = NULL;
8385 return(NULL);
8390 * xmlXPathNextAttribute:
8391 * @ctxt: the XPath Parser context
8392 * @cur: the current attribute in the traversal
8394 * Traversal function for the "attribute" direction
8395 * TODO: support DTD inherited default attributes
8397 * Returns the next element following that axis
8399 xmlNodePtr
8400 xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8401 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8402 if (ctxt->context->node == NULL)
8403 return(NULL);
8404 if (ctxt->context->node->type != XML_ELEMENT_NODE)
8405 return(NULL);
8406 if (cur == NULL) {
8407 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
8408 return(NULL);
8409 return((xmlNodePtr)ctxt->context->node->properties);
8411 return((xmlNodePtr)cur->next);
8414 /************************************************************************
8416 * NodeTest Functions *
8418 ************************************************************************/
8420 #define IS_FUNCTION 200
8423 /************************************************************************
8425 * Implicit tree core function library *
8427 ************************************************************************/
8430 * xmlXPathRoot:
8431 * @ctxt: the XPath Parser context
8433 * Initialize the context to the root of the document
8435 void
8436 xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
8437 if ((ctxt == NULL) || (ctxt->context == NULL))
8438 return;
8439 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8440 (xmlNodePtr) ctxt->context->doc));
8443 /************************************************************************
8445 * The explicit core function library *
8446 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
8448 ************************************************************************/
8452 * xmlXPathLastFunction:
8453 * @ctxt: the XPath Parser context
8454 * @nargs: the number of arguments
8456 * Implement the last() XPath function
8457 * number last()
8458 * The last function returns the number of nodes in the context node list.
8460 void
8461 xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8462 CHECK_ARITY(0);
8463 if (ctxt->context->contextSize >= 0) {
8464 valuePush(ctxt,
8465 xmlXPathCacheNewFloat(ctxt->context,
8466 (double) ctxt->context->contextSize));
8467 #ifdef DEBUG_EXPR
8468 xmlGenericError(xmlGenericErrorContext,
8469 "last() : %d\n", ctxt->context->contextSize);
8470 #endif
8471 } else {
8472 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
8477 * xmlXPathPositionFunction:
8478 * @ctxt: the XPath Parser context
8479 * @nargs: the number of arguments
8481 * Implement the position() XPath function
8482 * number position()
8483 * The position function returns the position of the context node in the
8484 * context node list. The first position is 1, and so the last position
8485 * will be equal to last().
8487 void
8488 xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8489 CHECK_ARITY(0);
8490 if (ctxt->context->proximityPosition >= 0) {
8491 valuePush(ctxt,
8492 xmlXPathCacheNewFloat(ctxt->context,
8493 (double) ctxt->context->proximityPosition));
8494 #ifdef DEBUG_EXPR
8495 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
8496 ctxt->context->proximityPosition);
8497 #endif
8498 } else {
8499 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
8504 * xmlXPathCountFunction:
8505 * @ctxt: the XPath Parser context
8506 * @nargs: the number of arguments
8508 * Implement the count() XPath function
8509 * number count(node-set)
8511 void
8512 xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8513 xmlXPathObjectPtr cur;
8515 CHECK_ARITY(1);
8516 if ((ctxt->value == NULL) ||
8517 ((ctxt->value->type != XPATH_NODESET) &&
8518 (ctxt->value->type != XPATH_XSLT_TREE)))
8519 XP_ERROR(XPATH_INVALID_TYPE);
8520 cur = valuePop(ctxt);
8522 if ((cur == NULL) || (cur->nodesetval == NULL))
8523 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8524 else
8525 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8526 (double) cur->nodesetval->nodeNr));
8527 xmlXPathReleaseObject(ctxt->context, cur);
8531 * xmlXPathGetElementsByIds:
8532 * @doc: the document
8533 * @ids: a whitespace separated list of IDs
8535 * Selects elements by their unique ID.
8537 * Returns a node-set of selected elements.
8539 static xmlNodeSetPtr
8540 xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
8541 xmlNodeSetPtr ret;
8542 const xmlChar *cur = ids;
8543 xmlChar *ID;
8544 xmlAttrPtr attr;
8545 xmlNodePtr elem = NULL;
8547 if (ids == NULL) return(NULL);
8549 ret = xmlXPathNodeSetCreate(NULL);
8550 if (ret == NULL)
8551 return(ret);
8553 while (IS_BLANK_CH(*cur)) cur++;
8554 while (*cur != 0) {
8555 while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
8556 cur++;
8558 ID = xmlStrndup(ids, cur - ids);
8559 if (ID != NULL) {
8561 * We used to check the fact that the value passed
8562 * was an NCName, but this generated much troubles for
8563 * me and Aleksey Sanin, people blatantly violated that
8564 * constraint, like Visa3D spec.
8565 * if (xmlValidateNCName(ID, 1) == 0)
8567 attr = xmlGetID(doc, ID);
8568 if (attr != NULL) {
8569 if (attr->type == XML_ATTRIBUTE_NODE)
8570 elem = attr->parent;
8571 else if (attr->type == XML_ELEMENT_NODE)
8572 elem = (xmlNodePtr) attr;
8573 else
8574 elem = NULL;
8575 /* TODO: Check memory error. */
8576 if (elem != NULL)
8577 xmlXPathNodeSetAdd(ret, elem);
8579 xmlFree(ID);
8582 while (IS_BLANK_CH(*cur)) cur++;
8583 ids = cur;
8585 return(ret);
8589 * xmlXPathIdFunction:
8590 * @ctxt: the XPath Parser context
8591 * @nargs: the number of arguments
8593 * Implement the id() XPath function
8594 * node-set id(object)
8595 * The id function selects elements by their unique ID
8596 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
8597 * then the result is the union of the result of applying id to the
8598 * string value of each of the nodes in the argument node-set. When the
8599 * argument to id is of any other type, the argument is converted to a
8600 * string as if by a call to the string function; the string is split
8601 * into a whitespace-separated list of tokens (whitespace is any sequence
8602 * of characters matching the production S); the result is a node-set
8603 * containing the elements in the same document as the context node that
8604 * have a unique ID equal to any of the tokens in the list.
8606 void
8607 xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8608 xmlChar *tokens;
8609 xmlNodeSetPtr ret;
8610 xmlXPathObjectPtr obj;
8612 CHECK_ARITY(1);
8613 obj = valuePop(ctxt);
8614 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8615 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
8616 xmlNodeSetPtr ns;
8617 int i;
8619 /* TODO: Check memory error. */
8620 ret = xmlXPathNodeSetCreate(NULL);
8622 if (obj->nodesetval != NULL) {
8623 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
8624 tokens =
8625 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
8626 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
8627 /* TODO: Check memory error. */
8628 ret = xmlXPathNodeSetMerge(ret, ns);
8629 xmlXPathFreeNodeSet(ns);
8630 if (tokens != NULL)
8631 xmlFree(tokens);
8634 xmlXPathReleaseObject(ctxt->context, obj);
8635 valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8636 return;
8638 obj = xmlXPathCacheConvertString(ctxt->context, obj);
8639 if (obj == NULL) return;
8640 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
8641 valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8642 xmlXPathReleaseObject(ctxt->context, obj);
8643 return;
8647 * xmlXPathLocalNameFunction:
8648 * @ctxt: the XPath Parser context
8649 * @nargs: the number of arguments
8651 * Implement the local-name() XPath function
8652 * string local-name(node-set?)
8653 * The local-name function returns a string containing the local part
8654 * of the name of the node in the argument node-set that is first in
8655 * document order. If the node-set is empty or the first node has no
8656 * name, an empty string is returned. If the argument is omitted it
8657 * defaults to the context node.
8659 void
8660 xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8661 xmlXPathObjectPtr cur;
8663 if (ctxt == NULL) return;
8665 if (nargs == 0) {
8666 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8667 ctxt->context->node));
8668 nargs = 1;
8671 CHECK_ARITY(1);
8672 if ((ctxt->value == NULL) ||
8673 ((ctxt->value->type != XPATH_NODESET) &&
8674 (ctxt->value->type != XPATH_XSLT_TREE)))
8675 XP_ERROR(XPATH_INVALID_TYPE);
8676 cur = valuePop(ctxt);
8678 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8679 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8680 } else {
8681 int i = 0; /* Should be first in document order !!!!! */
8682 switch (cur->nodesetval->nodeTab[i]->type) {
8683 case XML_ELEMENT_NODE:
8684 case XML_ATTRIBUTE_NODE:
8685 case XML_PI_NODE:
8686 if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8687 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8688 else
8689 valuePush(ctxt,
8690 xmlXPathCacheNewString(ctxt->context,
8691 cur->nodesetval->nodeTab[i]->name));
8692 break;
8693 case XML_NAMESPACE_DECL:
8694 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8695 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
8696 break;
8697 default:
8698 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8701 xmlXPathReleaseObject(ctxt->context, cur);
8705 * xmlXPathNamespaceURIFunction:
8706 * @ctxt: the XPath Parser context
8707 * @nargs: the number of arguments
8709 * Implement the namespace-uri() XPath function
8710 * string namespace-uri(node-set?)
8711 * The namespace-uri function returns a string containing the
8712 * namespace URI of the expanded name of the node in the argument
8713 * node-set that is first in document order. If the node-set is empty,
8714 * the first node has no name, or the expanded name has no namespace
8715 * URI, an empty string is returned. If the argument is omitted it
8716 * defaults to the context node.
8718 void
8719 xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8720 xmlXPathObjectPtr cur;
8722 if (ctxt == NULL) return;
8724 if (nargs == 0) {
8725 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8726 ctxt->context->node));
8727 nargs = 1;
8729 CHECK_ARITY(1);
8730 if ((ctxt->value == NULL) ||
8731 ((ctxt->value->type != XPATH_NODESET) &&
8732 (ctxt->value->type != XPATH_XSLT_TREE)))
8733 XP_ERROR(XPATH_INVALID_TYPE);
8734 cur = valuePop(ctxt);
8736 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8737 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8738 } else {
8739 int i = 0; /* Should be first in document order !!!!! */
8740 switch (cur->nodesetval->nodeTab[i]->type) {
8741 case XML_ELEMENT_NODE:
8742 case XML_ATTRIBUTE_NODE:
8743 if (cur->nodesetval->nodeTab[i]->ns == NULL)
8744 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8745 else
8746 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8747 cur->nodesetval->nodeTab[i]->ns->href));
8748 break;
8749 default:
8750 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8753 xmlXPathReleaseObject(ctxt->context, cur);
8757 * xmlXPathNameFunction:
8758 * @ctxt: the XPath Parser context
8759 * @nargs: the number of arguments
8761 * Implement the name() XPath function
8762 * string name(node-set?)
8763 * The name function returns a string containing a QName representing
8764 * the name of the node in the argument node-set that is first in document
8765 * order. The QName must represent the name with respect to the namespace
8766 * declarations in effect on the node whose name is being represented.
8767 * Typically, this will be the form in which the name occurred in the XML
8768 * source. This need not be the case if there are namespace declarations
8769 * in effect on the node that associate multiple prefixes with the same
8770 * namespace. However, an implementation may include information about
8771 * the original prefix in its representation of nodes; in this case, an
8772 * implementation can ensure that the returned string is always the same
8773 * as the QName used in the XML source. If the argument it omitted it
8774 * defaults to the context node.
8775 * Libxml keep the original prefix so the "real qualified name" used is
8776 * returned.
8778 static void
8779 xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
8781 xmlXPathObjectPtr cur;
8783 if (nargs == 0) {
8784 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8785 ctxt->context->node));
8786 nargs = 1;
8789 CHECK_ARITY(1);
8790 if ((ctxt->value == NULL) ||
8791 ((ctxt->value->type != XPATH_NODESET) &&
8792 (ctxt->value->type != XPATH_XSLT_TREE)))
8793 XP_ERROR(XPATH_INVALID_TYPE);
8794 cur = valuePop(ctxt);
8796 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8797 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8798 } else {
8799 int i = 0; /* Should be first in document order !!!!! */
8801 switch (cur->nodesetval->nodeTab[i]->type) {
8802 case XML_ELEMENT_NODE:
8803 case XML_ATTRIBUTE_NODE:
8804 if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8805 valuePush(ctxt,
8806 xmlXPathCacheNewCString(ctxt->context, ""));
8807 else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
8808 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
8809 valuePush(ctxt,
8810 xmlXPathCacheNewString(ctxt->context,
8811 cur->nodesetval->nodeTab[i]->name));
8812 } else {
8813 xmlChar *fullname;
8815 fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
8816 cur->nodesetval->nodeTab[i]->ns->prefix,
8817 NULL, 0);
8818 if (fullname == cur->nodesetval->nodeTab[i]->name)
8819 fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
8820 if (fullname == NULL)
8821 xmlXPathPErrMemory(ctxt, NULL);
8822 valuePush(ctxt, xmlXPathCacheWrapString(
8823 ctxt->context, fullname));
8825 break;
8826 default:
8827 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8828 cur->nodesetval->nodeTab[i]));
8829 xmlXPathLocalNameFunction(ctxt, 1);
8832 xmlXPathReleaseObject(ctxt->context, cur);
8837 * xmlXPathStringFunction:
8838 * @ctxt: the XPath Parser context
8839 * @nargs: the number of arguments
8841 * Implement the string() XPath function
8842 * string string(object?)
8843 * The string function converts an object to a string as follows:
8844 * - A node-set is converted to a string by returning the value of
8845 * the node in the node-set that is first in document order.
8846 * If the node-set is empty, an empty string is returned.
8847 * - A number is converted to a string as follows
8848 * + NaN is converted to the string NaN
8849 * + positive zero is converted to the string 0
8850 * + negative zero is converted to the string 0
8851 * + positive infinity is converted to the string Infinity
8852 * + negative infinity is converted to the string -Infinity
8853 * + if the number is an integer, the number is represented in
8854 * decimal form as a Number with no decimal point and no leading
8855 * zeros, preceded by a minus sign (-) if the number is negative
8856 * + otherwise, the number is represented in decimal form as a
8857 * Number including a decimal point with at least one digit
8858 * before the decimal point and at least one digit after the
8859 * decimal point, preceded by a minus sign (-) if the number
8860 * is negative; there must be no leading zeros before the decimal
8861 * point apart possibly from the one required digit immediately
8862 * before the decimal point; beyond the one required digit
8863 * after the decimal point there must be as many, but only as
8864 * many, more digits as are needed to uniquely distinguish the
8865 * number from all other IEEE 754 numeric values.
8866 * - The boolean false value is converted to the string false.
8867 * The boolean true value is converted to the string true.
8869 * If the argument is omitted, it defaults to a node-set with the
8870 * context node as its only member.
8872 void
8873 xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8874 xmlXPathObjectPtr cur;
8876 if (ctxt == NULL) return;
8877 if (nargs == 0) {
8878 valuePush(ctxt,
8879 xmlXPathCacheWrapString(ctxt->context,
8880 xmlXPathCastNodeToString(ctxt->context->node)));
8881 return;
8884 CHECK_ARITY(1);
8885 cur = valuePop(ctxt);
8886 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8887 valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
8891 * xmlXPathStringLengthFunction:
8892 * @ctxt: the XPath Parser context
8893 * @nargs: the number of arguments
8895 * Implement the string-length() XPath function
8896 * number string-length(string?)
8897 * The string-length returns the number of characters in the string
8898 * (see [3.6 Strings]). If the argument is omitted, it defaults to
8899 * the context node converted to a string, in other words the value
8900 * of the context node.
8902 void
8903 xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8904 xmlXPathObjectPtr cur;
8906 if (nargs == 0) {
8907 if ((ctxt == NULL) || (ctxt->context == NULL))
8908 return;
8909 if (ctxt->context->node == NULL) {
8910 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
8911 } else {
8912 xmlChar *content;
8914 content = xmlXPathCastNodeToString(ctxt->context->node);
8915 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8916 xmlUTF8Strlen(content)));
8917 xmlFree(content);
8919 return;
8921 CHECK_ARITY(1);
8922 CAST_TO_STRING;
8923 CHECK_TYPE(XPATH_STRING);
8924 cur = valuePop(ctxt);
8925 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8926 xmlUTF8Strlen(cur->stringval)));
8927 xmlXPathReleaseObject(ctxt->context, cur);
8931 * xmlXPathConcatFunction:
8932 * @ctxt: the XPath Parser context
8933 * @nargs: the number of arguments
8935 * Implement the concat() XPath function
8936 * string concat(string, string, string*)
8937 * The concat function returns the concatenation of its arguments.
8939 void
8940 xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8941 xmlXPathObjectPtr cur, newobj;
8942 xmlChar *tmp;
8944 if (ctxt == NULL) return;
8945 if (nargs < 2) {
8946 CHECK_ARITY(2);
8949 CAST_TO_STRING;
8950 cur = valuePop(ctxt);
8951 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
8952 xmlXPathReleaseObject(ctxt->context, cur);
8953 return;
8955 nargs--;
8957 while (nargs > 0) {
8958 CAST_TO_STRING;
8959 newobj = valuePop(ctxt);
8960 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
8961 xmlXPathReleaseObject(ctxt->context, newobj);
8962 xmlXPathReleaseObject(ctxt->context, cur);
8963 XP_ERROR(XPATH_INVALID_TYPE);
8965 tmp = xmlStrcat(newobj->stringval, cur->stringval);
8966 newobj->stringval = cur->stringval;
8967 cur->stringval = tmp;
8968 xmlXPathReleaseObject(ctxt->context, newobj);
8969 nargs--;
8971 valuePush(ctxt, cur);
8975 * xmlXPathContainsFunction:
8976 * @ctxt: the XPath Parser context
8977 * @nargs: the number of arguments
8979 * Implement the contains() XPath function
8980 * boolean contains(string, string)
8981 * The contains function returns true if the first argument string
8982 * contains the second argument string, and otherwise returns false.
8984 void
8985 xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8986 xmlXPathObjectPtr hay, needle;
8988 CHECK_ARITY(2);
8989 CAST_TO_STRING;
8990 CHECK_TYPE(XPATH_STRING);
8991 needle = valuePop(ctxt);
8992 CAST_TO_STRING;
8993 hay = valuePop(ctxt);
8995 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
8996 xmlXPathReleaseObject(ctxt->context, hay);
8997 xmlXPathReleaseObject(ctxt->context, needle);
8998 XP_ERROR(XPATH_INVALID_TYPE);
9000 if (xmlStrstr(hay->stringval, needle->stringval))
9001 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9002 else
9003 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9004 xmlXPathReleaseObject(ctxt->context, hay);
9005 xmlXPathReleaseObject(ctxt->context, needle);
9009 * xmlXPathStartsWithFunction:
9010 * @ctxt: the XPath Parser context
9011 * @nargs: the number of arguments
9013 * Implement the starts-with() XPath function
9014 * boolean starts-with(string, string)
9015 * The starts-with function returns true if the first argument string
9016 * starts with the second argument string, and otherwise returns false.
9018 void
9019 xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9020 xmlXPathObjectPtr hay, needle;
9021 int n;
9023 CHECK_ARITY(2);
9024 CAST_TO_STRING;
9025 CHECK_TYPE(XPATH_STRING);
9026 needle = valuePop(ctxt);
9027 CAST_TO_STRING;
9028 hay = valuePop(ctxt);
9030 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
9031 xmlXPathReleaseObject(ctxt->context, hay);
9032 xmlXPathReleaseObject(ctxt->context, needle);
9033 XP_ERROR(XPATH_INVALID_TYPE);
9035 n = xmlStrlen(needle->stringval);
9036 if (xmlStrncmp(hay->stringval, needle->stringval, n))
9037 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9038 else
9039 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9040 xmlXPathReleaseObject(ctxt->context, hay);
9041 xmlXPathReleaseObject(ctxt->context, needle);
9045 * xmlXPathSubstringFunction:
9046 * @ctxt: the XPath Parser context
9047 * @nargs: the number of arguments
9049 * Implement the substring() XPath function
9050 * string substring(string, number, number?)
9051 * The substring function returns the substring of the first argument
9052 * starting at the position specified in the second argument with
9053 * length specified in the third argument. For example,
9054 * substring("12345",2,3) returns "234". If the third argument is not
9055 * specified, it returns the substring starting at the position specified
9056 * in the second argument and continuing to the end of the string. For
9057 * example, substring("12345",2) returns "2345". More precisely, each
9058 * character in the string (see [3.6 Strings]) is considered to have a
9059 * numeric position: the position of the first character is 1, the position
9060 * of the second character is 2 and so on. The returned substring contains
9061 * those characters for which the position of the character is greater than
9062 * or equal to the second argument and, if the third argument is specified,
9063 * less than the sum of the second and third arguments; the comparisons
9064 * and addition used for the above follow the standard IEEE 754 rules. Thus:
9065 * - substring("12345", 1.5, 2.6) returns "234"
9066 * - substring("12345", 0, 3) returns "12"
9067 * - substring("12345", 0 div 0, 3) returns ""
9068 * - substring("12345", 1, 0 div 0) returns ""
9069 * - substring("12345", -42, 1 div 0) returns "12345"
9070 * - substring("12345", -1 div 0, 1 div 0) returns ""
9072 void
9073 xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9074 xmlXPathObjectPtr str, start, len;
9075 double le=0, in;
9076 int i = 1, j = INT_MAX;
9078 if (nargs < 2) {
9079 CHECK_ARITY(2);
9081 if (nargs > 3) {
9082 CHECK_ARITY(3);
9085 * take care of possible last (position) argument
9087 if (nargs == 3) {
9088 CAST_TO_NUMBER;
9089 CHECK_TYPE(XPATH_NUMBER);
9090 len = valuePop(ctxt);
9091 le = len->floatval;
9092 xmlXPathReleaseObject(ctxt->context, len);
9095 CAST_TO_NUMBER;
9096 CHECK_TYPE(XPATH_NUMBER);
9097 start = valuePop(ctxt);
9098 in = start->floatval;
9099 xmlXPathReleaseObject(ctxt->context, start);
9100 CAST_TO_STRING;
9101 CHECK_TYPE(XPATH_STRING);
9102 str = valuePop(ctxt);
9104 if (!(in < INT_MAX)) { /* Logical NOT to handle NaNs */
9105 i = INT_MAX;
9106 } else if (in >= 1.0) {
9107 i = (int)in;
9108 if (in - floor(in) >= 0.5)
9109 i += 1;
9112 if (nargs == 3) {
9113 double rin, rle, end;
9115 rin = floor(in);
9116 if (in - rin >= 0.5)
9117 rin += 1.0;
9119 rle = floor(le);
9120 if (le - rle >= 0.5)
9121 rle += 1.0;
9123 end = rin + rle;
9124 if (!(end >= 1.0)) { /* Logical NOT to handle NaNs */
9125 j = 1;
9126 } else if (end < INT_MAX) {
9127 j = (int)end;
9131 if (i < j) {
9132 xmlChar *ret = xmlUTF8Strsub(str->stringval, i - 1, j - i);
9133 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
9134 xmlFree(ret);
9135 } else {
9136 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
9139 xmlXPathReleaseObject(ctxt->context, str);
9143 * xmlXPathSubstringBeforeFunction:
9144 * @ctxt: the XPath Parser context
9145 * @nargs: the number of arguments
9147 * Implement the substring-before() XPath function
9148 * string substring-before(string, string)
9149 * The substring-before function returns the substring of the first
9150 * argument string that precedes the first occurrence of the second
9151 * argument string in the first argument string, or the empty string
9152 * if the first argument string does not contain the second argument
9153 * string. For example, substring-before("1999/04/01","/") returns 1999.
9155 void
9156 xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9157 xmlXPathObjectPtr str;
9158 xmlXPathObjectPtr find;
9159 xmlBufPtr target;
9160 const xmlChar *point;
9161 int offset;
9163 CHECK_ARITY(2);
9164 CAST_TO_STRING;
9165 find = valuePop(ctxt);
9166 CAST_TO_STRING;
9167 str = valuePop(ctxt);
9169 target = xmlBufCreate();
9170 if (target) {
9171 point = xmlStrstr(str->stringval, find->stringval);
9172 if (point) {
9173 offset = point - str->stringval;
9174 xmlBufAdd(target, str->stringval, offset);
9176 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9177 xmlBufContent(target)));
9178 xmlBufFree(target);
9180 xmlXPathReleaseObject(ctxt->context, str);
9181 xmlXPathReleaseObject(ctxt->context, find);
9185 * xmlXPathSubstringAfterFunction:
9186 * @ctxt: the XPath Parser context
9187 * @nargs: the number of arguments
9189 * Implement the substring-after() XPath function
9190 * string substring-after(string, string)
9191 * The substring-after function returns the substring of the first
9192 * argument string that follows the first occurrence of the second
9193 * argument string in the first argument string, or the empty stringi
9194 * if the first argument string does not contain the second argument
9195 * string. For example, substring-after("1999/04/01","/") returns 04/01,
9196 * and substring-after("1999/04/01","19") returns 99/04/01.
9198 void
9199 xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9200 xmlXPathObjectPtr str;
9201 xmlXPathObjectPtr find;
9202 xmlBufPtr target;
9203 const xmlChar *point;
9204 int offset;
9206 CHECK_ARITY(2);
9207 CAST_TO_STRING;
9208 find = valuePop(ctxt);
9209 CAST_TO_STRING;
9210 str = valuePop(ctxt);
9212 target = xmlBufCreate();
9213 if (target) {
9214 point = xmlStrstr(str->stringval, find->stringval);
9215 if (point) {
9216 offset = point - str->stringval + xmlStrlen(find->stringval);
9217 xmlBufAdd(target, &str->stringval[offset],
9218 xmlStrlen(str->stringval) - offset);
9220 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9221 xmlBufContent(target)));
9222 xmlBufFree(target);
9224 xmlXPathReleaseObject(ctxt->context, str);
9225 xmlXPathReleaseObject(ctxt->context, find);
9229 * xmlXPathNormalizeFunction:
9230 * @ctxt: the XPath Parser context
9231 * @nargs: the number of arguments
9233 * Implement the normalize-space() XPath function
9234 * string normalize-space(string?)
9235 * The normalize-space function returns the argument string with white
9236 * space normalized by stripping leading and trailing whitespace
9237 * and replacing sequences of whitespace characters by a single
9238 * space. Whitespace characters are the same allowed by the S production
9239 * in XML. If the argument is omitted, it defaults to the context
9240 * node converted to a string, in other words the value of the context node.
9242 void
9243 xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9244 xmlChar *source, *target;
9245 int blank;
9247 if (ctxt == NULL) return;
9248 if (nargs == 0) {
9249 /* Use current context node */
9250 valuePush(ctxt,
9251 xmlXPathCacheWrapString(ctxt->context,
9252 xmlXPathCastNodeToString(ctxt->context->node)));
9253 nargs = 1;
9256 CHECK_ARITY(1);
9257 CAST_TO_STRING;
9258 CHECK_TYPE(XPATH_STRING);
9259 source = ctxt->value->stringval;
9260 if (source == NULL)
9261 return;
9262 target = source;
9264 /* Skip leading whitespaces */
9265 while (IS_BLANK_CH(*source))
9266 source++;
9268 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
9269 blank = 0;
9270 while (*source) {
9271 if (IS_BLANK_CH(*source)) {
9272 blank = 1;
9273 } else {
9274 if (blank) {
9275 *target++ = 0x20;
9276 blank = 0;
9278 *target++ = *source;
9280 source++;
9282 *target = 0;
9286 * xmlXPathTranslateFunction:
9287 * @ctxt: the XPath Parser context
9288 * @nargs: the number of arguments
9290 * Implement the translate() XPath function
9291 * string translate(string, string, string)
9292 * The translate function returns the first argument string with
9293 * occurrences of characters in the second argument string replaced
9294 * by the character at the corresponding position in the third argument
9295 * string. For example, translate("bar","abc","ABC") returns the string
9296 * BAr. If there is a character in the second argument string with no
9297 * character at a corresponding position in the third argument string
9298 * (because the second argument string is longer than the third argument
9299 * string), then occurrences of that character in the first argument
9300 * string are removed. For example, translate("--aaa--","abc-","ABC")
9301 * returns "AAA". If a character occurs more than once in second
9302 * argument string, then the first occurrence determines the replacement
9303 * character. If the third argument string is longer than the second
9304 * argument string, then excess characters are ignored.
9306 void
9307 xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9308 xmlXPathObjectPtr str;
9309 xmlXPathObjectPtr from;
9310 xmlXPathObjectPtr to;
9311 xmlBufPtr target;
9312 int offset, max;
9313 int ch;
9314 const xmlChar *point;
9315 xmlChar *cptr;
9317 CHECK_ARITY(3);
9319 CAST_TO_STRING;
9320 to = valuePop(ctxt);
9321 CAST_TO_STRING;
9322 from = valuePop(ctxt);
9323 CAST_TO_STRING;
9324 str = valuePop(ctxt);
9326 target = xmlBufCreate();
9327 if (target) {
9328 max = xmlUTF8Strlen(to->stringval);
9329 for (cptr = str->stringval; (ch=*cptr); ) {
9330 offset = xmlUTF8Strloc(from->stringval, cptr);
9331 if (offset >= 0) {
9332 if (offset < max) {
9333 point = xmlUTF8Strpos(to->stringval, offset);
9334 if (point)
9335 xmlBufAdd(target, point, xmlUTF8Strsize(point, 1));
9337 } else
9338 xmlBufAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
9340 /* Step to next character in input */
9341 cptr++;
9342 if ( ch & 0x80 ) {
9343 /* if not simple ascii, verify proper format */
9344 if ( (ch & 0xc0) != 0xc0 ) {
9345 xmlGenericError(xmlGenericErrorContext,
9346 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9347 /* not asserting an XPath error is probably better */
9348 break;
9350 /* then skip over remaining bytes for this char */
9351 while ( (ch <<= 1) & 0x80 )
9352 if ( (*cptr++ & 0xc0) != 0x80 ) {
9353 xmlGenericError(xmlGenericErrorContext,
9354 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9355 /* not asserting an XPath error is probably better */
9356 break;
9358 if (ch & 0x80) /* must have had error encountered */
9359 break;
9363 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9364 xmlBufContent(target)));
9365 xmlBufFree(target);
9366 xmlXPathReleaseObject(ctxt->context, str);
9367 xmlXPathReleaseObject(ctxt->context, from);
9368 xmlXPathReleaseObject(ctxt->context, to);
9372 * xmlXPathBooleanFunction:
9373 * @ctxt: the XPath Parser context
9374 * @nargs: the number of arguments
9376 * Implement the boolean() XPath function
9377 * boolean boolean(object)
9378 * The boolean function converts its argument to a boolean as follows:
9379 * - a number is true if and only if it is neither positive or
9380 * negative zero nor NaN
9381 * - a node-set is true if and only if it is non-empty
9382 * - a string is true if and only if its length is non-zero
9384 void
9385 xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9386 xmlXPathObjectPtr cur;
9388 CHECK_ARITY(1);
9389 cur = valuePop(ctxt);
9390 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
9391 cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
9392 valuePush(ctxt, cur);
9396 * xmlXPathNotFunction:
9397 * @ctxt: the XPath Parser context
9398 * @nargs: the number of arguments
9400 * Implement the not() XPath function
9401 * boolean not(boolean)
9402 * The not function returns true if its argument is false,
9403 * and false otherwise.
9405 void
9406 xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9407 CHECK_ARITY(1);
9408 CAST_TO_BOOLEAN;
9409 CHECK_TYPE(XPATH_BOOLEAN);
9410 ctxt->value->boolval = ! ctxt->value->boolval;
9414 * xmlXPathTrueFunction:
9415 * @ctxt: the XPath Parser context
9416 * @nargs: the number of arguments
9418 * Implement the true() XPath function
9419 * boolean true()
9421 void
9422 xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9423 CHECK_ARITY(0);
9424 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9428 * xmlXPathFalseFunction:
9429 * @ctxt: the XPath Parser context
9430 * @nargs: the number of arguments
9432 * Implement the false() XPath function
9433 * boolean false()
9435 void
9436 xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9437 CHECK_ARITY(0);
9438 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9442 * xmlXPathLangFunction:
9443 * @ctxt: the XPath Parser context
9444 * @nargs: the number of arguments
9446 * Implement the lang() XPath function
9447 * boolean lang(string)
9448 * The lang function returns true or false depending on whether the
9449 * language of the context node as specified by xml:lang attributes
9450 * is the same as or is a sublanguage of the language specified by
9451 * the argument string. The language of the context node is determined
9452 * by the value of the xml:lang attribute on the context node, or, if
9453 * the context node has no xml:lang attribute, by the value of the
9454 * xml:lang attribute on the nearest ancestor of the context node that
9455 * has an xml:lang attribute. If there is no such attribute, then lang
9456 * returns false. If there is such an attribute, then lang returns
9457 * true if the attribute value is equal to the argument ignoring case,
9458 * or if there is some suffix starting with - such that the attribute
9459 * value is equal to the argument ignoring that suffix of the attribute
9460 * value and ignoring case.
9462 void
9463 xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9464 xmlXPathObjectPtr val = NULL;
9465 const xmlChar *theLang = NULL;
9466 const xmlChar *lang;
9467 int ret = 0;
9468 int i;
9470 CHECK_ARITY(1);
9471 CAST_TO_STRING;
9472 CHECK_TYPE(XPATH_STRING);
9473 val = valuePop(ctxt);
9474 lang = val->stringval;
9475 theLang = xmlNodeGetLang(ctxt->context->node);
9476 if ((theLang != NULL) && (lang != NULL)) {
9477 for (i = 0;lang[i] != 0;i++)
9478 if (toupper(lang[i]) != toupper(theLang[i]))
9479 goto not_equal;
9480 if ((theLang[i] == 0) || (theLang[i] == '-'))
9481 ret = 1;
9483 not_equal:
9484 if (theLang != NULL)
9485 xmlFree((void *)theLang);
9487 xmlXPathReleaseObject(ctxt->context, val);
9488 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
9492 * xmlXPathNumberFunction:
9493 * @ctxt: the XPath Parser context
9494 * @nargs: the number of arguments
9496 * Implement the number() XPath function
9497 * number number(object?)
9499 void
9500 xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9501 xmlXPathObjectPtr cur;
9502 double res;
9504 if (ctxt == NULL) return;
9505 if (nargs == 0) {
9506 if (ctxt->context->node == NULL) {
9507 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
9508 } else {
9509 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
9511 res = xmlXPathStringEvalNumber(content);
9512 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9513 xmlFree(content);
9515 return;
9518 CHECK_ARITY(1);
9519 cur = valuePop(ctxt);
9520 valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
9524 * xmlXPathSumFunction:
9525 * @ctxt: the XPath Parser context
9526 * @nargs: the number of arguments
9528 * Implement the sum() XPath function
9529 * number sum(node-set)
9530 * The sum function returns the sum of the values of the nodes in
9531 * the argument node-set.
9533 void
9534 xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9535 xmlXPathObjectPtr cur;
9536 int i;
9537 double res = 0.0;
9539 CHECK_ARITY(1);
9540 if ((ctxt->value == NULL) ||
9541 ((ctxt->value->type != XPATH_NODESET) &&
9542 (ctxt->value->type != XPATH_XSLT_TREE)))
9543 XP_ERROR(XPATH_INVALID_TYPE);
9544 cur = valuePop(ctxt);
9546 if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
9547 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
9548 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
9551 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9552 xmlXPathReleaseObject(ctxt->context, cur);
9556 * xmlXPathFloorFunction:
9557 * @ctxt: the XPath Parser context
9558 * @nargs: the number of arguments
9560 * Implement the floor() XPath function
9561 * number floor(number)
9562 * The floor function returns the largest (closest to positive infinity)
9563 * number that is not greater than the argument and that is an integer.
9565 void
9566 xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9567 CHECK_ARITY(1);
9568 CAST_TO_NUMBER;
9569 CHECK_TYPE(XPATH_NUMBER);
9571 ctxt->value->floatval = floor(ctxt->value->floatval);
9575 * xmlXPathCeilingFunction:
9576 * @ctxt: the XPath Parser context
9577 * @nargs: the number of arguments
9579 * Implement the ceiling() XPath function
9580 * number ceiling(number)
9581 * The ceiling function returns the smallest (closest to negative infinity)
9582 * number that is not less than the argument and that is an integer.
9584 void
9585 xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9586 CHECK_ARITY(1);
9587 CAST_TO_NUMBER;
9588 CHECK_TYPE(XPATH_NUMBER);
9590 #ifdef _AIX
9591 /* Work around buggy ceil() function on AIX */
9592 ctxt->value->floatval = copysign(ceil(ctxt->value->floatval), ctxt->value->floatval);
9593 #else
9594 ctxt->value->floatval = ceil(ctxt->value->floatval);
9595 #endif
9599 * xmlXPathRoundFunction:
9600 * @ctxt: the XPath Parser context
9601 * @nargs: the number of arguments
9603 * Implement the round() XPath function
9604 * number round(number)
9605 * The round function returns the number that is closest to the
9606 * argument and that is an integer. If there are two such numbers,
9607 * then the one that is closest to positive infinity is returned.
9609 void
9610 xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9611 double f;
9613 CHECK_ARITY(1);
9614 CAST_TO_NUMBER;
9615 CHECK_TYPE(XPATH_NUMBER);
9617 f = ctxt->value->floatval;
9619 if ((f >= -0.5) && (f < 0.5)) {
9620 /* Handles negative zero. */
9621 ctxt->value->floatval *= 0.0;
9623 else {
9624 double rounded = floor(f);
9625 if (f - rounded >= 0.5)
9626 rounded += 1.0;
9627 ctxt->value->floatval = rounded;
9631 /************************************************************************
9633 * The Parser *
9635 ************************************************************************/
9638 * a few forward declarations since we use a recursive call based
9639 * implementation.
9641 static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
9642 static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
9643 static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
9644 static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
9645 static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
9646 int qualified);
9649 * xmlXPathCurrentChar:
9650 * @ctxt: the XPath parser context
9651 * @cur: pointer to the beginning of the char
9652 * @len: pointer to the length of the char read
9654 * The current char value, if using UTF-8 this may actually span multiple
9655 * bytes in the input buffer.
9657 * Returns the current char value and its length
9660 static int
9661 xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
9662 unsigned char c;
9663 unsigned int val;
9664 const xmlChar *cur;
9666 if (ctxt == NULL)
9667 return(0);
9668 cur = ctxt->cur;
9671 * We are supposed to handle UTF8, check it's valid
9672 * From rfc2044: encoding of the Unicode values on UTF-8:
9674 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
9675 * 0000 0000-0000 007F 0xxxxxxx
9676 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
9677 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
9679 * Check for the 0x110000 limit too
9681 c = *cur;
9682 if (c & 0x80) {
9683 if ((cur[1] & 0xc0) != 0x80)
9684 goto encoding_error;
9685 if ((c & 0xe0) == 0xe0) {
9687 if ((cur[2] & 0xc0) != 0x80)
9688 goto encoding_error;
9689 if ((c & 0xf0) == 0xf0) {
9690 if (((c & 0xf8) != 0xf0) ||
9691 ((cur[3] & 0xc0) != 0x80))
9692 goto encoding_error;
9693 /* 4-byte code */
9694 *len = 4;
9695 val = (cur[0] & 0x7) << 18;
9696 val |= (cur[1] & 0x3f) << 12;
9697 val |= (cur[2] & 0x3f) << 6;
9698 val |= cur[3] & 0x3f;
9699 } else {
9700 /* 3-byte code */
9701 *len = 3;
9702 val = (cur[0] & 0xf) << 12;
9703 val |= (cur[1] & 0x3f) << 6;
9704 val |= cur[2] & 0x3f;
9706 } else {
9707 /* 2-byte code */
9708 *len = 2;
9709 val = (cur[0] & 0x1f) << 6;
9710 val |= cur[1] & 0x3f;
9712 if (!IS_CHAR(val)) {
9713 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
9715 return(val);
9716 } else {
9717 /* 1-byte code */
9718 *len = 1;
9719 return(*cur);
9721 encoding_error:
9723 * If we detect an UTF8 error that probably means that the
9724 * input encoding didn't get properly advertised in the
9725 * declaration header. Report the error and switch the encoding
9726 * to ISO-Latin-1 (if you don't like this policy, just declare the
9727 * encoding !)
9729 *len = 0;
9730 XP_ERROR0(XPATH_ENCODING_ERROR);
9734 * xmlXPathParseNCName:
9735 * @ctxt: the XPath Parser context
9737 * parse an XML namespace non qualified name.
9739 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
9741 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
9742 * CombiningChar | Extender
9744 * Returns the namespace name or NULL
9747 xmlChar *
9748 xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
9749 const xmlChar *in;
9750 xmlChar *ret;
9751 int count = 0;
9753 if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9755 * Accelerator for simple ASCII names
9757 in = ctxt->cur;
9758 if (((*in >= 0x61) && (*in <= 0x7A)) ||
9759 ((*in >= 0x41) && (*in <= 0x5A)) ||
9760 (*in == '_')) {
9761 in++;
9762 while (((*in >= 0x61) && (*in <= 0x7A)) ||
9763 ((*in >= 0x41) && (*in <= 0x5A)) ||
9764 ((*in >= 0x30) && (*in <= 0x39)) ||
9765 (*in == '_') || (*in == '.') ||
9766 (*in == '-'))
9767 in++;
9768 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
9769 (*in == '[') || (*in == ']') || (*in == ':') ||
9770 (*in == '@') || (*in == '*')) {
9771 count = in - ctxt->cur;
9772 if (count == 0)
9773 return(NULL);
9774 ret = xmlStrndup(ctxt->cur, count);
9775 ctxt->cur = in;
9776 return(ret);
9779 return(xmlXPathParseNameComplex(ctxt, 0));
9784 * xmlXPathParseQName:
9785 * @ctxt: the XPath Parser context
9786 * @prefix: a xmlChar **
9788 * parse an XML qualified name
9790 * [NS 5] QName ::= (Prefix ':')? LocalPart
9792 * [NS 6] Prefix ::= NCName
9794 * [NS 7] LocalPart ::= NCName
9796 * Returns the function returns the local part, and prefix is updated
9797 * to get the Prefix if any.
9800 static xmlChar *
9801 xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
9802 xmlChar *ret = NULL;
9804 *prefix = NULL;
9805 ret = xmlXPathParseNCName(ctxt);
9806 if (ret && CUR == ':') {
9807 *prefix = ret;
9808 NEXT;
9809 ret = xmlXPathParseNCName(ctxt);
9811 return(ret);
9815 * xmlXPathParseName:
9816 * @ctxt: the XPath Parser context
9818 * parse an XML name
9820 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
9821 * CombiningChar | Extender
9823 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
9825 * Returns the namespace name or NULL
9828 xmlChar *
9829 xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
9830 const xmlChar *in;
9831 xmlChar *ret;
9832 size_t count = 0;
9834 if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9836 * Accelerator for simple ASCII names
9838 in = ctxt->cur;
9839 if (((*in >= 0x61) && (*in <= 0x7A)) ||
9840 ((*in >= 0x41) && (*in <= 0x5A)) ||
9841 (*in == '_') || (*in == ':')) {
9842 in++;
9843 while (((*in >= 0x61) && (*in <= 0x7A)) ||
9844 ((*in >= 0x41) && (*in <= 0x5A)) ||
9845 ((*in >= 0x30) && (*in <= 0x39)) ||
9846 (*in == '_') || (*in == '-') ||
9847 (*in == ':') || (*in == '.'))
9848 in++;
9849 if ((*in > 0) && (*in < 0x80)) {
9850 count = in - ctxt->cur;
9851 if (count > XML_MAX_NAME_LENGTH) {
9852 ctxt->cur = in;
9853 XP_ERRORNULL(XPATH_EXPR_ERROR);
9855 ret = xmlStrndup(ctxt->cur, count);
9856 ctxt->cur = in;
9857 return(ret);
9860 return(xmlXPathParseNameComplex(ctxt, 1));
9863 static xmlChar *
9864 xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
9865 xmlChar buf[XML_MAX_NAMELEN + 5];
9866 int len = 0, l;
9867 int c;
9870 * Handler for more complex cases
9872 c = CUR_CHAR(l);
9873 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
9874 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
9875 (c == '*') || /* accelerators */
9876 (!IS_LETTER(c) && (c != '_') &&
9877 ((!qualified) || (c != ':')))) {
9878 return(NULL);
9881 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
9882 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
9883 (c == '.') || (c == '-') ||
9884 (c == '_') || ((qualified) && (c == ':')) ||
9885 (IS_COMBINING(c)) ||
9886 (IS_EXTENDER(c)))) {
9887 COPY_BUF(l,buf,len,c);
9888 NEXTL(l);
9889 c = CUR_CHAR(l);
9890 if (len >= XML_MAX_NAMELEN) {
9892 * Okay someone managed to make a huge name, so he's ready to pay
9893 * for the processing speed.
9895 xmlChar *buffer;
9896 int max = len * 2;
9898 if (len > XML_MAX_NAME_LENGTH) {
9899 XP_ERRORNULL(XPATH_EXPR_ERROR);
9901 buffer = (xmlChar *) xmlMallocAtomic(max);
9902 if (buffer == NULL) {
9903 XP_ERRORNULL(XPATH_MEMORY_ERROR);
9905 memcpy(buffer, buf, len);
9906 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
9907 (c == '.') || (c == '-') ||
9908 (c == '_') || ((qualified) && (c == ':')) ||
9909 (IS_COMBINING(c)) ||
9910 (IS_EXTENDER(c))) {
9911 if (len + 10 > max) {
9912 xmlChar *tmp;
9913 if (max > XML_MAX_NAME_LENGTH) {
9914 xmlFree(buffer);
9915 XP_ERRORNULL(XPATH_EXPR_ERROR);
9917 max *= 2;
9918 tmp = (xmlChar *) xmlRealloc(buffer, max);
9919 if (tmp == NULL) {
9920 xmlFree(buffer);
9921 XP_ERRORNULL(XPATH_MEMORY_ERROR);
9923 buffer = tmp;
9925 COPY_BUF(l,buffer,len,c);
9926 NEXTL(l);
9927 c = CUR_CHAR(l);
9929 buffer[len] = 0;
9930 return(buffer);
9933 if (len == 0)
9934 return(NULL);
9935 return(xmlStrndup(buf, len));
9938 #define MAX_FRAC 20
9941 * xmlXPathStringEvalNumber:
9942 * @str: A string to scan
9944 * [30a] Float ::= Number ('e' Digits?)?
9946 * [30] Number ::= Digits ('.' Digits?)?
9947 * | '.' Digits
9948 * [31] Digits ::= [0-9]+
9950 * Compile a Number in the string
9951 * In complement of the Number expression, this function also handles
9952 * negative values : '-' Number.
9954 * Returns the double value.
9956 double
9957 xmlXPathStringEvalNumber(const xmlChar *str) {
9958 const xmlChar *cur = str;
9959 double ret;
9960 int ok = 0;
9961 int isneg = 0;
9962 int exponent = 0;
9963 int is_exponent_negative = 0;
9964 #ifdef __GNUC__
9965 unsigned long tmp = 0;
9966 double temp;
9967 #endif
9968 if (cur == NULL) return(0);
9969 while (IS_BLANK_CH(*cur)) cur++;
9970 if (*cur == '-') {
9971 isneg = 1;
9972 cur++;
9974 if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
9975 return(xmlXPathNAN);
9978 #ifdef __GNUC__
9980 * tmp/temp is a workaround against a gcc compiler bug
9981 * http://veillard.com/gcc.bug
9983 ret = 0;
9984 while ((*cur >= '0') && (*cur <= '9')) {
9985 ret = ret * 10;
9986 tmp = (*cur - '0');
9987 ok = 1;
9988 cur++;
9989 temp = (double) tmp;
9990 ret = ret + temp;
9992 #else
9993 ret = 0;
9994 while ((*cur >= '0') && (*cur <= '9')) {
9995 ret = ret * 10 + (*cur - '0');
9996 ok = 1;
9997 cur++;
9999 #endif
10001 if (*cur == '.') {
10002 int v, frac = 0, max;
10003 double fraction = 0;
10005 cur++;
10006 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
10007 return(xmlXPathNAN);
10009 while (*cur == '0') {
10010 frac = frac + 1;
10011 cur++;
10013 max = frac + MAX_FRAC;
10014 while (((*cur >= '0') && (*cur <= '9')) && (frac < max)) {
10015 v = (*cur - '0');
10016 fraction = fraction * 10 + v;
10017 frac = frac + 1;
10018 cur++;
10020 fraction /= pow(10.0, frac);
10021 ret = ret + fraction;
10022 while ((*cur >= '0') && (*cur <= '9'))
10023 cur++;
10025 if ((*cur == 'e') || (*cur == 'E')) {
10026 cur++;
10027 if (*cur == '-') {
10028 is_exponent_negative = 1;
10029 cur++;
10030 } else if (*cur == '+') {
10031 cur++;
10033 while ((*cur >= '0') && (*cur <= '9')) {
10034 if (exponent < 1000000)
10035 exponent = exponent * 10 + (*cur - '0');
10036 cur++;
10039 while (IS_BLANK_CH(*cur)) cur++;
10040 if (*cur != 0) return(xmlXPathNAN);
10041 if (isneg) ret = -ret;
10042 if (is_exponent_negative) exponent = -exponent;
10043 ret *= pow(10.0, (double)exponent);
10044 return(ret);
10048 * xmlXPathCompNumber:
10049 * @ctxt: the XPath Parser context
10051 * [30] Number ::= Digits ('.' Digits?)?
10052 * | '.' Digits
10053 * [31] Digits ::= [0-9]+
10055 * Compile a Number, then push it on the stack
10058 static void
10059 xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
10061 double ret = 0.0;
10062 int ok = 0;
10063 int exponent = 0;
10064 int is_exponent_negative = 0;
10065 xmlXPathObjectPtr num;
10066 #ifdef __GNUC__
10067 unsigned long tmp = 0;
10068 double temp;
10069 #endif
10071 CHECK_ERROR;
10072 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
10073 XP_ERROR(XPATH_NUMBER_ERROR);
10075 #ifdef __GNUC__
10077 * tmp/temp is a workaround against a gcc compiler bug
10078 * http://veillard.com/gcc.bug
10080 ret = 0;
10081 while ((CUR >= '0') && (CUR <= '9')) {
10082 ret = ret * 10;
10083 tmp = (CUR - '0');
10084 ok = 1;
10085 NEXT;
10086 temp = (double) tmp;
10087 ret = ret + temp;
10089 #else
10090 ret = 0;
10091 while ((CUR >= '0') && (CUR <= '9')) {
10092 ret = ret * 10 + (CUR - '0');
10093 ok = 1;
10094 NEXT;
10096 #endif
10097 if (CUR == '.') {
10098 int v, frac = 0, max;
10099 double fraction = 0;
10101 NEXT;
10102 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
10103 XP_ERROR(XPATH_NUMBER_ERROR);
10105 while (CUR == '0') {
10106 frac = frac + 1;
10107 NEXT;
10109 max = frac + MAX_FRAC;
10110 while ((CUR >= '0') && (CUR <= '9') && (frac < max)) {
10111 v = (CUR - '0');
10112 fraction = fraction * 10 + v;
10113 frac = frac + 1;
10114 NEXT;
10116 fraction /= pow(10.0, frac);
10117 ret = ret + fraction;
10118 while ((CUR >= '0') && (CUR <= '9'))
10119 NEXT;
10121 if ((CUR == 'e') || (CUR == 'E')) {
10122 NEXT;
10123 if (CUR == '-') {
10124 is_exponent_negative = 1;
10125 NEXT;
10126 } else if (CUR == '+') {
10127 NEXT;
10129 while ((CUR >= '0') && (CUR <= '9')) {
10130 if (exponent < 1000000)
10131 exponent = exponent * 10 + (CUR - '0');
10132 NEXT;
10134 if (is_exponent_negative)
10135 exponent = -exponent;
10136 ret *= pow(10.0, (double) exponent);
10138 num = xmlXPathCacheNewFloat(ctxt->context, ret);
10139 if (num == NULL) {
10140 ctxt->error = XPATH_MEMORY_ERROR;
10141 } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, num,
10142 NULL) == -1) {
10143 xmlXPathReleaseObject(ctxt->context, num);
10148 * xmlXPathParseLiteral:
10149 * @ctxt: the XPath Parser context
10151 * Parse a Literal
10153 * [29] Literal ::= '"' [^"]* '"'
10154 * | "'" [^']* "'"
10156 * Returns the value found or NULL in case of error
10158 static xmlChar *
10159 xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
10160 const xmlChar *q;
10161 xmlChar *ret = NULL;
10163 if (CUR == '"') {
10164 NEXT;
10165 q = CUR_PTR;
10166 while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10167 NEXT;
10168 if (!IS_CHAR_CH(CUR)) {
10169 XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10170 } else {
10171 ret = xmlStrndup(q, CUR_PTR - q);
10172 NEXT;
10174 } else if (CUR == '\'') {
10175 NEXT;
10176 q = CUR_PTR;
10177 while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10178 NEXT;
10179 if (!IS_CHAR_CH(CUR)) {
10180 XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10181 } else {
10182 ret = xmlStrndup(q, CUR_PTR - q);
10183 NEXT;
10185 } else {
10186 XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
10188 return(ret);
10192 * xmlXPathCompLiteral:
10193 * @ctxt: the XPath Parser context
10195 * Parse a Literal and push it on the stack.
10197 * [29] Literal ::= '"' [^"]* '"'
10198 * | "'" [^']* "'"
10200 * TODO: xmlXPathCompLiteral memory allocation could be improved.
10202 static void
10203 xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
10204 const xmlChar *q;
10205 xmlChar *ret = NULL;
10206 xmlXPathObjectPtr lit;
10208 if (CUR == '"') {
10209 NEXT;
10210 q = CUR_PTR;
10211 while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10212 NEXT;
10213 if (!IS_CHAR_CH(CUR)) {
10214 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10215 } else {
10216 ret = xmlStrndup(q, CUR_PTR - q);
10217 NEXT;
10219 } else if (CUR == '\'') {
10220 NEXT;
10221 q = CUR_PTR;
10222 while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10223 NEXT;
10224 if (!IS_CHAR_CH(CUR)) {
10225 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10226 } else {
10227 ret = xmlStrndup(q, CUR_PTR - q);
10228 NEXT;
10230 } else {
10231 XP_ERROR(XPATH_START_LITERAL_ERROR);
10233 if (ret == NULL) {
10234 xmlXPathPErrMemory(ctxt, NULL);
10235 return;
10237 lit = xmlXPathCacheNewString(ctxt->context, ret);
10238 if (lit == NULL) {
10239 ctxt->error = XPATH_MEMORY_ERROR;
10240 } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, lit,
10241 NULL) == -1) {
10242 xmlXPathReleaseObject(ctxt->context, lit);
10244 xmlFree(ret);
10248 * xmlXPathCompVariableReference:
10249 * @ctxt: the XPath Parser context
10251 * Parse a VariableReference, evaluate it and push it on the stack.
10253 * The variable bindings consist of a mapping from variable names
10254 * to variable values. The value of a variable is an object, which can be
10255 * of any of the types that are possible for the value of an expression,
10256 * and may also be of additional types not specified here.
10258 * Early evaluation is possible since:
10259 * The variable bindings [...] used to evaluate a subexpression are
10260 * always the same as those used to evaluate the containing expression.
10262 * [36] VariableReference ::= '$' QName
10264 static void
10265 xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
10266 xmlChar *name;
10267 xmlChar *prefix;
10269 SKIP_BLANKS;
10270 if (CUR != '$') {
10271 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10273 NEXT;
10274 name = xmlXPathParseQName(ctxt, &prefix);
10275 if (name == NULL) {
10276 xmlFree(prefix);
10277 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10279 ctxt->comp->last = -1;
10280 if (PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, name, prefix) == -1) {
10281 xmlFree(prefix);
10282 xmlFree(name);
10284 SKIP_BLANKS;
10285 if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
10286 XP_ERROR(XPATH_FORBID_VARIABLE_ERROR);
10291 * xmlXPathIsNodeType:
10292 * @name: a name string
10294 * Is the name given a NodeType one.
10296 * [38] NodeType ::= 'comment'
10297 * | 'text'
10298 * | 'processing-instruction'
10299 * | 'node'
10301 * Returns 1 if true 0 otherwise
10304 xmlXPathIsNodeType(const xmlChar *name) {
10305 if (name == NULL)
10306 return(0);
10308 if (xmlStrEqual(name, BAD_CAST "node"))
10309 return(1);
10310 if (xmlStrEqual(name, BAD_CAST "text"))
10311 return(1);
10312 if (xmlStrEqual(name, BAD_CAST "comment"))
10313 return(1);
10314 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
10315 return(1);
10316 return(0);
10320 * xmlXPathCompFunctionCall:
10321 * @ctxt: the XPath Parser context
10323 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
10324 * [17] Argument ::= Expr
10326 * Compile a function call, the evaluation of all arguments are
10327 * pushed on the stack
10329 static void
10330 xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
10331 xmlChar *name;
10332 xmlChar *prefix;
10333 int nbargs = 0;
10334 int sort = 1;
10336 name = xmlXPathParseQName(ctxt, &prefix);
10337 if (name == NULL) {
10338 xmlFree(prefix);
10339 XP_ERROR(XPATH_EXPR_ERROR);
10341 SKIP_BLANKS;
10342 #ifdef DEBUG_EXPR
10343 if (prefix == NULL)
10344 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
10345 name);
10346 else
10347 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
10348 prefix, name);
10349 #endif
10351 if (CUR != '(') {
10352 xmlFree(name);
10353 xmlFree(prefix);
10354 XP_ERROR(XPATH_EXPR_ERROR);
10356 NEXT;
10357 SKIP_BLANKS;
10360 * Optimization for count(): we don't need the node-set to be sorted.
10362 if ((prefix == NULL) && (name[0] == 'c') &&
10363 xmlStrEqual(name, BAD_CAST "count"))
10365 sort = 0;
10367 ctxt->comp->last = -1;
10368 if (CUR != ')') {
10369 while (CUR != 0) {
10370 int op1 = ctxt->comp->last;
10371 ctxt->comp->last = -1;
10372 xmlXPathCompileExpr(ctxt, sort);
10373 if (ctxt->error != XPATH_EXPRESSION_OK) {
10374 xmlFree(name);
10375 xmlFree(prefix);
10376 return;
10378 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
10379 nbargs++;
10380 if (CUR == ')') break;
10381 if (CUR != ',') {
10382 xmlFree(name);
10383 xmlFree(prefix);
10384 XP_ERROR(XPATH_EXPR_ERROR);
10386 NEXT;
10387 SKIP_BLANKS;
10390 if (PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1) {
10391 xmlFree(prefix);
10392 xmlFree(name);
10394 NEXT;
10395 SKIP_BLANKS;
10399 * xmlXPathCompPrimaryExpr:
10400 * @ctxt: the XPath Parser context
10402 * [15] PrimaryExpr ::= VariableReference
10403 * | '(' Expr ')'
10404 * | Literal
10405 * | Number
10406 * | FunctionCall
10408 * Compile a primary expression.
10410 static void
10411 xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
10412 SKIP_BLANKS;
10413 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
10414 else if (CUR == '(') {
10415 NEXT;
10416 SKIP_BLANKS;
10417 xmlXPathCompileExpr(ctxt, 1);
10418 CHECK_ERROR;
10419 if (CUR != ')') {
10420 XP_ERROR(XPATH_EXPR_ERROR);
10422 NEXT;
10423 SKIP_BLANKS;
10424 } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10425 xmlXPathCompNumber(ctxt);
10426 } else if ((CUR == '\'') || (CUR == '"')) {
10427 xmlXPathCompLiteral(ctxt);
10428 } else {
10429 xmlXPathCompFunctionCall(ctxt);
10431 SKIP_BLANKS;
10435 * xmlXPathCompFilterExpr:
10436 * @ctxt: the XPath Parser context
10438 * [20] FilterExpr ::= PrimaryExpr
10439 * | FilterExpr Predicate
10441 * Compile a filter expression.
10442 * Square brackets are used to filter expressions in the same way that
10443 * they are used in location paths. It is an error if the expression to
10444 * be filtered does not evaluate to a node-set. The context node list
10445 * used for evaluating the expression in square brackets is the node-set
10446 * to be filtered listed in document order.
10449 static void
10450 xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
10451 xmlXPathCompPrimaryExpr(ctxt);
10452 CHECK_ERROR;
10453 SKIP_BLANKS;
10455 while (CUR == '[') {
10456 xmlXPathCompPredicate(ctxt, 1);
10457 SKIP_BLANKS;
10464 * xmlXPathScanName:
10465 * @ctxt: the XPath Parser context
10467 * Trickery: parse an XML name but without consuming the input flow
10468 * Needed to avoid insanity in the parser state.
10470 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
10471 * CombiningChar | Extender
10473 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
10475 * [6] Names ::= Name (S Name)*
10477 * Returns the Name parsed or NULL
10480 static xmlChar *
10481 xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
10482 int l;
10483 int c;
10484 const xmlChar *cur;
10485 xmlChar *ret;
10487 cur = ctxt->cur;
10489 c = CUR_CHAR(l);
10490 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
10491 (!IS_LETTER(c) && (c != '_') &&
10492 (c != ':'))) {
10493 return(NULL);
10496 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
10497 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
10498 (c == '.') || (c == '-') ||
10499 (c == '_') || (c == ':') ||
10500 (IS_COMBINING(c)) ||
10501 (IS_EXTENDER(c)))) {
10502 NEXTL(l);
10503 c = CUR_CHAR(l);
10505 ret = xmlStrndup(cur, ctxt->cur - cur);
10506 ctxt->cur = cur;
10507 return(ret);
10511 * xmlXPathCompPathExpr:
10512 * @ctxt: the XPath Parser context
10514 * [19] PathExpr ::= LocationPath
10515 * | FilterExpr
10516 * | FilterExpr '/' RelativeLocationPath
10517 * | FilterExpr '//' RelativeLocationPath
10519 * Compile a path expression.
10520 * The / operator and // operators combine an arbitrary expression
10521 * and a relative location path. It is an error if the expression
10522 * does not evaluate to a node-set.
10523 * The / operator does composition in the same way as when / is
10524 * used in a location path. As in location paths, // is short for
10525 * /descendant-or-self::node()/.
10528 static void
10529 xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
10530 int lc = 1; /* Should we branch to LocationPath ? */
10531 xmlChar *name = NULL; /* we may have to preparse a name to find out */
10533 SKIP_BLANKS;
10534 if ((CUR == '$') || (CUR == '(') ||
10535 (IS_ASCII_DIGIT(CUR)) ||
10536 (CUR == '\'') || (CUR == '"') ||
10537 (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10538 lc = 0;
10539 } else if (CUR == '*') {
10540 /* relative or absolute location path */
10541 lc = 1;
10542 } else if (CUR == '/') {
10543 /* relative or absolute location path */
10544 lc = 1;
10545 } else if (CUR == '@') {
10546 /* relative abbreviated attribute location path */
10547 lc = 1;
10548 } else if (CUR == '.') {
10549 /* relative abbreviated attribute location path */
10550 lc = 1;
10551 } else {
10553 * Problem is finding if we have a name here whether it's:
10554 * - a nodetype
10555 * - a function call in which case it's followed by '('
10556 * - an axis in which case it's followed by ':'
10557 * - a element name
10558 * We do an a priori analysis here rather than having to
10559 * maintain parsed token content through the recursive function
10560 * calls. This looks uglier but makes the code easier to
10561 * read/write/debug.
10563 SKIP_BLANKS;
10564 name = xmlXPathScanName(ctxt);
10565 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
10566 #ifdef DEBUG_STEP
10567 xmlGenericError(xmlGenericErrorContext,
10568 "PathExpr: Axis\n");
10569 #endif
10570 lc = 1;
10571 xmlFree(name);
10572 } else if (name != NULL) {
10573 int len =xmlStrlen(name);
10576 while (NXT(len) != 0) {
10577 if (NXT(len) == '/') {
10578 /* element name */
10579 #ifdef DEBUG_STEP
10580 xmlGenericError(xmlGenericErrorContext,
10581 "PathExpr: AbbrRelLocation\n");
10582 #endif
10583 lc = 1;
10584 break;
10585 } else if (IS_BLANK_CH(NXT(len))) {
10586 /* ignore blanks */
10588 } else if (NXT(len) == ':') {
10589 #ifdef DEBUG_STEP
10590 xmlGenericError(xmlGenericErrorContext,
10591 "PathExpr: AbbrRelLocation\n");
10592 #endif
10593 lc = 1;
10594 break;
10595 } else if ((NXT(len) == '(')) {
10596 /* Node Type or Function */
10597 if (xmlXPathIsNodeType(name)) {
10598 #ifdef DEBUG_STEP
10599 xmlGenericError(xmlGenericErrorContext,
10600 "PathExpr: Type search\n");
10601 #endif
10602 lc = 1;
10603 #ifdef LIBXML_XPTR_LOCS_ENABLED
10604 } else if (ctxt->xptr &&
10605 xmlStrEqual(name, BAD_CAST "range-to")) {
10606 lc = 1;
10607 #endif
10608 } else {
10609 #ifdef DEBUG_STEP
10610 xmlGenericError(xmlGenericErrorContext,
10611 "PathExpr: function call\n");
10612 #endif
10613 lc = 0;
10615 break;
10616 } else if ((NXT(len) == '[')) {
10617 /* element name */
10618 #ifdef DEBUG_STEP
10619 xmlGenericError(xmlGenericErrorContext,
10620 "PathExpr: AbbrRelLocation\n");
10621 #endif
10622 lc = 1;
10623 break;
10624 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
10625 (NXT(len) == '=')) {
10626 lc = 1;
10627 break;
10628 } else {
10629 lc = 1;
10630 break;
10632 len++;
10634 if (NXT(len) == 0) {
10635 #ifdef DEBUG_STEP
10636 xmlGenericError(xmlGenericErrorContext,
10637 "PathExpr: AbbrRelLocation\n");
10638 #endif
10639 /* element name */
10640 lc = 1;
10642 xmlFree(name);
10643 } else {
10644 /* make sure all cases are covered explicitly */
10645 XP_ERROR(XPATH_EXPR_ERROR);
10649 if (lc) {
10650 if (CUR == '/') {
10651 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
10652 } else {
10653 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10655 xmlXPathCompLocationPath(ctxt);
10656 } else {
10657 xmlXPathCompFilterExpr(ctxt);
10658 CHECK_ERROR;
10659 if ((CUR == '/') && (NXT(1) == '/')) {
10660 SKIP(2);
10661 SKIP_BLANKS;
10663 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
10664 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
10666 xmlXPathCompRelativeLocationPath(ctxt);
10667 } else if (CUR == '/') {
10668 xmlXPathCompRelativeLocationPath(ctxt);
10671 SKIP_BLANKS;
10675 * xmlXPathCompUnionExpr:
10676 * @ctxt: the XPath Parser context
10678 * [18] UnionExpr ::= PathExpr
10679 * | UnionExpr '|' PathExpr
10681 * Compile an union expression.
10684 static void
10685 xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
10686 xmlXPathCompPathExpr(ctxt);
10687 CHECK_ERROR;
10688 SKIP_BLANKS;
10689 while (CUR == '|') {
10690 int op1 = ctxt->comp->last;
10691 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10693 NEXT;
10694 SKIP_BLANKS;
10695 xmlXPathCompPathExpr(ctxt);
10697 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
10699 SKIP_BLANKS;
10704 * xmlXPathCompUnaryExpr:
10705 * @ctxt: the XPath Parser context
10707 * [27] UnaryExpr ::= UnionExpr
10708 * | '-' UnaryExpr
10710 * Compile an unary expression.
10713 static void
10714 xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
10715 int minus = 0;
10716 int found = 0;
10718 SKIP_BLANKS;
10719 while (CUR == '-') {
10720 minus = 1 - minus;
10721 found = 1;
10722 NEXT;
10723 SKIP_BLANKS;
10726 xmlXPathCompUnionExpr(ctxt);
10727 CHECK_ERROR;
10728 if (found) {
10729 if (minus)
10730 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
10731 else
10732 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
10737 * xmlXPathCompMultiplicativeExpr:
10738 * @ctxt: the XPath Parser context
10740 * [26] MultiplicativeExpr ::= UnaryExpr
10741 * | MultiplicativeExpr MultiplyOperator UnaryExpr
10742 * | MultiplicativeExpr 'div' UnaryExpr
10743 * | MultiplicativeExpr 'mod' UnaryExpr
10744 * [34] MultiplyOperator ::= '*'
10746 * Compile an Additive expression.
10749 static void
10750 xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
10751 xmlXPathCompUnaryExpr(ctxt);
10752 CHECK_ERROR;
10753 SKIP_BLANKS;
10754 while ((CUR == '*') ||
10755 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
10756 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
10757 int op = -1;
10758 int op1 = ctxt->comp->last;
10760 if (CUR == '*') {
10761 op = 0;
10762 NEXT;
10763 } else if (CUR == 'd') {
10764 op = 1;
10765 SKIP(3);
10766 } else if (CUR == 'm') {
10767 op = 2;
10768 SKIP(3);
10770 SKIP_BLANKS;
10771 xmlXPathCompUnaryExpr(ctxt);
10772 CHECK_ERROR;
10773 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
10774 SKIP_BLANKS;
10779 * xmlXPathCompAdditiveExpr:
10780 * @ctxt: the XPath Parser context
10782 * [25] AdditiveExpr ::= MultiplicativeExpr
10783 * | AdditiveExpr '+' MultiplicativeExpr
10784 * | AdditiveExpr '-' MultiplicativeExpr
10786 * Compile an Additive expression.
10789 static void
10790 xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
10792 xmlXPathCompMultiplicativeExpr(ctxt);
10793 CHECK_ERROR;
10794 SKIP_BLANKS;
10795 while ((CUR == '+') || (CUR == '-')) {
10796 int plus;
10797 int op1 = ctxt->comp->last;
10799 if (CUR == '+') plus = 1;
10800 else plus = 0;
10801 NEXT;
10802 SKIP_BLANKS;
10803 xmlXPathCompMultiplicativeExpr(ctxt);
10804 CHECK_ERROR;
10805 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
10806 SKIP_BLANKS;
10811 * xmlXPathCompRelationalExpr:
10812 * @ctxt: the XPath Parser context
10814 * [24] RelationalExpr ::= AdditiveExpr
10815 * | RelationalExpr '<' AdditiveExpr
10816 * | RelationalExpr '>' AdditiveExpr
10817 * | RelationalExpr '<=' AdditiveExpr
10818 * | RelationalExpr '>=' AdditiveExpr
10820 * A <= B > C is allowed ? Answer from James, yes with
10821 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
10822 * which is basically what got implemented.
10824 * Compile a Relational expression, then push the result
10825 * on the stack
10828 static void
10829 xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
10830 xmlXPathCompAdditiveExpr(ctxt);
10831 CHECK_ERROR;
10832 SKIP_BLANKS;
10833 while ((CUR == '<') || (CUR == '>')) {
10834 int inf, strict;
10835 int op1 = ctxt->comp->last;
10837 if (CUR == '<') inf = 1;
10838 else inf = 0;
10839 if (NXT(1) == '=') strict = 0;
10840 else strict = 1;
10841 NEXT;
10842 if (!strict) NEXT;
10843 SKIP_BLANKS;
10844 xmlXPathCompAdditiveExpr(ctxt);
10845 CHECK_ERROR;
10846 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
10847 SKIP_BLANKS;
10852 * xmlXPathCompEqualityExpr:
10853 * @ctxt: the XPath Parser context
10855 * [23] EqualityExpr ::= RelationalExpr
10856 * | EqualityExpr '=' RelationalExpr
10857 * | EqualityExpr '!=' RelationalExpr
10859 * A != B != C is allowed ? Answer from James, yes with
10860 * (RelationalExpr = RelationalExpr) = RelationalExpr
10861 * (RelationalExpr != RelationalExpr) != RelationalExpr
10862 * which is basically what got implemented.
10864 * Compile an Equality expression.
10867 static void
10868 xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
10869 xmlXPathCompRelationalExpr(ctxt);
10870 CHECK_ERROR;
10871 SKIP_BLANKS;
10872 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
10873 int eq;
10874 int op1 = ctxt->comp->last;
10876 if (CUR == '=') eq = 1;
10877 else eq = 0;
10878 NEXT;
10879 if (!eq) NEXT;
10880 SKIP_BLANKS;
10881 xmlXPathCompRelationalExpr(ctxt);
10882 CHECK_ERROR;
10883 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
10884 SKIP_BLANKS;
10889 * xmlXPathCompAndExpr:
10890 * @ctxt: the XPath Parser context
10892 * [22] AndExpr ::= EqualityExpr
10893 * | AndExpr 'and' EqualityExpr
10895 * Compile an AND expression.
10898 static void
10899 xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
10900 xmlXPathCompEqualityExpr(ctxt);
10901 CHECK_ERROR;
10902 SKIP_BLANKS;
10903 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
10904 int op1 = ctxt->comp->last;
10905 SKIP(3);
10906 SKIP_BLANKS;
10907 xmlXPathCompEqualityExpr(ctxt);
10908 CHECK_ERROR;
10909 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
10910 SKIP_BLANKS;
10915 * xmlXPathCompileExpr:
10916 * @ctxt: the XPath Parser context
10918 * [14] Expr ::= OrExpr
10919 * [21] OrExpr ::= AndExpr
10920 * | OrExpr 'or' AndExpr
10922 * Parse and compile an expression
10924 static void
10925 xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
10926 xmlXPathContextPtr xpctxt = ctxt->context;
10928 if (xpctxt != NULL) {
10929 if (xpctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
10930 XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
10932 * Parsing a single '(' pushes about 10 functions on the call stack
10933 * before recursing!
10935 xpctxt->depth += 10;
10938 xmlXPathCompAndExpr(ctxt);
10939 CHECK_ERROR;
10940 SKIP_BLANKS;
10941 while ((CUR == 'o') && (NXT(1) == 'r')) {
10942 int op1 = ctxt->comp->last;
10943 SKIP(2);
10944 SKIP_BLANKS;
10945 xmlXPathCompAndExpr(ctxt);
10946 CHECK_ERROR;
10947 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
10948 SKIP_BLANKS;
10950 if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
10951 /* more ops could be optimized too */
10953 * This is the main place to eliminate sorting for
10954 * operations which don't require a sorted node-set.
10955 * E.g. count().
10957 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
10960 if (xpctxt != NULL)
10961 xpctxt->depth -= 10;
10965 * xmlXPathCompPredicate:
10966 * @ctxt: the XPath Parser context
10967 * @filter: act as a filter
10969 * [8] Predicate ::= '[' PredicateExpr ']'
10970 * [9] PredicateExpr ::= Expr
10972 * Compile a predicate expression
10974 static void
10975 xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
10976 int op1 = ctxt->comp->last;
10978 SKIP_BLANKS;
10979 if (CUR != '[') {
10980 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
10982 NEXT;
10983 SKIP_BLANKS;
10985 ctxt->comp->last = -1;
10987 * This call to xmlXPathCompileExpr() will deactivate sorting
10988 * of the predicate result.
10989 * TODO: Sorting is still activated for filters, since I'm not
10990 * sure if needed. Normally sorting should not be needed, since
10991 * a filter can only diminish the number of items in a sequence,
10992 * but won't change its order; so if the initial sequence is sorted,
10993 * subsequent sorting is not needed.
10995 if (! filter)
10996 xmlXPathCompileExpr(ctxt, 0);
10997 else
10998 xmlXPathCompileExpr(ctxt, 1);
10999 CHECK_ERROR;
11001 if (CUR != ']') {
11002 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
11005 if (filter)
11006 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
11007 else
11008 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
11010 NEXT;
11011 SKIP_BLANKS;
11015 * xmlXPathCompNodeTest:
11016 * @ctxt: the XPath Parser context
11017 * @test: pointer to a xmlXPathTestVal
11018 * @type: pointer to a xmlXPathTypeVal
11019 * @prefix: placeholder for a possible name prefix
11021 * [7] NodeTest ::= NameTest
11022 * | NodeType '(' ')'
11023 * | 'processing-instruction' '(' Literal ')'
11025 * [37] NameTest ::= '*'
11026 * | NCName ':' '*'
11027 * | QName
11028 * [38] NodeType ::= 'comment'
11029 * | 'text'
11030 * | 'processing-instruction'
11031 * | 'node'
11033 * Returns the name found and updates @test, @type and @prefix appropriately
11035 static xmlChar *
11036 xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
11037 xmlXPathTypeVal *type, xmlChar **prefix,
11038 xmlChar *name) {
11039 int blanks;
11041 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
11042 STRANGE;
11043 return(NULL);
11045 *type = (xmlXPathTypeVal) 0;
11046 *test = (xmlXPathTestVal) 0;
11047 *prefix = NULL;
11048 SKIP_BLANKS;
11050 if ((name == NULL) && (CUR == '*')) {
11052 * All elements
11054 NEXT;
11055 *test = NODE_TEST_ALL;
11056 return(NULL);
11059 if (name == NULL)
11060 name = xmlXPathParseNCName(ctxt);
11061 if (name == NULL) {
11062 XP_ERRORNULL(XPATH_EXPR_ERROR);
11065 blanks = IS_BLANK_CH(CUR);
11066 SKIP_BLANKS;
11067 if (CUR == '(') {
11068 NEXT;
11070 * NodeType or PI search
11072 if (xmlStrEqual(name, BAD_CAST "comment"))
11073 *type = NODE_TYPE_COMMENT;
11074 else if (xmlStrEqual(name, BAD_CAST "node"))
11075 *type = NODE_TYPE_NODE;
11076 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
11077 *type = NODE_TYPE_PI;
11078 else if (xmlStrEqual(name, BAD_CAST "text"))
11079 *type = NODE_TYPE_TEXT;
11080 else {
11081 if (name != NULL)
11082 xmlFree(name);
11083 XP_ERRORNULL(XPATH_EXPR_ERROR);
11086 *test = NODE_TEST_TYPE;
11088 SKIP_BLANKS;
11089 if (*type == NODE_TYPE_PI) {
11091 * Specific case: search a PI by name.
11093 if (name != NULL)
11094 xmlFree(name);
11095 name = NULL;
11096 if (CUR != ')') {
11097 name = xmlXPathParseLiteral(ctxt);
11098 if (name == NULL) {
11099 XP_ERRORNULL(XPATH_EXPR_ERROR);
11101 *test = NODE_TEST_PI;
11102 SKIP_BLANKS;
11105 if (CUR != ')') {
11106 if (name != NULL)
11107 xmlFree(name);
11108 XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
11110 NEXT;
11111 return(name);
11113 *test = NODE_TEST_NAME;
11114 if ((!blanks) && (CUR == ':')) {
11115 NEXT;
11118 * Since currently the parser context don't have a
11119 * namespace list associated:
11120 * The namespace name for this prefix can be computed
11121 * only at evaluation time. The compilation is done
11122 * outside of any context.
11124 #if 0
11125 *prefix = xmlXPathNsLookup(ctxt->context, name);
11126 if (name != NULL)
11127 xmlFree(name);
11128 if (*prefix == NULL) {
11129 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
11131 #else
11132 *prefix = name;
11133 #endif
11135 if (CUR == '*') {
11137 * All elements
11139 NEXT;
11140 *test = NODE_TEST_ALL;
11141 return(NULL);
11144 name = xmlXPathParseNCName(ctxt);
11145 if (name == NULL) {
11146 XP_ERRORNULL(XPATH_EXPR_ERROR);
11149 return(name);
11153 * xmlXPathIsAxisName:
11154 * @name: a preparsed name token
11156 * [6] AxisName ::= 'ancestor'
11157 * | 'ancestor-or-self'
11158 * | 'attribute'
11159 * | 'child'
11160 * | 'descendant'
11161 * | 'descendant-or-self'
11162 * | 'following'
11163 * | 'following-sibling'
11164 * | 'namespace'
11165 * | 'parent'
11166 * | 'preceding'
11167 * | 'preceding-sibling'
11168 * | 'self'
11170 * Returns the axis or 0
11172 static xmlXPathAxisVal
11173 xmlXPathIsAxisName(const xmlChar *name) {
11174 xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
11175 switch (name[0]) {
11176 case 'a':
11177 if (xmlStrEqual(name, BAD_CAST "ancestor"))
11178 ret = AXIS_ANCESTOR;
11179 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
11180 ret = AXIS_ANCESTOR_OR_SELF;
11181 if (xmlStrEqual(name, BAD_CAST "attribute"))
11182 ret = AXIS_ATTRIBUTE;
11183 break;
11184 case 'c':
11185 if (xmlStrEqual(name, BAD_CAST "child"))
11186 ret = AXIS_CHILD;
11187 break;
11188 case 'd':
11189 if (xmlStrEqual(name, BAD_CAST "descendant"))
11190 ret = AXIS_DESCENDANT;
11191 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
11192 ret = AXIS_DESCENDANT_OR_SELF;
11193 break;
11194 case 'f':
11195 if (xmlStrEqual(name, BAD_CAST "following"))
11196 ret = AXIS_FOLLOWING;
11197 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
11198 ret = AXIS_FOLLOWING_SIBLING;
11199 break;
11200 case 'n':
11201 if (xmlStrEqual(name, BAD_CAST "namespace"))
11202 ret = AXIS_NAMESPACE;
11203 break;
11204 case 'p':
11205 if (xmlStrEqual(name, BAD_CAST "parent"))
11206 ret = AXIS_PARENT;
11207 if (xmlStrEqual(name, BAD_CAST "preceding"))
11208 ret = AXIS_PRECEDING;
11209 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
11210 ret = AXIS_PRECEDING_SIBLING;
11211 break;
11212 case 's':
11213 if (xmlStrEqual(name, BAD_CAST "self"))
11214 ret = AXIS_SELF;
11215 break;
11217 return(ret);
11221 * xmlXPathCompStep:
11222 * @ctxt: the XPath Parser context
11224 * [4] Step ::= AxisSpecifier NodeTest Predicate*
11225 * | AbbreviatedStep
11227 * [12] AbbreviatedStep ::= '.' | '..'
11229 * [5] AxisSpecifier ::= AxisName '::'
11230 * | AbbreviatedAxisSpecifier
11232 * [13] AbbreviatedAxisSpecifier ::= '@'?
11234 * Modified for XPtr range support as:
11236 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
11237 * | AbbreviatedStep
11238 * | 'range-to' '(' Expr ')' Predicate*
11240 * Compile one step in a Location Path
11241 * A location step of . is short for self::node(). This is
11242 * particularly useful in conjunction with //. For example, the
11243 * location path .//para is short for
11244 * self::node()/descendant-or-self::node()/child::para
11245 * and so will select all para descendant elements of the context
11246 * node.
11247 * Similarly, a location step of .. is short for parent::node().
11248 * For example, ../title is short for parent::node()/child::title
11249 * and so will select the title children of the parent of the context
11250 * node.
11252 static void
11253 xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
11254 #ifdef LIBXML_XPTR_LOCS_ENABLED
11255 int rangeto = 0;
11256 int op2 = -1;
11257 #endif
11259 SKIP_BLANKS;
11260 if ((CUR == '.') && (NXT(1) == '.')) {
11261 SKIP(2);
11262 SKIP_BLANKS;
11263 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
11264 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11265 } else if (CUR == '.') {
11266 NEXT;
11267 SKIP_BLANKS;
11268 } else {
11269 xmlChar *name = NULL;
11270 xmlChar *prefix = NULL;
11271 xmlXPathTestVal test = (xmlXPathTestVal) 0;
11272 xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
11273 xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
11274 int op1;
11277 * The modification needed for XPointer change to the production
11279 #ifdef LIBXML_XPTR_LOCS_ENABLED
11280 if (ctxt->xptr) {
11281 name = xmlXPathParseNCName(ctxt);
11282 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
11283 op2 = ctxt->comp->last;
11284 xmlFree(name);
11285 SKIP_BLANKS;
11286 if (CUR != '(') {
11287 XP_ERROR(XPATH_EXPR_ERROR);
11289 NEXT;
11290 SKIP_BLANKS;
11292 xmlXPathCompileExpr(ctxt, 1);
11293 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
11294 CHECK_ERROR;
11296 SKIP_BLANKS;
11297 if (CUR != ')') {
11298 XP_ERROR(XPATH_EXPR_ERROR);
11300 NEXT;
11301 rangeto = 1;
11302 goto eval_predicates;
11305 #endif
11306 if (CUR == '*') {
11307 axis = AXIS_CHILD;
11308 } else {
11309 if (name == NULL)
11310 name = xmlXPathParseNCName(ctxt);
11311 if (name != NULL) {
11312 axis = xmlXPathIsAxisName(name);
11313 if (axis != 0) {
11314 SKIP_BLANKS;
11315 if ((CUR == ':') && (NXT(1) == ':')) {
11316 SKIP(2);
11317 xmlFree(name);
11318 name = NULL;
11319 } else {
11320 /* an element name can conflict with an axis one :-\ */
11321 axis = AXIS_CHILD;
11323 } else {
11324 axis = AXIS_CHILD;
11326 } else if (CUR == '@') {
11327 NEXT;
11328 axis = AXIS_ATTRIBUTE;
11329 } else {
11330 axis = AXIS_CHILD;
11334 if (ctxt->error != XPATH_EXPRESSION_OK) {
11335 xmlFree(name);
11336 return;
11339 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
11340 if (test == 0)
11341 return;
11343 if ((prefix != NULL) && (ctxt->context != NULL) &&
11344 (ctxt->context->flags & XML_XPATH_CHECKNS)) {
11345 if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
11346 xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
11349 #ifdef DEBUG_STEP
11350 xmlGenericError(xmlGenericErrorContext,
11351 "Basis : computing new set\n");
11352 #endif
11354 #ifdef DEBUG_STEP
11355 xmlGenericError(xmlGenericErrorContext, "Basis : ");
11356 if (ctxt->value == NULL)
11357 xmlGenericError(xmlGenericErrorContext, "no value\n");
11358 else if (ctxt->value->nodesetval == NULL)
11359 xmlGenericError(xmlGenericErrorContext, "Empty\n");
11360 else
11361 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
11362 #endif
11364 #ifdef LIBXML_XPTR_LOCS_ENABLED
11365 eval_predicates:
11366 #endif
11367 op1 = ctxt->comp->last;
11368 ctxt->comp->last = -1;
11370 SKIP_BLANKS;
11371 while (CUR == '[') {
11372 xmlXPathCompPredicate(ctxt, 0);
11375 #ifdef LIBXML_XPTR_LOCS_ENABLED
11376 if (rangeto) {
11377 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
11378 } else
11379 #endif
11380 if (PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
11381 test, type, (void *)prefix, (void *)name) == -1) {
11382 xmlFree(prefix);
11383 xmlFree(name);
11386 #ifdef DEBUG_STEP
11387 xmlGenericError(xmlGenericErrorContext, "Step : ");
11388 if (ctxt->value == NULL)
11389 xmlGenericError(xmlGenericErrorContext, "no value\n");
11390 else if (ctxt->value->nodesetval == NULL)
11391 xmlGenericError(xmlGenericErrorContext, "Empty\n");
11392 else
11393 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
11394 ctxt->value->nodesetval);
11395 #endif
11399 * xmlXPathCompRelativeLocationPath:
11400 * @ctxt: the XPath Parser context
11402 * [3] RelativeLocationPath ::= Step
11403 * | RelativeLocationPath '/' Step
11404 * | AbbreviatedRelativeLocationPath
11405 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
11407 * Compile a relative location path.
11409 static void
11410 xmlXPathCompRelativeLocationPath
11411 (xmlXPathParserContextPtr ctxt) {
11412 SKIP_BLANKS;
11413 if ((CUR == '/') && (NXT(1) == '/')) {
11414 SKIP(2);
11415 SKIP_BLANKS;
11416 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11417 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11418 } else if (CUR == '/') {
11419 NEXT;
11420 SKIP_BLANKS;
11422 xmlXPathCompStep(ctxt);
11423 CHECK_ERROR;
11424 SKIP_BLANKS;
11425 while (CUR == '/') {
11426 if ((CUR == '/') && (NXT(1) == '/')) {
11427 SKIP(2);
11428 SKIP_BLANKS;
11429 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11430 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11431 xmlXPathCompStep(ctxt);
11432 } else if (CUR == '/') {
11433 NEXT;
11434 SKIP_BLANKS;
11435 xmlXPathCompStep(ctxt);
11437 SKIP_BLANKS;
11442 * xmlXPathCompLocationPath:
11443 * @ctxt: the XPath Parser context
11445 * [1] LocationPath ::= RelativeLocationPath
11446 * | AbsoluteLocationPath
11447 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
11448 * | AbbreviatedAbsoluteLocationPath
11449 * [10] AbbreviatedAbsoluteLocationPath ::=
11450 * '//' RelativeLocationPath
11452 * Compile a location path
11454 * // is short for /descendant-or-self::node()/. For example,
11455 * //para is short for /descendant-or-self::node()/child::para and
11456 * so will select any para element in the document (even a para element
11457 * that is a document element will be selected by //para since the
11458 * document element node is a child of the root node); div//para is
11459 * short for div/descendant-or-self::node()/child::para and so will
11460 * select all para descendants of div children.
11462 static void
11463 xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
11464 SKIP_BLANKS;
11465 if (CUR != '/') {
11466 xmlXPathCompRelativeLocationPath(ctxt);
11467 } else {
11468 while (CUR == '/') {
11469 if ((CUR == '/') && (NXT(1) == '/')) {
11470 SKIP(2);
11471 SKIP_BLANKS;
11472 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11473 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11474 xmlXPathCompRelativeLocationPath(ctxt);
11475 } else if (CUR == '/') {
11476 NEXT;
11477 SKIP_BLANKS;
11478 if ((CUR != 0 ) &&
11479 ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
11480 (CUR == '@') || (CUR == '*')))
11481 xmlXPathCompRelativeLocationPath(ctxt);
11483 CHECK_ERROR;
11488 /************************************************************************
11490 * XPath precompiled expression evaluation *
11492 ************************************************************************/
11494 static int
11495 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
11497 #ifdef DEBUG_STEP
11498 static void
11499 xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
11500 int nbNodes)
11502 xmlGenericError(xmlGenericErrorContext, "new step : ");
11503 switch (op->value) {
11504 case AXIS_ANCESTOR:
11505 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
11506 break;
11507 case AXIS_ANCESTOR_OR_SELF:
11508 xmlGenericError(xmlGenericErrorContext,
11509 "axis 'ancestors-or-self' ");
11510 break;
11511 case AXIS_ATTRIBUTE:
11512 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
11513 break;
11514 case AXIS_CHILD:
11515 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
11516 break;
11517 case AXIS_DESCENDANT:
11518 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
11519 break;
11520 case AXIS_DESCENDANT_OR_SELF:
11521 xmlGenericError(xmlGenericErrorContext,
11522 "axis 'descendant-or-self' ");
11523 break;
11524 case AXIS_FOLLOWING:
11525 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
11526 break;
11527 case AXIS_FOLLOWING_SIBLING:
11528 xmlGenericError(xmlGenericErrorContext,
11529 "axis 'following-siblings' ");
11530 break;
11531 case AXIS_NAMESPACE:
11532 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
11533 break;
11534 case AXIS_PARENT:
11535 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
11536 break;
11537 case AXIS_PRECEDING:
11538 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
11539 break;
11540 case AXIS_PRECEDING_SIBLING:
11541 xmlGenericError(xmlGenericErrorContext,
11542 "axis 'preceding-sibling' ");
11543 break;
11544 case AXIS_SELF:
11545 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
11546 break;
11548 xmlGenericError(xmlGenericErrorContext,
11549 " context contains %d nodes\n", nbNodes);
11550 switch (op->value2) {
11551 case NODE_TEST_NONE:
11552 xmlGenericError(xmlGenericErrorContext,
11553 " searching for none !!!\n");
11554 break;
11555 case NODE_TEST_TYPE:
11556 xmlGenericError(xmlGenericErrorContext,
11557 " searching for type %d\n", op->value3);
11558 break;
11559 case NODE_TEST_PI:
11560 xmlGenericError(xmlGenericErrorContext,
11561 " searching for PI !!!\n");
11562 break;
11563 case NODE_TEST_ALL:
11564 xmlGenericError(xmlGenericErrorContext,
11565 " searching for *\n");
11566 break;
11567 case NODE_TEST_NS:
11568 xmlGenericError(xmlGenericErrorContext,
11569 " searching for namespace %s\n",
11570 op->value5);
11571 break;
11572 case NODE_TEST_NAME:
11573 xmlGenericError(xmlGenericErrorContext,
11574 " searching for name %s\n", op->value5);
11575 if (op->value4)
11576 xmlGenericError(xmlGenericErrorContext,
11577 " with namespace %s\n", op->value4);
11578 break;
11580 xmlGenericError(xmlGenericErrorContext, "Testing : ");
11582 #endif /* DEBUG_STEP */
11585 * xmlXPathNodeSetFilter:
11586 * @ctxt: the XPath Parser context
11587 * @set: the node set to filter
11588 * @filterOpIndex: the index of the predicate/filter op
11589 * @minPos: minimum position in the filtered set (1-based)
11590 * @maxPos: maximum position in the filtered set (1-based)
11591 * @hasNsNodes: true if the node set may contain namespace nodes
11593 * Filter a node set, keeping only nodes for which the predicate expression
11594 * matches. Afterwards, keep only nodes between minPos and maxPos in the
11595 * filtered result.
11597 static void
11598 xmlXPathNodeSetFilter(xmlXPathParserContextPtr ctxt,
11599 xmlNodeSetPtr set,
11600 int filterOpIndex,
11601 int minPos, int maxPos,
11602 int hasNsNodes)
11604 xmlXPathContextPtr xpctxt;
11605 xmlNodePtr oldnode;
11606 xmlDocPtr olddoc;
11607 xmlXPathStepOpPtr filterOp;
11608 int oldcs, oldpp;
11609 int i, j, pos;
11611 if ((set == NULL) || (set->nodeNr == 0))
11612 return;
11615 * Check if the node set contains a sufficient number of nodes for
11616 * the requested range.
11618 if (set->nodeNr < minPos) {
11619 xmlXPathNodeSetClear(set, hasNsNodes);
11620 return;
11623 xpctxt = ctxt->context;
11624 oldnode = xpctxt->node;
11625 olddoc = xpctxt->doc;
11626 oldcs = xpctxt->contextSize;
11627 oldpp = xpctxt->proximityPosition;
11628 filterOp = &ctxt->comp->steps[filterOpIndex];
11630 xpctxt->contextSize = set->nodeNr;
11632 for (i = 0, j = 0, pos = 1; i < set->nodeNr; i++) {
11633 xmlNodePtr node = set->nodeTab[i];
11634 int res;
11636 xpctxt->node = node;
11637 xpctxt->proximityPosition = i + 1;
11640 * Also set the xpath document in case things like
11641 * key() are evaluated in the predicate.
11643 * TODO: Get real doc for namespace nodes.
11645 if ((node->type != XML_NAMESPACE_DECL) &&
11646 (node->doc != NULL))
11647 xpctxt->doc = node->doc;
11649 res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
11651 if (ctxt->error != XPATH_EXPRESSION_OK)
11652 break;
11653 if (res < 0) {
11654 /* Shouldn't happen */
11655 xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
11656 break;
11659 if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
11660 if (i != j) {
11661 set->nodeTab[j] = node;
11662 set->nodeTab[i] = NULL;
11665 j += 1;
11666 } else {
11667 /* Remove the entry from the initial node set. */
11668 set->nodeTab[i] = NULL;
11669 if (node->type == XML_NAMESPACE_DECL)
11670 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
11673 if (res != 0) {
11674 if (pos == maxPos) {
11675 i += 1;
11676 break;
11679 pos += 1;
11683 /* Free remaining nodes. */
11684 if (hasNsNodes) {
11685 for (; i < set->nodeNr; i++) {
11686 xmlNodePtr node = set->nodeTab[i];
11687 if ((node != NULL) && (node->type == XML_NAMESPACE_DECL))
11688 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
11692 set->nodeNr = j;
11694 /* If too many elements were removed, shrink table to preserve memory. */
11695 if ((set->nodeMax > XML_NODESET_DEFAULT) &&
11696 (set->nodeNr < set->nodeMax / 2)) {
11697 xmlNodePtr *tmp;
11698 int nodeMax = set->nodeNr;
11700 if (nodeMax < XML_NODESET_DEFAULT)
11701 nodeMax = XML_NODESET_DEFAULT;
11702 tmp = (xmlNodePtr *) xmlRealloc(set->nodeTab,
11703 nodeMax * sizeof(xmlNodePtr));
11704 if (tmp == NULL) {
11705 xmlXPathPErrMemory(ctxt, "shrinking nodeset\n");
11706 } else {
11707 set->nodeTab = tmp;
11708 set->nodeMax = nodeMax;
11712 xpctxt->node = oldnode;
11713 xpctxt->doc = olddoc;
11714 xpctxt->contextSize = oldcs;
11715 xpctxt->proximityPosition = oldpp;
11718 #ifdef LIBXML_XPTR_LOCS_ENABLED
11720 * xmlXPathLocationSetFilter:
11721 * @ctxt: the XPath Parser context
11722 * @locset: the location set to filter
11723 * @filterOpIndex: the index of the predicate/filter op
11724 * @minPos: minimum position in the filtered set (1-based)
11725 * @maxPos: maximum position in the filtered set (1-based)
11727 * Filter a location set, keeping only nodes for which the predicate
11728 * expression matches. Afterwards, keep only nodes between minPos and maxPos
11729 * in the filtered result.
11731 static void
11732 xmlXPathLocationSetFilter(xmlXPathParserContextPtr ctxt,
11733 xmlLocationSetPtr locset,
11734 int filterOpIndex,
11735 int minPos, int maxPos)
11737 xmlXPathContextPtr xpctxt;
11738 xmlNodePtr oldnode;
11739 xmlDocPtr olddoc;
11740 xmlXPathStepOpPtr filterOp;
11741 int oldcs, oldpp;
11742 int i, j, pos;
11744 if ((locset == NULL) || (locset->locNr == 0) || (filterOpIndex == -1))
11745 return;
11747 xpctxt = ctxt->context;
11748 oldnode = xpctxt->node;
11749 olddoc = xpctxt->doc;
11750 oldcs = xpctxt->contextSize;
11751 oldpp = xpctxt->proximityPosition;
11752 filterOp = &ctxt->comp->steps[filterOpIndex];
11754 xpctxt->contextSize = locset->locNr;
11756 for (i = 0, j = 0, pos = 1; i < locset->locNr; i++) {
11757 xmlNodePtr contextNode = locset->locTab[i]->user;
11758 int res;
11760 xpctxt->node = contextNode;
11761 xpctxt->proximityPosition = i + 1;
11764 * Also set the xpath document in case things like
11765 * key() are evaluated in the predicate.
11767 * TODO: Get real doc for namespace nodes.
11769 if ((contextNode->type != XML_NAMESPACE_DECL) &&
11770 (contextNode->doc != NULL))
11771 xpctxt->doc = contextNode->doc;
11773 res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
11775 if (ctxt->error != XPATH_EXPRESSION_OK)
11776 break;
11777 if (res < 0) {
11778 /* Shouldn't happen */
11779 xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
11780 break;
11783 if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
11784 if (i != j) {
11785 locset->locTab[j] = locset->locTab[i];
11786 locset->locTab[i] = NULL;
11789 j += 1;
11790 } else {
11791 /* Remove the entry from the initial location set. */
11792 xmlXPathFreeObject(locset->locTab[i]);
11793 locset->locTab[i] = NULL;
11796 if (res != 0) {
11797 if (pos == maxPos) {
11798 i += 1;
11799 break;
11802 pos += 1;
11806 /* Free remaining nodes. */
11807 for (; i < locset->locNr; i++)
11808 xmlXPathFreeObject(locset->locTab[i]);
11810 locset->locNr = j;
11812 /* If too many elements were removed, shrink table to preserve memory. */
11813 if ((locset->locMax > XML_NODESET_DEFAULT) &&
11814 (locset->locNr < locset->locMax / 2)) {
11815 xmlXPathObjectPtr *tmp;
11816 int locMax = locset->locNr;
11818 if (locMax < XML_NODESET_DEFAULT)
11819 locMax = XML_NODESET_DEFAULT;
11820 tmp = (xmlXPathObjectPtr *) xmlRealloc(locset->locTab,
11821 locMax * sizeof(xmlXPathObjectPtr));
11822 if (tmp == NULL) {
11823 xmlXPathPErrMemory(ctxt, "shrinking locset\n");
11824 } else {
11825 locset->locTab = tmp;
11826 locset->locMax = locMax;
11830 xpctxt->node = oldnode;
11831 xpctxt->doc = olddoc;
11832 xpctxt->contextSize = oldcs;
11833 xpctxt->proximityPosition = oldpp;
11835 #endif /* LIBXML_XPTR_LOCS_ENABLED */
11838 * xmlXPathCompOpEvalPredicate:
11839 * @ctxt: the XPath Parser context
11840 * @op: the predicate op
11841 * @set: the node set to filter
11842 * @minPos: minimum position in the filtered set (1-based)
11843 * @maxPos: maximum position in the filtered set (1-based)
11844 * @hasNsNodes: true if the node set may contain namespace nodes
11846 * Filter a node set, keeping only nodes for which the sequence of predicate
11847 * expressions matches. Afterwards, keep only nodes between minPos and maxPos
11848 * in the filtered result.
11850 static void
11851 xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
11852 xmlXPathStepOpPtr op,
11853 xmlNodeSetPtr set,
11854 int minPos, int maxPos,
11855 int hasNsNodes)
11857 if (op->ch1 != -1) {
11858 xmlXPathCompExprPtr comp = ctxt->comp;
11860 * Process inner predicates first.
11862 if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11863 xmlGenericError(xmlGenericErrorContext,
11864 "xmlXPathCompOpEvalPredicate: Expected a predicate\n");
11865 XP_ERROR(XPATH_INVALID_OPERAND);
11867 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
11868 XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
11869 ctxt->context->depth += 1;
11870 xmlXPathCompOpEvalPredicate(ctxt, &comp->steps[op->ch1], set,
11871 1, set->nodeNr, hasNsNodes);
11872 ctxt->context->depth -= 1;
11873 CHECK_ERROR;
11876 if (op->ch2 != -1)
11877 xmlXPathNodeSetFilter(ctxt, set, op->ch2, minPos, maxPos, hasNsNodes);
11880 static int
11881 xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
11882 xmlXPathStepOpPtr op,
11883 int *maxPos)
11886 xmlXPathStepOpPtr exprOp;
11889 * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
11893 * If not -1, then ch1 will point to:
11894 * 1) For predicates (XPATH_OP_PREDICATE):
11895 * - an inner predicate operator
11896 * 2) For filters (XPATH_OP_FILTER):
11897 * - an inner filter operator OR
11898 * - an expression selecting the node set.
11899 * E.g. "key('a', 'b')" or "(//foo | //bar)".
11901 if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
11902 return(0);
11904 if (op->ch2 != -1) {
11905 exprOp = &ctxt->comp->steps[op->ch2];
11906 } else
11907 return(0);
11909 if ((exprOp != NULL) &&
11910 (exprOp->op == XPATH_OP_VALUE) &&
11911 (exprOp->value4 != NULL) &&
11912 (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
11914 double floatval = ((xmlXPathObjectPtr) exprOp->value4)->floatval;
11917 * We have a "[n]" predicate here.
11918 * TODO: Unfortunately this simplistic test here is not
11919 * able to detect a position() predicate in compound
11920 * expressions like "[@attr = 'a" and position() = 1],
11921 * and even not the usage of position() in
11922 * "[position() = 1]"; thus - obviously - a position-range,
11923 * like it "[position() < 5]", is also not detected.
11924 * Maybe we could rewrite the AST to ease the optimization.
11927 if ((floatval > INT_MIN) && (floatval < INT_MAX)) {
11928 *maxPos = (int) floatval;
11929 if (floatval == (double) *maxPos)
11930 return(1);
11933 return(0);
11936 static int
11937 xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
11938 xmlXPathStepOpPtr op,
11939 xmlNodePtr * first, xmlNodePtr * last,
11940 int toBool)
11943 #define XP_TEST_HIT \
11944 if (hasAxisRange != 0) { \
11945 if (++pos == maxPos) { \
11946 if (addNode(seq, cur) < 0) \
11947 ctxt->error = XPATH_MEMORY_ERROR; \
11948 goto axis_range_end; } \
11949 } else { \
11950 if (addNode(seq, cur) < 0) \
11951 ctxt->error = XPATH_MEMORY_ERROR; \
11952 if (breakOnFirstHit) goto first_hit; }
11954 #define XP_TEST_HIT_NS \
11955 if (hasAxisRange != 0) { \
11956 if (++pos == maxPos) { \
11957 hasNsNodes = 1; \
11958 if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
11959 ctxt->error = XPATH_MEMORY_ERROR; \
11960 goto axis_range_end; } \
11961 } else { \
11962 hasNsNodes = 1; \
11963 if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
11964 ctxt->error = XPATH_MEMORY_ERROR; \
11965 if (breakOnFirstHit) goto first_hit; }
11967 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
11968 xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
11969 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
11970 const xmlChar *prefix = op->value4;
11971 const xmlChar *name = op->value5;
11972 const xmlChar *URI = NULL;
11974 #ifdef DEBUG_STEP
11975 int nbMatches = 0, prevMatches = 0;
11976 #endif
11977 int total = 0, hasNsNodes = 0;
11978 /* The popped object holding the context nodes */
11979 xmlXPathObjectPtr obj;
11980 /* The set of context nodes for the node tests */
11981 xmlNodeSetPtr contextSeq;
11982 int contextIdx;
11983 xmlNodePtr contextNode;
11984 /* The final resulting node set wrt to all context nodes */
11985 xmlNodeSetPtr outSeq;
11987 * The temporary resulting node set wrt 1 context node.
11988 * Used to feed predicate evaluation.
11990 xmlNodeSetPtr seq;
11991 xmlNodePtr cur;
11992 /* First predicate operator */
11993 xmlXPathStepOpPtr predOp;
11994 int maxPos; /* The requested position() (when a "[n]" predicate) */
11995 int hasPredicateRange, hasAxisRange, pos;
11996 int breakOnFirstHit;
11998 xmlXPathTraversalFunction next = NULL;
11999 int (*addNode) (xmlNodeSetPtr, xmlNodePtr);
12000 xmlXPathNodeSetMergeFunction mergeAndClear;
12001 xmlNodePtr oldContextNode;
12002 xmlXPathContextPtr xpctxt = ctxt->context;
12005 CHECK_TYPE0(XPATH_NODESET);
12006 obj = valuePop(ctxt);
12008 * Setup namespaces.
12010 if (prefix != NULL) {
12011 URI = xmlXPathNsLookup(xpctxt, prefix);
12012 if (URI == NULL) {
12013 xmlXPathReleaseObject(xpctxt, obj);
12014 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
12018 * Setup axis.
12020 * MAYBE FUTURE TODO: merging optimizations:
12021 * - If the nodes to be traversed wrt to the initial nodes and
12022 * the current axis cannot overlap, then we could avoid searching
12023 * for duplicates during the merge.
12024 * But the question is how/when to evaluate if they cannot overlap.
12025 * Example: if we know that for two initial nodes, the one is
12026 * not in the ancestor-or-self axis of the other, then we could safely
12027 * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
12028 * the descendant-or-self axis.
12030 mergeAndClear = xmlXPathNodeSetMergeAndClear;
12031 switch (axis) {
12032 case AXIS_ANCESTOR:
12033 first = NULL;
12034 next = xmlXPathNextAncestor;
12035 break;
12036 case AXIS_ANCESTOR_OR_SELF:
12037 first = NULL;
12038 next = xmlXPathNextAncestorOrSelf;
12039 break;
12040 case AXIS_ATTRIBUTE:
12041 first = NULL;
12042 last = NULL;
12043 next = xmlXPathNextAttribute;
12044 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12045 break;
12046 case AXIS_CHILD:
12047 last = NULL;
12048 if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
12049 (type == NODE_TYPE_NODE))
12052 * Optimization if an element node type is 'element'.
12054 next = xmlXPathNextChildElement;
12055 } else
12056 next = xmlXPathNextChild;
12057 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12058 break;
12059 case AXIS_DESCENDANT:
12060 last = NULL;
12061 next = xmlXPathNextDescendant;
12062 break;
12063 case AXIS_DESCENDANT_OR_SELF:
12064 last = NULL;
12065 next = xmlXPathNextDescendantOrSelf;
12066 break;
12067 case AXIS_FOLLOWING:
12068 last = NULL;
12069 next = xmlXPathNextFollowing;
12070 break;
12071 case AXIS_FOLLOWING_SIBLING:
12072 last = NULL;
12073 next = xmlXPathNextFollowingSibling;
12074 break;
12075 case AXIS_NAMESPACE:
12076 first = NULL;
12077 last = NULL;
12078 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
12079 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12080 break;
12081 case AXIS_PARENT:
12082 first = NULL;
12083 next = xmlXPathNextParent;
12084 break;
12085 case AXIS_PRECEDING:
12086 first = NULL;
12087 next = xmlXPathNextPrecedingInternal;
12088 break;
12089 case AXIS_PRECEDING_SIBLING:
12090 first = NULL;
12091 next = xmlXPathNextPrecedingSibling;
12092 break;
12093 case AXIS_SELF:
12094 first = NULL;
12095 last = NULL;
12096 next = xmlXPathNextSelf;
12097 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12098 break;
12101 #ifdef DEBUG_STEP
12102 xmlXPathDebugDumpStepAxis(op,
12103 (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
12104 #endif
12106 if (next == NULL) {
12107 xmlXPathReleaseObject(xpctxt, obj);
12108 return(0);
12110 contextSeq = obj->nodesetval;
12111 if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
12112 xmlXPathReleaseObject(xpctxt, obj);
12113 valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
12114 return(0);
12117 * Predicate optimization ---------------------------------------------
12118 * If this step has a last predicate, which contains a position(),
12119 * then we'll optimize (although not exactly "position()", but only
12120 * the short-hand form, i.e., "[n]".
12122 * Example - expression "/foo[parent::bar][1]":
12124 * COLLECT 'child' 'name' 'node' foo -- op (we are here)
12125 * ROOT -- op->ch1
12126 * PREDICATE -- op->ch2 (predOp)
12127 * PREDICATE -- predOp->ch1 = [parent::bar]
12128 * SORT
12129 * COLLECT 'parent' 'name' 'node' bar
12130 * NODE
12131 * ELEM Object is a number : 1 -- predOp->ch2 = [1]
12134 maxPos = 0;
12135 predOp = NULL;
12136 hasPredicateRange = 0;
12137 hasAxisRange = 0;
12138 if (op->ch2 != -1) {
12140 * There's at least one predicate. 16 == XPATH_OP_PREDICATE
12142 predOp = &ctxt->comp->steps[op->ch2];
12143 if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
12144 if (predOp->ch1 != -1) {
12146 * Use the next inner predicate operator.
12148 predOp = &ctxt->comp->steps[predOp->ch1];
12149 hasPredicateRange = 1;
12150 } else {
12152 * There's no other predicate than the [n] predicate.
12154 predOp = NULL;
12155 hasAxisRange = 1;
12159 breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
12161 * Axis traversal -----------------------------------------------------
12164 * 2.3 Node Tests
12165 * - For the attribute axis, the principal node type is attribute.
12166 * - For the namespace axis, the principal node type is namespace.
12167 * - For other axes, the principal node type is element.
12169 * A node test * is true for any node of the
12170 * principal node type. For example, child::* will
12171 * select all element children of the context node
12173 oldContextNode = xpctxt->node;
12174 addNode = xmlXPathNodeSetAddUnique;
12175 outSeq = NULL;
12176 seq = NULL;
12177 contextNode = NULL;
12178 contextIdx = 0;
12181 while (((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) &&
12182 (ctxt->error == XPATH_EXPRESSION_OK)) {
12183 xpctxt->node = contextSeq->nodeTab[contextIdx++];
12185 if (seq == NULL) {
12186 seq = xmlXPathNodeSetCreate(NULL);
12187 if (seq == NULL) {
12188 /* TODO: Propagate memory error. */
12189 total = 0;
12190 goto error;
12194 * Traverse the axis and test the nodes.
12196 pos = 0;
12197 cur = NULL;
12198 hasNsNodes = 0;
12199 do {
12200 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12201 goto error;
12203 cur = next(ctxt, cur);
12204 if (cur == NULL)
12205 break;
12208 * QUESTION TODO: What does the "first" and "last" stuff do?
12210 if ((first != NULL) && (*first != NULL)) {
12211 if (*first == cur)
12212 break;
12213 if (((total % 256) == 0) &&
12214 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12215 (xmlXPathCmpNodesExt(*first, cur) >= 0))
12216 #else
12217 (xmlXPathCmpNodes(*first, cur) >= 0))
12218 #endif
12220 break;
12223 if ((last != NULL) && (*last != NULL)) {
12224 if (*last == cur)
12225 break;
12226 if (((total % 256) == 0) &&
12227 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12228 (xmlXPathCmpNodesExt(cur, *last) >= 0))
12229 #else
12230 (xmlXPathCmpNodes(cur, *last) >= 0))
12231 #endif
12233 break;
12237 total++;
12239 #ifdef DEBUG_STEP
12240 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
12241 #endif
12243 switch (test) {
12244 case NODE_TEST_NONE:
12245 total = 0;
12246 STRANGE
12247 goto error;
12248 case NODE_TEST_TYPE:
12249 if (type == NODE_TYPE_NODE) {
12250 switch (cur->type) {
12251 case XML_DOCUMENT_NODE:
12252 case XML_HTML_DOCUMENT_NODE:
12253 case XML_ELEMENT_NODE:
12254 case XML_ATTRIBUTE_NODE:
12255 case XML_PI_NODE:
12256 case XML_COMMENT_NODE:
12257 case XML_CDATA_SECTION_NODE:
12258 case XML_TEXT_NODE:
12259 XP_TEST_HIT
12260 break;
12261 case XML_NAMESPACE_DECL: {
12262 if (axis == AXIS_NAMESPACE) {
12263 XP_TEST_HIT_NS
12264 } else {
12265 hasNsNodes = 1;
12266 XP_TEST_HIT
12268 break;
12270 default:
12271 break;
12273 } else if (cur->type == (xmlElementType) type) {
12274 if (cur->type == XML_NAMESPACE_DECL)
12275 XP_TEST_HIT_NS
12276 else
12277 XP_TEST_HIT
12278 } else if ((type == NODE_TYPE_TEXT) &&
12279 (cur->type == XML_CDATA_SECTION_NODE))
12281 XP_TEST_HIT
12283 break;
12284 case NODE_TEST_PI:
12285 if ((cur->type == XML_PI_NODE) &&
12286 ((name == NULL) || xmlStrEqual(name, cur->name)))
12288 XP_TEST_HIT
12290 break;
12291 case NODE_TEST_ALL:
12292 if (axis == AXIS_ATTRIBUTE) {
12293 if (cur->type == XML_ATTRIBUTE_NODE)
12295 if (prefix == NULL)
12297 XP_TEST_HIT
12298 } else if ((cur->ns != NULL) &&
12299 (xmlStrEqual(URI, cur->ns->href)))
12301 XP_TEST_HIT
12304 } else if (axis == AXIS_NAMESPACE) {
12305 if (cur->type == XML_NAMESPACE_DECL)
12307 XP_TEST_HIT_NS
12309 } else {
12310 if (cur->type == XML_ELEMENT_NODE) {
12311 if (prefix == NULL)
12313 XP_TEST_HIT
12315 } else if ((cur->ns != NULL) &&
12316 (xmlStrEqual(URI, cur->ns->href)))
12318 XP_TEST_HIT
12322 break;
12323 case NODE_TEST_NS:{
12324 TODO;
12325 break;
12327 case NODE_TEST_NAME:
12328 if (axis == AXIS_ATTRIBUTE) {
12329 if (cur->type != XML_ATTRIBUTE_NODE)
12330 break;
12331 } else if (axis == AXIS_NAMESPACE) {
12332 if (cur->type != XML_NAMESPACE_DECL)
12333 break;
12334 } else {
12335 if (cur->type != XML_ELEMENT_NODE)
12336 break;
12338 switch (cur->type) {
12339 case XML_ELEMENT_NODE:
12340 if (xmlStrEqual(name, cur->name)) {
12341 if (prefix == NULL) {
12342 if (cur->ns == NULL)
12344 XP_TEST_HIT
12346 } else {
12347 if ((cur->ns != NULL) &&
12348 (xmlStrEqual(URI, cur->ns->href)))
12350 XP_TEST_HIT
12354 break;
12355 case XML_ATTRIBUTE_NODE:{
12356 xmlAttrPtr attr = (xmlAttrPtr) cur;
12358 if (xmlStrEqual(name, attr->name)) {
12359 if (prefix == NULL) {
12360 if ((attr->ns == NULL) ||
12361 (attr->ns->prefix == NULL))
12363 XP_TEST_HIT
12365 } else {
12366 if ((attr->ns != NULL) &&
12367 (xmlStrEqual(URI,
12368 attr->ns->href)))
12370 XP_TEST_HIT
12374 break;
12376 case XML_NAMESPACE_DECL:
12377 if (cur->type == XML_NAMESPACE_DECL) {
12378 xmlNsPtr ns = (xmlNsPtr) cur;
12380 if ((ns->prefix != NULL) && (name != NULL)
12381 && (xmlStrEqual(ns->prefix, name)))
12383 XP_TEST_HIT_NS
12386 break;
12387 default:
12388 break;
12390 break;
12391 } /* switch(test) */
12392 } while ((cur != NULL) && (ctxt->error == XPATH_EXPRESSION_OK));
12394 goto apply_predicates;
12396 axis_range_end: /* ----------------------------------------------------- */
12398 * We have a "/foo[n]", and position() = n was reached.
12399 * Note that we can have as well "/foo/::parent::foo[1]", so
12400 * a duplicate-aware merge is still needed.
12401 * Merge with the result.
12403 if (outSeq == NULL) {
12404 outSeq = seq;
12405 seq = NULL;
12406 } else
12407 /* TODO: Check memory error. */
12408 outSeq = mergeAndClear(outSeq, seq);
12410 * Break if only a true/false result was requested.
12412 if (toBool)
12413 break;
12414 continue;
12416 first_hit: /* ---------------------------------------------------------- */
12418 * Break if only a true/false result was requested and
12419 * no predicates existed and a node test succeeded.
12421 if (outSeq == NULL) {
12422 outSeq = seq;
12423 seq = NULL;
12424 } else
12425 /* TODO: Check memory error. */
12426 outSeq = mergeAndClear(outSeq, seq);
12427 break;
12429 #ifdef DEBUG_STEP
12430 if (seq != NULL)
12431 nbMatches += seq->nodeNr;
12432 #endif
12434 apply_predicates: /* --------------------------------------------------- */
12435 if (ctxt->error != XPATH_EXPRESSION_OK)
12436 goto error;
12439 * Apply predicates.
12441 if ((predOp != NULL) && (seq->nodeNr > 0)) {
12443 * E.g. when we have a "/foo[some expression][n]".
12446 * QUESTION TODO: The old predicate evaluation took into
12447 * account location-sets.
12448 * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
12449 * Do we expect such a set here?
12450 * All what I learned now from the evaluation semantics
12451 * does not indicate that a location-set will be processed
12452 * here, so this looks OK.
12455 * Iterate over all predicates, starting with the outermost
12456 * predicate.
12457 * TODO: Problem: we cannot execute the inner predicates first
12458 * since we cannot go back *up* the operator tree!
12459 * Options we have:
12460 * 1) Use of recursive functions (like is it currently done
12461 * via xmlXPathCompOpEval())
12462 * 2) Add a predicate evaluation information stack to the
12463 * context struct
12464 * 3) Change the way the operators are linked; we need a
12465 * "parent" field on xmlXPathStepOp
12467 * For the moment, I'll try to solve this with a recursive
12468 * function: xmlXPathCompOpEvalPredicate().
12470 if (hasPredicateRange != 0)
12471 xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, maxPos, maxPos,
12472 hasNsNodes);
12473 else
12474 xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, 1, seq->nodeNr,
12475 hasNsNodes);
12477 if (ctxt->error != XPATH_EXPRESSION_OK) {
12478 total = 0;
12479 goto error;
12483 if (seq->nodeNr > 0) {
12485 * Add to result set.
12487 if (outSeq == NULL) {
12488 outSeq = seq;
12489 seq = NULL;
12490 } else {
12491 /* TODO: Check memory error. */
12492 outSeq = mergeAndClear(outSeq, seq);
12495 if (toBool)
12496 break;
12500 error:
12501 if ((obj->boolval) && (obj->user != NULL)) {
12503 * QUESTION TODO: What does this do and why?
12504 * TODO: Do we have to do this also for the "error"
12505 * cleanup further down?
12507 ctxt->value->boolval = 1;
12508 ctxt->value->user = obj->user;
12509 obj->user = NULL;
12510 obj->boolval = 0;
12512 xmlXPathReleaseObject(xpctxt, obj);
12515 * Ensure we return at least an empty set.
12517 if (outSeq == NULL) {
12518 if ((seq != NULL) && (seq->nodeNr == 0))
12519 outSeq = seq;
12520 else
12521 /* TODO: Check memory error. */
12522 outSeq = xmlXPathNodeSetCreate(NULL);
12524 if ((seq != NULL) && (seq != outSeq)) {
12525 xmlXPathFreeNodeSet(seq);
12528 * Hand over the result. Better to push the set also in
12529 * case of errors.
12531 valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
12533 * Reset the context node.
12535 xpctxt->node = oldContextNode;
12537 * When traversing the namespace axis in "toBool" mode, it's
12538 * possible that tmpNsList wasn't freed.
12540 if (xpctxt->tmpNsList != NULL) {
12541 xmlFree(xpctxt->tmpNsList);
12542 xpctxt->tmpNsList = NULL;
12545 #ifdef DEBUG_STEP
12546 xmlGenericError(xmlGenericErrorContext,
12547 "\nExamined %d nodes, found %d nodes at that step\n",
12548 total, nbMatches);
12549 #endif
12551 return(total);
12554 static int
12555 xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12556 xmlXPathStepOpPtr op, xmlNodePtr * first);
12559 * xmlXPathCompOpEvalFirst:
12560 * @ctxt: the XPath parser context with the compiled expression
12561 * @op: an XPath compiled operation
12562 * @first: the first elem found so far
12564 * Evaluate the Precompiled XPath operation searching only the first
12565 * element in document order
12567 * Returns the number of examined objects.
12569 static int
12570 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
12571 xmlXPathStepOpPtr op, xmlNodePtr * first)
12573 int total = 0, cur;
12574 xmlXPathCompExprPtr comp;
12575 xmlXPathObjectPtr arg1, arg2;
12577 CHECK_ERROR0;
12578 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12579 return(0);
12580 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12581 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12582 ctxt->context->depth += 1;
12583 comp = ctxt->comp;
12584 switch (op->op) {
12585 case XPATH_OP_END:
12586 break;
12587 case XPATH_OP_UNION:
12588 total =
12589 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12590 first);
12591 CHECK_ERROR0;
12592 if ((ctxt->value != NULL)
12593 && (ctxt->value->type == XPATH_NODESET)
12594 && (ctxt->value->nodesetval != NULL)
12595 && (ctxt->value->nodesetval->nodeNr >= 1)) {
12597 * limit tree traversing to first node in the result
12600 * OPTIMIZE TODO: This implicitly sorts
12601 * the result, even if not needed. E.g. if the argument
12602 * of the count() function, no sorting is needed.
12603 * OPTIMIZE TODO: How do we know if the node-list wasn't
12604 * already sorted?
12606 if (ctxt->value->nodesetval->nodeNr > 1)
12607 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12608 *first = ctxt->value->nodesetval->nodeTab[0];
12610 cur =
12611 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
12612 first);
12613 CHECK_ERROR0;
12615 arg2 = valuePop(ctxt);
12616 arg1 = valuePop(ctxt);
12617 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
12618 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
12619 xmlXPathReleaseObject(ctxt->context, arg1);
12620 xmlXPathReleaseObject(ctxt->context, arg2);
12621 XP_ERROR0(XPATH_INVALID_TYPE);
12623 if ((ctxt->context->opLimit != 0) &&
12624 (((arg1->nodesetval != NULL) &&
12625 (xmlXPathCheckOpLimit(ctxt,
12626 arg1->nodesetval->nodeNr) < 0)) ||
12627 ((arg2->nodesetval != NULL) &&
12628 (xmlXPathCheckOpLimit(ctxt,
12629 arg2->nodesetval->nodeNr) < 0)))) {
12630 xmlXPathReleaseObject(ctxt->context, arg1);
12631 xmlXPathReleaseObject(ctxt->context, arg2);
12632 break;
12635 /* TODO: Check memory error. */
12636 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12637 arg2->nodesetval);
12638 valuePush(ctxt, arg1);
12639 xmlXPathReleaseObject(ctxt->context, arg2);
12640 /* optimizer */
12641 if (total > cur)
12642 xmlXPathCompSwap(op);
12643 total += cur;
12644 break;
12645 case XPATH_OP_ROOT:
12646 xmlXPathRoot(ctxt);
12647 break;
12648 case XPATH_OP_NODE:
12649 if (op->ch1 != -1)
12650 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12651 CHECK_ERROR0;
12652 if (op->ch2 != -1)
12653 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12654 CHECK_ERROR0;
12655 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12656 ctxt->context->node));
12657 break;
12658 case XPATH_OP_COLLECT:{
12659 if (op->ch1 == -1)
12660 break;
12662 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12663 CHECK_ERROR0;
12665 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
12666 break;
12668 case XPATH_OP_VALUE:
12669 valuePush(ctxt,
12670 xmlXPathCacheObjectCopy(ctxt->context,
12671 (xmlXPathObjectPtr) op->value4));
12672 break;
12673 case XPATH_OP_SORT:
12674 if (op->ch1 != -1)
12675 total +=
12676 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12677 first);
12678 CHECK_ERROR0;
12679 if ((ctxt->value != NULL)
12680 && (ctxt->value->type == XPATH_NODESET)
12681 && (ctxt->value->nodesetval != NULL)
12682 && (ctxt->value->nodesetval->nodeNr > 1))
12683 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12684 break;
12685 #ifdef XP_OPTIMIZED_FILTER_FIRST
12686 case XPATH_OP_FILTER:
12687 total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
12688 break;
12689 #endif
12690 default:
12691 total += xmlXPathCompOpEval(ctxt, op);
12692 break;
12695 ctxt->context->depth -= 1;
12696 return(total);
12700 * xmlXPathCompOpEvalLast:
12701 * @ctxt: the XPath parser context with the compiled expression
12702 * @op: an XPath compiled operation
12703 * @last: the last elem found so far
12705 * Evaluate the Precompiled XPath operation searching only the last
12706 * element in document order
12708 * Returns the number of nodes traversed
12710 static int
12711 xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
12712 xmlNodePtr * last)
12714 int total = 0, cur;
12715 xmlXPathCompExprPtr comp;
12716 xmlXPathObjectPtr arg1, arg2;
12718 CHECK_ERROR0;
12719 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12720 return(0);
12721 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12722 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12723 ctxt->context->depth += 1;
12724 comp = ctxt->comp;
12725 switch (op->op) {
12726 case XPATH_OP_END:
12727 break;
12728 case XPATH_OP_UNION:
12729 total =
12730 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
12731 CHECK_ERROR0;
12732 if ((ctxt->value != NULL)
12733 && (ctxt->value->type == XPATH_NODESET)
12734 && (ctxt->value->nodesetval != NULL)
12735 && (ctxt->value->nodesetval->nodeNr >= 1)) {
12737 * limit tree traversing to first node in the result
12739 if (ctxt->value->nodesetval->nodeNr > 1)
12740 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12741 *last =
12742 ctxt->value->nodesetval->nodeTab[ctxt->value->
12743 nodesetval->nodeNr -
12746 cur =
12747 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
12748 CHECK_ERROR0;
12749 if ((ctxt->value != NULL)
12750 && (ctxt->value->type == XPATH_NODESET)
12751 && (ctxt->value->nodesetval != NULL)
12752 && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
12755 arg2 = valuePop(ctxt);
12756 arg1 = valuePop(ctxt);
12757 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
12758 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
12759 xmlXPathReleaseObject(ctxt->context, arg1);
12760 xmlXPathReleaseObject(ctxt->context, arg2);
12761 XP_ERROR0(XPATH_INVALID_TYPE);
12763 if ((ctxt->context->opLimit != 0) &&
12764 (((arg1->nodesetval != NULL) &&
12765 (xmlXPathCheckOpLimit(ctxt,
12766 arg1->nodesetval->nodeNr) < 0)) ||
12767 ((arg2->nodesetval != NULL) &&
12768 (xmlXPathCheckOpLimit(ctxt,
12769 arg2->nodesetval->nodeNr) < 0)))) {
12770 xmlXPathReleaseObject(ctxt->context, arg1);
12771 xmlXPathReleaseObject(ctxt->context, arg2);
12772 break;
12775 /* TODO: Check memory error. */
12776 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12777 arg2->nodesetval);
12778 valuePush(ctxt, arg1);
12779 xmlXPathReleaseObject(ctxt->context, arg2);
12780 /* optimizer */
12781 if (total > cur)
12782 xmlXPathCompSwap(op);
12783 total += cur;
12784 break;
12785 case XPATH_OP_ROOT:
12786 xmlXPathRoot(ctxt);
12787 break;
12788 case XPATH_OP_NODE:
12789 if (op->ch1 != -1)
12790 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12791 CHECK_ERROR0;
12792 if (op->ch2 != -1)
12793 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12794 CHECK_ERROR0;
12795 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12796 ctxt->context->node));
12797 break;
12798 case XPATH_OP_COLLECT:{
12799 if (op->ch1 == -1)
12800 break;
12802 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12803 CHECK_ERROR0;
12805 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
12806 break;
12808 case XPATH_OP_VALUE:
12809 valuePush(ctxt,
12810 xmlXPathCacheObjectCopy(ctxt->context,
12811 (xmlXPathObjectPtr) op->value4));
12812 break;
12813 case XPATH_OP_SORT:
12814 if (op->ch1 != -1)
12815 total +=
12816 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
12817 last);
12818 CHECK_ERROR0;
12819 if ((ctxt->value != NULL)
12820 && (ctxt->value->type == XPATH_NODESET)
12821 && (ctxt->value->nodesetval != NULL)
12822 && (ctxt->value->nodesetval->nodeNr > 1))
12823 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12824 break;
12825 default:
12826 total += xmlXPathCompOpEval(ctxt, op);
12827 break;
12830 ctxt->context->depth -= 1;
12831 return (total);
12834 #ifdef XP_OPTIMIZED_FILTER_FIRST
12835 static int
12836 xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12837 xmlXPathStepOpPtr op, xmlNodePtr * first)
12839 int total = 0;
12840 xmlXPathCompExprPtr comp;
12841 xmlXPathObjectPtr obj;
12842 xmlNodeSetPtr set;
12844 CHECK_ERROR0;
12845 comp = ctxt->comp;
12847 * Optimization for ()[last()] selection i.e. the last elem
12849 if ((op->ch1 != -1) && (op->ch2 != -1) &&
12850 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
12851 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
12852 int f = comp->steps[op->ch2].ch1;
12854 if ((f != -1) &&
12855 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
12856 (comp->steps[f].value5 == NULL) &&
12857 (comp->steps[f].value == 0) &&
12858 (comp->steps[f].value4 != NULL) &&
12859 (xmlStrEqual
12860 (comp->steps[f].value4, BAD_CAST "last"))) {
12861 xmlNodePtr last = NULL;
12863 total +=
12864 xmlXPathCompOpEvalLast(ctxt,
12865 &comp->steps[op->ch1],
12866 &last);
12867 CHECK_ERROR0;
12869 * The nodeset should be in document order,
12870 * Keep only the last value
12872 if ((ctxt->value != NULL) &&
12873 (ctxt->value->type == XPATH_NODESET) &&
12874 (ctxt->value->nodesetval != NULL) &&
12875 (ctxt->value->nodesetval->nodeTab != NULL) &&
12876 (ctxt->value->nodesetval->nodeNr > 1)) {
12877 xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
12878 *first = *(ctxt->value->nodesetval->nodeTab);
12880 return (total);
12884 if (op->ch1 != -1)
12885 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12886 CHECK_ERROR0;
12887 if (op->ch2 == -1)
12888 return (total);
12889 if (ctxt->value == NULL)
12890 return (total);
12892 #ifdef LIBXML_XPTR_LOCS_ENABLED
12894 * Hum are we filtering the result of an XPointer expression
12896 if (ctxt->value->type == XPATH_LOCATIONSET) {
12897 xmlLocationSetPtr locset = ctxt->value->user;
12899 if (locset != NULL) {
12900 xmlXPathLocationSetFilter(ctxt, locset, op->ch2, 1, 1);
12901 if (locset->locNr > 0)
12902 *first = (xmlNodePtr) locset->locTab[0]->user;
12905 return (total);
12907 #endif /* LIBXML_XPTR_LOCS_ENABLED */
12910 * In case of errors, xmlXPathNodeSetFilter can pop additional nodes from
12911 * the stack. We have to temporarily remove the nodeset object from the
12912 * stack to avoid freeing it prematurely.
12914 CHECK_TYPE0(XPATH_NODESET);
12915 obj = valuePop(ctxt);
12916 set = obj->nodesetval;
12917 if (set != NULL) {
12918 xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, 1, 1);
12919 if (set->nodeNr > 0)
12920 *first = set->nodeTab[0];
12922 valuePush(ctxt, obj);
12924 return (total);
12926 #endif /* XP_OPTIMIZED_FILTER_FIRST */
12929 * xmlXPathCompOpEval:
12930 * @ctxt: the XPath parser context with the compiled expression
12931 * @op: an XPath compiled operation
12933 * Evaluate the Precompiled XPath operation
12934 * Returns the number of nodes traversed
12936 static int
12937 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
12939 int total = 0;
12940 int equal, ret;
12941 xmlXPathCompExprPtr comp;
12942 xmlXPathObjectPtr arg1, arg2;
12944 CHECK_ERROR0;
12945 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12946 return(0);
12947 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12948 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12949 ctxt->context->depth += 1;
12950 comp = ctxt->comp;
12951 switch (op->op) {
12952 case XPATH_OP_END:
12953 break;
12954 case XPATH_OP_AND:
12955 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12956 CHECK_ERROR0;
12957 xmlXPathBooleanFunction(ctxt, 1);
12958 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
12959 break;
12960 arg2 = valuePop(ctxt);
12961 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12962 if (ctxt->error) {
12963 xmlXPathFreeObject(arg2);
12964 break;
12966 xmlXPathBooleanFunction(ctxt, 1);
12967 if (ctxt->value != NULL)
12968 ctxt->value->boolval &= arg2->boolval;
12969 xmlXPathReleaseObject(ctxt->context, arg2);
12970 break;
12971 case XPATH_OP_OR:
12972 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12973 CHECK_ERROR0;
12974 xmlXPathBooleanFunction(ctxt, 1);
12975 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
12976 break;
12977 arg2 = valuePop(ctxt);
12978 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12979 if (ctxt->error) {
12980 xmlXPathFreeObject(arg2);
12981 break;
12983 xmlXPathBooleanFunction(ctxt, 1);
12984 if (ctxt->value != NULL)
12985 ctxt->value->boolval |= arg2->boolval;
12986 xmlXPathReleaseObject(ctxt->context, arg2);
12987 break;
12988 case XPATH_OP_EQUAL:
12989 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12990 CHECK_ERROR0;
12991 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12992 CHECK_ERROR0;
12993 if (op->value)
12994 equal = xmlXPathEqualValues(ctxt);
12995 else
12996 equal = xmlXPathNotEqualValues(ctxt);
12997 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
12998 break;
12999 case XPATH_OP_CMP:
13000 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13001 CHECK_ERROR0;
13002 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13003 CHECK_ERROR0;
13004 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
13005 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
13006 break;
13007 case XPATH_OP_PLUS:
13008 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13009 CHECK_ERROR0;
13010 if (op->ch2 != -1) {
13011 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13013 CHECK_ERROR0;
13014 if (op->value == 0)
13015 xmlXPathSubValues(ctxt);
13016 else if (op->value == 1)
13017 xmlXPathAddValues(ctxt);
13018 else if (op->value == 2)
13019 xmlXPathValueFlipSign(ctxt);
13020 else if (op->value == 3) {
13021 CAST_TO_NUMBER;
13022 CHECK_TYPE0(XPATH_NUMBER);
13024 break;
13025 case XPATH_OP_MULT:
13026 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13027 CHECK_ERROR0;
13028 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13029 CHECK_ERROR0;
13030 if (op->value == 0)
13031 xmlXPathMultValues(ctxt);
13032 else if (op->value == 1)
13033 xmlXPathDivValues(ctxt);
13034 else if (op->value == 2)
13035 xmlXPathModValues(ctxt);
13036 break;
13037 case XPATH_OP_UNION:
13038 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13039 CHECK_ERROR0;
13040 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13041 CHECK_ERROR0;
13043 arg2 = valuePop(ctxt);
13044 arg1 = valuePop(ctxt);
13045 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
13046 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
13047 xmlXPathReleaseObject(ctxt->context, arg1);
13048 xmlXPathReleaseObject(ctxt->context, arg2);
13049 XP_ERROR0(XPATH_INVALID_TYPE);
13051 if ((ctxt->context->opLimit != 0) &&
13052 (((arg1->nodesetval != NULL) &&
13053 (xmlXPathCheckOpLimit(ctxt,
13054 arg1->nodesetval->nodeNr) < 0)) ||
13055 ((arg2->nodesetval != NULL) &&
13056 (xmlXPathCheckOpLimit(ctxt,
13057 arg2->nodesetval->nodeNr) < 0)))) {
13058 xmlXPathReleaseObject(ctxt->context, arg1);
13059 xmlXPathReleaseObject(ctxt->context, arg2);
13060 break;
13063 if ((arg1->nodesetval == NULL) ||
13064 ((arg2->nodesetval != NULL) &&
13065 (arg2->nodesetval->nodeNr != 0)))
13067 /* TODO: Check memory error. */
13068 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
13069 arg2->nodesetval);
13072 valuePush(ctxt, arg1);
13073 xmlXPathReleaseObject(ctxt->context, arg2);
13074 break;
13075 case XPATH_OP_ROOT:
13076 xmlXPathRoot(ctxt);
13077 break;
13078 case XPATH_OP_NODE:
13079 if (op->ch1 != -1)
13080 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13081 CHECK_ERROR0;
13082 if (op->ch2 != -1)
13083 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13084 CHECK_ERROR0;
13085 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
13086 ctxt->context->node));
13087 break;
13088 case XPATH_OP_COLLECT:{
13089 if (op->ch1 == -1)
13090 break;
13092 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13093 CHECK_ERROR0;
13095 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
13096 break;
13098 case XPATH_OP_VALUE:
13099 valuePush(ctxt,
13100 xmlXPathCacheObjectCopy(ctxt->context,
13101 (xmlXPathObjectPtr) op->value4));
13102 break;
13103 case XPATH_OP_VARIABLE:{
13104 xmlXPathObjectPtr val;
13106 if (op->ch1 != -1)
13107 total +=
13108 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13109 if (op->value5 == NULL) {
13110 val = xmlXPathVariableLookup(ctxt->context, op->value4);
13111 if (val == NULL)
13112 XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
13113 valuePush(ctxt, val);
13114 } else {
13115 const xmlChar *URI;
13117 URI = xmlXPathNsLookup(ctxt->context, op->value5);
13118 if (URI == NULL) {
13119 xmlGenericError(xmlGenericErrorContext,
13120 "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
13121 (char *) op->value4, (char *)op->value5);
13122 ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
13123 break;
13125 val = xmlXPathVariableLookupNS(ctxt->context,
13126 op->value4, URI);
13127 if (val == NULL)
13128 XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
13129 valuePush(ctxt, val);
13131 break;
13133 case XPATH_OP_FUNCTION:{
13134 xmlXPathFunction func;
13135 const xmlChar *oldFunc, *oldFuncURI;
13136 int i;
13137 int frame;
13139 frame = ctxt->valueNr;
13140 if (op->ch1 != -1) {
13141 total +=
13142 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13143 if (ctxt->error != XPATH_EXPRESSION_OK)
13144 break;
13146 if (ctxt->valueNr < frame + op->value) {
13147 xmlGenericError(xmlGenericErrorContext,
13148 "xmlXPathCompOpEval: parameter error\n");
13149 ctxt->error = XPATH_INVALID_OPERAND;
13150 break;
13152 for (i = 0; i < op->value; i++) {
13153 if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
13154 xmlGenericError(xmlGenericErrorContext,
13155 "xmlXPathCompOpEval: parameter error\n");
13156 ctxt->error = XPATH_INVALID_OPERAND;
13157 break;
13160 if (op->cache != NULL)
13161 func = op->cache;
13162 else {
13163 const xmlChar *URI = NULL;
13165 if (op->value5 == NULL)
13166 func =
13167 xmlXPathFunctionLookup(ctxt->context,
13168 op->value4);
13169 else {
13170 URI = xmlXPathNsLookup(ctxt->context, op->value5);
13171 if (URI == NULL) {
13172 xmlGenericError(xmlGenericErrorContext,
13173 "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
13174 (char *)op->value4, (char *)op->value5);
13175 ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
13176 break;
13178 func = xmlXPathFunctionLookupNS(ctxt->context,
13179 op->value4, URI);
13181 if (func == NULL) {
13182 xmlGenericError(xmlGenericErrorContext,
13183 "xmlXPathCompOpEval: function %s not found\n",
13184 (char *)op->value4);
13185 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
13187 op->cache = func;
13188 op->cacheURI = (void *) URI;
13190 oldFunc = ctxt->context->function;
13191 oldFuncURI = ctxt->context->functionURI;
13192 ctxt->context->function = op->value4;
13193 ctxt->context->functionURI = op->cacheURI;
13194 func(ctxt, op->value);
13195 ctxt->context->function = oldFunc;
13196 ctxt->context->functionURI = oldFuncURI;
13197 if ((ctxt->error == XPATH_EXPRESSION_OK) &&
13198 (ctxt->valueNr != frame + 1))
13199 XP_ERROR0(XPATH_STACK_ERROR);
13200 break;
13202 case XPATH_OP_ARG:
13203 if (op->ch1 != -1) {
13204 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13205 CHECK_ERROR0;
13207 if (op->ch2 != -1) {
13208 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13209 CHECK_ERROR0;
13211 break;
13212 case XPATH_OP_PREDICATE:
13213 case XPATH_OP_FILTER:{
13214 xmlXPathObjectPtr obj;
13215 xmlNodeSetPtr set;
13218 * Optimization for ()[1] selection i.e. the first elem
13220 if ((op->ch1 != -1) && (op->ch2 != -1) &&
13221 #ifdef XP_OPTIMIZED_FILTER_FIRST
13223 * FILTER TODO: Can we assume that the inner processing
13224 * will result in an ordered list if we have an
13225 * XPATH_OP_FILTER?
13226 * What about an additional field or flag on
13227 * xmlXPathObject like @sorted ? This way we wouldn't need
13228 * to assume anything, so it would be more robust and
13229 * easier to optimize.
13231 ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
13232 (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
13233 #else
13234 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13235 #endif
13236 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
13237 xmlXPathObjectPtr val;
13239 val = comp->steps[op->ch2].value4;
13240 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
13241 (val->floatval == 1.0)) {
13242 xmlNodePtr first = NULL;
13244 total +=
13245 xmlXPathCompOpEvalFirst(ctxt,
13246 &comp->steps[op->ch1],
13247 &first);
13248 CHECK_ERROR0;
13250 * The nodeset should be in document order,
13251 * Keep only the first value
13253 if ((ctxt->value != NULL) &&
13254 (ctxt->value->type == XPATH_NODESET) &&
13255 (ctxt->value->nodesetval != NULL) &&
13256 (ctxt->value->nodesetval->nodeNr > 1))
13257 xmlXPathNodeSetClearFromPos(ctxt->value->nodesetval,
13258 1, 1);
13259 break;
13263 * Optimization for ()[last()] selection i.e. the last elem
13265 if ((op->ch1 != -1) && (op->ch2 != -1) &&
13266 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13267 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
13268 int f = comp->steps[op->ch2].ch1;
13270 if ((f != -1) &&
13271 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
13272 (comp->steps[f].value5 == NULL) &&
13273 (comp->steps[f].value == 0) &&
13274 (comp->steps[f].value4 != NULL) &&
13275 (xmlStrEqual
13276 (comp->steps[f].value4, BAD_CAST "last"))) {
13277 xmlNodePtr last = NULL;
13279 total +=
13280 xmlXPathCompOpEvalLast(ctxt,
13281 &comp->steps[op->ch1],
13282 &last);
13283 CHECK_ERROR0;
13285 * The nodeset should be in document order,
13286 * Keep only the last value
13288 if ((ctxt->value != NULL) &&
13289 (ctxt->value->type == XPATH_NODESET) &&
13290 (ctxt->value->nodesetval != NULL) &&
13291 (ctxt->value->nodesetval->nodeTab != NULL) &&
13292 (ctxt->value->nodesetval->nodeNr > 1))
13293 xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
13294 break;
13298 * Process inner predicates first.
13299 * Example "index[parent::book][1]":
13300 * ...
13301 * PREDICATE <-- we are here "[1]"
13302 * PREDICATE <-- process "[parent::book]" first
13303 * SORT
13304 * COLLECT 'parent' 'name' 'node' book
13305 * NODE
13306 * ELEM Object is a number : 1
13308 if (op->ch1 != -1)
13309 total +=
13310 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13311 CHECK_ERROR0;
13312 if (op->ch2 == -1)
13313 break;
13314 if (ctxt->value == NULL)
13315 break;
13317 #ifdef LIBXML_XPTR_LOCS_ENABLED
13319 * Hum are we filtering the result of an XPointer expression
13321 if (ctxt->value->type == XPATH_LOCATIONSET) {
13322 xmlLocationSetPtr locset = ctxt->value->user;
13323 xmlXPathLocationSetFilter(ctxt, locset, op->ch2,
13324 1, locset->locNr);
13325 break;
13327 #endif /* LIBXML_XPTR_LOCS_ENABLED */
13330 * In case of errors, xmlXPathNodeSetFilter can pop additional
13331 * nodes from the stack. We have to temporarily remove the
13332 * nodeset object from the stack to avoid freeing it
13333 * prematurely.
13335 CHECK_TYPE0(XPATH_NODESET);
13336 obj = valuePop(ctxt);
13337 set = obj->nodesetval;
13338 if (set != NULL)
13339 xmlXPathNodeSetFilter(ctxt, set, op->ch2,
13340 1, set->nodeNr, 1);
13341 valuePush(ctxt, obj);
13342 break;
13344 case XPATH_OP_SORT:
13345 if (op->ch1 != -1)
13346 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13347 CHECK_ERROR0;
13348 if ((ctxt->value != NULL) &&
13349 (ctxt->value->type == XPATH_NODESET) &&
13350 (ctxt->value->nodesetval != NULL) &&
13351 (ctxt->value->nodesetval->nodeNr > 1))
13353 xmlXPathNodeSetSort(ctxt->value->nodesetval);
13355 break;
13356 #ifdef LIBXML_XPTR_LOCS_ENABLED
13357 case XPATH_OP_RANGETO:{
13358 xmlXPathObjectPtr range;
13359 xmlXPathObjectPtr res, obj;
13360 xmlXPathObjectPtr tmp;
13361 xmlLocationSetPtr newlocset = NULL;
13362 xmlLocationSetPtr oldlocset;
13363 xmlNodeSetPtr oldset;
13364 xmlNodePtr oldnode = ctxt->context->node;
13365 int oldcs = ctxt->context->contextSize;
13366 int oldpp = ctxt->context->proximityPosition;
13367 int i, j;
13369 if (op->ch1 != -1) {
13370 total +=
13371 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13372 CHECK_ERROR0;
13374 if (ctxt->value == NULL) {
13375 XP_ERROR0(XPATH_INVALID_OPERAND);
13377 if (op->ch2 == -1)
13378 break;
13380 if (ctxt->value->type == XPATH_LOCATIONSET) {
13382 * Extract the old locset, and then evaluate the result of the
13383 * expression for all the element in the locset. use it to grow
13384 * up a new locset.
13386 CHECK_TYPE0(XPATH_LOCATIONSET);
13388 if ((ctxt->value->user == NULL) ||
13389 (((xmlLocationSetPtr) ctxt->value->user)->locNr == 0))
13390 break;
13392 obj = valuePop(ctxt);
13393 oldlocset = obj->user;
13395 newlocset = xmlXPtrLocationSetCreate(NULL);
13397 for (i = 0; i < oldlocset->locNr; i++) {
13399 * Run the evaluation with a node list made of a
13400 * single item in the nodelocset.
13402 ctxt->context->node = oldlocset->locTab[i]->user;
13403 ctxt->context->contextSize = oldlocset->locNr;
13404 ctxt->context->proximityPosition = i + 1;
13405 tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13406 ctxt->context->node);
13407 valuePush(ctxt, tmp);
13409 if (op->ch2 != -1)
13410 total +=
13411 xmlXPathCompOpEval(ctxt,
13412 &comp->steps[op->ch2]);
13413 if (ctxt->error != XPATH_EXPRESSION_OK) {
13414 xmlXPtrFreeLocationSet(newlocset);
13415 goto rangeto_error;
13418 res = valuePop(ctxt);
13419 if (res->type == XPATH_LOCATIONSET) {
13420 xmlLocationSetPtr rloc =
13421 (xmlLocationSetPtr)res->user;
13422 for (j=0; j<rloc->locNr; j++) {
13423 range = xmlXPtrNewRange(
13424 oldlocset->locTab[i]->user,
13425 oldlocset->locTab[i]->index,
13426 rloc->locTab[j]->user2,
13427 rloc->locTab[j]->index2);
13428 if (range != NULL) {
13429 xmlXPtrLocationSetAdd(newlocset, range);
13432 } else {
13433 range = xmlXPtrNewRangeNodeObject(
13434 (xmlNodePtr)oldlocset->locTab[i]->user, res);
13435 if (range != NULL) {
13436 xmlXPtrLocationSetAdd(newlocset,range);
13441 * Cleanup
13443 if (res != NULL) {
13444 xmlXPathReleaseObject(ctxt->context, res);
13446 if (ctxt->value == tmp) {
13447 res = valuePop(ctxt);
13448 xmlXPathReleaseObject(ctxt->context, res);
13451 } else { /* Not a location set */
13452 CHECK_TYPE0(XPATH_NODESET);
13453 obj = valuePop(ctxt);
13454 oldset = obj->nodesetval;
13456 newlocset = xmlXPtrLocationSetCreate(NULL);
13458 if (oldset != NULL) {
13459 for (i = 0; i < oldset->nodeNr; i++) {
13461 * Run the evaluation with a node list made of a single item
13462 * in the nodeset.
13464 ctxt->context->node = oldset->nodeTab[i];
13466 * OPTIMIZE TODO: Avoid recreation for every iteration.
13468 tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13469 ctxt->context->node);
13470 valuePush(ctxt, tmp);
13472 if (op->ch2 != -1)
13473 total +=
13474 xmlXPathCompOpEval(ctxt,
13475 &comp->steps[op->ch2]);
13476 if (ctxt->error != XPATH_EXPRESSION_OK) {
13477 xmlXPtrFreeLocationSet(newlocset);
13478 goto rangeto_error;
13481 res = valuePop(ctxt);
13482 range =
13483 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
13484 res);
13485 if (range != NULL) {
13486 xmlXPtrLocationSetAdd(newlocset, range);
13490 * Cleanup
13492 if (res != NULL) {
13493 xmlXPathReleaseObject(ctxt->context, res);
13495 if (ctxt->value == tmp) {
13496 res = valuePop(ctxt);
13497 xmlXPathReleaseObject(ctxt->context, res);
13504 * The result is used as the new evaluation set.
13506 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13507 rangeto_error:
13508 xmlXPathReleaseObject(ctxt->context, obj);
13509 ctxt->context->node = oldnode;
13510 ctxt->context->contextSize = oldcs;
13511 ctxt->context->proximityPosition = oldpp;
13512 break;
13514 #endif /* LIBXML_XPTR_LOCS_ENABLED */
13515 default:
13516 xmlGenericError(xmlGenericErrorContext,
13517 "XPath: unknown precompiled operation %d\n", op->op);
13518 ctxt->error = XPATH_INVALID_OPERAND;
13519 break;
13522 ctxt->context->depth -= 1;
13523 return (total);
13527 * xmlXPathCompOpEvalToBoolean:
13528 * @ctxt: the XPath parser context
13530 * Evaluates if the expression evaluates to true.
13532 * Returns 1 if true, 0 if false and -1 on API or internal errors.
13534 static int
13535 xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
13536 xmlXPathStepOpPtr op,
13537 int isPredicate)
13539 xmlXPathObjectPtr resObj = NULL;
13541 start:
13542 if (OP_LIMIT_EXCEEDED(ctxt, 1))
13543 return(0);
13544 /* comp = ctxt->comp; */
13545 switch (op->op) {
13546 case XPATH_OP_END:
13547 return (0);
13548 case XPATH_OP_VALUE:
13549 resObj = (xmlXPathObjectPtr) op->value4;
13550 if (isPredicate)
13551 return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
13552 return(xmlXPathCastToBoolean(resObj));
13553 case XPATH_OP_SORT:
13555 * We don't need sorting for boolean results. Skip this one.
13557 if (op->ch1 != -1) {
13558 op = &ctxt->comp->steps[op->ch1];
13559 goto start;
13561 return(0);
13562 case XPATH_OP_COLLECT:
13563 if (op->ch1 == -1)
13564 return(0);
13566 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
13567 if (ctxt->error != XPATH_EXPRESSION_OK)
13568 return(-1);
13570 xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
13571 if (ctxt->error != XPATH_EXPRESSION_OK)
13572 return(-1);
13574 resObj = valuePop(ctxt);
13575 if (resObj == NULL)
13576 return(-1);
13577 break;
13578 default:
13580 * Fallback to call xmlXPathCompOpEval().
13582 xmlXPathCompOpEval(ctxt, op);
13583 if (ctxt->error != XPATH_EXPRESSION_OK)
13584 return(-1);
13586 resObj = valuePop(ctxt);
13587 if (resObj == NULL)
13588 return(-1);
13589 break;
13592 if (resObj) {
13593 int res;
13595 if (resObj->type == XPATH_BOOLEAN) {
13596 res = resObj->boolval;
13597 } else if (isPredicate) {
13599 * For predicates a result of type "number" is handled
13600 * differently:
13601 * SPEC XPath 1.0:
13602 * "If the result is a number, the result will be converted
13603 * to true if the number is equal to the context position
13604 * and will be converted to false otherwise;"
13606 res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
13607 } else {
13608 res = xmlXPathCastToBoolean(resObj);
13610 xmlXPathReleaseObject(ctxt->context, resObj);
13611 return(res);
13614 return(0);
13617 #ifdef XPATH_STREAMING
13619 * xmlXPathRunStreamEval:
13620 * @ctxt: the XPath parser context with the compiled expression
13622 * Evaluate the Precompiled Streamable XPath expression in the given context.
13624 static int
13625 xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
13626 xmlXPathObjectPtr *resultSeq, int toBool)
13628 int max_depth, min_depth;
13629 int from_root;
13630 int ret, depth;
13631 int eval_all_nodes;
13632 xmlNodePtr cur = NULL, limit = NULL;
13633 xmlStreamCtxtPtr patstream = NULL;
13635 if ((ctxt == NULL) || (comp == NULL))
13636 return(-1);
13637 max_depth = xmlPatternMaxDepth(comp);
13638 if (max_depth == -1)
13639 return(-1);
13640 if (max_depth == -2)
13641 max_depth = 10000;
13642 min_depth = xmlPatternMinDepth(comp);
13643 if (min_depth == -1)
13644 return(-1);
13645 from_root = xmlPatternFromRoot(comp);
13646 if (from_root < 0)
13647 return(-1);
13648 #if 0
13649 printf("stream eval: depth %d from root %d\n", max_depth, from_root);
13650 #endif
13652 if (! toBool) {
13653 if (resultSeq == NULL)
13654 return(-1);
13655 *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
13656 if (*resultSeq == NULL)
13657 return(-1);
13661 * handle the special cases of "/" amd "." being matched
13663 if (min_depth == 0) {
13664 if (from_root) {
13665 /* Select "/" */
13666 if (toBool)
13667 return(1);
13668 /* TODO: Check memory error. */
13669 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
13670 (xmlNodePtr) ctxt->doc);
13671 } else {
13672 /* Select "self::node()" */
13673 if (toBool)
13674 return(1);
13675 /* TODO: Check memory error. */
13676 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
13679 if (max_depth == 0) {
13680 return(0);
13683 if (from_root) {
13684 cur = (xmlNodePtr)ctxt->doc;
13685 } else if (ctxt->node != NULL) {
13686 switch (ctxt->node->type) {
13687 case XML_ELEMENT_NODE:
13688 case XML_DOCUMENT_NODE:
13689 case XML_DOCUMENT_FRAG_NODE:
13690 case XML_HTML_DOCUMENT_NODE:
13691 cur = ctxt->node;
13692 break;
13693 case XML_ATTRIBUTE_NODE:
13694 case XML_TEXT_NODE:
13695 case XML_CDATA_SECTION_NODE:
13696 case XML_ENTITY_REF_NODE:
13697 case XML_ENTITY_NODE:
13698 case XML_PI_NODE:
13699 case XML_COMMENT_NODE:
13700 case XML_NOTATION_NODE:
13701 case XML_DTD_NODE:
13702 case XML_DOCUMENT_TYPE_NODE:
13703 case XML_ELEMENT_DECL:
13704 case XML_ATTRIBUTE_DECL:
13705 case XML_ENTITY_DECL:
13706 case XML_NAMESPACE_DECL:
13707 case XML_XINCLUDE_START:
13708 case XML_XINCLUDE_END:
13709 break;
13711 limit = cur;
13713 if (cur == NULL) {
13714 return(0);
13717 patstream = xmlPatternGetStreamCtxt(comp);
13718 if (patstream == NULL) {
13720 * QUESTION TODO: Is this an error?
13722 return(0);
13725 eval_all_nodes = xmlStreamWantsAnyNode(patstream);
13727 if (from_root) {
13728 ret = xmlStreamPush(patstream, NULL, NULL);
13729 if (ret < 0) {
13730 } else if (ret == 1) {
13731 if (toBool)
13732 goto return_1;
13733 /* TODO: Check memory error. */
13734 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
13737 depth = 0;
13738 goto scan_children;
13739 next_node:
13740 do {
13741 if (ctxt->opLimit != 0) {
13742 if (ctxt->opCount >= ctxt->opLimit) {
13743 xmlGenericError(xmlGenericErrorContext,
13744 "XPath operation limit exceeded\n");
13745 xmlFreeStreamCtxt(patstream);
13746 return(-1);
13748 ctxt->opCount++;
13751 switch (cur->type) {
13752 case XML_ELEMENT_NODE:
13753 case XML_TEXT_NODE:
13754 case XML_CDATA_SECTION_NODE:
13755 case XML_COMMENT_NODE:
13756 case XML_PI_NODE:
13757 if (cur->type == XML_ELEMENT_NODE) {
13758 ret = xmlStreamPush(patstream, cur->name,
13759 (cur->ns ? cur->ns->href : NULL));
13760 } else if (eval_all_nodes)
13761 ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
13762 else
13763 break;
13765 if (ret < 0) {
13766 /* NOP. */
13767 } else if (ret == 1) {
13768 if (toBool)
13769 goto return_1;
13770 if (xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur)
13771 < 0) {
13772 ctxt->lastError.domain = XML_FROM_XPATH;
13773 ctxt->lastError.code = XML_ERR_NO_MEMORY;
13776 if ((cur->children == NULL) || (depth >= max_depth)) {
13777 ret = xmlStreamPop(patstream);
13778 while (cur->next != NULL) {
13779 cur = cur->next;
13780 if ((cur->type != XML_ENTITY_DECL) &&
13781 (cur->type != XML_DTD_NODE))
13782 goto next_node;
13785 default:
13786 break;
13789 scan_children:
13790 if (cur->type == XML_NAMESPACE_DECL) break;
13791 if ((cur->children != NULL) && (depth < max_depth)) {
13793 * Do not descend on entities declarations
13795 if (cur->children->type != XML_ENTITY_DECL) {
13796 cur = cur->children;
13797 depth++;
13799 * Skip DTDs
13801 if (cur->type != XML_DTD_NODE)
13802 continue;
13806 if (cur == limit)
13807 break;
13809 while (cur->next != NULL) {
13810 cur = cur->next;
13811 if ((cur->type != XML_ENTITY_DECL) &&
13812 (cur->type != XML_DTD_NODE))
13813 goto next_node;
13816 do {
13817 cur = cur->parent;
13818 depth--;
13819 if ((cur == NULL) || (cur == limit) ||
13820 (cur->type == XML_DOCUMENT_NODE))
13821 goto done;
13822 if (cur->type == XML_ELEMENT_NODE) {
13823 ret = xmlStreamPop(patstream);
13824 } else if ((eval_all_nodes) &&
13825 ((cur->type == XML_TEXT_NODE) ||
13826 (cur->type == XML_CDATA_SECTION_NODE) ||
13827 (cur->type == XML_COMMENT_NODE) ||
13828 (cur->type == XML_PI_NODE)))
13830 ret = xmlStreamPop(patstream);
13832 if (cur->next != NULL) {
13833 cur = cur->next;
13834 break;
13836 } while (cur != NULL);
13838 } while ((cur != NULL) && (depth >= 0));
13840 done:
13842 if (patstream)
13843 xmlFreeStreamCtxt(patstream);
13844 return(0);
13846 return_1:
13847 if (patstream)
13848 xmlFreeStreamCtxt(patstream);
13849 return(1);
13851 #endif /* XPATH_STREAMING */
13854 * xmlXPathRunEval:
13855 * @ctxt: the XPath parser context with the compiled expression
13856 * @toBool: evaluate to a boolean result
13858 * Evaluate the Precompiled XPath expression in the given context.
13860 static int
13861 xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
13863 xmlXPathCompExprPtr comp;
13864 int oldDepth;
13866 if ((ctxt == NULL) || (ctxt->comp == NULL))
13867 return(-1);
13869 if (ctxt->valueTab == NULL) {
13870 /* Allocate the value stack */
13871 ctxt->valueTab = (xmlXPathObjectPtr *)
13872 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
13873 if (ctxt->valueTab == NULL) {
13874 xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
13875 return(-1);
13877 ctxt->valueNr = 0;
13878 ctxt->valueMax = 10;
13879 ctxt->value = NULL;
13881 #ifdef XPATH_STREAMING
13882 if (ctxt->comp->stream) {
13883 int res;
13885 if (toBool) {
13887 * Evaluation to boolean result.
13889 res = xmlXPathRunStreamEval(ctxt->context,
13890 ctxt->comp->stream, NULL, 1);
13891 if (res != -1)
13892 return(res);
13893 } else {
13894 xmlXPathObjectPtr resObj = NULL;
13897 * Evaluation to a sequence.
13899 res = xmlXPathRunStreamEval(ctxt->context,
13900 ctxt->comp->stream, &resObj, 0);
13902 if ((res != -1) && (resObj != NULL)) {
13903 valuePush(ctxt, resObj);
13904 return(0);
13906 if (resObj != NULL)
13907 xmlXPathReleaseObject(ctxt->context, resObj);
13910 * QUESTION TODO: This falls back to normal XPath evaluation
13911 * if res == -1. Is this intended?
13914 #endif
13915 comp = ctxt->comp;
13916 if (comp->last < 0) {
13917 xmlGenericError(xmlGenericErrorContext,
13918 "xmlXPathRunEval: last is less than zero\n");
13919 return(-1);
13921 oldDepth = ctxt->context->depth;
13922 if (toBool)
13923 return(xmlXPathCompOpEvalToBoolean(ctxt,
13924 &comp->steps[comp->last], 0));
13925 else
13926 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
13927 ctxt->context->depth = oldDepth;
13929 return(0);
13932 /************************************************************************
13934 * Public interfaces *
13936 ************************************************************************/
13939 * xmlXPathEvalPredicate:
13940 * @ctxt: the XPath context
13941 * @res: the Predicate Expression evaluation result
13943 * Evaluate a predicate result for the current node.
13944 * A PredicateExpr is evaluated by evaluating the Expr and converting
13945 * the result to a boolean. If the result is a number, the result will
13946 * be converted to true if the number is equal to the position of the
13947 * context node in the context node list (as returned by the position
13948 * function) and will be converted to false otherwise; if the result
13949 * is not a number, then the result will be converted as if by a call
13950 * to the boolean function.
13952 * Returns 1 if predicate is true, 0 otherwise
13955 xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
13956 if ((ctxt == NULL) || (res == NULL)) return(0);
13957 switch (res->type) {
13958 case XPATH_BOOLEAN:
13959 return(res->boolval);
13960 case XPATH_NUMBER:
13961 return(res->floatval == ctxt->proximityPosition);
13962 case XPATH_NODESET:
13963 case XPATH_XSLT_TREE:
13964 if (res->nodesetval == NULL)
13965 return(0);
13966 return(res->nodesetval->nodeNr != 0);
13967 case XPATH_STRING:
13968 return((res->stringval != NULL) &&
13969 (xmlStrlen(res->stringval) != 0));
13970 default:
13971 STRANGE
13973 return(0);
13977 * xmlXPathEvaluatePredicateResult:
13978 * @ctxt: the XPath Parser context
13979 * @res: the Predicate Expression evaluation result
13981 * Evaluate a predicate result for the current node.
13982 * A PredicateExpr is evaluated by evaluating the Expr and converting
13983 * the result to a boolean. If the result is a number, the result will
13984 * be converted to true if the number is equal to the position of the
13985 * context node in the context node list (as returned by the position
13986 * function) and will be converted to false otherwise; if the result
13987 * is not a number, then the result will be converted as if by a call
13988 * to the boolean function.
13990 * Returns 1 if predicate is true, 0 otherwise
13993 xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
13994 xmlXPathObjectPtr res) {
13995 if ((ctxt == NULL) || (res == NULL)) return(0);
13996 switch (res->type) {
13997 case XPATH_BOOLEAN:
13998 return(res->boolval);
13999 case XPATH_NUMBER:
14000 #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
14001 return((res->floatval == ctxt->context->proximityPosition) &&
14002 (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
14003 #else
14004 return(res->floatval == ctxt->context->proximityPosition);
14005 #endif
14006 case XPATH_NODESET:
14007 case XPATH_XSLT_TREE:
14008 if (res->nodesetval == NULL)
14009 return(0);
14010 return(res->nodesetval->nodeNr != 0);
14011 case XPATH_STRING:
14012 return((res->stringval != NULL) && (res->stringval[0] != 0));
14013 #ifdef LIBXML_XPTR_LOCS_ENABLED
14014 case XPATH_LOCATIONSET:{
14015 xmlLocationSetPtr ptr = res->user;
14016 if (ptr == NULL)
14017 return(0);
14018 return (ptr->locNr != 0);
14020 #endif
14021 default:
14022 STRANGE
14024 return(0);
14027 #ifdef XPATH_STREAMING
14029 * xmlXPathTryStreamCompile:
14030 * @ctxt: an XPath context
14031 * @str: the XPath expression
14033 * Try to compile the XPath expression as a streamable subset.
14035 * Returns the compiled expression or NULL if failed to compile.
14037 static xmlXPathCompExprPtr
14038 xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14040 * Optimization: use streaming patterns when the XPath expression can
14041 * be compiled to a stream lookup
14043 xmlPatternPtr stream;
14044 xmlXPathCompExprPtr comp;
14045 xmlDictPtr dict = NULL;
14046 const xmlChar **namespaces = NULL;
14047 xmlNsPtr ns;
14048 int i, j;
14050 if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
14051 (!xmlStrchr(str, '@'))) {
14052 const xmlChar *tmp;
14055 * We don't try to handle expressions using the verbose axis
14056 * specifiers ("::"), just the simplified form at this point.
14057 * Additionally, if there is no list of namespaces available and
14058 * there's a ":" in the expression, indicating a prefixed QName,
14059 * then we won't try to compile either. xmlPatterncompile() needs
14060 * to have a list of namespaces at compilation time in order to
14061 * compile prefixed name tests.
14063 tmp = xmlStrchr(str, ':');
14064 if ((tmp != NULL) &&
14065 ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
14066 return(NULL);
14068 if (ctxt != NULL) {
14069 dict = ctxt->dict;
14070 if (ctxt->nsNr > 0) {
14071 namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
14072 if (namespaces == NULL) {
14073 xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
14074 return(NULL);
14076 for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
14077 ns = ctxt->namespaces[j];
14078 namespaces[i++] = ns->href;
14079 namespaces[i++] = ns->prefix;
14081 namespaces[i++] = NULL;
14082 namespaces[i] = NULL;
14086 stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH, namespaces);
14087 if (namespaces != NULL) {
14088 xmlFree((xmlChar **)namespaces);
14090 if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
14091 comp = xmlXPathNewCompExpr();
14092 if (comp == NULL) {
14093 xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
14094 xmlFreePattern(stream);
14095 return(NULL);
14097 comp->stream = stream;
14098 comp->dict = dict;
14099 if (comp->dict)
14100 xmlDictReference(comp->dict);
14101 return(comp);
14103 xmlFreePattern(stream);
14105 return(NULL);
14107 #endif /* XPATH_STREAMING */
14109 static void
14110 xmlXPathOptimizeExpression(xmlXPathParserContextPtr pctxt,
14111 xmlXPathStepOpPtr op)
14113 xmlXPathCompExprPtr comp = pctxt->comp;
14114 xmlXPathContextPtr ctxt;
14117 * Try to rewrite "descendant-or-self::node()/foo" to an optimized
14118 * internal representation.
14121 if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
14122 (op->ch1 != -1) &&
14123 (op->ch2 == -1 /* no predicate */))
14125 xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
14127 if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
14128 ((xmlXPathAxisVal) prevop->value ==
14129 AXIS_DESCENDANT_OR_SELF) &&
14130 (prevop->ch2 == -1) &&
14131 ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
14132 ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE))
14135 * This is a "descendant-or-self::node()" without predicates.
14136 * Try to eliminate it.
14139 switch ((xmlXPathAxisVal) op->value) {
14140 case AXIS_CHILD:
14141 case AXIS_DESCENDANT:
14143 * Convert "descendant-or-self::node()/child::" or
14144 * "descendant-or-self::node()/descendant::" to
14145 * "descendant::"
14147 op->ch1 = prevop->ch1;
14148 op->value = AXIS_DESCENDANT;
14149 break;
14150 case AXIS_SELF:
14151 case AXIS_DESCENDANT_OR_SELF:
14153 * Convert "descendant-or-self::node()/self::" or
14154 * "descendant-or-self::node()/descendant-or-self::" to
14155 * to "descendant-or-self::"
14157 op->ch1 = prevop->ch1;
14158 op->value = AXIS_DESCENDANT_OR_SELF;
14159 break;
14160 default:
14161 break;
14166 /* OP_VALUE has invalid ch1. */
14167 if (op->op == XPATH_OP_VALUE)
14168 return;
14170 /* Recurse */
14171 ctxt = pctxt->context;
14172 if (ctxt != NULL) {
14173 if (ctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
14174 return;
14175 ctxt->depth += 1;
14177 if (op->ch1 != -1)
14178 xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch1]);
14179 if (op->ch2 != -1)
14180 xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch2]);
14181 if (ctxt != NULL)
14182 ctxt->depth -= 1;
14186 * xmlXPathCtxtCompile:
14187 * @ctxt: an XPath context
14188 * @str: the XPath expression
14190 * Compile an XPath expression
14192 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14193 * the caller has to free the object.
14195 xmlXPathCompExprPtr
14196 xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14197 xmlXPathParserContextPtr pctxt;
14198 xmlXPathCompExprPtr comp;
14199 int oldDepth = 0;
14201 #ifdef XPATH_STREAMING
14202 comp = xmlXPathTryStreamCompile(ctxt, str);
14203 if (comp != NULL)
14204 return(comp);
14205 #endif
14207 xmlInitParser();
14209 pctxt = xmlXPathNewParserContext(str, ctxt);
14210 if (pctxt == NULL)
14211 return NULL;
14212 if (ctxt != NULL)
14213 oldDepth = ctxt->depth;
14214 xmlXPathCompileExpr(pctxt, 1);
14215 if (ctxt != NULL)
14216 ctxt->depth = oldDepth;
14218 if( pctxt->error != XPATH_EXPRESSION_OK )
14220 xmlXPathFreeParserContext(pctxt);
14221 return(NULL);
14224 if (*pctxt->cur != 0) {
14226 * aleksey: in some cases this line prints *second* error message
14227 * (see bug #78858) and probably this should be fixed.
14228 * However, we are not sure that all error messages are printed
14229 * out in other places. It's not critical so we leave it as-is for now
14231 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14232 comp = NULL;
14233 } else {
14234 comp = pctxt->comp;
14235 if ((comp->nbStep > 1) && (comp->last >= 0)) {
14236 if (ctxt != NULL)
14237 oldDepth = ctxt->depth;
14238 xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
14239 if (ctxt != NULL)
14240 ctxt->depth = oldDepth;
14242 pctxt->comp = NULL;
14244 xmlXPathFreeParserContext(pctxt);
14246 if (comp != NULL) {
14247 comp->expr = xmlStrdup(str);
14248 #ifdef DEBUG_EVAL_COUNTS
14249 comp->string = xmlStrdup(str);
14250 comp->nb = 0;
14251 #endif
14253 return(comp);
14257 * xmlXPathCompile:
14258 * @str: the XPath expression
14260 * Compile an XPath expression
14262 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14263 * the caller has to free the object.
14265 xmlXPathCompExprPtr
14266 xmlXPathCompile(const xmlChar *str) {
14267 return(xmlXPathCtxtCompile(NULL, str));
14271 * xmlXPathCompiledEvalInternal:
14272 * @comp: the compiled XPath expression
14273 * @ctxt: the XPath context
14274 * @resObj: the resulting XPath object or NULL
14275 * @toBool: 1 if only a boolean result is requested
14277 * Evaluate the Precompiled XPath expression in the given context.
14278 * The caller has to free @resObj.
14280 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14281 * the caller has to free the object.
14283 static int
14284 xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
14285 xmlXPathContextPtr ctxt,
14286 xmlXPathObjectPtr *resObjPtr,
14287 int toBool)
14289 xmlXPathParserContextPtr pctxt;
14290 xmlXPathObjectPtr resObj;
14291 #ifndef LIBXML_THREAD_ENABLED
14292 static int reentance = 0;
14293 #endif
14294 int res;
14296 CHECK_CTXT_NEG(ctxt)
14298 if (comp == NULL)
14299 return(-1);
14300 xmlInitParser();
14302 #ifndef LIBXML_THREAD_ENABLED
14303 reentance++;
14304 if (reentance > 1)
14305 xmlXPathDisableOptimizer = 1;
14306 #endif
14308 #ifdef DEBUG_EVAL_COUNTS
14309 comp->nb++;
14310 if ((comp->string != NULL) && (comp->nb > 100)) {
14311 fprintf(stderr, "100 x %s\n", comp->string);
14312 comp->nb = 0;
14314 #endif
14315 pctxt = xmlXPathCompParserContext(comp, ctxt);
14316 if (pctxt == NULL)
14317 return(-1);
14318 res = xmlXPathRunEval(pctxt, toBool);
14320 if (pctxt->error != XPATH_EXPRESSION_OK) {
14321 resObj = NULL;
14322 } else {
14323 resObj = valuePop(pctxt);
14324 if (resObj == NULL) {
14325 if (!toBool)
14326 xmlGenericError(xmlGenericErrorContext,
14327 "xmlXPathCompiledEval: No result on the stack.\n");
14328 } else if (pctxt->valueNr > 0) {
14329 xmlGenericError(xmlGenericErrorContext,
14330 "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
14331 pctxt->valueNr);
14335 if (resObjPtr)
14336 *resObjPtr = resObj;
14337 else
14338 xmlXPathReleaseObject(ctxt, resObj);
14340 pctxt->comp = NULL;
14341 xmlXPathFreeParserContext(pctxt);
14342 #ifndef LIBXML_THREAD_ENABLED
14343 reentance--;
14344 #endif
14346 return(res);
14350 * xmlXPathCompiledEval:
14351 * @comp: the compiled XPath expression
14352 * @ctx: the XPath context
14354 * Evaluate the Precompiled XPath expression in the given context.
14356 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14357 * the caller has to free the object.
14359 xmlXPathObjectPtr
14360 xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
14362 xmlXPathObjectPtr res = NULL;
14364 xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
14365 return(res);
14369 * xmlXPathCompiledEvalToBoolean:
14370 * @comp: the compiled XPath expression
14371 * @ctxt: the XPath context
14373 * Applies the XPath boolean() function on the result of the given
14374 * compiled expression.
14376 * Returns 1 if the expression evaluated to true, 0 if to false and
14377 * -1 in API and internal errors.
14380 xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
14381 xmlXPathContextPtr ctxt)
14383 return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
14387 * xmlXPathEvalExpr:
14388 * @ctxt: the XPath Parser context
14390 * Parse and evaluate an XPath expression in the given context,
14391 * then push the result on the context stack
14393 void
14394 xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
14395 #ifdef XPATH_STREAMING
14396 xmlXPathCompExprPtr comp;
14397 #endif
14398 int oldDepth = 0;
14400 if (ctxt == NULL) return;
14402 #ifdef XPATH_STREAMING
14403 comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
14404 if (comp != NULL) {
14405 if (ctxt->comp != NULL)
14406 xmlXPathFreeCompExpr(ctxt->comp);
14407 ctxt->comp = comp;
14408 } else
14409 #endif
14411 if (ctxt->context != NULL)
14412 oldDepth = ctxt->context->depth;
14413 xmlXPathCompileExpr(ctxt, 1);
14414 if (ctxt->context != NULL)
14415 ctxt->context->depth = oldDepth;
14416 CHECK_ERROR;
14418 /* Check for trailing characters. */
14419 if (*ctxt->cur != 0)
14420 XP_ERROR(XPATH_EXPR_ERROR);
14422 if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0)) {
14423 if (ctxt->context != NULL)
14424 oldDepth = ctxt->context->depth;
14425 xmlXPathOptimizeExpression(ctxt,
14426 &ctxt->comp->steps[ctxt->comp->last]);
14427 if (ctxt->context != NULL)
14428 ctxt->context->depth = oldDepth;
14432 xmlXPathRunEval(ctxt, 0);
14436 * xmlXPathEval:
14437 * @str: the XPath expression
14438 * @ctx: the XPath context
14440 * Evaluate the XPath Location Path in the given context.
14442 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14443 * the caller has to free the object.
14445 xmlXPathObjectPtr
14446 xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
14447 xmlXPathParserContextPtr ctxt;
14448 xmlXPathObjectPtr res;
14450 CHECK_CTXT(ctx)
14452 xmlInitParser();
14454 ctxt = xmlXPathNewParserContext(str, ctx);
14455 if (ctxt == NULL)
14456 return NULL;
14457 xmlXPathEvalExpr(ctxt);
14459 if (ctxt->error != XPATH_EXPRESSION_OK) {
14460 res = NULL;
14461 } else {
14462 res = valuePop(ctxt);
14463 if (res == NULL) {
14464 xmlGenericError(xmlGenericErrorContext,
14465 "xmlXPathCompiledEval: No result on the stack.\n");
14466 } else if (ctxt->valueNr > 0) {
14467 xmlGenericError(xmlGenericErrorContext,
14468 "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
14469 ctxt->valueNr);
14473 xmlXPathFreeParserContext(ctxt);
14474 return(res);
14478 * xmlXPathSetContextNode:
14479 * @node: the node to to use as the context node
14480 * @ctx: the XPath context
14482 * Sets 'node' as the context node. The node must be in the same
14483 * document as that associated with the context.
14485 * Returns -1 in case of error or 0 if successful
14488 xmlXPathSetContextNode(xmlNodePtr node, xmlXPathContextPtr ctx) {
14489 if ((node == NULL) || (ctx == NULL))
14490 return(-1);
14492 if (node->doc == ctx->doc) {
14493 ctx->node = node;
14494 return(0);
14496 return(-1);
14500 * xmlXPathNodeEval:
14501 * @node: the node to to use as the context node
14502 * @str: the XPath expression
14503 * @ctx: the XPath context
14505 * Evaluate the XPath Location Path in the given context. The node 'node'
14506 * is set as the context node. The context node is not restored.
14508 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14509 * the caller has to free the object.
14511 xmlXPathObjectPtr
14512 xmlXPathNodeEval(xmlNodePtr node, const xmlChar *str, xmlXPathContextPtr ctx) {
14513 if (str == NULL)
14514 return(NULL);
14515 if (xmlXPathSetContextNode(node, ctx) < 0)
14516 return(NULL);
14517 return(xmlXPathEval(str, ctx));
14521 * xmlXPathEvalExpression:
14522 * @str: the XPath expression
14523 * @ctxt: the XPath context
14525 * Alias for xmlXPathEval().
14527 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14528 * the caller has to free the object.
14530 xmlXPathObjectPtr
14531 xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
14532 return(xmlXPathEval(str, ctxt));
14535 /************************************************************************
14537 * Extra functions not pertaining to the XPath spec *
14539 ************************************************************************/
14541 * xmlXPathEscapeUriFunction:
14542 * @ctxt: the XPath Parser context
14543 * @nargs: the number of arguments
14545 * Implement the escape-uri() XPath function
14546 * string escape-uri(string $str, bool $escape-reserved)
14548 * This function applies the URI escaping rules defined in section 2 of [RFC
14549 * 2396] to the string supplied as $uri-part, which typically represents all
14550 * or part of a URI. The effect of the function is to replace any special
14551 * character in the string by an escape sequence of the form %xx%yy...,
14552 * where xxyy... is the hexadecimal representation of the octets used to
14553 * represent the character in UTF-8.
14555 * The set of characters that are escaped depends on the setting of the
14556 * boolean argument $escape-reserved.
14558 * If $escape-reserved is true, all characters are escaped other than lower
14559 * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
14560 * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
14561 * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
14562 * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
14563 * A-F).
14565 * If $escape-reserved is false, the behavior differs in that characters
14566 * referred to in [RFC 2396] as reserved characters are not escaped. These
14567 * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
14569 * [RFC 2396] does not define whether escaped URIs should use lower case or
14570 * upper case for hexadecimal digits. To ensure that escaped URIs can be
14571 * compared using string comparison functions, this function must always use
14572 * the upper-case letters A-F.
14574 * Generally, $escape-reserved should be set to true when escaping a string
14575 * that is to form a single part of a URI, and to false when escaping an
14576 * entire URI or URI reference.
14578 * In the case of non-ascii characters, the string is encoded according to
14579 * utf-8 and then converted according to RFC 2396.
14581 * Examples
14582 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
14583 * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
14584 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
14585 * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
14588 static void
14589 xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
14590 xmlXPathObjectPtr str;
14591 int escape_reserved;
14592 xmlBufPtr target;
14593 xmlChar *cptr;
14594 xmlChar escape[4];
14596 CHECK_ARITY(2);
14598 escape_reserved = xmlXPathPopBoolean(ctxt);
14600 CAST_TO_STRING;
14601 str = valuePop(ctxt);
14603 target = xmlBufCreate();
14605 escape[0] = '%';
14606 escape[3] = 0;
14608 if (target) {
14609 for (cptr = str->stringval; *cptr; cptr++) {
14610 if ((*cptr >= 'A' && *cptr <= 'Z') ||
14611 (*cptr >= 'a' && *cptr <= 'z') ||
14612 (*cptr >= '0' && *cptr <= '9') ||
14613 *cptr == '-' || *cptr == '_' || *cptr == '.' ||
14614 *cptr == '!' || *cptr == '~' || *cptr == '*' ||
14615 *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
14616 (*cptr == '%' &&
14617 ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
14618 (cptr[1] >= 'a' && cptr[1] <= 'f') ||
14619 (cptr[1] >= '0' && cptr[1] <= '9')) &&
14620 ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
14621 (cptr[2] >= 'a' && cptr[2] <= 'f') ||
14622 (cptr[2] >= '0' && cptr[2] <= '9'))) ||
14623 (!escape_reserved &&
14624 (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
14625 *cptr == ':' || *cptr == '@' || *cptr == '&' ||
14626 *cptr == '=' || *cptr == '+' || *cptr == '$' ||
14627 *cptr == ','))) {
14628 xmlBufAdd(target, cptr, 1);
14629 } else {
14630 if ((*cptr >> 4) < 10)
14631 escape[1] = '0' + (*cptr >> 4);
14632 else
14633 escape[1] = 'A' - 10 + (*cptr >> 4);
14634 if ((*cptr & 0xF) < 10)
14635 escape[2] = '0' + (*cptr & 0xF);
14636 else
14637 escape[2] = 'A' - 10 + (*cptr & 0xF);
14639 xmlBufAdd(target, &escape[0], 3);
14643 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
14644 xmlBufContent(target)));
14645 xmlBufFree(target);
14646 xmlXPathReleaseObject(ctxt->context, str);
14650 * xmlXPathRegisterAllFunctions:
14651 * @ctxt: the XPath context
14653 * Registers all default XPath functions in this context
14655 void
14656 xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
14658 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
14659 xmlXPathBooleanFunction);
14660 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
14661 xmlXPathCeilingFunction);
14662 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
14663 xmlXPathCountFunction);
14664 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
14665 xmlXPathConcatFunction);
14666 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
14667 xmlXPathContainsFunction);
14668 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
14669 xmlXPathIdFunction);
14670 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
14671 xmlXPathFalseFunction);
14672 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
14673 xmlXPathFloorFunction);
14674 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
14675 xmlXPathLastFunction);
14676 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
14677 xmlXPathLangFunction);
14678 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
14679 xmlXPathLocalNameFunction);
14680 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
14681 xmlXPathNotFunction);
14682 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
14683 xmlXPathNameFunction);
14684 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
14685 xmlXPathNamespaceURIFunction);
14686 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
14687 xmlXPathNormalizeFunction);
14688 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
14689 xmlXPathNumberFunction);
14690 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
14691 xmlXPathPositionFunction);
14692 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
14693 xmlXPathRoundFunction);
14694 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
14695 xmlXPathStringFunction);
14696 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
14697 xmlXPathStringLengthFunction);
14698 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
14699 xmlXPathStartsWithFunction);
14700 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
14701 xmlXPathSubstringFunction);
14702 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
14703 xmlXPathSubstringBeforeFunction);
14704 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
14705 xmlXPathSubstringAfterFunction);
14706 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
14707 xmlXPathSumFunction);
14708 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
14709 xmlXPathTrueFunction);
14710 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
14711 xmlXPathTranslateFunction);
14713 xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
14714 (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
14715 xmlXPathEscapeUriFunction);
14718 #endif /* LIBXML_XPATH_ENABLED */