dbghelp: Properly fail on PDB files generated by MSVC compiler version 14.31.
[wine.git] / libs / xml2 / xpath.c
blob886871749f9adfc9963be84681ae81385e8df712
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>
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32 #ifdef HAVE_MATH_H
33 #include <math.h>
34 #endif
35 #ifdef HAVE_FLOAT_H
36 #include <float.h>
37 #endif
38 #ifdef HAVE_CTYPE_H
39 #include <ctype.h>
40 #endif
41 #ifdef HAVE_SIGNAL_H
42 #include <signal.h>
43 #endif
45 #include <libxml/xmlmemory.h>
46 #include <libxml/tree.h>
47 #include <libxml/valid.h>
48 #include <libxml/xpath.h>
49 #include <libxml/xpathInternals.h>
50 #include <libxml/parserInternals.h>
51 #include <libxml/hash.h>
52 #ifdef LIBXML_XPTR_ENABLED
53 #include <libxml/xpointer.h>
54 #endif
55 #ifdef LIBXML_DEBUG_ENABLED
56 #include <libxml/debugXML.h>
57 #endif
58 #include <libxml/xmlerror.h>
59 #include <libxml/threads.h>
60 #include <libxml/globals.h>
61 #ifdef LIBXML_PATTERN_ENABLED
62 #include <libxml/pattern.h>
63 #endif
65 #include "buf.h"
67 #ifdef LIBXML_PATTERN_ENABLED
68 #define XPATH_STREAMING
69 #endif
71 #define TODO \
72 xmlGenericError(xmlGenericErrorContext, \
73 "Unimplemented block at %s:%d\n", \
74 __FILE__, __LINE__);
76 /**
77 * WITH_TIM_SORT:
79 * Use the Timsort algorithm provided in timsort.h to sort
80 * nodeset as this is a great improvement over the old Shell sort
81 * used in xmlXPathNodeSetSort()
83 #define WITH_TIM_SORT
86 * XP_OPTIMIZED_NON_ELEM_COMPARISON:
87 * If defined, this will use xmlXPathCmpNodesExt() instead of
88 * xmlXPathCmpNodes(). The new function is optimized comparison of
89 * non-element nodes; actually it will speed up comparison only if
90 * xmlXPathOrderDocElems() was called in order to index the elements of
91 * a tree in document order; Libxslt does such an indexing, thus it will
92 * benefit from this optimization.
94 #define XP_OPTIMIZED_NON_ELEM_COMPARISON
97 * XP_OPTIMIZED_FILTER_FIRST:
98 * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
99 * in a way, that it stop evaluation at the first node.
101 #define XP_OPTIMIZED_FILTER_FIRST
104 * XP_DEBUG_OBJ_USAGE:
105 * Internal flag to enable tracking of how much XPath objects have been
106 * created.
108 /* #define XP_DEBUG_OBJ_USAGE */
111 * XPATH_MAX_STEPS:
112 * when compiling an XPath expression we arbitrary limit the maximum
113 * number of step operation in the compiled expression. 1000000 is
114 * an insanely large value which should never be reached under normal
115 * circumstances
117 #define XPATH_MAX_STEPS 1000000
120 * XPATH_MAX_STACK_DEPTH:
121 * when evaluating an XPath expression we arbitrary limit the maximum
122 * number of object allowed to be pushed on the stack. 1000000 is
123 * an insanely large value which should never be reached under normal
124 * circumstances
126 #define XPATH_MAX_STACK_DEPTH 1000000
129 * XPATH_MAX_NODESET_LENGTH:
130 * when evaluating an XPath expression nodesets are created and we
131 * arbitrary limit the maximum length of those node set. 10000000 is
132 * an insanely large value which should never be reached under normal
133 * circumstances, one would first need to construct an in memory tree
134 * with more than 10 millions nodes.
136 #define XPATH_MAX_NODESET_LENGTH 10000000
139 * XPATH_MAX_RECRUSION_DEPTH:
140 * Maximum amount of nested functions calls when parsing or evaluating
141 * expressions
143 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
144 #define XPATH_MAX_RECURSION_DEPTH 500
145 #else
146 #define XPATH_MAX_RECURSION_DEPTH 5000
147 #endif
150 * TODO:
151 * There are a few spots where some tests are done which depend upon ascii
152 * data. These should be enhanced for full UTF8 support (see particularly
153 * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
156 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
158 * xmlXPathCmpNodesExt:
159 * @node1: the first node
160 * @node2: the second node
162 * Compare two nodes w.r.t document order.
163 * This one is optimized for handling of non-element nodes.
165 * Returns -2 in case of error 1 if first point < second point, 0 if
166 * it's the same node, -1 otherwise
168 static int
169 xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
170 int depth1, depth2;
171 int misc = 0, precedence1 = 0, precedence2 = 0;
172 xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
173 xmlNodePtr cur, root;
174 ptrdiff_t l1, l2;
176 if ((node1 == NULL) || (node2 == NULL))
177 return(-2);
179 if (node1 == node2)
180 return(0);
183 * a couple of optimizations which will avoid computations in most cases
185 switch (node1->type) {
186 case XML_ELEMENT_NODE:
187 if (node2->type == XML_ELEMENT_NODE) {
188 if ((0 > (ptrdiff_t) node1->content) &&
189 (0 > (ptrdiff_t) node2->content) &&
190 (node1->doc == node2->doc))
192 l1 = -((ptrdiff_t) node1->content);
193 l2 = -((ptrdiff_t) node2->content);
194 if (l1 < l2)
195 return(1);
196 if (l1 > l2)
197 return(-1);
198 } else
199 goto turtle_comparison;
201 break;
202 case XML_ATTRIBUTE_NODE:
203 precedence1 = 1; /* element is owner */
204 miscNode1 = node1;
205 node1 = node1->parent;
206 misc = 1;
207 break;
208 case XML_TEXT_NODE:
209 case XML_CDATA_SECTION_NODE:
210 case XML_COMMENT_NODE:
211 case XML_PI_NODE: {
212 miscNode1 = node1;
214 * Find nearest element node.
216 if (node1->prev != NULL) {
217 do {
218 node1 = node1->prev;
219 if (node1->type == XML_ELEMENT_NODE) {
220 precedence1 = 3; /* element in prev-sibl axis */
221 break;
223 if (node1->prev == NULL) {
224 precedence1 = 2; /* element is parent */
226 * URGENT TODO: Are there any cases, where the
227 * parent of such a node is not an element node?
229 node1 = node1->parent;
230 break;
232 } while (1);
233 } else {
234 precedence1 = 2; /* element is parent */
235 node1 = node1->parent;
237 if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
238 (0 <= (ptrdiff_t) node1->content)) {
240 * Fallback for whatever case.
242 node1 = miscNode1;
243 precedence1 = 0;
244 } else
245 misc = 1;
247 break;
248 case XML_NAMESPACE_DECL:
250 * TODO: why do we return 1 for namespace nodes?
252 return(1);
253 default:
254 break;
256 switch (node2->type) {
257 case XML_ELEMENT_NODE:
258 break;
259 case XML_ATTRIBUTE_NODE:
260 precedence2 = 1; /* element is owner */
261 miscNode2 = node2;
262 node2 = node2->parent;
263 misc = 1;
264 break;
265 case XML_TEXT_NODE:
266 case XML_CDATA_SECTION_NODE:
267 case XML_COMMENT_NODE:
268 case XML_PI_NODE: {
269 miscNode2 = node2;
270 if (node2->prev != NULL) {
271 do {
272 node2 = node2->prev;
273 if (node2->type == XML_ELEMENT_NODE) {
274 precedence2 = 3; /* element in prev-sibl axis */
275 break;
277 if (node2->prev == NULL) {
278 precedence2 = 2; /* element is parent */
279 node2 = node2->parent;
280 break;
282 } while (1);
283 } else {
284 precedence2 = 2; /* element is parent */
285 node2 = node2->parent;
287 if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
288 (0 <= (ptrdiff_t) node2->content))
290 node2 = miscNode2;
291 precedence2 = 0;
292 } else
293 misc = 1;
295 break;
296 case XML_NAMESPACE_DECL:
297 return(1);
298 default:
299 break;
301 if (misc) {
302 if (node1 == node2) {
303 if (precedence1 == precedence2) {
305 * The ugly case; but normally there aren't many
306 * adjacent non-element nodes around.
308 cur = miscNode2->prev;
309 while (cur != NULL) {
310 if (cur == miscNode1)
311 return(1);
312 if (cur->type == XML_ELEMENT_NODE)
313 return(-1);
314 cur = cur->prev;
316 return (-1);
317 } else {
319 * Evaluate based on higher precedence wrt to the element.
320 * TODO: This assumes attributes are sorted before content.
321 * Is this 100% correct?
323 if (precedence1 < precedence2)
324 return(1);
325 else
326 return(-1);
330 * Special case: One of the helper-elements is contained by the other.
331 * <foo>
332 * <node2>
333 * <node1>Text-1(precedence1 == 2)</node1>
334 * </node2>
335 * Text-6(precedence2 == 3)
336 * </foo>
338 if ((precedence2 == 3) && (precedence1 > 1)) {
339 cur = node1->parent;
340 while (cur) {
341 if (cur == node2)
342 return(1);
343 cur = cur->parent;
346 if ((precedence1 == 3) && (precedence2 > 1)) {
347 cur = node2->parent;
348 while (cur) {
349 if (cur == node1)
350 return(-1);
351 cur = cur->parent;
357 * Speedup using document order if available.
359 if ((node1->type == XML_ELEMENT_NODE) &&
360 (node2->type == XML_ELEMENT_NODE) &&
361 (0 > (ptrdiff_t) node1->content) &&
362 (0 > (ptrdiff_t) node2->content) &&
363 (node1->doc == node2->doc)) {
365 l1 = -((ptrdiff_t) node1->content);
366 l2 = -((ptrdiff_t) node2->content);
367 if (l1 < l2)
368 return(1);
369 if (l1 > l2)
370 return(-1);
373 turtle_comparison:
375 if (node1 == node2->prev)
376 return(1);
377 if (node1 == node2->next)
378 return(-1);
380 * compute depth to root
382 for (depth2 = 0, cur = node2; cur->parent != NULL; cur = cur->parent) {
383 if (cur->parent == node1)
384 return(1);
385 depth2++;
387 root = cur;
388 for (depth1 = 0, cur = node1; cur->parent != NULL; cur = cur->parent) {
389 if (cur->parent == node2)
390 return(-1);
391 depth1++;
394 * Distinct document (or distinct entities :-( ) case.
396 if (root != cur) {
397 return(-2);
400 * get the nearest common ancestor.
402 while (depth1 > depth2) {
403 depth1--;
404 node1 = node1->parent;
406 while (depth2 > depth1) {
407 depth2--;
408 node2 = node2->parent;
410 while (node1->parent != node2->parent) {
411 node1 = node1->parent;
412 node2 = node2->parent;
413 /* should not happen but just in case ... */
414 if ((node1 == NULL) || (node2 == NULL))
415 return(-2);
418 * Find who's first.
420 if (node1 == node2->prev)
421 return(1);
422 if (node1 == node2->next)
423 return(-1);
425 * Speedup using document order if available.
427 if ((node1->type == XML_ELEMENT_NODE) &&
428 (node2->type == XML_ELEMENT_NODE) &&
429 (0 > (ptrdiff_t) node1->content) &&
430 (0 > (ptrdiff_t) node2->content) &&
431 (node1->doc == node2->doc)) {
433 l1 = -((ptrdiff_t) node1->content);
434 l2 = -((ptrdiff_t) node2->content);
435 if (l1 < l2)
436 return(1);
437 if (l1 > l2)
438 return(-1);
441 for (cur = node1->next;cur != NULL;cur = cur->next)
442 if (cur == node2)
443 return(1);
444 return(-1); /* assume there is no sibling list corruption */
446 #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
449 * Wrapper for the Timsort algorithm from timsort.h
451 #ifdef WITH_TIM_SORT
452 #define SORT_NAME libxml_domnode
453 #define SORT_TYPE xmlNodePtr
455 * wrap_cmp:
456 * @x: a node
457 * @y: another node
459 * Comparison function for the Timsort implementation
461 * Returns -2 in case of error -1 if first point < second point, 0 if
462 * it's the same node, +1 otherwise
464 static
465 int wrap_cmp( xmlNodePtr x, xmlNodePtr y );
466 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
467 static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
469 int res = xmlXPathCmpNodesExt(x, y);
470 return res == -2 ? res : -res;
472 #else
473 static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
475 int res = xmlXPathCmpNodes(x, y);
476 return res == -2 ? res : -res;
478 #endif
479 #define SORT_CMP(x, y) (wrap_cmp(x, y))
480 #include "timsort.h"
481 #endif /* WITH_TIM_SORT */
483 #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
485 /************************************************************************
487 * Floating point stuff *
489 ************************************************************************/
491 double xmlXPathNAN;
492 double xmlXPathPINF;
493 double xmlXPathNINF;
496 * xmlXPathInit:
498 * Initialize the XPath environment
500 void
501 xmlXPathInit(void) {
502 /* Use MSVC definitions */
503 xmlXPathNAN = NAN;
504 xmlXPathPINF = INFINITY;
505 xmlXPathNINF = -INFINITY;
509 * xmlXPathIsNaN:
510 * @val: a double value
512 * Returns 1 if the value is a NaN, 0 otherwise
515 xmlXPathIsNaN(double val) {
516 #ifdef isnan
517 return isnan(val);
518 #else
519 return !(val == val);
520 #endif
524 * xmlXPathIsInf:
525 * @val: a double value
527 * Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise
530 xmlXPathIsInf(double val) {
531 #ifdef isinf
532 return isinf(val) ? (val > 0 ? 1 : -1) : 0;
533 #else
534 if (val >= xmlXPathPINF)
535 return 1;
536 if (val <= -xmlXPathPINF)
537 return -1;
538 return 0;
539 #endif
542 #endif /* SCHEMAS or XPATH */
544 #ifdef LIBXML_XPATH_ENABLED
547 * TODO: when compatibility allows remove all "fake node libxslt" strings
548 * the test should just be name[0] = ' '
550 #ifdef DEBUG_XPATH_EXPRESSION
551 #define DEBUG_STEP
552 #define DEBUG_EXPR
553 #define DEBUG_EVAL_COUNTS
554 #endif
556 static xmlNs xmlXPathXMLNamespaceStruct = {
557 NULL,
558 XML_NAMESPACE_DECL,
559 XML_XML_NAMESPACE,
560 BAD_CAST "xml",
561 NULL,
562 NULL
564 static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
565 #ifndef LIBXML_THREAD_ENABLED
567 * Optimizer is disabled only when threaded apps are detected while
568 * the library ain't compiled for thread safety.
570 static int xmlXPathDisableOptimizer = 0;
571 #endif
573 /************************************************************************
575 * Error handling routines *
577 ************************************************************************/
580 * XP_ERRORNULL:
581 * @X: the error code
583 * Macro to raise an XPath error and return NULL.
585 #define XP_ERRORNULL(X) \
586 { xmlXPathErr(ctxt, X); return(NULL); }
589 * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
591 static const char *xmlXPathErrorMessages[] = {
592 "Ok\n",
593 "Number encoding\n",
594 "Unfinished literal\n",
595 "Start of literal\n",
596 "Expected $ for variable reference\n",
597 "Undefined variable\n",
598 "Invalid predicate\n",
599 "Invalid expression\n",
600 "Missing closing curly brace\n",
601 "Unregistered function\n",
602 "Invalid operand\n",
603 "Invalid type\n",
604 "Invalid number of arguments\n",
605 "Invalid context size\n",
606 "Invalid context position\n",
607 "Memory allocation error\n",
608 "Syntax error\n",
609 "Resource error\n",
610 "Sub resource error\n",
611 "Undefined namespace prefix\n",
612 "Encoding error\n",
613 "Char out of XML range\n",
614 "Invalid or incomplete context\n",
615 "Stack usage error\n",
616 "Forbidden variable\n",
617 "Operation limit exceeded\n",
618 "Recursion limit exceeded\n",
619 "?? Unknown error ??\n" /* Must be last in the list! */
621 #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
622 sizeof(xmlXPathErrorMessages[0])) - 1)
624 * xmlXPathErrMemory:
625 * @ctxt: an XPath context
626 * @extra: extra information
628 * Handle a redefinition of attribute error
630 static void
631 xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
633 if (ctxt != NULL) {
634 xmlResetError(&ctxt->lastError);
635 if (extra) {
636 xmlChar buf[200];
638 xmlStrPrintf(buf, 200,
639 "Memory allocation failed : %s\n",
640 extra);
641 ctxt->lastError.message = (char *) xmlStrdup(buf);
642 } else {
643 ctxt->lastError.message = (char *)
644 xmlStrdup(BAD_CAST "Memory allocation failed\n");
646 ctxt->lastError.domain = XML_FROM_XPATH;
647 ctxt->lastError.code = XML_ERR_NO_MEMORY;
648 if (ctxt->error != NULL)
649 ctxt->error(ctxt->userData, &ctxt->lastError);
650 } else {
651 if (extra)
652 __xmlRaiseError(NULL, NULL, NULL,
653 NULL, NULL, XML_FROM_XPATH,
654 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
655 extra, NULL, NULL, 0, 0,
656 "Memory allocation failed : %s\n", extra);
657 else
658 __xmlRaiseError(NULL, NULL, NULL,
659 NULL, NULL, XML_FROM_XPATH,
660 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
661 NULL, NULL, NULL, 0, 0,
662 "Memory allocation failed\n");
667 * xmlXPathPErrMemory:
668 * @ctxt: an XPath parser context
669 * @extra: extra information
671 * Handle a redefinition of attribute error
673 static void
674 xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
676 if (ctxt == NULL)
677 xmlXPathErrMemory(NULL, extra);
678 else {
679 ctxt->error = XPATH_MEMORY_ERROR;
680 xmlXPathErrMemory(ctxt->context, extra);
685 * xmlXPathErr:
686 * @ctxt: a XPath parser context
687 * @error: the error code
689 * Handle an XPath error
691 void
692 xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
694 if ((error < 0) || (error > MAXERRNO))
695 error = MAXERRNO;
696 if (ctxt == NULL) {
697 __xmlRaiseError(NULL, NULL, NULL,
698 NULL, NULL, XML_FROM_XPATH,
699 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
700 XML_ERR_ERROR, NULL, 0,
701 NULL, NULL, NULL, 0, 0,
702 "%s", xmlXPathErrorMessages[error]);
703 return;
705 ctxt->error = error;
706 if (ctxt->context == NULL) {
707 __xmlRaiseError(NULL, NULL, NULL,
708 NULL, NULL, XML_FROM_XPATH,
709 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
710 XML_ERR_ERROR, NULL, 0,
711 (const char *) ctxt->base, NULL, NULL,
712 ctxt->cur - ctxt->base, 0,
713 "%s", xmlXPathErrorMessages[error]);
714 return;
717 /* cleanup current last error */
718 xmlResetError(&ctxt->context->lastError);
720 ctxt->context->lastError.domain = XML_FROM_XPATH;
721 ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
722 XPATH_EXPRESSION_OK;
723 ctxt->context->lastError.level = XML_ERR_ERROR;
724 ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
725 ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
726 ctxt->context->lastError.node = ctxt->context->debugNode;
727 if (ctxt->context->error != NULL) {
728 ctxt->context->error(ctxt->context->userData,
729 &ctxt->context->lastError);
730 } else {
731 __xmlRaiseError(NULL, NULL, NULL,
732 NULL, ctxt->context->debugNode, XML_FROM_XPATH,
733 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
734 XML_ERR_ERROR, NULL, 0,
735 (const char *) ctxt->base, NULL, NULL,
736 ctxt->cur - ctxt->base, 0,
737 "%s", xmlXPathErrorMessages[error]);
743 * xmlXPatherror:
744 * @ctxt: the XPath Parser context
745 * @file: the file name
746 * @line: the line number
747 * @no: the error number
749 * Formats an error message.
751 void
752 xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
753 int line ATTRIBUTE_UNUSED, int no) {
754 xmlXPathErr(ctxt, no);
758 * xmlXPathCheckOpLimit:
759 * @ctxt: the XPath Parser context
760 * @opCount: the number of operations to be added
762 * Adds opCount to the running total of operations and returns -1 if the
763 * operation limit is exceeded. Returns 0 otherwise.
765 static int
766 xmlXPathCheckOpLimit(xmlXPathParserContextPtr ctxt, unsigned long opCount) {
767 xmlXPathContextPtr xpctxt = ctxt->context;
769 if ((opCount > xpctxt->opLimit) ||
770 (xpctxt->opCount > xpctxt->opLimit - opCount)) {
771 xpctxt->opCount = xpctxt->opLimit;
772 xmlXPathErr(ctxt, XPATH_OP_LIMIT_EXCEEDED);
773 return(-1);
776 xpctxt->opCount += opCount;
777 return(0);
780 #define OP_LIMIT_EXCEEDED(ctxt, n) \
781 ((ctxt->context->opLimit != 0) && (xmlXPathCheckOpLimit(ctxt, n) < 0))
783 /************************************************************************
785 * Utilities *
787 ************************************************************************/
790 * xsltPointerList:
792 * Pointer-list for various purposes.
794 typedef struct _xmlPointerList xmlPointerList;
795 typedef xmlPointerList *xmlPointerListPtr;
796 struct _xmlPointerList {
797 void **items;
798 int number;
799 int size;
802 * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
803 * and here, we should make the functions public.
805 static int
806 xmlPointerListAddSize(xmlPointerListPtr list,
807 void *item,
808 int initialSize)
810 if (list->items == NULL) {
811 if (initialSize <= 0)
812 initialSize = 1;
813 list->items = (void **) xmlMalloc(initialSize * sizeof(void *));
814 if (list->items == NULL) {
815 xmlXPathErrMemory(NULL,
816 "xmlPointerListCreate: allocating item\n");
817 return(-1);
819 list->number = 0;
820 list->size = initialSize;
821 } else if (list->size <= list->number) {
822 if (list->size > 50000000) {
823 xmlXPathErrMemory(NULL,
824 "xmlPointerListAddSize: re-allocating item\n");
825 return(-1);
827 list->size *= 2;
828 list->items = (void **) xmlRealloc(list->items,
829 list->size * sizeof(void *));
830 if (list->items == NULL) {
831 xmlXPathErrMemory(NULL,
832 "xmlPointerListAddSize: re-allocating item\n");
833 list->size = 0;
834 return(-1);
837 list->items[list->number++] = item;
838 return(0);
842 * xsltPointerListCreate:
844 * Creates an xsltPointerList structure.
846 * Returns a xsltPointerList structure or NULL in case of an error.
848 static xmlPointerListPtr
849 xmlPointerListCreate(int initialSize)
851 xmlPointerListPtr ret;
853 ret = xmlMalloc(sizeof(xmlPointerList));
854 if (ret == NULL) {
855 xmlXPathErrMemory(NULL,
856 "xmlPointerListCreate: allocating item\n");
857 return (NULL);
859 memset(ret, 0, sizeof(xmlPointerList));
860 if (initialSize > 0) {
861 xmlPointerListAddSize(ret, NULL, initialSize);
862 ret->number = 0;
864 return (ret);
868 * xsltPointerListFree:
870 * Frees the xsltPointerList structure. This does not free
871 * the content of the list.
873 static void
874 xmlPointerListFree(xmlPointerListPtr list)
876 if (list == NULL)
877 return;
878 if (list->items != NULL)
879 xmlFree(list->items);
880 xmlFree(list);
883 /************************************************************************
885 * Parser Types *
887 ************************************************************************/
890 * Types are private:
893 typedef enum {
894 XPATH_OP_END=0,
895 XPATH_OP_AND,
896 XPATH_OP_OR,
897 XPATH_OP_EQUAL,
898 XPATH_OP_CMP,
899 XPATH_OP_PLUS,
900 XPATH_OP_MULT,
901 XPATH_OP_UNION,
902 XPATH_OP_ROOT,
903 XPATH_OP_NODE,
904 XPATH_OP_COLLECT,
905 XPATH_OP_VALUE, /* 11 */
906 XPATH_OP_VARIABLE,
907 XPATH_OP_FUNCTION,
908 XPATH_OP_ARG,
909 XPATH_OP_PREDICATE,
910 XPATH_OP_FILTER, /* 16 */
911 XPATH_OP_SORT /* 17 */
912 #ifdef LIBXML_XPTR_ENABLED
913 ,XPATH_OP_RANGETO
914 #endif
915 } xmlXPathOp;
917 typedef enum {
918 AXIS_ANCESTOR = 1,
919 AXIS_ANCESTOR_OR_SELF,
920 AXIS_ATTRIBUTE,
921 AXIS_CHILD,
922 AXIS_DESCENDANT,
923 AXIS_DESCENDANT_OR_SELF,
924 AXIS_FOLLOWING,
925 AXIS_FOLLOWING_SIBLING,
926 AXIS_NAMESPACE,
927 AXIS_PARENT,
928 AXIS_PRECEDING,
929 AXIS_PRECEDING_SIBLING,
930 AXIS_SELF
931 } xmlXPathAxisVal;
933 typedef enum {
934 NODE_TEST_NONE = 0,
935 NODE_TEST_TYPE = 1,
936 NODE_TEST_PI = 2,
937 NODE_TEST_ALL = 3,
938 NODE_TEST_NS = 4,
939 NODE_TEST_NAME = 5
940 } xmlXPathTestVal;
942 typedef enum {
943 NODE_TYPE_NODE = 0,
944 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
945 NODE_TYPE_TEXT = XML_TEXT_NODE,
946 NODE_TYPE_PI = XML_PI_NODE
947 } xmlXPathTypeVal;
949 typedef struct _xmlXPathStepOp xmlXPathStepOp;
950 typedef xmlXPathStepOp *xmlXPathStepOpPtr;
951 struct _xmlXPathStepOp {
952 xmlXPathOp op; /* The identifier of the operation */
953 int ch1; /* First child */
954 int ch2; /* Second child */
955 int value;
956 int value2;
957 int value3;
958 void *value4;
959 void *value5;
960 xmlXPathFunction cache;
961 void *cacheURI;
964 struct _xmlXPathCompExpr {
965 int nbStep; /* Number of steps in this expression */
966 int maxStep; /* Maximum number of steps allocated */
967 xmlXPathStepOp *steps; /* ops for computation of this expression */
968 int last; /* index of last step in expression */
969 xmlChar *expr; /* the expression being computed */
970 xmlDictPtr dict; /* the dictionary to use if any */
971 #ifdef DEBUG_EVAL_COUNTS
972 int nb;
973 xmlChar *string;
974 #endif
975 #ifdef XPATH_STREAMING
976 xmlPatternPtr stream;
977 #endif
980 /************************************************************************
982 * Forward declarations *
984 ************************************************************************/
985 static void
986 xmlXPathFreeValueTree(xmlNodeSetPtr obj);
987 static void
988 xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
989 static int
990 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
991 xmlXPathStepOpPtr op, xmlNodePtr *first);
992 static int
993 xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
994 xmlXPathStepOpPtr op,
995 int isPredicate);
996 static void
997 xmlXPathFreeObjectEntry(void *obj, const xmlChar *name);
999 /************************************************************************
1001 * Parser Type functions *
1003 ************************************************************************/
1006 * xmlXPathNewCompExpr:
1008 * Create a new Xpath component
1010 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
1012 static xmlXPathCompExprPtr
1013 xmlXPathNewCompExpr(void) {
1014 xmlXPathCompExprPtr cur;
1016 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
1017 if (cur == NULL) {
1018 xmlXPathErrMemory(NULL, "allocating component\n");
1019 return(NULL);
1021 memset(cur, 0, sizeof(xmlXPathCompExpr));
1022 cur->maxStep = 10;
1023 cur->nbStep = 0;
1024 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
1025 sizeof(xmlXPathStepOp));
1026 if (cur->steps == NULL) {
1027 xmlXPathErrMemory(NULL, "allocating steps\n");
1028 xmlFree(cur);
1029 return(NULL);
1031 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
1032 cur->last = -1;
1033 #ifdef DEBUG_EVAL_COUNTS
1034 cur->nb = 0;
1035 #endif
1036 return(cur);
1040 * xmlXPathFreeCompExpr:
1041 * @comp: an XPATH comp
1043 * Free up the memory allocated by @comp
1045 void
1046 xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
1048 xmlXPathStepOpPtr op;
1049 int i;
1051 if (comp == NULL)
1052 return;
1053 if (comp->dict == NULL) {
1054 for (i = 0; i < comp->nbStep; i++) {
1055 op = &comp->steps[i];
1056 if (op->value4 != NULL) {
1057 if (op->op == XPATH_OP_VALUE)
1058 xmlXPathFreeObject(op->value4);
1059 else
1060 xmlFree(op->value4);
1062 if (op->value5 != NULL)
1063 xmlFree(op->value5);
1065 } else {
1066 for (i = 0; i < comp->nbStep; i++) {
1067 op = &comp->steps[i];
1068 if (op->value4 != NULL) {
1069 if (op->op == XPATH_OP_VALUE)
1070 xmlXPathFreeObject(op->value4);
1073 xmlDictFree(comp->dict);
1075 if (comp->steps != NULL) {
1076 xmlFree(comp->steps);
1078 #ifdef DEBUG_EVAL_COUNTS
1079 if (comp->string != NULL) {
1080 xmlFree(comp->string);
1082 #endif
1083 #ifdef XPATH_STREAMING
1084 if (comp->stream != NULL) {
1085 xmlFreePatternList(comp->stream);
1087 #endif
1088 if (comp->expr != NULL) {
1089 xmlFree(comp->expr);
1092 xmlFree(comp);
1096 * xmlXPathCompExprAdd:
1097 * @comp: the compiled expression
1098 * @ch1: first child index
1099 * @ch2: second child index
1100 * @op: an op
1101 * @value: the first int value
1102 * @value2: the second int value
1103 * @value3: the third int value
1104 * @value4: the first string value
1105 * @value5: the second string value
1107 * Add a step to an XPath Compiled Expression
1109 * Returns -1 in case of failure, the index otherwise
1111 static int
1112 xmlXPathCompExprAdd(xmlXPathParserContextPtr ctxt, int ch1, int ch2,
1113 xmlXPathOp op, int value,
1114 int value2, int value3, void *value4, void *value5) {
1115 xmlXPathCompExprPtr comp = ctxt->comp;
1116 if (comp->nbStep >= comp->maxStep) {
1117 xmlXPathStepOp *real;
1119 if (comp->maxStep >= XPATH_MAX_STEPS) {
1120 xmlXPathPErrMemory(ctxt, "adding step\n");
1121 return(-1);
1123 comp->maxStep *= 2;
1124 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
1125 comp->maxStep * sizeof(xmlXPathStepOp));
1126 if (real == NULL) {
1127 comp->maxStep /= 2;
1128 xmlXPathPErrMemory(ctxt, "adding step\n");
1129 return(-1);
1131 comp->steps = real;
1133 comp->last = comp->nbStep;
1134 comp->steps[comp->nbStep].ch1 = ch1;
1135 comp->steps[comp->nbStep].ch2 = ch2;
1136 comp->steps[comp->nbStep].op = op;
1137 comp->steps[comp->nbStep].value = value;
1138 comp->steps[comp->nbStep].value2 = value2;
1139 comp->steps[comp->nbStep].value3 = value3;
1140 if ((comp->dict != NULL) &&
1141 ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
1142 (op == XPATH_OP_COLLECT))) {
1143 if (value4 != NULL) {
1144 comp->steps[comp->nbStep].value4 = (xmlChar *)
1145 (void *)xmlDictLookup(comp->dict, value4, -1);
1146 xmlFree(value4);
1147 } else
1148 comp->steps[comp->nbStep].value4 = NULL;
1149 if (value5 != NULL) {
1150 comp->steps[comp->nbStep].value5 = (xmlChar *)
1151 (void *)xmlDictLookup(comp->dict, value5, -1);
1152 xmlFree(value5);
1153 } else
1154 comp->steps[comp->nbStep].value5 = NULL;
1155 } else {
1156 comp->steps[comp->nbStep].value4 = value4;
1157 comp->steps[comp->nbStep].value5 = value5;
1159 comp->steps[comp->nbStep].cache = NULL;
1160 return(comp->nbStep++);
1164 * xmlXPathCompSwap:
1165 * @comp: the compiled expression
1166 * @op: operation index
1168 * Swaps 2 operations in the compiled expression
1170 static void
1171 xmlXPathCompSwap(xmlXPathStepOpPtr op) {
1172 int tmp;
1174 #ifndef LIBXML_THREAD_ENABLED
1176 * Since this manipulates possibly shared variables, this is
1177 * disabled if one detects that the library is used in a multithreaded
1178 * application
1180 if (xmlXPathDisableOptimizer)
1181 return;
1182 #endif
1184 tmp = op->ch1;
1185 op->ch1 = op->ch2;
1186 op->ch2 = tmp;
1189 #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
1190 xmlXPathCompExprAdd(ctxt, (op1), (op2), \
1191 (op), (val), (val2), (val3), (val4), (val5))
1192 #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
1193 xmlXPathCompExprAdd(ctxt, ctxt->comp->last, -1, \
1194 (op), (val), (val2), (val3), (val4), (val5))
1196 #define PUSH_LEAVE_EXPR(op, val, val2) \
1197 xmlXPathCompExprAdd(ctxt, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
1199 #define PUSH_UNARY_EXPR(op, ch, val, val2) \
1200 xmlXPathCompExprAdd(ctxt, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
1202 #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
1203 xmlXPathCompExprAdd(ctxt, (ch1), (ch2), (op), \
1204 (val), (val2), 0 ,NULL ,NULL)
1206 /************************************************************************
1208 * XPath object cache structures *
1210 ************************************************************************/
1212 /* #define XP_DEFAULT_CACHE_ON */
1214 #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
1216 typedef struct _xmlXPathContextCache xmlXPathContextCache;
1217 typedef xmlXPathContextCache *xmlXPathContextCachePtr;
1218 struct _xmlXPathContextCache {
1219 xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
1220 xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
1221 xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
1222 xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
1223 xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
1224 int maxNodeset;
1225 int maxString;
1226 int maxBoolean;
1227 int maxNumber;
1228 int maxMisc;
1229 #ifdef XP_DEBUG_OBJ_USAGE
1230 int dbgCachedAll;
1231 int dbgCachedNodeset;
1232 int dbgCachedString;
1233 int dbgCachedBool;
1234 int dbgCachedNumber;
1235 int dbgCachedPoint;
1236 int dbgCachedRange;
1237 int dbgCachedLocset;
1238 int dbgCachedUsers;
1239 int dbgCachedXSLTTree;
1240 int dbgCachedUndefined;
1243 int dbgReusedAll;
1244 int dbgReusedNodeset;
1245 int dbgReusedString;
1246 int dbgReusedBool;
1247 int dbgReusedNumber;
1248 int dbgReusedPoint;
1249 int dbgReusedRange;
1250 int dbgReusedLocset;
1251 int dbgReusedUsers;
1252 int dbgReusedXSLTTree;
1253 int dbgReusedUndefined;
1255 #endif
1258 /************************************************************************
1260 * Debugging related functions *
1262 ************************************************************************/
1264 #define STRANGE \
1265 xmlGenericError(xmlGenericErrorContext, \
1266 "Internal error at %s:%d\n", \
1267 __FILE__, __LINE__);
1269 #ifdef LIBXML_DEBUG_ENABLED
1270 static void
1271 xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
1272 int i;
1273 char shift[100];
1275 for (i = 0;((i < depth) && (i < 25));i++)
1276 shift[2 * i] = shift[2 * i + 1] = ' ';
1277 shift[2 * i] = shift[2 * i + 1] = 0;
1278 if (cur == NULL) {
1279 fprintf(output, "%s", shift);
1280 fprintf(output, "Node is NULL !\n");
1281 return;
1285 if ((cur->type == XML_DOCUMENT_NODE) ||
1286 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1287 fprintf(output, "%s", shift);
1288 fprintf(output, " /\n");
1289 } else if (cur->type == XML_ATTRIBUTE_NODE)
1290 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
1291 else
1292 xmlDebugDumpOneNode(output, cur, depth);
1294 static void
1295 xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
1296 xmlNodePtr tmp;
1297 int i;
1298 char shift[100];
1300 for (i = 0;((i < depth) && (i < 25));i++)
1301 shift[2 * i] = shift[2 * i + 1] = ' ';
1302 shift[2 * i] = shift[2 * i + 1] = 0;
1303 if (cur == NULL) {
1304 fprintf(output, "%s", shift);
1305 fprintf(output, "Node is NULL !\n");
1306 return;
1310 while (cur != NULL) {
1311 tmp = cur;
1312 cur = cur->next;
1313 xmlDebugDumpOneNode(output, tmp, depth);
1317 static void
1318 xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
1319 int i;
1320 char shift[100];
1322 for (i = 0;((i < depth) && (i < 25));i++)
1323 shift[2 * i] = shift[2 * i + 1] = ' ';
1324 shift[2 * i] = shift[2 * i + 1] = 0;
1326 if (cur == NULL) {
1327 fprintf(output, "%s", shift);
1328 fprintf(output, "NodeSet is NULL !\n");
1329 return;
1333 if (cur != NULL) {
1334 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
1335 for (i = 0;i < cur->nodeNr;i++) {
1336 fprintf(output, "%s", shift);
1337 fprintf(output, "%d", i + 1);
1338 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
1343 static void
1344 xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
1345 int i;
1346 char shift[100];
1348 for (i = 0;((i < depth) && (i < 25));i++)
1349 shift[2 * i] = shift[2 * i + 1] = ' ';
1350 shift[2 * i] = shift[2 * i + 1] = 0;
1352 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
1353 fprintf(output, "%s", shift);
1354 fprintf(output, "Value Tree is NULL !\n");
1355 return;
1359 fprintf(output, "%s", shift);
1360 fprintf(output, "%d", i + 1);
1361 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
1363 #if defined(LIBXML_XPTR_ENABLED)
1364 static void
1365 xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
1366 int i;
1367 char shift[100];
1369 for (i = 0;((i < depth) && (i < 25));i++)
1370 shift[2 * i] = shift[2 * i + 1] = ' ';
1371 shift[2 * i] = shift[2 * i + 1] = 0;
1373 if (cur == NULL) {
1374 fprintf(output, "%s", shift);
1375 fprintf(output, "LocationSet is NULL !\n");
1376 return;
1380 for (i = 0;i < cur->locNr;i++) {
1381 fprintf(output, "%s", shift);
1382 fprintf(output, "%d : ", i + 1);
1383 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
1386 #endif /* LIBXML_XPTR_ENABLED */
1389 * xmlXPathDebugDumpObject:
1390 * @output: the FILE * to dump the output
1391 * @cur: the object to inspect
1392 * @depth: indentation level
1394 * Dump the content of the object for debugging purposes
1396 void
1397 xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
1398 int i;
1399 char shift[100];
1401 if (output == NULL) return;
1403 for (i = 0;((i < depth) && (i < 25));i++)
1404 shift[2 * i] = shift[2 * i + 1] = ' ';
1405 shift[2 * i] = shift[2 * i + 1] = 0;
1408 fprintf(output, "%s", shift);
1410 if (cur == NULL) {
1411 fprintf(output, "Object is empty (NULL)\n");
1412 return;
1414 switch(cur->type) {
1415 case XPATH_UNDEFINED:
1416 fprintf(output, "Object is uninitialized\n");
1417 break;
1418 case XPATH_NODESET:
1419 fprintf(output, "Object is a Node Set :\n");
1420 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
1421 break;
1422 case XPATH_XSLT_TREE:
1423 fprintf(output, "Object is an XSLT value tree :\n");
1424 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
1425 break;
1426 case XPATH_BOOLEAN:
1427 fprintf(output, "Object is a Boolean : ");
1428 if (cur->boolval) fprintf(output, "true\n");
1429 else fprintf(output, "false\n");
1430 break;
1431 case XPATH_NUMBER:
1432 switch (xmlXPathIsInf(cur->floatval)) {
1433 case 1:
1434 fprintf(output, "Object is a number : Infinity\n");
1435 break;
1436 case -1:
1437 fprintf(output, "Object is a number : -Infinity\n");
1438 break;
1439 default:
1440 if (xmlXPathIsNaN(cur->floatval)) {
1441 fprintf(output, "Object is a number : NaN\n");
1442 } else if (cur->floatval == 0) {
1443 /* Omit sign for negative zero. */
1444 fprintf(output, "Object is a number : 0\n");
1445 } else {
1446 fprintf(output, "Object is a number : %0g\n", cur->floatval);
1449 break;
1450 case XPATH_STRING:
1451 fprintf(output, "Object is a string : ");
1452 xmlDebugDumpString(output, cur->stringval);
1453 fprintf(output, "\n");
1454 break;
1455 case XPATH_POINT:
1456 fprintf(output, "Object is a point : index %d in node", cur->index);
1457 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
1458 fprintf(output, "\n");
1459 break;
1460 case XPATH_RANGE:
1461 if ((cur->user2 == NULL) ||
1462 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
1463 fprintf(output, "Object is a collapsed range :\n");
1464 fprintf(output, "%s", shift);
1465 if (cur->index >= 0)
1466 fprintf(output, "index %d in ", cur->index);
1467 fprintf(output, "node\n");
1468 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1469 depth + 1);
1470 } else {
1471 fprintf(output, "Object is a range :\n");
1472 fprintf(output, "%s", shift);
1473 fprintf(output, "From ");
1474 if (cur->index >= 0)
1475 fprintf(output, "index %d in ", cur->index);
1476 fprintf(output, "node\n");
1477 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1478 depth + 1);
1479 fprintf(output, "%s", shift);
1480 fprintf(output, "To ");
1481 if (cur->index2 >= 0)
1482 fprintf(output, "index %d in ", cur->index2);
1483 fprintf(output, "node\n");
1484 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
1485 depth + 1);
1486 fprintf(output, "\n");
1488 break;
1489 case XPATH_LOCATIONSET:
1490 #if defined(LIBXML_XPTR_ENABLED)
1491 fprintf(output, "Object is a Location Set:\n");
1492 xmlXPathDebugDumpLocationSet(output,
1493 (xmlLocationSetPtr) cur->user, depth);
1494 #endif
1495 break;
1496 case XPATH_USERS:
1497 fprintf(output, "Object is user defined\n");
1498 break;
1502 static void
1503 xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
1504 xmlXPathStepOpPtr op, int depth) {
1505 int i;
1506 char shift[100];
1508 for (i = 0;((i < depth) && (i < 25));i++)
1509 shift[2 * i] = shift[2 * i + 1] = ' ';
1510 shift[2 * i] = shift[2 * i + 1] = 0;
1512 fprintf(output, "%s", shift);
1513 if (op == NULL) {
1514 fprintf(output, "Step is NULL\n");
1515 return;
1517 switch (op->op) {
1518 case XPATH_OP_END:
1519 fprintf(output, "END"); break;
1520 case XPATH_OP_AND:
1521 fprintf(output, "AND"); break;
1522 case XPATH_OP_OR:
1523 fprintf(output, "OR"); break;
1524 case XPATH_OP_EQUAL:
1525 if (op->value)
1526 fprintf(output, "EQUAL =");
1527 else
1528 fprintf(output, "EQUAL !=");
1529 break;
1530 case XPATH_OP_CMP:
1531 if (op->value)
1532 fprintf(output, "CMP <");
1533 else
1534 fprintf(output, "CMP >");
1535 if (!op->value2)
1536 fprintf(output, "=");
1537 break;
1538 case XPATH_OP_PLUS:
1539 if (op->value == 0)
1540 fprintf(output, "PLUS -");
1541 else if (op->value == 1)
1542 fprintf(output, "PLUS +");
1543 else if (op->value == 2)
1544 fprintf(output, "PLUS unary -");
1545 else if (op->value == 3)
1546 fprintf(output, "PLUS unary - -");
1547 break;
1548 case XPATH_OP_MULT:
1549 if (op->value == 0)
1550 fprintf(output, "MULT *");
1551 else if (op->value == 1)
1552 fprintf(output, "MULT div");
1553 else
1554 fprintf(output, "MULT mod");
1555 break;
1556 case XPATH_OP_UNION:
1557 fprintf(output, "UNION"); break;
1558 case XPATH_OP_ROOT:
1559 fprintf(output, "ROOT"); break;
1560 case XPATH_OP_NODE:
1561 fprintf(output, "NODE"); break;
1562 case XPATH_OP_SORT:
1563 fprintf(output, "SORT"); break;
1564 case XPATH_OP_COLLECT: {
1565 xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
1566 xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
1567 xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
1568 const xmlChar *prefix = op->value4;
1569 const xmlChar *name = op->value5;
1571 fprintf(output, "COLLECT ");
1572 switch (axis) {
1573 case AXIS_ANCESTOR:
1574 fprintf(output, " 'ancestors' "); break;
1575 case AXIS_ANCESTOR_OR_SELF:
1576 fprintf(output, " 'ancestors-or-self' "); break;
1577 case AXIS_ATTRIBUTE:
1578 fprintf(output, " 'attributes' "); break;
1579 case AXIS_CHILD:
1580 fprintf(output, " 'child' "); break;
1581 case AXIS_DESCENDANT:
1582 fprintf(output, " 'descendant' "); break;
1583 case AXIS_DESCENDANT_OR_SELF:
1584 fprintf(output, " 'descendant-or-self' "); break;
1585 case AXIS_FOLLOWING:
1586 fprintf(output, " 'following' "); break;
1587 case AXIS_FOLLOWING_SIBLING:
1588 fprintf(output, " 'following-siblings' "); break;
1589 case AXIS_NAMESPACE:
1590 fprintf(output, " 'namespace' "); break;
1591 case AXIS_PARENT:
1592 fprintf(output, " 'parent' "); break;
1593 case AXIS_PRECEDING:
1594 fprintf(output, " 'preceding' "); break;
1595 case AXIS_PRECEDING_SIBLING:
1596 fprintf(output, " 'preceding-sibling' "); break;
1597 case AXIS_SELF:
1598 fprintf(output, " 'self' "); break;
1600 switch (test) {
1601 case NODE_TEST_NONE:
1602 fprintf(output, "'none' "); break;
1603 case NODE_TEST_TYPE:
1604 fprintf(output, "'type' "); break;
1605 case NODE_TEST_PI:
1606 fprintf(output, "'PI' "); break;
1607 case NODE_TEST_ALL:
1608 fprintf(output, "'all' "); break;
1609 case NODE_TEST_NS:
1610 fprintf(output, "'namespace' "); break;
1611 case NODE_TEST_NAME:
1612 fprintf(output, "'name' "); break;
1614 switch (type) {
1615 case NODE_TYPE_NODE:
1616 fprintf(output, "'node' "); break;
1617 case NODE_TYPE_COMMENT:
1618 fprintf(output, "'comment' "); break;
1619 case NODE_TYPE_TEXT:
1620 fprintf(output, "'text' "); break;
1621 case NODE_TYPE_PI:
1622 fprintf(output, "'PI' "); break;
1624 if (prefix != NULL)
1625 fprintf(output, "%s:", prefix);
1626 if (name != NULL)
1627 fprintf(output, "%s", (const char *) name);
1628 break;
1631 case XPATH_OP_VALUE: {
1632 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
1634 fprintf(output, "ELEM ");
1635 xmlXPathDebugDumpObject(output, object, 0);
1636 goto finish;
1638 case XPATH_OP_VARIABLE: {
1639 const xmlChar *prefix = op->value5;
1640 const xmlChar *name = op->value4;
1642 if (prefix != NULL)
1643 fprintf(output, "VARIABLE %s:%s", prefix, name);
1644 else
1645 fprintf(output, "VARIABLE %s", name);
1646 break;
1648 case XPATH_OP_FUNCTION: {
1649 int nbargs = op->value;
1650 const xmlChar *prefix = op->value5;
1651 const xmlChar *name = op->value4;
1653 if (prefix != NULL)
1654 fprintf(output, "FUNCTION %s:%s(%d args)",
1655 prefix, name, nbargs);
1656 else
1657 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
1658 break;
1660 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
1661 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
1662 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
1663 #ifdef LIBXML_XPTR_ENABLED
1664 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
1665 #endif
1666 default:
1667 fprintf(output, "UNKNOWN %d\n", op->op); return;
1669 fprintf(output, "\n");
1670 finish:
1671 if (op->ch1 >= 0)
1672 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
1673 if (op->ch2 >= 0)
1674 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
1678 * xmlXPathDebugDumpCompExpr:
1679 * @output: the FILE * for the output
1680 * @comp: the precompiled XPath expression
1681 * @depth: the indentation level.
1683 * Dumps the tree of the compiled XPath expression.
1685 void
1686 xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
1687 int depth) {
1688 int i;
1689 char shift[100];
1691 if ((output == NULL) || (comp == NULL)) return;
1693 for (i = 0;((i < depth) && (i < 25));i++)
1694 shift[2 * i] = shift[2 * i + 1] = ' ';
1695 shift[2 * i] = shift[2 * i + 1] = 0;
1697 fprintf(output, "%s", shift);
1699 #ifdef XPATH_STREAMING
1700 if (comp->stream) {
1701 fprintf(output, "Streaming Expression\n");
1702 } else
1703 #endif
1705 fprintf(output, "Compiled Expression : %d elements\n",
1706 comp->nbStep);
1707 i = comp->last;
1708 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
1712 #ifdef XP_DEBUG_OBJ_USAGE
1715 * XPath object usage related debugging variables.
1717 static int xmlXPathDebugObjCounterUndefined = 0;
1718 static int xmlXPathDebugObjCounterNodeset = 0;
1719 static int xmlXPathDebugObjCounterBool = 0;
1720 static int xmlXPathDebugObjCounterNumber = 0;
1721 static int xmlXPathDebugObjCounterString = 0;
1722 static int xmlXPathDebugObjCounterPoint = 0;
1723 static int xmlXPathDebugObjCounterRange = 0;
1724 static int xmlXPathDebugObjCounterLocset = 0;
1725 static int xmlXPathDebugObjCounterUsers = 0;
1726 static int xmlXPathDebugObjCounterXSLTTree = 0;
1727 static int xmlXPathDebugObjCounterAll = 0;
1729 static int xmlXPathDebugObjTotalUndefined = 0;
1730 static int xmlXPathDebugObjTotalNodeset = 0;
1731 static int xmlXPathDebugObjTotalBool = 0;
1732 static int xmlXPathDebugObjTotalNumber = 0;
1733 static int xmlXPathDebugObjTotalString = 0;
1734 static int xmlXPathDebugObjTotalPoint = 0;
1735 static int xmlXPathDebugObjTotalRange = 0;
1736 static int xmlXPathDebugObjTotalLocset = 0;
1737 static int xmlXPathDebugObjTotalUsers = 0;
1738 static int xmlXPathDebugObjTotalXSLTTree = 0;
1739 static int xmlXPathDebugObjTotalAll = 0;
1741 static int xmlXPathDebugObjMaxUndefined = 0;
1742 static int xmlXPathDebugObjMaxNodeset = 0;
1743 static int xmlXPathDebugObjMaxBool = 0;
1744 static int xmlXPathDebugObjMaxNumber = 0;
1745 static int xmlXPathDebugObjMaxString = 0;
1746 static int xmlXPathDebugObjMaxPoint = 0;
1747 static int xmlXPathDebugObjMaxRange = 0;
1748 static int xmlXPathDebugObjMaxLocset = 0;
1749 static int xmlXPathDebugObjMaxUsers = 0;
1750 static int xmlXPathDebugObjMaxXSLTTree = 0;
1751 static int xmlXPathDebugObjMaxAll = 0;
1753 static void
1754 xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
1756 if (ctxt != NULL) {
1757 if (ctxt->cache != NULL) {
1758 xmlXPathContextCachePtr cache =
1759 (xmlXPathContextCachePtr) ctxt->cache;
1761 cache->dbgCachedAll = 0;
1762 cache->dbgCachedNodeset = 0;
1763 cache->dbgCachedString = 0;
1764 cache->dbgCachedBool = 0;
1765 cache->dbgCachedNumber = 0;
1766 cache->dbgCachedPoint = 0;
1767 cache->dbgCachedRange = 0;
1768 cache->dbgCachedLocset = 0;
1769 cache->dbgCachedUsers = 0;
1770 cache->dbgCachedXSLTTree = 0;
1771 cache->dbgCachedUndefined = 0;
1773 cache->dbgReusedAll = 0;
1774 cache->dbgReusedNodeset = 0;
1775 cache->dbgReusedString = 0;
1776 cache->dbgReusedBool = 0;
1777 cache->dbgReusedNumber = 0;
1778 cache->dbgReusedPoint = 0;
1779 cache->dbgReusedRange = 0;
1780 cache->dbgReusedLocset = 0;
1781 cache->dbgReusedUsers = 0;
1782 cache->dbgReusedXSLTTree = 0;
1783 cache->dbgReusedUndefined = 0;
1787 xmlXPathDebugObjCounterUndefined = 0;
1788 xmlXPathDebugObjCounterNodeset = 0;
1789 xmlXPathDebugObjCounterBool = 0;
1790 xmlXPathDebugObjCounterNumber = 0;
1791 xmlXPathDebugObjCounterString = 0;
1792 xmlXPathDebugObjCounterPoint = 0;
1793 xmlXPathDebugObjCounterRange = 0;
1794 xmlXPathDebugObjCounterLocset = 0;
1795 xmlXPathDebugObjCounterUsers = 0;
1796 xmlXPathDebugObjCounterXSLTTree = 0;
1797 xmlXPathDebugObjCounterAll = 0;
1799 xmlXPathDebugObjTotalUndefined = 0;
1800 xmlXPathDebugObjTotalNodeset = 0;
1801 xmlXPathDebugObjTotalBool = 0;
1802 xmlXPathDebugObjTotalNumber = 0;
1803 xmlXPathDebugObjTotalString = 0;
1804 xmlXPathDebugObjTotalPoint = 0;
1805 xmlXPathDebugObjTotalRange = 0;
1806 xmlXPathDebugObjTotalLocset = 0;
1807 xmlXPathDebugObjTotalUsers = 0;
1808 xmlXPathDebugObjTotalXSLTTree = 0;
1809 xmlXPathDebugObjTotalAll = 0;
1811 xmlXPathDebugObjMaxUndefined = 0;
1812 xmlXPathDebugObjMaxNodeset = 0;
1813 xmlXPathDebugObjMaxBool = 0;
1814 xmlXPathDebugObjMaxNumber = 0;
1815 xmlXPathDebugObjMaxString = 0;
1816 xmlXPathDebugObjMaxPoint = 0;
1817 xmlXPathDebugObjMaxRange = 0;
1818 xmlXPathDebugObjMaxLocset = 0;
1819 xmlXPathDebugObjMaxUsers = 0;
1820 xmlXPathDebugObjMaxXSLTTree = 0;
1821 xmlXPathDebugObjMaxAll = 0;
1825 static void
1826 xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
1827 xmlXPathObjectType objType)
1829 int isCached = 0;
1831 if (ctxt != NULL) {
1832 if (ctxt->cache != NULL) {
1833 xmlXPathContextCachePtr cache =
1834 (xmlXPathContextCachePtr) ctxt->cache;
1836 isCached = 1;
1838 cache->dbgReusedAll++;
1839 switch (objType) {
1840 case XPATH_UNDEFINED:
1841 cache->dbgReusedUndefined++;
1842 break;
1843 case XPATH_NODESET:
1844 cache->dbgReusedNodeset++;
1845 break;
1846 case XPATH_BOOLEAN:
1847 cache->dbgReusedBool++;
1848 break;
1849 case XPATH_NUMBER:
1850 cache->dbgReusedNumber++;
1851 break;
1852 case XPATH_STRING:
1853 cache->dbgReusedString++;
1854 break;
1855 case XPATH_POINT:
1856 cache->dbgReusedPoint++;
1857 break;
1858 case XPATH_RANGE:
1859 cache->dbgReusedRange++;
1860 break;
1861 case XPATH_LOCATIONSET:
1862 cache->dbgReusedLocset++;
1863 break;
1864 case XPATH_USERS:
1865 cache->dbgReusedUsers++;
1866 break;
1867 case XPATH_XSLT_TREE:
1868 cache->dbgReusedXSLTTree++;
1869 break;
1870 default:
1871 break;
1876 switch (objType) {
1877 case XPATH_UNDEFINED:
1878 if (! isCached)
1879 xmlXPathDebugObjTotalUndefined++;
1880 xmlXPathDebugObjCounterUndefined++;
1881 if (xmlXPathDebugObjCounterUndefined >
1882 xmlXPathDebugObjMaxUndefined)
1883 xmlXPathDebugObjMaxUndefined =
1884 xmlXPathDebugObjCounterUndefined;
1885 break;
1886 case XPATH_NODESET:
1887 if (! isCached)
1888 xmlXPathDebugObjTotalNodeset++;
1889 xmlXPathDebugObjCounterNodeset++;
1890 if (xmlXPathDebugObjCounterNodeset >
1891 xmlXPathDebugObjMaxNodeset)
1892 xmlXPathDebugObjMaxNodeset =
1893 xmlXPathDebugObjCounterNodeset;
1894 break;
1895 case XPATH_BOOLEAN:
1896 if (! isCached)
1897 xmlXPathDebugObjTotalBool++;
1898 xmlXPathDebugObjCounterBool++;
1899 if (xmlXPathDebugObjCounterBool >
1900 xmlXPathDebugObjMaxBool)
1901 xmlXPathDebugObjMaxBool =
1902 xmlXPathDebugObjCounterBool;
1903 break;
1904 case XPATH_NUMBER:
1905 if (! isCached)
1906 xmlXPathDebugObjTotalNumber++;
1907 xmlXPathDebugObjCounterNumber++;
1908 if (xmlXPathDebugObjCounterNumber >
1909 xmlXPathDebugObjMaxNumber)
1910 xmlXPathDebugObjMaxNumber =
1911 xmlXPathDebugObjCounterNumber;
1912 break;
1913 case XPATH_STRING:
1914 if (! isCached)
1915 xmlXPathDebugObjTotalString++;
1916 xmlXPathDebugObjCounterString++;
1917 if (xmlXPathDebugObjCounterString >
1918 xmlXPathDebugObjMaxString)
1919 xmlXPathDebugObjMaxString =
1920 xmlXPathDebugObjCounterString;
1921 break;
1922 case XPATH_POINT:
1923 if (! isCached)
1924 xmlXPathDebugObjTotalPoint++;
1925 xmlXPathDebugObjCounterPoint++;
1926 if (xmlXPathDebugObjCounterPoint >
1927 xmlXPathDebugObjMaxPoint)
1928 xmlXPathDebugObjMaxPoint =
1929 xmlXPathDebugObjCounterPoint;
1930 break;
1931 case XPATH_RANGE:
1932 if (! isCached)
1933 xmlXPathDebugObjTotalRange++;
1934 xmlXPathDebugObjCounterRange++;
1935 if (xmlXPathDebugObjCounterRange >
1936 xmlXPathDebugObjMaxRange)
1937 xmlXPathDebugObjMaxRange =
1938 xmlXPathDebugObjCounterRange;
1939 break;
1940 case XPATH_LOCATIONSET:
1941 if (! isCached)
1942 xmlXPathDebugObjTotalLocset++;
1943 xmlXPathDebugObjCounterLocset++;
1944 if (xmlXPathDebugObjCounterLocset >
1945 xmlXPathDebugObjMaxLocset)
1946 xmlXPathDebugObjMaxLocset =
1947 xmlXPathDebugObjCounterLocset;
1948 break;
1949 case XPATH_USERS:
1950 if (! isCached)
1951 xmlXPathDebugObjTotalUsers++;
1952 xmlXPathDebugObjCounterUsers++;
1953 if (xmlXPathDebugObjCounterUsers >
1954 xmlXPathDebugObjMaxUsers)
1955 xmlXPathDebugObjMaxUsers =
1956 xmlXPathDebugObjCounterUsers;
1957 break;
1958 case XPATH_XSLT_TREE:
1959 if (! isCached)
1960 xmlXPathDebugObjTotalXSLTTree++;
1961 xmlXPathDebugObjCounterXSLTTree++;
1962 if (xmlXPathDebugObjCounterXSLTTree >
1963 xmlXPathDebugObjMaxXSLTTree)
1964 xmlXPathDebugObjMaxXSLTTree =
1965 xmlXPathDebugObjCounterXSLTTree;
1966 break;
1967 default:
1968 break;
1970 if (! isCached)
1971 xmlXPathDebugObjTotalAll++;
1972 xmlXPathDebugObjCounterAll++;
1973 if (xmlXPathDebugObjCounterAll >
1974 xmlXPathDebugObjMaxAll)
1975 xmlXPathDebugObjMaxAll =
1976 xmlXPathDebugObjCounterAll;
1979 static void
1980 xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
1981 xmlXPathObjectType objType)
1983 int isCached = 0;
1985 if (ctxt != NULL) {
1986 if (ctxt->cache != NULL) {
1987 xmlXPathContextCachePtr cache =
1988 (xmlXPathContextCachePtr) ctxt->cache;
1990 isCached = 1;
1992 cache->dbgCachedAll++;
1993 switch (objType) {
1994 case XPATH_UNDEFINED:
1995 cache->dbgCachedUndefined++;
1996 break;
1997 case XPATH_NODESET:
1998 cache->dbgCachedNodeset++;
1999 break;
2000 case XPATH_BOOLEAN:
2001 cache->dbgCachedBool++;
2002 break;
2003 case XPATH_NUMBER:
2004 cache->dbgCachedNumber++;
2005 break;
2006 case XPATH_STRING:
2007 cache->dbgCachedString++;
2008 break;
2009 case XPATH_POINT:
2010 cache->dbgCachedPoint++;
2011 break;
2012 case XPATH_RANGE:
2013 cache->dbgCachedRange++;
2014 break;
2015 case XPATH_LOCATIONSET:
2016 cache->dbgCachedLocset++;
2017 break;
2018 case XPATH_USERS:
2019 cache->dbgCachedUsers++;
2020 break;
2021 case XPATH_XSLT_TREE:
2022 cache->dbgCachedXSLTTree++;
2023 break;
2024 default:
2025 break;
2030 switch (objType) {
2031 case XPATH_UNDEFINED:
2032 xmlXPathDebugObjCounterUndefined--;
2033 break;
2034 case XPATH_NODESET:
2035 xmlXPathDebugObjCounterNodeset--;
2036 break;
2037 case XPATH_BOOLEAN:
2038 xmlXPathDebugObjCounterBool--;
2039 break;
2040 case XPATH_NUMBER:
2041 xmlXPathDebugObjCounterNumber--;
2042 break;
2043 case XPATH_STRING:
2044 xmlXPathDebugObjCounterString--;
2045 break;
2046 case XPATH_POINT:
2047 xmlXPathDebugObjCounterPoint--;
2048 break;
2049 case XPATH_RANGE:
2050 xmlXPathDebugObjCounterRange--;
2051 break;
2052 case XPATH_LOCATIONSET:
2053 xmlXPathDebugObjCounterLocset--;
2054 break;
2055 case XPATH_USERS:
2056 xmlXPathDebugObjCounterUsers--;
2057 break;
2058 case XPATH_XSLT_TREE:
2059 xmlXPathDebugObjCounterXSLTTree--;
2060 break;
2061 default:
2062 break;
2064 xmlXPathDebugObjCounterAll--;
2067 static void
2068 xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
2070 int reqAll, reqNodeset, reqString, reqBool, reqNumber,
2071 reqXSLTTree, reqUndefined;
2072 int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
2073 caNumber = 0, caXSLTTree = 0, caUndefined = 0;
2074 int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
2075 reNumber = 0, reXSLTTree = 0, reUndefined = 0;
2076 int leftObjs = xmlXPathDebugObjCounterAll;
2078 reqAll = xmlXPathDebugObjTotalAll;
2079 reqNodeset = xmlXPathDebugObjTotalNodeset;
2080 reqString = xmlXPathDebugObjTotalString;
2081 reqBool = xmlXPathDebugObjTotalBool;
2082 reqNumber = xmlXPathDebugObjTotalNumber;
2083 reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
2084 reqUndefined = xmlXPathDebugObjTotalUndefined;
2086 printf("# XPath object usage:\n");
2088 if (ctxt != NULL) {
2089 if (ctxt->cache != NULL) {
2090 xmlXPathContextCachePtr cache =
2091 (xmlXPathContextCachePtr) ctxt->cache;
2093 reAll = cache->dbgReusedAll;
2094 reqAll += reAll;
2095 reNodeset = cache->dbgReusedNodeset;
2096 reqNodeset += reNodeset;
2097 reString = cache->dbgReusedString;
2098 reqString += reString;
2099 reBool = cache->dbgReusedBool;
2100 reqBool += reBool;
2101 reNumber = cache->dbgReusedNumber;
2102 reqNumber += reNumber;
2103 reXSLTTree = cache->dbgReusedXSLTTree;
2104 reqXSLTTree += reXSLTTree;
2105 reUndefined = cache->dbgReusedUndefined;
2106 reqUndefined += reUndefined;
2108 caAll = cache->dbgCachedAll;
2109 caBool = cache->dbgCachedBool;
2110 caNodeset = cache->dbgCachedNodeset;
2111 caString = cache->dbgCachedString;
2112 caNumber = cache->dbgCachedNumber;
2113 caXSLTTree = cache->dbgCachedXSLTTree;
2114 caUndefined = cache->dbgCachedUndefined;
2116 if (cache->nodesetObjs)
2117 leftObjs -= cache->nodesetObjs->number;
2118 if (cache->stringObjs)
2119 leftObjs -= cache->stringObjs->number;
2120 if (cache->booleanObjs)
2121 leftObjs -= cache->booleanObjs->number;
2122 if (cache->numberObjs)
2123 leftObjs -= cache->numberObjs->number;
2124 if (cache->miscObjs)
2125 leftObjs -= cache->miscObjs->number;
2129 printf("# all\n");
2130 printf("# total : %d\n", reqAll);
2131 printf("# left : %d\n", leftObjs);
2132 printf("# created: %d\n", xmlXPathDebugObjTotalAll);
2133 printf("# reused : %d\n", reAll);
2134 printf("# max : %d\n", xmlXPathDebugObjMaxAll);
2136 printf("# node-sets\n");
2137 printf("# total : %d\n", reqNodeset);
2138 printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
2139 printf("# reused : %d\n", reNodeset);
2140 printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
2142 printf("# strings\n");
2143 printf("# total : %d\n", reqString);
2144 printf("# created: %d\n", xmlXPathDebugObjTotalString);
2145 printf("# reused : %d\n", reString);
2146 printf("# max : %d\n", xmlXPathDebugObjMaxString);
2148 printf("# booleans\n");
2149 printf("# total : %d\n", reqBool);
2150 printf("# created: %d\n", xmlXPathDebugObjTotalBool);
2151 printf("# reused : %d\n", reBool);
2152 printf("# max : %d\n", xmlXPathDebugObjMaxBool);
2154 printf("# numbers\n");
2155 printf("# total : %d\n", reqNumber);
2156 printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
2157 printf("# reused : %d\n", reNumber);
2158 printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
2160 printf("# XSLT result tree fragments\n");
2161 printf("# total : %d\n", reqXSLTTree);
2162 printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
2163 printf("# reused : %d\n", reXSLTTree);
2164 printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
2166 printf("# undefined\n");
2167 printf("# total : %d\n", reqUndefined);
2168 printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
2169 printf("# reused : %d\n", reUndefined);
2170 printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
2174 #endif /* XP_DEBUG_OBJ_USAGE */
2176 #endif /* LIBXML_DEBUG_ENABLED */
2178 /************************************************************************
2180 * XPath object caching *
2182 ************************************************************************/
2185 * xmlXPathNewCache:
2187 * Create a new object cache
2189 * Returns the xmlXPathCache just allocated.
2191 static xmlXPathContextCachePtr
2192 xmlXPathNewCache(void)
2194 xmlXPathContextCachePtr ret;
2196 ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
2197 if (ret == NULL) {
2198 xmlXPathErrMemory(NULL, "creating object cache\n");
2199 return(NULL);
2201 memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
2202 ret->maxNodeset = 100;
2203 ret->maxString = 100;
2204 ret->maxBoolean = 100;
2205 ret->maxNumber = 100;
2206 ret->maxMisc = 100;
2207 return(ret);
2210 static void
2211 xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
2213 int i;
2214 xmlXPathObjectPtr obj;
2216 if (list == NULL)
2217 return;
2219 for (i = 0; i < list->number; i++) {
2220 obj = list->items[i];
2222 * Note that it is already assured that we don't need to
2223 * look out for namespace nodes in the node-set.
2225 if (obj->nodesetval != NULL) {
2226 if (obj->nodesetval->nodeTab != NULL)
2227 xmlFree(obj->nodesetval->nodeTab);
2228 xmlFree(obj->nodesetval);
2230 xmlFree(obj);
2231 #ifdef XP_DEBUG_OBJ_USAGE
2232 xmlXPathDebugObjCounterAll--;
2233 #endif
2235 xmlPointerListFree(list);
2238 static void
2239 xmlXPathFreeCache(xmlXPathContextCachePtr cache)
2241 if (cache == NULL)
2242 return;
2243 if (cache->nodesetObjs)
2244 xmlXPathCacheFreeObjectList(cache->nodesetObjs);
2245 if (cache->stringObjs)
2246 xmlXPathCacheFreeObjectList(cache->stringObjs);
2247 if (cache->booleanObjs)
2248 xmlXPathCacheFreeObjectList(cache->booleanObjs);
2249 if (cache->numberObjs)
2250 xmlXPathCacheFreeObjectList(cache->numberObjs);
2251 if (cache->miscObjs)
2252 xmlXPathCacheFreeObjectList(cache->miscObjs);
2253 xmlFree(cache);
2257 * xmlXPathContextSetCache:
2259 * @ctxt: the XPath context
2260 * @active: enables/disables (creates/frees) the cache
2261 * @value: a value with semantics dependent on @options
2262 * @options: options (currently only the value 0 is used)
2264 * Creates/frees an object cache on the XPath context.
2265 * If activates XPath objects (xmlXPathObject) will be cached internally
2266 * to be reused.
2267 * @options:
2268 * 0: This will set the XPath object caching:
2269 * @value:
2270 * This will set the maximum number of XPath objects
2271 * to be cached per slot
2272 * There are 5 slots for: node-set, string, number, boolean, and
2273 * misc objects. Use <0 for the default number (100).
2274 * Other values for @options have currently no effect.
2276 * Returns 0 if the setting succeeded, and -1 on API or internal errors.
2279 xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
2280 int active,
2281 int value,
2282 int options)
2284 if (ctxt == NULL)
2285 return(-1);
2286 if (active) {
2287 xmlXPathContextCachePtr cache;
2289 if (ctxt->cache == NULL) {
2290 ctxt->cache = xmlXPathNewCache();
2291 if (ctxt->cache == NULL)
2292 return(-1);
2294 cache = (xmlXPathContextCachePtr) ctxt->cache;
2295 if (options == 0) {
2296 if (value < 0)
2297 value = 100;
2298 cache->maxNodeset = value;
2299 cache->maxString = value;
2300 cache->maxNumber = value;
2301 cache->maxBoolean = value;
2302 cache->maxMisc = value;
2304 } else if (ctxt->cache != NULL) {
2305 xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
2306 ctxt->cache = NULL;
2308 return(0);
2312 * xmlXPathCacheWrapNodeSet:
2313 * @ctxt: the XPath context
2314 * @val: the NodePtr value
2316 * This is the cached version of xmlXPathWrapNodeSet().
2317 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
2319 * Returns the created or reused object.
2321 static xmlXPathObjectPtr
2322 xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
2324 if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2325 xmlXPathContextCachePtr cache =
2326 (xmlXPathContextCachePtr) ctxt->cache;
2328 if ((cache->miscObjs != NULL) &&
2329 (cache->miscObjs->number != 0))
2331 xmlXPathObjectPtr ret;
2333 ret = (xmlXPathObjectPtr)
2334 cache->miscObjs->items[--cache->miscObjs->number];
2335 ret->type = XPATH_NODESET;
2336 ret->nodesetval = val;
2337 #ifdef XP_DEBUG_OBJ_USAGE
2338 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2339 #endif
2340 return(ret);
2344 return(xmlXPathWrapNodeSet(val));
2349 * xmlXPathCacheWrapString:
2350 * @ctxt: the XPath context
2351 * @val: the xmlChar * value
2353 * This is the cached version of xmlXPathWrapString().
2354 * Wraps the @val string into an XPath object.
2356 * Returns the created or reused object.
2358 static xmlXPathObjectPtr
2359 xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
2361 if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2362 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2364 if ((cache->stringObjs != NULL) &&
2365 (cache->stringObjs->number != 0))
2368 xmlXPathObjectPtr ret;
2370 ret = (xmlXPathObjectPtr)
2371 cache->stringObjs->items[--cache->stringObjs->number];
2372 ret->type = XPATH_STRING;
2373 ret->stringval = val;
2374 #ifdef XP_DEBUG_OBJ_USAGE
2375 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2376 #endif
2377 return(ret);
2378 } else if ((cache->miscObjs != NULL) &&
2379 (cache->miscObjs->number != 0))
2381 xmlXPathObjectPtr ret;
2383 * Fallback to misc-cache.
2385 ret = (xmlXPathObjectPtr)
2386 cache->miscObjs->items[--cache->miscObjs->number];
2388 ret->type = XPATH_STRING;
2389 ret->stringval = val;
2390 #ifdef XP_DEBUG_OBJ_USAGE
2391 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2392 #endif
2393 return(ret);
2396 return(xmlXPathWrapString(val));
2400 * xmlXPathCacheNewNodeSet:
2401 * @ctxt: the XPath context
2402 * @val: the NodePtr value
2404 * This is the cached version of xmlXPathNewNodeSet().
2405 * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
2406 * it with the single Node @val
2408 * Returns the created or reused object.
2410 static xmlXPathObjectPtr
2411 xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
2413 if ((ctxt != NULL) && (ctxt->cache)) {
2414 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2416 if ((cache->nodesetObjs != NULL) &&
2417 (cache->nodesetObjs->number != 0))
2419 xmlXPathObjectPtr ret;
2421 * Use the nodeset-cache.
2423 ret = (xmlXPathObjectPtr)
2424 cache->nodesetObjs->items[--cache->nodesetObjs->number];
2425 ret->type = XPATH_NODESET;
2426 ret->boolval = 0;
2427 if (val) {
2428 if ((ret->nodesetval->nodeMax == 0) ||
2429 (val->type == XML_NAMESPACE_DECL))
2431 /* TODO: Check memory error. */
2432 xmlXPathNodeSetAddUnique(ret->nodesetval, val);
2433 } else {
2434 ret->nodesetval->nodeTab[0] = val;
2435 ret->nodesetval->nodeNr = 1;
2438 #ifdef XP_DEBUG_OBJ_USAGE
2439 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2440 #endif
2441 return(ret);
2442 } else if ((cache->miscObjs != NULL) &&
2443 (cache->miscObjs->number != 0))
2445 xmlXPathObjectPtr ret;
2447 * Fallback to misc-cache.
2450 ret = (xmlXPathObjectPtr)
2451 cache->miscObjs->items[--cache->miscObjs->number];
2453 ret->type = XPATH_NODESET;
2454 ret->boolval = 0;
2455 ret->nodesetval = xmlXPathNodeSetCreate(val);
2456 if (ret->nodesetval == NULL) {
2457 ctxt->lastError.domain = XML_FROM_XPATH;
2458 ctxt->lastError.code = XML_ERR_NO_MEMORY;
2459 return(NULL);
2461 #ifdef XP_DEBUG_OBJ_USAGE
2462 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2463 #endif
2464 return(ret);
2467 return(xmlXPathNewNodeSet(val));
2471 * xmlXPathCacheNewCString:
2472 * @ctxt: the XPath context
2473 * @val: the char * value
2475 * This is the cached version of xmlXPathNewCString().
2476 * Acquire an xmlXPathObjectPtr of type string and of value @val
2478 * Returns the created or reused object.
2480 static xmlXPathObjectPtr
2481 xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
2483 if ((ctxt != NULL) && (ctxt->cache)) {
2484 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2486 if ((cache->stringObjs != NULL) &&
2487 (cache->stringObjs->number != 0))
2489 xmlXPathObjectPtr ret;
2491 ret = (xmlXPathObjectPtr)
2492 cache->stringObjs->items[--cache->stringObjs->number];
2494 ret->type = XPATH_STRING;
2495 ret->stringval = xmlStrdup(BAD_CAST val);
2496 #ifdef XP_DEBUG_OBJ_USAGE
2497 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2498 #endif
2499 return(ret);
2500 } else if ((cache->miscObjs != NULL) &&
2501 (cache->miscObjs->number != 0))
2503 xmlXPathObjectPtr ret;
2505 ret = (xmlXPathObjectPtr)
2506 cache->miscObjs->items[--cache->miscObjs->number];
2508 ret->type = XPATH_STRING;
2509 ret->stringval = xmlStrdup(BAD_CAST val);
2510 #ifdef XP_DEBUG_OBJ_USAGE
2511 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2512 #endif
2513 return(ret);
2516 return(xmlXPathNewCString(val));
2520 * xmlXPathCacheNewString:
2521 * @ctxt: the XPath context
2522 * @val: the xmlChar * value
2524 * This is the cached version of xmlXPathNewString().
2525 * Acquire an xmlXPathObjectPtr of type string and of value @val
2527 * Returns the created or reused object.
2529 static xmlXPathObjectPtr
2530 xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
2532 if ((ctxt != NULL) && (ctxt->cache)) {
2533 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2535 if ((cache->stringObjs != NULL) &&
2536 (cache->stringObjs->number != 0))
2538 xmlXPathObjectPtr ret;
2540 ret = (xmlXPathObjectPtr)
2541 cache->stringObjs->items[--cache->stringObjs->number];
2542 ret->type = XPATH_STRING;
2543 if (val != NULL)
2544 ret->stringval = xmlStrdup(val);
2545 else
2546 ret->stringval = xmlStrdup((const xmlChar *)"");
2547 #ifdef XP_DEBUG_OBJ_USAGE
2548 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2549 #endif
2550 return(ret);
2551 } else if ((cache->miscObjs != NULL) &&
2552 (cache->miscObjs->number != 0))
2554 xmlXPathObjectPtr ret;
2556 ret = (xmlXPathObjectPtr)
2557 cache->miscObjs->items[--cache->miscObjs->number];
2559 ret->type = XPATH_STRING;
2560 if (val != NULL)
2561 ret->stringval = xmlStrdup(val);
2562 else
2563 ret->stringval = xmlStrdup((const xmlChar *)"");
2564 #ifdef XP_DEBUG_OBJ_USAGE
2565 xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2566 #endif
2567 return(ret);
2570 return(xmlXPathNewString(val));
2574 * xmlXPathCacheNewBoolean:
2575 * @ctxt: the XPath context
2576 * @val: the boolean value
2578 * This is the cached version of xmlXPathNewBoolean().
2579 * Acquires an xmlXPathObjectPtr of type boolean and of value @val
2581 * Returns the created or reused object.
2583 static xmlXPathObjectPtr
2584 xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
2586 if ((ctxt != NULL) && (ctxt->cache)) {
2587 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2589 if ((cache->booleanObjs != NULL) &&
2590 (cache->booleanObjs->number != 0))
2592 xmlXPathObjectPtr ret;
2594 ret = (xmlXPathObjectPtr)
2595 cache->booleanObjs->items[--cache->booleanObjs->number];
2596 ret->type = XPATH_BOOLEAN;
2597 ret->boolval = (val != 0);
2598 #ifdef XP_DEBUG_OBJ_USAGE
2599 xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2600 #endif
2601 return(ret);
2602 } else if ((cache->miscObjs != NULL) &&
2603 (cache->miscObjs->number != 0))
2605 xmlXPathObjectPtr ret;
2607 ret = (xmlXPathObjectPtr)
2608 cache->miscObjs->items[--cache->miscObjs->number];
2610 ret->type = XPATH_BOOLEAN;
2611 ret->boolval = (val != 0);
2612 #ifdef XP_DEBUG_OBJ_USAGE
2613 xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2614 #endif
2615 return(ret);
2618 return(xmlXPathNewBoolean(val));
2622 * xmlXPathCacheNewFloat:
2623 * @ctxt: the XPath context
2624 * @val: the double value
2626 * This is the cached version of xmlXPathNewFloat().
2627 * Acquires an xmlXPathObjectPtr of type double and of value @val
2629 * Returns the created or reused object.
2631 static xmlXPathObjectPtr
2632 xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
2634 if ((ctxt != NULL) && (ctxt->cache)) {
2635 xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2637 if ((cache->numberObjs != NULL) &&
2638 (cache->numberObjs->number != 0))
2640 xmlXPathObjectPtr ret;
2642 ret = (xmlXPathObjectPtr)
2643 cache->numberObjs->items[--cache->numberObjs->number];
2644 ret->type = XPATH_NUMBER;
2645 ret->floatval = val;
2646 #ifdef XP_DEBUG_OBJ_USAGE
2647 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2648 #endif
2649 return(ret);
2650 } else if ((cache->miscObjs != NULL) &&
2651 (cache->miscObjs->number != 0))
2653 xmlXPathObjectPtr ret;
2655 ret = (xmlXPathObjectPtr)
2656 cache->miscObjs->items[--cache->miscObjs->number];
2658 ret->type = XPATH_NUMBER;
2659 ret->floatval = val;
2660 #ifdef XP_DEBUG_OBJ_USAGE
2661 xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2662 #endif
2663 return(ret);
2666 return(xmlXPathNewFloat(val));
2670 * xmlXPathCacheConvertString:
2671 * @ctxt: the XPath context
2672 * @val: an XPath object
2674 * This is the cached version of xmlXPathConvertString().
2675 * Converts an existing object to its string() equivalent
2677 * Returns a created or reused object, the old one is freed (cached)
2678 * (or the operation is done directly on @val)
2681 static xmlXPathObjectPtr
2682 xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2683 xmlChar *res = NULL;
2685 if (val == NULL)
2686 return(xmlXPathCacheNewCString(ctxt, ""));
2688 switch (val->type) {
2689 case XPATH_UNDEFINED:
2690 #ifdef DEBUG_EXPR
2691 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2692 #endif
2693 break;
2694 case XPATH_NODESET:
2695 case XPATH_XSLT_TREE:
2696 res = xmlXPathCastNodeSetToString(val->nodesetval);
2697 break;
2698 case XPATH_STRING:
2699 return(val);
2700 case XPATH_BOOLEAN:
2701 res = xmlXPathCastBooleanToString(val->boolval);
2702 break;
2703 case XPATH_NUMBER:
2704 res = xmlXPathCastNumberToString(val->floatval);
2705 break;
2706 case XPATH_USERS:
2707 case XPATH_POINT:
2708 case XPATH_RANGE:
2709 case XPATH_LOCATIONSET:
2710 TODO;
2711 break;
2713 xmlXPathReleaseObject(ctxt, val);
2714 if (res == NULL)
2715 return(xmlXPathCacheNewCString(ctxt, ""));
2716 return(xmlXPathCacheWrapString(ctxt, res));
2720 * xmlXPathCacheObjectCopy:
2721 * @ctxt: the XPath context
2722 * @val: the original object
2724 * This is the cached version of xmlXPathObjectCopy().
2725 * Acquire a copy of a given object
2727 * Returns a created or reused created object.
2729 static xmlXPathObjectPtr
2730 xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
2732 if (val == NULL)
2733 return(NULL);
2735 if (XP_HAS_CACHE(ctxt)) {
2736 switch (val->type) {
2737 case XPATH_NODESET:
2738 return(xmlXPathCacheWrapNodeSet(ctxt,
2739 xmlXPathNodeSetMerge(NULL, val->nodesetval)));
2740 case XPATH_STRING:
2741 return(xmlXPathCacheNewString(ctxt, val->stringval));
2742 case XPATH_BOOLEAN:
2743 return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
2744 case XPATH_NUMBER:
2745 return(xmlXPathCacheNewFloat(ctxt, val->floatval));
2746 default:
2747 break;
2750 return(xmlXPathObjectCopy(val));
2754 * xmlXPathCacheConvertBoolean:
2755 * @ctxt: the XPath context
2756 * @val: an XPath object
2758 * This is the cached version of xmlXPathConvertBoolean().
2759 * Converts an existing object to its boolean() equivalent
2761 * Returns a created or reused object, the old one is freed (or the operation
2762 * is done directly on @val)
2764 static xmlXPathObjectPtr
2765 xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2766 xmlXPathObjectPtr ret;
2768 if (val == NULL)
2769 return(xmlXPathCacheNewBoolean(ctxt, 0));
2770 if (val->type == XPATH_BOOLEAN)
2771 return(val);
2772 ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
2773 xmlXPathReleaseObject(ctxt, val);
2774 return(ret);
2778 * xmlXPathCacheConvertNumber:
2779 * @ctxt: the XPath context
2780 * @val: an XPath object
2782 * This is the cached version of xmlXPathConvertNumber().
2783 * Converts an existing object to its number() equivalent
2785 * Returns a created or reused object, the old one is freed (or the operation
2786 * is done directly on @val)
2788 static xmlXPathObjectPtr
2789 xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2790 xmlXPathObjectPtr ret;
2792 if (val == NULL)
2793 return(xmlXPathCacheNewFloat(ctxt, 0.0));
2794 if (val->type == XPATH_NUMBER)
2795 return(val);
2796 ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
2797 xmlXPathReleaseObject(ctxt, val);
2798 return(ret);
2801 /************************************************************************
2803 * Parser stacks related functions and macros *
2805 ************************************************************************/
2808 * xmlXPathSetFrame:
2809 * @ctxt: an XPath parser context
2811 * Set the callee evaluation frame
2813 * Returns the previous frame value to be restored once done
2815 static int
2816 xmlXPathSetFrame(xmlXPathParserContextPtr ctxt) {
2817 int ret;
2819 if (ctxt == NULL)
2820 return(0);
2821 ret = ctxt->valueFrame;
2822 ctxt->valueFrame = ctxt->valueNr;
2823 return(ret);
2827 * xmlXPathPopFrame:
2828 * @ctxt: an XPath parser context
2829 * @frame: the previous frame value
2831 * Remove the callee evaluation frame
2833 static void
2834 xmlXPathPopFrame(xmlXPathParserContextPtr ctxt, int frame) {
2835 if (ctxt == NULL)
2836 return;
2837 if (ctxt->valueNr < ctxt->valueFrame) {
2838 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
2840 ctxt->valueFrame = frame;
2844 * valuePop:
2845 * @ctxt: an XPath evaluation context
2847 * Pops the top XPath object from the value stack
2849 * Returns the XPath object just removed
2851 xmlXPathObjectPtr
2852 valuePop(xmlXPathParserContextPtr ctxt)
2854 xmlXPathObjectPtr ret;
2856 if ((ctxt == NULL) || (ctxt->valueNr <= 0))
2857 return (NULL);
2859 if (ctxt->valueNr <= ctxt->valueFrame) {
2860 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
2861 return (NULL);
2864 ctxt->valueNr--;
2865 if (ctxt->valueNr > 0)
2866 ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
2867 else
2868 ctxt->value = NULL;
2869 ret = ctxt->valueTab[ctxt->valueNr];
2870 ctxt->valueTab[ctxt->valueNr] = NULL;
2871 return (ret);
2874 * valuePush:
2875 * @ctxt: an XPath evaluation context
2876 * @value: the XPath object
2878 * Pushes a new XPath object on top of the value stack. If value is NULL,
2879 * a memory error is recorded in the parser context.
2881 * Returns the number of items on the value stack, or -1 in case of error.
2884 valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
2886 if (ctxt == NULL) return(-1);
2887 if (value == NULL) {
2889 * A NULL value typically indicates that a memory allocation failed,
2890 * so we set ctxt->error here to propagate the error.
2892 ctxt->error = XPATH_MEMORY_ERROR;
2893 return(-1);
2895 if (ctxt->valueNr >= ctxt->valueMax) {
2896 xmlXPathObjectPtr *tmp;
2898 if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) {
2899 xmlXPathPErrMemory(ctxt, "XPath stack depth limit reached\n");
2900 return (-1);
2902 tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
2903 2 * ctxt->valueMax *
2904 sizeof(ctxt->valueTab[0]));
2905 if (tmp == NULL) {
2906 xmlXPathPErrMemory(ctxt, "pushing value\n");
2907 return (-1);
2909 ctxt->valueMax *= 2;
2910 ctxt->valueTab = tmp;
2912 ctxt->valueTab[ctxt->valueNr] = value;
2913 ctxt->value = value;
2914 return (ctxt->valueNr++);
2918 * xmlXPathPopBoolean:
2919 * @ctxt: an XPath parser context
2921 * Pops a boolean from the stack, handling conversion if needed.
2922 * Check error with #xmlXPathCheckError.
2924 * Returns the boolean
2927 xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
2928 xmlXPathObjectPtr obj;
2929 int ret;
2931 obj = valuePop(ctxt);
2932 if (obj == NULL) {
2933 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2934 return(0);
2936 if (obj->type != XPATH_BOOLEAN)
2937 ret = xmlXPathCastToBoolean(obj);
2938 else
2939 ret = obj->boolval;
2940 xmlXPathReleaseObject(ctxt->context, obj);
2941 return(ret);
2945 * xmlXPathPopNumber:
2946 * @ctxt: an XPath parser context
2948 * Pops a number from the stack, handling conversion if needed.
2949 * Check error with #xmlXPathCheckError.
2951 * Returns the number
2953 double
2954 xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
2955 xmlXPathObjectPtr obj;
2956 double ret;
2958 obj = valuePop(ctxt);
2959 if (obj == NULL) {
2960 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2961 return(0);
2963 if (obj->type != XPATH_NUMBER)
2964 ret = xmlXPathCastToNumber(obj);
2965 else
2966 ret = obj->floatval;
2967 xmlXPathReleaseObject(ctxt->context, obj);
2968 return(ret);
2972 * xmlXPathPopString:
2973 * @ctxt: an XPath parser context
2975 * Pops a string from the stack, handling conversion if needed.
2976 * Check error with #xmlXPathCheckError.
2978 * Returns the string
2980 xmlChar *
2981 xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
2982 xmlXPathObjectPtr obj;
2983 xmlChar * ret;
2985 obj = valuePop(ctxt);
2986 if (obj == NULL) {
2987 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2988 return(NULL);
2990 ret = xmlXPathCastToString(obj); /* this does required strdup */
2991 /* TODO: needs refactoring somewhere else */
2992 if (obj->stringval == ret)
2993 obj->stringval = NULL;
2994 xmlXPathReleaseObject(ctxt->context, obj);
2995 return(ret);
2999 * xmlXPathPopNodeSet:
3000 * @ctxt: an XPath parser context
3002 * Pops a node-set from the stack, handling conversion if needed.
3003 * Check error with #xmlXPathCheckError.
3005 * Returns the node-set
3007 xmlNodeSetPtr
3008 xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
3009 xmlXPathObjectPtr obj;
3010 xmlNodeSetPtr ret;
3012 if (ctxt == NULL) return(NULL);
3013 if (ctxt->value == NULL) {
3014 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
3015 return(NULL);
3017 if (!xmlXPathStackIsNodeSet(ctxt)) {
3018 xmlXPathSetTypeError(ctxt);
3019 return(NULL);
3021 obj = valuePop(ctxt);
3022 ret = obj->nodesetval;
3023 #if 0
3024 /* to fix memory leak of not clearing obj->user */
3025 if (obj->boolval && obj->user != NULL)
3026 xmlFreeNodeList((xmlNodePtr) obj->user);
3027 #endif
3028 obj->nodesetval = NULL;
3029 xmlXPathReleaseObject(ctxt->context, obj);
3030 return(ret);
3034 * xmlXPathPopExternal:
3035 * @ctxt: an XPath parser context
3037 * Pops an external object from the stack, handling conversion if needed.
3038 * Check error with #xmlXPathCheckError.
3040 * Returns the object
3042 void *
3043 xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
3044 xmlXPathObjectPtr obj;
3045 void * ret;
3047 if ((ctxt == NULL) || (ctxt->value == NULL)) {
3048 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
3049 return(NULL);
3051 if (ctxt->value->type != XPATH_USERS) {
3052 xmlXPathSetTypeError(ctxt);
3053 return(NULL);
3055 obj = valuePop(ctxt);
3056 ret = obj->user;
3057 obj->user = NULL;
3058 xmlXPathReleaseObject(ctxt->context, obj);
3059 return(ret);
3063 * Macros for accessing the content. Those should be used only by the parser,
3064 * and not exported.
3066 * Dirty macros, i.e. one need to make assumption on the context to use them
3068 * CUR_PTR return the current pointer to the xmlChar to be parsed.
3069 * CUR returns the current xmlChar value, i.e. a 8 bit value
3070 * in ISO-Latin or UTF-8.
3071 * This should be used internally by the parser
3072 * only to compare to ASCII values otherwise it would break when
3073 * running with UTF-8 encoding.
3074 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
3075 * to compare on ASCII based substring.
3076 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
3077 * strings within the parser.
3078 * CURRENT Returns the current char value, with the full decoding of
3079 * UTF-8 if we are using this mode. It returns an int.
3080 * NEXT Skip to the next character, this does the proper decoding
3081 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
3082 * It returns the pointer to the current xmlChar.
3085 #define CUR (*ctxt->cur)
3086 #define SKIP(val) ctxt->cur += (val)
3087 #define NXT(val) ctxt->cur[(val)]
3088 #define CUR_PTR ctxt->cur
3089 #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
3091 #define COPY_BUF(l,b,i,v) \
3092 if (l == 1) b[i++] = (xmlChar) v; \
3093 else i += xmlCopyChar(l,&b[i],v)
3095 #define NEXTL(l) ctxt->cur += l
3097 #define SKIP_BLANKS \
3098 while (IS_BLANK_CH(*(ctxt->cur))) NEXT
3100 #define CURRENT (*ctxt->cur)
3101 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
3104 #ifndef DBL_DIG
3105 #define DBL_DIG 16
3106 #endif
3107 #ifndef DBL_EPSILON
3108 #define DBL_EPSILON 1E-9
3109 #endif
3111 #define UPPER_DOUBLE 1E9
3112 #define LOWER_DOUBLE 1E-5
3113 #define LOWER_DOUBLE_EXP 5
3115 #define INTEGER_DIGITS DBL_DIG
3116 #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
3117 #define EXPONENT_DIGITS (3 + 2)
3120 * xmlXPathFormatNumber:
3121 * @number: number to format
3122 * @buffer: output buffer
3123 * @buffersize: size of output buffer
3125 * Convert the number into a string representation.
3127 static void
3128 xmlXPathFormatNumber(double number, char buffer[], int buffersize)
3130 switch (xmlXPathIsInf(number)) {
3131 case 1:
3132 if (buffersize > (int)sizeof("Infinity"))
3133 snprintf(buffer, buffersize, "Infinity");
3134 break;
3135 case -1:
3136 if (buffersize > (int)sizeof("-Infinity"))
3137 snprintf(buffer, buffersize, "-Infinity");
3138 break;
3139 default:
3140 if (xmlXPathIsNaN(number)) {
3141 if (buffersize > (int)sizeof("NaN"))
3142 snprintf(buffer, buffersize, "NaN");
3143 } else if (number == 0) {
3144 /* Omit sign for negative zero. */
3145 snprintf(buffer, buffersize, "0");
3146 } else if ((number > INT_MIN) && (number < INT_MAX) &&
3147 (number == (int) number)) {
3148 char work[30];
3149 char *ptr, *cur;
3150 int value = (int) number;
3152 ptr = &buffer[0];
3153 if (value == 0) {
3154 *ptr++ = '0';
3155 } else {
3156 snprintf(work, 29, "%d", value);
3157 cur = &work[0];
3158 while ((*cur) && (ptr - buffer < buffersize)) {
3159 *ptr++ = *cur++;
3162 if (ptr - buffer < buffersize) {
3163 *ptr = 0;
3164 } else if (buffersize > 0) {
3165 ptr--;
3166 *ptr = 0;
3168 } else {
3170 For the dimension of work,
3171 DBL_DIG is number of significant digits
3172 EXPONENT is only needed for "scientific notation"
3173 3 is sign, decimal point, and terminating zero
3174 LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
3175 Note that this dimension is slightly (a few characters)
3176 larger than actually necessary.
3178 char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
3179 int integer_place, fraction_place;
3180 char *ptr;
3181 char *after_fraction;
3182 double absolute_value;
3183 int size;
3185 absolute_value = fabs(number);
3188 * First choose format - scientific or regular floating point.
3189 * In either case, result is in work, and after_fraction points
3190 * just past the fractional part.
3192 if ( ((absolute_value > UPPER_DOUBLE) ||
3193 (absolute_value < LOWER_DOUBLE)) &&
3194 (absolute_value != 0.0) ) {
3195 /* Use scientific notation */
3196 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
3197 fraction_place = DBL_DIG - 1;
3198 size = snprintf(work, sizeof(work),"%*.*e",
3199 integer_place, fraction_place, number);
3200 while ((size > 0) && (work[size] != 'e')) size--;
3203 else {
3204 /* Use regular notation */
3205 if (absolute_value > 0.0) {
3206 integer_place = (int)log10(absolute_value);
3207 if (integer_place > 0)
3208 fraction_place = DBL_DIG - integer_place - 1;
3209 else
3210 fraction_place = DBL_DIG - integer_place;
3211 } else {
3212 fraction_place = 1;
3214 size = snprintf(work, sizeof(work), "%0.*f",
3215 fraction_place, number);
3218 /* Remove leading spaces sometimes inserted by snprintf */
3219 while (work[0] == ' ') {
3220 for (ptr = &work[0];(ptr[0] = ptr[1]);ptr++);
3221 size--;
3224 /* Remove fractional trailing zeroes */
3225 after_fraction = work + size;
3226 ptr = after_fraction;
3227 while (*(--ptr) == '0')
3229 if (*ptr != '.')
3230 ptr++;
3231 while ((*ptr++ = *after_fraction++) != 0);
3233 /* Finally copy result back to caller */
3234 size = strlen(work) + 1;
3235 if (size > buffersize) {
3236 work[buffersize - 1] = 0;
3237 size = buffersize;
3239 memmove(buffer, work, size);
3241 break;
3246 /************************************************************************
3248 * Routines to handle NodeSets *
3250 ************************************************************************/
3253 * xmlXPathOrderDocElems:
3254 * @doc: an input document
3256 * Call this routine to speed up XPath computation on static documents.
3257 * This stamps all the element nodes with the document order
3258 * Like for line information, the order is kept in the element->content
3259 * field, the value stored is actually - the node number (starting at -1)
3260 * to be able to differentiate from line numbers.
3262 * Returns the number of elements found in the document or -1 in case
3263 * of error.
3265 long
3266 xmlXPathOrderDocElems(xmlDocPtr doc) {
3267 ptrdiff_t count = 0;
3268 xmlNodePtr cur;
3270 if (doc == NULL)
3271 return(-1);
3272 cur = doc->children;
3273 while (cur != NULL) {
3274 if (cur->type == XML_ELEMENT_NODE) {
3275 cur->content = (void *) (-(++count));
3276 if (cur->children != NULL) {
3277 cur = cur->children;
3278 continue;
3281 if (cur->next != NULL) {
3282 cur = cur->next;
3283 continue;
3285 do {
3286 cur = cur->parent;
3287 if (cur == NULL)
3288 break;
3289 if (cur == (xmlNodePtr) doc) {
3290 cur = NULL;
3291 break;
3293 if (cur->next != NULL) {
3294 cur = cur->next;
3295 break;
3297 } while (cur != NULL);
3299 return((long) count);
3303 * xmlXPathCmpNodes:
3304 * @node1: the first node
3305 * @node2: the second node
3307 * Compare two nodes w.r.t document order
3309 * Returns -2 in case of error 1 if first point < second point, 0 if
3310 * it's the same node, -1 otherwise
3313 xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
3314 int depth1, depth2;
3315 int attr1 = 0, attr2 = 0;
3316 xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
3317 xmlNodePtr cur, root;
3319 if ((node1 == NULL) || (node2 == NULL))
3320 return(-2);
3322 * a couple of optimizations which will avoid computations in most cases
3324 if (node1 == node2) /* trivial case */
3325 return(0);
3326 if (node1->type == XML_ATTRIBUTE_NODE) {
3327 attr1 = 1;
3328 attrNode1 = node1;
3329 node1 = node1->parent;
3331 if (node2->type == XML_ATTRIBUTE_NODE) {
3332 attr2 = 1;
3333 attrNode2 = node2;
3334 node2 = node2->parent;
3336 if (node1 == node2) {
3337 if (attr1 == attr2) {
3338 /* not required, but we keep attributes in order */
3339 if (attr1 != 0) {
3340 cur = attrNode2->prev;
3341 while (cur != NULL) {
3342 if (cur == attrNode1)
3343 return (1);
3344 cur = cur->prev;
3346 return (-1);
3348 return(0);
3350 if (attr2 == 1)
3351 return(1);
3352 return(-1);
3354 if ((node1->type == XML_NAMESPACE_DECL) ||
3355 (node2->type == XML_NAMESPACE_DECL))
3356 return(1);
3357 if (node1 == node2->prev)
3358 return(1);
3359 if (node1 == node2->next)
3360 return(-1);
3363 * Speedup using document order if available.
3365 if ((node1->type == XML_ELEMENT_NODE) &&
3366 (node2->type == XML_ELEMENT_NODE) &&
3367 (0 > (ptrdiff_t) node1->content) &&
3368 (0 > (ptrdiff_t) node2->content) &&
3369 (node1->doc == node2->doc)) {
3370 ptrdiff_t l1, l2;
3372 l1 = -((ptrdiff_t) node1->content);
3373 l2 = -((ptrdiff_t) node2->content);
3374 if (l1 < l2)
3375 return(1);
3376 if (l1 > l2)
3377 return(-1);
3381 * compute depth to root
3383 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
3384 if (cur->parent == node1)
3385 return(1);
3386 depth2++;
3388 root = cur;
3389 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
3390 if (cur->parent == node2)
3391 return(-1);
3392 depth1++;
3395 * Distinct document (or distinct entities :-( ) case.
3397 if (root != cur) {
3398 return(-2);
3401 * get the nearest common ancestor.
3403 while (depth1 > depth2) {
3404 depth1--;
3405 node1 = node1->parent;
3407 while (depth2 > depth1) {
3408 depth2--;
3409 node2 = node2->parent;
3411 while (node1->parent != node2->parent) {
3412 node1 = node1->parent;
3413 node2 = node2->parent;
3414 /* should not happen but just in case ... */
3415 if ((node1 == NULL) || (node2 == NULL))
3416 return(-2);
3419 * Find who's first.
3421 if (node1 == node2->prev)
3422 return(1);
3423 if (node1 == node2->next)
3424 return(-1);
3426 * Speedup using document order if available.
3428 if ((node1->type == XML_ELEMENT_NODE) &&
3429 (node2->type == XML_ELEMENT_NODE) &&
3430 (0 > (ptrdiff_t) node1->content) &&
3431 (0 > (ptrdiff_t) node2->content) &&
3432 (node1->doc == node2->doc)) {
3433 ptrdiff_t l1, l2;
3435 l1 = -((ptrdiff_t) node1->content);
3436 l2 = -((ptrdiff_t) node2->content);
3437 if (l1 < l2)
3438 return(1);
3439 if (l1 > l2)
3440 return(-1);
3443 for (cur = node1->next;cur != NULL;cur = cur->next)
3444 if (cur == node2)
3445 return(1);
3446 return(-1); /* assume there is no sibling list corruption */
3450 * xmlXPathNodeSetSort:
3451 * @set: the node set
3453 * Sort the node set in document order
3455 void
3456 xmlXPathNodeSetSort(xmlNodeSetPtr set) {
3457 #ifndef WITH_TIM_SORT
3458 int i, j, incr, len;
3459 xmlNodePtr tmp;
3460 #endif
3462 if (set == NULL)
3463 return;
3465 #ifndef WITH_TIM_SORT
3467 * Use the old Shell's sort implementation to sort the node-set
3468 * Timsort ought to be quite faster
3470 len = set->nodeNr;
3471 for (incr = len / 2; incr > 0; incr /= 2) {
3472 for (i = incr; i < len; i++) {
3473 j = i - incr;
3474 while (j >= 0) {
3475 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
3476 if (xmlXPathCmpNodesExt(set->nodeTab[j],
3477 set->nodeTab[j + incr]) == -1)
3478 #else
3479 if (xmlXPathCmpNodes(set->nodeTab[j],
3480 set->nodeTab[j + incr]) == -1)
3481 #endif
3483 tmp = set->nodeTab[j];
3484 set->nodeTab[j] = set->nodeTab[j + incr];
3485 set->nodeTab[j + incr] = tmp;
3486 j -= incr;
3487 } else
3488 break;
3492 #else /* WITH_TIM_SORT */
3493 libxml_domnode_tim_sort(set->nodeTab, set->nodeNr);
3494 #endif /* WITH_TIM_SORT */
3497 #define XML_NODESET_DEFAULT 10
3499 * xmlXPathNodeSetDupNs:
3500 * @node: the parent node of the namespace XPath node
3501 * @ns: the libxml namespace declaration node.
3503 * Namespace node in libxml don't match the XPath semantic. In a node set
3504 * the namespace nodes are duplicated and the next pointer is set to the
3505 * parent node in the XPath semantic.
3507 * Returns the newly created object.
3509 static xmlNodePtr
3510 xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
3511 xmlNsPtr cur;
3513 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3514 return(NULL);
3515 if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
3516 return((xmlNodePtr) ns);
3519 * Allocate a new Namespace and fill the fields.
3521 cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
3522 if (cur == NULL) {
3523 xmlXPathErrMemory(NULL, "duplicating namespace\n");
3524 return(NULL);
3526 memset(cur, 0, sizeof(xmlNs));
3527 cur->type = XML_NAMESPACE_DECL;
3528 if (ns->href != NULL)
3529 cur->href = xmlStrdup(ns->href);
3530 if (ns->prefix != NULL)
3531 cur->prefix = xmlStrdup(ns->prefix);
3532 cur->next = (xmlNsPtr) node;
3533 return((xmlNodePtr) cur);
3537 * xmlXPathNodeSetFreeNs:
3538 * @ns: the XPath namespace node found in a nodeset.
3540 * Namespace nodes in libxml don't match the XPath semantic. In a node set
3541 * the namespace nodes are duplicated and the next pointer is set to the
3542 * parent node in the XPath semantic. Check if such a node needs to be freed
3544 void
3545 xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
3546 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3547 return;
3549 if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
3550 if (ns->href != NULL)
3551 xmlFree((xmlChar *)ns->href);
3552 if (ns->prefix != NULL)
3553 xmlFree((xmlChar *)ns->prefix);
3554 xmlFree(ns);
3559 * xmlXPathNodeSetCreate:
3560 * @val: an initial xmlNodePtr, or NULL
3562 * Create a new xmlNodeSetPtr of type double and of value @val
3564 * Returns the newly created object.
3566 xmlNodeSetPtr
3567 xmlXPathNodeSetCreate(xmlNodePtr val) {
3568 xmlNodeSetPtr ret;
3570 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3571 if (ret == NULL) {
3572 xmlXPathErrMemory(NULL, "creating nodeset\n");
3573 return(NULL);
3575 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
3576 if (val != NULL) {
3577 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3578 sizeof(xmlNodePtr));
3579 if (ret->nodeTab == NULL) {
3580 xmlXPathErrMemory(NULL, "creating nodeset\n");
3581 xmlFree(ret);
3582 return(NULL);
3584 memset(ret->nodeTab, 0 ,
3585 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3586 ret->nodeMax = XML_NODESET_DEFAULT;
3587 if (val->type == XML_NAMESPACE_DECL) {
3588 xmlNsPtr ns = (xmlNsPtr) val;
3590 /* TODO: Check memory error. */
3591 ret->nodeTab[ret->nodeNr++] =
3592 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3593 } else
3594 ret->nodeTab[ret->nodeNr++] = val;
3596 return(ret);
3600 * xmlXPathNodeSetContains:
3601 * @cur: the node-set
3602 * @val: the node
3604 * checks whether @cur contains @val
3606 * Returns true (1) if @cur contains @val, false (0) otherwise
3609 xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
3610 int i;
3612 if ((cur == NULL) || (val == NULL)) return(0);
3613 if (val->type == XML_NAMESPACE_DECL) {
3614 for (i = 0; i < cur->nodeNr; i++) {
3615 if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
3616 xmlNsPtr ns1, ns2;
3618 ns1 = (xmlNsPtr) val;
3619 ns2 = (xmlNsPtr) cur->nodeTab[i];
3620 if (ns1 == ns2)
3621 return(1);
3622 if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
3623 (xmlStrEqual(ns1->prefix, ns2->prefix)))
3624 return(1);
3627 } else {
3628 for (i = 0; i < cur->nodeNr; i++) {
3629 if (cur->nodeTab[i] == val)
3630 return(1);
3633 return(0);
3637 * xmlXPathNodeSetAddNs:
3638 * @cur: the initial node set
3639 * @node: the hosting node
3640 * @ns: a the namespace node
3642 * add a new namespace node to an existing NodeSet
3644 * Returns 0 in case of success and -1 in case of error
3647 xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
3648 int i;
3651 if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
3652 (ns->type != XML_NAMESPACE_DECL) ||
3653 (node->type != XML_ELEMENT_NODE))
3654 return(-1);
3656 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3658 * prevent duplicates
3660 for (i = 0;i < cur->nodeNr;i++) {
3661 if ((cur->nodeTab[i] != NULL) &&
3662 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
3663 (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
3664 (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
3665 return(0);
3669 * grow the nodeTab if needed
3671 if (cur->nodeMax == 0) {
3672 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3673 sizeof(xmlNodePtr));
3674 if (cur->nodeTab == NULL) {
3675 xmlXPathErrMemory(NULL, "growing nodeset\n");
3676 return(-1);
3678 memset(cur->nodeTab, 0 ,
3679 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3680 cur->nodeMax = XML_NODESET_DEFAULT;
3681 } else if (cur->nodeNr == cur->nodeMax) {
3682 xmlNodePtr *temp;
3684 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3685 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3686 return(-1);
3688 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3689 sizeof(xmlNodePtr));
3690 if (temp == NULL) {
3691 xmlXPathErrMemory(NULL, "growing nodeset\n");
3692 return(-1);
3694 cur->nodeMax *= 2;
3695 cur->nodeTab = temp;
3697 /* TODO: Check memory error. */
3698 cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
3699 return(0);
3703 * xmlXPathNodeSetAdd:
3704 * @cur: the initial node set
3705 * @val: a new xmlNodePtr
3707 * add a new xmlNodePtr to an existing NodeSet
3709 * Returns 0 in case of success, and -1 in case of error
3712 xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
3713 int i;
3715 if ((cur == NULL) || (val == NULL)) return(-1);
3717 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3719 * prevent duplicates
3721 for (i = 0;i < cur->nodeNr;i++)
3722 if (cur->nodeTab[i] == val) return(0);
3725 * grow the nodeTab if needed
3727 if (cur->nodeMax == 0) {
3728 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3729 sizeof(xmlNodePtr));
3730 if (cur->nodeTab == NULL) {
3731 xmlXPathErrMemory(NULL, "growing nodeset\n");
3732 return(-1);
3734 memset(cur->nodeTab, 0 ,
3735 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3736 cur->nodeMax = XML_NODESET_DEFAULT;
3737 } else if (cur->nodeNr == cur->nodeMax) {
3738 xmlNodePtr *temp;
3740 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3741 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3742 return(-1);
3744 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3745 sizeof(xmlNodePtr));
3746 if (temp == NULL) {
3747 xmlXPathErrMemory(NULL, "growing nodeset\n");
3748 return(-1);
3750 cur->nodeMax *= 2;
3751 cur->nodeTab = temp;
3753 if (val->type == XML_NAMESPACE_DECL) {
3754 xmlNsPtr ns = (xmlNsPtr) val;
3756 /* TODO: Check memory error. */
3757 cur->nodeTab[cur->nodeNr++] =
3758 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3759 } else
3760 cur->nodeTab[cur->nodeNr++] = val;
3761 return(0);
3765 * xmlXPathNodeSetAddUnique:
3766 * @cur: the initial node set
3767 * @val: a new xmlNodePtr
3769 * add a new xmlNodePtr to an existing NodeSet, optimized version
3770 * when we are sure the node is not already in the set.
3772 * Returns 0 in case of success and -1 in case of failure
3775 xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
3776 if ((cur == NULL) || (val == NULL)) return(-1);
3778 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3780 * grow the nodeTab if needed
3782 if (cur->nodeMax == 0) {
3783 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3784 sizeof(xmlNodePtr));
3785 if (cur->nodeTab == NULL) {
3786 xmlXPathErrMemory(NULL, "growing nodeset\n");
3787 return(-1);
3789 memset(cur->nodeTab, 0 ,
3790 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3791 cur->nodeMax = XML_NODESET_DEFAULT;
3792 } else if (cur->nodeNr == cur->nodeMax) {
3793 xmlNodePtr *temp;
3795 if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3796 xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
3797 return(-1);
3799 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
3800 sizeof(xmlNodePtr));
3801 if (temp == NULL) {
3802 xmlXPathErrMemory(NULL, "growing nodeset\n");
3803 return(-1);
3805 cur->nodeTab = temp;
3806 cur->nodeMax *= 2;
3808 if (val->type == XML_NAMESPACE_DECL) {
3809 xmlNsPtr ns = (xmlNsPtr) val;
3811 /* TODO: Check memory error. */
3812 cur->nodeTab[cur->nodeNr++] =
3813 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3814 } else
3815 cur->nodeTab[cur->nodeNr++] = val;
3816 return(0);
3820 * xmlXPathNodeSetMerge:
3821 * @val1: the first NodeSet or NULL
3822 * @val2: the second NodeSet
3824 * Merges two nodesets, all nodes from @val2 are added to @val1
3825 * if @val1 is NULL, a new set is created and copied from @val2
3827 * Returns @val1 once extended or NULL in case of error.
3829 xmlNodeSetPtr
3830 xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
3831 int i, j, initNr, skip;
3832 xmlNodePtr n1, n2;
3834 if (val2 == NULL) return(val1);
3835 if (val1 == NULL) {
3836 val1 = xmlXPathNodeSetCreate(NULL);
3837 if (val1 == NULL)
3838 return (NULL);
3839 #if 0
3841 * TODO: The optimization won't work in every case, since
3842 * those nasty namespace nodes need to be added with
3843 * xmlXPathNodeSetDupNs() to the set; thus a pure
3844 * memcpy is not possible.
3845 * If there was a flag on the nodesetval, indicating that
3846 * some temporary nodes are in, that would be helpful.
3849 * Optimization: Create an equally sized node-set
3850 * and memcpy the content.
3852 val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
3853 if (val1 == NULL)
3854 return(NULL);
3855 if (val2->nodeNr != 0) {
3856 if (val2->nodeNr == 1)
3857 *(val1->nodeTab) = *(val2->nodeTab);
3858 else {
3859 memcpy(val1->nodeTab, val2->nodeTab,
3860 val2->nodeNr * sizeof(xmlNodePtr));
3862 val1->nodeNr = val2->nodeNr;
3864 return(val1);
3865 #endif
3868 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3869 initNr = val1->nodeNr;
3871 for (i = 0;i < val2->nodeNr;i++) {
3872 n2 = val2->nodeTab[i];
3874 * check against duplicates
3876 skip = 0;
3877 for (j = 0; j < initNr; j++) {
3878 n1 = val1->nodeTab[j];
3879 if (n1 == n2) {
3880 skip = 1;
3881 break;
3882 } else if ((n1->type == XML_NAMESPACE_DECL) &&
3883 (n2->type == XML_NAMESPACE_DECL)) {
3884 if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3885 (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3886 ((xmlNsPtr) n2)->prefix)))
3888 skip = 1;
3889 break;
3893 if (skip)
3894 continue;
3897 * grow the nodeTab if needed
3899 if (val1->nodeMax == 0) {
3900 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3901 sizeof(xmlNodePtr));
3902 if (val1->nodeTab == NULL) {
3903 xmlXPathErrMemory(NULL, "merging nodeset\n");
3904 return(NULL);
3906 memset(val1->nodeTab, 0 ,
3907 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3908 val1->nodeMax = XML_NODESET_DEFAULT;
3909 } else if (val1->nodeNr == val1->nodeMax) {
3910 xmlNodePtr *temp;
3912 if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3913 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
3914 return(NULL);
3916 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
3917 sizeof(xmlNodePtr));
3918 if (temp == NULL) {
3919 xmlXPathErrMemory(NULL, "merging nodeset\n");
3920 return(NULL);
3922 val1->nodeTab = temp;
3923 val1->nodeMax *= 2;
3925 if (n2->type == XML_NAMESPACE_DECL) {
3926 xmlNsPtr ns = (xmlNsPtr) n2;
3928 /* TODO: Check memory error. */
3929 val1->nodeTab[val1->nodeNr++] =
3930 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3931 } else
3932 val1->nodeTab[val1->nodeNr++] = n2;
3935 return(val1);
3940 * xmlXPathNodeSetMergeAndClear:
3941 * @set1: the first NodeSet or NULL
3942 * @set2: the second NodeSet
3944 * Merges two nodesets, all nodes from @set2 are added to @set1.
3945 * Checks for duplicate nodes. Clears set2.
3947 * Returns @set1 once extended or NULL in case of error.
3949 static xmlNodeSetPtr
3950 xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
3953 int i, j, initNbSet1;
3954 xmlNodePtr n1, n2;
3956 initNbSet1 = set1->nodeNr;
3957 for (i = 0;i < set2->nodeNr;i++) {
3958 n2 = set2->nodeTab[i];
3960 * Skip duplicates.
3962 for (j = 0; j < initNbSet1; j++) {
3963 n1 = set1->nodeTab[j];
3964 if (n1 == n2) {
3965 goto skip_node;
3966 } else if ((n1->type == XML_NAMESPACE_DECL) &&
3967 (n2->type == XML_NAMESPACE_DECL))
3969 if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3970 (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3971 ((xmlNsPtr) n2)->prefix)))
3974 * Free the namespace node.
3976 set2->nodeTab[i] = NULL;
3977 xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
3978 goto skip_node;
3983 * grow the nodeTab if needed
3985 if (set1->nodeMax == 0) {
3986 set1->nodeTab = (xmlNodePtr *) xmlMalloc(
3987 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3988 if (set1->nodeTab == NULL) {
3989 xmlXPathErrMemory(NULL, "merging nodeset\n");
3990 return(NULL);
3992 memset(set1->nodeTab, 0,
3993 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3994 set1->nodeMax = XML_NODESET_DEFAULT;
3995 } else if (set1->nodeNr >= set1->nodeMax) {
3996 xmlNodePtr *temp;
3998 if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
3999 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
4000 return(NULL);
4002 temp = (xmlNodePtr *) xmlRealloc(
4003 set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
4004 if (temp == NULL) {
4005 xmlXPathErrMemory(NULL, "merging nodeset\n");
4006 return(NULL);
4008 set1->nodeTab = temp;
4009 set1->nodeMax *= 2;
4011 set1->nodeTab[set1->nodeNr++] = n2;
4012 skip_node:
4016 set2->nodeNr = 0;
4017 return(set1);
4021 * xmlXPathNodeSetMergeAndClearNoDupls:
4022 * @set1: the first NodeSet or NULL
4023 * @set2: the second NodeSet
4025 * Merges two nodesets, all nodes from @set2 are added to @set1.
4026 * Doesn't check for duplicate nodes. Clears set2.
4028 * Returns @set1 once extended or NULL in case of error.
4030 static xmlNodeSetPtr
4031 xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
4034 int i;
4035 xmlNodePtr n2;
4037 for (i = 0;i < set2->nodeNr;i++) {
4038 n2 = set2->nodeTab[i];
4039 if (set1->nodeMax == 0) {
4040 set1->nodeTab = (xmlNodePtr *) xmlMalloc(
4041 XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
4042 if (set1->nodeTab == NULL) {
4043 xmlXPathErrMemory(NULL, "merging nodeset\n");
4044 return(NULL);
4046 memset(set1->nodeTab, 0,
4047 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
4048 set1->nodeMax = XML_NODESET_DEFAULT;
4049 } else if (set1->nodeNr >= set1->nodeMax) {
4050 xmlNodePtr *temp;
4052 if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
4053 xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
4054 return(NULL);
4056 temp = (xmlNodePtr *) xmlRealloc(
4057 set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
4058 if (temp == NULL) {
4059 xmlXPathErrMemory(NULL, "merging nodeset\n");
4060 return(NULL);
4062 set1->nodeTab = temp;
4063 set1->nodeMax *= 2;
4065 set1->nodeTab[set1->nodeNr++] = n2;
4068 set2->nodeNr = 0;
4069 return(set1);
4073 * xmlXPathNodeSetDel:
4074 * @cur: the initial node set
4075 * @val: an xmlNodePtr
4077 * Removes an xmlNodePtr from an existing NodeSet
4079 void
4080 xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
4081 int i;
4083 if (cur == NULL) return;
4084 if (val == NULL) return;
4087 * find node in nodeTab
4089 for (i = 0;i < cur->nodeNr;i++)
4090 if (cur->nodeTab[i] == val) break;
4092 if (i >= cur->nodeNr) { /* not found */
4093 #ifdef DEBUG
4094 xmlGenericError(xmlGenericErrorContext,
4095 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
4096 val->name);
4097 #endif
4098 return;
4100 if ((cur->nodeTab[i] != NULL) &&
4101 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
4102 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
4103 cur->nodeNr--;
4104 for (;i < cur->nodeNr;i++)
4105 cur->nodeTab[i] = cur->nodeTab[i + 1];
4106 cur->nodeTab[cur->nodeNr] = NULL;
4110 * xmlXPathNodeSetRemove:
4111 * @cur: the initial node set
4112 * @val: the index to remove
4114 * Removes an entry from an existing NodeSet list.
4116 void
4117 xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
4118 if (cur == NULL) return;
4119 if (val >= cur->nodeNr) return;
4120 if ((cur->nodeTab[val] != NULL) &&
4121 (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
4122 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
4123 cur->nodeNr--;
4124 for (;val < cur->nodeNr;val++)
4125 cur->nodeTab[val] = cur->nodeTab[val + 1];
4126 cur->nodeTab[cur->nodeNr] = NULL;
4130 * xmlXPathFreeNodeSet:
4131 * @obj: the xmlNodeSetPtr to free
4133 * Free the NodeSet compound (not the actual nodes !).
4135 void
4136 xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
4137 if (obj == NULL) return;
4138 if (obj->nodeTab != NULL) {
4139 int i;
4141 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4142 for (i = 0;i < obj->nodeNr;i++)
4143 if ((obj->nodeTab[i] != NULL) &&
4144 (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
4145 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4146 xmlFree(obj->nodeTab);
4148 xmlFree(obj);
4152 * xmlXPathNodeSetClearFromPos:
4153 * @set: the node set to be cleared
4154 * @pos: the start position to clear from
4156 * Clears the list from temporary XPath objects (e.g. namespace nodes
4157 * are feed) starting with the entry at @pos, but does *not* free the list
4158 * itself. Sets the length of the list to @pos.
4160 static void
4161 xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
4163 if ((set == NULL) || (pos >= set->nodeNr))
4164 return;
4165 else if ((hasNsNodes)) {
4166 int i;
4167 xmlNodePtr node;
4169 for (i = pos; i < set->nodeNr; i++) {
4170 node = set->nodeTab[i];
4171 if ((node != NULL) &&
4172 (node->type == XML_NAMESPACE_DECL))
4173 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4176 set->nodeNr = pos;
4180 * xmlXPathNodeSetClear:
4181 * @set: the node set to clear
4183 * Clears the list from all temporary XPath objects (e.g. namespace nodes
4184 * are feed), but does *not* free the list itself. Sets the length of the
4185 * list to 0.
4187 static void
4188 xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
4190 xmlXPathNodeSetClearFromPos(set, 0, hasNsNodes);
4194 * xmlXPathNodeSetKeepLast:
4195 * @set: the node set to be cleared
4197 * Move the last node to the first position and clear temporary XPath objects
4198 * (e.g. namespace nodes) from all other nodes. Sets the length of the list
4199 * to 1.
4201 static void
4202 xmlXPathNodeSetKeepLast(xmlNodeSetPtr set)
4204 int i;
4205 xmlNodePtr node;
4207 if ((set == NULL) || (set->nodeNr <= 1))
4208 return;
4209 for (i = 0; i < set->nodeNr - 1; i++) {
4210 node = set->nodeTab[i];
4211 if ((node != NULL) &&
4212 (node->type == XML_NAMESPACE_DECL))
4213 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4215 set->nodeTab[0] = set->nodeTab[set->nodeNr-1];
4216 set->nodeNr = 1;
4220 * xmlXPathFreeValueTree:
4221 * @obj: the xmlNodeSetPtr to free
4223 * Free the NodeSet compound and the actual tree, this is different
4224 * from xmlXPathFreeNodeSet()
4226 static void
4227 xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
4228 int i;
4230 if (obj == NULL) return;
4232 if (obj->nodeTab != NULL) {
4233 for (i = 0;i < obj->nodeNr;i++) {
4234 if (obj->nodeTab[i] != NULL) {
4235 if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
4236 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4237 } else {
4238 xmlFreeNodeList(obj->nodeTab[i]);
4242 xmlFree(obj->nodeTab);
4244 xmlFree(obj);
4247 #if defined(DEBUG) || defined(DEBUG_STEP)
4249 * xmlGenericErrorContextNodeSet:
4250 * @output: a FILE * for the output
4251 * @obj: the xmlNodeSetPtr to display
4253 * Quick display of a NodeSet
4255 void
4256 xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
4257 int i;
4259 if (output == NULL) output = xmlGenericErrorContext;
4260 if (obj == NULL) {
4261 fprintf(output, "NodeSet == NULL !\n");
4262 return;
4264 if (obj->nodeNr == 0) {
4265 fprintf(output, "NodeSet is empty\n");
4266 return;
4268 if (obj->nodeTab == NULL) {
4269 fprintf(output, " nodeTab == NULL !\n");
4270 return;
4272 for (i = 0; i < obj->nodeNr; i++) {
4273 if (obj->nodeTab[i] == NULL) {
4274 fprintf(output, " NULL !\n");
4275 return;
4277 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
4278 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
4279 fprintf(output, " /");
4280 else if (obj->nodeTab[i]->name == NULL)
4281 fprintf(output, " noname!");
4282 else fprintf(output, " %s", obj->nodeTab[i]->name);
4284 fprintf(output, "\n");
4286 #endif
4289 * xmlXPathNewNodeSet:
4290 * @val: the NodePtr value
4292 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4293 * it with the single Node @val
4295 * Returns the newly created object.
4297 xmlXPathObjectPtr
4298 xmlXPathNewNodeSet(xmlNodePtr val) {
4299 xmlXPathObjectPtr ret;
4301 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4302 if (ret == NULL) {
4303 xmlXPathErrMemory(NULL, "creating nodeset\n");
4304 return(NULL);
4306 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4307 ret->type = XPATH_NODESET;
4308 ret->boolval = 0;
4309 /* TODO: Check memory error. */
4310 ret->nodesetval = xmlXPathNodeSetCreate(val);
4311 /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4312 #ifdef XP_DEBUG_OBJ_USAGE
4313 xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4314 #endif
4315 return(ret);
4319 * xmlXPathNewValueTree:
4320 * @val: the NodePtr value
4322 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
4323 * it with the tree root @val
4325 * Returns the newly created object.
4327 xmlXPathObjectPtr
4328 xmlXPathNewValueTree(xmlNodePtr val) {
4329 xmlXPathObjectPtr ret;
4331 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4332 if (ret == NULL) {
4333 xmlXPathErrMemory(NULL, "creating result value tree\n");
4334 return(NULL);
4336 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4337 ret->type = XPATH_XSLT_TREE;
4338 ret->boolval = 1;
4339 ret->user = (void *) val;
4340 ret->nodesetval = xmlXPathNodeSetCreate(val);
4341 #ifdef XP_DEBUG_OBJ_USAGE
4342 xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
4343 #endif
4344 return(ret);
4348 * xmlXPathNewNodeSetList:
4349 * @val: an existing NodeSet
4351 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4352 * it with the Nodeset @val
4354 * Returns the newly created object.
4356 xmlXPathObjectPtr
4357 xmlXPathNewNodeSetList(xmlNodeSetPtr val)
4359 xmlXPathObjectPtr ret;
4360 int i;
4362 if (val == NULL)
4363 ret = NULL;
4364 else if (val->nodeTab == NULL)
4365 ret = xmlXPathNewNodeSet(NULL);
4366 else {
4367 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
4368 if (ret) {
4369 for (i = 1; i < val->nodeNr; ++i) {
4370 /* TODO: Propagate memory error. */
4371 if (xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i])
4372 < 0) break;
4377 return (ret);
4381 * xmlXPathWrapNodeSet:
4382 * @val: the NodePtr value
4384 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
4386 * Returns the newly created object.
4388 xmlXPathObjectPtr
4389 xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
4390 xmlXPathObjectPtr ret;
4392 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4393 if (ret == NULL) {
4394 xmlXPathErrMemory(NULL, "creating node set object\n");
4395 return(NULL);
4397 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4398 ret->type = XPATH_NODESET;
4399 ret->nodesetval = val;
4400 #ifdef XP_DEBUG_OBJ_USAGE
4401 xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4402 #endif
4403 return(ret);
4407 * xmlXPathFreeNodeSetList:
4408 * @obj: an existing NodeSetList object
4410 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
4411 * the list contrary to xmlXPathFreeObject().
4413 void
4414 xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
4415 if (obj == NULL) return;
4416 #ifdef XP_DEBUG_OBJ_USAGE
4417 xmlXPathDebugObjUsageReleased(NULL, obj->type);
4418 #endif
4419 xmlFree(obj);
4423 * xmlXPathDifference:
4424 * @nodes1: a node-set
4425 * @nodes2: a node-set
4427 * Implements the EXSLT - Sets difference() function:
4428 * node-set set:difference (node-set, node-set)
4430 * Returns the difference between the two node sets, or nodes1 if
4431 * nodes2 is empty
4433 xmlNodeSetPtr
4434 xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4435 xmlNodeSetPtr ret;
4436 int i, l1;
4437 xmlNodePtr cur;
4439 if (xmlXPathNodeSetIsEmpty(nodes2))
4440 return(nodes1);
4442 /* TODO: Check memory error. */
4443 ret = xmlXPathNodeSetCreate(NULL);
4444 if (xmlXPathNodeSetIsEmpty(nodes1))
4445 return(ret);
4447 l1 = xmlXPathNodeSetGetLength(nodes1);
4449 for (i = 0; i < l1; i++) {
4450 cur = xmlXPathNodeSetItem(nodes1, i);
4451 if (!xmlXPathNodeSetContains(nodes2, cur)) {
4452 /* TODO: Propagate memory error. */
4453 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4454 break;
4457 return(ret);
4461 * xmlXPathIntersection:
4462 * @nodes1: a node-set
4463 * @nodes2: a node-set
4465 * Implements the EXSLT - Sets intersection() function:
4466 * node-set set:intersection (node-set, node-set)
4468 * Returns a node set comprising the nodes that are within both the
4469 * node sets passed as arguments
4471 xmlNodeSetPtr
4472 xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4473 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
4474 int i, l1;
4475 xmlNodePtr cur;
4477 if (ret == NULL)
4478 return(ret);
4479 if (xmlXPathNodeSetIsEmpty(nodes1))
4480 return(ret);
4481 if (xmlXPathNodeSetIsEmpty(nodes2))
4482 return(ret);
4484 l1 = xmlXPathNodeSetGetLength(nodes1);
4486 for (i = 0; i < l1; i++) {
4487 cur = xmlXPathNodeSetItem(nodes1, i);
4488 if (xmlXPathNodeSetContains(nodes2, cur)) {
4489 /* TODO: Propagate memory error. */
4490 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4491 break;
4494 return(ret);
4498 * xmlXPathDistinctSorted:
4499 * @nodes: a node-set, sorted by document order
4501 * Implements the EXSLT - Sets distinct() function:
4502 * node-set set:distinct (node-set)
4504 * Returns a subset of the nodes contained in @nodes, or @nodes if
4505 * it is empty
4507 xmlNodeSetPtr
4508 xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
4509 xmlNodeSetPtr ret;
4510 xmlHashTablePtr hash;
4511 int i, l;
4512 xmlChar * strval;
4513 xmlNodePtr cur;
4515 if (xmlXPathNodeSetIsEmpty(nodes))
4516 return(nodes);
4518 ret = xmlXPathNodeSetCreate(NULL);
4519 if (ret == NULL)
4520 return(ret);
4521 l = xmlXPathNodeSetGetLength(nodes);
4522 hash = xmlHashCreate (l);
4523 for (i = 0; i < l; i++) {
4524 cur = xmlXPathNodeSetItem(nodes, i);
4525 strval = xmlXPathCastNodeToString(cur);
4526 if (xmlHashLookup(hash, strval) == NULL) {
4527 xmlHashAddEntry(hash, strval, strval);
4528 /* TODO: Propagate memory error. */
4529 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4530 break;
4531 } else {
4532 xmlFree(strval);
4535 xmlHashFree(hash, xmlHashDefaultDeallocator);
4536 return(ret);
4540 * xmlXPathDistinct:
4541 * @nodes: a node-set
4543 * Implements the EXSLT - Sets distinct() function:
4544 * node-set set:distinct (node-set)
4545 * @nodes is sorted by document order, then #exslSetsDistinctSorted
4546 * is called with the sorted node-set
4548 * Returns a subset of the nodes contained in @nodes, or @nodes if
4549 * it is empty
4551 xmlNodeSetPtr
4552 xmlXPathDistinct (xmlNodeSetPtr nodes) {
4553 if (xmlXPathNodeSetIsEmpty(nodes))
4554 return(nodes);
4556 xmlXPathNodeSetSort(nodes);
4557 return(xmlXPathDistinctSorted(nodes));
4561 * xmlXPathHasSameNodes:
4562 * @nodes1: a node-set
4563 * @nodes2: a node-set
4565 * Implements the EXSLT - Sets has-same-nodes function:
4566 * boolean set:has-same-node(node-set, node-set)
4568 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
4569 * otherwise
4572 xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4573 int i, l;
4574 xmlNodePtr cur;
4576 if (xmlXPathNodeSetIsEmpty(nodes1) ||
4577 xmlXPathNodeSetIsEmpty(nodes2))
4578 return(0);
4580 l = xmlXPathNodeSetGetLength(nodes1);
4581 for (i = 0; i < l; i++) {
4582 cur = xmlXPathNodeSetItem(nodes1, i);
4583 if (xmlXPathNodeSetContains(nodes2, cur))
4584 return(1);
4586 return(0);
4590 * xmlXPathNodeLeadingSorted:
4591 * @nodes: a node-set, sorted by document order
4592 * @node: a node
4594 * Implements the EXSLT - Sets leading() function:
4595 * node-set set:leading (node-set, node-set)
4597 * Returns the nodes in @nodes that precede @node in document order,
4598 * @nodes if @node is NULL or an empty node-set if @nodes
4599 * doesn't contain @node
4601 xmlNodeSetPtr
4602 xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4603 int i, l;
4604 xmlNodePtr cur;
4605 xmlNodeSetPtr ret;
4607 if (node == NULL)
4608 return(nodes);
4610 ret = xmlXPathNodeSetCreate(NULL);
4611 if (ret == NULL)
4612 return(ret);
4613 if (xmlXPathNodeSetIsEmpty(nodes) ||
4614 (!xmlXPathNodeSetContains(nodes, node)))
4615 return(ret);
4617 l = xmlXPathNodeSetGetLength(nodes);
4618 for (i = 0; i < l; i++) {
4619 cur = xmlXPathNodeSetItem(nodes, i);
4620 if (cur == node)
4621 break;
4622 /* TODO: Propagate memory error. */
4623 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4624 break;
4626 return(ret);
4630 * xmlXPathNodeLeading:
4631 * @nodes: a node-set
4632 * @node: a node
4634 * Implements the EXSLT - Sets leading() function:
4635 * node-set set:leading (node-set, node-set)
4636 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
4637 * is called.
4639 * Returns the nodes in @nodes that precede @node in document order,
4640 * @nodes if @node is NULL or an empty node-set if @nodes
4641 * doesn't contain @node
4643 xmlNodeSetPtr
4644 xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
4645 xmlXPathNodeSetSort(nodes);
4646 return(xmlXPathNodeLeadingSorted(nodes, node));
4650 * xmlXPathLeadingSorted:
4651 * @nodes1: a node-set, sorted by document order
4652 * @nodes2: a node-set, sorted by document order
4654 * Implements the EXSLT - Sets leading() function:
4655 * node-set set:leading (node-set, node-set)
4657 * Returns the nodes in @nodes1 that precede the first node in @nodes2
4658 * in document order, @nodes1 if @nodes2 is NULL or empty or
4659 * an empty node-set if @nodes1 doesn't contain @nodes2
4661 xmlNodeSetPtr
4662 xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4663 if (xmlXPathNodeSetIsEmpty(nodes2))
4664 return(nodes1);
4665 return(xmlXPathNodeLeadingSorted(nodes1,
4666 xmlXPathNodeSetItem(nodes2, 1)));
4670 * xmlXPathLeading:
4671 * @nodes1: a node-set
4672 * @nodes2: a node-set
4674 * Implements the EXSLT - Sets leading() function:
4675 * node-set set:leading (node-set, node-set)
4676 * @nodes1 and @nodes2 are sorted by document order, then
4677 * #exslSetsLeadingSorted is called.
4679 * Returns the nodes in @nodes1 that precede the first node in @nodes2
4680 * in document order, @nodes1 if @nodes2 is NULL or empty or
4681 * an empty node-set if @nodes1 doesn't contain @nodes2
4683 xmlNodeSetPtr
4684 xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4685 if (xmlXPathNodeSetIsEmpty(nodes2))
4686 return(nodes1);
4687 if (xmlXPathNodeSetIsEmpty(nodes1))
4688 return(xmlXPathNodeSetCreate(NULL));
4689 xmlXPathNodeSetSort(nodes1);
4690 xmlXPathNodeSetSort(nodes2);
4691 return(xmlXPathNodeLeadingSorted(nodes1,
4692 xmlXPathNodeSetItem(nodes2, 1)));
4696 * xmlXPathNodeTrailingSorted:
4697 * @nodes: a node-set, sorted by document order
4698 * @node: a node
4700 * Implements the EXSLT - Sets trailing() function:
4701 * node-set set:trailing (node-set, node-set)
4703 * Returns the nodes in @nodes that follow @node in document order,
4704 * @nodes if @node is NULL or an empty node-set if @nodes
4705 * doesn't contain @node
4707 xmlNodeSetPtr
4708 xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4709 int i, l;
4710 xmlNodePtr cur;
4711 xmlNodeSetPtr ret;
4713 if (node == NULL)
4714 return(nodes);
4716 ret = xmlXPathNodeSetCreate(NULL);
4717 if (ret == NULL)
4718 return(ret);
4719 if (xmlXPathNodeSetIsEmpty(nodes) ||
4720 (!xmlXPathNodeSetContains(nodes, node)))
4721 return(ret);
4723 l = xmlXPathNodeSetGetLength(nodes);
4724 for (i = l - 1; i >= 0; i--) {
4725 cur = xmlXPathNodeSetItem(nodes, i);
4726 if (cur == node)
4727 break;
4728 /* TODO: Propagate memory error. */
4729 if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
4730 break;
4732 xmlXPathNodeSetSort(ret); /* bug 413451 */
4733 return(ret);
4737 * xmlXPathNodeTrailing:
4738 * @nodes: a node-set
4739 * @node: a node
4741 * Implements the EXSLT - Sets trailing() function:
4742 * node-set set:trailing (node-set, node-set)
4743 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
4744 * is called.
4746 * Returns the nodes in @nodes that follow @node in document order,
4747 * @nodes if @node is NULL or an empty node-set if @nodes
4748 * doesn't contain @node
4750 xmlNodeSetPtr
4751 xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
4752 xmlXPathNodeSetSort(nodes);
4753 return(xmlXPathNodeTrailingSorted(nodes, node));
4757 * xmlXPathTrailingSorted:
4758 * @nodes1: a node-set, sorted by document order
4759 * @nodes2: a node-set, sorted by document order
4761 * Implements the EXSLT - Sets trailing() function:
4762 * node-set set:trailing (node-set, node-set)
4764 * Returns the nodes in @nodes1 that follow the first node in @nodes2
4765 * in document order, @nodes1 if @nodes2 is NULL or empty or
4766 * an empty node-set if @nodes1 doesn't contain @nodes2
4768 xmlNodeSetPtr
4769 xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4770 if (xmlXPathNodeSetIsEmpty(nodes2))
4771 return(nodes1);
4772 return(xmlXPathNodeTrailingSorted(nodes1,
4773 xmlXPathNodeSetItem(nodes2, 0)));
4777 * xmlXPathTrailing:
4778 * @nodes1: a node-set
4779 * @nodes2: a node-set
4781 * Implements the EXSLT - Sets trailing() function:
4782 * node-set set:trailing (node-set, node-set)
4783 * @nodes1 and @nodes2 are sorted by document order, then
4784 * #xmlXPathTrailingSorted is called.
4786 * Returns the nodes in @nodes1 that follow the first node in @nodes2
4787 * in document order, @nodes1 if @nodes2 is NULL or empty or
4788 * an empty node-set if @nodes1 doesn't contain @nodes2
4790 xmlNodeSetPtr
4791 xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4792 if (xmlXPathNodeSetIsEmpty(nodes2))
4793 return(nodes1);
4794 if (xmlXPathNodeSetIsEmpty(nodes1))
4795 return(xmlXPathNodeSetCreate(NULL));
4796 xmlXPathNodeSetSort(nodes1);
4797 xmlXPathNodeSetSort(nodes2);
4798 return(xmlXPathNodeTrailingSorted(nodes1,
4799 xmlXPathNodeSetItem(nodes2, 0)));
4802 /************************************************************************
4804 * Routines to handle extra functions *
4806 ************************************************************************/
4809 * xmlXPathRegisterFunc:
4810 * @ctxt: the XPath context
4811 * @name: the function name
4812 * @f: the function implementation or NULL
4814 * Register a new function. If @f is NULL it unregisters the function
4816 * Returns 0 in case of success, -1 in case of error
4819 xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
4820 xmlXPathFunction f) {
4821 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
4825 * xmlXPathRegisterFuncNS:
4826 * @ctxt: the XPath context
4827 * @name: the function name
4828 * @ns_uri: the function namespace URI
4829 * @f: the function implementation or NULL
4831 * Register a new function. If @f is NULL it unregisters the function
4833 * Returns 0 in case of success, -1 in case of error
4836 xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4837 const xmlChar *ns_uri, xmlXPathFunction f) {
4838 if (ctxt == NULL)
4839 return(-1);
4840 if (name == NULL)
4841 return(-1);
4843 if (ctxt->funcHash == NULL)
4844 ctxt->funcHash = xmlHashCreate(0);
4845 if (ctxt->funcHash == NULL)
4846 return(-1);
4847 if (f == NULL)
4848 return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
4849 XML_IGNORE_PEDANTIC_WARNINGS
4850 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
4851 XML_POP_WARNINGS
4855 * xmlXPathRegisterFuncLookup:
4856 * @ctxt: the XPath context
4857 * @f: the lookup function
4858 * @funcCtxt: the lookup data
4860 * Registers an external mechanism to do function lookup.
4862 void
4863 xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
4864 xmlXPathFuncLookupFunc f,
4865 void *funcCtxt) {
4866 if (ctxt == NULL)
4867 return;
4868 ctxt->funcLookupFunc = f;
4869 ctxt->funcLookupData = funcCtxt;
4873 * xmlXPathFunctionLookup:
4874 * @ctxt: the XPath context
4875 * @name: the function name
4877 * Search in the Function array of the context for the given
4878 * function.
4880 * Returns the xmlXPathFunction or NULL if not found
4882 xmlXPathFunction
4883 xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
4884 if (ctxt == NULL)
4885 return (NULL);
4887 if (ctxt->funcLookupFunc != NULL) {
4888 xmlXPathFunction ret;
4889 xmlXPathFuncLookupFunc f;
4891 f = ctxt->funcLookupFunc;
4892 ret = f(ctxt->funcLookupData, name, NULL);
4893 if (ret != NULL)
4894 return(ret);
4896 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
4900 * xmlXPathFunctionLookupNS:
4901 * @ctxt: the XPath context
4902 * @name: the function name
4903 * @ns_uri: the function namespace URI
4905 * Search in the Function array of the context for the given
4906 * function.
4908 * Returns the xmlXPathFunction or NULL if not found
4910 xmlXPathFunction
4911 xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4912 const xmlChar *ns_uri) {
4913 xmlXPathFunction ret;
4915 if (ctxt == NULL)
4916 return(NULL);
4917 if (name == NULL)
4918 return(NULL);
4920 if (ctxt->funcLookupFunc != NULL) {
4921 xmlXPathFuncLookupFunc f;
4923 f = ctxt->funcLookupFunc;
4924 ret = f(ctxt->funcLookupData, name, ns_uri);
4925 if (ret != NULL)
4926 return(ret);
4929 if (ctxt->funcHash == NULL)
4930 return(NULL);
4932 XML_IGNORE_PEDANTIC_WARNINGS
4933 ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
4934 XML_POP_WARNINGS
4935 return(ret);
4939 * xmlXPathRegisteredFuncsCleanup:
4940 * @ctxt: the XPath context
4942 * Cleanup the XPath context data associated to registered functions
4944 void
4945 xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
4946 if (ctxt == NULL)
4947 return;
4949 xmlHashFree(ctxt->funcHash, NULL);
4950 ctxt->funcHash = NULL;
4953 /************************************************************************
4955 * Routines to handle Variables *
4957 ************************************************************************/
4960 * xmlXPathRegisterVariable:
4961 * @ctxt: the XPath context
4962 * @name: the variable name
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 xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
4972 xmlXPathObjectPtr value) {
4973 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
4977 * xmlXPathRegisterVariableNS:
4978 * @ctxt: the XPath context
4979 * @name: the variable name
4980 * @ns_uri: the variable namespace URI
4981 * @value: the variable value or NULL
4983 * Register a new variable value. If @value is NULL it unregisters
4984 * the variable
4986 * Returns 0 in case of success, -1 in case of error
4989 xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4990 const xmlChar *ns_uri,
4991 xmlXPathObjectPtr value) {
4992 if (ctxt == NULL)
4993 return(-1);
4994 if (name == NULL)
4995 return(-1);
4997 if (ctxt->varHash == NULL)
4998 ctxt->varHash = xmlHashCreate(0);
4999 if (ctxt->varHash == NULL)
5000 return(-1);
5001 if (value == NULL)
5002 return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
5003 xmlXPathFreeObjectEntry));
5004 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
5005 (void *) value, xmlXPathFreeObjectEntry));
5009 * xmlXPathRegisterVariableLookup:
5010 * @ctxt: the XPath context
5011 * @f: the lookup function
5012 * @data: the lookup data
5014 * register an external mechanism to do variable lookup
5016 void
5017 xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
5018 xmlXPathVariableLookupFunc f, void *data) {
5019 if (ctxt == NULL)
5020 return;
5021 ctxt->varLookupFunc = f;
5022 ctxt->varLookupData = data;
5026 * xmlXPathVariableLookup:
5027 * @ctxt: the XPath context
5028 * @name: the variable name
5030 * Search in the Variable array of the context for the given
5031 * variable value.
5033 * Returns a copy of the value or NULL if not found
5035 xmlXPathObjectPtr
5036 xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
5037 if (ctxt == NULL)
5038 return(NULL);
5040 if (ctxt->varLookupFunc != NULL) {
5041 xmlXPathObjectPtr ret;
5043 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5044 (ctxt->varLookupData, name, NULL);
5045 return(ret);
5047 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
5051 * xmlXPathVariableLookupNS:
5052 * @ctxt: the XPath context
5053 * @name: the variable name
5054 * @ns_uri: the variable namespace URI
5056 * Search in the Variable array of the context for the given
5057 * variable value.
5059 * Returns the a copy of the value or NULL if not found
5061 xmlXPathObjectPtr
5062 xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
5063 const xmlChar *ns_uri) {
5064 if (ctxt == NULL)
5065 return(NULL);
5067 if (ctxt->varLookupFunc != NULL) {
5068 xmlXPathObjectPtr ret;
5070 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5071 (ctxt->varLookupData, name, ns_uri);
5072 if (ret != NULL) return(ret);
5075 if (ctxt->varHash == NULL)
5076 return(NULL);
5077 if (name == NULL)
5078 return(NULL);
5080 return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
5081 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
5085 * xmlXPathRegisteredVariablesCleanup:
5086 * @ctxt: the XPath context
5088 * Cleanup the XPath context data associated to registered variables
5090 void
5091 xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
5092 if (ctxt == NULL)
5093 return;
5095 xmlHashFree(ctxt->varHash, xmlXPathFreeObjectEntry);
5096 ctxt->varHash = NULL;
5100 * xmlXPathRegisterNs:
5101 * @ctxt: the XPath context
5102 * @prefix: the namespace prefix cannot be NULL or empty string
5103 * @ns_uri: the namespace name
5105 * Register a new namespace. If @ns_uri is NULL it unregisters
5106 * the namespace
5108 * Returns 0 in case of success, -1 in case of error
5111 xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
5112 const xmlChar *ns_uri) {
5113 if (ctxt == NULL)
5114 return(-1);
5115 if (prefix == NULL)
5116 return(-1);
5117 if (prefix[0] == 0)
5118 return(-1);
5120 if (ctxt->nsHash == NULL)
5121 ctxt->nsHash = xmlHashCreate(10);
5122 if (ctxt->nsHash == NULL)
5123 return(-1);
5124 if (ns_uri == NULL)
5125 return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
5126 xmlHashDefaultDeallocator));
5127 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
5128 xmlHashDefaultDeallocator));
5132 * xmlXPathNsLookup:
5133 * @ctxt: the XPath context
5134 * @prefix: the namespace prefix value
5136 * Search in the namespace declaration array of the context for the given
5137 * namespace name associated to the given prefix
5139 * Returns the value or NULL if not found
5141 const xmlChar *
5142 xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
5143 if (ctxt == NULL)
5144 return(NULL);
5145 if (prefix == NULL)
5146 return(NULL);
5148 #ifdef XML_XML_NAMESPACE
5149 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
5150 return(XML_XML_NAMESPACE);
5151 #endif
5153 if (ctxt->namespaces != NULL) {
5154 int i;
5156 for (i = 0;i < ctxt->nsNr;i++) {
5157 if ((ctxt->namespaces[i] != NULL) &&
5158 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
5159 return(ctxt->namespaces[i]->href);
5163 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
5167 * xmlXPathRegisteredNsCleanup:
5168 * @ctxt: the XPath context
5170 * Cleanup the XPath context data associated to registered variables
5172 void
5173 xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
5174 if (ctxt == NULL)
5175 return;
5177 xmlHashFree(ctxt->nsHash, xmlHashDefaultDeallocator);
5178 ctxt->nsHash = NULL;
5181 /************************************************************************
5183 * Routines to handle Values *
5185 ************************************************************************/
5187 /* Allocations are terrible, one needs to optimize all this !!! */
5190 * xmlXPathNewFloat:
5191 * @val: the double value
5193 * Create a new xmlXPathObjectPtr of type double and of value @val
5195 * Returns the newly created object.
5197 xmlXPathObjectPtr
5198 xmlXPathNewFloat(double val) {
5199 xmlXPathObjectPtr ret;
5201 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5202 if (ret == NULL) {
5203 xmlXPathErrMemory(NULL, "creating float object\n");
5204 return(NULL);
5206 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5207 ret->type = XPATH_NUMBER;
5208 ret->floatval = val;
5209 #ifdef XP_DEBUG_OBJ_USAGE
5210 xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
5211 #endif
5212 return(ret);
5216 * xmlXPathNewBoolean:
5217 * @val: the boolean value
5219 * Create a new xmlXPathObjectPtr of type boolean and of value @val
5221 * Returns the newly created object.
5223 xmlXPathObjectPtr
5224 xmlXPathNewBoolean(int val) {
5225 xmlXPathObjectPtr ret;
5227 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5228 if (ret == NULL) {
5229 xmlXPathErrMemory(NULL, "creating boolean object\n");
5230 return(NULL);
5232 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5233 ret->type = XPATH_BOOLEAN;
5234 ret->boolval = (val != 0);
5235 #ifdef XP_DEBUG_OBJ_USAGE
5236 xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
5237 #endif
5238 return(ret);
5242 * xmlXPathNewString:
5243 * @val: the xmlChar * value
5245 * Create a new xmlXPathObjectPtr of type string and of value @val
5247 * Returns the newly created object.
5249 xmlXPathObjectPtr
5250 xmlXPathNewString(const xmlChar *val) {
5251 xmlXPathObjectPtr ret;
5253 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5254 if (ret == NULL) {
5255 xmlXPathErrMemory(NULL, "creating string object\n");
5256 return(NULL);
5258 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5259 ret->type = XPATH_STRING;
5260 if (val != NULL)
5261 ret->stringval = xmlStrdup(val);
5262 else
5263 ret->stringval = xmlStrdup((const xmlChar *)"");
5264 #ifdef XP_DEBUG_OBJ_USAGE
5265 xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5266 #endif
5267 return(ret);
5271 * xmlXPathWrapString:
5272 * @val: the xmlChar * value
5274 * Wraps the @val string into an XPath object.
5276 * Returns the newly created object.
5278 xmlXPathObjectPtr
5279 xmlXPathWrapString (xmlChar *val) {
5280 xmlXPathObjectPtr ret;
5282 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5283 if (ret == NULL) {
5284 xmlXPathErrMemory(NULL, "creating string object\n");
5285 return(NULL);
5287 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5288 ret->type = XPATH_STRING;
5289 ret->stringval = val;
5290 #ifdef XP_DEBUG_OBJ_USAGE
5291 xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5292 #endif
5293 return(ret);
5297 * xmlXPathNewCString:
5298 * @val: the char * value
5300 * Create a new xmlXPathObjectPtr of type string and of value @val
5302 * Returns the newly created object.
5304 xmlXPathObjectPtr
5305 xmlXPathNewCString(const char *val) {
5306 xmlXPathObjectPtr ret;
5308 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5309 if (ret == NULL) {
5310 xmlXPathErrMemory(NULL, "creating string object\n");
5311 return(NULL);
5313 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5314 ret->type = XPATH_STRING;
5315 ret->stringval = xmlStrdup(BAD_CAST val);
5316 #ifdef XP_DEBUG_OBJ_USAGE
5317 xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5318 #endif
5319 return(ret);
5323 * xmlXPathWrapCString:
5324 * @val: the char * value
5326 * Wraps a string into an XPath object.
5328 * Returns the newly created object.
5330 xmlXPathObjectPtr
5331 xmlXPathWrapCString (char * val) {
5332 return(xmlXPathWrapString((xmlChar *)(val)));
5336 * xmlXPathWrapExternal:
5337 * @val: the user data
5339 * Wraps the @val data into an XPath object.
5341 * Returns the newly created object.
5343 xmlXPathObjectPtr
5344 xmlXPathWrapExternal (void *val) {
5345 xmlXPathObjectPtr ret;
5347 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5348 if (ret == NULL) {
5349 xmlXPathErrMemory(NULL, "creating user object\n");
5350 return(NULL);
5352 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5353 ret->type = XPATH_USERS;
5354 ret->user = val;
5355 #ifdef XP_DEBUG_OBJ_USAGE
5356 xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
5357 #endif
5358 return(ret);
5362 * xmlXPathObjectCopy:
5363 * @val: the original object
5365 * allocate a new copy of a given object
5367 * Returns the newly created object.
5369 xmlXPathObjectPtr
5370 xmlXPathObjectCopy(xmlXPathObjectPtr val) {
5371 xmlXPathObjectPtr ret;
5373 if (val == NULL)
5374 return(NULL);
5376 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5377 if (ret == NULL) {
5378 xmlXPathErrMemory(NULL, "copying object\n");
5379 return(NULL);
5381 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
5382 #ifdef XP_DEBUG_OBJ_USAGE
5383 xmlXPathDebugObjUsageRequested(NULL, val->type);
5384 #endif
5385 switch (val->type) {
5386 case XPATH_BOOLEAN:
5387 case XPATH_NUMBER:
5388 case XPATH_POINT:
5389 case XPATH_RANGE:
5390 break;
5391 case XPATH_STRING:
5392 ret->stringval = xmlStrdup(val->stringval);
5393 break;
5394 case XPATH_XSLT_TREE:
5395 #if 0
5397 Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
5398 this previous handling is no longer correct, and can cause some serious
5399 problems (ref. bug 145547)
5401 if ((val->nodesetval != NULL) &&
5402 (val->nodesetval->nodeTab != NULL)) {
5403 xmlNodePtr cur, tmp;
5404 xmlDocPtr top;
5406 ret->boolval = 1;
5407 top = xmlNewDoc(NULL);
5408 top->name = (char *)
5409 xmlStrdup(val->nodesetval->nodeTab[0]->name);
5410 ret->user = top;
5411 if (top != NULL) {
5412 top->doc = top;
5413 cur = val->nodesetval->nodeTab[0]->children;
5414 while (cur != NULL) {
5415 tmp = xmlDocCopyNode(cur, top, 1);
5416 xmlAddChild((xmlNodePtr) top, tmp);
5417 cur = cur->next;
5421 ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
5422 } else
5423 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
5424 /* Deallocate the copied tree value */
5425 break;
5426 #endif
5427 case XPATH_NODESET:
5428 /* TODO: Check memory error. */
5429 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
5430 /* Do not deallocate the copied tree value */
5431 ret->boolval = 0;
5432 break;
5433 case XPATH_LOCATIONSET:
5434 #ifdef LIBXML_XPTR_ENABLED
5436 xmlLocationSetPtr loc = val->user;
5437 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
5438 break;
5440 #endif
5441 case XPATH_USERS:
5442 ret->user = val->user;
5443 break;
5444 case XPATH_UNDEFINED:
5445 xmlGenericError(xmlGenericErrorContext,
5446 "xmlXPathObjectCopy: unsupported type %d\n",
5447 val->type);
5448 break;
5450 return(ret);
5454 * xmlXPathFreeObject:
5455 * @obj: the object to free
5457 * Free up an xmlXPathObjectPtr object.
5459 void
5460 xmlXPathFreeObject(xmlXPathObjectPtr obj) {
5461 if (obj == NULL) return;
5462 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
5463 if (obj->boolval) {
5464 #if 0
5465 if (obj->user != NULL) {
5466 xmlXPathFreeNodeSet(obj->nodesetval);
5467 xmlFreeNodeList((xmlNodePtr) obj->user);
5468 } else
5469 #endif
5470 obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
5471 if (obj->nodesetval != NULL)
5472 xmlXPathFreeValueTree(obj->nodesetval);
5473 } else {
5474 if (obj->nodesetval != NULL)
5475 xmlXPathFreeNodeSet(obj->nodesetval);
5477 #ifdef LIBXML_XPTR_ENABLED
5478 } else if (obj->type == XPATH_LOCATIONSET) {
5479 if (obj->user != NULL)
5480 xmlXPtrFreeLocationSet(obj->user);
5481 #endif
5482 } else if (obj->type == XPATH_STRING) {
5483 if (obj->stringval != NULL)
5484 xmlFree(obj->stringval);
5486 #ifdef XP_DEBUG_OBJ_USAGE
5487 xmlXPathDebugObjUsageReleased(NULL, obj->type);
5488 #endif
5489 xmlFree(obj);
5492 static void
5493 xmlXPathFreeObjectEntry(void *obj, const xmlChar *name ATTRIBUTE_UNUSED) {
5494 xmlXPathFreeObject((xmlXPathObjectPtr) obj);
5498 * xmlXPathReleaseObject:
5499 * @obj: the xmlXPathObjectPtr to free or to cache
5501 * Depending on the state of the cache this frees the given
5502 * XPath object or stores it in the cache.
5504 static void
5505 xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
5507 #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
5508 sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
5509 if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
5511 #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
5513 if (obj == NULL)
5514 return;
5515 if ((ctxt == NULL) || (ctxt->cache == NULL)) {
5516 xmlXPathFreeObject(obj);
5517 } else {
5518 xmlXPathContextCachePtr cache =
5519 (xmlXPathContextCachePtr) ctxt->cache;
5521 switch (obj->type) {
5522 case XPATH_NODESET:
5523 case XPATH_XSLT_TREE:
5524 if (obj->nodesetval != NULL) {
5525 if (obj->boolval) {
5527 * It looks like the @boolval is used for
5528 * evaluation if this an XSLT Result Tree Fragment.
5529 * TODO: Check if this assumption is correct.
5531 obj->type = XPATH_XSLT_TREE; /* just for debugging */
5532 xmlXPathFreeValueTree(obj->nodesetval);
5533 obj->nodesetval = NULL;
5534 } else if ((obj->nodesetval->nodeMax <= 40) &&
5535 (XP_CACHE_WANTS(cache->nodesetObjs,
5536 cache->maxNodeset)))
5538 XP_CACHE_ADD(cache->nodesetObjs, obj);
5539 goto obj_cached;
5540 } else {
5541 xmlXPathFreeNodeSet(obj->nodesetval);
5542 obj->nodesetval = NULL;
5545 break;
5546 case XPATH_STRING:
5547 if (obj->stringval != NULL)
5548 xmlFree(obj->stringval);
5550 if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
5551 XP_CACHE_ADD(cache->stringObjs, obj);
5552 goto obj_cached;
5554 break;
5555 case XPATH_BOOLEAN:
5556 if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
5557 XP_CACHE_ADD(cache->booleanObjs, obj);
5558 goto obj_cached;
5560 break;
5561 case XPATH_NUMBER:
5562 if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
5563 XP_CACHE_ADD(cache->numberObjs, obj);
5564 goto obj_cached;
5566 break;
5567 #ifdef LIBXML_XPTR_ENABLED
5568 case XPATH_LOCATIONSET:
5569 if (obj->user != NULL) {
5570 xmlXPtrFreeLocationSet(obj->user);
5572 goto free_obj;
5573 #endif
5574 default:
5575 goto free_obj;
5579 * Fallback to adding to the misc-objects slot.
5581 if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
5582 XP_CACHE_ADD(cache->miscObjs, obj);
5583 } else
5584 goto free_obj;
5586 obj_cached:
5588 #ifdef XP_DEBUG_OBJ_USAGE
5589 xmlXPathDebugObjUsageReleased(ctxt, obj->type);
5590 #endif
5592 if (obj->nodesetval != NULL) {
5593 xmlNodeSetPtr tmpset = obj->nodesetval;
5596 * TODO: Due to those nasty ns-nodes, we need to traverse
5597 * the list and free the ns-nodes.
5598 * URGENT TODO: Check if it's actually slowing things down.
5599 * Maybe we shouldn't try to preserve the list.
5601 if (tmpset->nodeNr > 1) {
5602 int i;
5603 xmlNodePtr node;
5605 for (i = 0; i < tmpset->nodeNr; i++) {
5606 node = tmpset->nodeTab[i];
5607 if ((node != NULL) &&
5608 (node->type == XML_NAMESPACE_DECL))
5610 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
5613 } else if (tmpset->nodeNr == 1) {
5614 if ((tmpset->nodeTab[0] != NULL) &&
5615 (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
5616 xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
5618 tmpset->nodeNr = 0;
5619 memset(obj, 0, sizeof(xmlXPathObject));
5620 obj->nodesetval = tmpset;
5621 } else
5622 memset(obj, 0, sizeof(xmlXPathObject));
5624 return;
5626 free_obj:
5628 * Cache is full; free the object.
5630 if (obj->nodesetval != NULL)
5631 xmlXPathFreeNodeSet(obj->nodesetval);
5632 #ifdef XP_DEBUG_OBJ_USAGE
5633 xmlXPathDebugObjUsageReleased(NULL, obj->type);
5634 #endif
5635 xmlFree(obj);
5637 return;
5641 /************************************************************************
5643 * Type Casting Routines *
5645 ************************************************************************/
5648 * xmlXPathCastBooleanToString:
5649 * @val: a boolean
5651 * Converts a boolean to its string value.
5653 * Returns a newly allocated string.
5655 xmlChar *
5656 xmlXPathCastBooleanToString (int val) {
5657 xmlChar *ret;
5658 if (val)
5659 ret = xmlStrdup((const xmlChar *) "true");
5660 else
5661 ret = xmlStrdup((const xmlChar *) "false");
5662 return(ret);
5666 * xmlXPathCastNumberToString:
5667 * @val: a number
5669 * Converts a number to its string value.
5671 * Returns a newly allocated string.
5673 xmlChar *
5674 xmlXPathCastNumberToString (double val) {
5675 xmlChar *ret;
5676 switch (xmlXPathIsInf(val)) {
5677 case 1:
5678 ret = xmlStrdup((const xmlChar *) "Infinity");
5679 break;
5680 case -1:
5681 ret = xmlStrdup((const xmlChar *) "-Infinity");
5682 break;
5683 default:
5684 if (xmlXPathIsNaN(val)) {
5685 ret = xmlStrdup((const xmlChar *) "NaN");
5686 } else if (val == 0) {
5687 /* Omit sign for negative zero. */
5688 ret = xmlStrdup((const xmlChar *) "0");
5689 } else {
5690 /* could be improved */
5691 char buf[100];
5692 xmlXPathFormatNumber(val, buf, 99);
5693 buf[99] = 0;
5694 ret = xmlStrdup((const xmlChar *) buf);
5697 return(ret);
5701 * xmlXPathCastNodeToString:
5702 * @node: a node
5704 * Converts a node to its string value.
5706 * Returns a newly allocated string.
5708 xmlChar *
5709 xmlXPathCastNodeToString (xmlNodePtr node) {
5710 xmlChar *ret;
5711 if ((ret = xmlNodeGetContent(node)) == NULL)
5712 ret = xmlStrdup((const xmlChar *) "");
5713 return(ret);
5717 * xmlXPathCastNodeSetToString:
5718 * @ns: a node-set
5720 * Converts a node-set to its string value.
5722 * Returns a newly allocated string.
5724 xmlChar *
5725 xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
5726 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
5727 return(xmlStrdup((const xmlChar *) ""));
5729 if (ns->nodeNr > 1)
5730 xmlXPathNodeSetSort(ns);
5731 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
5735 * xmlXPathCastToString:
5736 * @val: an XPath object
5738 * Converts an existing object to its string() equivalent
5740 * Returns the allocated string value of the object, NULL in case of error.
5741 * It's up to the caller to free the string memory with xmlFree().
5743 xmlChar *
5744 xmlXPathCastToString(xmlXPathObjectPtr val) {
5745 xmlChar *ret = NULL;
5747 if (val == NULL)
5748 return(xmlStrdup((const xmlChar *) ""));
5749 switch (val->type) {
5750 case XPATH_UNDEFINED:
5751 #ifdef DEBUG_EXPR
5752 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
5753 #endif
5754 ret = xmlStrdup((const xmlChar *) "");
5755 break;
5756 case XPATH_NODESET:
5757 case XPATH_XSLT_TREE:
5758 ret = xmlXPathCastNodeSetToString(val->nodesetval);
5759 break;
5760 case XPATH_STRING:
5761 return(xmlStrdup(val->stringval));
5762 case XPATH_BOOLEAN:
5763 ret = xmlXPathCastBooleanToString(val->boolval);
5764 break;
5765 case XPATH_NUMBER: {
5766 ret = xmlXPathCastNumberToString(val->floatval);
5767 break;
5769 case XPATH_USERS:
5770 case XPATH_POINT:
5771 case XPATH_RANGE:
5772 case XPATH_LOCATIONSET:
5773 TODO
5774 ret = xmlStrdup((const xmlChar *) "");
5775 break;
5777 return(ret);
5781 * xmlXPathConvertString:
5782 * @val: an XPath object
5784 * Converts an existing object to its string() equivalent
5786 * Returns the new object, the old one is freed (or the operation
5787 * is done directly on @val)
5789 xmlXPathObjectPtr
5790 xmlXPathConvertString(xmlXPathObjectPtr val) {
5791 xmlChar *res = NULL;
5793 if (val == NULL)
5794 return(xmlXPathNewCString(""));
5796 switch (val->type) {
5797 case XPATH_UNDEFINED:
5798 #ifdef DEBUG_EXPR
5799 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
5800 #endif
5801 break;
5802 case XPATH_NODESET:
5803 case XPATH_XSLT_TREE:
5804 res = xmlXPathCastNodeSetToString(val->nodesetval);
5805 break;
5806 case XPATH_STRING:
5807 return(val);
5808 case XPATH_BOOLEAN:
5809 res = xmlXPathCastBooleanToString(val->boolval);
5810 break;
5811 case XPATH_NUMBER:
5812 res = xmlXPathCastNumberToString(val->floatval);
5813 break;
5814 case XPATH_USERS:
5815 case XPATH_POINT:
5816 case XPATH_RANGE:
5817 case XPATH_LOCATIONSET:
5818 TODO;
5819 break;
5821 xmlXPathFreeObject(val);
5822 if (res == NULL)
5823 return(xmlXPathNewCString(""));
5824 return(xmlXPathWrapString(res));
5828 * xmlXPathCastBooleanToNumber:
5829 * @val: a boolean
5831 * Converts a boolean to its number value
5833 * Returns the number value
5835 double
5836 xmlXPathCastBooleanToNumber(int val) {
5837 if (val)
5838 return(1.0);
5839 return(0.0);
5843 * xmlXPathCastStringToNumber:
5844 * @val: a string
5846 * Converts a string to its number value
5848 * Returns the number value
5850 double
5851 xmlXPathCastStringToNumber(const xmlChar * val) {
5852 return(xmlXPathStringEvalNumber(val));
5856 * xmlXPathCastNodeToNumber:
5857 * @node: a node
5859 * Converts a node to its number value
5861 * Returns the number value
5863 double
5864 xmlXPathCastNodeToNumber (xmlNodePtr node) {
5865 xmlChar *strval;
5866 double ret;
5868 if (node == NULL)
5869 return(xmlXPathNAN);
5870 strval = xmlXPathCastNodeToString(node);
5871 if (strval == NULL)
5872 return(xmlXPathNAN);
5873 ret = xmlXPathCastStringToNumber(strval);
5874 xmlFree(strval);
5876 return(ret);
5880 * xmlXPathCastNodeSetToNumber:
5881 * @ns: a node-set
5883 * Converts a node-set to its number value
5885 * Returns the number value
5887 double
5888 xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
5889 xmlChar *str;
5890 double ret;
5892 if (ns == NULL)
5893 return(xmlXPathNAN);
5894 str = xmlXPathCastNodeSetToString(ns);
5895 ret = xmlXPathCastStringToNumber(str);
5896 xmlFree(str);
5897 return(ret);
5901 * xmlXPathCastToNumber:
5902 * @val: an XPath object
5904 * Converts an XPath object to its number value
5906 * Returns the number value
5908 double
5909 xmlXPathCastToNumber(xmlXPathObjectPtr val) {
5910 double ret = 0.0;
5912 if (val == NULL)
5913 return(xmlXPathNAN);
5914 switch (val->type) {
5915 case XPATH_UNDEFINED:
5916 #ifdef DEBUG_EXPR
5917 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5918 #endif
5919 ret = xmlXPathNAN;
5920 break;
5921 case XPATH_NODESET:
5922 case XPATH_XSLT_TREE:
5923 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
5924 break;
5925 case XPATH_STRING:
5926 ret = xmlXPathCastStringToNumber(val->stringval);
5927 break;
5928 case XPATH_NUMBER:
5929 ret = val->floatval;
5930 break;
5931 case XPATH_BOOLEAN:
5932 ret = xmlXPathCastBooleanToNumber(val->boolval);
5933 break;
5934 case XPATH_USERS:
5935 case XPATH_POINT:
5936 case XPATH_RANGE:
5937 case XPATH_LOCATIONSET:
5938 TODO;
5939 ret = xmlXPathNAN;
5940 break;
5942 return(ret);
5946 * xmlXPathConvertNumber:
5947 * @val: an XPath object
5949 * Converts an existing object to its number() equivalent
5951 * Returns the new object, the old one is freed (or the operation
5952 * is done directly on @val)
5954 xmlXPathObjectPtr
5955 xmlXPathConvertNumber(xmlXPathObjectPtr val) {
5956 xmlXPathObjectPtr ret;
5958 if (val == NULL)
5959 return(xmlXPathNewFloat(0.0));
5960 if (val->type == XPATH_NUMBER)
5961 return(val);
5962 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
5963 xmlXPathFreeObject(val);
5964 return(ret);
5968 * xmlXPathCastNumberToBoolean:
5969 * @val: a number
5971 * Converts a number to its boolean value
5973 * Returns the boolean value
5976 xmlXPathCastNumberToBoolean (double val) {
5977 if (xmlXPathIsNaN(val) || (val == 0.0))
5978 return(0);
5979 return(1);
5983 * xmlXPathCastStringToBoolean:
5984 * @val: a string
5986 * Converts a string to its boolean value
5988 * Returns the boolean value
5991 xmlXPathCastStringToBoolean (const xmlChar *val) {
5992 if ((val == NULL) || (xmlStrlen(val) == 0))
5993 return(0);
5994 return(1);
5998 * xmlXPathCastNodeSetToBoolean:
5999 * @ns: a node-set
6001 * Converts a node-set to its boolean value
6003 * Returns the boolean value
6006 xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
6007 if ((ns == NULL) || (ns->nodeNr == 0))
6008 return(0);
6009 return(1);
6013 * xmlXPathCastToBoolean:
6014 * @val: an XPath object
6016 * Converts an XPath object to its boolean value
6018 * Returns the boolean value
6021 xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
6022 int ret = 0;
6024 if (val == NULL)
6025 return(0);
6026 switch (val->type) {
6027 case XPATH_UNDEFINED:
6028 #ifdef DEBUG_EXPR
6029 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
6030 #endif
6031 ret = 0;
6032 break;
6033 case XPATH_NODESET:
6034 case XPATH_XSLT_TREE:
6035 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
6036 break;
6037 case XPATH_STRING:
6038 ret = xmlXPathCastStringToBoolean(val->stringval);
6039 break;
6040 case XPATH_NUMBER:
6041 ret = xmlXPathCastNumberToBoolean(val->floatval);
6042 break;
6043 case XPATH_BOOLEAN:
6044 ret = val->boolval;
6045 break;
6046 case XPATH_USERS:
6047 case XPATH_POINT:
6048 case XPATH_RANGE:
6049 case XPATH_LOCATIONSET:
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 , (size_t) 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 , (size_t) 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 , (size_t) 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;
6260 ret->valueFrame = 0;
6262 ret->context = ctxt;
6263 ret->comp = comp;
6265 return(ret);
6269 * xmlXPathFreeParserContext:
6270 * @ctxt: the context to free
6272 * Free up an xmlXPathParserContext
6274 void
6275 xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
6276 int i;
6278 if (ctxt->valueTab != NULL) {
6279 for (i = 0; i < ctxt->valueNr; i++) {
6280 if (ctxt->context)
6281 xmlXPathReleaseObject(ctxt->context, ctxt->valueTab[i]);
6282 else
6283 xmlXPathFreeObject(ctxt->valueTab[i]);
6285 xmlFree(ctxt->valueTab);
6287 if (ctxt->comp != NULL) {
6288 #ifdef XPATH_STREAMING
6289 if (ctxt->comp->stream != NULL) {
6290 xmlFreePatternList(ctxt->comp->stream);
6291 ctxt->comp->stream = NULL;
6293 #endif
6294 xmlXPathFreeCompExpr(ctxt->comp);
6296 xmlFree(ctxt);
6299 /************************************************************************
6301 * The implicit core function library *
6303 ************************************************************************/
6306 * xmlXPathNodeValHash:
6307 * @node: a node pointer
6309 * Function computing the beginning of the string value of the node,
6310 * used to speed up comparisons
6312 * Returns an int usable as a hash
6314 static unsigned int
6315 xmlXPathNodeValHash(xmlNodePtr node) {
6316 int len = 2;
6317 const xmlChar * string = NULL;
6318 xmlNodePtr tmp = NULL;
6319 unsigned int ret = 0;
6321 if (node == NULL)
6322 return(0);
6324 if (node->type == XML_DOCUMENT_NODE) {
6325 tmp = xmlDocGetRootElement((xmlDocPtr) node);
6326 if (tmp == NULL)
6327 node = node->children;
6328 else
6329 node = tmp;
6331 if (node == NULL)
6332 return(0);
6335 switch (node->type) {
6336 case XML_COMMENT_NODE:
6337 case XML_PI_NODE:
6338 case XML_CDATA_SECTION_NODE:
6339 case XML_TEXT_NODE:
6340 string = node->content;
6341 if (string == NULL)
6342 return(0);
6343 if (string[0] == 0)
6344 return(0);
6345 return(((unsigned int) string[0]) +
6346 (((unsigned int) string[1]) << 8));
6347 case XML_NAMESPACE_DECL:
6348 string = ((xmlNsPtr)node)->href;
6349 if (string == NULL)
6350 return(0);
6351 if (string[0] == 0)
6352 return(0);
6353 return(((unsigned int) string[0]) +
6354 (((unsigned int) string[1]) << 8));
6355 case XML_ATTRIBUTE_NODE:
6356 tmp = ((xmlAttrPtr) node)->children;
6357 break;
6358 case XML_ELEMENT_NODE:
6359 tmp = node->children;
6360 break;
6361 default:
6362 return(0);
6364 while (tmp != NULL) {
6365 switch (tmp->type) {
6366 case XML_CDATA_SECTION_NODE:
6367 case XML_TEXT_NODE:
6368 string = tmp->content;
6369 break;
6370 default:
6371 string = NULL;
6372 break;
6374 if ((string != NULL) && (string[0] != 0)) {
6375 if (len == 1) {
6376 return(ret + (((unsigned int) string[0]) << 8));
6378 if (string[1] == 0) {
6379 len = 1;
6380 ret = (unsigned int) string[0];
6381 } else {
6382 return(((unsigned int) string[0]) +
6383 (((unsigned int) string[1]) << 8));
6387 * Skip to next node
6389 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
6390 if (tmp->children->type != XML_ENTITY_DECL) {
6391 tmp = tmp->children;
6392 continue;
6395 if (tmp == node)
6396 break;
6398 if (tmp->next != NULL) {
6399 tmp = tmp->next;
6400 continue;
6403 do {
6404 tmp = tmp->parent;
6405 if (tmp == NULL)
6406 break;
6407 if (tmp == node) {
6408 tmp = NULL;
6409 break;
6411 if (tmp->next != NULL) {
6412 tmp = tmp->next;
6413 break;
6415 } while (tmp != NULL);
6417 return(ret);
6421 * xmlXPathStringHash:
6422 * @string: a string
6424 * Function computing the beginning of the string value of the node,
6425 * used to speed up comparisons
6427 * Returns an int usable as a hash
6429 static unsigned int
6430 xmlXPathStringHash(const xmlChar * string) {
6431 if (string == NULL)
6432 return((unsigned int) 0);
6433 if (string[0] == 0)
6434 return(0);
6435 return(((unsigned int) string[0]) +
6436 (((unsigned int) string[1]) << 8));
6440 * xmlXPathCompareNodeSetFloat:
6441 * @ctxt: the XPath Parser context
6442 * @inf: less than (1) or greater than (0)
6443 * @strict: is the comparison strict
6444 * @arg: the node set
6445 * @f: the value
6447 * Implement the compare operation between a nodeset and a number
6448 * @ns < @val (1, 1, ...
6449 * @ns <= @val (1, 0, ...
6450 * @ns > @val (0, 1, ...
6451 * @ns >= @val (0, 0, ...
6453 * If one object to be compared is a node-set and the other is a number,
6454 * then the comparison will be true if and only if there is a node in the
6455 * node-set such that the result of performing the comparison on the number
6456 * to be compared and on the result of converting the string-value of that
6457 * node to a number using the number function is true.
6459 * Returns 0 or 1 depending on the results of the test.
6461 static int
6462 xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
6463 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
6464 int i, ret = 0;
6465 xmlNodeSetPtr ns;
6466 xmlChar *str2;
6468 if ((f == NULL) || (arg == NULL) ||
6469 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6470 xmlXPathReleaseObject(ctxt->context, arg);
6471 xmlXPathReleaseObject(ctxt->context, f);
6472 return(0);
6474 ns = arg->nodesetval;
6475 if (ns != NULL) {
6476 for (i = 0;i < ns->nodeNr;i++) {
6477 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6478 if (str2 != NULL) {
6479 valuePush(ctxt,
6480 xmlXPathCacheNewString(ctxt->context, str2));
6481 xmlFree(str2);
6482 xmlXPathNumberFunction(ctxt, 1);
6483 valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
6484 ret = xmlXPathCompareValues(ctxt, inf, strict);
6485 if (ret)
6486 break;
6490 xmlXPathReleaseObject(ctxt->context, arg);
6491 xmlXPathReleaseObject(ctxt->context, f);
6492 return(ret);
6496 * xmlXPathCompareNodeSetString:
6497 * @ctxt: the XPath Parser context
6498 * @inf: less than (1) or greater than (0)
6499 * @strict: is the comparison strict
6500 * @arg: the node set
6501 * @s: the value
6503 * Implement the compare operation between a nodeset and a string
6504 * @ns < @val (1, 1, ...
6505 * @ns <= @val (1, 0, ...
6506 * @ns > @val (0, 1, ...
6507 * @ns >= @val (0, 0, ...
6509 * If one object to be compared is a node-set and the other is a string,
6510 * then the comparison will be true if and only if there is a node in
6511 * the node-set such that the result of performing the comparison on the
6512 * string-value of the node and the other string is true.
6514 * Returns 0 or 1 depending on the results of the test.
6516 static int
6517 xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
6518 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
6519 int i, ret = 0;
6520 xmlNodeSetPtr ns;
6521 xmlChar *str2;
6523 if ((s == NULL) || (arg == NULL) ||
6524 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6525 xmlXPathReleaseObject(ctxt->context, arg);
6526 xmlXPathReleaseObject(ctxt->context, s);
6527 return(0);
6529 ns = arg->nodesetval;
6530 if (ns != NULL) {
6531 for (i = 0;i < ns->nodeNr;i++) {
6532 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6533 if (str2 != NULL) {
6534 valuePush(ctxt,
6535 xmlXPathCacheNewString(ctxt->context, str2));
6536 xmlFree(str2);
6537 valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
6538 ret = xmlXPathCompareValues(ctxt, inf, strict);
6539 if (ret)
6540 break;
6544 xmlXPathReleaseObject(ctxt->context, arg);
6545 xmlXPathReleaseObject(ctxt->context, s);
6546 return(ret);
6550 * xmlXPathCompareNodeSets:
6551 * @inf: less than (1) or greater than (0)
6552 * @strict: is the comparison strict
6553 * @arg1: the first node set object
6554 * @arg2: the second node set object
6556 * Implement the compare operation on nodesets:
6558 * If both objects to be compared are node-sets, then the comparison
6559 * will be true if and only if there is a node in the first node-set
6560 * and a node in the second node-set such that the result of performing
6561 * the comparison on the string-values of the two nodes is true.
6562 * ....
6563 * When neither object to be compared is a node-set and the operator
6564 * is <=, <, >= or >, then the objects are compared by converting both
6565 * objects to numbers and comparing the numbers according to IEEE 754.
6566 * ....
6567 * The number function converts its argument to a number as follows:
6568 * - a string that consists of optional whitespace followed by an
6569 * optional minus sign followed by a Number followed by whitespace
6570 * is converted to the IEEE 754 number that is nearest (according
6571 * to the IEEE 754 round-to-nearest rule) to the mathematical value
6572 * represented by the string; any other string is converted to NaN
6574 * Conclusion all nodes need to be converted first to their string value
6575 * and then the comparison must be done when possible
6577 static int
6578 xmlXPathCompareNodeSets(int inf, int strict,
6579 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6580 int i, j, init = 0;
6581 double val1;
6582 double *values2;
6583 int ret = 0;
6584 xmlNodeSetPtr ns1;
6585 xmlNodeSetPtr ns2;
6587 if ((arg1 == NULL) ||
6588 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
6589 xmlXPathFreeObject(arg2);
6590 return(0);
6592 if ((arg2 == NULL) ||
6593 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
6594 xmlXPathFreeObject(arg1);
6595 xmlXPathFreeObject(arg2);
6596 return(0);
6599 ns1 = arg1->nodesetval;
6600 ns2 = arg2->nodesetval;
6602 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
6603 xmlXPathFreeObject(arg1);
6604 xmlXPathFreeObject(arg2);
6605 return(0);
6607 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
6608 xmlXPathFreeObject(arg1);
6609 xmlXPathFreeObject(arg2);
6610 return(0);
6613 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
6614 if (values2 == NULL) {
6615 /* TODO: Propagate memory error. */
6616 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6617 xmlXPathFreeObject(arg1);
6618 xmlXPathFreeObject(arg2);
6619 return(0);
6621 for (i = 0;i < ns1->nodeNr;i++) {
6622 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
6623 if (xmlXPathIsNaN(val1))
6624 continue;
6625 for (j = 0;j < ns2->nodeNr;j++) {
6626 if (init == 0) {
6627 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
6629 if (xmlXPathIsNaN(values2[j]))
6630 continue;
6631 if (inf && strict)
6632 ret = (val1 < values2[j]);
6633 else if (inf && !strict)
6634 ret = (val1 <= values2[j]);
6635 else if (!inf && strict)
6636 ret = (val1 > values2[j]);
6637 else if (!inf && !strict)
6638 ret = (val1 >= values2[j]);
6639 if (ret)
6640 break;
6642 if (ret)
6643 break;
6644 init = 1;
6646 xmlFree(values2);
6647 xmlXPathFreeObject(arg1);
6648 xmlXPathFreeObject(arg2);
6649 return(ret);
6653 * xmlXPathCompareNodeSetValue:
6654 * @ctxt: the XPath Parser context
6655 * @inf: less than (1) or greater than (0)
6656 * @strict: is the comparison strict
6657 * @arg: the node set
6658 * @val: the value
6660 * Implement the compare operation between a nodeset and a value
6661 * @ns < @val (1, 1, ...
6662 * @ns <= @val (1, 0, ...
6663 * @ns > @val (0, 1, ...
6664 * @ns >= @val (0, 0, ...
6666 * If one object to be compared is a node-set and the other is a boolean,
6667 * then the comparison will be true if and only if the result of performing
6668 * the comparison on the boolean and on the result of converting
6669 * the node-set to a boolean using the boolean function is true.
6671 * Returns 0 or 1 depending on the results of the test.
6673 static int
6674 xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
6675 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
6676 if ((val == NULL) || (arg == NULL) ||
6677 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6678 return(0);
6680 switch(val->type) {
6681 case XPATH_NUMBER:
6682 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
6683 case XPATH_NODESET:
6684 case XPATH_XSLT_TREE:
6685 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
6686 case XPATH_STRING:
6687 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
6688 case XPATH_BOOLEAN:
6689 valuePush(ctxt, arg);
6690 xmlXPathBooleanFunction(ctxt, 1);
6691 valuePush(ctxt, val);
6692 return(xmlXPathCompareValues(ctxt, inf, strict));
6693 default:
6694 xmlGenericError(xmlGenericErrorContext,
6695 "xmlXPathCompareNodeSetValue: Can't compare node set "
6696 "and object of type %d\n",
6697 val->type);
6698 xmlXPathReleaseObject(ctxt->context, arg);
6699 xmlXPathReleaseObject(ctxt->context, val);
6700 XP_ERROR0(XPATH_INVALID_TYPE);
6702 return(0);
6706 * xmlXPathEqualNodeSetString:
6707 * @arg: the nodeset object argument
6708 * @str: the string to compare to.
6709 * @neq: flag to show whether for '=' (0) or '!=' (1)
6711 * Implement the equal operation on XPath objects content: @arg1 == @arg2
6712 * If one object to be compared is a node-set and the other is a string,
6713 * then the comparison will be true if and only if there is a node in
6714 * the node-set such that the result of performing the comparison on the
6715 * string-value of the node and the other string is true.
6717 * Returns 0 or 1 depending on the results of the test.
6719 static int
6720 xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
6722 int i;
6723 xmlNodeSetPtr ns;
6724 xmlChar *str2;
6725 unsigned int hash;
6727 if ((str == NULL) || (arg == NULL) ||
6728 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6729 return (0);
6730 ns = arg->nodesetval;
6732 * A NULL nodeset compared with a string is always false
6733 * (since there is no node equal, and no node not equal)
6735 if ((ns == NULL) || (ns->nodeNr <= 0) )
6736 return (0);
6737 hash = xmlXPathStringHash(str);
6738 for (i = 0; i < ns->nodeNr; i++) {
6739 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
6740 str2 = xmlNodeGetContent(ns->nodeTab[i]);
6741 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
6742 xmlFree(str2);
6743 if (neq)
6744 continue;
6745 return (1);
6746 } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
6747 if (neq)
6748 continue;
6749 return (1);
6750 } else if (neq) {
6751 if (str2 != NULL)
6752 xmlFree(str2);
6753 return (1);
6755 if (str2 != NULL)
6756 xmlFree(str2);
6757 } else if (neq)
6758 return (1);
6760 return (0);
6764 * xmlXPathEqualNodeSetFloat:
6765 * @arg: the nodeset object argument
6766 * @f: the float to compare to
6767 * @neq: flag to show whether to compare '=' (0) or '!=' (1)
6769 * Implement the equal operation on XPath objects content: @arg1 == @arg2
6770 * If one object to be compared is a node-set and the other is a number,
6771 * then the comparison will be true if and only if there is a node in
6772 * the node-set such that the result of performing the comparison on the
6773 * number to be compared and on the result of converting the string-value
6774 * of that node to a number using the number function is true.
6776 * Returns 0 or 1 depending on the results of the test.
6778 static int
6779 xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
6780 xmlXPathObjectPtr arg, double f, int neq) {
6781 int i, ret=0;
6782 xmlNodeSetPtr ns;
6783 xmlChar *str2;
6784 xmlXPathObjectPtr val;
6785 double v;
6787 if ((arg == NULL) ||
6788 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6789 return(0);
6791 ns = arg->nodesetval;
6792 if (ns != NULL) {
6793 for (i=0;i<ns->nodeNr;i++) {
6794 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6795 if (str2 != NULL) {
6796 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
6797 xmlFree(str2);
6798 xmlXPathNumberFunction(ctxt, 1);
6799 val = valuePop(ctxt);
6800 v = val->floatval;
6801 xmlXPathReleaseObject(ctxt->context, val);
6802 if (!xmlXPathIsNaN(v)) {
6803 if ((!neq) && (v==f)) {
6804 ret = 1;
6805 break;
6806 } else if ((neq) && (v!=f)) {
6807 ret = 1;
6808 break;
6810 } else { /* NaN is unequal to any value */
6811 if (neq)
6812 ret = 1;
6818 return(ret);
6823 * xmlXPathEqualNodeSets:
6824 * @arg1: first nodeset object argument
6825 * @arg2: second nodeset object argument
6826 * @neq: flag to show whether to test '=' (0) or '!=' (1)
6828 * Implement the equal / not equal operation on XPath nodesets:
6829 * @arg1 == @arg2 or @arg1 != @arg2
6830 * If both objects to be compared are node-sets, then the comparison
6831 * will be true if and only if there is a node in the first node-set and
6832 * a node in the second node-set such that the result of performing the
6833 * comparison on the string-values of the two nodes is true.
6835 * (needless to say, this is a costly operation)
6837 * Returns 0 or 1 depending on the results of the test.
6839 static int
6840 xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
6841 int i, j;
6842 unsigned int *hashs1;
6843 unsigned int *hashs2;
6844 xmlChar **values1;
6845 xmlChar **values2;
6846 int ret = 0;
6847 xmlNodeSetPtr ns1;
6848 xmlNodeSetPtr ns2;
6850 if ((arg1 == NULL) ||
6851 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
6852 return(0);
6853 if ((arg2 == NULL) ||
6854 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
6855 return(0);
6857 ns1 = arg1->nodesetval;
6858 ns2 = arg2->nodesetval;
6860 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
6861 return(0);
6862 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
6863 return(0);
6866 * for equal, check if there is a node pertaining to both sets
6868 if (neq == 0)
6869 for (i = 0;i < ns1->nodeNr;i++)
6870 for (j = 0;j < ns2->nodeNr;j++)
6871 if (ns1->nodeTab[i] == ns2->nodeTab[j])
6872 return(1);
6874 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
6875 if (values1 == NULL) {
6876 /* TODO: Propagate memory error. */
6877 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6878 return(0);
6880 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
6881 if (hashs1 == NULL) {
6882 /* TODO: Propagate memory error. */
6883 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6884 xmlFree(values1);
6885 return(0);
6887 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
6888 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
6889 if (values2 == NULL) {
6890 /* TODO: Propagate memory error. */
6891 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6892 xmlFree(hashs1);
6893 xmlFree(values1);
6894 return(0);
6896 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
6897 if (hashs2 == NULL) {
6898 /* TODO: Propagate memory error. */
6899 xmlXPathErrMemory(NULL, "comparing nodesets\n");
6900 xmlFree(hashs1);
6901 xmlFree(values1);
6902 xmlFree(values2);
6903 return(0);
6905 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
6906 for (i = 0;i < ns1->nodeNr;i++) {
6907 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
6908 for (j = 0;j < ns2->nodeNr;j++) {
6909 if (i == 0)
6910 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
6911 if (hashs1[i] != hashs2[j]) {
6912 if (neq) {
6913 ret = 1;
6914 break;
6917 else {
6918 if (values1[i] == NULL)
6919 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
6920 if (values2[j] == NULL)
6921 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
6922 ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
6923 if (ret)
6924 break;
6927 if (ret)
6928 break;
6930 for (i = 0;i < ns1->nodeNr;i++)
6931 if (values1[i] != NULL)
6932 xmlFree(values1[i]);
6933 for (j = 0;j < ns2->nodeNr;j++)
6934 if (values2[j] != NULL)
6935 xmlFree(values2[j]);
6936 xmlFree(values1);
6937 xmlFree(values2);
6938 xmlFree(hashs1);
6939 xmlFree(hashs2);
6940 return(ret);
6943 static int
6944 xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
6945 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6946 int ret = 0;
6948 *At this point we are assured neither arg1 nor arg2
6949 *is a nodeset, so we can just pick the appropriate routine.
6951 switch (arg1->type) {
6952 case XPATH_UNDEFINED:
6953 #ifdef DEBUG_EXPR
6954 xmlGenericError(xmlGenericErrorContext,
6955 "Equal: undefined\n");
6956 #endif
6957 break;
6958 case XPATH_BOOLEAN:
6959 switch (arg2->type) {
6960 case XPATH_UNDEFINED:
6961 #ifdef DEBUG_EXPR
6962 xmlGenericError(xmlGenericErrorContext,
6963 "Equal: undefined\n");
6964 #endif
6965 break;
6966 case XPATH_BOOLEAN:
6967 #ifdef DEBUG_EXPR
6968 xmlGenericError(xmlGenericErrorContext,
6969 "Equal: %d boolean %d \n",
6970 arg1->boolval, arg2->boolval);
6971 #endif
6972 ret = (arg1->boolval == arg2->boolval);
6973 break;
6974 case XPATH_NUMBER:
6975 ret = (arg1->boolval ==
6976 xmlXPathCastNumberToBoolean(arg2->floatval));
6977 break;
6978 case XPATH_STRING:
6979 if ((arg2->stringval == NULL) ||
6980 (arg2->stringval[0] == 0)) ret = 0;
6981 else
6982 ret = 1;
6983 ret = (arg1->boolval == ret);
6984 break;
6985 case XPATH_USERS:
6986 case XPATH_POINT:
6987 case XPATH_RANGE:
6988 case XPATH_LOCATIONSET:
6989 TODO
6990 break;
6991 case XPATH_NODESET:
6992 case XPATH_XSLT_TREE:
6993 break;
6995 break;
6996 case XPATH_NUMBER:
6997 switch (arg2->type) {
6998 case XPATH_UNDEFINED:
6999 #ifdef DEBUG_EXPR
7000 xmlGenericError(xmlGenericErrorContext,
7001 "Equal: undefined\n");
7002 #endif
7003 break;
7004 case XPATH_BOOLEAN:
7005 ret = (arg2->boolval==
7006 xmlXPathCastNumberToBoolean(arg1->floatval));
7007 break;
7008 case XPATH_STRING:
7009 valuePush(ctxt, arg2);
7010 xmlXPathNumberFunction(ctxt, 1);
7011 arg2 = valuePop(ctxt);
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 case XPATH_POINT:
7044 case XPATH_RANGE:
7045 case XPATH_LOCATIONSET:
7046 TODO
7047 break;
7048 case XPATH_NODESET:
7049 case XPATH_XSLT_TREE:
7050 break;
7052 break;
7053 case XPATH_STRING:
7054 switch (arg2->type) {
7055 case XPATH_UNDEFINED:
7056 #ifdef DEBUG_EXPR
7057 xmlGenericError(xmlGenericErrorContext,
7058 "Equal: undefined\n");
7059 #endif
7060 break;
7061 case XPATH_BOOLEAN:
7062 if ((arg1->stringval == NULL) ||
7063 (arg1->stringval[0] == 0)) ret = 0;
7064 else
7065 ret = 1;
7066 ret = (arg2->boolval == ret);
7067 break;
7068 case XPATH_STRING:
7069 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
7070 break;
7071 case XPATH_NUMBER:
7072 valuePush(ctxt, arg1);
7073 xmlXPathNumberFunction(ctxt, 1);
7074 arg1 = valuePop(ctxt);
7075 /* Hand check NaN and Infinity equalities */
7076 if (xmlXPathIsNaN(arg1->floatval) ||
7077 xmlXPathIsNaN(arg2->floatval)) {
7078 ret = 0;
7079 } else if (xmlXPathIsInf(arg1->floatval) == 1) {
7080 if (xmlXPathIsInf(arg2->floatval) == 1)
7081 ret = 1;
7082 else
7083 ret = 0;
7084 } else if (xmlXPathIsInf(arg1->floatval) == -1) {
7085 if (xmlXPathIsInf(arg2->floatval) == -1)
7086 ret = 1;
7087 else
7088 ret = 0;
7089 } else if (xmlXPathIsInf(arg2->floatval) == 1) {
7090 if (xmlXPathIsInf(arg1->floatval) == 1)
7091 ret = 1;
7092 else
7093 ret = 0;
7094 } else if (xmlXPathIsInf(arg2->floatval) == -1) {
7095 if (xmlXPathIsInf(arg1->floatval) == -1)
7096 ret = 1;
7097 else
7098 ret = 0;
7099 } else {
7100 ret = (arg1->floatval == arg2->floatval);
7102 break;
7103 case XPATH_USERS:
7104 case XPATH_POINT:
7105 case XPATH_RANGE:
7106 case XPATH_LOCATIONSET:
7107 TODO
7108 break;
7109 case XPATH_NODESET:
7110 case XPATH_XSLT_TREE:
7111 break;
7113 break;
7114 case XPATH_USERS:
7115 case XPATH_POINT:
7116 case XPATH_RANGE:
7117 case XPATH_LOCATIONSET:
7118 TODO
7119 break;
7120 case XPATH_NODESET:
7121 case XPATH_XSLT_TREE:
7122 break;
7124 xmlXPathReleaseObject(ctxt->context, arg1);
7125 xmlXPathReleaseObject(ctxt->context, arg2);
7126 return(ret);
7130 * xmlXPathEqualValues:
7131 * @ctxt: the XPath Parser context
7133 * Implement the equal operation on XPath objects content: @arg1 == @arg2
7135 * Returns 0 or 1 depending on the results of the test.
7138 xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
7139 xmlXPathObjectPtr arg1, arg2, argtmp;
7140 int ret = 0;
7142 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7143 arg2 = valuePop(ctxt);
7144 arg1 = valuePop(ctxt);
7145 if ((arg1 == NULL) || (arg2 == NULL)) {
7146 if (arg1 != NULL)
7147 xmlXPathReleaseObject(ctxt->context, arg1);
7148 else
7149 xmlXPathReleaseObject(ctxt->context, arg2);
7150 XP_ERROR0(XPATH_INVALID_OPERAND);
7153 if (arg1 == arg2) {
7154 #ifdef DEBUG_EXPR
7155 xmlGenericError(xmlGenericErrorContext,
7156 "Equal: by pointer\n");
7157 #endif
7158 xmlXPathFreeObject(arg1);
7159 return(1);
7163 *If either argument is a nodeset, it's a 'special case'
7165 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7166 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7168 *Hack it to assure arg1 is the nodeset
7170 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7171 argtmp = arg2;
7172 arg2 = arg1;
7173 arg1 = argtmp;
7175 switch (arg2->type) {
7176 case XPATH_UNDEFINED:
7177 #ifdef DEBUG_EXPR
7178 xmlGenericError(xmlGenericErrorContext,
7179 "Equal: undefined\n");
7180 #endif
7181 break;
7182 case XPATH_NODESET:
7183 case XPATH_XSLT_TREE:
7184 ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
7185 break;
7186 case XPATH_BOOLEAN:
7187 if ((arg1->nodesetval == NULL) ||
7188 (arg1->nodesetval->nodeNr == 0)) ret = 0;
7189 else
7190 ret = 1;
7191 ret = (ret == arg2->boolval);
7192 break;
7193 case XPATH_NUMBER:
7194 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
7195 break;
7196 case XPATH_STRING:
7197 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
7198 break;
7199 case XPATH_USERS:
7200 case XPATH_POINT:
7201 case XPATH_RANGE:
7202 case XPATH_LOCATIONSET:
7203 TODO
7204 break;
7206 xmlXPathReleaseObject(ctxt->context, arg1);
7207 xmlXPathReleaseObject(ctxt->context, arg2);
7208 return(ret);
7211 return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7215 * xmlXPathNotEqualValues:
7216 * @ctxt: the XPath Parser context
7218 * Implement the equal operation on XPath objects content: @arg1 == @arg2
7220 * Returns 0 or 1 depending on the results of the test.
7223 xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
7224 xmlXPathObjectPtr arg1, arg2, argtmp;
7225 int ret = 0;
7227 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7228 arg2 = valuePop(ctxt);
7229 arg1 = valuePop(ctxt);
7230 if ((arg1 == NULL) || (arg2 == NULL)) {
7231 if (arg1 != NULL)
7232 xmlXPathReleaseObject(ctxt->context, arg1);
7233 else
7234 xmlXPathReleaseObject(ctxt->context, arg2);
7235 XP_ERROR0(XPATH_INVALID_OPERAND);
7238 if (arg1 == arg2) {
7239 #ifdef DEBUG_EXPR
7240 xmlGenericError(xmlGenericErrorContext,
7241 "NotEqual: by pointer\n");
7242 #endif
7243 xmlXPathReleaseObject(ctxt->context, arg1);
7244 return(0);
7248 *If either argument is a nodeset, it's a 'special case'
7250 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7251 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7253 *Hack it to assure arg1 is the nodeset
7255 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7256 argtmp = arg2;
7257 arg2 = arg1;
7258 arg1 = argtmp;
7260 switch (arg2->type) {
7261 case XPATH_UNDEFINED:
7262 #ifdef DEBUG_EXPR
7263 xmlGenericError(xmlGenericErrorContext,
7264 "NotEqual: undefined\n");
7265 #endif
7266 break;
7267 case XPATH_NODESET:
7268 case XPATH_XSLT_TREE:
7269 ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
7270 break;
7271 case XPATH_BOOLEAN:
7272 if ((arg1->nodesetval == NULL) ||
7273 (arg1->nodesetval->nodeNr == 0)) ret = 0;
7274 else
7275 ret = 1;
7276 ret = (ret != arg2->boolval);
7277 break;
7278 case XPATH_NUMBER:
7279 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
7280 break;
7281 case XPATH_STRING:
7282 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
7283 break;
7284 case XPATH_USERS:
7285 case XPATH_POINT:
7286 case XPATH_RANGE:
7287 case XPATH_LOCATIONSET:
7288 TODO
7289 break;
7291 xmlXPathReleaseObject(ctxt->context, arg1);
7292 xmlXPathReleaseObject(ctxt->context, arg2);
7293 return(ret);
7296 return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7300 * xmlXPathCompareValues:
7301 * @ctxt: the XPath Parser context
7302 * @inf: less than (1) or greater than (0)
7303 * @strict: is the comparison strict
7305 * Implement the compare operation on XPath objects:
7306 * @arg1 < @arg2 (1, 1, ...
7307 * @arg1 <= @arg2 (1, 0, ...
7308 * @arg1 > @arg2 (0, 1, ...
7309 * @arg1 >= @arg2 (0, 0, ...
7311 * When neither object to be compared is a node-set and the operator is
7312 * <=, <, >=, >, then the objects are compared by converted both objects
7313 * to numbers and comparing the numbers according to IEEE 754. The <
7314 * comparison will be true if and only if the first number is less than the
7315 * second number. The <= comparison will be true if and only if the first
7316 * number is less than or equal to the second number. The > comparison
7317 * will be true if and only if the first number is greater than the second
7318 * number. The >= comparison will be true if and only if the first number
7319 * is greater than or equal to the second number.
7321 * Returns 1 if the comparison succeeded, 0 if it failed
7324 xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
7325 int ret = 0, arg1i = 0, arg2i = 0;
7326 xmlXPathObjectPtr arg1, arg2;
7328 if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7329 arg2 = valuePop(ctxt);
7330 arg1 = valuePop(ctxt);
7331 if ((arg1 == NULL) || (arg2 == NULL)) {
7332 if (arg1 != NULL)
7333 xmlXPathReleaseObject(ctxt->context, arg1);
7334 else
7335 xmlXPathReleaseObject(ctxt->context, arg2);
7336 XP_ERROR0(XPATH_INVALID_OPERAND);
7339 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7340 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7342 * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
7343 * are not freed from within this routine; they will be freed from the
7344 * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
7346 if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
7347 ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
7348 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
7349 } else {
7350 if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7351 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
7352 arg1, arg2);
7353 } else {
7354 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
7355 arg2, arg1);
7358 return(ret);
7361 if (arg1->type != XPATH_NUMBER) {
7362 valuePush(ctxt, arg1);
7363 xmlXPathNumberFunction(ctxt, 1);
7364 arg1 = valuePop(ctxt);
7366 if (arg1->type != XPATH_NUMBER) {
7367 xmlXPathFreeObject(arg1);
7368 xmlXPathFreeObject(arg2);
7369 XP_ERROR0(XPATH_INVALID_OPERAND);
7371 if (arg2->type != XPATH_NUMBER) {
7372 valuePush(ctxt, arg2);
7373 xmlXPathNumberFunction(ctxt, 1);
7374 arg2 = valuePop(ctxt);
7376 if (arg2->type != XPATH_NUMBER) {
7377 xmlXPathReleaseObject(ctxt->context, arg1);
7378 xmlXPathReleaseObject(ctxt->context, arg2);
7379 XP_ERROR0(XPATH_INVALID_OPERAND);
7382 * Add tests for infinity and nan
7383 * => feedback on 3.4 for Inf and NaN
7385 /* Hand check NaN and Infinity comparisons */
7386 if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
7387 ret=0;
7388 } else {
7389 arg1i=xmlXPathIsInf(arg1->floatval);
7390 arg2i=xmlXPathIsInf(arg2->floatval);
7391 if (inf && strict) {
7392 if ((arg1i == -1 && arg2i != -1) ||
7393 (arg2i == 1 && arg1i != 1)) {
7394 ret = 1;
7395 } else if (arg1i == 0 && arg2i == 0) {
7396 ret = (arg1->floatval < arg2->floatval);
7397 } else {
7398 ret = 0;
7401 else if (inf && !strict) {
7402 if (arg1i == -1 || arg2i == 1) {
7403 ret = 1;
7404 } else if (arg1i == 0 && arg2i == 0) {
7405 ret = (arg1->floatval <= arg2->floatval);
7406 } else {
7407 ret = 0;
7410 else if (!inf && strict) {
7411 if ((arg1i == 1 && arg2i != 1) ||
7412 (arg2i == -1 && arg1i != -1)) {
7413 ret = 1;
7414 } else if (arg1i == 0 && arg2i == 0) {
7415 ret = (arg1->floatval > arg2->floatval);
7416 } else {
7417 ret = 0;
7420 else if (!inf && !strict) {
7421 if (arg1i == 1 || arg2i == -1) {
7422 ret = 1;
7423 } else if (arg1i == 0 && arg2i == 0) {
7424 ret = (arg1->floatval >= arg2->floatval);
7425 } else {
7426 ret = 0;
7430 xmlXPathReleaseObject(ctxt->context, arg1);
7431 xmlXPathReleaseObject(ctxt->context, arg2);
7432 return(ret);
7436 * xmlXPathValueFlipSign:
7437 * @ctxt: the XPath Parser context
7439 * Implement the unary - operation on an XPath object
7440 * The numeric operators convert their operands to numbers as if
7441 * by calling the number function.
7443 void
7444 xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
7445 if ((ctxt == NULL) || (ctxt->context == NULL)) return;
7446 CAST_TO_NUMBER;
7447 CHECK_TYPE(XPATH_NUMBER);
7448 ctxt->value->floatval = -ctxt->value->floatval;
7452 * xmlXPathAddValues:
7453 * @ctxt: the XPath Parser context
7455 * Implement the add operation on XPath objects:
7456 * The numeric operators convert their operands to numbers as if
7457 * by calling the number function.
7459 void
7460 xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
7461 xmlXPathObjectPtr arg;
7462 double val;
7464 arg = valuePop(ctxt);
7465 if (arg == NULL)
7466 XP_ERROR(XPATH_INVALID_OPERAND);
7467 val = xmlXPathCastToNumber(arg);
7468 xmlXPathReleaseObject(ctxt->context, arg);
7469 CAST_TO_NUMBER;
7470 CHECK_TYPE(XPATH_NUMBER);
7471 ctxt->value->floatval += val;
7475 * xmlXPathSubValues:
7476 * @ctxt: the XPath Parser context
7478 * Implement the subtraction operation on XPath objects:
7479 * The numeric operators convert their operands to numbers as if
7480 * by calling the number function.
7482 void
7483 xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
7484 xmlXPathObjectPtr arg;
7485 double val;
7487 arg = valuePop(ctxt);
7488 if (arg == NULL)
7489 XP_ERROR(XPATH_INVALID_OPERAND);
7490 val = xmlXPathCastToNumber(arg);
7491 xmlXPathReleaseObject(ctxt->context, arg);
7492 CAST_TO_NUMBER;
7493 CHECK_TYPE(XPATH_NUMBER);
7494 ctxt->value->floatval -= val;
7498 * xmlXPathMultValues:
7499 * @ctxt: the XPath Parser context
7501 * Implement the multiply operation on XPath objects:
7502 * The numeric operators convert their operands to numbers as if
7503 * by calling the number function.
7505 void
7506 xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
7507 xmlXPathObjectPtr arg;
7508 double val;
7510 arg = valuePop(ctxt);
7511 if (arg == NULL)
7512 XP_ERROR(XPATH_INVALID_OPERAND);
7513 val = xmlXPathCastToNumber(arg);
7514 xmlXPathReleaseObject(ctxt->context, arg);
7515 CAST_TO_NUMBER;
7516 CHECK_TYPE(XPATH_NUMBER);
7517 ctxt->value->floatval *= val;
7521 * xmlXPathDivValues:
7522 * @ctxt: the XPath Parser context
7524 * Implement the div operation on XPath objects @arg1 / @arg2:
7525 * The numeric operators convert their operands to numbers as if
7526 * by calling the number function.
7528 ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
7529 void
7530 xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
7531 xmlXPathObjectPtr arg;
7532 double val;
7534 arg = valuePop(ctxt);
7535 if (arg == NULL)
7536 XP_ERROR(XPATH_INVALID_OPERAND);
7537 val = xmlXPathCastToNumber(arg);
7538 xmlXPathReleaseObject(ctxt->context, arg);
7539 CAST_TO_NUMBER;
7540 CHECK_TYPE(XPATH_NUMBER);
7541 ctxt->value->floatval /= val;
7545 * xmlXPathModValues:
7546 * @ctxt: the XPath Parser context
7548 * Implement the mod operation on XPath objects: @arg1 / @arg2
7549 * The numeric operators convert their operands to numbers as if
7550 * by calling the number function.
7552 void
7553 xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
7554 xmlXPathObjectPtr arg;
7555 double arg1, arg2;
7557 arg = valuePop(ctxt);
7558 if (arg == NULL)
7559 XP_ERROR(XPATH_INVALID_OPERAND);
7560 arg2 = xmlXPathCastToNumber(arg);
7561 xmlXPathReleaseObject(ctxt->context, arg);
7562 CAST_TO_NUMBER;
7563 CHECK_TYPE(XPATH_NUMBER);
7564 arg1 = ctxt->value->floatval;
7565 if (arg2 == 0)
7566 ctxt->value->floatval = xmlXPathNAN;
7567 else {
7568 ctxt->value->floatval = fmod(arg1, arg2);
7572 /************************************************************************
7574 * The traversal functions *
7576 ************************************************************************/
7579 * A traversal function enumerates nodes along an axis.
7580 * Initially it must be called with NULL, and it indicates
7581 * termination on the axis by returning NULL.
7583 typedef xmlNodePtr (*xmlXPathTraversalFunction)
7584 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
7587 * xmlXPathTraversalFunctionExt:
7588 * A traversal function enumerates nodes along an axis.
7589 * Initially it must be called with NULL, and it indicates
7590 * termination on the axis by returning NULL.
7591 * The context node of the traversal is specified via @contextNode.
7593 typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
7594 (xmlNodePtr cur, xmlNodePtr contextNode);
7597 * xmlXPathNodeSetMergeFunction:
7598 * Used for merging node sets in xmlXPathCollectAndTest().
7600 typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
7601 (xmlNodeSetPtr, xmlNodeSetPtr);
7605 * xmlXPathNextSelf:
7606 * @ctxt: the XPath Parser context
7607 * @cur: the current node in the traversal
7609 * Traversal function for the "self" direction
7610 * The self axis contains just the context node itself
7612 * Returns the next element following that axis
7614 xmlNodePtr
7615 xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7616 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7617 if (cur == NULL)
7618 return(ctxt->context->node);
7619 return(NULL);
7623 * xmlXPathNextChild:
7624 * @ctxt: the XPath Parser context
7625 * @cur: the current node in the traversal
7627 * Traversal function for the "child" direction
7628 * The child axis contains the children of the context node in document order.
7630 * Returns the next element following that axis
7632 xmlNodePtr
7633 xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7634 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7635 if (cur == NULL) {
7636 if (ctxt->context->node == NULL) return(NULL);
7637 switch (ctxt->context->node->type) {
7638 case XML_ELEMENT_NODE:
7639 case XML_TEXT_NODE:
7640 case XML_CDATA_SECTION_NODE:
7641 case XML_ENTITY_REF_NODE:
7642 case XML_ENTITY_NODE:
7643 case XML_PI_NODE:
7644 case XML_COMMENT_NODE:
7645 case XML_NOTATION_NODE:
7646 case XML_DTD_NODE:
7647 return(ctxt->context->node->children);
7648 case XML_DOCUMENT_NODE:
7649 case XML_DOCUMENT_TYPE_NODE:
7650 case XML_DOCUMENT_FRAG_NODE:
7651 case XML_HTML_DOCUMENT_NODE:
7652 #ifdef LIBXML_DOCB_ENABLED
7653 case XML_DOCB_DOCUMENT_NODE:
7654 #endif
7655 return(((xmlDocPtr) ctxt->context->node)->children);
7656 case XML_ELEMENT_DECL:
7657 case XML_ATTRIBUTE_DECL:
7658 case XML_ENTITY_DECL:
7659 case XML_ATTRIBUTE_NODE:
7660 case XML_NAMESPACE_DECL:
7661 case XML_XINCLUDE_START:
7662 case XML_XINCLUDE_END:
7663 return(NULL);
7665 return(NULL);
7667 if ((cur->type == XML_DOCUMENT_NODE) ||
7668 (cur->type == XML_HTML_DOCUMENT_NODE))
7669 return(NULL);
7670 return(cur->next);
7674 * xmlXPathNextChildElement:
7675 * @ctxt: the XPath Parser context
7676 * @cur: the current node in the traversal
7678 * Traversal function for the "child" direction and nodes of type element.
7679 * The child axis contains the children of the context node in document order.
7681 * Returns the next element following that axis
7683 static xmlNodePtr
7684 xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7685 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7686 if (cur == NULL) {
7687 cur = ctxt->context->node;
7688 if (cur == NULL) return(NULL);
7690 * Get the first element child.
7692 switch (cur->type) {
7693 case XML_ELEMENT_NODE:
7694 case XML_DOCUMENT_FRAG_NODE:
7695 case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
7696 case XML_ENTITY_NODE:
7697 cur = cur->children;
7698 if (cur != NULL) {
7699 if (cur->type == XML_ELEMENT_NODE)
7700 return(cur);
7701 do {
7702 cur = cur->next;
7703 } while ((cur != NULL) &&
7704 (cur->type != XML_ELEMENT_NODE));
7705 return(cur);
7707 return(NULL);
7708 case XML_DOCUMENT_NODE:
7709 case XML_HTML_DOCUMENT_NODE:
7710 #ifdef LIBXML_DOCB_ENABLED
7711 case XML_DOCB_DOCUMENT_NODE:
7712 #endif
7713 return(xmlDocGetRootElement((xmlDocPtr) cur));
7714 default:
7715 return(NULL);
7717 return(NULL);
7720 * Get the next sibling element node.
7722 switch (cur->type) {
7723 case XML_ELEMENT_NODE:
7724 case XML_TEXT_NODE:
7725 case XML_ENTITY_REF_NODE:
7726 case XML_ENTITY_NODE:
7727 case XML_CDATA_SECTION_NODE:
7728 case XML_PI_NODE:
7729 case XML_COMMENT_NODE:
7730 case XML_XINCLUDE_END:
7731 break;
7732 /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
7733 default:
7734 return(NULL);
7736 if (cur->next != NULL) {
7737 if (cur->next->type == XML_ELEMENT_NODE)
7738 return(cur->next);
7739 cur = cur->next;
7740 do {
7741 cur = cur->next;
7742 } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
7743 return(cur);
7745 return(NULL);
7748 #if 0
7750 * xmlXPathNextDescendantOrSelfElemParent:
7751 * @ctxt: the XPath Parser context
7752 * @cur: the current node in the traversal
7754 * Traversal function for the "descendant-or-self" axis.
7755 * Additionally it returns only nodes which can be parents of
7756 * element nodes.
7759 * Returns the next element following that axis
7761 static xmlNodePtr
7762 xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
7763 xmlNodePtr contextNode)
7765 if (cur == NULL) {
7766 if (contextNode == NULL)
7767 return(NULL);
7768 switch (contextNode->type) {
7769 case XML_ELEMENT_NODE:
7770 case XML_XINCLUDE_START:
7771 case XML_DOCUMENT_FRAG_NODE:
7772 case XML_DOCUMENT_NODE:
7773 #ifdef LIBXML_DOCB_ENABLED
7774 case XML_DOCB_DOCUMENT_NODE:
7775 #endif
7776 case XML_HTML_DOCUMENT_NODE:
7777 return(contextNode);
7778 default:
7779 return(NULL);
7781 return(NULL);
7782 } else {
7783 xmlNodePtr start = cur;
7785 while (cur != NULL) {
7786 switch (cur->type) {
7787 case XML_ELEMENT_NODE:
7788 /* TODO: OK to have XInclude here? */
7789 case XML_XINCLUDE_START:
7790 case XML_DOCUMENT_FRAG_NODE:
7791 if (cur != start)
7792 return(cur);
7793 if (cur->children != NULL) {
7794 cur = cur->children;
7795 continue;
7797 break;
7798 /* Not sure if we need those here. */
7799 case XML_DOCUMENT_NODE:
7800 #ifdef LIBXML_DOCB_ENABLED
7801 case XML_DOCB_DOCUMENT_NODE:
7802 #endif
7803 case XML_HTML_DOCUMENT_NODE:
7804 if (cur != start)
7805 return(cur);
7806 return(xmlDocGetRootElement((xmlDocPtr) cur));
7807 default:
7808 break;
7811 next_sibling:
7812 if ((cur == NULL) || (cur == contextNode))
7813 return(NULL);
7814 if (cur->next != NULL) {
7815 cur = cur->next;
7816 } else {
7817 cur = cur->parent;
7818 goto next_sibling;
7822 return(NULL);
7824 #endif
7827 * xmlXPathNextDescendant:
7828 * @ctxt: the XPath Parser context
7829 * @cur: the current node in the traversal
7831 * Traversal function for the "descendant" direction
7832 * the descendant axis contains the descendants of the context node in document
7833 * order; a descendant is a child or a child of a child and so on.
7835 * Returns the next element following that axis
7837 xmlNodePtr
7838 xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7839 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7840 if (cur == NULL) {
7841 if (ctxt->context->node == NULL)
7842 return(NULL);
7843 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7844 (ctxt->context->node->type == XML_NAMESPACE_DECL))
7845 return(NULL);
7847 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
7848 return(ctxt->context->doc->children);
7849 return(ctxt->context->node->children);
7852 if (cur->type == XML_NAMESPACE_DECL)
7853 return(NULL);
7854 if (cur->children != NULL) {
7856 * Do not descend on entities declarations
7858 if (cur->children->type != XML_ENTITY_DECL) {
7859 cur = cur->children;
7861 * Skip DTDs
7863 if (cur->type != XML_DTD_NODE)
7864 return(cur);
7868 if (cur == ctxt->context->node) return(NULL);
7870 while (cur->next != NULL) {
7871 cur = cur->next;
7872 if ((cur->type != XML_ENTITY_DECL) &&
7873 (cur->type != XML_DTD_NODE))
7874 return(cur);
7877 do {
7878 cur = cur->parent;
7879 if (cur == NULL) break;
7880 if (cur == ctxt->context->node) return(NULL);
7881 if (cur->next != NULL) {
7882 cur = cur->next;
7883 return(cur);
7885 } while (cur != NULL);
7886 return(cur);
7890 * xmlXPathNextDescendantOrSelf:
7891 * @ctxt: the XPath Parser context
7892 * @cur: the current node in the traversal
7894 * Traversal function for the "descendant-or-self" direction
7895 * the descendant-or-self axis contains the context node and the descendants
7896 * of the context node in document order; thus the context node is the first
7897 * node on the axis, and the first child of the context node is the second node
7898 * on the axis
7900 * Returns the next element following that axis
7902 xmlNodePtr
7903 xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7904 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7905 if (cur == NULL)
7906 return(ctxt->context->node);
7908 if (ctxt->context->node == NULL)
7909 return(NULL);
7910 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7911 (ctxt->context->node->type == XML_NAMESPACE_DECL))
7912 return(NULL);
7914 return(xmlXPathNextDescendant(ctxt, cur));
7918 * xmlXPathNextParent:
7919 * @ctxt: the XPath Parser context
7920 * @cur: the current node in the traversal
7922 * Traversal function for the "parent" direction
7923 * The parent axis contains the parent of the context node, if there is one.
7925 * Returns the next element following that axis
7927 xmlNodePtr
7928 xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7929 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7931 * the parent of an attribute or namespace node is the element
7932 * to which the attribute or namespace node is attached
7933 * Namespace handling !!!
7935 if (cur == NULL) {
7936 if (ctxt->context->node == NULL) return(NULL);
7937 switch (ctxt->context->node->type) {
7938 case XML_ELEMENT_NODE:
7939 case XML_TEXT_NODE:
7940 case XML_CDATA_SECTION_NODE:
7941 case XML_ENTITY_REF_NODE:
7942 case XML_ENTITY_NODE:
7943 case XML_PI_NODE:
7944 case XML_COMMENT_NODE:
7945 case XML_NOTATION_NODE:
7946 case XML_DTD_NODE:
7947 case XML_ELEMENT_DECL:
7948 case XML_ATTRIBUTE_DECL:
7949 case XML_XINCLUDE_START:
7950 case XML_XINCLUDE_END:
7951 case XML_ENTITY_DECL:
7952 if (ctxt->context->node->parent == NULL)
7953 return((xmlNodePtr) ctxt->context->doc);
7954 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
7955 ((ctxt->context->node->parent->name[0] == ' ') ||
7956 (xmlStrEqual(ctxt->context->node->parent->name,
7957 BAD_CAST "fake node libxslt"))))
7958 return(NULL);
7959 return(ctxt->context->node->parent);
7960 case XML_ATTRIBUTE_NODE: {
7961 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
7963 return(att->parent);
7965 case XML_DOCUMENT_NODE:
7966 case XML_DOCUMENT_TYPE_NODE:
7967 case XML_DOCUMENT_FRAG_NODE:
7968 case XML_HTML_DOCUMENT_NODE:
7969 #ifdef LIBXML_DOCB_ENABLED
7970 case XML_DOCB_DOCUMENT_NODE:
7971 #endif
7972 return(NULL);
7973 case XML_NAMESPACE_DECL: {
7974 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
7976 if ((ns->next != NULL) &&
7977 (ns->next->type != XML_NAMESPACE_DECL))
7978 return((xmlNodePtr) ns->next);
7979 return(NULL);
7983 return(NULL);
7987 * xmlXPathNextAncestor:
7988 * @ctxt: the XPath Parser context
7989 * @cur: the current node in the traversal
7991 * Traversal function for the "ancestor" direction
7992 * the ancestor axis contains the ancestors of the context node; the ancestors
7993 * of the context node consist of the parent of context node and the parent's
7994 * parent and so on; the nodes are ordered in reverse document order; thus the
7995 * parent is the first node on the axis, and the parent's parent is the second
7996 * node on the axis
7998 * Returns the next element following that axis
8000 xmlNodePtr
8001 xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8002 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8004 * the parent of an attribute or namespace node is the element
8005 * to which the attribute or namespace node is attached
8006 * !!!!!!!!!!!!!
8008 if (cur == NULL) {
8009 if (ctxt->context->node == NULL) return(NULL);
8010 switch (ctxt->context->node->type) {
8011 case XML_ELEMENT_NODE:
8012 case XML_TEXT_NODE:
8013 case XML_CDATA_SECTION_NODE:
8014 case XML_ENTITY_REF_NODE:
8015 case XML_ENTITY_NODE:
8016 case XML_PI_NODE:
8017 case XML_COMMENT_NODE:
8018 case XML_DTD_NODE:
8019 case XML_ELEMENT_DECL:
8020 case XML_ATTRIBUTE_DECL:
8021 case XML_ENTITY_DECL:
8022 case XML_NOTATION_NODE:
8023 case XML_XINCLUDE_START:
8024 case XML_XINCLUDE_END:
8025 if (ctxt->context->node->parent == NULL)
8026 return((xmlNodePtr) ctxt->context->doc);
8027 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
8028 ((ctxt->context->node->parent->name[0] == ' ') ||
8029 (xmlStrEqual(ctxt->context->node->parent->name,
8030 BAD_CAST "fake node libxslt"))))
8031 return(NULL);
8032 return(ctxt->context->node->parent);
8033 case XML_ATTRIBUTE_NODE: {
8034 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
8036 return(tmp->parent);
8038 case XML_DOCUMENT_NODE:
8039 case XML_DOCUMENT_TYPE_NODE:
8040 case XML_DOCUMENT_FRAG_NODE:
8041 case XML_HTML_DOCUMENT_NODE:
8042 #ifdef LIBXML_DOCB_ENABLED
8043 case XML_DOCB_DOCUMENT_NODE:
8044 #endif
8045 return(NULL);
8046 case XML_NAMESPACE_DECL: {
8047 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
8049 if ((ns->next != NULL) &&
8050 (ns->next->type != XML_NAMESPACE_DECL))
8051 return((xmlNodePtr) ns->next);
8052 /* Bad, how did that namespace end up here ? */
8053 return(NULL);
8056 return(NULL);
8058 if (cur == ctxt->context->doc->children)
8059 return((xmlNodePtr) ctxt->context->doc);
8060 if (cur == (xmlNodePtr) ctxt->context->doc)
8061 return(NULL);
8062 switch (cur->type) {
8063 case XML_ELEMENT_NODE:
8064 case XML_TEXT_NODE:
8065 case XML_CDATA_SECTION_NODE:
8066 case XML_ENTITY_REF_NODE:
8067 case XML_ENTITY_NODE:
8068 case XML_PI_NODE:
8069 case XML_COMMENT_NODE:
8070 case XML_NOTATION_NODE:
8071 case XML_DTD_NODE:
8072 case XML_ELEMENT_DECL:
8073 case XML_ATTRIBUTE_DECL:
8074 case XML_ENTITY_DECL:
8075 case XML_XINCLUDE_START:
8076 case XML_XINCLUDE_END:
8077 if (cur->parent == NULL)
8078 return(NULL);
8079 if ((cur->parent->type == XML_ELEMENT_NODE) &&
8080 ((cur->parent->name[0] == ' ') ||
8081 (xmlStrEqual(cur->parent->name,
8082 BAD_CAST "fake node libxslt"))))
8083 return(NULL);
8084 return(cur->parent);
8085 case XML_ATTRIBUTE_NODE: {
8086 xmlAttrPtr att = (xmlAttrPtr) cur;
8088 return(att->parent);
8090 case XML_NAMESPACE_DECL: {
8091 xmlNsPtr ns = (xmlNsPtr) cur;
8093 if ((ns->next != NULL) &&
8094 (ns->next->type != XML_NAMESPACE_DECL))
8095 return((xmlNodePtr) ns->next);
8096 /* Bad, how did that namespace end up here ? */
8097 return(NULL);
8099 case XML_DOCUMENT_NODE:
8100 case XML_DOCUMENT_TYPE_NODE:
8101 case XML_DOCUMENT_FRAG_NODE:
8102 case XML_HTML_DOCUMENT_NODE:
8103 #ifdef LIBXML_DOCB_ENABLED
8104 case XML_DOCB_DOCUMENT_NODE:
8105 #endif
8106 return(NULL);
8108 return(NULL);
8112 * xmlXPathNextAncestorOrSelf:
8113 * @ctxt: the XPath Parser context
8114 * @cur: the current node in the traversal
8116 * Traversal function for the "ancestor-or-self" direction
8117 * he ancestor-or-self axis contains the context node and ancestors of
8118 * the context node in reverse document order; thus the context node is
8119 * the first node on the axis, and the context node's parent the second;
8120 * parent here is defined the same as with the parent axis.
8122 * Returns the next element following that axis
8124 xmlNodePtr
8125 xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8126 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8127 if (cur == NULL)
8128 return(ctxt->context->node);
8129 return(xmlXPathNextAncestor(ctxt, cur));
8133 * xmlXPathNextFollowingSibling:
8134 * @ctxt: the XPath Parser context
8135 * @cur: the current node in the traversal
8137 * Traversal function for the "following-sibling" direction
8138 * The following-sibling axis contains the following siblings of the context
8139 * node in document order.
8141 * Returns the next element following that axis
8143 xmlNodePtr
8144 xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8145 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8146 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8147 (ctxt->context->node->type == XML_NAMESPACE_DECL))
8148 return(NULL);
8149 if (cur == (xmlNodePtr) ctxt->context->doc)
8150 return(NULL);
8151 if (cur == NULL)
8152 return(ctxt->context->node->next);
8153 return(cur->next);
8157 * xmlXPathNextPrecedingSibling:
8158 * @ctxt: the XPath Parser context
8159 * @cur: the current node in the traversal
8161 * Traversal function for the "preceding-sibling" direction
8162 * The preceding-sibling axis contains the preceding siblings of the context
8163 * node in reverse document order; the first preceding sibling is first on the
8164 * axis; the sibling preceding that node is the second on the axis and so on.
8166 * Returns the next element following that axis
8168 xmlNodePtr
8169 xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8170 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8171 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8172 (ctxt->context->node->type == XML_NAMESPACE_DECL))
8173 return(NULL);
8174 if (cur == (xmlNodePtr) ctxt->context->doc)
8175 return(NULL);
8176 if (cur == NULL)
8177 return(ctxt->context->node->prev);
8178 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
8179 cur = cur->prev;
8180 if (cur == NULL)
8181 return(ctxt->context->node->prev);
8183 return(cur->prev);
8187 * xmlXPathNextFollowing:
8188 * @ctxt: the XPath Parser context
8189 * @cur: the current node in the traversal
8191 * Traversal function for the "following" direction
8192 * The following axis contains all nodes in the same document as the context
8193 * node that are after the context node in document order, excluding any
8194 * descendants and excluding attribute nodes and namespace nodes; the nodes
8195 * are ordered in document order
8197 * Returns the next element following that axis
8199 xmlNodePtr
8200 xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8201 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8202 if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
8203 (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
8204 return(cur->children);
8206 if (cur == NULL) {
8207 cur = ctxt->context->node;
8208 if (cur->type == XML_ATTRIBUTE_NODE) {
8209 cur = cur->parent;
8210 } else if (cur->type == XML_NAMESPACE_DECL) {
8211 xmlNsPtr ns = (xmlNsPtr) cur;
8213 if ((ns->next == NULL) ||
8214 (ns->next->type == XML_NAMESPACE_DECL))
8215 return (NULL);
8216 cur = (xmlNodePtr) ns->next;
8219 if (cur == NULL) return(NULL) ; /* ERROR */
8220 if (cur->next != NULL) return(cur->next) ;
8221 do {
8222 cur = cur->parent;
8223 if (cur == NULL) break;
8224 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
8225 if (cur->next != NULL) return(cur->next);
8226 } while (cur != NULL);
8227 return(cur);
8231 * xmlXPathIsAncestor:
8232 * @ancestor: the ancestor node
8233 * @node: the current node
8235 * Check that @ancestor is a @node's ancestor
8237 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
8239 static int
8240 xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
8241 if ((ancestor == NULL) || (node == NULL)) return(0);
8242 if (node->type == XML_NAMESPACE_DECL)
8243 return(0);
8244 if (ancestor->type == XML_NAMESPACE_DECL)
8245 return(0);
8246 /* nodes need to be in the same document */
8247 if (ancestor->doc != node->doc) return(0);
8248 /* avoid searching if ancestor or node is the root node */
8249 if (ancestor == (xmlNodePtr) node->doc) return(1);
8250 if (node == (xmlNodePtr) ancestor->doc) return(0);
8251 while (node->parent != NULL) {
8252 if (node->parent == ancestor)
8253 return(1);
8254 node = node->parent;
8256 return(0);
8260 * xmlXPathNextPreceding:
8261 * @ctxt: the XPath Parser context
8262 * @cur: the current node in the traversal
8264 * Traversal function for the "preceding" direction
8265 * the preceding axis contains all nodes in the same document as the context
8266 * node that are before the context node in document order, excluding any
8267 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8268 * ordered in reverse document order
8270 * Returns the next element following that axis
8272 xmlNodePtr
8273 xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
8275 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8276 if (cur == NULL) {
8277 cur = ctxt->context->node;
8278 if (cur->type == XML_ATTRIBUTE_NODE) {
8279 cur = cur->parent;
8280 } else if (cur->type == XML_NAMESPACE_DECL) {
8281 xmlNsPtr ns = (xmlNsPtr) cur;
8283 if ((ns->next == NULL) ||
8284 (ns->next->type == XML_NAMESPACE_DECL))
8285 return (NULL);
8286 cur = (xmlNodePtr) ns->next;
8289 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
8290 return (NULL);
8291 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8292 cur = cur->prev;
8293 do {
8294 if (cur->prev != NULL) {
8295 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
8296 return (cur);
8299 cur = cur->parent;
8300 if (cur == NULL)
8301 return (NULL);
8302 if (cur == ctxt->context->doc->children)
8303 return (NULL);
8304 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
8305 return (cur);
8309 * xmlXPathNextPrecedingInternal:
8310 * @ctxt: the XPath Parser context
8311 * @cur: the current node in the traversal
8313 * Traversal function for the "preceding" direction
8314 * the preceding axis contains all nodes in the same document as the context
8315 * node that are before the context node in document order, excluding any
8316 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8317 * ordered in reverse document order
8318 * This is a faster implementation but internal only since it requires a
8319 * state kept in the parser context: ctxt->ancestor.
8321 * Returns the next element following that axis
8323 static xmlNodePtr
8324 xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
8325 xmlNodePtr cur)
8327 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8328 if (cur == NULL) {
8329 cur = ctxt->context->node;
8330 if (cur == NULL)
8331 return (NULL);
8332 if (cur->type == XML_ATTRIBUTE_NODE) {
8333 cur = cur->parent;
8334 } else if (cur->type == XML_NAMESPACE_DECL) {
8335 xmlNsPtr ns = (xmlNsPtr) cur;
8337 if ((ns->next == NULL) ||
8338 (ns->next->type == XML_NAMESPACE_DECL))
8339 return (NULL);
8340 cur = (xmlNodePtr) ns->next;
8342 ctxt->ancestor = cur->parent;
8344 if (cur->type == XML_NAMESPACE_DECL)
8345 return(NULL);
8346 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8347 cur = cur->prev;
8348 while (cur->prev == NULL) {
8349 cur = cur->parent;
8350 if (cur == NULL)
8351 return (NULL);
8352 if (cur == ctxt->context->doc->children)
8353 return (NULL);
8354 if (cur != ctxt->ancestor)
8355 return (cur);
8356 ctxt->ancestor = cur->parent;
8358 cur = cur->prev;
8359 while (cur->last != NULL)
8360 cur = cur->last;
8361 return (cur);
8365 * xmlXPathNextNamespace:
8366 * @ctxt: the XPath Parser context
8367 * @cur: the current attribute in the traversal
8369 * Traversal function for the "namespace" direction
8370 * the namespace axis contains the namespace nodes of the context node;
8371 * the order of nodes on this axis is implementation-defined; the axis will
8372 * be empty unless the context node is an element
8374 * We keep the XML namespace node at the end of the list.
8376 * Returns the next element following that axis
8378 xmlNodePtr
8379 xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8380 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8381 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
8382 if (cur == NULL) {
8383 if (ctxt->context->tmpNsList != NULL)
8384 xmlFree(ctxt->context->tmpNsList);
8385 ctxt->context->tmpNsList =
8386 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
8387 ctxt->context->tmpNsNr = 0;
8388 if (ctxt->context->tmpNsList != NULL) {
8389 while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
8390 ctxt->context->tmpNsNr++;
8393 return((xmlNodePtr) xmlXPathXMLNamespace);
8395 if (ctxt->context->tmpNsNr > 0) {
8396 return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
8397 } else {
8398 if (ctxt->context->tmpNsList != NULL)
8399 xmlFree(ctxt->context->tmpNsList);
8400 ctxt->context->tmpNsList = NULL;
8401 return(NULL);
8406 * xmlXPathNextAttribute:
8407 * @ctxt: the XPath Parser context
8408 * @cur: the current attribute in the traversal
8410 * Traversal function for the "attribute" direction
8411 * TODO: support DTD inherited default attributes
8413 * Returns the next element following that axis
8415 xmlNodePtr
8416 xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8417 if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8418 if (ctxt->context->node == NULL)
8419 return(NULL);
8420 if (ctxt->context->node->type != XML_ELEMENT_NODE)
8421 return(NULL);
8422 if (cur == NULL) {
8423 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
8424 return(NULL);
8425 return((xmlNodePtr)ctxt->context->node->properties);
8427 return((xmlNodePtr)cur->next);
8430 /************************************************************************
8432 * NodeTest Functions *
8434 ************************************************************************/
8436 #define IS_FUNCTION 200
8439 /************************************************************************
8441 * Implicit tree core function library *
8443 ************************************************************************/
8446 * xmlXPathRoot:
8447 * @ctxt: the XPath Parser context
8449 * Initialize the context to the root of the document
8451 void
8452 xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
8453 if ((ctxt == NULL) || (ctxt->context == NULL))
8454 return;
8455 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8456 (xmlNodePtr) ctxt->context->doc));
8459 /************************************************************************
8461 * The explicit core function library *
8462 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
8464 ************************************************************************/
8468 * xmlXPathLastFunction:
8469 * @ctxt: the XPath Parser context
8470 * @nargs: the number of arguments
8472 * Implement the last() XPath function
8473 * number last()
8474 * The last function returns the number of nodes in the context node list.
8476 void
8477 xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8478 CHECK_ARITY(0);
8479 if (ctxt->context->contextSize >= 0) {
8480 valuePush(ctxt,
8481 xmlXPathCacheNewFloat(ctxt->context,
8482 (double) ctxt->context->contextSize));
8483 #ifdef DEBUG_EXPR
8484 xmlGenericError(xmlGenericErrorContext,
8485 "last() : %d\n", ctxt->context->contextSize);
8486 #endif
8487 } else {
8488 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
8493 * xmlXPathPositionFunction:
8494 * @ctxt: the XPath Parser context
8495 * @nargs: the number of arguments
8497 * Implement the position() XPath function
8498 * number position()
8499 * The position function returns the position of the context node in the
8500 * context node list. The first position is 1, and so the last position
8501 * will be equal to last().
8503 void
8504 xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8505 CHECK_ARITY(0);
8506 if (ctxt->context->proximityPosition >= 0) {
8507 valuePush(ctxt,
8508 xmlXPathCacheNewFloat(ctxt->context,
8509 (double) ctxt->context->proximityPosition));
8510 #ifdef DEBUG_EXPR
8511 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
8512 ctxt->context->proximityPosition);
8513 #endif
8514 } else {
8515 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
8520 * xmlXPathCountFunction:
8521 * @ctxt: the XPath Parser context
8522 * @nargs: the number of arguments
8524 * Implement the count() XPath function
8525 * number count(node-set)
8527 void
8528 xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8529 xmlXPathObjectPtr cur;
8531 CHECK_ARITY(1);
8532 if ((ctxt->value == NULL) ||
8533 ((ctxt->value->type != XPATH_NODESET) &&
8534 (ctxt->value->type != XPATH_XSLT_TREE)))
8535 XP_ERROR(XPATH_INVALID_TYPE);
8536 cur = valuePop(ctxt);
8538 if ((cur == NULL) || (cur->nodesetval == NULL))
8539 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8540 else
8541 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8542 (double) cur->nodesetval->nodeNr));
8543 xmlXPathReleaseObject(ctxt->context, cur);
8547 * xmlXPathGetElementsByIds:
8548 * @doc: the document
8549 * @ids: a whitespace separated list of IDs
8551 * Selects elements by their unique ID.
8553 * Returns a node-set of selected elements.
8555 static xmlNodeSetPtr
8556 xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
8557 xmlNodeSetPtr ret;
8558 const xmlChar *cur = ids;
8559 xmlChar *ID;
8560 xmlAttrPtr attr;
8561 xmlNodePtr elem = NULL;
8563 if (ids == NULL) return(NULL);
8565 ret = xmlXPathNodeSetCreate(NULL);
8566 if (ret == NULL)
8567 return(ret);
8569 while (IS_BLANK_CH(*cur)) cur++;
8570 while (*cur != 0) {
8571 while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
8572 cur++;
8574 ID = xmlStrndup(ids, cur - ids);
8575 if (ID != NULL) {
8577 * We used to check the fact that the value passed
8578 * was an NCName, but this generated much troubles for
8579 * me and Aleksey Sanin, people blatantly violated that
8580 * constraint, like Visa3D spec.
8581 * if (xmlValidateNCName(ID, 1) == 0)
8583 attr = xmlGetID(doc, ID);
8584 if (attr != NULL) {
8585 if (attr->type == XML_ATTRIBUTE_NODE)
8586 elem = attr->parent;
8587 else if (attr->type == XML_ELEMENT_NODE)
8588 elem = (xmlNodePtr) attr;
8589 else
8590 elem = NULL;
8591 /* TODO: Check memory error. */
8592 if (elem != NULL)
8593 xmlXPathNodeSetAdd(ret, elem);
8595 xmlFree(ID);
8598 while (IS_BLANK_CH(*cur)) cur++;
8599 ids = cur;
8601 return(ret);
8605 * xmlXPathIdFunction:
8606 * @ctxt: the XPath Parser context
8607 * @nargs: the number of arguments
8609 * Implement the id() XPath function
8610 * node-set id(object)
8611 * The id function selects elements by their unique ID
8612 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
8613 * then the result is the union of the result of applying id to the
8614 * string value of each of the nodes in the argument node-set. When the
8615 * argument to id is of any other type, the argument is converted to a
8616 * string as if by a call to the string function; the string is split
8617 * into a whitespace-separated list of tokens (whitespace is any sequence
8618 * of characters matching the production S); the result is a node-set
8619 * containing the elements in the same document as the context node that
8620 * have a unique ID equal to any of the tokens in the list.
8622 void
8623 xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8624 xmlChar *tokens;
8625 xmlNodeSetPtr ret;
8626 xmlXPathObjectPtr obj;
8628 CHECK_ARITY(1);
8629 obj = valuePop(ctxt);
8630 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8631 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
8632 xmlNodeSetPtr ns;
8633 int i;
8635 /* TODO: Check memory error. */
8636 ret = xmlXPathNodeSetCreate(NULL);
8638 if (obj->nodesetval != NULL) {
8639 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
8640 tokens =
8641 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
8642 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
8643 /* TODO: Check memory error. */
8644 ret = xmlXPathNodeSetMerge(ret, ns);
8645 xmlXPathFreeNodeSet(ns);
8646 if (tokens != NULL)
8647 xmlFree(tokens);
8650 xmlXPathReleaseObject(ctxt->context, obj);
8651 valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8652 return;
8654 obj = xmlXPathCacheConvertString(ctxt->context, obj);
8655 if (obj == NULL) return;
8656 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
8657 valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8658 xmlXPathReleaseObject(ctxt->context, obj);
8659 return;
8663 * xmlXPathLocalNameFunction:
8664 * @ctxt: the XPath Parser context
8665 * @nargs: the number of arguments
8667 * Implement the local-name() XPath function
8668 * string local-name(node-set?)
8669 * The local-name function returns a string containing the local part
8670 * of the name of the node in the argument node-set that is first in
8671 * document order. If the node-set is empty or the first node has no
8672 * name, an empty string is returned. If the argument is omitted it
8673 * defaults to the context node.
8675 void
8676 xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8677 xmlXPathObjectPtr cur;
8679 if (ctxt == NULL) return;
8681 if (nargs == 0) {
8682 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8683 ctxt->context->node));
8684 nargs = 1;
8687 CHECK_ARITY(1);
8688 if ((ctxt->value == NULL) ||
8689 ((ctxt->value->type != XPATH_NODESET) &&
8690 (ctxt->value->type != XPATH_XSLT_TREE)))
8691 XP_ERROR(XPATH_INVALID_TYPE);
8692 cur = valuePop(ctxt);
8694 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8695 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8696 } else {
8697 int i = 0; /* Should be first in document order !!!!! */
8698 switch (cur->nodesetval->nodeTab[i]->type) {
8699 case XML_ELEMENT_NODE:
8700 case XML_ATTRIBUTE_NODE:
8701 case XML_PI_NODE:
8702 if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8703 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8704 else
8705 valuePush(ctxt,
8706 xmlXPathCacheNewString(ctxt->context,
8707 cur->nodesetval->nodeTab[i]->name));
8708 break;
8709 case XML_NAMESPACE_DECL:
8710 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8711 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
8712 break;
8713 default:
8714 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8717 xmlXPathReleaseObject(ctxt->context, cur);
8721 * xmlXPathNamespaceURIFunction:
8722 * @ctxt: the XPath Parser context
8723 * @nargs: the number of arguments
8725 * Implement the namespace-uri() XPath function
8726 * string namespace-uri(node-set?)
8727 * The namespace-uri function returns a string containing the
8728 * namespace URI of the expanded name of the node in the argument
8729 * node-set that is first in document order. If the node-set is empty,
8730 * the first node has no name, or the expanded name has no namespace
8731 * URI, an empty string is returned. If the argument is omitted it
8732 * defaults to the context node.
8734 void
8735 xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8736 xmlXPathObjectPtr cur;
8738 if (ctxt == NULL) return;
8740 if (nargs == 0) {
8741 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8742 ctxt->context->node));
8743 nargs = 1;
8745 CHECK_ARITY(1);
8746 if ((ctxt->value == NULL) ||
8747 ((ctxt->value->type != XPATH_NODESET) &&
8748 (ctxt->value->type != XPATH_XSLT_TREE)))
8749 XP_ERROR(XPATH_INVALID_TYPE);
8750 cur = valuePop(ctxt);
8752 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8753 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8754 } else {
8755 int i = 0; /* Should be first in document order !!!!! */
8756 switch (cur->nodesetval->nodeTab[i]->type) {
8757 case XML_ELEMENT_NODE:
8758 case XML_ATTRIBUTE_NODE:
8759 if (cur->nodesetval->nodeTab[i]->ns == NULL)
8760 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8761 else
8762 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8763 cur->nodesetval->nodeTab[i]->ns->href));
8764 break;
8765 default:
8766 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8769 xmlXPathReleaseObject(ctxt->context, cur);
8773 * xmlXPathNameFunction:
8774 * @ctxt: the XPath Parser context
8775 * @nargs: the number of arguments
8777 * Implement the name() XPath function
8778 * string name(node-set?)
8779 * The name function returns a string containing a QName representing
8780 * the name of the node in the argument node-set that is first in document
8781 * order. The QName must represent the name with respect to the namespace
8782 * declarations in effect on the node whose name is being represented.
8783 * Typically, this will be the form in which the name occurred in the XML
8784 * source. This need not be the case if there are namespace declarations
8785 * in effect on the node that associate multiple prefixes with the same
8786 * namespace. However, an implementation may include information about
8787 * the original prefix in its representation of nodes; in this case, an
8788 * implementation can ensure that the returned string is always the same
8789 * as the QName used in the XML source. If the argument it omitted it
8790 * defaults to the context node.
8791 * Libxml keep the original prefix so the "real qualified name" used is
8792 * returned.
8794 static void
8795 xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
8797 xmlXPathObjectPtr cur;
8799 if (nargs == 0) {
8800 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8801 ctxt->context->node));
8802 nargs = 1;
8805 CHECK_ARITY(1);
8806 if ((ctxt->value == NULL) ||
8807 ((ctxt->value->type != XPATH_NODESET) &&
8808 (ctxt->value->type != XPATH_XSLT_TREE)))
8809 XP_ERROR(XPATH_INVALID_TYPE);
8810 cur = valuePop(ctxt);
8812 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8813 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8814 } else {
8815 int i = 0; /* Should be first in document order !!!!! */
8817 switch (cur->nodesetval->nodeTab[i]->type) {
8818 case XML_ELEMENT_NODE:
8819 case XML_ATTRIBUTE_NODE:
8820 if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8821 valuePush(ctxt,
8822 xmlXPathCacheNewCString(ctxt->context, ""));
8823 else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
8824 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
8825 valuePush(ctxt,
8826 xmlXPathCacheNewString(ctxt->context,
8827 cur->nodesetval->nodeTab[i]->name));
8828 } else {
8829 xmlChar *fullname;
8831 fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
8832 cur->nodesetval->nodeTab[i]->ns->prefix,
8833 NULL, 0);
8834 if (fullname == cur->nodesetval->nodeTab[i]->name)
8835 fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
8836 if (fullname == NULL) {
8837 XP_ERROR(XPATH_MEMORY_ERROR);
8839 valuePush(ctxt, xmlXPathCacheWrapString(
8840 ctxt->context, fullname));
8842 break;
8843 default:
8844 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8845 cur->nodesetval->nodeTab[i]));
8846 xmlXPathLocalNameFunction(ctxt, 1);
8849 xmlXPathReleaseObject(ctxt->context, cur);
8854 * xmlXPathStringFunction:
8855 * @ctxt: the XPath Parser context
8856 * @nargs: the number of arguments
8858 * Implement the string() XPath function
8859 * string string(object?)
8860 * The string function converts an object to a string as follows:
8861 * - A node-set is converted to a string by returning the value of
8862 * the node in the node-set that is first in document order.
8863 * If the node-set is empty, an empty string is returned.
8864 * - A number is converted to a string as follows
8865 * + NaN is converted to the string NaN
8866 * + positive zero is converted to the string 0
8867 * + negative zero is converted to the string 0
8868 * + positive infinity is converted to the string Infinity
8869 * + negative infinity is converted to the string -Infinity
8870 * + if the number is an integer, the number is represented in
8871 * decimal form as a Number with no decimal point and no leading
8872 * zeros, preceded by a minus sign (-) if the number is negative
8873 * + otherwise, the number is represented in decimal form as a
8874 * Number including a decimal point with at least one digit
8875 * before the decimal point and at least one digit after the
8876 * decimal point, preceded by a minus sign (-) if the number
8877 * is negative; there must be no leading zeros before the decimal
8878 * point apart possibly from the one required digit immediately
8879 * before the decimal point; beyond the one required digit
8880 * after the decimal point there must be as many, but only as
8881 * many, more digits as are needed to uniquely distinguish the
8882 * number from all other IEEE 754 numeric values.
8883 * - The boolean false value is converted to the string false.
8884 * The boolean true value is converted to the string true.
8886 * If the argument is omitted, it defaults to a node-set with the
8887 * context node as its only member.
8889 void
8890 xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8891 xmlXPathObjectPtr cur;
8893 if (ctxt == NULL) return;
8894 if (nargs == 0) {
8895 valuePush(ctxt,
8896 xmlXPathCacheWrapString(ctxt->context,
8897 xmlXPathCastNodeToString(ctxt->context->node)));
8898 return;
8901 CHECK_ARITY(1);
8902 cur = valuePop(ctxt);
8903 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8904 valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
8908 * xmlXPathStringLengthFunction:
8909 * @ctxt: the XPath Parser context
8910 * @nargs: the number of arguments
8912 * Implement the string-length() XPath function
8913 * number string-length(string?)
8914 * The string-length returns the number of characters in the string
8915 * (see [3.6 Strings]). If the argument is omitted, it defaults to
8916 * the context node converted to a string, in other words the value
8917 * of the context node.
8919 void
8920 xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8921 xmlXPathObjectPtr cur;
8923 if (nargs == 0) {
8924 if ((ctxt == NULL) || (ctxt->context == NULL))
8925 return;
8926 if (ctxt->context->node == NULL) {
8927 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
8928 } else {
8929 xmlChar *content;
8931 content = xmlXPathCastNodeToString(ctxt->context->node);
8932 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8933 xmlUTF8Strlen(content)));
8934 xmlFree(content);
8936 return;
8938 CHECK_ARITY(1);
8939 CAST_TO_STRING;
8940 CHECK_TYPE(XPATH_STRING);
8941 cur = valuePop(ctxt);
8942 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8943 xmlUTF8Strlen(cur->stringval)));
8944 xmlXPathReleaseObject(ctxt->context, cur);
8948 * xmlXPathConcatFunction:
8949 * @ctxt: the XPath Parser context
8950 * @nargs: the number of arguments
8952 * Implement the concat() XPath function
8953 * string concat(string, string, string*)
8954 * The concat function returns the concatenation of its arguments.
8956 void
8957 xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8958 xmlXPathObjectPtr cur, newobj;
8959 xmlChar *tmp;
8961 if (ctxt == NULL) return;
8962 if (nargs < 2) {
8963 CHECK_ARITY(2);
8966 CAST_TO_STRING;
8967 cur = valuePop(ctxt);
8968 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
8969 xmlXPathReleaseObject(ctxt->context, cur);
8970 return;
8972 nargs--;
8974 while (nargs > 0) {
8975 CAST_TO_STRING;
8976 newobj = valuePop(ctxt);
8977 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
8978 xmlXPathReleaseObject(ctxt->context, newobj);
8979 xmlXPathReleaseObject(ctxt->context, cur);
8980 XP_ERROR(XPATH_INVALID_TYPE);
8982 tmp = xmlStrcat(newobj->stringval, cur->stringval);
8983 newobj->stringval = cur->stringval;
8984 cur->stringval = tmp;
8985 xmlXPathReleaseObject(ctxt->context, newobj);
8986 nargs--;
8988 valuePush(ctxt, cur);
8992 * xmlXPathContainsFunction:
8993 * @ctxt: the XPath Parser context
8994 * @nargs: the number of arguments
8996 * Implement the contains() XPath function
8997 * boolean contains(string, string)
8998 * The contains function returns true if the first argument string
8999 * contains the second argument string, and otherwise returns false.
9001 void
9002 xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9003 xmlXPathObjectPtr hay, needle;
9005 CHECK_ARITY(2);
9006 CAST_TO_STRING;
9007 CHECK_TYPE(XPATH_STRING);
9008 needle = valuePop(ctxt);
9009 CAST_TO_STRING;
9010 hay = valuePop(ctxt);
9012 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
9013 xmlXPathReleaseObject(ctxt->context, hay);
9014 xmlXPathReleaseObject(ctxt->context, needle);
9015 XP_ERROR(XPATH_INVALID_TYPE);
9017 if (xmlStrstr(hay->stringval, needle->stringval))
9018 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9019 else
9020 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9021 xmlXPathReleaseObject(ctxt->context, hay);
9022 xmlXPathReleaseObject(ctxt->context, needle);
9026 * xmlXPathStartsWithFunction:
9027 * @ctxt: the XPath Parser context
9028 * @nargs: the number of arguments
9030 * Implement the starts-with() XPath function
9031 * boolean starts-with(string, string)
9032 * The starts-with function returns true if the first argument string
9033 * starts with the second argument string, and otherwise returns false.
9035 void
9036 xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9037 xmlXPathObjectPtr hay, needle;
9038 int n;
9040 CHECK_ARITY(2);
9041 CAST_TO_STRING;
9042 CHECK_TYPE(XPATH_STRING);
9043 needle = valuePop(ctxt);
9044 CAST_TO_STRING;
9045 hay = valuePop(ctxt);
9047 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
9048 xmlXPathReleaseObject(ctxt->context, hay);
9049 xmlXPathReleaseObject(ctxt->context, needle);
9050 XP_ERROR(XPATH_INVALID_TYPE);
9052 n = xmlStrlen(needle->stringval);
9053 if (xmlStrncmp(hay->stringval, needle->stringval, n))
9054 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9055 else
9056 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9057 xmlXPathReleaseObject(ctxt->context, hay);
9058 xmlXPathReleaseObject(ctxt->context, needle);
9062 * xmlXPathSubstringFunction:
9063 * @ctxt: the XPath Parser context
9064 * @nargs: the number of arguments
9066 * Implement the substring() XPath function
9067 * string substring(string, number, number?)
9068 * The substring function returns the substring of the first argument
9069 * starting at the position specified in the second argument with
9070 * length specified in the third argument. For example,
9071 * substring("12345",2,3) returns "234". If the third argument is not
9072 * specified, it returns the substring starting at the position specified
9073 * in the second argument and continuing to the end of the string. For
9074 * example, substring("12345",2) returns "2345". More precisely, each
9075 * character in the string (see [3.6 Strings]) is considered to have a
9076 * numeric position: the position of the first character is 1, the position
9077 * of the second character is 2 and so on. The returned substring contains
9078 * those characters for which the position of the character is greater than
9079 * or equal to the second argument and, if the third argument is specified,
9080 * less than the sum of the second and third arguments; the comparisons
9081 * and addition used for the above follow the standard IEEE 754 rules. Thus:
9082 * - substring("12345", 1.5, 2.6) returns "234"
9083 * - substring("12345", 0, 3) returns "12"
9084 * - substring("12345", 0 div 0, 3) returns ""
9085 * - substring("12345", 1, 0 div 0) returns ""
9086 * - substring("12345", -42, 1 div 0) returns "12345"
9087 * - substring("12345", -1 div 0, 1 div 0) returns ""
9089 void
9090 xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9091 xmlXPathObjectPtr str, start, len;
9092 double le=0, in;
9093 int i = 1, j = INT_MAX;
9095 if (nargs < 2) {
9096 CHECK_ARITY(2);
9098 if (nargs > 3) {
9099 CHECK_ARITY(3);
9102 * take care of possible last (position) argument
9104 if (nargs == 3) {
9105 CAST_TO_NUMBER;
9106 CHECK_TYPE(XPATH_NUMBER);
9107 len = valuePop(ctxt);
9108 le = len->floatval;
9109 xmlXPathReleaseObject(ctxt->context, len);
9112 CAST_TO_NUMBER;
9113 CHECK_TYPE(XPATH_NUMBER);
9114 start = valuePop(ctxt);
9115 in = start->floatval;
9116 xmlXPathReleaseObject(ctxt->context, start);
9117 CAST_TO_STRING;
9118 CHECK_TYPE(XPATH_STRING);
9119 str = valuePop(ctxt);
9121 if (!(in < INT_MAX)) { /* Logical NOT to handle NaNs */
9122 i = INT_MAX;
9123 } else if (in >= 1.0) {
9124 i = (int)in;
9125 if (in - floor(in) >= 0.5)
9126 i += 1;
9129 if (nargs == 3) {
9130 double rin, rle, end;
9132 rin = floor(in);
9133 if (in - rin >= 0.5)
9134 rin += 1.0;
9136 rle = floor(le);
9137 if (le - rle >= 0.5)
9138 rle += 1.0;
9140 end = rin + rle;
9141 if (!(end >= 1.0)) { /* Logical NOT to handle NaNs */
9142 j = 1;
9143 } else if (end < INT_MAX) {
9144 j = (int)end;
9148 if (i < j) {
9149 xmlChar *ret = xmlUTF8Strsub(str->stringval, i - 1, j - i);
9150 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
9151 xmlFree(ret);
9152 } else {
9153 valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
9156 xmlXPathReleaseObject(ctxt->context, str);
9160 * xmlXPathSubstringBeforeFunction:
9161 * @ctxt: the XPath Parser context
9162 * @nargs: the number of arguments
9164 * Implement the substring-before() XPath function
9165 * string substring-before(string, string)
9166 * The substring-before function returns the substring of the first
9167 * argument string that precedes the first occurrence of the second
9168 * argument string in the first argument string, or the empty string
9169 * if the first argument string does not contain the second argument
9170 * string. For example, substring-before("1999/04/01","/") returns 1999.
9172 void
9173 xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9174 xmlXPathObjectPtr str;
9175 xmlXPathObjectPtr find;
9176 xmlBufPtr target;
9177 const xmlChar *point;
9178 int offset;
9180 CHECK_ARITY(2);
9181 CAST_TO_STRING;
9182 find = valuePop(ctxt);
9183 CAST_TO_STRING;
9184 str = valuePop(ctxt);
9186 target = xmlBufCreate();
9187 if (target) {
9188 point = xmlStrstr(str->stringval, find->stringval);
9189 if (point) {
9190 offset = (int)(point - str->stringval);
9191 xmlBufAdd(target, str->stringval, offset);
9193 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9194 xmlBufContent(target)));
9195 xmlBufFree(target);
9197 xmlXPathReleaseObject(ctxt->context, str);
9198 xmlXPathReleaseObject(ctxt->context, find);
9202 * xmlXPathSubstringAfterFunction:
9203 * @ctxt: the XPath Parser context
9204 * @nargs: the number of arguments
9206 * Implement the substring-after() XPath function
9207 * string substring-after(string, string)
9208 * The substring-after function returns the substring of the first
9209 * argument string that follows the first occurrence of the second
9210 * argument string in the first argument string, or the empty stringi
9211 * if the first argument string does not contain the second argument
9212 * string. For example, substring-after("1999/04/01","/") returns 04/01,
9213 * and substring-after("1999/04/01","19") returns 99/04/01.
9215 void
9216 xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9217 xmlXPathObjectPtr str;
9218 xmlXPathObjectPtr find;
9219 xmlBufPtr target;
9220 const xmlChar *point;
9221 int offset;
9223 CHECK_ARITY(2);
9224 CAST_TO_STRING;
9225 find = valuePop(ctxt);
9226 CAST_TO_STRING;
9227 str = valuePop(ctxt);
9229 target = xmlBufCreate();
9230 if (target) {
9231 point = xmlStrstr(str->stringval, find->stringval);
9232 if (point) {
9233 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
9234 xmlBufAdd(target, &str->stringval[offset],
9235 xmlStrlen(str->stringval) - offset);
9237 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9238 xmlBufContent(target)));
9239 xmlBufFree(target);
9241 xmlXPathReleaseObject(ctxt->context, str);
9242 xmlXPathReleaseObject(ctxt->context, find);
9246 * xmlXPathNormalizeFunction:
9247 * @ctxt: the XPath Parser context
9248 * @nargs: the number of arguments
9250 * Implement the normalize-space() XPath function
9251 * string normalize-space(string?)
9252 * The normalize-space function returns the argument string with white
9253 * space normalized by stripping leading and trailing whitespace
9254 * and replacing sequences of whitespace characters by a single
9255 * space. Whitespace characters are the same allowed by the S production
9256 * in XML. If the argument is omitted, it defaults to the context
9257 * node converted to a string, in other words the value of the context node.
9259 void
9260 xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9261 xmlXPathObjectPtr obj = NULL;
9262 xmlChar *source = NULL;
9263 xmlBufPtr target;
9264 xmlChar blank;
9266 if (ctxt == NULL) return;
9267 if (nargs == 0) {
9268 /* Use current context node */
9269 valuePush(ctxt,
9270 xmlXPathCacheWrapString(ctxt->context,
9271 xmlXPathCastNodeToString(ctxt->context->node)));
9272 nargs = 1;
9275 CHECK_ARITY(1);
9276 CAST_TO_STRING;
9277 CHECK_TYPE(XPATH_STRING);
9278 obj = valuePop(ctxt);
9279 source = obj->stringval;
9281 target = xmlBufCreate();
9282 if (target && source) {
9284 /* Skip leading whitespaces */
9285 while (IS_BLANK_CH(*source))
9286 source++;
9288 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
9289 blank = 0;
9290 while (*source) {
9291 if (IS_BLANK_CH(*source)) {
9292 blank = 0x20;
9293 } else {
9294 if (blank) {
9295 xmlBufAdd(target, &blank, 1);
9296 blank = 0;
9298 xmlBufAdd(target, source, 1);
9300 source++;
9302 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9303 xmlBufContent(target)));
9304 xmlBufFree(target);
9306 xmlXPathReleaseObject(ctxt->context, obj);
9310 * xmlXPathTranslateFunction:
9311 * @ctxt: the XPath Parser context
9312 * @nargs: the number of arguments
9314 * Implement the translate() XPath function
9315 * string translate(string, string, string)
9316 * The translate function returns the first argument string with
9317 * occurrences of characters in the second argument string replaced
9318 * by the character at the corresponding position in the third argument
9319 * string. For example, translate("bar","abc","ABC") returns the string
9320 * BAr. If there is a character in the second argument string with no
9321 * character at a corresponding position in the third argument string
9322 * (because the second argument string is longer than the third argument
9323 * string), then occurrences of that character in the first argument
9324 * string are removed. For example, translate("--aaa--","abc-","ABC")
9325 * returns "AAA". If a character occurs more than once in second
9326 * argument string, then the first occurrence determines the replacement
9327 * character. If the third argument string is longer than the second
9328 * argument string, then excess characters are ignored.
9330 void
9331 xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9332 xmlXPathObjectPtr str;
9333 xmlXPathObjectPtr from;
9334 xmlXPathObjectPtr to;
9335 xmlBufPtr target;
9336 int offset, max;
9337 xmlChar ch;
9338 const xmlChar *point;
9339 xmlChar *cptr;
9341 CHECK_ARITY(3);
9343 CAST_TO_STRING;
9344 to = valuePop(ctxt);
9345 CAST_TO_STRING;
9346 from = valuePop(ctxt);
9347 CAST_TO_STRING;
9348 str = valuePop(ctxt);
9350 target = xmlBufCreate();
9351 if (target) {
9352 max = xmlUTF8Strlen(to->stringval);
9353 for (cptr = str->stringval; (ch=*cptr); ) {
9354 offset = xmlUTF8Strloc(from->stringval, cptr);
9355 if (offset >= 0) {
9356 if (offset < max) {
9357 point = xmlUTF8Strpos(to->stringval, offset);
9358 if (point)
9359 xmlBufAdd(target, point, xmlUTF8Strsize(point, 1));
9361 } else
9362 xmlBufAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
9364 /* Step to next character in input */
9365 cptr++;
9366 if ( ch & 0x80 ) {
9367 /* if not simple ascii, verify proper format */
9368 if ( (ch & 0xc0) != 0xc0 ) {
9369 xmlGenericError(xmlGenericErrorContext,
9370 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9371 /* not asserting an XPath error is probably better */
9372 break;
9374 /* then skip over remaining bytes for this char */
9375 while ( (ch <<= 1) & 0x80 )
9376 if ( (*cptr++ & 0xc0) != 0x80 ) {
9377 xmlGenericError(xmlGenericErrorContext,
9378 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9379 /* not asserting an XPath error is probably better */
9380 break;
9382 if (ch & 0x80) /* must have had error encountered */
9383 break;
9387 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9388 xmlBufContent(target)));
9389 xmlBufFree(target);
9390 xmlXPathReleaseObject(ctxt->context, str);
9391 xmlXPathReleaseObject(ctxt->context, from);
9392 xmlXPathReleaseObject(ctxt->context, to);
9396 * xmlXPathBooleanFunction:
9397 * @ctxt: the XPath Parser context
9398 * @nargs: the number of arguments
9400 * Implement the boolean() XPath function
9401 * boolean boolean(object)
9402 * The boolean function converts its argument to a boolean as follows:
9403 * - a number is true if and only if it is neither positive or
9404 * negative zero nor NaN
9405 * - a node-set is true if and only if it is non-empty
9406 * - a string is true if and only if its length is non-zero
9408 void
9409 xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9410 xmlXPathObjectPtr cur;
9412 CHECK_ARITY(1);
9413 cur = valuePop(ctxt);
9414 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
9415 cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
9416 valuePush(ctxt, cur);
9420 * xmlXPathNotFunction:
9421 * @ctxt: the XPath Parser context
9422 * @nargs: the number of arguments
9424 * Implement the not() XPath function
9425 * boolean not(boolean)
9426 * The not function returns true if its argument is false,
9427 * and false otherwise.
9429 void
9430 xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9431 CHECK_ARITY(1);
9432 CAST_TO_BOOLEAN;
9433 CHECK_TYPE(XPATH_BOOLEAN);
9434 ctxt->value->boolval = ! ctxt->value->boolval;
9438 * xmlXPathTrueFunction:
9439 * @ctxt: the XPath Parser context
9440 * @nargs: the number of arguments
9442 * Implement the true() XPath function
9443 * boolean true()
9445 void
9446 xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9447 CHECK_ARITY(0);
9448 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9452 * xmlXPathFalseFunction:
9453 * @ctxt: the XPath Parser context
9454 * @nargs: the number of arguments
9456 * Implement the false() XPath function
9457 * boolean false()
9459 void
9460 xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9461 CHECK_ARITY(0);
9462 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9466 * xmlXPathLangFunction:
9467 * @ctxt: the XPath Parser context
9468 * @nargs: the number of arguments
9470 * Implement the lang() XPath function
9471 * boolean lang(string)
9472 * The lang function returns true or false depending on whether the
9473 * language of the context node as specified by xml:lang attributes
9474 * is the same as or is a sublanguage of the language specified by
9475 * the argument string. The language of the context node is determined
9476 * by the value of the xml:lang attribute on the context node, or, if
9477 * the context node has no xml:lang attribute, by the value of the
9478 * xml:lang attribute on the nearest ancestor of the context node that
9479 * has an xml:lang attribute. If there is no such attribute, then lang
9480 * returns false. If there is such an attribute, then lang returns
9481 * true if the attribute value is equal to the argument ignoring case,
9482 * or if there is some suffix starting with - such that the attribute
9483 * value is equal to the argument ignoring that suffix of the attribute
9484 * value and ignoring case.
9486 void
9487 xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9488 xmlXPathObjectPtr val = NULL;
9489 const xmlChar *theLang = NULL;
9490 const xmlChar *lang;
9491 int ret = 0;
9492 int i;
9494 CHECK_ARITY(1);
9495 CAST_TO_STRING;
9496 CHECK_TYPE(XPATH_STRING);
9497 val = valuePop(ctxt);
9498 lang = val->stringval;
9499 theLang = xmlNodeGetLang(ctxt->context->node);
9500 if ((theLang != NULL) && (lang != NULL)) {
9501 for (i = 0;lang[i] != 0;i++)
9502 if (toupper(lang[i]) != toupper(theLang[i]))
9503 goto not_equal;
9504 if ((theLang[i] == 0) || (theLang[i] == '-'))
9505 ret = 1;
9507 not_equal:
9508 if (theLang != NULL)
9509 xmlFree((void *)theLang);
9511 xmlXPathReleaseObject(ctxt->context, val);
9512 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
9516 * xmlXPathNumberFunction:
9517 * @ctxt: the XPath Parser context
9518 * @nargs: the number of arguments
9520 * Implement the number() XPath function
9521 * number number(object?)
9523 void
9524 xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9525 xmlXPathObjectPtr cur;
9526 double res;
9528 if (ctxt == NULL) return;
9529 if (nargs == 0) {
9530 if (ctxt->context->node == NULL) {
9531 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
9532 } else {
9533 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
9535 res = xmlXPathStringEvalNumber(content);
9536 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9537 xmlFree(content);
9539 return;
9542 CHECK_ARITY(1);
9543 cur = valuePop(ctxt);
9544 valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
9548 * xmlXPathSumFunction:
9549 * @ctxt: the XPath Parser context
9550 * @nargs: the number of arguments
9552 * Implement the sum() XPath function
9553 * number sum(node-set)
9554 * The sum function returns the sum of the values of the nodes in
9555 * the argument node-set.
9557 void
9558 xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9559 xmlXPathObjectPtr cur;
9560 int i;
9561 double res = 0.0;
9563 CHECK_ARITY(1);
9564 if ((ctxt->value == NULL) ||
9565 ((ctxt->value->type != XPATH_NODESET) &&
9566 (ctxt->value->type != XPATH_XSLT_TREE)))
9567 XP_ERROR(XPATH_INVALID_TYPE);
9568 cur = valuePop(ctxt);
9570 if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
9571 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
9572 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
9575 valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9576 xmlXPathReleaseObject(ctxt->context, cur);
9580 * xmlXPathFloorFunction:
9581 * @ctxt: the XPath Parser context
9582 * @nargs: the number of arguments
9584 * Implement the floor() XPath function
9585 * number floor(number)
9586 * The floor function returns the largest (closest to positive infinity)
9587 * number that is not greater than the argument and that is an integer.
9589 void
9590 xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9591 CHECK_ARITY(1);
9592 CAST_TO_NUMBER;
9593 CHECK_TYPE(XPATH_NUMBER);
9595 ctxt->value->floatval = floor(ctxt->value->floatval);
9599 * xmlXPathCeilingFunction:
9600 * @ctxt: the XPath Parser context
9601 * @nargs: the number of arguments
9603 * Implement the ceiling() XPath function
9604 * number ceiling(number)
9605 * The ceiling function returns the smallest (closest to negative infinity)
9606 * number that is not less than the argument and that is an integer.
9608 void
9609 xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9610 CHECK_ARITY(1);
9611 CAST_TO_NUMBER;
9612 CHECK_TYPE(XPATH_NUMBER);
9614 #ifdef _AIX
9615 /* Work around buggy ceil() function on AIX */
9616 ctxt->value->floatval = copysign(ceil(ctxt->value->floatval), ctxt->value->floatval);
9617 #else
9618 ctxt->value->floatval = ceil(ctxt->value->floatval);
9619 #endif
9623 * xmlXPathRoundFunction:
9624 * @ctxt: the XPath Parser context
9625 * @nargs: the number of arguments
9627 * Implement the round() XPath function
9628 * number round(number)
9629 * The round function returns the number that is closest to the
9630 * argument and that is an integer. If there are two such numbers,
9631 * then the one that is closest to positive infinity is returned.
9633 void
9634 xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9635 double f;
9637 CHECK_ARITY(1);
9638 CAST_TO_NUMBER;
9639 CHECK_TYPE(XPATH_NUMBER);
9641 f = ctxt->value->floatval;
9643 if ((f >= -0.5) && (f < 0.5)) {
9644 /* Handles negative zero. */
9645 ctxt->value->floatval *= 0.0;
9647 else {
9648 double rounded = floor(f);
9649 if (f - rounded >= 0.5)
9650 rounded += 1.0;
9651 ctxt->value->floatval = rounded;
9655 /************************************************************************
9657 * The Parser *
9659 ************************************************************************/
9662 * a few forward declarations since we use a recursive call based
9663 * implementation.
9665 static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
9666 static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
9667 static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
9668 static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
9669 static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
9670 int qualified);
9673 * xmlXPathCurrentChar:
9674 * @ctxt: the XPath parser context
9675 * @cur: pointer to the beginning of the char
9676 * @len: pointer to the length of the char read
9678 * The current char value, if using UTF-8 this may actually span multiple
9679 * bytes in the input buffer.
9681 * Returns the current char value and its length
9684 static int
9685 xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
9686 unsigned char c;
9687 unsigned int val;
9688 const xmlChar *cur;
9690 if (ctxt == NULL)
9691 return(0);
9692 cur = ctxt->cur;
9695 * We are supposed to handle UTF8, check it's valid
9696 * From rfc2044: encoding of the Unicode values on UTF-8:
9698 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
9699 * 0000 0000-0000 007F 0xxxxxxx
9700 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
9701 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
9703 * Check for the 0x110000 limit too
9705 c = *cur;
9706 if (c & 0x80) {
9707 if ((cur[1] & 0xc0) != 0x80)
9708 goto encoding_error;
9709 if ((c & 0xe0) == 0xe0) {
9711 if ((cur[2] & 0xc0) != 0x80)
9712 goto encoding_error;
9713 if ((c & 0xf0) == 0xf0) {
9714 if (((c & 0xf8) != 0xf0) ||
9715 ((cur[3] & 0xc0) != 0x80))
9716 goto encoding_error;
9717 /* 4-byte code */
9718 *len = 4;
9719 val = (cur[0] & 0x7) << 18;
9720 val |= (cur[1] & 0x3f) << 12;
9721 val |= (cur[2] & 0x3f) << 6;
9722 val |= cur[3] & 0x3f;
9723 } else {
9724 /* 3-byte code */
9725 *len = 3;
9726 val = (cur[0] & 0xf) << 12;
9727 val |= (cur[1] & 0x3f) << 6;
9728 val |= cur[2] & 0x3f;
9730 } else {
9731 /* 2-byte code */
9732 *len = 2;
9733 val = (cur[0] & 0x1f) << 6;
9734 val |= cur[1] & 0x3f;
9736 if (!IS_CHAR(val)) {
9737 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
9739 return(val);
9740 } else {
9741 /* 1-byte code */
9742 *len = 1;
9743 return((int) *cur);
9745 encoding_error:
9747 * If we detect an UTF8 error that probably means that the
9748 * input encoding didn't get properly advertised in the
9749 * declaration header. Report the error and switch the encoding
9750 * to ISO-Latin-1 (if you don't like this policy, just declare the
9751 * encoding !)
9753 *len = 0;
9754 XP_ERROR0(XPATH_ENCODING_ERROR);
9758 * xmlXPathParseNCName:
9759 * @ctxt: the XPath Parser context
9761 * parse an XML namespace non qualified name.
9763 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
9765 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
9766 * CombiningChar | Extender
9768 * Returns the namespace name or NULL
9771 xmlChar *
9772 xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
9773 const xmlChar *in;
9774 xmlChar *ret;
9775 int count = 0;
9777 if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9779 * Accelerator for simple ASCII names
9781 in = ctxt->cur;
9782 if (((*in >= 0x61) && (*in <= 0x7A)) ||
9783 ((*in >= 0x41) && (*in <= 0x5A)) ||
9784 (*in == '_')) {
9785 in++;
9786 while (((*in >= 0x61) && (*in <= 0x7A)) ||
9787 ((*in >= 0x41) && (*in <= 0x5A)) ||
9788 ((*in >= 0x30) && (*in <= 0x39)) ||
9789 (*in == '_') || (*in == '.') ||
9790 (*in == '-'))
9791 in++;
9792 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
9793 (*in == '[') || (*in == ']') || (*in == ':') ||
9794 (*in == '@') || (*in == '*')) {
9795 count = in - ctxt->cur;
9796 if (count == 0)
9797 return(NULL);
9798 ret = xmlStrndup(ctxt->cur, count);
9799 ctxt->cur = in;
9800 return(ret);
9803 return(xmlXPathParseNameComplex(ctxt, 0));
9808 * xmlXPathParseQName:
9809 * @ctxt: the XPath Parser context
9810 * @prefix: a xmlChar **
9812 * parse an XML qualified name
9814 * [NS 5] QName ::= (Prefix ':')? LocalPart
9816 * [NS 6] Prefix ::= NCName
9818 * [NS 7] LocalPart ::= NCName
9820 * Returns the function returns the local part, and prefix is updated
9821 * to get the Prefix if any.
9824 static xmlChar *
9825 xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
9826 xmlChar *ret = NULL;
9828 *prefix = NULL;
9829 ret = xmlXPathParseNCName(ctxt);
9830 if (ret && CUR == ':') {
9831 *prefix = ret;
9832 NEXT;
9833 ret = xmlXPathParseNCName(ctxt);
9835 return(ret);
9839 * xmlXPathParseName:
9840 * @ctxt: the XPath Parser context
9842 * parse an XML name
9844 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
9845 * CombiningChar | Extender
9847 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
9849 * Returns the namespace name or NULL
9852 xmlChar *
9853 xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
9854 const xmlChar *in;
9855 xmlChar *ret;
9856 size_t count = 0;
9858 if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9860 * Accelerator for simple ASCII names
9862 in = ctxt->cur;
9863 if (((*in >= 0x61) && (*in <= 0x7A)) ||
9864 ((*in >= 0x41) && (*in <= 0x5A)) ||
9865 (*in == '_') || (*in == ':')) {
9866 in++;
9867 while (((*in >= 0x61) && (*in <= 0x7A)) ||
9868 ((*in >= 0x41) && (*in <= 0x5A)) ||
9869 ((*in >= 0x30) && (*in <= 0x39)) ||
9870 (*in == '_') || (*in == '-') ||
9871 (*in == ':') || (*in == '.'))
9872 in++;
9873 if ((*in > 0) && (*in < 0x80)) {
9874 count = in - ctxt->cur;
9875 if (count > XML_MAX_NAME_LENGTH) {
9876 ctxt->cur = in;
9877 XP_ERRORNULL(XPATH_EXPR_ERROR);
9879 ret = xmlStrndup(ctxt->cur, count);
9880 ctxt->cur = in;
9881 return(ret);
9884 return(xmlXPathParseNameComplex(ctxt, 1));
9887 static xmlChar *
9888 xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
9889 xmlChar buf[XML_MAX_NAMELEN + 5];
9890 int len = 0, l;
9891 int c;
9894 * Handler for more complex cases
9896 c = CUR_CHAR(l);
9897 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
9898 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
9899 (c == '*') || /* accelerators */
9900 (!IS_LETTER(c) && (c != '_') &&
9901 ((!qualified) || (c != ':')))) {
9902 return(NULL);
9905 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
9906 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
9907 (c == '.') || (c == '-') ||
9908 (c == '_') || ((qualified) && (c == ':')) ||
9909 (IS_COMBINING(c)) ||
9910 (IS_EXTENDER(c)))) {
9911 COPY_BUF(l,buf,len,c);
9912 NEXTL(l);
9913 c = CUR_CHAR(l);
9914 if (len >= XML_MAX_NAMELEN) {
9916 * Okay someone managed to make a huge name, so he's ready to pay
9917 * for the processing speed.
9919 xmlChar *buffer;
9920 int max = len * 2;
9922 if (len > XML_MAX_NAME_LENGTH) {
9923 XP_ERRORNULL(XPATH_EXPR_ERROR);
9925 buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar));
9926 if (buffer == NULL) {
9927 XP_ERRORNULL(XPATH_MEMORY_ERROR);
9929 memcpy(buffer, buf, len);
9930 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
9931 (c == '.') || (c == '-') ||
9932 (c == '_') || ((qualified) && (c == ':')) ||
9933 (IS_COMBINING(c)) ||
9934 (IS_EXTENDER(c))) {
9935 if (len + 10 > max) {
9936 xmlChar *tmp;
9937 if (max > XML_MAX_NAME_LENGTH) {
9938 xmlFree(buffer);
9939 XP_ERRORNULL(XPATH_EXPR_ERROR);
9941 max *= 2;
9942 tmp = (xmlChar *) xmlRealloc(buffer,
9943 max * sizeof(xmlChar));
9944 if (tmp == NULL) {
9945 xmlFree(buffer);
9946 XP_ERRORNULL(XPATH_MEMORY_ERROR);
9948 buffer = tmp;
9950 COPY_BUF(l,buffer,len,c);
9951 NEXTL(l);
9952 c = CUR_CHAR(l);
9954 buffer[len] = 0;
9955 return(buffer);
9958 if (len == 0)
9959 return(NULL);
9960 return(xmlStrndup(buf, len));
9963 #define MAX_FRAC 20
9966 * xmlXPathStringEvalNumber:
9967 * @str: A string to scan
9969 * [30a] Float ::= Number ('e' Digits?)?
9971 * [30] Number ::= Digits ('.' Digits?)?
9972 * | '.' Digits
9973 * [31] Digits ::= [0-9]+
9975 * Compile a Number in the string
9976 * In complement of the Number expression, this function also handles
9977 * negative values : '-' Number.
9979 * Returns the double value.
9981 double
9982 xmlXPathStringEvalNumber(const xmlChar *str) {
9983 const xmlChar *cur = str;
9984 double ret;
9985 int ok = 0;
9986 int isneg = 0;
9987 int exponent = 0;
9988 int is_exponent_negative = 0;
9989 #ifdef __GNUC__
9990 unsigned long tmp = 0;
9991 double temp;
9992 #endif
9993 if (cur == NULL) return(0);
9994 while (IS_BLANK_CH(*cur)) cur++;
9995 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
9996 return(xmlXPathNAN);
9998 if (*cur == '-') {
9999 isneg = 1;
10000 cur++;
10003 #ifdef __GNUC__
10005 * tmp/temp is a workaround against a gcc compiler bug
10006 * http://veillard.com/gcc.bug
10008 ret = 0;
10009 while ((*cur >= '0') && (*cur <= '9')) {
10010 ret = ret * 10;
10011 tmp = (*cur - '0');
10012 ok = 1;
10013 cur++;
10014 temp = (double) tmp;
10015 ret = ret + temp;
10017 #else
10018 ret = 0;
10019 while ((*cur >= '0') && (*cur <= '9')) {
10020 ret = ret * 10 + (*cur - '0');
10021 ok = 1;
10022 cur++;
10024 #endif
10026 if (*cur == '.') {
10027 int v, frac = 0, max;
10028 double fraction = 0;
10030 cur++;
10031 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
10032 return(xmlXPathNAN);
10034 while (*cur == '0') {
10035 frac = frac + 1;
10036 cur++;
10038 max = frac + MAX_FRAC;
10039 while (((*cur >= '0') && (*cur <= '9')) && (frac < max)) {
10040 v = (*cur - '0');
10041 fraction = fraction * 10 + v;
10042 frac = frac + 1;
10043 cur++;
10045 fraction /= pow(10.0, frac);
10046 ret = ret + fraction;
10047 while ((*cur >= '0') && (*cur <= '9'))
10048 cur++;
10050 if ((*cur == 'e') || (*cur == 'E')) {
10051 cur++;
10052 if (*cur == '-') {
10053 is_exponent_negative = 1;
10054 cur++;
10055 } else if (*cur == '+') {
10056 cur++;
10058 while ((*cur >= '0') && (*cur <= '9')) {
10059 if (exponent < 1000000)
10060 exponent = exponent * 10 + (*cur - '0');
10061 cur++;
10064 while (IS_BLANK_CH(*cur)) cur++;
10065 if (*cur != 0) return(xmlXPathNAN);
10066 if (isneg) ret = -ret;
10067 if (is_exponent_negative) exponent = -exponent;
10068 ret *= pow(10.0, (double)exponent);
10069 return(ret);
10073 * xmlXPathCompNumber:
10074 * @ctxt: the XPath Parser context
10076 * [30] Number ::= Digits ('.' Digits?)?
10077 * | '.' Digits
10078 * [31] Digits ::= [0-9]+
10080 * Compile a Number, then push it on the stack
10083 static void
10084 xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
10086 double ret = 0.0;
10087 int ok = 0;
10088 int exponent = 0;
10089 int is_exponent_negative = 0;
10090 xmlXPathObjectPtr num;
10091 #ifdef __GNUC__
10092 unsigned long tmp = 0;
10093 double temp;
10094 #endif
10096 CHECK_ERROR;
10097 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
10098 XP_ERROR(XPATH_NUMBER_ERROR);
10100 #ifdef __GNUC__
10102 * tmp/temp is a workaround against a gcc compiler bug
10103 * http://veillard.com/gcc.bug
10105 ret = 0;
10106 while ((CUR >= '0') && (CUR <= '9')) {
10107 ret = ret * 10;
10108 tmp = (CUR - '0');
10109 ok = 1;
10110 NEXT;
10111 temp = (double) tmp;
10112 ret = ret + temp;
10114 #else
10115 ret = 0;
10116 while ((CUR >= '0') && (CUR <= '9')) {
10117 ret = ret * 10 + (CUR - '0');
10118 ok = 1;
10119 NEXT;
10121 #endif
10122 if (CUR == '.') {
10123 int v, frac = 0, max;
10124 double fraction = 0;
10126 NEXT;
10127 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
10128 XP_ERROR(XPATH_NUMBER_ERROR);
10130 while (CUR == '0') {
10131 frac = frac + 1;
10132 NEXT;
10134 max = frac + MAX_FRAC;
10135 while ((CUR >= '0') && (CUR <= '9') && (frac < max)) {
10136 v = (CUR - '0');
10137 fraction = fraction * 10 + v;
10138 frac = frac + 1;
10139 NEXT;
10141 fraction /= pow(10.0, frac);
10142 ret = ret + fraction;
10143 while ((CUR >= '0') && (CUR <= '9'))
10144 NEXT;
10146 if ((CUR == 'e') || (CUR == 'E')) {
10147 NEXT;
10148 if (CUR == '-') {
10149 is_exponent_negative = 1;
10150 NEXT;
10151 } else if (CUR == '+') {
10152 NEXT;
10154 while ((CUR >= '0') && (CUR <= '9')) {
10155 if (exponent < 1000000)
10156 exponent = exponent * 10 + (CUR - '0');
10157 NEXT;
10159 if (is_exponent_negative)
10160 exponent = -exponent;
10161 ret *= pow(10.0, (double) exponent);
10163 num = xmlXPathCacheNewFloat(ctxt->context, ret);
10164 if (num == NULL) {
10165 ctxt->error = XPATH_MEMORY_ERROR;
10166 } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, num,
10167 NULL) == -1) {
10168 xmlXPathReleaseObject(ctxt->context, num);
10173 * xmlXPathParseLiteral:
10174 * @ctxt: the XPath Parser context
10176 * Parse a Literal
10178 * [29] Literal ::= '"' [^"]* '"'
10179 * | "'" [^']* "'"
10181 * Returns the value found or NULL in case of error
10183 static xmlChar *
10184 xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
10185 const xmlChar *q;
10186 xmlChar *ret = NULL;
10188 if (CUR == '"') {
10189 NEXT;
10190 q = CUR_PTR;
10191 while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10192 NEXT;
10193 if (!IS_CHAR_CH(CUR)) {
10194 XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10195 } else {
10196 ret = xmlStrndup(q, CUR_PTR - q);
10197 NEXT;
10199 } else if (CUR == '\'') {
10200 NEXT;
10201 q = CUR_PTR;
10202 while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10203 NEXT;
10204 if (!IS_CHAR_CH(CUR)) {
10205 XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10206 } else {
10207 ret = xmlStrndup(q, CUR_PTR - q);
10208 NEXT;
10210 } else {
10211 XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
10213 return(ret);
10217 * xmlXPathCompLiteral:
10218 * @ctxt: the XPath Parser context
10220 * Parse a Literal and push it on the stack.
10222 * [29] Literal ::= '"' [^"]* '"'
10223 * | "'" [^']* "'"
10225 * TODO: xmlXPathCompLiteral memory allocation could be improved.
10227 static void
10228 xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
10229 const xmlChar *q;
10230 xmlChar *ret = NULL;
10231 xmlXPathObjectPtr lit;
10233 if (CUR == '"') {
10234 NEXT;
10235 q = CUR_PTR;
10236 while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10237 NEXT;
10238 if (!IS_CHAR_CH(CUR)) {
10239 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10240 } else {
10241 ret = xmlStrndup(q, CUR_PTR - q);
10242 NEXT;
10244 } else if (CUR == '\'') {
10245 NEXT;
10246 q = CUR_PTR;
10247 while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10248 NEXT;
10249 if (!IS_CHAR_CH(CUR)) {
10250 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10251 } else {
10252 ret = xmlStrndup(q, CUR_PTR - q);
10253 NEXT;
10255 } else {
10256 XP_ERROR(XPATH_START_LITERAL_ERROR);
10258 if (ret == NULL) return;
10259 lit = xmlXPathCacheNewString(ctxt->context, ret);
10260 if (lit == NULL) {
10261 ctxt->error = XPATH_MEMORY_ERROR;
10262 } else if (PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, lit,
10263 NULL) == -1) {
10264 xmlXPathReleaseObject(ctxt->context, lit);
10266 xmlFree(ret);
10270 * xmlXPathCompVariableReference:
10271 * @ctxt: the XPath Parser context
10273 * Parse a VariableReference, evaluate it and push it on the stack.
10275 * The variable bindings consist of a mapping from variable names
10276 * to variable values. The value of a variable is an object, which can be
10277 * of any of the types that are possible for the value of an expression,
10278 * and may also be of additional types not specified here.
10280 * Early evaluation is possible since:
10281 * The variable bindings [...] used to evaluate a subexpression are
10282 * always the same as those used to evaluate the containing expression.
10284 * [36] VariableReference ::= '$' QName
10286 static void
10287 xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
10288 xmlChar *name;
10289 xmlChar *prefix;
10291 SKIP_BLANKS;
10292 if (CUR != '$') {
10293 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10295 NEXT;
10296 name = xmlXPathParseQName(ctxt, &prefix);
10297 if (name == NULL) {
10298 xmlFree(prefix);
10299 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10301 ctxt->comp->last = -1;
10302 if (PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, name, prefix) == -1) {
10303 xmlFree(prefix);
10304 xmlFree(name);
10306 SKIP_BLANKS;
10307 if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
10308 XP_ERROR(XPATH_FORBID_VARIABLE_ERROR);
10313 * xmlXPathIsNodeType:
10314 * @name: a name string
10316 * Is the name given a NodeType one.
10318 * [38] NodeType ::= 'comment'
10319 * | 'text'
10320 * | 'processing-instruction'
10321 * | 'node'
10323 * Returns 1 if true 0 otherwise
10326 xmlXPathIsNodeType(const xmlChar *name) {
10327 if (name == NULL)
10328 return(0);
10330 if (xmlStrEqual(name, BAD_CAST "node"))
10331 return(1);
10332 if (xmlStrEqual(name, BAD_CAST "text"))
10333 return(1);
10334 if (xmlStrEqual(name, BAD_CAST "comment"))
10335 return(1);
10336 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
10337 return(1);
10338 return(0);
10342 * xmlXPathCompFunctionCall:
10343 * @ctxt: the XPath Parser context
10345 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
10346 * [17] Argument ::= Expr
10348 * Compile a function call, the evaluation of all arguments are
10349 * pushed on the stack
10351 static void
10352 xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
10353 xmlChar *name;
10354 xmlChar *prefix;
10355 int nbargs = 0;
10356 int sort = 1;
10358 name = xmlXPathParseQName(ctxt, &prefix);
10359 if (name == NULL) {
10360 xmlFree(prefix);
10361 XP_ERROR(XPATH_EXPR_ERROR);
10363 SKIP_BLANKS;
10364 #ifdef DEBUG_EXPR
10365 if (prefix == NULL)
10366 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
10367 name);
10368 else
10369 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
10370 prefix, name);
10371 #endif
10373 if (CUR != '(') {
10374 xmlFree(name);
10375 xmlFree(prefix);
10376 XP_ERROR(XPATH_EXPR_ERROR);
10378 NEXT;
10379 SKIP_BLANKS;
10382 * Optimization for count(): we don't need the node-set to be sorted.
10384 if ((prefix == NULL) && (name[0] == 'c') &&
10385 xmlStrEqual(name, BAD_CAST "count"))
10387 sort = 0;
10389 ctxt->comp->last = -1;
10390 if (CUR != ')') {
10391 while (CUR != 0) {
10392 int op1 = ctxt->comp->last;
10393 ctxt->comp->last = -1;
10394 xmlXPathCompileExpr(ctxt, sort);
10395 if (ctxt->error != XPATH_EXPRESSION_OK) {
10396 xmlFree(name);
10397 xmlFree(prefix);
10398 return;
10400 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
10401 nbargs++;
10402 if (CUR == ')') break;
10403 if (CUR != ',') {
10404 xmlFree(name);
10405 xmlFree(prefix);
10406 XP_ERROR(XPATH_EXPR_ERROR);
10408 NEXT;
10409 SKIP_BLANKS;
10412 if (PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1) {
10413 xmlFree(prefix);
10414 xmlFree(name);
10416 NEXT;
10417 SKIP_BLANKS;
10421 * xmlXPathCompPrimaryExpr:
10422 * @ctxt: the XPath Parser context
10424 * [15] PrimaryExpr ::= VariableReference
10425 * | '(' Expr ')'
10426 * | Literal
10427 * | Number
10428 * | FunctionCall
10430 * Compile a primary expression.
10432 static void
10433 xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
10434 SKIP_BLANKS;
10435 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
10436 else if (CUR == '(') {
10437 NEXT;
10438 SKIP_BLANKS;
10439 xmlXPathCompileExpr(ctxt, 1);
10440 CHECK_ERROR;
10441 if (CUR != ')') {
10442 XP_ERROR(XPATH_EXPR_ERROR);
10444 NEXT;
10445 SKIP_BLANKS;
10446 } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10447 xmlXPathCompNumber(ctxt);
10448 } else if ((CUR == '\'') || (CUR == '"')) {
10449 xmlXPathCompLiteral(ctxt);
10450 } else {
10451 xmlXPathCompFunctionCall(ctxt);
10453 SKIP_BLANKS;
10457 * xmlXPathCompFilterExpr:
10458 * @ctxt: the XPath Parser context
10460 * [20] FilterExpr ::= PrimaryExpr
10461 * | FilterExpr Predicate
10463 * Compile a filter expression.
10464 * Square brackets are used to filter expressions in the same way that
10465 * they are used in location paths. It is an error if the expression to
10466 * be filtered does not evaluate to a node-set. The context node list
10467 * used for evaluating the expression in square brackets is the node-set
10468 * to be filtered listed in document order.
10471 static void
10472 xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
10473 xmlXPathCompPrimaryExpr(ctxt);
10474 CHECK_ERROR;
10475 SKIP_BLANKS;
10477 while (CUR == '[') {
10478 xmlXPathCompPredicate(ctxt, 1);
10479 SKIP_BLANKS;
10486 * xmlXPathScanName:
10487 * @ctxt: the XPath Parser context
10489 * Trickery: parse an XML name but without consuming the input flow
10490 * Needed to avoid insanity in the parser state.
10492 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
10493 * CombiningChar | Extender
10495 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
10497 * [6] Names ::= Name (S Name)*
10499 * Returns the Name parsed or NULL
10502 static xmlChar *
10503 xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
10504 int len = 0, l;
10505 int c;
10506 const xmlChar *cur;
10507 xmlChar *ret;
10509 cur = ctxt->cur;
10511 c = CUR_CHAR(l);
10512 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
10513 (!IS_LETTER(c) && (c != '_') &&
10514 (c != ':'))) {
10515 return(NULL);
10518 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
10519 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
10520 (c == '.') || (c == '-') ||
10521 (c == '_') || (c == ':') ||
10522 (IS_COMBINING(c)) ||
10523 (IS_EXTENDER(c)))) {
10524 len += l;
10525 NEXTL(l);
10526 c = CUR_CHAR(l);
10528 ret = xmlStrndup(cur, ctxt->cur - cur);
10529 ctxt->cur = cur;
10530 return(ret);
10534 * xmlXPathCompPathExpr:
10535 * @ctxt: the XPath Parser context
10537 * [19] PathExpr ::= LocationPath
10538 * | FilterExpr
10539 * | FilterExpr '/' RelativeLocationPath
10540 * | FilterExpr '//' RelativeLocationPath
10542 * Compile a path expression.
10543 * The / operator and // operators combine an arbitrary expression
10544 * and a relative location path. It is an error if the expression
10545 * does not evaluate to a node-set.
10546 * The / operator does composition in the same way as when / is
10547 * used in a location path. As in location paths, // is short for
10548 * /descendant-or-self::node()/.
10551 static void
10552 xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
10553 int lc = 1; /* Should we branch to LocationPath ? */
10554 xmlChar *name = NULL; /* we may have to preparse a name to find out */
10556 SKIP_BLANKS;
10557 if ((CUR == '$') || (CUR == '(') ||
10558 (IS_ASCII_DIGIT(CUR)) ||
10559 (CUR == '\'') || (CUR == '"') ||
10560 (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10561 lc = 0;
10562 } else if (CUR == '*') {
10563 /* relative or absolute location path */
10564 lc = 1;
10565 } else if (CUR == '/') {
10566 /* relative or absolute location path */
10567 lc = 1;
10568 } else if (CUR == '@') {
10569 /* relative abbreviated attribute location path */
10570 lc = 1;
10571 } else if (CUR == '.') {
10572 /* relative abbreviated attribute location path */
10573 lc = 1;
10574 } else {
10576 * Problem is finding if we have a name here whether it's:
10577 * - a nodetype
10578 * - a function call in which case it's followed by '('
10579 * - an axis in which case it's followed by ':'
10580 * - a element name
10581 * We do an a priori analysis here rather than having to
10582 * maintain parsed token content through the recursive function
10583 * calls. This looks uglier but makes the code easier to
10584 * read/write/debug.
10586 SKIP_BLANKS;
10587 name = xmlXPathScanName(ctxt);
10588 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
10589 #ifdef DEBUG_STEP
10590 xmlGenericError(xmlGenericErrorContext,
10591 "PathExpr: Axis\n");
10592 #endif
10593 lc = 1;
10594 xmlFree(name);
10595 } else if (name != NULL) {
10596 int len =xmlStrlen(name);
10599 while (NXT(len) != 0) {
10600 if (NXT(len) == '/') {
10601 /* element name */
10602 #ifdef DEBUG_STEP
10603 xmlGenericError(xmlGenericErrorContext,
10604 "PathExpr: AbbrRelLocation\n");
10605 #endif
10606 lc = 1;
10607 break;
10608 } else if (IS_BLANK_CH(NXT(len))) {
10609 /* ignore blanks */
10611 } else if (NXT(len) == ':') {
10612 #ifdef DEBUG_STEP
10613 xmlGenericError(xmlGenericErrorContext,
10614 "PathExpr: AbbrRelLocation\n");
10615 #endif
10616 lc = 1;
10617 break;
10618 } else if ((NXT(len) == '(')) {
10619 /* Node Type or Function */
10620 if (xmlXPathIsNodeType(name)) {
10621 #ifdef DEBUG_STEP
10622 xmlGenericError(xmlGenericErrorContext,
10623 "PathExpr: Type search\n");
10624 #endif
10625 lc = 1;
10626 #ifdef LIBXML_XPTR_ENABLED
10627 } else if (ctxt->xptr &&
10628 xmlStrEqual(name, BAD_CAST "range-to")) {
10629 lc = 1;
10630 #endif
10631 } else {
10632 #ifdef DEBUG_STEP
10633 xmlGenericError(xmlGenericErrorContext,
10634 "PathExpr: function call\n");
10635 #endif
10636 lc = 0;
10638 break;
10639 } else if ((NXT(len) == '[')) {
10640 /* element name */
10641 #ifdef DEBUG_STEP
10642 xmlGenericError(xmlGenericErrorContext,
10643 "PathExpr: AbbrRelLocation\n");
10644 #endif
10645 lc = 1;
10646 break;
10647 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
10648 (NXT(len) == '=')) {
10649 lc = 1;
10650 break;
10651 } else {
10652 lc = 1;
10653 break;
10655 len++;
10657 if (NXT(len) == 0) {
10658 #ifdef DEBUG_STEP
10659 xmlGenericError(xmlGenericErrorContext,
10660 "PathExpr: AbbrRelLocation\n");
10661 #endif
10662 /* element name */
10663 lc = 1;
10665 xmlFree(name);
10666 } else {
10667 /* make sure all cases are covered explicitly */
10668 XP_ERROR(XPATH_EXPR_ERROR);
10672 if (lc) {
10673 if (CUR == '/') {
10674 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
10675 } else {
10676 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10678 xmlXPathCompLocationPath(ctxt);
10679 } else {
10680 xmlXPathCompFilterExpr(ctxt);
10681 CHECK_ERROR;
10682 if ((CUR == '/') && (NXT(1) == '/')) {
10683 SKIP(2);
10684 SKIP_BLANKS;
10686 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
10687 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
10689 xmlXPathCompRelativeLocationPath(ctxt);
10690 } else if (CUR == '/') {
10691 xmlXPathCompRelativeLocationPath(ctxt);
10694 SKIP_BLANKS;
10698 * xmlXPathCompUnionExpr:
10699 * @ctxt: the XPath Parser context
10701 * [18] UnionExpr ::= PathExpr
10702 * | UnionExpr '|' PathExpr
10704 * Compile an union expression.
10707 static void
10708 xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
10709 xmlXPathCompPathExpr(ctxt);
10710 CHECK_ERROR;
10711 SKIP_BLANKS;
10712 while (CUR == '|') {
10713 int op1 = ctxt->comp->last;
10714 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10716 NEXT;
10717 SKIP_BLANKS;
10718 xmlXPathCompPathExpr(ctxt);
10720 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
10722 SKIP_BLANKS;
10727 * xmlXPathCompUnaryExpr:
10728 * @ctxt: the XPath Parser context
10730 * [27] UnaryExpr ::= UnionExpr
10731 * | '-' UnaryExpr
10733 * Compile an unary expression.
10736 static void
10737 xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
10738 int minus = 0;
10739 int found = 0;
10741 SKIP_BLANKS;
10742 while (CUR == '-') {
10743 minus = 1 - minus;
10744 found = 1;
10745 NEXT;
10746 SKIP_BLANKS;
10749 xmlXPathCompUnionExpr(ctxt);
10750 CHECK_ERROR;
10751 if (found) {
10752 if (minus)
10753 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
10754 else
10755 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
10760 * xmlXPathCompMultiplicativeExpr:
10761 * @ctxt: the XPath Parser context
10763 * [26] MultiplicativeExpr ::= UnaryExpr
10764 * | MultiplicativeExpr MultiplyOperator UnaryExpr
10765 * | MultiplicativeExpr 'div' UnaryExpr
10766 * | MultiplicativeExpr 'mod' UnaryExpr
10767 * [34] MultiplyOperator ::= '*'
10769 * Compile an Additive expression.
10772 static void
10773 xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
10774 xmlXPathCompUnaryExpr(ctxt);
10775 CHECK_ERROR;
10776 SKIP_BLANKS;
10777 while ((CUR == '*') ||
10778 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
10779 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
10780 int op = -1;
10781 int op1 = ctxt->comp->last;
10783 if (CUR == '*') {
10784 op = 0;
10785 NEXT;
10786 } else if (CUR == 'd') {
10787 op = 1;
10788 SKIP(3);
10789 } else if (CUR == 'm') {
10790 op = 2;
10791 SKIP(3);
10793 SKIP_BLANKS;
10794 xmlXPathCompUnaryExpr(ctxt);
10795 CHECK_ERROR;
10796 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
10797 SKIP_BLANKS;
10802 * xmlXPathCompAdditiveExpr:
10803 * @ctxt: the XPath Parser context
10805 * [25] AdditiveExpr ::= MultiplicativeExpr
10806 * | AdditiveExpr '+' MultiplicativeExpr
10807 * | AdditiveExpr '-' MultiplicativeExpr
10809 * Compile an Additive expression.
10812 static void
10813 xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
10815 xmlXPathCompMultiplicativeExpr(ctxt);
10816 CHECK_ERROR;
10817 SKIP_BLANKS;
10818 while ((CUR == '+') || (CUR == '-')) {
10819 int plus;
10820 int op1 = ctxt->comp->last;
10822 if (CUR == '+') plus = 1;
10823 else plus = 0;
10824 NEXT;
10825 SKIP_BLANKS;
10826 xmlXPathCompMultiplicativeExpr(ctxt);
10827 CHECK_ERROR;
10828 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
10829 SKIP_BLANKS;
10834 * xmlXPathCompRelationalExpr:
10835 * @ctxt: the XPath Parser context
10837 * [24] RelationalExpr ::= AdditiveExpr
10838 * | RelationalExpr '<' AdditiveExpr
10839 * | RelationalExpr '>' AdditiveExpr
10840 * | RelationalExpr '<=' AdditiveExpr
10841 * | RelationalExpr '>=' AdditiveExpr
10843 * A <= B > C is allowed ? Answer from James, yes with
10844 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
10845 * which is basically what got implemented.
10847 * Compile a Relational expression, then push the result
10848 * on the stack
10851 static void
10852 xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
10853 xmlXPathCompAdditiveExpr(ctxt);
10854 CHECK_ERROR;
10855 SKIP_BLANKS;
10856 while ((CUR == '<') || (CUR == '>')) {
10857 int inf, strict;
10858 int op1 = ctxt->comp->last;
10860 if (CUR == '<') inf = 1;
10861 else inf = 0;
10862 if (NXT(1) == '=') strict = 0;
10863 else strict = 1;
10864 NEXT;
10865 if (!strict) NEXT;
10866 SKIP_BLANKS;
10867 xmlXPathCompAdditiveExpr(ctxt);
10868 CHECK_ERROR;
10869 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
10870 SKIP_BLANKS;
10875 * xmlXPathCompEqualityExpr:
10876 * @ctxt: the XPath Parser context
10878 * [23] EqualityExpr ::= RelationalExpr
10879 * | EqualityExpr '=' RelationalExpr
10880 * | EqualityExpr '!=' RelationalExpr
10882 * A != B != C is allowed ? Answer from James, yes with
10883 * (RelationalExpr = RelationalExpr) = RelationalExpr
10884 * (RelationalExpr != RelationalExpr) != RelationalExpr
10885 * which is basically what got implemented.
10887 * Compile an Equality expression.
10890 static void
10891 xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
10892 xmlXPathCompRelationalExpr(ctxt);
10893 CHECK_ERROR;
10894 SKIP_BLANKS;
10895 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
10896 int eq;
10897 int op1 = ctxt->comp->last;
10899 if (CUR == '=') eq = 1;
10900 else eq = 0;
10901 NEXT;
10902 if (!eq) NEXT;
10903 SKIP_BLANKS;
10904 xmlXPathCompRelationalExpr(ctxt);
10905 CHECK_ERROR;
10906 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
10907 SKIP_BLANKS;
10912 * xmlXPathCompAndExpr:
10913 * @ctxt: the XPath Parser context
10915 * [22] AndExpr ::= EqualityExpr
10916 * | AndExpr 'and' EqualityExpr
10918 * Compile an AND expression.
10921 static void
10922 xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
10923 xmlXPathCompEqualityExpr(ctxt);
10924 CHECK_ERROR;
10925 SKIP_BLANKS;
10926 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
10927 int op1 = ctxt->comp->last;
10928 SKIP(3);
10929 SKIP_BLANKS;
10930 xmlXPathCompEqualityExpr(ctxt);
10931 CHECK_ERROR;
10932 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
10933 SKIP_BLANKS;
10938 * xmlXPathCompileExpr:
10939 * @ctxt: the XPath Parser context
10941 * [14] Expr ::= OrExpr
10942 * [21] OrExpr ::= AndExpr
10943 * | OrExpr 'or' AndExpr
10945 * Parse and compile an expression
10947 static void
10948 xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
10949 xmlXPathContextPtr xpctxt = ctxt->context;
10951 if (xpctxt != NULL) {
10952 if (xpctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
10953 XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
10955 * Parsing a single '(' pushes about 10 functions on the call stack
10956 * before recursing!
10958 xpctxt->depth += 10;
10961 xmlXPathCompAndExpr(ctxt);
10962 CHECK_ERROR;
10963 SKIP_BLANKS;
10964 while ((CUR == 'o') && (NXT(1) == 'r')) {
10965 int op1 = ctxt->comp->last;
10966 SKIP(2);
10967 SKIP_BLANKS;
10968 xmlXPathCompAndExpr(ctxt);
10969 CHECK_ERROR;
10970 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
10971 SKIP_BLANKS;
10973 if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
10974 /* more ops could be optimized too */
10976 * This is the main place to eliminate sorting for
10977 * operations which don't require a sorted node-set.
10978 * E.g. count().
10980 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
10983 if (xpctxt != NULL)
10984 xpctxt->depth -= 10;
10988 * xmlXPathCompPredicate:
10989 * @ctxt: the XPath Parser context
10990 * @filter: act as a filter
10992 * [8] Predicate ::= '[' PredicateExpr ']'
10993 * [9] PredicateExpr ::= Expr
10995 * Compile a predicate expression
10997 static void
10998 xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
10999 int op1 = ctxt->comp->last;
11001 SKIP_BLANKS;
11002 if (CUR != '[') {
11003 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
11005 NEXT;
11006 SKIP_BLANKS;
11008 ctxt->comp->last = -1;
11010 * This call to xmlXPathCompileExpr() will deactivate sorting
11011 * of the predicate result.
11012 * TODO: Sorting is still activated for filters, since I'm not
11013 * sure if needed. Normally sorting should not be needed, since
11014 * a filter can only diminish the number of items in a sequence,
11015 * but won't change its order; so if the initial sequence is sorted,
11016 * subsequent sorting is not needed.
11018 if (! filter)
11019 xmlXPathCompileExpr(ctxt, 0);
11020 else
11021 xmlXPathCompileExpr(ctxt, 1);
11022 CHECK_ERROR;
11024 if (CUR != ']') {
11025 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
11028 if (filter)
11029 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
11030 else
11031 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
11033 NEXT;
11034 SKIP_BLANKS;
11038 * xmlXPathCompNodeTest:
11039 * @ctxt: the XPath Parser context
11040 * @test: pointer to a xmlXPathTestVal
11041 * @type: pointer to a xmlXPathTypeVal
11042 * @prefix: placeholder for a possible name prefix
11044 * [7] NodeTest ::= NameTest
11045 * | NodeType '(' ')'
11046 * | 'processing-instruction' '(' Literal ')'
11048 * [37] NameTest ::= '*'
11049 * | NCName ':' '*'
11050 * | QName
11051 * [38] NodeType ::= 'comment'
11052 * | 'text'
11053 * | 'processing-instruction'
11054 * | 'node'
11056 * Returns the name found and updates @test, @type and @prefix appropriately
11058 static xmlChar *
11059 xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
11060 xmlXPathTypeVal *type, xmlChar **prefix,
11061 xmlChar *name) {
11062 int blanks;
11064 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
11065 STRANGE;
11066 return(NULL);
11068 *type = (xmlXPathTypeVal) 0;
11069 *test = (xmlXPathTestVal) 0;
11070 *prefix = NULL;
11071 SKIP_BLANKS;
11073 if ((name == NULL) && (CUR == '*')) {
11075 * All elements
11077 NEXT;
11078 *test = NODE_TEST_ALL;
11079 return(NULL);
11082 if (name == NULL)
11083 name = xmlXPathParseNCName(ctxt);
11084 if (name == NULL) {
11085 XP_ERRORNULL(XPATH_EXPR_ERROR);
11088 blanks = IS_BLANK_CH(CUR);
11089 SKIP_BLANKS;
11090 if (CUR == '(') {
11091 NEXT;
11093 * NodeType or PI search
11095 if (xmlStrEqual(name, BAD_CAST "comment"))
11096 *type = NODE_TYPE_COMMENT;
11097 else if (xmlStrEqual(name, BAD_CAST "node"))
11098 *type = NODE_TYPE_NODE;
11099 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
11100 *type = NODE_TYPE_PI;
11101 else if (xmlStrEqual(name, BAD_CAST "text"))
11102 *type = NODE_TYPE_TEXT;
11103 else {
11104 if (name != NULL)
11105 xmlFree(name);
11106 XP_ERRORNULL(XPATH_EXPR_ERROR);
11109 *test = NODE_TEST_TYPE;
11111 SKIP_BLANKS;
11112 if (*type == NODE_TYPE_PI) {
11114 * Specific case: search a PI by name.
11116 if (name != NULL)
11117 xmlFree(name);
11118 name = NULL;
11119 if (CUR != ')') {
11120 name = xmlXPathParseLiteral(ctxt);
11121 if (name == NULL) {
11122 XP_ERRORNULL(XPATH_EXPR_ERROR);
11124 *test = NODE_TEST_PI;
11125 SKIP_BLANKS;
11128 if (CUR != ')') {
11129 if (name != NULL)
11130 xmlFree(name);
11131 XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
11133 NEXT;
11134 return(name);
11136 *test = NODE_TEST_NAME;
11137 if ((!blanks) && (CUR == ':')) {
11138 NEXT;
11141 * Since currently the parser context don't have a
11142 * namespace list associated:
11143 * The namespace name for this prefix can be computed
11144 * only at evaluation time. The compilation is done
11145 * outside of any context.
11147 #if 0
11148 *prefix = xmlXPathNsLookup(ctxt->context, name);
11149 if (name != NULL)
11150 xmlFree(name);
11151 if (*prefix == NULL) {
11152 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
11154 #else
11155 *prefix = name;
11156 #endif
11158 if (CUR == '*') {
11160 * All elements
11162 NEXT;
11163 *test = NODE_TEST_ALL;
11164 return(NULL);
11167 name = xmlXPathParseNCName(ctxt);
11168 if (name == NULL) {
11169 XP_ERRORNULL(XPATH_EXPR_ERROR);
11172 return(name);
11176 * xmlXPathIsAxisName:
11177 * @name: a preparsed name token
11179 * [6] AxisName ::= 'ancestor'
11180 * | 'ancestor-or-self'
11181 * | 'attribute'
11182 * | 'child'
11183 * | 'descendant'
11184 * | 'descendant-or-self'
11185 * | 'following'
11186 * | 'following-sibling'
11187 * | 'namespace'
11188 * | 'parent'
11189 * | 'preceding'
11190 * | 'preceding-sibling'
11191 * | 'self'
11193 * Returns the axis or 0
11195 static xmlXPathAxisVal
11196 xmlXPathIsAxisName(const xmlChar *name) {
11197 xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
11198 switch (name[0]) {
11199 case 'a':
11200 if (xmlStrEqual(name, BAD_CAST "ancestor"))
11201 ret = AXIS_ANCESTOR;
11202 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
11203 ret = AXIS_ANCESTOR_OR_SELF;
11204 if (xmlStrEqual(name, BAD_CAST "attribute"))
11205 ret = AXIS_ATTRIBUTE;
11206 break;
11207 case 'c':
11208 if (xmlStrEqual(name, BAD_CAST "child"))
11209 ret = AXIS_CHILD;
11210 break;
11211 case 'd':
11212 if (xmlStrEqual(name, BAD_CAST "descendant"))
11213 ret = AXIS_DESCENDANT;
11214 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
11215 ret = AXIS_DESCENDANT_OR_SELF;
11216 break;
11217 case 'f':
11218 if (xmlStrEqual(name, BAD_CAST "following"))
11219 ret = AXIS_FOLLOWING;
11220 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
11221 ret = AXIS_FOLLOWING_SIBLING;
11222 break;
11223 case 'n':
11224 if (xmlStrEqual(name, BAD_CAST "namespace"))
11225 ret = AXIS_NAMESPACE;
11226 break;
11227 case 'p':
11228 if (xmlStrEqual(name, BAD_CAST "parent"))
11229 ret = AXIS_PARENT;
11230 if (xmlStrEqual(name, BAD_CAST "preceding"))
11231 ret = AXIS_PRECEDING;
11232 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
11233 ret = AXIS_PRECEDING_SIBLING;
11234 break;
11235 case 's':
11236 if (xmlStrEqual(name, BAD_CAST "self"))
11237 ret = AXIS_SELF;
11238 break;
11240 return(ret);
11244 * xmlXPathCompStep:
11245 * @ctxt: the XPath Parser context
11247 * [4] Step ::= AxisSpecifier NodeTest Predicate*
11248 * | AbbreviatedStep
11250 * [12] AbbreviatedStep ::= '.' | '..'
11252 * [5] AxisSpecifier ::= AxisName '::'
11253 * | AbbreviatedAxisSpecifier
11255 * [13] AbbreviatedAxisSpecifier ::= '@'?
11257 * Modified for XPtr range support as:
11259 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
11260 * | AbbreviatedStep
11261 * | 'range-to' '(' Expr ')' Predicate*
11263 * Compile one step in a Location Path
11264 * A location step of . is short for self::node(). This is
11265 * particularly useful in conjunction with //. For example, the
11266 * location path .//para is short for
11267 * self::node()/descendant-or-self::node()/child::para
11268 * and so will select all para descendant elements of the context
11269 * node.
11270 * Similarly, a location step of .. is short for parent::node().
11271 * For example, ../title is short for parent::node()/child::title
11272 * and so will select the title children of the parent of the context
11273 * node.
11275 static void
11276 xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
11277 #ifdef LIBXML_XPTR_ENABLED
11278 int rangeto = 0;
11279 int op2 = -1;
11280 #endif
11282 SKIP_BLANKS;
11283 if ((CUR == '.') && (NXT(1) == '.')) {
11284 SKIP(2);
11285 SKIP_BLANKS;
11286 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
11287 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11288 } else if (CUR == '.') {
11289 NEXT;
11290 SKIP_BLANKS;
11291 } else {
11292 xmlChar *name = NULL;
11293 xmlChar *prefix = NULL;
11294 xmlXPathTestVal test = (xmlXPathTestVal) 0;
11295 xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
11296 xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
11297 int op1;
11300 * The modification needed for XPointer change to the production
11302 #ifdef LIBXML_XPTR_ENABLED
11303 if (ctxt->xptr) {
11304 name = xmlXPathParseNCName(ctxt);
11305 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
11306 op2 = ctxt->comp->last;
11307 xmlFree(name);
11308 SKIP_BLANKS;
11309 if (CUR != '(') {
11310 XP_ERROR(XPATH_EXPR_ERROR);
11312 NEXT;
11313 SKIP_BLANKS;
11315 xmlXPathCompileExpr(ctxt, 1);
11316 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
11317 CHECK_ERROR;
11319 SKIP_BLANKS;
11320 if (CUR != ')') {
11321 XP_ERROR(XPATH_EXPR_ERROR);
11323 NEXT;
11324 rangeto = 1;
11325 goto eval_predicates;
11328 #endif
11329 if (CUR == '*') {
11330 axis = AXIS_CHILD;
11331 } else {
11332 if (name == NULL)
11333 name = xmlXPathParseNCName(ctxt);
11334 if (name != NULL) {
11335 axis = xmlXPathIsAxisName(name);
11336 if (axis != 0) {
11337 SKIP_BLANKS;
11338 if ((CUR == ':') && (NXT(1) == ':')) {
11339 SKIP(2);
11340 xmlFree(name);
11341 name = NULL;
11342 } else {
11343 /* an element name can conflict with an axis one :-\ */
11344 axis = AXIS_CHILD;
11346 } else {
11347 axis = AXIS_CHILD;
11349 } else if (CUR == '@') {
11350 NEXT;
11351 axis = AXIS_ATTRIBUTE;
11352 } else {
11353 axis = AXIS_CHILD;
11357 if (ctxt->error != XPATH_EXPRESSION_OK) {
11358 xmlFree(name);
11359 return;
11362 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
11363 if (test == 0)
11364 return;
11366 if ((prefix != NULL) && (ctxt->context != NULL) &&
11367 (ctxt->context->flags & XML_XPATH_CHECKNS)) {
11368 if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
11369 xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
11372 #ifdef DEBUG_STEP
11373 xmlGenericError(xmlGenericErrorContext,
11374 "Basis : computing new set\n");
11375 #endif
11377 #ifdef DEBUG_STEP
11378 xmlGenericError(xmlGenericErrorContext, "Basis : ");
11379 if (ctxt->value == NULL)
11380 xmlGenericError(xmlGenericErrorContext, "no value\n");
11381 else if (ctxt->value->nodesetval == NULL)
11382 xmlGenericError(xmlGenericErrorContext, "Empty\n");
11383 else
11384 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
11385 #endif
11387 #ifdef LIBXML_XPTR_ENABLED
11388 eval_predicates:
11389 #endif
11390 op1 = ctxt->comp->last;
11391 ctxt->comp->last = -1;
11393 SKIP_BLANKS;
11394 while (CUR == '[') {
11395 xmlXPathCompPredicate(ctxt, 0);
11398 #ifdef LIBXML_XPTR_ENABLED
11399 if (rangeto) {
11400 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
11401 } else
11402 #endif
11403 if (PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
11404 test, type, (void *)prefix, (void *)name) == -1) {
11405 xmlFree(prefix);
11406 xmlFree(name);
11409 #ifdef DEBUG_STEP
11410 xmlGenericError(xmlGenericErrorContext, "Step : ");
11411 if (ctxt->value == NULL)
11412 xmlGenericError(xmlGenericErrorContext, "no value\n");
11413 else if (ctxt->value->nodesetval == NULL)
11414 xmlGenericError(xmlGenericErrorContext, "Empty\n");
11415 else
11416 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
11417 ctxt->value->nodesetval);
11418 #endif
11422 * xmlXPathCompRelativeLocationPath:
11423 * @ctxt: the XPath Parser context
11425 * [3] RelativeLocationPath ::= Step
11426 * | RelativeLocationPath '/' Step
11427 * | AbbreviatedRelativeLocationPath
11428 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
11430 * Compile a relative location path.
11432 static void
11433 xmlXPathCompRelativeLocationPath
11434 (xmlXPathParserContextPtr ctxt) {
11435 SKIP_BLANKS;
11436 if ((CUR == '/') && (NXT(1) == '/')) {
11437 SKIP(2);
11438 SKIP_BLANKS;
11439 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11440 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11441 } else if (CUR == '/') {
11442 NEXT;
11443 SKIP_BLANKS;
11445 xmlXPathCompStep(ctxt);
11446 CHECK_ERROR;
11447 SKIP_BLANKS;
11448 while (CUR == '/') {
11449 if ((CUR == '/') && (NXT(1) == '/')) {
11450 SKIP(2);
11451 SKIP_BLANKS;
11452 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11453 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11454 xmlXPathCompStep(ctxt);
11455 } else if (CUR == '/') {
11456 NEXT;
11457 SKIP_BLANKS;
11458 xmlXPathCompStep(ctxt);
11460 SKIP_BLANKS;
11465 * xmlXPathCompLocationPath:
11466 * @ctxt: the XPath Parser context
11468 * [1] LocationPath ::= RelativeLocationPath
11469 * | AbsoluteLocationPath
11470 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
11471 * | AbbreviatedAbsoluteLocationPath
11472 * [10] AbbreviatedAbsoluteLocationPath ::=
11473 * '//' RelativeLocationPath
11475 * Compile a location path
11477 * // is short for /descendant-or-self::node()/. For example,
11478 * //para is short for /descendant-or-self::node()/child::para and
11479 * so will select any para element in the document (even a para element
11480 * that is a document element will be selected by //para since the
11481 * document element node is a child of the root node); div//para is
11482 * short for div/descendant-or-self::node()/child::para and so will
11483 * select all para descendants of div children.
11485 static void
11486 xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
11487 SKIP_BLANKS;
11488 if (CUR != '/') {
11489 xmlXPathCompRelativeLocationPath(ctxt);
11490 } else {
11491 while (CUR == '/') {
11492 if ((CUR == '/') && (NXT(1) == '/')) {
11493 SKIP(2);
11494 SKIP_BLANKS;
11495 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11496 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11497 xmlXPathCompRelativeLocationPath(ctxt);
11498 } else if (CUR == '/') {
11499 NEXT;
11500 SKIP_BLANKS;
11501 if ((CUR != 0 ) &&
11502 ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
11503 (CUR == '@') || (CUR == '*')))
11504 xmlXPathCompRelativeLocationPath(ctxt);
11506 CHECK_ERROR;
11511 /************************************************************************
11513 * XPath precompiled expression evaluation *
11515 ************************************************************************/
11517 static int
11518 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
11520 #ifdef DEBUG_STEP
11521 static void
11522 xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
11523 int nbNodes)
11525 xmlGenericError(xmlGenericErrorContext, "new step : ");
11526 switch (op->value) {
11527 case AXIS_ANCESTOR:
11528 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
11529 break;
11530 case AXIS_ANCESTOR_OR_SELF:
11531 xmlGenericError(xmlGenericErrorContext,
11532 "axis 'ancestors-or-self' ");
11533 break;
11534 case AXIS_ATTRIBUTE:
11535 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
11536 break;
11537 case AXIS_CHILD:
11538 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
11539 break;
11540 case AXIS_DESCENDANT:
11541 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
11542 break;
11543 case AXIS_DESCENDANT_OR_SELF:
11544 xmlGenericError(xmlGenericErrorContext,
11545 "axis 'descendant-or-self' ");
11546 break;
11547 case AXIS_FOLLOWING:
11548 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
11549 break;
11550 case AXIS_FOLLOWING_SIBLING:
11551 xmlGenericError(xmlGenericErrorContext,
11552 "axis 'following-siblings' ");
11553 break;
11554 case AXIS_NAMESPACE:
11555 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
11556 break;
11557 case AXIS_PARENT:
11558 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
11559 break;
11560 case AXIS_PRECEDING:
11561 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
11562 break;
11563 case AXIS_PRECEDING_SIBLING:
11564 xmlGenericError(xmlGenericErrorContext,
11565 "axis 'preceding-sibling' ");
11566 break;
11567 case AXIS_SELF:
11568 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
11569 break;
11571 xmlGenericError(xmlGenericErrorContext,
11572 " context contains %d nodes\n", nbNodes);
11573 switch (op->value2) {
11574 case NODE_TEST_NONE:
11575 xmlGenericError(xmlGenericErrorContext,
11576 " searching for none !!!\n");
11577 break;
11578 case NODE_TEST_TYPE:
11579 xmlGenericError(xmlGenericErrorContext,
11580 " searching for type %d\n", op->value3);
11581 break;
11582 case NODE_TEST_PI:
11583 xmlGenericError(xmlGenericErrorContext,
11584 " searching for PI !!!\n");
11585 break;
11586 case NODE_TEST_ALL:
11587 xmlGenericError(xmlGenericErrorContext,
11588 " searching for *\n");
11589 break;
11590 case NODE_TEST_NS:
11591 xmlGenericError(xmlGenericErrorContext,
11592 " searching for namespace %s\n",
11593 op->value5);
11594 break;
11595 case NODE_TEST_NAME:
11596 xmlGenericError(xmlGenericErrorContext,
11597 " searching for name %s\n", op->value5);
11598 if (op->value4)
11599 xmlGenericError(xmlGenericErrorContext,
11600 " with namespace %s\n", op->value4);
11601 break;
11603 xmlGenericError(xmlGenericErrorContext, "Testing : ");
11605 #endif /* DEBUG_STEP */
11608 * xmlXPathNodeSetFilter:
11609 * @ctxt: the XPath Parser context
11610 * @set: the node set to filter
11611 * @filterOpIndex: the index of the predicate/filter op
11612 * @minPos: minimum position in the filtered set (1-based)
11613 * @maxPos: maximum position in the filtered set (1-based)
11614 * @hasNsNodes: true if the node set may contain namespace nodes
11616 * Filter a node set, keeping only nodes for which the predicate expression
11617 * matches. Afterwards, keep only nodes between minPos and maxPos in the
11618 * filtered result.
11620 static void
11621 xmlXPathNodeSetFilter(xmlXPathParserContextPtr ctxt,
11622 xmlNodeSetPtr set,
11623 int filterOpIndex,
11624 int minPos, int maxPos,
11625 int hasNsNodes)
11627 xmlXPathContextPtr xpctxt;
11628 xmlNodePtr oldnode;
11629 xmlDocPtr olddoc;
11630 xmlXPathStepOpPtr filterOp;
11631 int oldcs, oldpp;
11632 int i, j, pos;
11634 if ((set == NULL) || (set->nodeNr == 0))
11635 return;
11638 * Check if the node set contains a sufficient number of nodes for
11639 * the requested range.
11641 if (set->nodeNr < minPos) {
11642 xmlXPathNodeSetClear(set, hasNsNodes);
11643 return;
11646 xpctxt = ctxt->context;
11647 oldnode = xpctxt->node;
11648 olddoc = xpctxt->doc;
11649 oldcs = xpctxt->contextSize;
11650 oldpp = xpctxt->proximityPosition;
11651 filterOp = &ctxt->comp->steps[filterOpIndex];
11653 xpctxt->contextSize = set->nodeNr;
11655 for (i = 0, j = 0, pos = 1; i < set->nodeNr; i++) {
11656 xmlNodePtr node = set->nodeTab[i];
11657 int res;
11659 xpctxt->node = node;
11660 xpctxt->proximityPosition = i + 1;
11663 * Also set the xpath document in case things like
11664 * key() are evaluated in the predicate.
11666 * TODO: Get real doc for namespace nodes.
11668 if ((node->type != XML_NAMESPACE_DECL) &&
11669 (node->doc != NULL))
11670 xpctxt->doc = node->doc;
11672 res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
11674 if (ctxt->error != XPATH_EXPRESSION_OK)
11675 break;
11676 if (res < 0) {
11677 /* Shouldn't happen */
11678 xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
11679 break;
11682 if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
11683 if (i != j) {
11684 set->nodeTab[j] = node;
11685 set->nodeTab[i] = NULL;
11688 j += 1;
11689 } else {
11690 /* Remove the entry from the initial node set. */
11691 set->nodeTab[i] = NULL;
11692 if (node->type == XML_NAMESPACE_DECL)
11693 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
11696 if (res != 0) {
11697 if (pos == maxPos) {
11698 i += 1;
11699 break;
11702 pos += 1;
11706 /* Free remaining nodes. */
11707 if (hasNsNodes) {
11708 for (; i < set->nodeNr; i++) {
11709 xmlNodePtr node = set->nodeTab[i];
11710 if ((node != NULL) && (node->type == XML_NAMESPACE_DECL))
11711 xmlXPathNodeSetFreeNs((xmlNsPtr) node);
11715 set->nodeNr = j;
11717 /* If too many elements were removed, shrink table to preserve memory. */
11718 if ((set->nodeMax > XML_NODESET_DEFAULT) &&
11719 (set->nodeNr < set->nodeMax / 2)) {
11720 xmlNodePtr *tmp;
11721 int nodeMax = set->nodeNr;
11723 if (nodeMax < XML_NODESET_DEFAULT)
11724 nodeMax = XML_NODESET_DEFAULT;
11725 tmp = (xmlNodePtr *) xmlRealloc(set->nodeTab,
11726 nodeMax * sizeof(xmlNodePtr));
11727 if (tmp == NULL) {
11728 xmlXPathPErrMemory(ctxt, "shrinking nodeset\n");
11729 } else {
11730 set->nodeTab = tmp;
11731 set->nodeMax = nodeMax;
11735 xpctxt->node = oldnode;
11736 xpctxt->doc = olddoc;
11737 xpctxt->contextSize = oldcs;
11738 xpctxt->proximityPosition = oldpp;
11741 #ifdef LIBXML_XPTR_ENABLED
11743 * xmlXPathLocationSetFilter:
11744 * @ctxt: the XPath Parser context
11745 * @locset: the location set to filter
11746 * @filterOpIndex: the index of the predicate/filter op
11747 * @minPos: minimum position in the filtered set (1-based)
11748 * @maxPos: maximum position in the filtered set (1-based)
11750 * Filter a location set, keeping only nodes for which the predicate
11751 * expression matches. Afterwards, keep only nodes between minPos and maxPos
11752 * in the filtered result.
11754 static void
11755 xmlXPathLocationSetFilter(xmlXPathParserContextPtr ctxt,
11756 xmlLocationSetPtr locset,
11757 int filterOpIndex,
11758 int minPos, int maxPos)
11760 xmlXPathContextPtr xpctxt;
11761 xmlNodePtr oldnode;
11762 xmlDocPtr olddoc;
11763 xmlXPathStepOpPtr filterOp;
11764 int oldcs, oldpp;
11765 int i, j, pos;
11767 if ((locset == NULL) || (locset->locNr == 0) || (filterOpIndex == -1))
11768 return;
11770 xpctxt = ctxt->context;
11771 oldnode = xpctxt->node;
11772 olddoc = xpctxt->doc;
11773 oldcs = xpctxt->contextSize;
11774 oldpp = xpctxt->proximityPosition;
11775 filterOp = &ctxt->comp->steps[filterOpIndex];
11777 xpctxt->contextSize = locset->locNr;
11779 for (i = 0, j = 0, pos = 1; i < locset->locNr; i++) {
11780 xmlNodePtr contextNode = locset->locTab[i]->user;
11781 int res;
11783 xpctxt->node = contextNode;
11784 xpctxt->proximityPosition = i + 1;
11787 * Also set the xpath document in case things like
11788 * key() are evaluated in the predicate.
11790 * TODO: Get real doc for namespace nodes.
11792 if ((contextNode->type != XML_NAMESPACE_DECL) &&
11793 (contextNode->doc != NULL))
11794 xpctxt->doc = contextNode->doc;
11796 res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
11798 if (ctxt->error != XPATH_EXPRESSION_OK)
11799 break;
11800 if (res < 0) {
11801 /* Shouldn't happen */
11802 xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
11803 break;
11806 if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
11807 if (i != j) {
11808 locset->locTab[j] = locset->locTab[i];
11809 locset->locTab[i] = NULL;
11812 j += 1;
11813 } else {
11814 /* Remove the entry from the initial location set. */
11815 xmlXPathFreeObject(locset->locTab[i]);
11816 locset->locTab[i] = NULL;
11819 if (res != 0) {
11820 if (pos == maxPos) {
11821 i += 1;
11822 break;
11825 pos += 1;
11829 /* Free remaining nodes. */
11830 for (; i < locset->locNr; i++)
11831 xmlXPathFreeObject(locset->locTab[i]);
11833 locset->locNr = j;
11835 /* If too many elements were removed, shrink table to preserve memory. */
11836 if ((locset->locMax > XML_NODESET_DEFAULT) &&
11837 (locset->locNr < locset->locMax / 2)) {
11838 xmlXPathObjectPtr *tmp;
11839 int locMax = locset->locNr;
11841 if (locMax < XML_NODESET_DEFAULT)
11842 locMax = XML_NODESET_DEFAULT;
11843 tmp = (xmlXPathObjectPtr *) xmlRealloc(locset->locTab,
11844 locMax * sizeof(xmlXPathObjectPtr));
11845 if (tmp == NULL) {
11846 xmlXPathPErrMemory(ctxt, "shrinking locset\n");
11847 } else {
11848 locset->locTab = tmp;
11849 locset->locMax = locMax;
11853 xpctxt->node = oldnode;
11854 xpctxt->doc = olddoc;
11855 xpctxt->contextSize = oldcs;
11856 xpctxt->proximityPosition = oldpp;
11858 #endif /* LIBXML_XPTR_ENABLED */
11861 * xmlXPathCompOpEvalPredicate:
11862 * @ctxt: the XPath Parser context
11863 * @op: the predicate op
11864 * @set: the node set to filter
11865 * @minPos: minimum position in the filtered set (1-based)
11866 * @maxPos: maximum position in the filtered set (1-based)
11867 * @hasNsNodes: true if the node set may contain namespace nodes
11869 * Filter a node set, keeping only nodes for which the sequence of predicate
11870 * expressions matches. Afterwards, keep only nodes between minPos and maxPos
11871 * in the filtered result.
11873 static void
11874 xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
11875 xmlXPathStepOpPtr op,
11876 xmlNodeSetPtr set,
11877 int minPos, int maxPos,
11878 int hasNsNodes)
11880 if (op->ch1 != -1) {
11881 xmlXPathCompExprPtr comp = ctxt->comp;
11883 * Process inner predicates first.
11885 if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11886 xmlGenericError(xmlGenericErrorContext,
11887 "xmlXPathCompOpEvalPredicate: Expected a predicate\n");
11888 XP_ERROR(XPATH_INVALID_OPERAND);
11890 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
11891 XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
11892 ctxt->context->depth += 1;
11893 xmlXPathCompOpEvalPredicate(ctxt, &comp->steps[op->ch1], set,
11894 1, set->nodeNr, hasNsNodes);
11895 ctxt->context->depth -= 1;
11896 CHECK_ERROR;
11899 if (op->ch2 != -1)
11900 xmlXPathNodeSetFilter(ctxt, set, op->ch2, minPos, maxPos, hasNsNodes);
11903 static int
11904 xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
11905 xmlXPathStepOpPtr op,
11906 int *maxPos)
11909 xmlXPathStepOpPtr exprOp;
11912 * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
11916 * If not -1, then ch1 will point to:
11917 * 1) For predicates (XPATH_OP_PREDICATE):
11918 * - an inner predicate operator
11919 * 2) For filters (XPATH_OP_FILTER):
11920 * - an inner filter operator OR
11921 * - an expression selecting the node set.
11922 * E.g. "key('a', 'b')" or "(//foo | //bar)".
11924 if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
11925 return(0);
11927 if (op->ch2 != -1) {
11928 exprOp = &ctxt->comp->steps[op->ch2];
11929 } else
11930 return(0);
11932 if ((exprOp != NULL) &&
11933 (exprOp->op == XPATH_OP_VALUE) &&
11934 (exprOp->value4 != NULL) &&
11935 (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
11937 double floatval = ((xmlXPathObjectPtr) exprOp->value4)->floatval;
11940 * We have a "[n]" predicate here.
11941 * TODO: Unfortunately this simplistic test here is not
11942 * able to detect a position() predicate in compound
11943 * expressions like "[@attr = 'a" and position() = 1],
11944 * and even not the usage of position() in
11945 * "[position() = 1]"; thus - obviously - a position-range,
11946 * like it "[position() < 5]", is also not detected.
11947 * Maybe we could rewrite the AST to ease the optimization.
11950 if ((floatval > INT_MIN) && (floatval < INT_MAX)) {
11951 *maxPos = (int) floatval;
11952 if (floatval == (double) *maxPos)
11953 return(1);
11956 return(0);
11959 static int
11960 xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
11961 xmlXPathStepOpPtr op,
11962 xmlNodePtr * first, xmlNodePtr * last,
11963 int toBool)
11966 #define XP_TEST_HIT \
11967 if (hasAxisRange != 0) { \
11968 if (++pos == maxPos) { \
11969 if (addNode(seq, cur) < 0) \
11970 ctxt->error = XPATH_MEMORY_ERROR; \
11971 goto axis_range_end; } \
11972 } else { \
11973 if (addNode(seq, cur) < 0) \
11974 ctxt->error = XPATH_MEMORY_ERROR; \
11975 if (breakOnFirstHit) goto first_hit; }
11977 #define XP_TEST_HIT_NS \
11978 if (hasAxisRange != 0) { \
11979 if (++pos == maxPos) { \
11980 hasNsNodes = 1; \
11981 if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
11982 ctxt->error = XPATH_MEMORY_ERROR; \
11983 goto axis_range_end; } \
11984 } else { \
11985 hasNsNodes = 1; \
11986 if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
11987 ctxt->error = XPATH_MEMORY_ERROR; \
11988 if (breakOnFirstHit) goto first_hit; }
11990 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
11991 xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
11992 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
11993 const xmlChar *prefix = op->value4;
11994 const xmlChar *name = op->value5;
11995 const xmlChar *URI = NULL;
11997 #ifdef DEBUG_STEP
11998 int nbMatches = 0, prevMatches = 0;
11999 #endif
12000 int total = 0, hasNsNodes = 0;
12001 /* The popped object holding the context nodes */
12002 xmlXPathObjectPtr obj;
12003 /* The set of context nodes for the node tests */
12004 xmlNodeSetPtr contextSeq;
12005 int contextIdx;
12006 xmlNodePtr contextNode;
12007 /* The final resulting node set wrt to all context nodes */
12008 xmlNodeSetPtr outSeq;
12010 * The temporary resulting node set wrt 1 context node.
12011 * Used to feed predicate evaluation.
12013 xmlNodeSetPtr seq;
12014 xmlNodePtr cur;
12015 /* First predicate operator */
12016 xmlXPathStepOpPtr predOp;
12017 int maxPos; /* The requested position() (when a "[n]" predicate) */
12018 int hasPredicateRange, hasAxisRange, pos;
12019 int breakOnFirstHit;
12021 xmlXPathTraversalFunction next = NULL;
12022 int (*addNode) (xmlNodeSetPtr, xmlNodePtr);
12023 xmlXPathNodeSetMergeFunction mergeAndClear;
12024 xmlNodePtr oldContextNode;
12025 xmlXPathContextPtr xpctxt = ctxt->context;
12028 CHECK_TYPE0(XPATH_NODESET);
12029 obj = valuePop(ctxt);
12031 * Setup namespaces.
12033 if (prefix != NULL) {
12034 URI = xmlXPathNsLookup(xpctxt, prefix);
12035 if (URI == NULL) {
12036 xmlXPathReleaseObject(xpctxt, obj);
12037 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
12041 * Setup axis.
12043 * MAYBE FUTURE TODO: merging optimizations:
12044 * - If the nodes to be traversed wrt to the initial nodes and
12045 * the current axis cannot overlap, then we could avoid searching
12046 * for duplicates during the merge.
12047 * But the question is how/when to evaluate if they cannot overlap.
12048 * Example: if we know that for two initial nodes, the one is
12049 * not in the ancestor-or-self axis of the other, then we could safely
12050 * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
12051 * the descendant-or-self axis.
12053 mergeAndClear = xmlXPathNodeSetMergeAndClear;
12054 switch (axis) {
12055 case AXIS_ANCESTOR:
12056 first = NULL;
12057 next = xmlXPathNextAncestor;
12058 break;
12059 case AXIS_ANCESTOR_OR_SELF:
12060 first = NULL;
12061 next = xmlXPathNextAncestorOrSelf;
12062 break;
12063 case AXIS_ATTRIBUTE:
12064 first = NULL;
12065 last = NULL;
12066 next = xmlXPathNextAttribute;
12067 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12068 break;
12069 case AXIS_CHILD:
12070 last = NULL;
12071 if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
12072 (type == NODE_TYPE_NODE))
12075 * Optimization if an element node type is 'element'.
12077 next = xmlXPathNextChildElement;
12078 } else
12079 next = xmlXPathNextChild;
12080 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12081 break;
12082 case AXIS_DESCENDANT:
12083 last = NULL;
12084 next = xmlXPathNextDescendant;
12085 break;
12086 case AXIS_DESCENDANT_OR_SELF:
12087 last = NULL;
12088 next = xmlXPathNextDescendantOrSelf;
12089 break;
12090 case AXIS_FOLLOWING:
12091 last = NULL;
12092 next = xmlXPathNextFollowing;
12093 break;
12094 case AXIS_FOLLOWING_SIBLING:
12095 last = NULL;
12096 next = xmlXPathNextFollowingSibling;
12097 break;
12098 case AXIS_NAMESPACE:
12099 first = NULL;
12100 last = NULL;
12101 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
12102 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12103 break;
12104 case AXIS_PARENT:
12105 first = NULL;
12106 next = xmlXPathNextParent;
12107 break;
12108 case AXIS_PRECEDING:
12109 first = NULL;
12110 next = xmlXPathNextPrecedingInternal;
12111 break;
12112 case AXIS_PRECEDING_SIBLING:
12113 first = NULL;
12114 next = xmlXPathNextPrecedingSibling;
12115 break;
12116 case AXIS_SELF:
12117 first = NULL;
12118 last = NULL;
12119 next = xmlXPathNextSelf;
12120 mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12121 break;
12124 #ifdef DEBUG_STEP
12125 xmlXPathDebugDumpStepAxis(op,
12126 (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
12127 #endif
12129 if (next == NULL) {
12130 xmlXPathReleaseObject(xpctxt, obj);
12131 return(0);
12133 contextSeq = obj->nodesetval;
12134 if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
12135 xmlXPathReleaseObject(xpctxt, obj);
12136 valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
12137 return(0);
12140 * Predicate optimization ---------------------------------------------
12141 * If this step has a last predicate, which contains a position(),
12142 * then we'll optimize (although not exactly "position()", but only
12143 * the short-hand form, i.e., "[n]".
12145 * Example - expression "/foo[parent::bar][1]":
12147 * COLLECT 'child' 'name' 'node' foo -- op (we are here)
12148 * ROOT -- op->ch1
12149 * PREDICATE -- op->ch2 (predOp)
12150 * PREDICATE -- predOp->ch1 = [parent::bar]
12151 * SORT
12152 * COLLECT 'parent' 'name' 'node' bar
12153 * NODE
12154 * ELEM Object is a number : 1 -- predOp->ch2 = [1]
12157 maxPos = 0;
12158 predOp = NULL;
12159 hasPredicateRange = 0;
12160 hasAxisRange = 0;
12161 if (op->ch2 != -1) {
12163 * There's at least one predicate. 16 == XPATH_OP_PREDICATE
12165 predOp = &ctxt->comp->steps[op->ch2];
12166 if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
12167 if (predOp->ch1 != -1) {
12169 * Use the next inner predicate operator.
12171 predOp = &ctxt->comp->steps[predOp->ch1];
12172 hasPredicateRange = 1;
12173 } else {
12175 * There's no other predicate than the [n] predicate.
12177 predOp = NULL;
12178 hasAxisRange = 1;
12182 breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
12184 * Axis traversal -----------------------------------------------------
12187 * 2.3 Node Tests
12188 * - For the attribute axis, the principal node type is attribute.
12189 * - For the namespace axis, the principal node type is namespace.
12190 * - For other axes, the principal node type is element.
12192 * A node test * is true for any node of the
12193 * principal node type. For example, child::* will
12194 * select all element children of the context node
12196 oldContextNode = xpctxt->node;
12197 addNode = xmlXPathNodeSetAddUnique;
12198 outSeq = NULL;
12199 seq = NULL;
12200 contextNode = NULL;
12201 contextIdx = 0;
12204 while (((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) &&
12205 (ctxt->error == XPATH_EXPRESSION_OK)) {
12206 xpctxt->node = contextSeq->nodeTab[contextIdx++];
12208 if (seq == NULL) {
12209 seq = xmlXPathNodeSetCreate(NULL);
12210 if (seq == NULL) {
12211 /* TODO: Propagate memory error. */
12212 total = 0;
12213 goto error;
12217 * Traverse the axis and test the nodes.
12219 pos = 0;
12220 cur = NULL;
12221 hasNsNodes = 0;
12222 do {
12223 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12224 goto error;
12226 cur = next(ctxt, cur);
12227 if (cur == NULL)
12228 break;
12231 * QUESTION TODO: What does the "first" and "last" stuff do?
12233 if ((first != NULL) && (*first != NULL)) {
12234 if (*first == cur)
12235 break;
12236 if (((total % 256) == 0) &&
12237 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12238 (xmlXPathCmpNodesExt(*first, cur) >= 0))
12239 #else
12240 (xmlXPathCmpNodes(*first, cur) >= 0))
12241 #endif
12243 break;
12246 if ((last != NULL) && (*last != NULL)) {
12247 if (*last == cur)
12248 break;
12249 if (((total % 256) == 0) &&
12250 #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12251 (xmlXPathCmpNodesExt(cur, *last) >= 0))
12252 #else
12253 (xmlXPathCmpNodes(cur, *last) >= 0))
12254 #endif
12256 break;
12260 total++;
12262 #ifdef DEBUG_STEP
12263 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
12264 #endif
12266 switch (test) {
12267 case NODE_TEST_NONE:
12268 total = 0;
12269 STRANGE
12270 goto error;
12271 case NODE_TEST_TYPE:
12272 if (type == NODE_TYPE_NODE) {
12273 switch (cur->type) {
12274 case XML_DOCUMENT_NODE:
12275 case XML_HTML_DOCUMENT_NODE:
12276 #ifdef LIBXML_DOCB_ENABLED
12277 case XML_DOCB_DOCUMENT_NODE:
12278 #endif
12279 case XML_ELEMENT_NODE:
12280 case XML_ATTRIBUTE_NODE:
12281 case XML_PI_NODE:
12282 case XML_COMMENT_NODE:
12283 case XML_CDATA_SECTION_NODE:
12284 case XML_TEXT_NODE:
12285 XP_TEST_HIT
12286 break;
12287 case XML_NAMESPACE_DECL: {
12288 if (axis == AXIS_NAMESPACE) {
12289 XP_TEST_HIT_NS
12290 } else {
12291 hasNsNodes = 1;
12292 XP_TEST_HIT
12294 break;
12296 default:
12297 break;
12299 } else if (cur->type == (xmlElementType) type) {
12300 if (cur->type == XML_NAMESPACE_DECL)
12301 XP_TEST_HIT_NS
12302 else
12303 XP_TEST_HIT
12304 } else if ((type == NODE_TYPE_TEXT) &&
12305 (cur->type == XML_CDATA_SECTION_NODE))
12307 XP_TEST_HIT
12309 break;
12310 case NODE_TEST_PI:
12311 if ((cur->type == XML_PI_NODE) &&
12312 ((name == NULL) || xmlStrEqual(name, cur->name)))
12314 XP_TEST_HIT
12316 break;
12317 case NODE_TEST_ALL:
12318 if (axis == AXIS_ATTRIBUTE) {
12319 if (cur->type == XML_ATTRIBUTE_NODE)
12321 if (prefix == NULL)
12323 XP_TEST_HIT
12324 } else if ((cur->ns != NULL) &&
12325 (xmlStrEqual(URI, cur->ns->href)))
12327 XP_TEST_HIT
12330 } else if (axis == AXIS_NAMESPACE) {
12331 if (cur->type == XML_NAMESPACE_DECL)
12333 XP_TEST_HIT_NS
12335 } else {
12336 if (cur->type == XML_ELEMENT_NODE) {
12337 if (prefix == NULL)
12339 XP_TEST_HIT
12341 } else if ((cur->ns != NULL) &&
12342 (xmlStrEqual(URI, cur->ns->href)))
12344 XP_TEST_HIT
12348 break;
12349 case NODE_TEST_NS:{
12350 TODO;
12351 break;
12353 case NODE_TEST_NAME:
12354 if (axis == AXIS_ATTRIBUTE) {
12355 if (cur->type != XML_ATTRIBUTE_NODE)
12356 break;
12357 } else if (axis == AXIS_NAMESPACE) {
12358 if (cur->type != XML_NAMESPACE_DECL)
12359 break;
12360 } else {
12361 if (cur->type != XML_ELEMENT_NODE)
12362 break;
12364 switch (cur->type) {
12365 case XML_ELEMENT_NODE:
12366 if (xmlStrEqual(name, cur->name)) {
12367 if (prefix == NULL) {
12368 if (cur->ns == NULL)
12370 XP_TEST_HIT
12372 } else {
12373 if ((cur->ns != NULL) &&
12374 (xmlStrEqual(URI, cur->ns->href)))
12376 XP_TEST_HIT
12380 break;
12381 case XML_ATTRIBUTE_NODE:{
12382 xmlAttrPtr attr = (xmlAttrPtr) cur;
12384 if (xmlStrEqual(name, attr->name)) {
12385 if (prefix == NULL) {
12386 if ((attr->ns == NULL) ||
12387 (attr->ns->prefix == NULL))
12389 XP_TEST_HIT
12391 } else {
12392 if ((attr->ns != NULL) &&
12393 (xmlStrEqual(URI,
12394 attr->ns->href)))
12396 XP_TEST_HIT
12400 break;
12402 case XML_NAMESPACE_DECL:
12403 if (cur->type == XML_NAMESPACE_DECL) {
12404 xmlNsPtr ns = (xmlNsPtr) cur;
12406 if ((ns->prefix != NULL) && (name != NULL)
12407 && (xmlStrEqual(ns->prefix, name)))
12409 XP_TEST_HIT_NS
12412 break;
12413 default:
12414 break;
12416 break;
12417 } /* switch(test) */
12418 } while ((cur != NULL) && (ctxt->error == XPATH_EXPRESSION_OK));
12420 goto apply_predicates;
12422 axis_range_end: /* ----------------------------------------------------- */
12424 * We have a "/foo[n]", and position() = n was reached.
12425 * Note that we can have as well "/foo/::parent::foo[1]", so
12426 * a duplicate-aware merge is still needed.
12427 * Merge with the result.
12429 if (outSeq == NULL) {
12430 outSeq = seq;
12431 seq = NULL;
12432 } else
12433 /* TODO: Check memory error. */
12434 outSeq = mergeAndClear(outSeq, seq);
12436 * Break if only a true/false result was requested.
12438 if (toBool)
12439 break;
12440 continue;
12442 first_hit: /* ---------------------------------------------------------- */
12444 * Break if only a true/false result was requested and
12445 * no predicates existed and a node test succeeded.
12447 if (outSeq == NULL) {
12448 outSeq = seq;
12449 seq = NULL;
12450 } else
12451 /* TODO: Check memory error. */
12452 outSeq = mergeAndClear(outSeq, seq);
12453 break;
12455 #ifdef DEBUG_STEP
12456 if (seq != NULL)
12457 nbMatches += seq->nodeNr;
12458 #endif
12460 apply_predicates: /* --------------------------------------------------- */
12461 if (ctxt->error != XPATH_EXPRESSION_OK)
12462 goto error;
12465 * Apply predicates.
12467 if ((predOp != NULL) && (seq->nodeNr > 0)) {
12469 * E.g. when we have a "/foo[some expression][n]".
12472 * QUESTION TODO: The old predicate evaluation took into
12473 * account location-sets.
12474 * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
12475 * Do we expect such a set here?
12476 * All what I learned now from the evaluation semantics
12477 * does not indicate that a location-set will be processed
12478 * here, so this looks OK.
12481 * Iterate over all predicates, starting with the outermost
12482 * predicate.
12483 * TODO: Problem: we cannot execute the inner predicates first
12484 * since we cannot go back *up* the operator tree!
12485 * Options we have:
12486 * 1) Use of recursive functions (like is it currently done
12487 * via xmlXPathCompOpEval())
12488 * 2) Add a predicate evaluation information stack to the
12489 * context struct
12490 * 3) Change the way the operators are linked; we need a
12491 * "parent" field on xmlXPathStepOp
12493 * For the moment, I'll try to solve this with a recursive
12494 * function: xmlXPathCompOpEvalPredicate().
12496 if (hasPredicateRange != 0)
12497 xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, maxPos, maxPos,
12498 hasNsNodes);
12499 else
12500 xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, 1, seq->nodeNr,
12501 hasNsNodes);
12503 if (ctxt->error != XPATH_EXPRESSION_OK) {
12504 total = 0;
12505 goto error;
12509 if (seq->nodeNr > 0) {
12511 * Add to result set.
12513 if (outSeq == NULL) {
12514 outSeq = seq;
12515 seq = NULL;
12516 } else {
12517 /* TODO: Check memory error. */
12518 outSeq = mergeAndClear(outSeq, seq);
12521 if (toBool)
12522 break;
12526 error:
12527 if ((obj->boolval) && (obj->user != NULL)) {
12529 * QUESTION TODO: What does this do and why?
12530 * TODO: Do we have to do this also for the "error"
12531 * cleanup further down?
12533 ctxt->value->boolval = 1;
12534 ctxt->value->user = obj->user;
12535 obj->user = NULL;
12536 obj->boolval = 0;
12538 xmlXPathReleaseObject(xpctxt, obj);
12541 * Ensure we return at least an empty set.
12543 if (outSeq == NULL) {
12544 if ((seq != NULL) && (seq->nodeNr == 0))
12545 outSeq = seq;
12546 else
12547 /* TODO: Check memory error. */
12548 outSeq = xmlXPathNodeSetCreate(NULL);
12550 if ((seq != NULL) && (seq != outSeq)) {
12551 xmlXPathFreeNodeSet(seq);
12554 * Hand over the result. Better to push the set also in
12555 * case of errors.
12557 valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
12559 * Reset the context node.
12561 xpctxt->node = oldContextNode;
12563 * When traversing the namespace axis in "toBool" mode, it's
12564 * possible that tmpNsList wasn't freed.
12566 if (xpctxt->tmpNsList != NULL) {
12567 xmlFree(xpctxt->tmpNsList);
12568 xpctxt->tmpNsList = NULL;
12571 #ifdef DEBUG_STEP
12572 xmlGenericError(xmlGenericErrorContext,
12573 "\nExamined %d nodes, found %d nodes at that step\n",
12574 total, nbMatches);
12575 #endif
12577 return(total);
12580 static int
12581 xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12582 xmlXPathStepOpPtr op, xmlNodePtr * first);
12585 * xmlXPathCompOpEvalFirst:
12586 * @ctxt: the XPath parser context with the compiled expression
12587 * @op: an XPath compiled operation
12588 * @first: the first elem found so far
12590 * Evaluate the Precompiled XPath operation searching only the first
12591 * element in document order
12593 * Returns the number of examined objects.
12595 static int
12596 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
12597 xmlXPathStepOpPtr op, xmlNodePtr * first)
12599 int total = 0, cur;
12600 xmlXPathCompExprPtr comp;
12601 xmlXPathObjectPtr arg1, arg2;
12603 CHECK_ERROR0;
12604 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12605 return(0);
12606 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12607 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12608 ctxt->context->depth += 1;
12609 comp = ctxt->comp;
12610 switch (op->op) {
12611 case XPATH_OP_END:
12612 break;
12613 case XPATH_OP_UNION:
12614 total =
12615 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12616 first);
12617 CHECK_ERROR0;
12618 if ((ctxt->value != NULL)
12619 && (ctxt->value->type == XPATH_NODESET)
12620 && (ctxt->value->nodesetval != NULL)
12621 && (ctxt->value->nodesetval->nodeNr >= 1)) {
12623 * limit tree traversing to first node in the result
12626 * OPTIMIZE TODO: This implicitly sorts
12627 * the result, even if not needed. E.g. if the argument
12628 * of the count() function, no sorting is needed.
12629 * OPTIMIZE TODO: How do we know if the node-list wasn't
12630 * already sorted?
12632 if (ctxt->value->nodesetval->nodeNr > 1)
12633 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12634 *first = ctxt->value->nodesetval->nodeTab[0];
12636 cur =
12637 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
12638 first);
12639 CHECK_ERROR0;
12641 arg2 = valuePop(ctxt);
12642 arg1 = valuePop(ctxt);
12643 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
12644 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
12645 xmlXPathReleaseObject(ctxt->context, arg1);
12646 xmlXPathReleaseObject(ctxt->context, arg2);
12647 XP_ERROR0(XPATH_INVALID_TYPE);
12649 if ((ctxt->context->opLimit != 0) &&
12650 (((arg1->nodesetval != NULL) &&
12651 (xmlXPathCheckOpLimit(ctxt,
12652 arg1->nodesetval->nodeNr) < 0)) ||
12653 ((arg2->nodesetval != NULL) &&
12654 (xmlXPathCheckOpLimit(ctxt,
12655 arg2->nodesetval->nodeNr) < 0)))) {
12656 xmlXPathReleaseObject(ctxt->context, arg1);
12657 xmlXPathReleaseObject(ctxt->context, arg2);
12658 break;
12661 /* TODO: Check memory error. */
12662 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12663 arg2->nodesetval);
12664 valuePush(ctxt, arg1);
12665 xmlXPathReleaseObject(ctxt->context, arg2);
12666 /* optimizer */
12667 if (total > cur)
12668 xmlXPathCompSwap(op);
12669 total += cur;
12670 break;
12671 case XPATH_OP_ROOT:
12672 xmlXPathRoot(ctxt);
12673 break;
12674 case XPATH_OP_NODE:
12675 if (op->ch1 != -1)
12676 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12677 CHECK_ERROR0;
12678 if (op->ch2 != -1)
12679 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12680 CHECK_ERROR0;
12681 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12682 ctxt->context->node));
12683 break;
12684 case XPATH_OP_COLLECT:{
12685 if (op->ch1 == -1)
12686 break;
12688 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12689 CHECK_ERROR0;
12691 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
12692 break;
12694 case XPATH_OP_VALUE:
12695 valuePush(ctxt,
12696 xmlXPathCacheObjectCopy(ctxt->context,
12697 (xmlXPathObjectPtr) op->value4));
12698 break;
12699 case XPATH_OP_SORT:
12700 if (op->ch1 != -1)
12701 total +=
12702 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12703 first);
12704 CHECK_ERROR0;
12705 if ((ctxt->value != NULL)
12706 && (ctxt->value->type == XPATH_NODESET)
12707 && (ctxt->value->nodesetval != NULL)
12708 && (ctxt->value->nodesetval->nodeNr > 1))
12709 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12710 break;
12711 #ifdef XP_OPTIMIZED_FILTER_FIRST
12712 case XPATH_OP_FILTER:
12713 total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
12714 break;
12715 #endif
12716 default:
12717 total += xmlXPathCompOpEval(ctxt, op);
12718 break;
12721 ctxt->context->depth -= 1;
12722 return(total);
12726 * xmlXPathCompOpEvalLast:
12727 * @ctxt: the XPath parser context with the compiled expression
12728 * @op: an XPath compiled operation
12729 * @last: the last elem found so far
12731 * Evaluate the Precompiled XPath operation searching only the last
12732 * element in document order
12734 * Returns the number of nodes traversed
12736 static int
12737 xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
12738 xmlNodePtr * last)
12740 int total = 0, cur;
12741 xmlXPathCompExprPtr comp;
12742 xmlXPathObjectPtr arg1, arg2;
12744 CHECK_ERROR0;
12745 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12746 return(0);
12747 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12748 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12749 ctxt->context->depth += 1;
12750 comp = ctxt->comp;
12751 switch (op->op) {
12752 case XPATH_OP_END:
12753 break;
12754 case XPATH_OP_UNION:
12755 total =
12756 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
12757 CHECK_ERROR0;
12758 if ((ctxt->value != NULL)
12759 && (ctxt->value->type == XPATH_NODESET)
12760 && (ctxt->value->nodesetval != NULL)
12761 && (ctxt->value->nodesetval->nodeNr >= 1)) {
12763 * limit tree traversing to first node in the result
12765 if (ctxt->value->nodesetval->nodeNr > 1)
12766 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12767 *last =
12768 ctxt->value->nodesetval->nodeTab[ctxt->value->
12769 nodesetval->nodeNr -
12772 cur =
12773 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
12774 CHECK_ERROR0;
12775 if ((ctxt->value != NULL)
12776 && (ctxt->value->type == XPATH_NODESET)
12777 && (ctxt->value->nodesetval != NULL)
12778 && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
12781 arg2 = valuePop(ctxt);
12782 arg1 = valuePop(ctxt);
12783 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
12784 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
12785 xmlXPathReleaseObject(ctxt->context, arg1);
12786 xmlXPathReleaseObject(ctxt->context, arg2);
12787 XP_ERROR0(XPATH_INVALID_TYPE);
12789 if ((ctxt->context->opLimit != 0) &&
12790 (((arg1->nodesetval != NULL) &&
12791 (xmlXPathCheckOpLimit(ctxt,
12792 arg1->nodesetval->nodeNr) < 0)) ||
12793 ((arg2->nodesetval != NULL) &&
12794 (xmlXPathCheckOpLimit(ctxt,
12795 arg2->nodesetval->nodeNr) < 0)))) {
12796 xmlXPathReleaseObject(ctxt->context, arg1);
12797 xmlXPathReleaseObject(ctxt->context, arg2);
12798 break;
12801 /* TODO: Check memory error. */
12802 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12803 arg2->nodesetval);
12804 valuePush(ctxt, arg1);
12805 xmlXPathReleaseObject(ctxt->context, arg2);
12806 /* optimizer */
12807 if (total > cur)
12808 xmlXPathCompSwap(op);
12809 total += cur;
12810 break;
12811 case XPATH_OP_ROOT:
12812 xmlXPathRoot(ctxt);
12813 break;
12814 case XPATH_OP_NODE:
12815 if (op->ch1 != -1)
12816 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12817 CHECK_ERROR0;
12818 if (op->ch2 != -1)
12819 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12820 CHECK_ERROR0;
12821 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12822 ctxt->context->node));
12823 break;
12824 case XPATH_OP_COLLECT:{
12825 if (op->ch1 == -1)
12826 break;
12828 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12829 CHECK_ERROR0;
12831 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
12832 break;
12834 case XPATH_OP_VALUE:
12835 valuePush(ctxt,
12836 xmlXPathCacheObjectCopy(ctxt->context,
12837 (xmlXPathObjectPtr) op->value4));
12838 break;
12839 case XPATH_OP_SORT:
12840 if (op->ch1 != -1)
12841 total +=
12842 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
12843 last);
12844 CHECK_ERROR0;
12845 if ((ctxt->value != NULL)
12846 && (ctxt->value->type == XPATH_NODESET)
12847 && (ctxt->value->nodesetval != NULL)
12848 && (ctxt->value->nodesetval->nodeNr > 1))
12849 xmlXPathNodeSetSort(ctxt->value->nodesetval);
12850 break;
12851 default:
12852 total += xmlXPathCompOpEval(ctxt, op);
12853 break;
12856 ctxt->context->depth -= 1;
12857 return (total);
12860 #ifdef XP_OPTIMIZED_FILTER_FIRST
12861 static int
12862 xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12863 xmlXPathStepOpPtr op, xmlNodePtr * first)
12865 int total = 0;
12866 xmlXPathCompExprPtr comp;
12867 xmlNodeSetPtr set;
12869 CHECK_ERROR0;
12870 comp = ctxt->comp;
12872 * Optimization for ()[last()] selection i.e. the last elem
12874 if ((op->ch1 != -1) && (op->ch2 != -1) &&
12875 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
12876 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
12877 int f = comp->steps[op->ch2].ch1;
12879 if ((f != -1) &&
12880 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
12881 (comp->steps[f].value5 == NULL) &&
12882 (comp->steps[f].value == 0) &&
12883 (comp->steps[f].value4 != NULL) &&
12884 (xmlStrEqual
12885 (comp->steps[f].value4, BAD_CAST "last"))) {
12886 xmlNodePtr last = NULL;
12888 total +=
12889 xmlXPathCompOpEvalLast(ctxt,
12890 &comp->steps[op->ch1],
12891 &last);
12892 CHECK_ERROR0;
12894 * The nodeset should be in document order,
12895 * Keep only the last value
12897 if ((ctxt->value != NULL) &&
12898 (ctxt->value->type == XPATH_NODESET) &&
12899 (ctxt->value->nodesetval != NULL) &&
12900 (ctxt->value->nodesetval->nodeTab != NULL) &&
12901 (ctxt->value->nodesetval->nodeNr > 1)) {
12902 xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
12903 *first = *(ctxt->value->nodesetval->nodeTab);
12905 return (total);
12909 if (op->ch1 != -1)
12910 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12911 CHECK_ERROR0;
12912 if (op->ch2 == -1)
12913 return (total);
12914 if (ctxt->value == NULL)
12915 return (total);
12917 #ifdef LIBXML_XPTR_ENABLED
12919 * Hum are we filtering the result of an XPointer expression
12921 if (ctxt->value->type == XPATH_LOCATIONSET) {
12922 xmlLocationSetPtr locset = ctxt->value->user;
12924 if (locset != NULL) {
12925 xmlXPathLocationSetFilter(ctxt, locset, op->ch2, 1, 1);
12926 if (locset->locNr > 0)
12927 *first = (xmlNodePtr) locset->locTab[0]->user;
12930 return (total);
12932 #endif /* LIBXML_XPTR_ENABLED */
12934 CHECK_TYPE0(XPATH_NODESET);
12935 set = ctxt->value->nodesetval;
12936 if (set != NULL) {
12937 xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, 1, 1);
12938 if (set->nodeNr > 0)
12939 *first = set->nodeTab[0];
12942 return (total);
12944 #endif /* XP_OPTIMIZED_FILTER_FIRST */
12947 * xmlXPathCompOpEval:
12948 * @ctxt: the XPath parser context with the compiled expression
12949 * @op: an XPath compiled operation
12951 * Evaluate the Precompiled XPath operation
12952 * Returns the number of nodes traversed
12954 static int
12955 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
12957 int total = 0;
12958 int equal, ret;
12959 xmlXPathCompExprPtr comp;
12960 xmlXPathObjectPtr arg1, arg2;
12962 CHECK_ERROR0;
12963 if (OP_LIMIT_EXCEEDED(ctxt, 1))
12964 return(0);
12965 if (ctxt->context->depth >= XPATH_MAX_RECURSION_DEPTH)
12966 XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
12967 ctxt->context->depth += 1;
12968 comp = ctxt->comp;
12969 switch (op->op) {
12970 case XPATH_OP_END:
12971 break;
12972 case XPATH_OP_AND:
12973 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12974 CHECK_ERROR0;
12975 xmlXPathBooleanFunction(ctxt, 1);
12976 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
12977 break;
12978 arg2 = valuePop(ctxt);
12979 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12980 if (ctxt->error) {
12981 xmlXPathFreeObject(arg2);
12982 break;
12984 xmlXPathBooleanFunction(ctxt, 1);
12985 if (ctxt->value != NULL)
12986 ctxt->value->boolval &= arg2->boolval;
12987 xmlXPathReleaseObject(ctxt->context, arg2);
12988 break;
12989 case XPATH_OP_OR:
12990 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12991 CHECK_ERROR0;
12992 xmlXPathBooleanFunction(ctxt, 1);
12993 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
12994 break;
12995 arg2 = valuePop(ctxt);
12996 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12997 if (ctxt->error) {
12998 xmlXPathFreeObject(arg2);
12999 break;
13001 xmlXPathBooleanFunction(ctxt, 1);
13002 if (ctxt->value != NULL)
13003 ctxt->value->boolval |= arg2->boolval;
13004 xmlXPathReleaseObject(ctxt->context, arg2);
13005 break;
13006 case XPATH_OP_EQUAL:
13007 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13008 CHECK_ERROR0;
13009 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13010 CHECK_ERROR0;
13011 if (op->value)
13012 equal = xmlXPathEqualValues(ctxt);
13013 else
13014 equal = xmlXPathNotEqualValues(ctxt);
13015 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
13016 break;
13017 case XPATH_OP_CMP:
13018 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13019 CHECK_ERROR0;
13020 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13021 CHECK_ERROR0;
13022 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
13023 valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
13024 break;
13025 case XPATH_OP_PLUS:
13026 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13027 CHECK_ERROR0;
13028 if (op->ch2 != -1) {
13029 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13031 CHECK_ERROR0;
13032 if (op->value == 0)
13033 xmlXPathSubValues(ctxt);
13034 else if (op->value == 1)
13035 xmlXPathAddValues(ctxt);
13036 else if (op->value == 2)
13037 xmlXPathValueFlipSign(ctxt);
13038 else if (op->value == 3) {
13039 CAST_TO_NUMBER;
13040 CHECK_TYPE0(XPATH_NUMBER);
13042 break;
13043 case XPATH_OP_MULT:
13044 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13045 CHECK_ERROR0;
13046 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13047 CHECK_ERROR0;
13048 if (op->value == 0)
13049 xmlXPathMultValues(ctxt);
13050 else if (op->value == 1)
13051 xmlXPathDivValues(ctxt);
13052 else if (op->value == 2)
13053 xmlXPathModValues(ctxt);
13054 break;
13055 case XPATH_OP_UNION:
13056 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13057 CHECK_ERROR0;
13058 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13059 CHECK_ERROR0;
13061 arg2 = valuePop(ctxt);
13062 arg1 = valuePop(ctxt);
13063 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET) ||
13064 (arg2 == NULL) || (arg2->type != XPATH_NODESET)) {
13065 xmlXPathReleaseObject(ctxt->context, arg1);
13066 xmlXPathReleaseObject(ctxt->context, arg2);
13067 XP_ERROR0(XPATH_INVALID_TYPE);
13069 if ((ctxt->context->opLimit != 0) &&
13070 (((arg1->nodesetval != NULL) &&
13071 (xmlXPathCheckOpLimit(ctxt,
13072 arg1->nodesetval->nodeNr) < 0)) ||
13073 ((arg2->nodesetval != NULL) &&
13074 (xmlXPathCheckOpLimit(ctxt,
13075 arg2->nodesetval->nodeNr) < 0)))) {
13076 xmlXPathReleaseObject(ctxt->context, arg1);
13077 xmlXPathReleaseObject(ctxt->context, arg2);
13078 break;
13081 if ((arg1->nodesetval == NULL) ||
13082 ((arg2->nodesetval != NULL) &&
13083 (arg2->nodesetval->nodeNr != 0)))
13085 /* TODO: Check memory error. */
13086 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
13087 arg2->nodesetval);
13090 valuePush(ctxt, arg1);
13091 xmlXPathReleaseObject(ctxt->context, arg2);
13092 break;
13093 case XPATH_OP_ROOT:
13094 xmlXPathRoot(ctxt);
13095 break;
13096 case XPATH_OP_NODE:
13097 if (op->ch1 != -1)
13098 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13099 CHECK_ERROR0;
13100 if (op->ch2 != -1)
13101 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13102 CHECK_ERROR0;
13103 valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
13104 ctxt->context->node));
13105 break;
13106 case XPATH_OP_COLLECT:{
13107 if (op->ch1 == -1)
13108 break;
13110 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13111 CHECK_ERROR0;
13113 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
13114 break;
13116 case XPATH_OP_VALUE:
13117 valuePush(ctxt,
13118 xmlXPathCacheObjectCopy(ctxt->context,
13119 (xmlXPathObjectPtr) op->value4));
13120 break;
13121 case XPATH_OP_VARIABLE:{
13122 xmlXPathObjectPtr val;
13124 if (op->ch1 != -1)
13125 total +=
13126 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13127 if (op->value5 == NULL) {
13128 val = xmlXPathVariableLookup(ctxt->context, op->value4);
13129 if (val == NULL)
13130 XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
13131 valuePush(ctxt, val);
13132 } else {
13133 const xmlChar *URI;
13135 URI = xmlXPathNsLookup(ctxt->context, op->value5);
13136 if (URI == NULL) {
13137 xmlGenericError(xmlGenericErrorContext,
13138 "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
13139 (char *) op->value4, (char *)op->value5);
13140 ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
13141 break;
13143 val = xmlXPathVariableLookupNS(ctxt->context,
13144 op->value4, URI);
13145 if (val == NULL)
13146 XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
13147 valuePush(ctxt, val);
13149 break;
13151 case XPATH_OP_FUNCTION:{
13152 xmlXPathFunction func;
13153 const xmlChar *oldFunc, *oldFuncURI;
13154 int i;
13155 int frame;
13157 frame = xmlXPathSetFrame(ctxt);
13158 if (op->ch1 != -1) {
13159 total +=
13160 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13161 if (ctxt->error != XPATH_EXPRESSION_OK) {
13162 xmlXPathPopFrame(ctxt, frame);
13163 break;
13166 if (ctxt->valueNr < ctxt->valueFrame + op->value) {
13167 xmlGenericError(xmlGenericErrorContext,
13168 "xmlXPathCompOpEval: parameter error\n");
13169 ctxt->error = XPATH_INVALID_OPERAND;
13170 xmlXPathPopFrame(ctxt, frame);
13171 break;
13173 for (i = 0; i < op->value; i++) {
13174 if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
13175 xmlGenericError(xmlGenericErrorContext,
13176 "xmlXPathCompOpEval: parameter error\n");
13177 ctxt->error = XPATH_INVALID_OPERAND;
13178 xmlXPathPopFrame(ctxt, frame);
13179 break;
13182 if (op->cache != NULL)
13183 func = op->cache;
13184 else {
13185 const xmlChar *URI = NULL;
13187 if (op->value5 == NULL)
13188 func =
13189 xmlXPathFunctionLookup(ctxt->context,
13190 op->value4);
13191 else {
13192 URI = xmlXPathNsLookup(ctxt->context, op->value5);
13193 if (URI == NULL) {
13194 xmlGenericError(xmlGenericErrorContext,
13195 "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
13196 (char *)op->value4, (char *)op->value5);
13197 xmlXPathPopFrame(ctxt, frame);
13198 ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
13199 break;
13201 func = xmlXPathFunctionLookupNS(ctxt->context,
13202 op->value4, URI);
13204 if (func == NULL) {
13205 xmlGenericError(xmlGenericErrorContext,
13206 "xmlXPathCompOpEval: function %s not found\n",
13207 (char *)op->value4);
13208 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
13210 op->cache = func;
13211 op->cacheURI = (void *) URI;
13213 oldFunc = ctxt->context->function;
13214 oldFuncURI = ctxt->context->functionURI;
13215 ctxt->context->function = op->value4;
13216 ctxt->context->functionURI = op->cacheURI;
13217 func(ctxt, op->value);
13218 ctxt->context->function = oldFunc;
13219 ctxt->context->functionURI = oldFuncURI;
13220 if ((ctxt->error == XPATH_EXPRESSION_OK) &&
13221 (ctxt->valueNr != ctxt->valueFrame + 1))
13222 XP_ERROR0(XPATH_STACK_ERROR);
13223 xmlXPathPopFrame(ctxt, frame);
13224 break;
13226 case XPATH_OP_ARG:
13227 if (op->ch1 != -1) {
13228 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13229 CHECK_ERROR0;
13231 if (op->ch2 != -1) {
13232 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13233 CHECK_ERROR0;
13235 break;
13236 case XPATH_OP_PREDICATE:
13237 case XPATH_OP_FILTER:{
13238 xmlNodeSetPtr set;
13241 * Optimization for ()[1] selection i.e. the first elem
13243 if ((op->ch1 != -1) && (op->ch2 != -1) &&
13244 #ifdef XP_OPTIMIZED_FILTER_FIRST
13246 * FILTER TODO: Can we assume that the inner processing
13247 * will result in an ordered list if we have an
13248 * XPATH_OP_FILTER?
13249 * What about an additional field or flag on
13250 * xmlXPathObject like @sorted ? This way we wouldn't need
13251 * to assume anything, so it would be more robust and
13252 * easier to optimize.
13254 ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
13255 (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
13256 #else
13257 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13258 #endif
13259 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
13260 xmlXPathObjectPtr val;
13262 val = comp->steps[op->ch2].value4;
13263 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
13264 (val->floatval == 1.0)) {
13265 xmlNodePtr first = NULL;
13267 total +=
13268 xmlXPathCompOpEvalFirst(ctxt,
13269 &comp->steps[op->ch1],
13270 &first);
13271 CHECK_ERROR0;
13273 * The nodeset should be in document order,
13274 * Keep only the first value
13276 if ((ctxt->value != NULL) &&
13277 (ctxt->value->type == XPATH_NODESET) &&
13278 (ctxt->value->nodesetval != NULL) &&
13279 (ctxt->value->nodesetval->nodeNr > 1))
13280 xmlXPathNodeSetClearFromPos(ctxt->value->nodesetval,
13281 1, 1);
13282 break;
13286 * Optimization for ()[last()] selection i.e. the last elem
13288 if ((op->ch1 != -1) && (op->ch2 != -1) &&
13289 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13290 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
13291 int f = comp->steps[op->ch2].ch1;
13293 if ((f != -1) &&
13294 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
13295 (comp->steps[f].value5 == NULL) &&
13296 (comp->steps[f].value == 0) &&
13297 (comp->steps[f].value4 != NULL) &&
13298 (xmlStrEqual
13299 (comp->steps[f].value4, BAD_CAST "last"))) {
13300 xmlNodePtr last = NULL;
13302 total +=
13303 xmlXPathCompOpEvalLast(ctxt,
13304 &comp->steps[op->ch1],
13305 &last);
13306 CHECK_ERROR0;
13308 * The nodeset should be in document order,
13309 * Keep only the last value
13311 if ((ctxt->value != NULL) &&
13312 (ctxt->value->type == XPATH_NODESET) &&
13313 (ctxt->value->nodesetval != NULL) &&
13314 (ctxt->value->nodesetval->nodeTab != NULL) &&
13315 (ctxt->value->nodesetval->nodeNr > 1))
13316 xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
13317 break;
13321 * Process inner predicates first.
13322 * Example "index[parent::book][1]":
13323 * ...
13324 * PREDICATE <-- we are here "[1]"
13325 * PREDICATE <-- process "[parent::book]" first
13326 * SORT
13327 * COLLECT 'parent' 'name' 'node' book
13328 * NODE
13329 * ELEM Object is a number : 1
13331 if (op->ch1 != -1)
13332 total +=
13333 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13334 CHECK_ERROR0;
13335 if (op->ch2 == -1)
13336 break;
13337 if (ctxt->value == NULL)
13338 break;
13340 #ifdef LIBXML_XPTR_ENABLED
13342 * Hum are we filtering the result of an XPointer expression
13344 if (ctxt->value->type == XPATH_LOCATIONSET) {
13345 xmlLocationSetPtr locset = ctxt->value->user;
13346 xmlXPathLocationSetFilter(ctxt, locset, op->ch2,
13347 1, locset->locNr);
13348 break;
13350 #endif /* LIBXML_XPTR_ENABLED */
13352 CHECK_TYPE0(XPATH_NODESET);
13353 set = ctxt->value->nodesetval;
13354 if (set != NULL)
13355 xmlXPathNodeSetFilter(ctxt, set, op->ch2,
13356 1, set->nodeNr, 1);
13357 break;
13359 case XPATH_OP_SORT:
13360 if (op->ch1 != -1)
13361 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13362 CHECK_ERROR0;
13363 if ((ctxt->value != NULL) &&
13364 (ctxt->value->type == XPATH_NODESET) &&
13365 (ctxt->value->nodesetval != NULL) &&
13366 (ctxt->value->nodesetval->nodeNr > 1))
13368 xmlXPathNodeSetSort(ctxt->value->nodesetval);
13370 break;
13371 #ifdef LIBXML_XPTR_ENABLED
13372 case XPATH_OP_RANGETO:{
13373 xmlXPathObjectPtr range;
13374 xmlXPathObjectPtr res, obj;
13375 xmlXPathObjectPtr tmp;
13376 xmlLocationSetPtr newlocset = NULL;
13377 xmlLocationSetPtr oldlocset;
13378 xmlNodeSetPtr oldset;
13379 xmlNodePtr oldnode = ctxt->context->node;
13380 int oldcs = ctxt->context->contextSize;
13381 int oldpp = ctxt->context->proximityPosition;
13382 int i, j;
13384 if (op->ch1 != -1) {
13385 total +=
13386 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13387 CHECK_ERROR0;
13389 if (ctxt->value == NULL) {
13390 XP_ERROR0(XPATH_INVALID_OPERAND);
13392 if (op->ch2 == -1)
13393 break;
13395 if (ctxt->value->type == XPATH_LOCATIONSET) {
13397 * Extract the old locset, and then evaluate the result of the
13398 * expression for all the element in the locset. use it to grow
13399 * up a new locset.
13401 CHECK_TYPE0(XPATH_LOCATIONSET);
13403 if ((ctxt->value->user == NULL) ||
13404 (((xmlLocationSetPtr) ctxt->value->user)->locNr == 0))
13405 break;
13407 obj = valuePop(ctxt);
13408 oldlocset = obj->user;
13410 newlocset = xmlXPtrLocationSetCreate(NULL);
13412 for (i = 0; i < oldlocset->locNr; i++) {
13414 * Run the evaluation with a node list made of a
13415 * single item in the nodelocset.
13417 ctxt->context->node = oldlocset->locTab[i]->user;
13418 ctxt->context->contextSize = oldlocset->locNr;
13419 ctxt->context->proximityPosition = i + 1;
13420 tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13421 ctxt->context->node);
13422 valuePush(ctxt, tmp);
13424 if (op->ch2 != -1)
13425 total +=
13426 xmlXPathCompOpEval(ctxt,
13427 &comp->steps[op->ch2]);
13428 if (ctxt->error != XPATH_EXPRESSION_OK) {
13429 xmlXPtrFreeLocationSet(newlocset);
13430 goto rangeto_error;
13433 res = valuePop(ctxt);
13434 if (res->type == XPATH_LOCATIONSET) {
13435 xmlLocationSetPtr rloc =
13436 (xmlLocationSetPtr)res->user;
13437 for (j=0; j<rloc->locNr; j++) {
13438 range = xmlXPtrNewRange(
13439 oldlocset->locTab[i]->user,
13440 oldlocset->locTab[i]->index,
13441 rloc->locTab[j]->user2,
13442 rloc->locTab[j]->index2);
13443 if (range != NULL) {
13444 xmlXPtrLocationSetAdd(newlocset, range);
13447 } else {
13448 range = xmlXPtrNewRangeNodeObject(
13449 (xmlNodePtr)oldlocset->locTab[i]->user, res);
13450 if (range != NULL) {
13451 xmlXPtrLocationSetAdd(newlocset,range);
13456 * Cleanup
13458 if (res != NULL) {
13459 xmlXPathReleaseObject(ctxt->context, res);
13461 if (ctxt->value == tmp) {
13462 res = valuePop(ctxt);
13463 xmlXPathReleaseObject(ctxt->context, res);
13466 } else { /* Not a location set */
13467 CHECK_TYPE0(XPATH_NODESET);
13468 obj = valuePop(ctxt);
13469 oldset = obj->nodesetval;
13471 newlocset = xmlXPtrLocationSetCreate(NULL);
13473 if (oldset != NULL) {
13474 for (i = 0; i < oldset->nodeNr; i++) {
13476 * Run the evaluation with a node list made of a single item
13477 * in the nodeset.
13479 ctxt->context->node = oldset->nodeTab[i];
13481 * OPTIMIZE TODO: Avoid recreation for every iteration.
13483 tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13484 ctxt->context->node);
13485 valuePush(ctxt, tmp);
13487 if (op->ch2 != -1)
13488 total +=
13489 xmlXPathCompOpEval(ctxt,
13490 &comp->steps[op->ch2]);
13491 if (ctxt->error != XPATH_EXPRESSION_OK) {
13492 xmlXPtrFreeLocationSet(newlocset);
13493 goto rangeto_error;
13496 res = valuePop(ctxt);
13497 range =
13498 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
13499 res);
13500 if (range != NULL) {
13501 xmlXPtrLocationSetAdd(newlocset, range);
13505 * Cleanup
13507 if (res != NULL) {
13508 xmlXPathReleaseObject(ctxt->context, res);
13510 if (ctxt->value == tmp) {
13511 res = valuePop(ctxt);
13512 xmlXPathReleaseObject(ctxt->context, res);
13519 * The result is used as the new evaluation set.
13521 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13522 rangeto_error:
13523 xmlXPathReleaseObject(ctxt->context, obj);
13524 ctxt->context->node = oldnode;
13525 ctxt->context->contextSize = oldcs;
13526 ctxt->context->proximityPosition = oldpp;
13527 break;
13529 #endif /* LIBXML_XPTR_ENABLED */
13530 default:
13531 xmlGenericError(xmlGenericErrorContext,
13532 "XPath: unknown precompiled operation %d\n", op->op);
13533 ctxt->error = XPATH_INVALID_OPERAND;
13534 break;
13537 ctxt->context->depth -= 1;
13538 return (total);
13542 * xmlXPathCompOpEvalToBoolean:
13543 * @ctxt: the XPath parser context
13545 * Evaluates if the expression evaluates to true.
13547 * Returns 1 if true, 0 if false and -1 on API or internal errors.
13549 static int
13550 xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
13551 xmlXPathStepOpPtr op,
13552 int isPredicate)
13554 xmlXPathObjectPtr resObj = NULL;
13556 start:
13557 if (OP_LIMIT_EXCEEDED(ctxt, 1))
13558 return(0);
13559 /* comp = ctxt->comp; */
13560 switch (op->op) {
13561 case XPATH_OP_END:
13562 return (0);
13563 case XPATH_OP_VALUE:
13564 resObj = (xmlXPathObjectPtr) op->value4;
13565 if (isPredicate)
13566 return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
13567 return(xmlXPathCastToBoolean(resObj));
13568 case XPATH_OP_SORT:
13570 * We don't need sorting for boolean results. Skip this one.
13572 if (op->ch1 != -1) {
13573 op = &ctxt->comp->steps[op->ch1];
13574 goto start;
13576 return(0);
13577 case XPATH_OP_COLLECT:
13578 if (op->ch1 == -1)
13579 return(0);
13581 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
13582 if (ctxt->error != XPATH_EXPRESSION_OK)
13583 return(-1);
13585 xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
13586 if (ctxt->error != XPATH_EXPRESSION_OK)
13587 return(-1);
13589 resObj = valuePop(ctxt);
13590 if (resObj == NULL)
13591 return(-1);
13592 break;
13593 default:
13595 * Fallback to call xmlXPathCompOpEval().
13597 xmlXPathCompOpEval(ctxt, op);
13598 if (ctxt->error != XPATH_EXPRESSION_OK)
13599 return(-1);
13601 resObj = valuePop(ctxt);
13602 if (resObj == NULL)
13603 return(-1);
13604 break;
13607 if (resObj) {
13608 int res;
13610 if (resObj->type == XPATH_BOOLEAN) {
13611 res = resObj->boolval;
13612 } else if (isPredicate) {
13614 * For predicates a result of type "number" is handled
13615 * differently:
13616 * SPEC XPath 1.0:
13617 * "If the result is a number, the result will be converted
13618 * to true if the number is equal to the context position
13619 * and will be converted to false otherwise;"
13621 res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
13622 } else {
13623 res = xmlXPathCastToBoolean(resObj);
13625 xmlXPathReleaseObject(ctxt->context, resObj);
13626 return(res);
13629 return(0);
13632 #ifdef XPATH_STREAMING
13634 * xmlXPathRunStreamEval:
13635 * @ctxt: the XPath parser context with the compiled expression
13637 * Evaluate the Precompiled Streamable XPath expression in the given context.
13639 static int
13640 xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
13641 xmlXPathObjectPtr *resultSeq, int toBool)
13643 int max_depth, min_depth;
13644 int from_root;
13645 int ret, depth;
13646 int eval_all_nodes;
13647 xmlNodePtr cur = NULL, limit = NULL;
13648 xmlStreamCtxtPtr patstream = NULL;
13650 int nb_nodes = 0;
13652 if ((ctxt == NULL) || (comp == NULL))
13653 return(-1);
13654 max_depth = xmlPatternMaxDepth(comp);
13655 if (max_depth == -1)
13656 return(-1);
13657 if (max_depth == -2)
13658 max_depth = 10000;
13659 min_depth = xmlPatternMinDepth(comp);
13660 if (min_depth == -1)
13661 return(-1);
13662 from_root = xmlPatternFromRoot(comp);
13663 if (from_root < 0)
13664 return(-1);
13665 #if 0
13666 printf("stream eval: depth %d from root %d\n", max_depth, from_root);
13667 #endif
13669 if (! toBool) {
13670 if (resultSeq == NULL)
13671 return(-1);
13672 *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
13673 if (*resultSeq == NULL)
13674 return(-1);
13678 * handle the special cases of "/" amd "." being matched
13680 if (min_depth == 0) {
13681 if (from_root) {
13682 /* Select "/" */
13683 if (toBool)
13684 return(1);
13685 /* TODO: Check memory error. */
13686 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
13687 (xmlNodePtr) ctxt->doc);
13688 } else {
13689 /* Select "self::node()" */
13690 if (toBool)
13691 return(1);
13692 /* TODO: Check memory error. */
13693 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
13696 if (max_depth == 0) {
13697 return(0);
13700 if (from_root) {
13701 cur = (xmlNodePtr)ctxt->doc;
13702 } else if (ctxt->node != NULL) {
13703 switch (ctxt->node->type) {
13704 case XML_ELEMENT_NODE:
13705 case XML_DOCUMENT_NODE:
13706 case XML_DOCUMENT_FRAG_NODE:
13707 case XML_HTML_DOCUMENT_NODE:
13708 #ifdef LIBXML_DOCB_ENABLED
13709 case XML_DOCB_DOCUMENT_NODE:
13710 #endif
13711 cur = ctxt->node;
13712 break;
13713 case XML_ATTRIBUTE_NODE:
13714 case XML_TEXT_NODE:
13715 case XML_CDATA_SECTION_NODE:
13716 case XML_ENTITY_REF_NODE:
13717 case XML_ENTITY_NODE:
13718 case XML_PI_NODE:
13719 case XML_COMMENT_NODE:
13720 case XML_NOTATION_NODE:
13721 case XML_DTD_NODE:
13722 case XML_DOCUMENT_TYPE_NODE:
13723 case XML_ELEMENT_DECL:
13724 case XML_ATTRIBUTE_DECL:
13725 case XML_ENTITY_DECL:
13726 case XML_NAMESPACE_DECL:
13727 case XML_XINCLUDE_START:
13728 case XML_XINCLUDE_END:
13729 break;
13731 limit = cur;
13733 if (cur == NULL) {
13734 return(0);
13737 patstream = xmlPatternGetStreamCtxt(comp);
13738 if (patstream == NULL) {
13740 * QUESTION TODO: Is this an error?
13742 return(0);
13745 eval_all_nodes = xmlStreamWantsAnyNode(patstream);
13747 if (from_root) {
13748 ret = xmlStreamPush(patstream, NULL, NULL);
13749 if (ret < 0) {
13750 } else if (ret == 1) {
13751 if (toBool)
13752 goto return_1;
13753 /* TODO: Check memory error. */
13754 xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
13757 depth = 0;
13758 goto scan_children;
13759 next_node:
13760 do {
13761 if (ctxt->opLimit != 0) {
13762 if (ctxt->opCount >= ctxt->opLimit) {
13763 xmlGenericError(xmlGenericErrorContext,
13764 "XPath operation limit exceeded\n");
13765 xmlFreeStreamCtxt(patstream);
13766 return(-1);
13768 ctxt->opCount++;
13771 nb_nodes++;
13773 switch (cur->type) {
13774 case XML_ELEMENT_NODE:
13775 case XML_TEXT_NODE:
13776 case XML_CDATA_SECTION_NODE:
13777 case XML_COMMENT_NODE:
13778 case XML_PI_NODE:
13779 if (cur->type == XML_ELEMENT_NODE) {
13780 ret = xmlStreamPush(patstream, cur->name,
13781 (cur->ns ? cur->ns->href : NULL));
13782 } else if (eval_all_nodes)
13783 ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
13784 else
13785 break;
13787 if (ret < 0) {
13788 /* NOP. */
13789 } else if (ret == 1) {
13790 if (toBool)
13791 goto return_1;
13792 if (xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur)
13793 < 0) {
13794 ctxt->lastError.domain = XML_FROM_XPATH;
13795 ctxt->lastError.code = XML_ERR_NO_MEMORY;
13798 if ((cur->children == NULL) || (depth >= max_depth)) {
13799 ret = xmlStreamPop(patstream);
13800 while (cur->next != NULL) {
13801 cur = cur->next;
13802 if ((cur->type != XML_ENTITY_DECL) &&
13803 (cur->type != XML_DTD_NODE))
13804 goto next_node;
13807 default:
13808 break;
13811 scan_children:
13812 if (cur->type == XML_NAMESPACE_DECL) break;
13813 if ((cur->children != NULL) && (depth < max_depth)) {
13815 * Do not descend on entities declarations
13817 if (cur->children->type != XML_ENTITY_DECL) {
13818 cur = cur->children;
13819 depth++;
13821 * Skip DTDs
13823 if (cur->type != XML_DTD_NODE)
13824 continue;
13828 if (cur == limit)
13829 break;
13831 while (cur->next != NULL) {
13832 cur = cur->next;
13833 if ((cur->type != XML_ENTITY_DECL) &&
13834 (cur->type != XML_DTD_NODE))
13835 goto next_node;
13838 do {
13839 cur = cur->parent;
13840 depth--;
13841 if ((cur == NULL) || (cur == limit) ||
13842 (cur->type == XML_DOCUMENT_NODE))
13843 goto done;
13844 if (cur->type == XML_ELEMENT_NODE) {
13845 ret = xmlStreamPop(patstream);
13846 } else if ((eval_all_nodes) &&
13847 ((cur->type == XML_TEXT_NODE) ||
13848 (cur->type == XML_CDATA_SECTION_NODE) ||
13849 (cur->type == XML_COMMENT_NODE) ||
13850 (cur->type == XML_PI_NODE)))
13852 ret = xmlStreamPop(patstream);
13854 if (cur->next != NULL) {
13855 cur = cur->next;
13856 break;
13858 } while (cur != NULL);
13860 } while ((cur != NULL) && (depth >= 0));
13862 done:
13864 #if 0
13865 printf("stream eval: checked %d nodes selected %d\n",
13866 nb_nodes, retObj->nodesetval->nodeNr);
13867 #endif
13869 if (patstream)
13870 xmlFreeStreamCtxt(patstream);
13871 return(0);
13873 return_1:
13874 if (patstream)
13875 xmlFreeStreamCtxt(patstream);
13876 return(1);
13878 #endif /* XPATH_STREAMING */
13881 * xmlXPathRunEval:
13882 * @ctxt: the XPath parser context with the compiled expression
13883 * @toBool: evaluate to a boolean result
13885 * Evaluate the Precompiled XPath expression in the given context.
13887 static int
13888 xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
13890 xmlXPathCompExprPtr comp;
13892 if ((ctxt == NULL) || (ctxt->comp == NULL))
13893 return(-1);
13895 ctxt->context->depth = 0;
13897 if (ctxt->valueTab == NULL) {
13898 /* Allocate the value stack */
13899 ctxt->valueTab = (xmlXPathObjectPtr *)
13900 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
13901 if (ctxt->valueTab == NULL) {
13902 xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
13903 xmlFree(ctxt);
13905 ctxt->valueNr = 0;
13906 ctxt->valueMax = 10;
13907 ctxt->value = NULL;
13908 ctxt->valueFrame = 0;
13910 #ifdef XPATH_STREAMING
13911 if (ctxt->comp->stream) {
13912 int res;
13914 if (toBool) {
13916 * Evaluation to boolean result.
13918 res = xmlXPathRunStreamEval(ctxt->context,
13919 ctxt->comp->stream, NULL, 1);
13920 if (res != -1)
13921 return(res);
13922 } else {
13923 xmlXPathObjectPtr resObj = NULL;
13926 * Evaluation to a sequence.
13928 res = xmlXPathRunStreamEval(ctxt->context,
13929 ctxt->comp->stream, &resObj, 0);
13931 if ((res != -1) && (resObj != NULL)) {
13932 valuePush(ctxt, resObj);
13933 return(0);
13935 if (resObj != NULL)
13936 xmlXPathReleaseObject(ctxt->context, resObj);
13939 * QUESTION TODO: This falls back to normal XPath evaluation
13940 * if res == -1. Is this intended?
13943 #endif
13944 comp = ctxt->comp;
13945 if (comp->last < 0) {
13946 xmlGenericError(xmlGenericErrorContext,
13947 "xmlXPathRunEval: last is less than zero\n");
13948 return(-1);
13950 if (toBool)
13951 return(xmlXPathCompOpEvalToBoolean(ctxt,
13952 &comp->steps[comp->last], 0));
13953 else
13954 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
13956 return(0);
13959 /************************************************************************
13961 * Public interfaces *
13963 ************************************************************************/
13966 * xmlXPathEvalPredicate:
13967 * @ctxt: the XPath context
13968 * @res: the Predicate Expression evaluation result
13970 * Evaluate a predicate result for the current node.
13971 * A PredicateExpr is evaluated by evaluating the Expr and converting
13972 * the result to a boolean. If the result is a number, the result will
13973 * be converted to true if the number is equal to the position of the
13974 * context node in the context node list (as returned by the position
13975 * function) and will be converted to false otherwise; if the result
13976 * is not a number, then the result will be converted as if by a call
13977 * to the boolean function.
13979 * Returns 1 if predicate is true, 0 otherwise
13982 xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
13983 if ((ctxt == NULL) || (res == NULL)) return(0);
13984 switch (res->type) {
13985 case XPATH_BOOLEAN:
13986 return(res->boolval);
13987 case XPATH_NUMBER:
13988 return(res->floatval == ctxt->proximityPosition);
13989 case XPATH_NODESET:
13990 case XPATH_XSLT_TREE:
13991 if (res->nodesetval == NULL)
13992 return(0);
13993 return(res->nodesetval->nodeNr != 0);
13994 case XPATH_STRING:
13995 return((res->stringval != NULL) &&
13996 (xmlStrlen(res->stringval) != 0));
13997 default:
13998 STRANGE
14000 return(0);
14004 * xmlXPathEvaluatePredicateResult:
14005 * @ctxt: the XPath Parser context
14006 * @res: the Predicate Expression evaluation result
14008 * Evaluate a predicate result for the current node.
14009 * A PredicateExpr is evaluated by evaluating the Expr and converting
14010 * the result to a boolean. If the result is a number, the result will
14011 * be converted to true if the number is equal to the position of the
14012 * context node in the context node list (as returned by the position
14013 * function) and will be converted to false otherwise; if the result
14014 * is not a number, then the result will be converted as if by a call
14015 * to the boolean function.
14017 * Returns 1 if predicate is true, 0 otherwise
14020 xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
14021 xmlXPathObjectPtr res) {
14022 if ((ctxt == NULL) || (res == NULL)) return(0);
14023 switch (res->type) {
14024 case XPATH_BOOLEAN:
14025 return(res->boolval);
14026 case XPATH_NUMBER:
14027 #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
14028 return((res->floatval == ctxt->context->proximityPosition) &&
14029 (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
14030 #else
14031 return(res->floatval == ctxt->context->proximityPosition);
14032 #endif
14033 case XPATH_NODESET:
14034 case XPATH_XSLT_TREE:
14035 if (res->nodesetval == NULL)
14036 return(0);
14037 return(res->nodesetval->nodeNr != 0);
14038 case XPATH_STRING:
14039 return((res->stringval != NULL) && (res->stringval[0] != 0));
14040 #ifdef LIBXML_XPTR_ENABLED
14041 case XPATH_LOCATIONSET:{
14042 xmlLocationSetPtr ptr = res->user;
14043 if (ptr == NULL)
14044 return(0);
14045 return (ptr->locNr != 0);
14047 #endif
14048 default:
14049 STRANGE
14051 return(0);
14054 #ifdef XPATH_STREAMING
14056 * xmlXPathTryStreamCompile:
14057 * @ctxt: an XPath context
14058 * @str: the XPath expression
14060 * Try to compile the XPath expression as a streamable subset.
14062 * Returns the compiled expression or NULL if failed to compile.
14064 static xmlXPathCompExprPtr
14065 xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14067 * Optimization: use streaming patterns when the XPath expression can
14068 * be compiled to a stream lookup
14070 xmlPatternPtr stream;
14071 xmlXPathCompExprPtr comp;
14072 xmlDictPtr dict = NULL;
14073 const xmlChar **namespaces = NULL;
14074 xmlNsPtr ns;
14075 int i, j;
14077 if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
14078 (!xmlStrchr(str, '@'))) {
14079 const xmlChar *tmp;
14082 * We don't try to handle expressions using the verbose axis
14083 * specifiers ("::"), just the simplified form at this point.
14084 * Additionally, if there is no list of namespaces available and
14085 * there's a ":" in the expression, indicating a prefixed QName,
14086 * then we won't try to compile either. xmlPatterncompile() needs
14087 * to have a list of namespaces at compilation time in order to
14088 * compile prefixed name tests.
14090 tmp = xmlStrchr(str, ':');
14091 if ((tmp != NULL) &&
14092 ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
14093 return(NULL);
14095 if (ctxt != NULL) {
14096 dict = ctxt->dict;
14097 if (ctxt->nsNr > 0) {
14098 namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
14099 if (namespaces == NULL) {
14100 xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
14101 return(NULL);
14103 for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
14104 ns = ctxt->namespaces[j];
14105 namespaces[i++] = ns->href;
14106 namespaces[i++] = ns->prefix;
14108 namespaces[i++] = NULL;
14109 namespaces[i] = NULL;
14113 stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH, namespaces);
14114 if (namespaces != NULL) {
14115 xmlFree((xmlChar **)namespaces);
14117 if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
14118 comp = xmlXPathNewCompExpr();
14119 if (comp == NULL) {
14120 xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
14121 return(NULL);
14123 comp->stream = stream;
14124 comp->dict = dict;
14125 if (comp->dict)
14126 xmlDictReference(comp->dict);
14127 return(comp);
14129 xmlFreePattern(stream);
14131 return(NULL);
14133 #endif /* XPATH_STREAMING */
14135 static void
14136 xmlXPathOptimizeExpression(xmlXPathParserContextPtr pctxt,
14137 xmlXPathStepOpPtr op)
14139 xmlXPathCompExprPtr comp = pctxt->comp;
14140 xmlXPathContextPtr ctxt;
14143 * Try to rewrite "descendant-or-self::node()/foo" to an optimized
14144 * internal representation.
14147 if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
14148 (op->ch1 != -1) &&
14149 (op->ch2 == -1 /* no predicate */))
14151 xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
14153 if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
14154 ((xmlXPathAxisVal) prevop->value ==
14155 AXIS_DESCENDANT_OR_SELF) &&
14156 (prevop->ch2 == -1) &&
14157 ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
14158 ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE))
14161 * This is a "descendant-or-self::node()" without predicates.
14162 * Try to eliminate it.
14165 switch ((xmlXPathAxisVal) op->value) {
14166 case AXIS_CHILD:
14167 case AXIS_DESCENDANT:
14169 * Convert "descendant-or-self::node()/child::" or
14170 * "descendant-or-self::node()/descendant::" to
14171 * "descendant::"
14173 op->ch1 = prevop->ch1;
14174 op->value = AXIS_DESCENDANT;
14175 break;
14176 case AXIS_SELF:
14177 case AXIS_DESCENDANT_OR_SELF:
14179 * Convert "descendant-or-self::node()/self::" or
14180 * "descendant-or-self::node()/descendant-or-self::" to
14181 * to "descendant-or-self::"
14183 op->ch1 = prevop->ch1;
14184 op->value = AXIS_DESCENDANT_OR_SELF;
14185 break;
14186 default:
14187 break;
14192 /* OP_VALUE has invalid ch1. */
14193 if (op->op == XPATH_OP_VALUE)
14194 return;
14196 /* Recurse */
14197 ctxt = pctxt->context;
14198 if (ctxt != NULL) {
14199 if (ctxt->depth >= XPATH_MAX_RECURSION_DEPTH)
14200 return;
14201 ctxt->depth += 1;
14203 if (op->ch1 != -1)
14204 xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch1]);
14205 if (op->ch2 != -1)
14206 xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch2]);
14207 if (ctxt != NULL)
14208 ctxt->depth -= 1;
14212 * xmlXPathCtxtCompile:
14213 * @ctxt: an XPath context
14214 * @str: the XPath expression
14216 * Compile an XPath expression
14218 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14219 * the caller has to free the object.
14221 xmlXPathCompExprPtr
14222 xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14223 xmlXPathParserContextPtr pctxt;
14224 xmlXPathCompExprPtr comp;
14226 #ifdef XPATH_STREAMING
14227 comp = xmlXPathTryStreamCompile(ctxt, str);
14228 if (comp != NULL)
14229 return(comp);
14230 #endif
14232 xmlInitParser();
14234 pctxt = xmlXPathNewParserContext(str, ctxt);
14235 if (pctxt == NULL)
14236 return NULL;
14237 if (ctxt != NULL)
14238 ctxt->depth = 0;
14239 xmlXPathCompileExpr(pctxt, 1);
14241 if( pctxt->error != XPATH_EXPRESSION_OK )
14243 xmlXPathFreeParserContext(pctxt);
14244 return(NULL);
14247 if (*pctxt->cur != 0) {
14249 * aleksey: in some cases this line prints *second* error message
14250 * (see bug #78858) and probably this should be fixed.
14251 * However, we are not sure that all error messages are printed
14252 * out in other places. It's not critical so we leave it as-is for now
14254 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14255 comp = NULL;
14256 } else {
14257 comp = pctxt->comp;
14258 if ((comp->nbStep > 1) && (comp->last >= 0)) {
14259 if (ctxt != NULL)
14260 ctxt->depth = 0;
14261 xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
14263 pctxt->comp = NULL;
14265 xmlXPathFreeParserContext(pctxt);
14267 if (comp != NULL) {
14268 comp->expr = xmlStrdup(str);
14269 #ifdef DEBUG_EVAL_COUNTS
14270 comp->string = xmlStrdup(str);
14271 comp->nb = 0;
14272 #endif
14274 return(comp);
14278 * xmlXPathCompile:
14279 * @str: the XPath expression
14281 * Compile an XPath expression
14283 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14284 * the caller has to free the object.
14286 xmlXPathCompExprPtr
14287 xmlXPathCompile(const xmlChar *str) {
14288 return(xmlXPathCtxtCompile(NULL, str));
14292 * xmlXPathCompiledEvalInternal:
14293 * @comp: the compiled XPath expression
14294 * @ctxt: the XPath context
14295 * @resObj: the resulting XPath object or NULL
14296 * @toBool: 1 if only a boolean result is requested
14298 * Evaluate the Precompiled XPath expression in the given context.
14299 * The caller has to free @resObj.
14301 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14302 * the caller has to free the object.
14304 static int
14305 xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
14306 xmlXPathContextPtr ctxt,
14307 xmlXPathObjectPtr *resObjPtr,
14308 int toBool)
14310 xmlXPathParserContextPtr pctxt;
14311 xmlXPathObjectPtr resObj;
14312 #ifndef LIBXML_THREAD_ENABLED
14313 static int reentance = 0;
14314 #endif
14315 int res;
14317 CHECK_CTXT_NEG(ctxt)
14319 if (comp == NULL)
14320 return(-1);
14321 xmlInitParser();
14323 #ifndef LIBXML_THREAD_ENABLED
14324 reentance++;
14325 if (reentance > 1)
14326 xmlXPathDisableOptimizer = 1;
14327 #endif
14329 #ifdef DEBUG_EVAL_COUNTS
14330 comp->nb++;
14331 if ((comp->string != NULL) && (comp->nb > 100)) {
14332 fprintf(stderr, "100 x %s\n", comp->string);
14333 comp->nb = 0;
14335 #endif
14336 pctxt = xmlXPathCompParserContext(comp, ctxt);
14337 res = xmlXPathRunEval(pctxt, toBool);
14339 if (pctxt->error != XPATH_EXPRESSION_OK) {
14340 resObj = NULL;
14341 } else {
14342 resObj = valuePop(pctxt);
14343 if (resObj == NULL) {
14344 if (!toBool)
14345 xmlGenericError(xmlGenericErrorContext,
14346 "xmlXPathCompiledEval: No result on the stack.\n");
14347 } else if (pctxt->valueNr > 0) {
14348 xmlGenericError(xmlGenericErrorContext,
14349 "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
14350 pctxt->valueNr);
14354 if (resObjPtr)
14355 *resObjPtr = resObj;
14356 else
14357 xmlXPathReleaseObject(ctxt, resObj);
14359 pctxt->comp = NULL;
14360 xmlXPathFreeParserContext(pctxt);
14361 #ifndef LIBXML_THREAD_ENABLED
14362 reentance--;
14363 #endif
14365 return(res);
14369 * xmlXPathCompiledEval:
14370 * @comp: the compiled XPath expression
14371 * @ctx: the XPath context
14373 * Evaluate the Precompiled XPath expression in the given context.
14375 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14376 * the caller has to free the object.
14378 xmlXPathObjectPtr
14379 xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
14381 xmlXPathObjectPtr res = NULL;
14383 xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
14384 return(res);
14388 * xmlXPathCompiledEvalToBoolean:
14389 * @comp: the compiled XPath expression
14390 * @ctxt: the XPath context
14392 * Applies the XPath boolean() function on the result of the given
14393 * compiled expression.
14395 * Returns 1 if the expression evaluated to true, 0 if to false and
14396 * -1 in API and internal errors.
14399 xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
14400 xmlXPathContextPtr ctxt)
14402 return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
14406 * xmlXPathEvalExpr:
14407 * @ctxt: the XPath Parser context
14409 * Parse and evaluate an XPath expression in the given context,
14410 * then push the result on the context stack
14412 void
14413 xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
14414 #ifdef XPATH_STREAMING
14415 xmlXPathCompExprPtr comp;
14416 #endif
14418 if (ctxt == NULL) return;
14420 #ifdef XPATH_STREAMING
14421 comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
14422 if (comp != NULL) {
14423 if (ctxt->comp != NULL)
14424 xmlXPathFreeCompExpr(ctxt->comp);
14425 ctxt->comp = comp;
14426 } else
14427 #endif
14429 if (ctxt->context != NULL)
14430 ctxt->context->depth = 0;
14431 xmlXPathCompileExpr(ctxt, 1);
14432 CHECK_ERROR;
14434 /* Check for trailing characters. */
14435 if (*ctxt->cur != 0)
14436 XP_ERROR(XPATH_EXPR_ERROR);
14438 if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0)) {
14439 if (ctxt->context != NULL)
14440 ctxt->context->depth = 0;
14441 xmlXPathOptimizeExpression(ctxt,
14442 &ctxt->comp->steps[ctxt->comp->last]);
14446 xmlXPathRunEval(ctxt, 0);
14450 * xmlXPathEval:
14451 * @str: the XPath expression
14452 * @ctx: the XPath context
14454 * Evaluate the XPath Location Path in the given context.
14456 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14457 * the caller has to free the object.
14459 xmlXPathObjectPtr
14460 xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
14461 xmlXPathParserContextPtr ctxt;
14462 xmlXPathObjectPtr res;
14464 CHECK_CTXT(ctx)
14466 xmlInitParser();
14468 ctxt = xmlXPathNewParserContext(str, ctx);
14469 if (ctxt == NULL)
14470 return NULL;
14471 xmlXPathEvalExpr(ctxt);
14473 if (ctxt->error != XPATH_EXPRESSION_OK) {
14474 res = NULL;
14475 } else {
14476 res = valuePop(ctxt);
14477 if (res == NULL) {
14478 xmlGenericError(xmlGenericErrorContext,
14479 "xmlXPathCompiledEval: No result on the stack.\n");
14480 } else if (ctxt->valueNr > 0) {
14481 xmlGenericError(xmlGenericErrorContext,
14482 "xmlXPathCompiledEval: %d object(s) left on the stack.\n",
14483 ctxt->valueNr);
14487 xmlXPathFreeParserContext(ctxt);
14488 return(res);
14492 * xmlXPathSetContextNode:
14493 * @node: the node to to use as the context node
14494 * @ctx: the XPath context
14496 * Sets 'node' as the context node. The node must be in the same
14497 * document as that associated with the context.
14499 * Returns -1 in case of error or 0 if successful
14502 xmlXPathSetContextNode(xmlNodePtr node, xmlXPathContextPtr ctx) {
14503 if ((node == NULL) || (ctx == NULL))
14504 return(-1);
14506 if (node->doc == ctx->doc) {
14507 ctx->node = node;
14508 return(0);
14510 return(-1);
14514 * xmlXPathNodeEval:
14515 * @node: the node to to use as the context node
14516 * @str: the XPath expression
14517 * @ctx: the XPath context
14519 * Evaluate the XPath Location Path in the given context. The node 'node'
14520 * is set as the context node. The context node is not restored.
14522 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14523 * the caller has to free the object.
14525 xmlXPathObjectPtr
14526 xmlXPathNodeEval(xmlNodePtr node, const xmlChar *str, xmlXPathContextPtr ctx) {
14527 if (str == NULL)
14528 return(NULL);
14529 if (xmlXPathSetContextNode(node, ctx) < 0)
14530 return(NULL);
14531 return(xmlXPathEval(str, ctx));
14535 * xmlXPathEvalExpression:
14536 * @str: the XPath expression
14537 * @ctxt: the XPath context
14539 * Alias for xmlXPathEval().
14541 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14542 * the caller has to free the object.
14544 xmlXPathObjectPtr
14545 xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
14546 return(xmlXPathEval(str, ctxt));
14549 /************************************************************************
14551 * Extra functions not pertaining to the XPath spec *
14553 ************************************************************************/
14555 * xmlXPathEscapeUriFunction:
14556 * @ctxt: the XPath Parser context
14557 * @nargs: the number of arguments
14559 * Implement the escape-uri() XPath function
14560 * string escape-uri(string $str, bool $escape-reserved)
14562 * This function applies the URI escaping rules defined in section 2 of [RFC
14563 * 2396] to the string supplied as $uri-part, which typically represents all
14564 * or part of a URI. The effect of the function is to replace any special
14565 * character in the string by an escape sequence of the form %xx%yy...,
14566 * where xxyy... is the hexadecimal representation of the octets used to
14567 * represent the character in UTF-8.
14569 * The set of characters that are escaped depends on the setting of the
14570 * boolean argument $escape-reserved.
14572 * If $escape-reserved is true, all characters are escaped other than lower
14573 * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
14574 * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
14575 * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
14576 * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
14577 * A-F).
14579 * If $escape-reserved is false, the behavior differs in that characters
14580 * referred to in [RFC 2396] as reserved characters are not escaped. These
14581 * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
14583 * [RFC 2396] does not define whether escaped URIs should use lower case or
14584 * upper case for hexadecimal digits. To ensure that escaped URIs can be
14585 * compared using string comparison functions, this function must always use
14586 * the upper-case letters A-F.
14588 * Generally, $escape-reserved should be set to true when escaping a string
14589 * that is to form a single part of a URI, and to false when escaping an
14590 * entire URI or URI reference.
14592 * In the case of non-ascii characters, the string is encoded according to
14593 * utf-8 and then converted according to RFC 2396.
14595 * Examples
14596 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
14597 * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
14598 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
14599 * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
14602 static void
14603 xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
14604 xmlXPathObjectPtr str;
14605 int escape_reserved;
14606 xmlBufPtr target;
14607 xmlChar *cptr;
14608 xmlChar escape[4];
14610 CHECK_ARITY(2);
14612 escape_reserved = xmlXPathPopBoolean(ctxt);
14614 CAST_TO_STRING;
14615 str = valuePop(ctxt);
14617 target = xmlBufCreate();
14619 escape[0] = '%';
14620 escape[3] = 0;
14622 if (target) {
14623 for (cptr = str->stringval; *cptr; cptr++) {
14624 if ((*cptr >= 'A' && *cptr <= 'Z') ||
14625 (*cptr >= 'a' && *cptr <= 'z') ||
14626 (*cptr >= '0' && *cptr <= '9') ||
14627 *cptr == '-' || *cptr == '_' || *cptr == '.' ||
14628 *cptr == '!' || *cptr == '~' || *cptr == '*' ||
14629 *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
14630 (*cptr == '%' &&
14631 ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
14632 (cptr[1] >= 'a' && cptr[1] <= 'f') ||
14633 (cptr[1] >= '0' && cptr[1] <= '9')) &&
14634 ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
14635 (cptr[2] >= 'a' && cptr[2] <= 'f') ||
14636 (cptr[2] >= '0' && cptr[2] <= '9'))) ||
14637 (!escape_reserved &&
14638 (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
14639 *cptr == ':' || *cptr == '@' || *cptr == '&' ||
14640 *cptr == '=' || *cptr == '+' || *cptr == '$' ||
14641 *cptr == ','))) {
14642 xmlBufAdd(target, cptr, 1);
14643 } else {
14644 if ((*cptr >> 4) < 10)
14645 escape[1] = '0' + (*cptr >> 4);
14646 else
14647 escape[1] = 'A' - 10 + (*cptr >> 4);
14648 if ((*cptr & 0xF) < 10)
14649 escape[2] = '0' + (*cptr & 0xF);
14650 else
14651 escape[2] = 'A' - 10 + (*cptr & 0xF);
14653 xmlBufAdd(target, &escape[0], 3);
14657 valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
14658 xmlBufContent(target)));
14659 xmlBufFree(target);
14660 xmlXPathReleaseObject(ctxt->context, str);
14664 * xmlXPathRegisterAllFunctions:
14665 * @ctxt: the XPath context
14667 * Registers all default XPath functions in this context
14669 void
14670 xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
14672 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
14673 xmlXPathBooleanFunction);
14674 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
14675 xmlXPathCeilingFunction);
14676 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
14677 xmlXPathCountFunction);
14678 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
14679 xmlXPathConcatFunction);
14680 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
14681 xmlXPathContainsFunction);
14682 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
14683 xmlXPathIdFunction);
14684 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
14685 xmlXPathFalseFunction);
14686 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
14687 xmlXPathFloorFunction);
14688 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
14689 xmlXPathLastFunction);
14690 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
14691 xmlXPathLangFunction);
14692 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
14693 xmlXPathLocalNameFunction);
14694 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
14695 xmlXPathNotFunction);
14696 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
14697 xmlXPathNameFunction);
14698 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
14699 xmlXPathNamespaceURIFunction);
14700 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
14701 xmlXPathNormalizeFunction);
14702 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
14703 xmlXPathNumberFunction);
14704 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
14705 xmlXPathPositionFunction);
14706 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
14707 xmlXPathRoundFunction);
14708 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
14709 xmlXPathStringFunction);
14710 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
14711 xmlXPathStringLengthFunction);
14712 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
14713 xmlXPathStartsWithFunction);
14714 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
14715 xmlXPathSubstringFunction);
14716 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
14717 xmlXPathSubstringBeforeFunction);
14718 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
14719 xmlXPathSubstringAfterFunction);
14720 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
14721 xmlXPathSumFunction);
14722 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
14723 xmlXPathTrueFunction);
14724 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
14725 xmlXPathTranslateFunction);
14727 xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
14728 (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
14729 xmlXPathEscapeUriFunction);
14732 #endif /* LIBXML_XPATH_ENABLED */
14733 #define bottom_xpath
14734 #include "elfgcchack.h"