Annotation of embedaddon/libxml2/xpath.c, revision 1.1.1.3
1.1 misho 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
5: *f
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
10: *
11: * See Copyright for the status of this software
12: *
13: * Author: daniel@veillard.com
14: *
15: */
16:
17: #define IN_LIBXML
18: #include "libxml.h"
19:
20: #include <string.h>
21:
22: #ifdef HAVE_SYS_TYPES_H
23: #include <sys/types.h>
24: #endif
25: #ifdef HAVE_MATH_H
26: #include <math.h>
27: #endif
28: #ifdef HAVE_FLOAT_H
29: #include <float.h>
30: #endif
31: #ifdef HAVE_CTYPE_H
32: #include <ctype.h>
33: #endif
34: #ifdef HAVE_SIGNAL_H
35: #include <signal.h>
36: #endif
37:
38: #include <libxml/xmlmemory.h>
39: #include <libxml/tree.h>
40: #include <libxml/valid.h>
41: #include <libxml/xpath.h>
42: #include <libxml/xpathInternals.h>
43: #include <libxml/parserInternals.h>
44: #include <libxml/hash.h>
45: #ifdef LIBXML_XPTR_ENABLED
46: #include <libxml/xpointer.h>
47: #endif
48: #ifdef LIBXML_DEBUG_ENABLED
49: #include <libxml/debugXML.h>
50: #endif
51: #include <libxml/xmlerror.h>
52: #include <libxml/threads.h>
53: #include <libxml/globals.h>
54: #ifdef LIBXML_PATTERN_ENABLED
55: #include <libxml/pattern.h>
56: #endif
57:
1.1.1.3 ! misho 58: #include "buf.h"
! 59:
1.1 misho 60: #ifdef LIBXML_PATTERN_ENABLED
61: #define XPATH_STREAMING
62: #endif
63:
64: #define TODO \
65: xmlGenericError(xmlGenericErrorContext, \
66: "Unimplemented block at %s:%d\n", \
67: __FILE__, __LINE__);
68:
1.1.1.3 ! misho 69: /**
! 70: * WITH_TIM_SORT:
! 71: *
! 72: * Use the Timsort algorithm provided in timsort.h to sort
! 73: * nodeset as this is a great improvement over the old Shell sort
! 74: * used in xmlXPathNodeSetSort()
! 75: */
! 76: #define WITH_TIM_SORT
! 77:
1.1 misho 78: /*
79: * XP_OPTIMIZED_NON_ELEM_COMPARISON:
80: * If defined, this will use xmlXPathCmpNodesExt() instead of
81: * xmlXPathCmpNodes(). The new function is optimized comparison of
82: * non-element nodes; actually it will speed up comparison only if
83: * xmlXPathOrderDocElems() was called in order to index the elements of
84: * a tree in document order; Libxslt does such an indexing, thus it will
85: * benefit from this optimization.
86: */
87: #define XP_OPTIMIZED_NON_ELEM_COMPARISON
88:
89: /*
90: * XP_OPTIMIZED_FILTER_FIRST:
91: * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
92: * in a way, that it stop evaluation at the first node.
93: */
94: #define XP_OPTIMIZED_FILTER_FIRST
95:
96: /*
97: * XP_DEBUG_OBJ_USAGE:
98: * Internal flag to enable tracking of how much XPath objects have been
99: * created.
100: */
101: /* #define XP_DEBUG_OBJ_USAGE */
102:
103: /*
1.1.1.3 ! misho 104: * XPATH_MAX_STEPS:
! 105: * when compiling an XPath expression we arbitrary limit the maximum
! 106: * number of step operation in the compiled expression. 1000000 is
! 107: * an insanely large value which should never be reached under normal
! 108: * circumstances
! 109: */
! 110: #define XPATH_MAX_STEPS 1000000
! 111:
! 112: /*
! 113: * XPATH_MAX_STACK_DEPTH:
! 114: * when evaluating an XPath expression we arbitrary limit the maximum
! 115: * number of object allowed to be pushed on the stack. 1000000 is
! 116: * an insanely large value which should never be reached under normal
! 117: * circumstances
! 118: */
! 119: #define XPATH_MAX_STACK_DEPTH 1000000
! 120:
! 121: /*
! 122: * XPATH_MAX_NODESET_LENGTH:
! 123: * when evaluating an XPath expression nodesets are created and we
! 124: * arbitrary limit the maximum length of those node set. 10000000 is
! 125: * an insanely large value which should never be reached under normal
! 126: * circumstances, one would first need to construct an in memory tree
! 127: * with more than 10 millions nodes.
! 128: */
! 129: #define XPATH_MAX_NODESET_LENGTH 10000000
! 130:
! 131: /*
1.1 misho 132: * TODO:
133: * There are a few spots where some tests are done which depend upon ascii
134: * data. These should be enhanced for full UTF8 support (see particularly
135: * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
136: */
137:
1.1.1.3 ! misho 138: /*
! 139: * Wrapper for the Timsort argorithm from timsort.h
! 140: */
! 141: #ifdef WITH_TIM_SORT
! 142: #define SORT_NAME libxml_domnode
! 143: #define SORT_TYPE xmlNodePtr
! 144: /**
! 145: * wrap_cmp:
! 146: * @x: a node
! 147: * @y: another node
! 148: *
! 149: * Comparison function for the Timsort implementation
! 150: *
! 151: * Returns -2 in case of error -1 if first point < second point, 0 if
! 152: * it's the same node, +1 otherwise
! 153: */
! 154: static
! 155: int wrap_cmp( xmlNodePtr x, xmlNodePtr y );
! 156: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
! 157: static int xmlXPathCmpNodesExt(xmlNodePtr, xmlNodePtr);
! 158: static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
! 159: {
! 160: int res = xmlXPathCmpNodesExt(x, y);
! 161: return res == -2 ? res : -res;
! 162: }
! 163: #else
! 164: static int wrap_cmp( xmlNodePtr x, xmlNodePtr y )
! 165: {
! 166: int res = xmlXPathCmpNodes(x, y);
! 167: return res == -2 ? res : -res;
! 168: }
! 169: #endif
! 170: #define SORT_CMP(x, y) (wrap_cmp(x, y))
! 171: #include "timsort.h"
! 172: #endif /* WITH_TIM_SORT */
! 173:
1.1 misho 174: #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
175:
176: /************************************************************************
177: * *
178: * Floating point stuff *
179: * *
180: ************************************************************************/
181:
182: #ifndef TRIO_REPLACE_STDIO
183: #define TRIO_PUBLIC static
184: #endif
185: #include "trionan.c"
186:
187: /*
188: * The lack of portability of this section of the libc is annoying !
189: */
190: double xmlXPathNAN = 0;
191: double xmlXPathPINF = 1;
192: double xmlXPathNINF = -1;
193: static double xmlXPathNZERO = 0; /* not exported from headers */
194: static int xmlXPathInitialized = 0;
195:
196: /**
197: * xmlXPathInit:
198: *
199: * Initialize the XPath environment
200: */
201: void
202: xmlXPathInit(void) {
203: if (xmlXPathInitialized) return;
204:
205: xmlXPathPINF = trio_pinf();
206: xmlXPathNINF = trio_ninf();
207: xmlXPathNAN = trio_nan();
208: xmlXPathNZERO = trio_nzero();
209:
210: xmlXPathInitialized = 1;
211: }
212:
213: /**
214: * xmlXPathIsNaN:
215: * @val: a double value
216: *
217: * Provides a portable isnan() function to detect whether a double
218: * is a NotaNumber. Based on trio code
219: * http://sourceforge.net/projects/ctrio/
220: *
221: * Returns 1 if the value is a NaN, 0 otherwise
222: */
223: int
224: xmlXPathIsNaN(double val) {
225: return(trio_isnan(val));
226: }
227:
228: /**
229: * xmlXPathIsInf:
230: * @val: a double value
231: *
232: * Provides a portable isinf() function to detect whether a double
233: * is a +Infinite or -Infinite. Based on trio code
234: * http://sourceforge.net/projects/ctrio/
235: *
236: * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
237: */
238: int
239: xmlXPathIsInf(double val) {
240: return(trio_isinf(val));
241: }
242:
243: #endif /* SCHEMAS or XPATH */
244: #ifdef LIBXML_XPATH_ENABLED
245: /**
246: * xmlXPathGetSign:
247: * @val: a double value
248: *
249: * Provides a portable function to detect the sign of a double
250: * Modified from trio code
251: * http://sourceforge.net/projects/ctrio/
252: *
253: * Returns 1 if the value is Negative, 0 if positive
254: */
255: static int
256: xmlXPathGetSign(double val) {
257: return(trio_signbit(val));
258: }
259:
260:
261: /*
262: * TODO: when compatibility allows remove all "fake node libxslt" strings
263: * the test should just be name[0] = ' '
264: */
265: #ifdef DEBUG_XPATH_EXPRESSION
266: #define DEBUG_STEP
267: #define DEBUG_EXPR
268: #define DEBUG_EVAL_COUNTS
269: #endif
270:
271: static xmlNs xmlXPathXMLNamespaceStruct = {
272: NULL,
273: XML_NAMESPACE_DECL,
274: XML_XML_NAMESPACE,
275: BAD_CAST "xml",
276: NULL,
277: NULL
278: };
279: static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
280: #ifndef LIBXML_THREAD_ENABLED
281: /*
282: * Optimizer is disabled only when threaded apps are detected while
283: * the library ain't compiled for thread safety.
284: */
285: static int xmlXPathDisableOptimizer = 0;
286: #endif
287:
288: /************************************************************************
289: * *
290: * Error handling routines *
291: * *
292: ************************************************************************/
293:
294: /**
295: * XP_ERRORNULL:
296: * @X: the error code
297: *
298: * Macro to raise an XPath error and return NULL.
299: */
300: #define XP_ERRORNULL(X) \
301: { xmlXPathErr(ctxt, X); return(NULL); }
302:
303: /*
304: * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
305: */
306: static const char *xmlXPathErrorMessages[] = {
307: "Ok\n",
308: "Number encoding\n",
309: "Unfinished literal\n",
310: "Start of literal\n",
311: "Expected $ for variable reference\n",
312: "Undefined variable\n",
313: "Invalid predicate\n",
314: "Invalid expression\n",
315: "Missing closing curly brace\n",
316: "Unregistered function\n",
317: "Invalid operand\n",
318: "Invalid type\n",
319: "Invalid number of arguments\n",
320: "Invalid context size\n",
321: "Invalid context position\n",
322: "Memory allocation error\n",
323: "Syntax error\n",
324: "Resource error\n",
325: "Sub resource error\n",
326: "Undefined namespace prefix\n",
327: "Encoding error\n",
328: "Char out of XML range\n",
329: "Invalid or incomplete context\n",
1.1.1.2 misho 330: "Stack usage errror\n",
1.1.1.3 ! misho 331: "Forbidden variable\n",
1.1 misho 332: "?? Unknown error ??\n" /* Must be last in the list! */
333: };
334: #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
335: sizeof(xmlXPathErrorMessages[0])) - 1)
336: /**
337: * xmlXPathErrMemory:
338: * @ctxt: an XPath context
339: * @extra: extra informations
340: *
341: * Handle a redefinition of attribute error
342: */
343: static void
344: xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
345: {
346: if (ctxt != NULL) {
347: if (extra) {
348: xmlChar buf[200];
349:
350: xmlStrPrintf(buf, 200,
351: BAD_CAST "Memory allocation failed : %s\n",
352: extra);
353: ctxt->lastError.message = (char *) xmlStrdup(buf);
354: } else {
355: ctxt->lastError.message = (char *)
356: xmlStrdup(BAD_CAST "Memory allocation failed\n");
357: }
358: ctxt->lastError.domain = XML_FROM_XPATH;
359: ctxt->lastError.code = XML_ERR_NO_MEMORY;
360: if (ctxt->error != NULL)
361: ctxt->error(ctxt->userData, &ctxt->lastError);
362: } else {
363: if (extra)
364: __xmlRaiseError(NULL, NULL, NULL,
365: NULL, NULL, XML_FROM_XPATH,
366: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
367: extra, NULL, NULL, 0, 0,
368: "Memory allocation failed : %s\n", extra);
369: else
370: __xmlRaiseError(NULL, NULL, NULL,
371: NULL, NULL, XML_FROM_XPATH,
372: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
373: NULL, NULL, NULL, 0, 0,
374: "Memory allocation failed\n");
375: }
376: }
377:
378: /**
379: * xmlXPathPErrMemory:
380: * @ctxt: an XPath parser context
381: * @extra: extra informations
382: *
383: * Handle a redefinition of attribute error
384: */
385: static void
386: xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
387: {
388: if (ctxt == NULL)
389: xmlXPathErrMemory(NULL, extra);
390: else {
391: ctxt->error = XPATH_MEMORY_ERROR;
392: xmlXPathErrMemory(ctxt->context, extra);
393: }
394: }
395:
396: /**
397: * xmlXPathErr:
398: * @ctxt: a XPath parser context
399: * @error: the error code
400: *
401: * Handle an XPath error
402: */
403: void
404: xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
405: {
406: if ((error < 0) || (error > MAXERRNO))
407: error = MAXERRNO;
408: if (ctxt == NULL) {
409: __xmlRaiseError(NULL, NULL, NULL,
410: NULL, NULL, XML_FROM_XPATH,
411: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
412: XML_ERR_ERROR, NULL, 0,
413: NULL, NULL, NULL, 0, 0,
414: "%s", xmlXPathErrorMessages[error]);
415: return;
416: }
417: ctxt->error = error;
418: if (ctxt->context == NULL) {
419: __xmlRaiseError(NULL, NULL, NULL,
420: NULL, NULL, XML_FROM_XPATH,
421: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
422: XML_ERR_ERROR, NULL, 0,
423: (const char *) ctxt->base, NULL, NULL,
424: ctxt->cur - ctxt->base, 0,
425: "%s", xmlXPathErrorMessages[error]);
426: return;
427: }
428:
429: /* cleanup current last error */
430: xmlResetError(&ctxt->context->lastError);
431:
432: ctxt->context->lastError.domain = XML_FROM_XPATH;
433: ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
434: XPATH_EXPRESSION_OK;
435: ctxt->context->lastError.level = XML_ERR_ERROR;
436: ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
437: ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
438: ctxt->context->lastError.node = ctxt->context->debugNode;
439: if (ctxt->context->error != NULL) {
440: ctxt->context->error(ctxt->context->userData,
441: &ctxt->context->lastError);
442: } else {
443: __xmlRaiseError(NULL, NULL, NULL,
444: NULL, ctxt->context->debugNode, XML_FROM_XPATH,
445: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
446: XML_ERR_ERROR, NULL, 0,
447: (const char *) ctxt->base, NULL, NULL,
448: ctxt->cur - ctxt->base, 0,
449: "%s", xmlXPathErrorMessages[error]);
450: }
451:
452: }
453:
454: /**
455: * xmlXPatherror:
456: * @ctxt: the XPath Parser context
457: * @file: the file name
458: * @line: the line number
459: * @no: the error number
460: *
461: * Formats an error message.
462: */
463: void
464: xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
465: int line ATTRIBUTE_UNUSED, int no) {
466: xmlXPathErr(ctxt, no);
467: }
468:
469: /************************************************************************
470: * *
471: * Utilities *
472: * *
473: ************************************************************************/
474:
475: /**
476: * xsltPointerList:
477: *
478: * Pointer-list for various purposes.
479: */
480: typedef struct _xmlPointerList xmlPointerList;
481: typedef xmlPointerList *xmlPointerListPtr;
482: struct _xmlPointerList {
483: void **items;
484: int number;
485: int size;
486: };
487: /*
488: * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
489: * and here, we should make the functions public.
490: */
491: static int
492: xmlPointerListAddSize(xmlPointerListPtr list,
493: void *item,
494: int initialSize)
495: {
496: if (list->items == NULL) {
497: if (initialSize <= 0)
498: initialSize = 1;
1.1.1.3 ! misho 499: list->items = (void **) xmlMalloc(initialSize * sizeof(void *));
1.1 misho 500: if (list->items == NULL) {
501: xmlXPathErrMemory(NULL,
502: "xmlPointerListCreate: allocating item\n");
503: return(-1);
504: }
505: list->number = 0;
506: list->size = initialSize;
507: } else if (list->size <= list->number) {
1.1.1.3 ! misho 508: if (list->size > 50000000) {
! 509: xmlXPathErrMemory(NULL,
! 510: "xmlPointerListAddSize: re-allocating item\n");
! 511: return(-1);
! 512: }
1.1 misho 513: list->size *= 2;
514: list->items = (void **) xmlRealloc(list->items,
515: list->size * sizeof(void *));
516: if (list->items == NULL) {
517: xmlXPathErrMemory(NULL,
1.1.1.3 ! misho 518: "xmlPointerListAddSize: re-allocating item\n");
1.1 misho 519: list->size = 0;
520: return(-1);
521: }
522: }
523: list->items[list->number++] = item;
524: return(0);
525: }
526:
527: /**
528: * xsltPointerListCreate:
529: *
530: * Creates an xsltPointerList structure.
531: *
532: * Returns a xsltPointerList structure or NULL in case of an error.
533: */
534: static xmlPointerListPtr
535: xmlPointerListCreate(int initialSize)
536: {
537: xmlPointerListPtr ret;
538:
539: ret = xmlMalloc(sizeof(xmlPointerList));
540: if (ret == NULL) {
541: xmlXPathErrMemory(NULL,
542: "xmlPointerListCreate: allocating item\n");
543: return (NULL);
544: }
545: memset(ret, 0, sizeof(xmlPointerList));
546: if (initialSize > 0) {
547: xmlPointerListAddSize(ret, NULL, initialSize);
548: ret->number = 0;
549: }
550: return (ret);
551: }
552:
553: /**
554: * xsltPointerListFree:
555: *
556: * Frees the xsltPointerList structure. This does not free
557: * the content of the list.
558: */
559: static void
560: xmlPointerListFree(xmlPointerListPtr list)
561: {
562: if (list == NULL)
563: return;
564: if (list->items != NULL)
565: xmlFree(list->items);
566: xmlFree(list);
567: }
568:
569: /************************************************************************
570: * *
571: * Parser Types *
572: * *
573: ************************************************************************/
574:
575: /*
576: * Types are private:
577: */
578:
579: typedef enum {
580: XPATH_OP_END=0,
581: XPATH_OP_AND,
582: XPATH_OP_OR,
583: XPATH_OP_EQUAL,
584: XPATH_OP_CMP,
585: XPATH_OP_PLUS,
586: XPATH_OP_MULT,
587: XPATH_OP_UNION,
588: XPATH_OP_ROOT,
589: XPATH_OP_NODE,
590: XPATH_OP_RESET, /* 10 */
591: XPATH_OP_COLLECT,
592: XPATH_OP_VALUE, /* 12 */
593: XPATH_OP_VARIABLE,
594: XPATH_OP_FUNCTION,
595: XPATH_OP_ARG,
596: XPATH_OP_PREDICATE,
597: XPATH_OP_FILTER, /* 17 */
598: XPATH_OP_SORT /* 18 */
599: #ifdef LIBXML_XPTR_ENABLED
600: ,XPATH_OP_RANGETO
601: #endif
602: } xmlXPathOp;
603:
604: typedef enum {
605: AXIS_ANCESTOR = 1,
606: AXIS_ANCESTOR_OR_SELF,
607: AXIS_ATTRIBUTE,
608: AXIS_CHILD,
609: AXIS_DESCENDANT,
610: AXIS_DESCENDANT_OR_SELF,
611: AXIS_FOLLOWING,
612: AXIS_FOLLOWING_SIBLING,
613: AXIS_NAMESPACE,
614: AXIS_PARENT,
615: AXIS_PRECEDING,
616: AXIS_PRECEDING_SIBLING,
617: AXIS_SELF
618: } xmlXPathAxisVal;
619:
620: typedef enum {
621: NODE_TEST_NONE = 0,
622: NODE_TEST_TYPE = 1,
623: NODE_TEST_PI = 2,
624: NODE_TEST_ALL = 3,
625: NODE_TEST_NS = 4,
626: NODE_TEST_NAME = 5
627: } xmlXPathTestVal;
628:
629: typedef enum {
630: NODE_TYPE_NODE = 0,
631: NODE_TYPE_COMMENT = XML_COMMENT_NODE,
632: NODE_TYPE_TEXT = XML_TEXT_NODE,
633: NODE_TYPE_PI = XML_PI_NODE
634: } xmlXPathTypeVal;
635:
636: typedef struct _xmlXPathStepOp xmlXPathStepOp;
637: typedef xmlXPathStepOp *xmlXPathStepOpPtr;
638: struct _xmlXPathStepOp {
639: xmlXPathOp op; /* The identifier of the operation */
640: int ch1; /* First child */
641: int ch2; /* Second child */
642: int value;
643: int value2;
644: int value3;
645: void *value4;
646: void *value5;
647: void *cache;
648: void *cacheURI;
649: };
650:
651: struct _xmlXPathCompExpr {
652: int nbStep; /* Number of steps in this expression */
653: int maxStep; /* Maximum number of steps allocated */
654: xmlXPathStepOp *steps; /* ops for computation of this expression */
655: int last; /* index of last step in expression */
656: xmlChar *expr; /* the expression being computed */
657: xmlDictPtr dict; /* the dictionnary to use if any */
658: #ifdef DEBUG_EVAL_COUNTS
659: int nb;
660: xmlChar *string;
661: #endif
662: #ifdef XPATH_STREAMING
663: xmlPatternPtr stream;
664: #endif
665: };
666:
667: /************************************************************************
668: * *
669: * Forward declarations *
670: * *
671: ************************************************************************/
672: static void
673: xmlXPathFreeValueTree(xmlNodeSetPtr obj);
674: static void
675: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
676: static int
677: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
678: xmlXPathStepOpPtr op, xmlNodePtr *first);
679: static int
680: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
681: xmlXPathStepOpPtr op,
682: int isPredicate);
683:
684: /************************************************************************
685: * *
686: * Parser Type functions *
687: * *
688: ************************************************************************/
689:
690: /**
691: * xmlXPathNewCompExpr:
692: *
693: * Create a new Xpath component
694: *
695: * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
696: */
697: static xmlXPathCompExprPtr
698: xmlXPathNewCompExpr(void) {
699: xmlXPathCompExprPtr cur;
700:
701: cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
702: if (cur == NULL) {
703: xmlXPathErrMemory(NULL, "allocating component\n");
704: return(NULL);
705: }
706: memset(cur, 0, sizeof(xmlXPathCompExpr));
707: cur->maxStep = 10;
708: cur->nbStep = 0;
709: cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
710: sizeof(xmlXPathStepOp));
711: if (cur->steps == NULL) {
712: xmlXPathErrMemory(NULL, "allocating steps\n");
713: xmlFree(cur);
714: return(NULL);
715: }
716: memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
717: cur->last = -1;
718: #ifdef DEBUG_EVAL_COUNTS
719: cur->nb = 0;
720: #endif
721: return(cur);
722: }
723:
724: /**
725: * xmlXPathFreeCompExpr:
726: * @comp: an XPATH comp
727: *
728: * Free up the memory allocated by @comp
729: */
730: void
731: xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
732: {
733: xmlXPathStepOpPtr op;
734: int i;
735:
736: if (comp == NULL)
737: return;
738: if (comp->dict == NULL) {
739: for (i = 0; i < comp->nbStep; i++) {
740: op = &comp->steps[i];
741: if (op->value4 != NULL) {
742: if (op->op == XPATH_OP_VALUE)
743: xmlXPathFreeObject(op->value4);
744: else
745: xmlFree(op->value4);
746: }
747: if (op->value5 != NULL)
748: xmlFree(op->value5);
749: }
750: } else {
751: for (i = 0; i < comp->nbStep; i++) {
752: op = &comp->steps[i];
753: if (op->value4 != NULL) {
754: if (op->op == XPATH_OP_VALUE)
755: xmlXPathFreeObject(op->value4);
756: }
757: }
758: xmlDictFree(comp->dict);
759: }
760: if (comp->steps != NULL) {
761: xmlFree(comp->steps);
762: }
763: #ifdef DEBUG_EVAL_COUNTS
764: if (comp->string != NULL) {
765: xmlFree(comp->string);
766: }
767: #endif
768: #ifdef XPATH_STREAMING
769: if (comp->stream != NULL) {
770: xmlFreePatternList(comp->stream);
771: }
772: #endif
773: if (comp->expr != NULL) {
774: xmlFree(comp->expr);
775: }
776:
777: xmlFree(comp);
778: }
779:
780: /**
781: * xmlXPathCompExprAdd:
782: * @comp: the compiled expression
783: * @ch1: first child index
784: * @ch2: second child index
785: * @op: an op
786: * @value: the first int value
787: * @value2: the second int value
788: * @value3: the third int value
789: * @value4: the first string value
790: * @value5: the second string value
791: *
792: * Add a step to an XPath Compiled Expression
793: *
794: * Returns -1 in case of failure, the index otherwise
795: */
796: static int
797: xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
798: xmlXPathOp op, int value,
799: int value2, int value3, void *value4, void *value5) {
800: if (comp->nbStep >= comp->maxStep) {
801: xmlXPathStepOp *real;
802:
1.1.1.3 ! misho 803: if (comp->maxStep >= XPATH_MAX_STEPS) {
! 804: xmlXPathErrMemory(NULL, "adding step\n");
! 805: return(-1);
! 806: }
1.1 misho 807: comp->maxStep *= 2;
808: real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
809: comp->maxStep * sizeof(xmlXPathStepOp));
810: if (real == NULL) {
811: comp->maxStep /= 2;
812: xmlXPathErrMemory(NULL, "adding step\n");
813: return(-1);
814: }
815: comp->steps = real;
816: }
817: comp->last = comp->nbStep;
818: comp->steps[comp->nbStep].ch1 = ch1;
819: comp->steps[comp->nbStep].ch2 = ch2;
820: comp->steps[comp->nbStep].op = op;
821: comp->steps[comp->nbStep].value = value;
822: comp->steps[comp->nbStep].value2 = value2;
823: comp->steps[comp->nbStep].value3 = value3;
824: if ((comp->dict != NULL) &&
825: ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
826: (op == XPATH_OP_COLLECT))) {
827: if (value4 != NULL) {
828: comp->steps[comp->nbStep].value4 = (xmlChar *)
829: (void *)xmlDictLookup(comp->dict, value4, -1);
830: xmlFree(value4);
831: } else
832: comp->steps[comp->nbStep].value4 = NULL;
833: if (value5 != NULL) {
834: comp->steps[comp->nbStep].value5 = (xmlChar *)
835: (void *)xmlDictLookup(comp->dict, value5, -1);
836: xmlFree(value5);
837: } else
838: comp->steps[comp->nbStep].value5 = NULL;
839: } else {
840: comp->steps[comp->nbStep].value4 = value4;
841: comp->steps[comp->nbStep].value5 = value5;
842: }
843: comp->steps[comp->nbStep].cache = NULL;
844: return(comp->nbStep++);
845: }
846:
847: /**
848: * xmlXPathCompSwap:
849: * @comp: the compiled expression
850: * @op: operation index
851: *
852: * Swaps 2 operations in the compiled expression
853: */
854: static void
855: xmlXPathCompSwap(xmlXPathStepOpPtr op) {
856: int tmp;
857:
858: #ifndef LIBXML_THREAD_ENABLED
859: /*
860: * Since this manipulates possibly shared variables, this is
861: * disabled if one detects that the library is used in a multithreaded
862: * application
863: */
864: if (xmlXPathDisableOptimizer)
865: return;
866: #endif
867:
868: tmp = op->ch1;
869: op->ch1 = op->ch2;
870: op->ch2 = tmp;
871: }
872:
873: #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
874: xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
875: (op), (val), (val2), (val3), (val4), (val5))
876: #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
877: xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
878: (op), (val), (val2), (val3), (val4), (val5))
879:
880: #define PUSH_LEAVE_EXPR(op, val, val2) \
881: xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
882:
883: #define PUSH_UNARY_EXPR(op, ch, val, val2) \
884: xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
885:
886: #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
887: xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \
888: (val), (val2), 0 ,NULL ,NULL)
889:
890: /************************************************************************
891: * *
892: * XPath object cache structures *
893: * *
894: ************************************************************************/
895:
896: /* #define XP_DEFAULT_CACHE_ON */
897:
898: #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
899:
900: typedef struct _xmlXPathContextCache xmlXPathContextCache;
901: typedef xmlXPathContextCache *xmlXPathContextCachePtr;
902: struct _xmlXPathContextCache {
903: xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
904: xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
905: xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
906: xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
907: xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
908: int maxNodeset;
909: int maxString;
910: int maxBoolean;
911: int maxNumber;
912: int maxMisc;
913: #ifdef XP_DEBUG_OBJ_USAGE
914: int dbgCachedAll;
915: int dbgCachedNodeset;
916: int dbgCachedString;
917: int dbgCachedBool;
918: int dbgCachedNumber;
919: int dbgCachedPoint;
920: int dbgCachedRange;
921: int dbgCachedLocset;
922: int dbgCachedUsers;
923: int dbgCachedXSLTTree;
924: int dbgCachedUndefined;
925:
926:
927: int dbgReusedAll;
928: int dbgReusedNodeset;
929: int dbgReusedString;
930: int dbgReusedBool;
931: int dbgReusedNumber;
932: int dbgReusedPoint;
933: int dbgReusedRange;
934: int dbgReusedLocset;
935: int dbgReusedUsers;
936: int dbgReusedXSLTTree;
937: int dbgReusedUndefined;
938:
939: #endif
940: };
941:
942: /************************************************************************
943: * *
944: * Debugging related functions *
945: * *
946: ************************************************************************/
947:
948: #define STRANGE \
949: xmlGenericError(xmlGenericErrorContext, \
950: "Internal error at %s:%d\n", \
951: __FILE__, __LINE__);
952:
953: #ifdef LIBXML_DEBUG_ENABLED
954: static void
955: xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
956: int i;
957: char shift[100];
958:
959: for (i = 0;((i < depth) && (i < 25));i++)
960: shift[2 * i] = shift[2 * i + 1] = ' ';
961: shift[2 * i] = shift[2 * i + 1] = 0;
962: if (cur == NULL) {
963: fprintf(output, "%s", shift);
964: fprintf(output, "Node is NULL !\n");
965: return;
966:
967: }
968:
969: if ((cur->type == XML_DOCUMENT_NODE) ||
970: (cur->type == XML_HTML_DOCUMENT_NODE)) {
971: fprintf(output, "%s", shift);
972: fprintf(output, " /\n");
973: } else if (cur->type == XML_ATTRIBUTE_NODE)
974: xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
975: else
976: xmlDebugDumpOneNode(output, cur, depth);
977: }
978: static void
979: xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
980: xmlNodePtr tmp;
981: int i;
982: char shift[100];
983:
984: for (i = 0;((i < depth) && (i < 25));i++)
985: shift[2 * i] = shift[2 * i + 1] = ' ';
986: shift[2 * i] = shift[2 * i + 1] = 0;
987: if (cur == NULL) {
988: fprintf(output, "%s", shift);
989: fprintf(output, "Node is NULL !\n");
990: return;
991:
992: }
993:
994: while (cur != NULL) {
995: tmp = cur;
996: cur = cur->next;
997: xmlDebugDumpOneNode(output, tmp, depth);
998: }
999: }
1000:
1001: static void
1002: xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
1003: int i;
1004: char shift[100];
1005:
1006: for (i = 0;((i < depth) && (i < 25));i++)
1007: shift[2 * i] = shift[2 * i + 1] = ' ';
1008: shift[2 * i] = shift[2 * i + 1] = 0;
1009:
1010: if (cur == NULL) {
1011: fprintf(output, "%s", shift);
1012: fprintf(output, "NodeSet is NULL !\n");
1013: return;
1014:
1015: }
1016:
1017: if (cur != NULL) {
1018: fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
1019: for (i = 0;i < cur->nodeNr;i++) {
1020: fprintf(output, "%s", shift);
1021: fprintf(output, "%d", i + 1);
1022: xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
1023: }
1024: }
1025: }
1026:
1027: static void
1028: xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
1029: int i;
1030: char shift[100];
1031:
1032: for (i = 0;((i < depth) && (i < 25));i++)
1033: shift[2 * i] = shift[2 * i + 1] = ' ';
1034: shift[2 * i] = shift[2 * i + 1] = 0;
1035:
1036: if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
1037: fprintf(output, "%s", shift);
1038: fprintf(output, "Value Tree is NULL !\n");
1039: return;
1040:
1041: }
1042:
1043: fprintf(output, "%s", shift);
1044: fprintf(output, "%d", i + 1);
1045: xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
1046: }
1047: #if defined(LIBXML_XPTR_ENABLED)
1048: static void
1049: xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
1050: int i;
1051: char shift[100];
1052:
1053: for (i = 0;((i < depth) && (i < 25));i++)
1054: shift[2 * i] = shift[2 * i + 1] = ' ';
1055: shift[2 * i] = shift[2 * i + 1] = 0;
1056:
1057: if (cur == NULL) {
1058: fprintf(output, "%s", shift);
1059: fprintf(output, "LocationSet is NULL !\n");
1060: return;
1061:
1062: }
1063:
1064: for (i = 0;i < cur->locNr;i++) {
1065: fprintf(output, "%s", shift);
1066: fprintf(output, "%d : ", i + 1);
1067: xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
1068: }
1069: }
1070: #endif /* LIBXML_XPTR_ENABLED */
1071:
1072: /**
1073: * xmlXPathDebugDumpObject:
1074: * @output: the FILE * to dump the output
1075: * @cur: the object to inspect
1076: * @depth: indentation level
1077: *
1078: * Dump the content of the object for debugging purposes
1079: */
1080: void
1081: xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
1082: int i;
1083: char shift[100];
1084:
1085: if (output == NULL) return;
1086:
1087: for (i = 0;((i < depth) && (i < 25));i++)
1088: shift[2 * i] = shift[2 * i + 1] = ' ';
1089: shift[2 * i] = shift[2 * i + 1] = 0;
1090:
1091:
1092: fprintf(output, "%s", shift);
1093:
1094: if (cur == NULL) {
1095: fprintf(output, "Object is empty (NULL)\n");
1096: return;
1097: }
1098: switch(cur->type) {
1099: case XPATH_UNDEFINED:
1100: fprintf(output, "Object is uninitialized\n");
1101: break;
1102: case XPATH_NODESET:
1103: fprintf(output, "Object is a Node Set :\n");
1104: xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
1105: break;
1106: case XPATH_XSLT_TREE:
1107: fprintf(output, "Object is an XSLT value tree :\n");
1108: xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
1109: break;
1110: case XPATH_BOOLEAN:
1111: fprintf(output, "Object is a Boolean : ");
1112: if (cur->boolval) fprintf(output, "true\n");
1113: else fprintf(output, "false\n");
1114: break;
1115: case XPATH_NUMBER:
1116: switch (xmlXPathIsInf(cur->floatval)) {
1117: case 1:
1118: fprintf(output, "Object is a number : Infinity\n");
1119: break;
1120: case -1:
1121: fprintf(output, "Object is a number : -Infinity\n");
1122: break;
1123: default:
1124: if (xmlXPathIsNaN(cur->floatval)) {
1125: fprintf(output, "Object is a number : NaN\n");
1126: } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) {
1127: fprintf(output, "Object is a number : 0\n");
1128: } else {
1129: fprintf(output, "Object is a number : %0g\n", cur->floatval);
1130: }
1131: }
1132: break;
1133: case XPATH_STRING:
1134: fprintf(output, "Object is a string : ");
1135: xmlDebugDumpString(output, cur->stringval);
1136: fprintf(output, "\n");
1137: break;
1138: case XPATH_POINT:
1139: fprintf(output, "Object is a point : index %d in node", cur->index);
1140: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
1141: fprintf(output, "\n");
1142: break;
1143: case XPATH_RANGE:
1144: if ((cur->user2 == NULL) ||
1145: ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
1146: fprintf(output, "Object is a collapsed range :\n");
1147: fprintf(output, "%s", shift);
1148: if (cur->index >= 0)
1149: fprintf(output, "index %d in ", cur->index);
1150: fprintf(output, "node\n");
1151: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1152: depth + 1);
1153: } else {
1154: fprintf(output, "Object is a range :\n");
1155: fprintf(output, "%s", shift);
1156: fprintf(output, "From ");
1157: if (cur->index >= 0)
1158: fprintf(output, "index %d in ", cur->index);
1159: fprintf(output, "node\n");
1160: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1161: depth + 1);
1162: fprintf(output, "%s", shift);
1163: fprintf(output, "To ");
1164: if (cur->index2 >= 0)
1165: fprintf(output, "index %d in ", cur->index2);
1166: fprintf(output, "node\n");
1167: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
1168: depth + 1);
1169: fprintf(output, "\n");
1170: }
1171: break;
1172: case XPATH_LOCATIONSET:
1173: #if defined(LIBXML_XPTR_ENABLED)
1174: fprintf(output, "Object is a Location Set:\n");
1175: xmlXPathDebugDumpLocationSet(output,
1176: (xmlLocationSetPtr) cur->user, depth);
1177: #endif
1178: break;
1179: case XPATH_USERS:
1180: fprintf(output, "Object is user defined\n");
1181: break;
1182: }
1183: }
1184:
1185: static void
1186: xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
1187: xmlXPathStepOpPtr op, int depth) {
1188: int i;
1189: char shift[100];
1190:
1191: for (i = 0;((i < depth) && (i < 25));i++)
1192: shift[2 * i] = shift[2 * i + 1] = ' ';
1193: shift[2 * i] = shift[2 * i + 1] = 0;
1194:
1195: fprintf(output, "%s", shift);
1196: if (op == NULL) {
1197: fprintf(output, "Step is NULL\n");
1198: return;
1199: }
1200: switch (op->op) {
1201: case XPATH_OP_END:
1202: fprintf(output, "END"); break;
1203: case XPATH_OP_AND:
1204: fprintf(output, "AND"); break;
1205: case XPATH_OP_OR:
1206: fprintf(output, "OR"); break;
1207: case XPATH_OP_EQUAL:
1208: if (op->value)
1209: fprintf(output, "EQUAL =");
1210: else
1211: fprintf(output, "EQUAL !=");
1212: break;
1213: case XPATH_OP_CMP:
1214: if (op->value)
1215: fprintf(output, "CMP <");
1216: else
1217: fprintf(output, "CMP >");
1218: if (!op->value2)
1219: fprintf(output, "=");
1220: break;
1221: case XPATH_OP_PLUS:
1222: if (op->value == 0)
1223: fprintf(output, "PLUS -");
1224: else if (op->value == 1)
1225: fprintf(output, "PLUS +");
1226: else if (op->value == 2)
1227: fprintf(output, "PLUS unary -");
1228: else if (op->value == 3)
1229: fprintf(output, "PLUS unary - -");
1230: break;
1231: case XPATH_OP_MULT:
1232: if (op->value == 0)
1233: fprintf(output, "MULT *");
1234: else if (op->value == 1)
1235: fprintf(output, "MULT div");
1236: else
1237: fprintf(output, "MULT mod");
1238: break;
1239: case XPATH_OP_UNION:
1240: fprintf(output, "UNION"); break;
1241: case XPATH_OP_ROOT:
1242: fprintf(output, "ROOT"); break;
1243: case XPATH_OP_NODE:
1244: fprintf(output, "NODE"); break;
1245: case XPATH_OP_RESET:
1246: fprintf(output, "RESET"); break;
1247: case XPATH_OP_SORT:
1248: fprintf(output, "SORT"); break;
1249: case XPATH_OP_COLLECT: {
1250: xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
1251: xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
1252: xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
1253: const xmlChar *prefix = op->value4;
1254: const xmlChar *name = op->value5;
1255:
1256: fprintf(output, "COLLECT ");
1257: switch (axis) {
1258: case AXIS_ANCESTOR:
1259: fprintf(output, " 'ancestors' "); break;
1260: case AXIS_ANCESTOR_OR_SELF:
1261: fprintf(output, " 'ancestors-or-self' "); break;
1262: case AXIS_ATTRIBUTE:
1263: fprintf(output, " 'attributes' "); break;
1264: case AXIS_CHILD:
1265: fprintf(output, " 'child' "); break;
1266: case AXIS_DESCENDANT:
1267: fprintf(output, " 'descendant' "); break;
1268: case AXIS_DESCENDANT_OR_SELF:
1269: fprintf(output, " 'descendant-or-self' "); break;
1270: case AXIS_FOLLOWING:
1271: fprintf(output, " 'following' "); break;
1272: case AXIS_FOLLOWING_SIBLING:
1273: fprintf(output, " 'following-siblings' "); break;
1274: case AXIS_NAMESPACE:
1275: fprintf(output, " 'namespace' "); break;
1276: case AXIS_PARENT:
1277: fprintf(output, " 'parent' "); break;
1278: case AXIS_PRECEDING:
1279: fprintf(output, " 'preceding' "); break;
1280: case AXIS_PRECEDING_SIBLING:
1281: fprintf(output, " 'preceding-sibling' "); break;
1282: case AXIS_SELF:
1283: fprintf(output, " 'self' "); break;
1284: }
1285: switch (test) {
1286: case NODE_TEST_NONE:
1287: fprintf(output, "'none' "); break;
1288: case NODE_TEST_TYPE:
1289: fprintf(output, "'type' "); break;
1290: case NODE_TEST_PI:
1291: fprintf(output, "'PI' "); break;
1292: case NODE_TEST_ALL:
1293: fprintf(output, "'all' "); break;
1294: case NODE_TEST_NS:
1295: fprintf(output, "'namespace' "); break;
1296: case NODE_TEST_NAME:
1297: fprintf(output, "'name' "); break;
1298: }
1299: switch (type) {
1300: case NODE_TYPE_NODE:
1301: fprintf(output, "'node' "); break;
1302: case NODE_TYPE_COMMENT:
1303: fprintf(output, "'comment' "); break;
1304: case NODE_TYPE_TEXT:
1305: fprintf(output, "'text' "); break;
1306: case NODE_TYPE_PI:
1307: fprintf(output, "'PI' "); break;
1308: }
1309: if (prefix != NULL)
1310: fprintf(output, "%s:", prefix);
1311: if (name != NULL)
1312: fprintf(output, "%s", (const char *) name);
1313: break;
1314:
1315: }
1316: case XPATH_OP_VALUE: {
1317: xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
1318:
1319: fprintf(output, "ELEM ");
1320: xmlXPathDebugDumpObject(output, object, 0);
1321: goto finish;
1322: }
1323: case XPATH_OP_VARIABLE: {
1324: const xmlChar *prefix = op->value5;
1325: const xmlChar *name = op->value4;
1326:
1327: if (prefix != NULL)
1328: fprintf(output, "VARIABLE %s:%s", prefix, name);
1329: else
1330: fprintf(output, "VARIABLE %s", name);
1331: break;
1332: }
1333: case XPATH_OP_FUNCTION: {
1334: int nbargs = op->value;
1335: const xmlChar *prefix = op->value5;
1336: const xmlChar *name = op->value4;
1337:
1338: if (prefix != NULL)
1339: fprintf(output, "FUNCTION %s:%s(%d args)",
1340: prefix, name, nbargs);
1341: else
1342: fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
1343: break;
1344: }
1345: case XPATH_OP_ARG: fprintf(output, "ARG"); break;
1346: case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
1347: case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
1348: #ifdef LIBXML_XPTR_ENABLED
1349: case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
1350: #endif
1351: default:
1352: fprintf(output, "UNKNOWN %d\n", op->op); return;
1353: }
1354: fprintf(output, "\n");
1355: finish:
1356: if (op->ch1 >= 0)
1357: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
1358: if (op->ch2 >= 0)
1359: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
1360: }
1361:
1362: /**
1363: * xmlXPathDebugDumpCompExpr:
1364: * @output: the FILE * for the output
1365: * @comp: the precompiled XPath expression
1366: * @depth: the indentation level.
1367: *
1368: * Dumps the tree of the compiled XPath expression.
1369: */
1370: void
1371: xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
1372: int depth) {
1373: int i;
1374: char shift[100];
1375:
1376: if ((output == NULL) || (comp == NULL)) return;
1377:
1378: for (i = 0;((i < depth) && (i < 25));i++)
1379: shift[2 * i] = shift[2 * i + 1] = ' ';
1380: shift[2 * i] = shift[2 * i + 1] = 0;
1381:
1382: fprintf(output, "%s", shift);
1383:
1384: fprintf(output, "Compiled Expression : %d elements\n",
1385: comp->nbStep);
1386: i = comp->last;
1387: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
1388: }
1389:
1390: #ifdef XP_DEBUG_OBJ_USAGE
1391:
1392: /*
1393: * XPath object usage related debugging variables.
1394: */
1395: static int xmlXPathDebugObjCounterUndefined = 0;
1396: static int xmlXPathDebugObjCounterNodeset = 0;
1397: static int xmlXPathDebugObjCounterBool = 0;
1398: static int xmlXPathDebugObjCounterNumber = 0;
1399: static int xmlXPathDebugObjCounterString = 0;
1400: static int xmlXPathDebugObjCounterPoint = 0;
1401: static int xmlXPathDebugObjCounterRange = 0;
1402: static int xmlXPathDebugObjCounterLocset = 0;
1403: static int xmlXPathDebugObjCounterUsers = 0;
1404: static int xmlXPathDebugObjCounterXSLTTree = 0;
1405: static int xmlXPathDebugObjCounterAll = 0;
1406:
1407: static int xmlXPathDebugObjTotalUndefined = 0;
1408: static int xmlXPathDebugObjTotalNodeset = 0;
1409: static int xmlXPathDebugObjTotalBool = 0;
1410: static int xmlXPathDebugObjTotalNumber = 0;
1411: static int xmlXPathDebugObjTotalString = 0;
1412: static int xmlXPathDebugObjTotalPoint = 0;
1413: static int xmlXPathDebugObjTotalRange = 0;
1414: static int xmlXPathDebugObjTotalLocset = 0;
1415: static int xmlXPathDebugObjTotalUsers = 0;
1416: static int xmlXPathDebugObjTotalXSLTTree = 0;
1417: static int xmlXPathDebugObjTotalAll = 0;
1418:
1419: static int xmlXPathDebugObjMaxUndefined = 0;
1420: static int xmlXPathDebugObjMaxNodeset = 0;
1421: static int xmlXPathDebugObjMaxBool = 0;
1422: static int xmlXPathDebugObjMaxNumber = 0;
1423: static int xmlXPathDebugObjMaxString = 0;
1424: static int xmlXPathDebugObjMaxPoint = 0;
1425: static int xmlXPathDebugObjMaxRange = 0;
1426: static int xmlXPathDebugObjMaxLocset = 0;
1427: static int xmlXPathDebugObjMaxUsers = 0;
1428: static int xmlXPathDebugObjMaxXSLTTree = 0;
1429: static int xmlXPathDebugObjMaxAll = 0;
1430:
1431: /* REVISIT TODO: Make this static when committing */
1432: static void
1433: xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
1434: {
1435: if (ctxt != NULL) {
1436: if (ctxt->cache != NULL) {
1437: xmlXPathContextCachePtr cache =
1438: (xmlXPathContextCachePtr) ctxt->cache;
1439:
1440: cache->dbgCachedAll = 0;
1441: cache->dbgCachedNodeset = 0;
1442: cache->dbgCachedString = 0;
1443: cache->dbgCachedBool = 0;
1444: cache->dbgCachedNumber = 0;
1445: cache->dbgCachedPoint = 0;
1446: cache->dbgCachedRange = 0;
1447: cache->dbgCachedLocset = 0;
1448: cache->dbgCachedUsers = 0;
1449: cache->dbgCachedXSLTTree = 0;
1450: cache->dbgCachedUndefined = 0;
1451:
1452: cache->dbgReusedAll = 0;
1453: cache->dbgReusedNodeset = 0;
1454: cache->dbgReusedString = 0;
1455: cache->dbgReusedBool = 0;
1456: cache->dbgReusedNumber = 0;
1457: cache->dbgReusedPoint = 0;
1458: cache->dbgReusedRange = 0;
1459: cache->dbgReusedLocset = 0;
1460: cache->dbgReusedUsers = 0;
1461: cache->dbgReusedXSLTTree = 0;
1462: cache->dbgReusedUndefined = 0;
1463: }
1464: }
1465:
1466: xmlXPathDebugObjCounterUndefined = 0;
1467: xmlXPathDebugObjCounterNodeset = 0;
1468: xmlXPathDebugObjCounterBool = 0;
1469: xmlXPathDebugObjCounterNumber = 0;
1470: xmlXPathDebugObjCounterString = 0;
1471: xmlXPathDebugObjCounterPoint = 0;
1472: xmlXPathDebugObjCounterRange = 0;
1473: xmlXPathDebugObjCounterLocset = 0;
1474: xmlXPathDebugObjCounterUsers = 0;
1475: xmlXPathDebugObjCounterXSLTTree = 0;
1476: xmlXPathDebugObjCounterAll = 0;
1477:
1478: xmlXPathDebugObjTotalUndefined = 0;
1479: xmlXPathDebugObjTotalNodeset = 0;
1480: xmlXPathDebugObjTotalBool = 0;
1481: xmlXPathDebugObjTotalNumber = 0;
1482: xmlXPathDebugObjTotalString = 0;
1483: xmlXPathDebugObjTotalPoint = 0;
1484: xmlXPathDebugObjTotalRange = 0;
1485: xmlXPathDebugObjTotalLocset = 0;
1486: xmlXPathDebugObjTotalUsers = 0;
1487: xmlXPathDebugObjTotalXSLTTree = 0;
1488: xmlXPathDebugObjTotalAll = 0;
1489:
1490: xmlXPathDebugObjMaxUndefined = 0;
1491: xmlXPathDebugObjMaxNodeset = 0;
1492: xmlXPathDebugObjMaxBool = 0;
1493: xmlXPathDebugObjMaxNumber = 0;
1494: xmlXPathDebugObjMaxString = 0;
1495: xmlXPathDebugObjMaxPoint = 0;
1496: xmlXPathDebugObjMaxRange = 0;
1497: xmlXPathDebugObjMaxLocset = 0;
1498: xmlXPathDebugObjMaxUsers = 0;
1499: xmlXPathDebugObjMaxXSLTTree = 0;
1500: xmlXPathDebugObjMaxAll = 0;
1501:
1502: }
1503:
1504: static void
1505: xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
1506: xmlXPathObjectType objType)
1507: {
1508: int isCached = 0;
1509:
1510: if (ctxt != NULL) {
1511: if (ctxt->cache != NULL) {
1512: xmlXPathContextCachePtr cache =
1513: (xmlXPathContextCachePtr) ctxt->cache;
1514:
1515: isCached = 1;
1516:
1517: cache->dbgReusedAll++;
1518: switch (objType) {
1519: case XPATH_UNDEFINED:
1520: cache->dbgReusedUndefined++;
1521: break;
1522: case XPATH_NODESET:
1523: cache->dbgReusedNodeset++;
1524: break;
1525: case XPATH_BOOLEAN:
1526: cache->dbgReusedBool++;
1527: break;
1528: case XPATH_NUMBER:
1529: cache->dbgReusedNumber++;
1530: break;
1531: case XPATH_STRING:
1532: cache->dbgReusedString++;
1533: break;
1534: case XPATH_POINT:
1535: cache->dbgReusedPoint++;
1536: break;
1537: case XPATH_RANGE:
1538: cache->dbgReusedRange++;
1539: break;
1540: case XPATH_LOCATIONSET:
1541: cache->dbgReusedLocset++;
1542: break;
1543: case XPATH_USERS:
1544: cache->dbgReusedUsers++;
1545: break;
1546: case XPATH_XSLT_TREE:
1547: cache->dbgReusedXSLTTree++;
1548: break;
1549: default:
1550: break;
1551: }
1552: }
1553: }
1554:
1555: switch (objType) {
1556: case XPATH_UNDEFINED:
1557: if (! isCached)
1558: xmlXPathDebugObjTotalUndefined++;
1559: xmlXPathDebugObjCounterUndefined++;
1560: if (xmlXPathDebugObjCounterUndefined >
1561: xmlXPathDebugObjMaxUndefined)
1562: xmlXPathDebugObjMaxUndefined =
1563: xmlXPathDebugObjCounterUndefined;
1564: break;
1565: case XPATH_NODESET:
1566: if (! isCached)
1567: xmlXPathDebugObjTotalNodeset++;
1568: xmlXPathDebugObjCounterNodeset++;
1569: if (xmlXPathDebugObjCounterNodeset >
1570: xmlXPathDebugObjMaxNodeset)
1571: xmlXPathDebugObjMaxNodeset =
1572: xmlXPathDebugObjCounterNodeset;
1573: break;
1574: case XPATH_BOOLEAN:
1575: if (! isCached)
1576: xmlXPathDebugObjTotalBool++;
1577: xmlXPathDebugObjCounterBool++;
1578: if (xmlXPathDebugObjCounterBool >
1579: xmlXPathDebugObjMaxBool)
1580: xmlXPathDebugObjMaxBool =
1581: xmlXPathDebugObjCounterBool;
1582: break;
1583: case XPATH_NUMBER:
1584: if (! isCached)
1585: xmlXPathDebugObjTotalNumber++;
1586: xmlXPathDebugObjCounterNumber++;
1587: if (xmlXPathDebugObjCounterNumber >
1588: xmlXPathDebugObjMaxNumber)
1589: xmlXPathDebugObjMaxNumber =
1590: xmlXPathDebugObjCounterNumber;
1591: break;
1592: case XPATH_STRING:
1593: if (! isCached)
1594: xmlXPathDebugObjTotalString++;
1595: xmlXPathDebugObjCounterString++;
1596: if (xmlXPathDebugObjCounterString >
1597: xmlXPathDebugObjMaxString)
1598: xmlXPathDebugObjMaxString =
1599: xmlXPathDebugObjCounterString;
1600: break;
1601: case XPATH_POINT:
1602: if (! isCached)
1603: xmlXPathDebugObjTotalPoint++;
1604: xmlXPathDebugObjCounterPoint++;
1605: if (xmlXPathDebugObjCounterPoint >
1606: xmlXPathDebugObjMaxPoint)
1607: xmlXPathDebugObjMaxPoint =
1608: xmlXPathDebugObjCounterPoint;
1609: break;
1610: case XPATH_RANGE:
1611: if (! isCached)
1612: xmlXPathDebugObjTotalRange++;
1613: xmlXPathDebugObjCounterRange++;
1614: if (xmlXPathDebugObjCounterRange >
1615: xmlXPathDebugObjMaxRange)
1616: xmlXPathDebugObjMaxRange =
1617: xmlXPathDebugObjCounterRange;
1618: break;
1619: case XPATH_LOCATIONSET:
1620: if (! isCached)
1621: xmlXPathDebugObjTotalLocset++;
1622: xmlXPathDebugObjCounterLocset++;
1623: if (xmlXPathDebugObjCounterLocset >
1624: xmlXPathDebugObjMaxLocset)
1625: xmlXPathDebugObjMaxLocset =
1626: xmlXPathDebugObjCounterLocset;
1627: break;
1628: case XPATH_USERS:
1629: if (! isCached)
1630: xmlXPathDebugObjTotalUsers++;
1631: xmlXPathDebugObjCounterUsers++;
1632: if (xmlXPathDebugObjCounterUsers >
1633: xmlXPathDebugObjMaxUsers)
1634: xmlXPathDebugObjMaxUsers =
1635: xmlXPathDebugObjCounterUsers;
1636: break;
1637: case XPATH_XSLT_TREE:
1638: if (! isCached)
1639: xmlXPathDebugObjTotalXSLTTree++;
1640: xmlXPathDebugObjCounterXSLTTree++;
1641: if (xmlXPathDebugObjCounterXSLTTree >
1642: xmlXPathDebugObjMaxXSLTTree)
1643: xmlXPathDebugObjMaxXSLTTree =
1644: xmlXPathDebugObjCounterXSLTTree;
1645: break;
1646: default:
1647: break;
1648: }
1649: if (! isCached)
1650: xmlXPathDebugObjTotalAll++;
1651: xmlXPathDebugObjCounterAll++;
1652: if (xmlXPathDebugObjCounterAll >
1653: xmlXPathDebugObjMaxAll)
1654: xmlXPathDebugObjMaxAll =
1655: xmlXPathDebugObjCounterAll;
1656: }
1657:
1658: static void
1659: xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
1660: xmlXPathObjectType objType)
1661: {
1662: int isCached = 0;
1663:
1664: if (ctxt != NULL) {
1665: if (ctxt->cache != NULL) {
1666: xmlXPathContextCachePtr cache =
1667: (xmlXPathContextCachePtr) ctxt->cache;
1668:
1669: isCached = 1;
1670:
1671: cache->dbgCachedAll++;
1672: switch (objType) {
1673: case XPATH_UNDEFINED:
1674: cache->dbgCachedUndefined++;
1675: break;
1676: case XPATH_NODESET:
1677: cache->dbgCachedNodeset++;
1678: break;
1679: case XPATH_BOOLEAN:
1680: cache->dbgCachedBool++;
1681: break;
1682: case XPATH_NUMBER:
1683: cache->dbgCachedNumber++;
1684: break;
1685: case XPATH_STRING:
1686: cache->dbgCachedString++;
1687: break;
1688: case XPATH_POINT:
1689: cache->dbgCachedPoint++;
1690: break;
1691: case XPATH_RANGE:
1692: cache->dbgCachedRange++;
1693: break;
1694: case XPATH_LOCATIONSET:
1695: cache->dbgCachedLocset++;
1696: break;
1697: case XPATH_USERS:
1698: cache->dbgCachedUsers++;
1699: break;
1700: case XPATH_XSLT_TREE:
1701: cache->dbgCachedXSLTTree++;
1702: break;
1703: default:
1704: break;
1705: }
1706:
1707: }
1708: }
1709: switch (objType) {
1710: case XPATH_UNDEFINED:
1711: xmlXPathDebugObjCounterUndefined--;
1712: break;
1713: case XPATH_NODESET:
1714: xmlXPathDebugObjCounterNodeset--;
1715: break;
1716: case XPATH_BOOLEAN:
1717: xmlXPathDebugObjCounterBool--;
1718: break;
1719: case XPATH_NUMBER:
1720: xmlXPathDebugObjCounterNumber--;
1721: break;
1722: case XPATH_STRING:
1723: xmlXPathDebugObjCounterString--;
1724: break;
1725: case XPATH_POINT:
1726: xmlXPathDebugObjCounterPoint--;
1727: break;
1728: case XPATH_RANGE:
1729: xmlXPathDebugObjCounterRange--;
1730: break;
1731: case XPATH_LOCATIONSET:
1732: xmlXPathDebugObjCounterLocset--;
1733: break;
1734: case XPATH_USERS:
1735: xmlXPathDebugObjCounterUsers--;
1736: break;
1737: case XPATH_XSLT_TREE:
1738: xmlXPathDebugObjCounterXSLTTree--;
1739: break;
1740: default:
1741: break;
1742: }
1743: xmlXPathDebugObjCounterAll--;
1744: }
1745:
1746: /* REVISIT TODO: Make this static when committing */
1747: static void
1748: xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
1749: {
1750: int reqAll, reqNodeset, reqString, reqBool, reqNumber,
1751: reqXSLTTree, reqUndefined;
1752: int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
1753: caNumber = 0, caXSLTTree = 0, caUndefined = 0;
1754: int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
1755: reNumber = 0, reXSLTTree = 0, reUndefined = 0;
1756: int leftObjs = xmlXPathDebugObjCounterAll;
1757:
1758: reqAll = xmlXPathDebugObjTotalAll;
1759: reqNodeset = xmlXPathDebugObjTotalNodeset;
1760: reqString = xmlXPathDebugObjTotalString;
1761: reqBool = xmlXPathDebugObjTotalBool;
1762: reqNumber = xmlXPathDebugObjTotalNumber;
1763: reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
1764: reqUndefined = xmlXPathDebugObjTotalUndefined;
1765:
1766: printf("# XPath object usage:\n");
1767:
1768: if (ctxt != NULL) {
1769: if (ctxt->cache != NULL) {
1770: xmlXPathContextCachePtr cache =
1771: (xmlXPathContextCachePtr) ctxt->cache;
1772:
1773: reAll = cache->dbgReusedAll;
1774: reqAll += reAll;
1775: reNodeset = cache->dbgReusedNodeset;
1776: reqNodeset += reNodeset;
1777: reString = cache->dbgReusedString;
1778: reqString += reString;
1779: reBool = cache->dbgReusedBool;
1780: reqBool += reBool;
1781: reNumber = cache->dbgReusedNumber;
1782: reqNumber += reNumber;
1783: reXSLTTree = cache->dbgReusedXSLTTree;
1784: reqXSLTTree += reXSLTTree;
1785: reUndefined = cache->dbgReusedUndefined;
1786: reqUndefined += reUndefined;
1787:
1788: caAll = cache->dbgCachedAll;
1789: caBool = cache->dbgCachedBool;
1790: caNodeset = cache->dbgCachedNodeset;
1791: caString = cache->dbgCachedString;
1792: caNumber = cache->dbgCachedNumber;
1793: caXSLTTree = cache->dbgCachedXSLTTree;
1794: caUndefined = cache->dbgCachedUndefined;
1795:
1796: if (cache->nodesetObjs)
1797: leftObjs -= cache->nodesetObjs->number;
1798: if (cache->stringObjs)
1799: leftObjs -= cache->stringObjs->number;
1800: if (cache->booleanObjs)
1801: leftObjs -= cache->booleanObjs->number;
1802: if (cache->numberObjs)
1803: leftObjs -= cache->numberObjs->number;
1804: if (cache->miscObjs)
1805: leftObjs -= cache->miscObjs->number;
1806: }
1807: }
1808:
1809: printf("# all\n");
1810: printf("# total : %d\n", reqAll);
1811: printf("# left : %d\n", leftObjs);
1812: printf("# created: %d\n", xmlXPathDebugObjTotalAll);
1813: printf("# reused : %d\n", reAll);
1814: printf("# max : %d\n", xmlXPathDebugObjMaxAll);
1815:
1816: printf("# node-sets\n");
1817: printf("# total : %d\n", reqNodeset);
1818: printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
1819: printf("# reused : %d\n", reNodeset);
1820: printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
1821:
1822: printf("# strings\n");
1823: printf("# total : %d\n", reqString);
1824: printf("# created: %d\n", xmlXPathDebugObjTotalString);
1825: printf("# reused : %d\n", reString);
1826: printf("# max : %d\n", xmlXPathDebugObjMaxString);
1827:
1828: printf("# booleans\n");
1829: printf("# total : %d\n", reqBool);
1830: printf("# created: %d\n", xmlXPathDebugObjTotalBool);
1831: printf("# reused : %d\n", reBool);
1832: printf("# max : %d\n", xmlXPathDebugObjMaxBool);
1833:
1834: printf("# numbers\n");
1835: printf("# total : %d\n", reqNumber);
1836: printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
1837: printf("# reused : %d\n", reNumber);
1838: printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
1839:
1840: printf("# XSLT result tree fragments\n");
1841: printf("# total : %d\n", reqXSLTTree);
1842: printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
1843: printf("# reused : %d\n", reXSLTTree);
1844: printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
1845:
1846: printf("# undefined\n");
1847: printf("# total : %d\n", reqUndefined);
1848: printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
1849: printf("# reused : %d\n", reUndefined);
1850: printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
1851:
1852: }
1853:
1854: #endif /* XP_DEBUG_OBJ_USAGE */
1855:
1856: #endif /* LIBXML_DEBUG_ENABLED */
1857:
1858: /************************************************************************
1859: * *
1860: * XPath object caching *
1861: * *
1862: ************************************************************************/
1863:
1864: /**
1865: * xmlXPathNewCache:
1866: *
1867: * Create a new object cache
1868: *
1869: * Returns the xmlXPathCache just allocated.
1870: */
1871: static xmlXPathContextCachePtr
1872: xmlXPathNewCache(void)
1873: {
1874: xmlXPathContextCachePtr ret;
1875:
1876: ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
1877: if (ret == NULL) {
1878: xmlXPathErrMemory(NULL, "creating object cache\n");
1879: return(NULL);
1880: }
1881: memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
1882: ret->maxNodeset = 100;
1883: ret->maxString = 100;
1884: ret->maxBoolean = 100;
1885: ret->maxNumber = 100;
1886: ret->maxMisc = 100;
1887: return(ret);
1888: }
1889:
1890: static void
1891: xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
1892: {
1893: int i;
1894: xmlXPathObjectPtr obj;
1895:
1896: if (list == NULL)
1897: return;
1898:
1899: for (i = 0; i < list->number; i++) {
1900: obj = list->items[i];
1901: /*
1902: * Note that it is already assured that we don't need to
1903: * look out for namespace nodes in the node-set.
1904: */
1905: if (obj->nodesetval != NULL) {
1906: if (obj->nodesetval->nodeTab != NULL)
1907: xmlFree(obj->nodesetval->nodeTab);
1908: xmlFree(obj->nodesetval);
1909: }
1910: xmlFree(obj);
1911: #ifdef XP_DEBUG_OBJ_USAGE
1912: xmlXPathDebugObjCounterAll--;
1913: #endif
1914: }
1915: xmlPointerListFree(list);
1916: }
1917:
1918: static void
1919: xmlXPathFreeCache(xmlXPathContextCachePtr cache)
1920: {
1921: if (cache == NULL)
1922: return;
1923: if (cache->nodesetObjs)
1924: xmlXPathCacheFreeObjectList(cache->nodesetObjs);
1925: if (cache->stringObjs)
1926: xmlXPathCacheFreeObjectList(cache->stringObjs);
1927: if (cache->booleanObjs)
1928: xmlXPathCacheFreeObjectList(cache->booleanObjs);
1929: if (cache->numberObjs)
1930: xmlXPathCacheFreeObjectList(cache->numberObjs);
1931: if (cache->miscObjs)
1932: xmlXPathCacheFreeObjectList(cache->miscObjs);
1933: xmlFree(cache);
1934: }
1935:
1936: /**
1937: * xmlXPathContextSetCache:
1938: *
1939: * @ctxt: the XPath context
1940: * @active: enables/disables (creates/frees) the cache
1941: * @value: a value with semantics dependant on @options
1942: * @options: options (currently only the value 0 is used)
1943: *
1944: * Creates/frees an object cache on the XPath context.
1945: * If activates XPath objects (xmlXPathObject) will be cached internally
1946: * to be reused.
1947: * @options:
1948: * 0: This will set the XPath object caching:
1949: * @value:
1950: * This will set the maximum number of XPath objects
1951: * to be cached per slot
1952: * There are 5 slots for: node-set, string, number, boolean, and
1953: * misc objects. Use <0 for the default number (100).
1954: * Other values for @options have currently no effect.
1955: *
1956: * Returns 0 if the setting succeeded, and -1 on API or internal errors.
1957: */
1958: int
1959: xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
1960: int active,
1961: int value,
1962: int options)
1963: {
1964: if (ctxt == NULL)
1965: return(-1);
1966: if (active) {
1967: xmlXPathContextCachePtr cache;
1968:
1969: if (ctxt->cache == NULL) {
1970: ctxt->cache = xmlXPathNewCache();
1971: if (ctxt->cache == NULL)
1972: return(-1);
1973: }
1974: cache = (xmlXPathContextCachePtr) ctxt->cache;
1975: if (options == 0) {
1976: if (value < 0)
1977: value = 100;
1978: cache->maxNodeset = value;
1979: cache->maxString = value;
1980: cache->maxNumber = value;
1981: cache->maxBoolean = value;
1982: cache->maxMisc = value;
1983: }
1984: } else if (ctxt->cache != NULL) {
1985: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
1986: ctxt->cache = NULL;
1987: }
1988: return(0);
1989: }
1990:
1991: /**
1992: * xmlXPathCacheWrapNodeSet:
1993: * @ctxt: the XPath context
1994: * @val: the NodePtr value
1995: *
1996: * This is the cached version of xmlXPathWrapNodeSet().
1997: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1998: *
1999: * Returns the created or reused object.
2000: */
2001: static xmlXPathObjectPtr
2002: xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
2003: {
2004: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2005: xmlXPathContextCachePtr cache =
2006: (xmlXPathContextCachePtr) ctxt->cache;
2007:
2008: if ((cache->miscObjs != NULL) &&
2009: (cache->miscObjs->number != 0))
2010: {
2011: xmlXPathObjectPtr ret;
2012:
2013: ret = (xmlXPathObjectPtr)
2014: cache->miscObjs->items[--cache->miscObjs->number];
2015: ret->type = XPATH_NODESET;
2016: ret->nodesetval = val;
2017: #ifdef XP_DEBUG_OBJ_USAGE
2018: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2019: #endif
2020: return(ret);
2021: }
2022: }
2023:
2024: return(xmlXPathWrapNodeSet(val));
2025:
2026: }
2027:
2028: /**
2029: * xmlXPathCacheWrapString:
2030: * @ctxt: the XPath context
2031: * @val: the xmlChar * value
2032: *
2033: * This is the cached version of xmlXPathWrapString().
2034: * Wraps the @val string into an XPath object.
2035: *
2036: * Returns the created or reused object.
2037: */
2038: static xmlXPathObjectPtr
2039: xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
2040: {
2041: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
2042: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2043:
2044: if ((cache->stringObjs != NULL) &&
2045: (cache->stringObjs->number != 0))
2046: {
2047:
2048: xmlXPathObjectPtr ret;
2049:
2050: ret = (xmlXPathObjectPtr)
2051: cache->stringObjs->items[--cache->stringObjs->number];
2052: ret->type = XPATH_STRING;
2053: ret->stringval = val;
2054: #ifdef XP_DEBUG_OBJ_USAGE
2055: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2056: #endif
2057: return(ret);
2058: } else if ((cache->miscObjs != NULL) &&
2059: (cache->miscObjs->number != 0))
2060: {
2061: xmlXPathObjectPtr ret;
2062: /*
2063: * Fallback to misc-cache.
2064: */
2065: ret = (xmlXPathObjectPtr)
2066: cache->miscObjs->items[--cache->miscObjs->number];
2067:
2068: ret->type = XPATH_STRING;
2069: ret->stringval = val;
2070: #ifdef XP_DEBUG_OBJ_USAGE
2071: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2072: #endif
2073: return(ret);
2074: }
2075: }
2076: return(xmlXPathWrapString(val));
2077: }
2078:
2079: /**
2080: * xmlXPathCacheNewNodeSet:
2081: * @ctxt: the XPath context
2082: * @val: the NodePtr value
2083: *
2084: * This is the cached version of xmlXPathNewNodeSet().
2085: * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
2086: * it with the single Node @val
2087: *
2088: * Returns the created or reused object.
2089: */
2090: static xmlXPathObjectPtr
2091: xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
2092: {
2093: if ((ctxt != NULL) && (ctxt->cache)) {
2094: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2095:
2096: if ((cache->nodesetObjs != NULL) &&
2097: (cache->nodesetObjs->number != 0))
2098: {
2099: xmlXPathObjectPtr ret;
2100: /*
2101: * Use the nodset-cache.
2102: */
2103: ret = (xmlXPathObjectPtr)
2104: cache->nodesetObjs->items[--cache->nodesetObjs->number];
2105: ret->type = XPATH_NODESET;
2106: ret->boolval = 0;
2107: if (val) {
2108: if ((ret->nodesetval->nodeMax == 0) ||
2109: (val->type == XML_NAMESPACE_DECL))
2110: {
2111: xmlXPathNodeSetAddUnique(ret->nodesetval, val);
2112: } else {
2113: ret->nodesetval->nodeTab[0] = val;
2114: ret->nodesetval->nodeNr = 1;
2115: }
2116: }
2117: #ifdef XP_DEBUG_OBJ_USAGE
2118: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2119: #endif
2120: return(ret);
2121: } else if ((cache->miscObjs != NULL) &&
2122: (cache->miscObjs->number != 0))
2123: {
2124: xmlXPathObjectPtr ret;
2125: /*
2126: * Fallback to misc-cache.
2127: */
2128:
2129: ret = (xmlXPathObjectPtr)
2130: cache->miscObjs->items[--cache->miscObjs->number];
2131:
2132: ret->type = XPATH_NODESET;
2133: ret->boolval = 0;
2134: ret->nodesetval = xmlXPathNodeSetCreate(val);
1.1.1.3 ! misho 2135: if (ret->nodesetval == NULL) {
! 2136: ctxt->lastError.domain = XML_FROM_XPATH;
! 2137: ctxt->lastError.code = XML_ERR_NO_MEMORY;
! 2138: return(NULL);
! 2139: }
1.1 misho 2140: #ifdef XP_DEBUG_OBJ_USAGE
2141: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2142: #endif
2143: return(ret);
2144: }
2145: }
2146: return(xmlXPathNewNodeSet(val));
2147: }
2148:
2149: /**
2150: * xmlXPathCacheNewCString:
2151: * @ctxt: the XPath context
2152: * @val: the char * value
2153: *
2154: * This is the cached version of xmlXPathNewCString().
2155: * Acquire an xmlXPathObjectPtr of type string and of value @val
2156: *
2157: * Returns the created or reused object.
2158: */
2159: static xmlXPathObjectPtr
2160: xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
2161: {
2162: if ((ctxt != NULL) && (ctxt->cache)) {
2163: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2164:
2165: if ((cache->stringObjs != NULL) &&
2166: (cache->stringObjs->number != 0))
2167: {
2168: xmlXPathObjectPtr ret;
2169:
2170: ret = (xmlXPathObjectPtr)
2171: cache->stringObjs->items[--cache->stringObjs->number];
2172:
2173: ret->type = XPATH_STRING;
2174: ret->stringval = xmlStrdup(BAD_CAST val);
2175: #ifdef XP_DEBUG_OBJ_USAGE
2176: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2177: #endif
2178: return(ret);
2179: } else if ((cache->miscObjs != NULL) &&
2180: (cache->miscObjs->number != 0))
2181: {
2182: xmlXPathObjectPtr ret;
2183:
2184: ret = (xmlXPathObjectPtr)
2185: cache->miscObjs->items[--cache->miscObjs->number];
2186:
2187: ret->type = XPATH_STRING;
2188: ret->stringval = xmlStrdup(BAD_CAST val);
2189: #ifdef XP_DEBUG_OBJ_USAGE
2190: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2191: #endif
2192: return(ret);
2193: }
2194: }
2195: return(xmlXPathNewCString(val));
2196: }
2197:
2198: /**
2199: * xmlXPathCacheNewString:
2200: * @ctxt: the XPath context
2201: * @val: the xmlChar * value
2202: *
2203: * This is the cached version of xmlXPathNewString().
2204: * Acquire an xmlXPathObjectPtr of type string and of value @val
2205: *
2206: * Returns the created or reused object.
2207: */
2208: static xmlXPathObjectPtr
2209: xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
2210: {
2211: if ((ctxt != NULL) && (ctxt->cache)) {
2212: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2213:
2214: if ((cache->stringObjs != NULL) &&
2215: (cache->stringObjs->number != 0))
2216: {
2217: xmlXPathObjectPtr ret;
2218:
2219: ret = (xmlXPathObjectPtr)
2220: cache->stringObjs->items[--cache->stringObjs->number];
2221: ret->type = XPATH_STRING;
2222: if (val != NULL)
2223: ret->stringval = xmlStrdup(val);
2224: else
2225: ret->stringval = xmlStrdup((const xmlChar *)"");
2226: #ifdef XP_DEBUG_OBJ_USAGE
2227: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2228: #endif
2229: return(ret);
2230: } else if ((cache->miscObjs != NULL) &&
2231: (cache->miscObjs->number != 0))
2232: {
2233: xmlXPathObjectPtr ret;
2234:
2235: ret = (xmlXPathObjectPtr)
2236: cache->miscObjs->items[--cache->miscObjs->number];
2237:
2238: ret->type = XPATH_STRING;
2239: if (val != NULL)
2240: ret->stringval = xmlStrdup(val);
2241: else
2242: ret->stringval = xmlStrdup((const xmlChar *)"");
2243: #ifdef XP_DEBUG_OBJ_USAGE
2244: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2245: #endif
2246: return(ret);
2247: }
2248: }
2249: return(xmlXPathNewString(val));
2250: }
2251:
2252: /**
2253: * xmlXPathCacheNewBoolean:
2254: * @ctxt: the XPath context
2255: * @val: the boolean value
2256: *
2257: * This is the cached version of xmlXPathNewBoolean().
2258: * Acquires an xmlXPathObjectPtr of type boolean and of value @val
2259: *
2260: * Returns the created or reused object.
2261: */
2262: static xmlXPathObjectPtr
2263: xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
2264: {
2265: if ((ctxt != NULL) && (ctxt->cache)) {
2266: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2267:
2268: if ((cache->booleanObjs != NULL) &&
2269: (cache->booleanObjs->number != 0))
2270: {
2271: xmlXPathObjectPtr ret;
2272:
2273: ret = (xmlXPathObjectPtr)
2274: cache->booleanObjs->items[--cache->booleanObjs->number];
2275: ret->type = XPATH_BOOLEAN;
2276: ret->boolval = (val != 0);
2277: #ifdef XP_DEBUG_OBJ_USAGE
2278: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2279: #endif
2280: return(ret);
2281: } else if ((cache->miscObjs != NULL) &&
2282: (cache->miscObjs->number != 0))
2283: {
2284: xmlXPathObjectPtr ret;
2285:
2286: ret = (xmlXPathObjectPtr)
2287: cache->miscObjs->items[--cache->miscObjs->number];
2288:
2289: ret->type = XPATH_BOOLEAN;
2290: ret->boolval = (val != 0);
2291: #ifdef XP_DEBUG_OBJ_USAGE
2292: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2293: #endif
2294: return(ret);
2295: }
2296: }
2297: return(xmlXPathNewBoolean(val));
2298: }
2299:
2300: /**
2301: * xmlXPathCacheNewFloat:
2302: * @ctxt: the XPath context
2303: * @val: the double value
2304: *
2305: * This is the cached version of xmlXPathNewFloat().
2306: * Acquires an xmlXPathObjectPtr of type double and of value @val
2307: *
2308: * Returns the created or reused object.
2309: */
2310: static xmlXPathObjectPtr
2311: xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
2312: {
2313: if ((ctxt != NULL) && (ctxt->cache)) {
2314: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2315:
2316: if ((cache->numberObjs != NULL) &&
2317: (cache->numberObjs->number != 0))
2318: {
2319: xmlXPathObjectPtr ret;
2320:
2321: ret = (xmlXPathObjectPtr)
2322: cache->numberObjs->items[--cache->numberObjs->number];
2323: ret->type = XPATH_NUMBER;
2324: ret->floatval = val;
2325: #ifdef XP_DEBUG_OBJ_USAGE
2326: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2327: #endif
2328: return(ret);
2329: } else if ((cache->miscObjs != NULL) &&
2330: (cache->miscObjs->number != 0))
2331: {
2332: xmlXPathObjectPtr ret;
2333:
2334: ret = (xmlXPathObjectPtr)
2335: cache->miscObjs->items[--cache->miscObjs->number];
2336:
2337: ret->type = XPATH_NUMBER;
2338: ret->floatval = val;
2339: #ifdef XP_DEBUG_OBJ_USAGE
2340: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2341: #endif
2342: return(ret);
2343: }
2344: }
2345: return(xmlXPathNewFloat(val));
2346: }
2347:
2348: /**
2349: * xmlXPathCacheConvertString:
2350: * @ctxt: the XPath context
2351: * @val: an XPath object
2352: *
2353: * This is the cached version of xmlXPathConvertString().
2354: * Converts an existing object to its string() equivalent
2355: *
2356: * Returns a created or reused object, the old one is freed (cached)
2357: * (or the operation is done directly on @val)
2358: */
2359:
2360: static xmlXPathObjectPtr
2361: xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2362: xmlChar *res = NULL;
2363:
2364: if (val == NULL)
2365: return(xmlXPathCacheNewCString(ctxt, ""));
2366:
2367: switch (val->type) {
2368: case XPATH_UNDEFINED:
2369: #ifdef DEBUG_EXPR
2370: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2371: #endif
2372: break;
2373: case XPATH_NODESET:
2374: case XPATH_XSLT_TREE:
2375: res = xmlXPathCastNodeSetToString(val->nodesetval);
2376: break;
2377: case XPATH_STRING:
2378: return(val);
2379: case XPATH_BOOLEAN:
2380: res = xmlXPathCastBooleanToString(val->boolval);
2381: break;
2382: case XPATH_NUMBER:
2383: res = xmlXPathCastNumberToString(val->floatval);
2384: break;
2385: case XPATH_USERS:
2386: case XPATH_POINT:
2387: case XPATH_RANGE:
2388: case XPATH_LOCATIONSET:
2389: TODO;
2390: break;
2391: }
2392: xmlXPathReleaseObject(ctxt, val);
2393: if (res == NULL)
2394: return(xmlXPathCacheNewCString(ctxt, ""));
2395: return(xmlXPathCacheWrapString(ctxt, res));
2396: }
2397:
2398: /**
2399: * xmlXPathCacheObjectCopy:
2400: * @ctxt: the XPath context
2401: * @val: the original object
2402: *
2403: * This is the cached version of xmlXPathObjectCopy().
2404: * Acquire a copy of a given object
2405: *
2406: * Returns a created or reused created object.
2407: */
2408: static xmlXPathObjectPtr
2409: xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
2410: {
2411: if (val == NULL)
2412: return(NULL);
2413:
2414: if (XP_HAS_CACHE(ctxt)) {
2415: switch (val->type) {
2416: case XPATH_NODESET:
2417: return(xmlXPathCacheWrapNodeSet(ctxt,
2418: xmlXPathNodeSetMerge(NULL, val->nodesetval)));
2419: case XPATH_STRING:
2420: return(xmlXPathCacheNewString(ctxt, val->stringval));
2421: case XPATH_BOOLEAN:
2422: return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
2423: case XPATH_NUMBER:
2424: return(xmlXPathCacheNewFloat(ctxt, val->floatval));
2425: default:
2426: break;
2427: }
2428: }
2429: return(xmlXPathObjectCopy(val));
2430: }
2431:
2432: /**
2433: * xmlXPathCacheConvertBoolean:
2434: * @ctxt: the XPath context
2435: * @val: an XPath object
2436: *
2437: * This is the cached version of xmlXPathConvertBoolean().
2438: * Converts an existing object to its boolean() equivalent
2439: *
2440: * Returns a created or reused object, the old one is freed (or the operation
2441: * is done directly on @val)
2442: */
2443: static xmlXPathObjectPtr
2444: xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2445: xmlXPathObjectPtr ret;
2446:
2447: if (val == NULL)
2448: return(xmlXPathCacheNewBoolean(ctxt, 0));
2449: if (val->type == XPATH_BOOLEAN)
2450: return(val);
2451: ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
2452: xmlXPathReleaseObject(ctxt, val);
2453: return(ret);
2454: }
2455:
2456: /**
2457: * xmlXPathCacheConvertNumber:
2458: * @ctxt: the XPath context
2459: * @val: an XPath object
2460: *
2461: * This is the cached version of xmlXPathConvertNumber().
2462: * Converts an existing object to its number() equivalent
2463: *
2464: * Returns a created or reused object, the old one is freed (or the operation
2465: * is done directly on @val)
2466: */
2467: static xmlXPathObjectPtr
2468: xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2469: xmlXPathObjectPtr ret;
2470:
2471: if (val == NULL)
2472: return(xmlXPathCacheNewFloat(ctxt, 0.0));
2473: if (val->type == XPATH_NUMBER)
2474: return(val);
2475: ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
2476: xmlXPathReleaseObject(ctxt, val);
2477: return(ret);
2478: }
2479:
2480: /************************************************************************
2481: * *
2482: * Parser stacks related functions and macros *
2483: * *
2484: ************************************************************************/
2485:
2486: /**
1.1.1.2 misho 2487: * xmlXPathSetFrame:
2488: * @ctxt: an XPath parser context
2489: *
2490: * Set the callee evaluation frame
2491: *
2492: * Returns the previous frame value to be restored once done
2493: */
2494: static int
2495: xmlXPathSetFrame(xmlXPathParserContextPtr ctxt) {
2496: int ret;
2497:
2498: if (ctxt == NULL)
2499: return(0);
2500: ret = ctxt->valueFrame;
2501: ctxt->valueFrame = ctxt->valueNr;
2502: return(ret);
2503: }
2504:
2505: /**
2506: * xmlXPathPopFrame:
2507: * @ctxt: an XPath parser context
2508: * @frame: the previous frame value
2509: *
2510: * Remove the callee evaluation frame
2511: */
2512: static void
2513: xmlXPathPopFrame(xmlXPathParserContextPtr ctxt, int frame) {
2514: if (ctxt == NULL)
2515: return;
2516: if (ctxt->valueNr < ctxt->valueFrame) {
2517: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
2518: }
2519: ctxt->valueFrame = frame;
2520: }
2521:
2522: /**
1.1 misho 2523: * valuePop:
2524: * @ctxt: an XPath evaluation context
2525: *
2526: * Pops the top XPath object from the value stack
2527: *
2528: * Returns the XPath object just removed
2529: */
2530: xmlXPathObjectPtr
2531: valuePop(xmlXPathParserContextPtr ctxt)
2532: {
2533: xmlXPathObjectPtr ret;
2534:
2535: if ((ctxt == NULL) || (ctxt->valueNr <= 0))
2536: return (NULL);
1.1.1.2 misho 2537:
2538: if (ctxt->valueNr <= ctxt->valueFrame) {
2539: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR);
2540: return (NULL);
2541: }
2542:
1.1 misho 2543: ctxt->valueNr--;
2544: if (ctxt->valueNr > 0)
2545: ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
2546: else
2547: ctxt->value = NULL;
2548: ret = ctxt->valueTab[ctxt->valueNr];
2549: ctxt->valueTab[ctxt->valueNr] = NULL;
2550: return (ret);
2551: }
2552: /**
2553: * valuePush:
2554: * @ctxt: an XPath evaluation context
2555: * @value: the XPath object
2556: *
2557: * Pushes a new XPath object on top of the value stack
2558: *
2559: * returns the number of items on the value stack
2560: */
2561: int
2562: valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
2563: {
2564: if ((ctxt == NULL) || (value == NULL)) return(-1);
2565: if (ctxt->valueNr >= ctxt->valueMax) {
2566: xmlXPathObjectPtr *tmp;
2567:
1.1.1.3 ! misho 2568: if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) {
! 2569: xmlXPathErrMemory(NULL, "XPath stack depth limit reached\n");
! 2570: ctxt->error = XPATH_MEMORY_ERROR;
! 2571: return (0);
! 2572: }
1.1 misho 2573: tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
2574: 2 * ctxt->valueMax *
2575: sizeof(ctxt->valueTab[0]));
2576: if (tmp == NULL) {
1.1.1.3 ! misho 2577: xmlXPathErrMemory(NULL, "pushing value\n");
1.1.1.2 misho 2578: ctxt->error = XPATH_MEMORY_ERROR;
1.1 misho 2579: return (0);
2580: }
2581: ctxt->valueMax *= 2;
2582: ctxt->valueTab = tmp;
2583: }
2584: ctxt->valueTab[ctxt->valueNr] = value;
2585: ctxt->value = value;
2586: return (ctxt->valueNr++);
2587: }
2588:
2589: /**
2590: * xmlXPathPopBoolean:
2591: * @ctxt: an XPath parser context
2592: *
2593: * Pops a boolean from the stack, handling conversion if needed.
2594: * Check error with #xmlXPathCheckError.
2595: *
2596: * Returns the boolean
2597: */
2598: int
2599: xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
2600: xmlXPathObjectPtr obj;
2601: int ret;
2602:
2603: obj = valuePop(ctxt);
2604: if (obj == NULL) {
2605: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2606: return(0);
2607: }
2608: if (obj->type != XPATH_BOOLEAN)
2609: ret = xmlXPathCastToBoolean(obj);
2610: else
2611: ret = obj->boolval;
2612: xmlXPathReleaseObject(ctxt->context, obj);
2613: return(ret);
2614: }
2615:
2616: /**
2617: * xmlXPathPopNumber:
2618: * @ctxt: an XPath parser context
2619: *
2620: * Pops a number from the stack, handling conversion if needed.
2621: * Check error with #xmlXPathCheckError.
2622: *
2623: * Returns the number
2624: */
2625: double
2626: xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
2627: xmlXPathObjectPtr obj;
2628: double ret;
2629:
2630: obj = valuePop(ctxt);
2631: if (obj == NULL) {
2632: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2633: return(0);
2634: }
2635: if (obj->type != XPATH_NUMBER)
2636: ret = xmlXPathCastToNumber(obj);
2637: else
2638: ret = obj->floatval;
2639: xmlXPathReleaseObject(ctxt->context, obj);
2640: return(ret);
2641: }
2642:
2643: /**
2644: * xmlXPathPopString:
2645: * @ctxt: an XPath parser context
2646: *
2647: * Pops a string from the stack, handling conversion if needed.
2648: * Check error with #xmlXPathCheckError.
2649: *
2650: * Returns the string
2651: */
2652: xmlChar *
2653: xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
2654: xmlXPathObjectPtr obj;
2655: xmlChar * ret;
2656:
2657: obj = valuePop(ctxt);
2658: if (obj == NULL) {
2659: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2660: return(NULL);
2661: }
2662: ret = xmlXPathCastToString(obj); /* this does required strdup */
2663: /* TODO: needs refactoring somewhere else */
2664: if (obj->stringval == ret)
2665: obj->stringval = NULL;
2666: xmlXPathReleaseObject(ctxt->context, obj);
2667: return(ret);
2668: }
2669:
2670: /**
2671: * xmlXPathPopNodeSet:
2672: * @ctxt: an XPath parser context
2673: *
2674: * Pops a node-set from the stack, handling conversion if needed.
2675: * Check error with #xmlXPathCheckError.
2676: *
2677: * Returns the node-set
2678: */
2679: xmlNodeSetPtr
2680: xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
2681: xmlXPathObjectPtr obj;
2682: xmlNodeSetPtr ret;
2683:
2684: if (ctxt == NULL) return(NULL);
2685: if (ctxt->value == NULL) {
2686: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2687: return(NULL);
2688: }
2689: if (!xmlXPathStackIsNodeSet(ctxt)) {
2690: xmlXPathSetTypeError(ctxt);
2691: return(NULL);
2692: }
2693: obj = valuePop(ctxt);
2694: ret = obj->nodesetval;
2695: #if 0
2696: /* to fix memory leak of not clearing obj->user */
2697: if (obj->boolval && obj->user != NULL)
2698: xmlFreeNodeList((xmlNodePtr) obj->user);
2699: #endif
2700: obj->nodesetval = NULL;
2701: xmlXPathReleaseObject(ctxt->context, obj);
2702: return(ret);
2703: }
2704:
2705: /**
2706: * xmlXPathPopExternal:
2707: * @ctxt: an XPath parser context
2708: *
2709: * Pops an external object from the stack, handling conversion if needed.
2710: * Check error with #xmlXPathCheckError.
2711: *
2712: * Returns the object
2713: */
2714: void *
2715: xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
2716: xmlXPathObjectPtr obj;
2717: void * ret;
2718:
2719: if ((ctxt == NULL) || (ctxt->value == NULL)) {
2720: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2721: return(NULL);
2722: }
2723: if (ctxt->value->type != XPATH_USERS) {
2724: xmlXPathSetTypeError(ctxt);
2725: return(NULL);
2726: }
2727: obj = valuePop(ctxt);
2728: ret = obj->user;
2729: obj->user = NULL;
2730: xmlXPathReleaseObject(ctxt->context, obj);
2731: return(ret);
2732: }
2733:
2734: /*
2735: * Macros for accessing the content. Those should be used only by the parser,
2736: * and not exported.
2737: *
2738: * Dirty macros, i.e. one need to make assumption on the context to use them
2739: *
2740: * CUR_PTR return the current pointer to the xmlChar to be parsed.
2741: * CUR returns the current xmlChar value, i.e. a 8 bit value
2742: * in ISO-Latin or UTF-8.
2743: * This should be used internally by the parser
2744: * only to compare to ASCII values otherwise it would break when
2745: * running with UTF-8 encoding.
2746: * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
2747: * to compare on ASCII based substring.
2748: * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
2749: * strings within the parser.
2750: * CURRENT Returns the current char value, with the full decoding of
2751: * UTF-8 if we are using this mode. It returns an int.
2752: * NEXT Skip to the next character, this does the proper decoding
2753: * in UTF-8 mode. It also pop-up unfinished entities on the fly.
2754: * It returns the pointer to the current xmlChar.
2755: */
2756:
2757: #define CUR (*ctxt->cur)
2758: #define SKIP(val) ctxt->cur += (val)
2759: #define NXT(val) ctxt->cur[(val)]
2760: #define CUR_PTR ctxt->cur
2761: #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
2762:
2763: #define COPY_BUF(l,b,i,v) \
2764: if (l == 1) b[i++] = (xmlChar) v; \
2765: else i += xmlCopyChar(l,&b[i],v)
2766:
2767: #define NEXTL(l) ctxt->cur += l
2768:
2769: #define SKIP_BLANKS \
2770: while (IS_BLANK_CH(*(ctxt->cur))) NEXT
2771:
2772: #define CURRENT (*ctxt->cur)
2773: #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
2774:
2775:
2776: #ifndef DBL_DIG
2777: #define DBL_DIG 16
2778: #endif
2779: #ifndef DBL_EPSILON
2780: #define DBL_EPSILON 1E-9
2781: #endif
2782:
2783: #define UPPER_DOUBLE 1E9
2784: #define LOWER_DOUBLE 1E-5
2785: #define LOWER_DOUBLE_EXP 5
2786:
2787: #define INTEGER_DIGITS DBL_DIG
2788: #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
2789: #define EXPONENT_DIGITS (3 + 2)
2790:
2791: /**
2792: * xmlXPathFormatNumber:
2793: * @number: number to format
2794: * @buffer: output buffer
2795: * @buffersize: size of output buffer
2796: *
2797: * Convert the number into a string representation.
2798: */
2799: static void
2800: xmlXPathFormatNumber(double number, char buffer[], int buffersize)
2801: {
2802: switch (xmlXPathIsInf(number)) {
2803: case 1:
2804: if (buffersize > (int)sizeof("Infinity"))
2805: snprintf(buffer, buffersize, "Infinity");
2806: break;
2807: case -1:
2808: if (buffersize > (int)sizeof("-Infinity"))
2809: snprintf(buffer, buffersize, "-Infinity");
2810: break;
2811: default:
2812: if (xmlXPathIsNaN(number)) {
2813: if (buffersize > (int)sizeof("NaN"))
2814: snprintf(buffer, buffersize, "NaN");
2815: } else if (number == 0 && xmlXPathGetSign(number) != 0) {
2816: snprintf(buffer, buffersize, "0");
2817: } else if (number == ((int) number)) {
2818: char work[30];
2819: char *ptr, *cur;
2820: int value = (int) number;
2821:
2822: ptr = &buffer[0];
2823: if (value == 0) {
2824: *ptr++ = '0';
2825: } else {
2826: snprintf(work, 29, "%d", value);
2827: cur = &work[0];
2828: while ((*cur) && (ptr - buffer < buffersize)) {
2829: *ptr++ = *cur++;
2830: }
2831: }
2832: if (ptr - buffer < buffersize) {
2833: *ptr = 0;
2834: } else if (buffersize > 0) {
2835: ptr--;
2836: *ptr = 0;
2837: }
2838: } else {
2839: /*
2840: For the dimension of work,
2841: DBL_DIG is number of significant digits
2842: EXPONENT is only needed for "scientific notation"
2843: 3 is sign, decimal point, and terminating zero
2844: LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
2845: Note that this dimension is slightly (a few characters)
2846: larger than actually necessary.
2847: */
2848: char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
2849: int integer_place, fraction_place;
2850: char *ptr;
2851: char *after_fraction;
2852: double absolute_value;
2853: int size;
2854:
2855: absolute_value = fabs(number);
2856:
2857: /*
2858: * First choose format - scientific or regular floating point.
2859: * In either case, result is in work, and after_fraction points
2860: * just past the fractional part.
2861: */
2862: if ( ((absolute_value > UPPER_DOUBLE) ||
2863: (absolute_value < LOWER_DOUBLE)) &&
2864: (absolute_value != 0.0) ) {
2865: /* Use scientific notation */
2866: integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
2867: fraction_place = DBL_DIG - 1;
2868: size = snprintf(work, sizeof(work),"%*.*e",
2869: integer_place, fraction_place, number);
2870: while ((size > 0) && (work[size] != 'e')) size--;
2871:
2872: }
2873: else {
2874: /* Use regular notation */
2875: if (absolute_value > 0.0) {
2876: integer_place = (int)log10(absolute_value);
2877: if (integer_place > 0)
2878: fraction_place = DBL_DIG - integer_place - 1;
2879: else
2880: fraction_place = DBL_DIG - integer_place;
2881: } else {
2882: fraction_place = 1;
2883: }
2884: size = snprintf(work, sizeof(work), "%0.*f",
2885: fraction_place, number);
2886: }
2887:
2888: /* Remove fractional trailing zeroes */
2889: after_fraction = work + size;
2890: ptr = after_fraction;
2891: while (*(--ptr) == '0')
2892: ;
2893: if (*ptr != '.')
2894: ptr++;
2895: while ((*ptr++ = *after_fraction++) != 0);
2896:
2897: /* Finally copy result back to caller */
2898: size = strlen(work) + 1;
2899: if (size > buffersize) {
2900: work[buffersize - 1] = 0;
2901: size = buffersize;
2902: }
2903: memmove(buffer, work, size);
2904: }
2905: break;
2906: }
2907: }
2908:
2909:
2910: /************************************************************************
2911: * *
2912: * Routines to handle NodeSets *
2913: * *
2914: ************************************************************************/
2915:
2916: /**
2917: * xmlXPathOrderDocElems:
2918: * @doc: an input document
2919: *
2920: * Call this routine to speed up XPath computation on static documents.
2921: * This stamps all the element nodes with the document order
2922: * Like for line information, the order is kept in the element->content
2923: * field, the value stored is actually - the node number (starting at -1)
2924: * to be able to differentiate from line numbers.
2925: *
2926: * Returns the number of elements found in the document or -1 in case
2927: * of error.
2928: */
2929: long
2930: xmlXPathOrderDocElems(xmlDocPtr doc) {
2931: long count = 0;
2932: xmlNodePtr cur;
2933:
2934: if (doc == NULL)
2935: return(-1);
2936: cur = doc->children;
2937: while (cur != NULL) {
2938: if (cur->type == XML_ELEMENT_NODE) {
2939: cur->content = (void *) (-(++count));
2940: if (cur->children != NULL) {
2941: cur = cur->children;
2942: continue;
2943: }
2944: }
2945: if (cur->next != NULL) {
2946: cur = cur->next;
2947: continue;
2948: }
2949: do {
2950: cur = cur->parent;
2951: if (cur == NULL)
2952: break;
2953: if (cur == (xmlNodePtr) doc) {
2954: cur = NULL;
2955: break;
2956: }
2957: if (cur->next != NULL) {
2958: cur = cur->next;
2959: break;
2960: }
2961: } while (cur != NULL);
2962: }
2963: return(count);
2964: }
2965:
2966: /**
2967: * xmlXPathCmpNodes:
2968: * @node1: the first node
2969: * @node2: the second node
2970: *
2971: * Compare two nodes w.r.t document order
2972: *
2973: * Returns -2 in case of error 1 if first point < second point, 0 if
2974: * it's the same node, -1 otherwise
2975: */
2976: int
2977: xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
2978: int depth1, depth2;
2979: int attr1 = 0, attr2 = 0;
2980: xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
2981: xmlNodePtr cur, root;
2982:
2983: if ((node1 == NULL) || (node2 == NULL))
2984: return(-2);
2985: /*
2986: * a couple of optimizations which will avoid computations in most cases
2987: */
2988: if (node1 == node2) /* trivial case */
2989: return(0);
2990: if (node1->type == XML_ATTRIBUTE_NODE) {
2991: attr1 = 1;
2992: attrNode1 = node1;
2993: node1 = node1->parent;
2994: }
2995: if (node2->type == XML_ATTRIBUTE_NODE) {
2996: attr2 = 1;
2997: attrNode2 = node2;
2998: node2 = node2->parent;
2999: }
3000: if (node1 == node2) {
3001: if (attr1 == attr2) {
3002: /* not required, but we keep attributes in order */
3003: if (attr1 != 0) {
3004: cur = attrNode2->prev;
3005: while (cur != NULL) {
3006: if (cur == attrNode1)
3007: return (1);
3008: cur = cur->prev;
3009: }
3010: return (-1);
3011: }
3012: return(0);
3013: }
3014: if (attr2 == 1)
3015: return(1);
3016: return(-1);
3017: }
3018: if ((node1->type == XML_NAMESPACE_DECL) ||
3019: (node2->type == XML_NAMESPACE_DECL))
3020: return(1);
3021: if (node1 == node2->prev)
3022: return(1);
3023: if (node1 == node2->next)
3024: return(-1);
3025:
3026: /*
3027: * Speedup using document order if availble.
3028: */
3029: if ((node1->type == XML_ELEMENT_NODE) &&
3030: (node2->type == XML_ELEMENT_NODE) &&
3031: (0 > (long) node1->content) &&
3032: (0 > (long) node2->content) &&
3033: (node1->doc == node2->doc)) {
3034: long l1, l2;
3035:
3036: l1 = -((long) node1->content);
3037: l2 = -((long) node2->content);
3038: if (l1 < l2)
3039: return(1);
3040: if (l1 > l2)
3041: return(-1);
3042: }
3043:
3044: /*
3045: * compute depth to root
3046: */
3047: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
3048: if (cur == node1)
3049: return(1);
3050: depth2++;
3051: }
3052: root = cur;
3053: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
3054: if (cur == node2)
3055: return(-1);
3056: depth1++;
3057: }
3058: /*
3059: * Distinct document (or distinct entities :-( ) case.
3060: */
3061: if (root != cur) {
3062: return(-2);
3063: }
3064: /*
3065: * get the nearest common ancestor.
3066: */
3067: while (depth1 > depth2) {
3068: depth1--;
3069: node1 = node1->parent;
3070: }
3071: while (depth2 > depth1) {
3072: depth2--;
3073: node2 = node2->parent;
3074: }
3075: while (node1->parent != node2->parent) {
3076: node1 = node1->parent;
3077: node2 = node2->parent;
3078: /* should not happen but just in case ... */
3079: if ((node1 == NULL) || (node2 == NULL))
3080: return(-2);
3081: }
3082: /*
3083: * Find who's first.
3084: */
3085: if (node1 == node2->prev)
3086: return(1);
3087: if (node1 == node2->next)
3088: return(-1);
3089: /*
3090: * Speedup using document order if availble.
3091: */
3092: if ((node1->type == XML_ELEMENT_NODE) &&
3093: (node2->type == XML_ELEMENT_NODE) &&
3094: (0 > (long) node1->content) &&
3095: (0 > (long) node2->content) &&
3096: (node1->doc == node2->doc)) {
3097: long l1, l2;
3098:
3099: l1 = -((long) node1->content);
3100: l2 = -((long) node2->content);
3101: if (l1 < l2)
3102: return(1);
3103: if (l1 > l2)
3104: return(-1);
3105: }
3106:
3107: for (cur = node1->next;cur != NULL;cur = cur->next)
3108: if (cur == node2)
3109: return(1);
3110: return(-1); /* assume there is no sibling list corruption */
3111: }
3112:
3113: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
3114: /**
3115: * xmlXPathCmpNodesExt:
3116: * @node1: the first node
3117: * @node2: the second node
3118: *
3119: * Compare two nodes w.r.t document order.
3120: * This one is optimized for handling of non-element nodes.
3121: *
3122: * Returns -2 in case of error 1 if first point < second point, 0 if
3123: * it's the same node, -1 otherwise
3124: */
3125: static int
3126: xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
3127: int depth1, depth2;
3128: int misc = 0, precedence1 = 0, precedence2 = 0;
3129: xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
3130: xmlNodePtr cur, root;
3131: long l1, l2;
3132:
3133: if ((node1 == NULL) || (node2 == NULL))
3134: return(-2);
3135:
3136: if (node1 == node2)
3137: return(0);
3138:
3139: /*
3140: * a couple of optimizations which will avoid computations in most cases
3141: */
3142: switch (node1->type) {
3143: case XML_ELEMENT_NODE:
3144: if (node2->type == XML_ELEMENT_NODE) {
3145: if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */
3146: (0 > (long) node2->content) &&
3147: (node1->doc == node2->doc))
3148: {
3149: l1 = -((long) node1->content);
3150: l2 = -((long) node2->content);
3151: if (l1 < l2)
3152: return(1);
3153: if (l1 > l2)
3154: return(-1);
3155: } else
3156: goto turtle_comparison;
3157: }
3158: break;
3159: case XML_ATTRIBUTE_NODE:
3160: precedence1 = 1; /* element is owner */
3161: miscNode1 = node1;
3162: node1 = node1->parent;
3163: misc = 1;
3164: break;
3165: case XML_TEXT_NODE:
3166: case XML_CDATA_SECTION_NODE:
3167: case XML_COMMENT_NODE:
3168: case XML_PI_NODE: {
3169: miscNode1 = node1;
3170: /*
3171: * Find nearest element node.
3172: */
3173: if (node1->prev != NULL) {
3174: do {
3175: node1 = node1->prev;
3176: if (node1->type == XML_ELEMENT_NODE) {
3177: precedence1 = 3; /* element in prev-sibl axis */
3178: break;
3179: }
3180: if (node1->prev == NULL) {
3181: precedence1 = 2; /* element is parent */
3182: /*
3183: * URGENT TODO: Are there any cases, where the
3184: * parent of such a node is not an element node?
3185: */
3186: node1 = node1->parent;
3187: break;
3188: }
3189: } while (1);
3190: } else {
3191: precedence1 = 2; /* element is parent */
3192: node1 = node1->parent;
3193: }
3194: if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
3195: (0 <= (long) node1->content)) {
3196: /*
3197: * Fallback for whatever case.
3198: */
3199: node1 = miscNode1;
3200: precedence1 = 0;
3201: } else
3202: misc = 1;
3203: }
3204: break;
3205: case XML_NAMESPACE_DECL:
3206: /*
3207: * TODO: why do we return 1 for namespace nodes?
3208: */
3209: return(1);
3210: default:
3211: break;
3212: }
3213: switch (node2->type) {
3214: case XML_ELEMENT_NODE:
3215: break;
3216: case XML_ATTRIBUTE_NODE:
3217: precedence2 = 1; /* element is owner */
3218: miscNode2 = node2;
3219: node2 = node2->parent;
3220: misc = 1;
3221: break;
3222: case XML_TEXT_NODE:
3223: case XML_CDATA_SECTION_NODE:
3224: case XML_COMMENT_NODE:
3225: case XML_PI_NODE: {
3226: miscNode2 = node2;
3227: if (node2->prev != NULL) {
3228: do {
3229: node2 = node2->prev;
3230: if (node2->type == XML_ELEMENT_NODE) {
3231: precedence2 = 3; /* element in prev-sibl axis */
3232: break;
3233: }
3234: if (node2->prev == NULL) {
3235: precedence2 = 2; /* element is parent */
3236: node2 = node2->parent;
3237: break;
3238: }
3239: } while (1);
3240: } else {
3241: precedence2 = 2; /* element is parent */
3242: node2 = node2->parent;
3243: }
3244: if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
3245: (0 <= (long) node1->content))
3246: {
3247: node2 = miscNode2;
3248: precedence2 = 0;
3249: } else
3250: misc = 1;
3251: }
3252: break;
3253: case XML_NAMESPACE_DECL:
3254: return(1);
3255: default:
3256: break;
3257: }
3258: if (misc) {
3259: if (node1 == node2) {
3260: if (precedence1 == precedence2) {
3261: /*
3262: * The ugly case; but normally there aren't many
3263: * adjacent non-element nodes around.
3264: */
3265: cur = miscNode2->prev;
3266: while (cur != NULL) {
3267: if (cur == miscNode1)
3268: return(1);
3269: if (cur->type == XML_ELEMENT_NODE)
3270: return(-1);
3271: cur = cur->prev;
3272: }
3273: return (-1);
3274: } else {
3275: /*
3276: * Evaluate based on higher precedence wrt to the element.
3277: * TODO: This assumes attributes are sorted before content.
3278: * Is this 100% correct?
3279: */
3280: if (precedence1 < precedence2)
3281: return(1);
3282: else
3283: return(-1);
3284: }
3285: }
3286: /*
3287: * Special case: One of the helper-elements is contained by the other.
3288: * <foo>
3289: * <node2>
3290: * <node1>Text-1(precedence1 == 2)</node1>
3291: * </node2>
3292: * Text-6(precedence2 == 3)
3293: * </foo>
3294: */
3295: if ((precedence2 == 3) && (precedence1 > 1)) {
3296: cur = node1->parent;
3297: while (cur) {
3298: if (cur == node2)
3299: return(1);
3300: cur = cur->parent;
3301: }
3302: }
3303: if ((precedence1 == 3) && (precedence2 > 1)) {
3304: cur = node2->parent;
3305: while (cur) {
3306: if (cur == node1)
3307: return(-1);
3308: cur = cur->parent;
3309: }
3310: }
3311: }
3312:
3313: /*
3314: * Speedup using document order if availble.
3315: */
3316: if ((node1->type == XML_ELEMENT_NODE) &&
3317: (node2->type == XML_ELEMENT_NODE) &&
3318: (0 > (long) node1->content) &&
3319: (0 > (long) node2->content) &&
3320: (node1->doc == node2->doc)) {
3321:
3322: l1 = -((long) node1->content);
3323: l2 = -((long) node2->content);
3324: if (l1 < l2)
3325: return(1);
3326: if (l1 > l2)
3327: return(-1);
3328: }
3329:
3330: turtle_comparison:
3331:
3332: if (node1 == node2->prev)
3333: return(1);
3334: if (node1 == node2->next)
3335: return(-1);
3336: /*
3337: * compute depth to root
3338: */
3339: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
3340: if (cur == node1)
3341: return(1);
3342: depth2++;
3343: }
3344: root = cur;
3345: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
3346: if (cur == node2)
3347: return(-1);
3348: depth1++;
3349: }
3350: /*
3351: * Distinct document (or distinct entities :-( ) case.
3352: */
3353: if (root != cur) {
3354: return(-2);
3355: }
3356: /*
3357: * get the nearest common ancestor.
3358: */
3359: while (depth1 > depth2) {
3360: depth1--;
3361: node1 = node1->parent;
3362: }
3363: while (depth2 > depth1) {
3364: depth2--;
3365: node2 = node2->parent;
3366: }
3367: while (node1->parent != node2->parent) {
3368: node1 = node1->parent;
3369: node2 = node2->parent;
3370: /* should not happen but just in case ... */
3371: if ((node1 == NULL) || (node2 == NULL))
3372: return(-2);
3373: }
3374: /*
3375: * Find who's first.
3376: */
3377: if (node1 == node2->prev)
3378: return(1);
3379: if (node1 == node2->next)
3380: return(-1);
3381: /*
3382: * Speedup using document order if availble.
3383: */
3384: if ((node1->type == XML_ELEMENT_NODE) &&
3385: (node2->type == XML_ELEMENT_NODE) &&
3386: (0 > (long) node1->content) &&
3387: (0 > (long) node2->content) &&
3388: (node1->doc == node2->doc)) {
3389:
3390: l1 = -((long) node1->content);
3391: l2 = -((long) node2->content);
3392: if (l1 < l2)
3393: return(1);
3394: if (l1 > l2)
3395: return(-1);
3396: }
3397:
3398: for (cur = node1->next;cur != NULL;cur = cur->next)
3399: if (cur == node2)
3400: return(1);
3401: return(-1); /* assume there is no sibling list corruption */
3402: }
3403: #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
3404:
3405: /**
3406: * xmlXPathNodeSetSort:
3407: * @set: the node set
3408: *
3409: * Sort the node set in document order
3410: */
3411: void
3412: xmlXPathNodeSetSort(xmlNodeSetPtr set) {
1.1.1.3 ! misho 3413: #ifndef WITH_TIM_SORT
1.1 misho 3414: int i, j, incr, len;
3415: xmlNodePtr tmp;
1.1.1.3 ! misho 3416: #endif
1.1 misho 3417:
3418: if (set == NULL)
3419: return;
3420:
1.1.1.3 ! misho 3421: #ifndef WITH_TIM_SORT
! 3422: /*
! 3423: * Use the old Shell's sort implementation to sort the node-set
! 3424: * Timsort ought to be quite faster
! 3425: */
1.1 misho 3426: len = set->nodeNr;
3427: for (incr = len / 2; incr > 0; incr /= 2) {
3428: for (i = incr; i < len; i++) {
3429: j = i - incr;
3430: while (j >= 0) {
3431: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
3432: if (xmlXPathCmpNodesExt(set->nodeTab[j],
3433: set->nodeTab[j + incr]) == -1)
3434: #else
3435: if (xmlXPathCmpNodes(set->nodeTab[j],
3436: set->nodeTab[j + incr]) == -1)
3437: #endif
3438: {
3439: tmp = set->nodeTab[j];
3440: set->nodeTab[j] = set->nodeTab[j + incr];
3441: set->nodeTab[j + incr] = tmp;
3442: j -= incr;
3443: } else
3444: break;
3445: }
3446: }
3447: }
1.1.1.3 ! misho 3448: #else /* WITH_TIM_SORT */
! 3449: libxml_domnode_tim_sort(set->nodeTab, set->nodeNr);
! 3450: #endif /* WITH_TIM_SORT */
1.1 misho 3451: }
3452:
3453: #define XML_NODESET_DEFAULT 10
3454: /**
3455: * xmlXPathNodeSetDupNs:
3456: * @node: the parent node of the namespace XPath node
3457: * @ns: the libxml namespace declaration node.
3458: *
3459: * Namespace node in libxml don't match the XPath semantic. In a node set
3460: * the namespace nodes are duplicated and the next pointer is set to the
3461: * parent node in the XPath semantic.
3462: *
3463: * Returns the newly created object.
3464: */
3465: static xmlNodePtr
3466: xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
3467: xmlNsPtr cur;
3468:
3469: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3470: return(NULL);
3471: if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
3472: return((xmlNodePtr) ns);
3473:
3474: /*
3475: * Allocate a new Namespace and fill the fields.
3476: */
3477: cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
3478: if (cur == NULL) {
3479: xmlXPathErrMemory(NULL, "duplicating namespace\n");
3480: return(NULL);
3481: }
3482: memset(cur, 0, sizeof(xmlNs));
3483: cur->type = XML_NAMESPACE_DECL;
3484: if (ns->href != NULL)
3485: cur->href = xmlStrdup(ns->href);
3486: if (ns->prefix != NULL)
3487: cur->prefix = xmlStrdup(ns->prefix);
3488: cur->next = (xmlNsPtr) node;
3489: return((xmlNodePtr) cur);
3490: }
3491:
3492: /**
3493: * xmlXPathNodeSetFreeNs:
3494: * @ns: the XPath namespace node found in a nodeset.
3495: *
3496: * Namespace nodes in libxml don't match the XPath semantic. In a node set
3497: * the namespace nodes are duplicated and the next pointer is set to the
3498: * parent node in the XPath semantic. Check if such a node needs to be freed
3499: */
3500: void
3501: xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
3502: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3503: return;
3504:
3505: if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
3506: if (ns->href != NULL)
3507: xmlFree((xmlChar *)ns->href);
3508: if (ns->prefix != NULL)
3509: xmlFree((xmlChar *)ns->prefix);
3510: xmlFree(ns);
3511: }
3512: }
3513:
3514: /**
3515: * xmlXPathNodeSetCreate:
3516: * @val: an initial xmlNodePtr, or NULL
3517: *
3518: * Create a new xmlNodeSetPtr of type double and of value @val
3519: *
3520: * Returns the newly created object.
3521: */
3522: xmlNodeSetPtr
3523: xmlXPathNodeSetCreate(xmlNodePtr val) {
3524: xmlNodeSetPtr ret;
3525:
3526: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3527: if (ret == NULL) {
3528: xmlXPathErrMemory(NULL, "creating nodeset\n");
3529: return(NULL);
3530: }
3531: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
3532: if (val != NULL) {
3533: ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3534: sizeof(xmlNodePtr));
3535: if (ret->nodeTab == NULL) {
3536: xmlXPathErrMemory(NULL, "creating nodeset\n");
3537: xmlFree(ret);
3538: return(NULL);
3539: }
3540: memset(ret->nodeTab, 0 ,
3541: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3542: ret->nodeMax = XML_NODESET_DEFAULT;
3543: if (val->type == XML_NAMESPACE_DECL) {
3544: xmlNsPtr ns = (xmlNsPtr) val;
3545:
3546: ret->nodeTab[ret->nodeNr++] =
3547: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3548: } else
3549: ret->nodeTab[ret->nodeNr++] = val;
3550: }
3551: return(ret);
3552: }
3553:
3554: /**
3555: * xmlXPathNodeSetCreateSize:
3556: * @size: the initial size of the set
3557: *
3558: * Create a new xmlNodeSetPtr of type double and of value @val
3559: *
3560: * Returns the newly created object.
3561: */
3562: static xmlNodeSetPtr
3563: xmlXPathNodeSetCreateSize(int size) {
3564: xmlNodeSetPtr ret;
3565:
3566: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3567: if (ret == NULL) {
3568: xmlXPathErrMemory(NULL, "creating nodeset\n");
3569: return(NULL);
3570: }
3571: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
3572: if (size < XML_NODESET_DEFAULT)
3573: size = XML_NODESET_DEFAULT;
3574: ret->nodeTab = (xmlNodePtr *) xmlMalloc(size * sizeof(xmlNodePtr));
3575: if (ret->nodeTab == NULL) {
3576: xmlXPathErrMemory(NULL, "creating nodeset\n");
3577: xmlFree(ret);
3578: return(NULL);
3579: }
3580: memset(ret->nodeTab, 0 , size * (size_t) sizeof(xmlNodePtr));
3581: ret->nodeMax = size;
3582: return(ret);
3583: }
3584:
3585: /**
3586: * xmlXPathNodeSetContains:
3587: * @cur: the node-set
3588: * @val: the node
3589: *
3590: * checks whether @cur contains @val
3591: *
3592: * Returns true (1) if @cur contains @val, false (0) otherwise
3593: */
3594: int
3595: xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
3596: int i;
3597:
3598: if ((cur == NULL) || (val == NULL)) return(0);
3599: if (val->type == XML_NAMESPACE_DECL) {
3600: for (i = 0; i < cur->nodeNr; i++) {
3601: if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
3602: xmlNsPtr ns1, ns2;
3603:
3604: ns1 = (xmlNsPtr) val;
3605: ns2 = (xmlNsPtr) cur->nodeTab[i];
3606: if (ns1 == ns2)
3607: return(1);
3608: if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
3609: (xmlStrEqual(ns1->prefix, ns2->prefix)))
3610: return(1);
3611: }
3612: }
3613: } else {
3614: for (i = 0; i < cur->nodeNr; i++) {
3615: if (cur->nodeTab[i] == val)
3616: return(1);
3617: }
3618: }
3619: return(0);
3620: }
3621:
3622: /**
3623: * xmlXPathNodeSetAddNs:
3624: * @cur: the initial node set
3625: * @node: the hosting node
3626: * @ns: a the namespace node
3627: *
3628: * add a new namespace node to an existing NodeSet
1.1.1.3 ! misho 3629: *
! 3630: * Returns 0 in case of success and -1 in case of error
1.1 misho 3631: */
1.1.1.3 ! misho 3632: int
1.1 misho 3633: xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
3634: int i;
3635:
3636:
3637: if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
3638: (ns->type != XML_NAMESPACE_DECL) ||
3639: (node->type != XML_ELEMENT_NODE))
1.1.1.3 ! misho 3640: return(-1);
1.1 misho 3641:
3642: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3643: /*
3644: * prevent duplicates
3645: */
3646: for (i = 0;i < cur->nodeNr;i++) {
3647: if ((cur->nodeTab[i] != NULL) &&
3648: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
3649: (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
3650: (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
1.1.1.3 ! misho 3651: return(0);
1.1 misho 3652: }
3653:
3654: /*
3655: * grow the nodeTab if needed
3656: */
3657: if (cur->nodeMax == 0) {
3658: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3659: sizeof(xmlNodePtr));
3660: if (cur->nodeTab == NULL) {
3661: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3662: return(-1);
1.1 misho 3663: }
3664: memset(cur->nodeTab, 0 ,
3665: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3666: cur->nodeMax = XML_NODESET_DEFAULT;
3667: } else if (cur->nodeNr == cur->nodeMax) {
3668: xmlNodePtr *temp;
3669:
1.1.1.3 ! misho 3670: if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 3671: xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
! 3672: return(-1);
! 3673: }
1.1.1.2 misho 3674: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
1.1 misho 3675: sizeof(xmlNodePtr));
3676: if (temp == NULL) {
3677: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3678: return(-1);
1.1 misho 3679: }
1.1.1.2 misho 3680: cur->nodeMax *= 2;
1.1 misho 3681: cur->nodeTab = temp;
3682: }
3683: cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
1.1.1.3 ! misho 3684: return(0);
1.1 misho 3685: }
3686:
3687: /**
3688: * xmlXPathNodeSetAdd:
3689: * @cur: the initial node set
3690: * @val: a new xmlNodePtr
3691: *
3692: * add a new xmlNodePtr to an existing NodeSet
1.1.1.3 ! misho 3693: *
! 3694: * Returns 0 in case of success, and -1 in case of error
1.1 misho 3695: */
1.1.1.3 ! misho 3696: int
1.1 misho 3697: xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
3698: int i;
3699:
1.1.1.3 ! misho 3700: if ((cur == NULL) || (val == NULL)) return(-1);
1.1 misho 3701:
3702: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3703: /*
3704: * prevent duplcates
3705: */
3706: for (i = 0;i < cur->nodeNr;i++)
1.1.1.3 ! misho 3707: if (cur->nodeTab[i] == val) return(0);
1.1 misho 3708:
3709: /*
3710: * grow the nodeTab if needed
3711: */
3712: if (cur->nodeMax == 0) {
3713: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3714: sizeof(xmlNodePtr));
3715: if (cur->nodeTab == NULL) {
3716: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3717: return(-1);
1.1 misho 3718: }
3719: memset(cur->nodeTab, 0 ,
3720: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3721: cur->nodeMax = XML_NODESET_DEFAULT;
3722: } else if (cur->nodeNr == cur->nodeMax) {
3723: xmlNodePtr *temp;
3724:
1.1.1.3 ! misho 3725: if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 3726: xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
! 3727: return(-1);
! 3728: }
1.1.1.2 misho 3729: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
1.1 misho 3730: sizeof(xmlNodePtr));
3731: if (temp == NULL) {
3732: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3733: return(-1);
1.1 misho 3734: }
1.1.1.2 misho 3735: cur->nodeMax *= 2;
1.1 misho 3736: cur->nodeTab = temp;
3737: }
3738: if (val->type == XML_NAMESPACE_DECL) {
3739: xmlNsPtr ns = (xmlNsPtr) val;
3740:
3741: cur->nodeTab[cur->nodeNr++] =
3742: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3743: } else
3744: cur->nodeTab[cur->nodeNr++] = val;
1.1.1.3 ! misho 3745: return(0);
1.1 misho 3746: }
3747:
3748: /**
3749: * xmlXPathNodeSetAddUnique:
3750: * @cur: the initial node set
3751: * @val: a new xmlNodePtr
3752: *
3753: * add a new xmlNodePtr to an existing NodeSet, optimized version
3754: * when we are sure the node is not already in the set.
1.1.1.3 ! misho 3755: *
! 3756: * Returns 0 in case of success and -1 in case of failure
1.1 misho 3757: */
1.1.1.3 ! misho 3758: int
1.1 misho 3759: xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1.1.1.3 ! misho 3760: if ((cur == NULL) || (val == NULL)) return(-1);
1.1 misho 3761:
3762: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3763: /*
3764: * grow the nodeTab if needed
3765: */
3766: if (cur->nodeMax == 0) {
3767: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3768: sizeof(xmlNodePtr));
3769: if (cur->nodeTab == NULL) {
3770: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3771: return(-1);
1.1 misho 3772: }
3773: memset(cur->nodeTab, 0 ,
3774: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3775: cur->nodeMax = XML_NODESET_DEFAULT;
3776: } else if (cur->nodeNr == cur->nodeMax) {
3777: xmlNodePtr *temp;
3778:
1.1.1.3 ! misho 3779: if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 3780: xmlXPathErrMemory(NULL, "growing nodeset hit limit\n");
! 3781: return(-1);
! 3782: }
1.1.1.2 misho 3783: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
1.1 misho 3784: sizeof(xmlNodePtr));
3785: if (temp == NULL) {
3786: xmlXPathErrMemory(NULL, "growing nodeset\n");
1.1.1.3 ! misho 3787: return(-1);
1.1 misho 3788: }
3789: cur->nodeTab = temp;
1.1.1.2 misho 3790: cur->nodeMax *= 2;
1.1 misho 3791: }
3792: if (val->type == XML_NAMESPACE_DECL) {
3793: xmlNsPtr ns = (xmlNsPtr) val;
3794:
3795: cur->nodeTab[cur->nodeNr++] =
3796: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3797: } else
3798: cur->nodeTab[cur->nodeNr++] = val;
1.1.1.3 ! misho 3799: return(0);
1.1 misho 3800: }
3801:
3802: /**
3803: * xmlXPathNodeSetMerge:
3804: * @val1: the first NodeSet or NULL
3805: * @val2: the second NodeSet
3806: *
3807: * Merges two nodesets, all nodes from @val2 are added to @val1
3808: * if @val1 is NULL, a new set is created and copied from @val2
3809: *
3810: * Returns @val1 once extended or NULL in case of error.
3811: */
3812: xmlNodeSetPtr
3813: xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
3814: int i, j, initNr, skip;
3815: xmlNodePtr n1, n2;
3816:
3817: if (val2 == NULL) return(val1);
3818: if (val1 == NULL) {
3819: val1 = xmlXPathNodeSetCreate(NULL);
3820: if (val1 == NULL)
3821: return (NULL);
3822: #if 0
3823: /*
3824: * TODO: The optimization won't work in every case, since
3825: * those nasty namespace nodes need to be added with
3826: * xmlXPathNodeSetDupNs() to the set; thus a pure
3827: * memcpy is not possible.
3828: * If there was a flag on the nodesetval, indicating that
3829: * some temporary nodes are in, that would be helpfull.
3830: */
3831: /*
3832: * Optimization: Create an equally sized node-set
3833: * and memcpy the content.
3834: */
3835: val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
3836: if (val1 == NULL)
3837: return(NULL);
3838: if (val2->nodeNr != 0) {
3839: if (val2->nodeNr == 1)
3840: *(val1->nodeTab) = *(val2->nodeTab);
3841: else {
3842: memcpy(val1->nodeTab, val2->nodeTab,
3843: val2->nodeNr * sizeof(xmlNodePtr));
3844: }
3845: val1->nodeNr = val2->nodeNr;
3846: }
3847: return(val1);
3848: #endif
3849: }
3850:
3851: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3852: initNr = val1->nodeNr;
3853:
3854: for (i = 0;i < val2->nodeNr;i++) {
3855: n2 = val2->nodeTab[i];
3856: /*
3857: * check against duplicates
3858: */
3859: skip = 0;
3860: for (j = 0; j < initNr; j++) {
3861: n1 = val1->nodeTab[j];
3862: if (n1 == n2) {
3863: skip = 1;
3864: break;
3865: } else if ((n1->type == XML_NAMESPACE_DECL) &&
3866: (n2->type == XML_NAMESPACE_DECL)) {
3867: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3868: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3869: ((xmlNsPtr) n2)->prefix)))
3870: {
3871: skip = 1;
3872: break;
3873: }
3874: }
3875: }
3876: if (skip)
3877: continue;
3878:
3879: /*
3880: * grow the nodeTab if needed
3881: */
3882: if (val1->nodeMax == 0) {
3883: val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3884: sizeof(xmlNodePtr));
3885: if (val1->nodeTab == NULL) {
3886: xmlXPathErrMemory(NULL, "merging nodeset\n");
3887: return(NULL);
3888: }
3889: memset(val1->nodeTab, 0 ,
3890: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3891: val1->nodeMax = XML_NODESET_DEFAULT;
3892: } else if (val1->nodeNr == val1->nodeMax) {
3893: xmlNodePtr *temp;
3894:
1.1.1.3 ! misho 3895: if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 3896: xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
! 3897: return(NULL);
! 3898: }
1.1.1.2 misho 3899: temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
1.1 misho 3900: sizeof(xmlNodePtr));
3901: if (temp == NULL) {
3902: xmlXPathErrMemory(NULL, "merging nodeset\n");
3903: return(NULL);
3904: }
3905: val1->nodeTab = temp;
1.1.1.2 misho 3906: val1->nodeMax *= 2;
1.1 misho 3907: }
3908: if (n2->type == XML_NAMESPACE_DECL) {
3909: xmlNsPtr ns = (xmlNsPtr) n2;
3910:
3911: val1->nodeTab[val1->nodeNr++] =
3912: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3913: } else
3914: val1->nodeTab[val1->nodeNr++] = n2;
3915: }
3916:
3917: return(val1);
3918: }
3919:
3920:
3921: /**
3922: * xmlXPathNodeSetMergeAndClear:
3923: * @set1: the first NodeSet or NULL
3924: * @set2: the second NodeSet
3925: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
3926: *
3927: * Merges two nodesets, all nodes from @set2 are added to @set1
3928: * if @set1 is NULL, a new set is created and copied from @set2.
3929: * Checks for duplicate nodes. Clears set2.
3930: *
3931: * Returns @set1 once extended or NULL in case of error.
3932: */
3933: static xmlNodeSetPtr
3934: xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
3935: int hasNullEntries)
3936: {
3937: if ((set1 == NULL) && (hasNullEntries == 0)) {
3938: /*
3939: * Note that doing a memcpy of the list, namespace nodes are
3940: * just assigned to set1, since set2 is cleared anyway.
3941: */
3942: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
3943: if (set1 == NULL)
3944: return(NULL);
3945: if (set2->nodeNr != 0) {
3946: memcpy(set1->nodeTab, set2->nodeTab,
3947: set2->nodeNr * sizeof(xmlNodePtr));
3948: set1->nodeNr = set2->nodeNr;
3949: }
3950: } else {
3951: int i, j, initNbSet1;
3952: xmlNodePtr n1, n2;
3953:
3954: if (set1 == NULL)
3955: set1 = xmlXPathNodeSetCreate(NULL);
3956: if (set1 == NULL)
3957: return (NULL);
3958:
3959: initNbSet1 = set1->nodeNr;
3960: for (i = 0;i < set2->nodeNr;i++) {
3961: n2 = set2->nodeTab[i];
3962: /*
3963: * Skip NULLed entries.
3964: */
3965: if (n2 == NULL)
3966: continue;
3967: /*
3968: * Skip duplicates.
3969: */
3970: for (j = 0; j < initNbSet1; j++) {
3971: n1 = set1->nodeTab[j];
3972: if (n1 == n2) {
3973: goto skip_node;
3974: } else if ((n1->type == XML_NAMESPACE_DECL) &&
3975: (n2->type == XML_NAMESPACE_DECL))
3976: {
3977: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3978: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3979: ((xmlNsPtr) n2)->prefix)))
3980: {
3981: /*
3982: * Free the namespace node.
3983: */
3984: set2->nodeTab[i] = NULL;
3985: xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
3986: goto skip_node;
3987: }
3988: }
3989: }
3990: /*
3991: * grow the nodeTab if needed
3992: */
3993: if (set1->nodeMax == 0) {
3994: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
3995: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3996: if (set1->nodeTab == NULL) {
3997: xmlXPathErrMemory(NULL, "merging nodeset\n");
3998: return(NULL);
3999: }
4000: memset(set1->nodeTab, 0,
4001: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
4002: set1->nodeMax = XML_NODESET_DEFAULT;
4003: } else if (set1->nodeNr >= set1->nodeMax) {
4004: xmlNodePtr *temp;
4005:
1.1.1.3 ! misho 4006: if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 4007: xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
! 4008: return(NULL);
! 4009: }
1.1 misho 4010: temp = (xmlNodePtr *) xmlRealloc(
1.1.1.2 misho 4011: set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
1.1 misho 4012: if (temp == NULL) {
4013: xmlXPathErrMemory(NULL, "merging nodeset\n");
4014: return(NULL);
4015: }
4016: set1->nodeTab = temp;
1.1.1.2 misho 4017: set1->nodeMax *= 2;
1.1 misho 4018: }
4019: if (n2->type == XML_NAMESPACE_DECL) {
4020: xmlNsPtr ns = (xmlNsPtr) n2;
4021:
4022: set1->nodeTab[set1->nodeNr++] =
4023: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
4024: } else
4025: set1->nodeTab[set1->nodeNr++] = n2;
4026: skip_node:
4027: {}
4028: }
4029: }
4030: set2->nodeNr = 0;
4031: return(set1);
4032: }
4033:
4034: /**
4035: * xmlXPathNodeSetMergeAndClearNoDupls:
4036: * @set1: the first NodeSet or NULL
4037: * @set2: the second NodeSet
4038: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
4039: *
4040: * Merges two nodesets, all nodes from @set2 are added to @set1
4041: * if @set1 is NULL, a new set is created and copied from @set2.
4042: * Doesn't chack for duplicate nodes. Clears set2.
4043: *
4044: * Returns @set1 once extended or NULL in case of error.
4045: */
4046: static xmlNodeSetPtr
4047: xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
4048: int hasNullEntries)
4049: {
4050: if (set2 == NULL)
4051: return(set1);
4052: if ((set1 == NULL) && (hasNullEntries == 0)) {
4053: /*
4054: * Note that doing a memcpy of the list, namespace nodes are
4055: * just assigned to set1, since set2 is cleared anyway.
4056: */
4057: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
4058: if (set1 == NULL)
4059: return(NULL);
4060: if (set2->nodeNr != 0) {
4061: memcpy(set1->nodeTab, set2->nodeTab,
4062: set2->nodeNr * sizeof(xmlNodePtr));
4063: set1->nodeNr = set2->nodeNr;
4064: }
4065: } else {
4066: int i;
4067: xmlNodePtr n2;
4068:
4069: if (set1 == NULL)
4070: set1 = xmlXPathNodeSetCreate(NULL);
4071: if (set1 == NULL)
4072: return (NULL);
4073:
4074: for (i = 0;i < set2->nodeNr;i++) {
4075: n2 = set2->nodeTab[i];
4076: /*
4077: * Skip NULLed entries.
4078: */
4079: if (n2 == NULL)
4080: continue;
4081: if (set1->nodeMax == 0) {
4082: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
4083: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
4084: if (set1->nodeTab == NULL) {
4085: xmlXPathErrMemory(NULL, "merging nodeset\n");
4086: return(NULL);
4087: }
4088: memset(set1->nodeTab, 0,
4089: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
4090: set1->nodeMax = XML_NODESET_DEFAULT;
4091: } else if (set1->nodeNr >= set1->nodeMax) {
4092: xmlNodePtr *temp;
4093:
1.1.1.3 ! misho 4094: if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
! 4095: xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
! 4096: return(NULL);
! 4097: }
1.1 misho 4098: temp = (xmlNodePtr *) xmlRealloc(
1.1.1.2 misho 4099: set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
1.1 misho 4100: if (temp == NULL) {
4101: xmlXPathErrMemory(NULL, "merging nodeset\n");
4102: return(NULL);
4103: }
4104: set1->nodeTab = temp;
1.1.1.2 misho 4105: set1->nodeMax *= 2;
1.1 misho 4106: }
4107: set1->nodeTab[set1->nodeNr++] = n2;
4108: }
4109: }
4110: set2->nodeNr = 0;
4111: return(set1);
4112: }
4113:
4114: /**
4115: * xmlXPathNodeSetDel:
4116: * @cur: the initial node set
4117: * @val: an xmlNodePtr
4118: *
4119: * Removes an xmlNodePtr from an existing NodeSet
4120: */
4121: void
4122: xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
4123: int i;
4124:
4125: if (cur == NULL) return;
4126: if (val == NULL) return;
4127:
4128: /*
4129: * find node in nodeTab
4130: */
4131: for (i = 0;i < cur->nodeNr;i++)
4132: if (cur->nodeTab[i] == val) break;
4133:
4134: if (i >= cur->nodeNr) { /* not found */
4135: #ifdef DEBUG
4136: xmlGenericError(xmlGenericErrorContext,
4137: "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
4138: val->name);
4139: #endif
4140: return;
4141: }
4142: if ((cur->nodeTab[i] != NULL) &&
4143: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
4144: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
4145: cur->nodeNr--;
4146: for (;i < cur->nodeNr;i++)
4147: cur->nodeTab[i] = cur->nodeTab[i + 1];
4148: cur->nodeTab[cur->nodeNr] = NULL;
4149: }
4150:
4151: /**
4152: * xmlXPathNodeSetRemove:
4153: * @cur: the initial node set
4154: * @val: the index to remove
4155: *
4156: * Removes an entry from an existing NodeSet list.
4157: */
4158: void
4159: xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
4160: if (cur == NULL) return;
4161: if (val >= cur->nodeNr) return;
4162: if ((cur->nodeTab[val] != NULL) &&
4163: (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
4164: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
4165: cur->nodeNr--;
4166: for (;val < cur->nodeNr;val++)
4167: cur->nodeTab[val] = cur->nodeTab[val + 1];
4168: cur->nodeTab[cur->nodeNr] = NULL;
4169: }
4170:
4171: /**
4172: * xmlXPathFreeNodeSet:
4173: * @obj: the xmlNodeSetPtr to free
4174: *
4175: * Free the NodeSet compound (not the actual nodes !).
4176: */
4177: void
4178: xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
4179: if (obj == NULL) return;
4180: if (obj->nodeTab != NULL) {
4181: int i;
4182:
4183: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4184: for (i = 0;i < obj->nodeNr;i++)
4185: if ((obj->nodeTab[i] != NULL) &&
4186: (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
4187: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4188: xmlFree(obj->nodeTab);
4189: }
4190: xmlFree(obj);
4191: }
4192:
4193: /**
4194: * xmlXPathNodeSetClear:
4195: * @set: the node set to clear
4196: *
4197: * Clears the list from all temporary XPath objects (e.g. namespace nodes
4198: * are feed), but does *not* free the list itself. Sets the length of the
4199: * list to 0.
4200: */
4201: static void
4202: xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
4203: {
4204: if ((set == NULL) || (set->nodeNr <= 0))
4205: return;
4206: else if (hasNsNodes) {
4207: int i;
4208: xmlNodePtr node;
4209:
4210: for (i = 0; i < set->nodeNr; i++) {
4211: node = set->nodeTab[i];
4212: if ((node != NULL) &&
4213: (node->type == XML_NAMESPACE_DECL))
4214: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4215: }
4216: }
4217: set->nodeNr = 0;
4218: }
4219:
4220: /**
4221: * xmlXPathNodeSetClearFromPos:
4222: * @set: the node set to be cleared
4223: * @pos: the start position to clear from
4224: *
4225: * Clears the list from temporary XPath objects (e.g. namespace nodes
4226: * are feed) starting with the entry at @pos, but does *not* free the list
4227: * itself. Sets the length of the list to @pos.
4228: */
4229: static void
4230: xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
4231: {
4232: if ((set == NULL) || (set->nodeNr <= 0) || (pos >= set->nodeNr))
4233: return;
4234: else if ((hasNsNodes)) {
4235: int i;
4236: xmlNodePtr node;
4237:
4238: for (i = pos; i < set->nodeNr; i++) {
4239: node = set->nodeTab[i];
4240: if ((node != NULL) &&
4241: (node->type == XML_NAMESPACE_DECL))
4242: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4243: }
4244: }
4245: set->nodeNr = pos;
4246: }
4247:
4248: /**
4249: * xmlXPathFreeValueTree:
4250: * @obj: the xmlNodeSetPtr to free
4251: *
4252: * Free the NodeSet compound and the actual tree, this is different
4253: * from xmlXPathFreeNodeSet()
4254: */
4255: static void
4256: xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
4257: int i;
4258:
4259: if (obj == NULL) return;
4260:
4261: if (obj->nodeTab != NULL) {
4262: for (i = 0;i < obj->nodeNr;i++) {
4263: if (obj->nodeTab[i] != NULL) {
4264: if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
4265: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4266: } else {
4267: xmlFreeNodeList(obj->nodeTab[i]);
4268: }
4269: }
4270: }
4271: xmlFree(obj->nodeTab);
4272: }
4273: xmlFree(obj);
4274: }
4275:
4276: #if defined(DEBUG) || defined(DEBUG_STEP)
4277: /**
4278: * xmlGenericErrorContextNodeSet:
4279: * @output: a FILE * for the output
4280: * @obj: the xmlNodeSetPtr to display
4281: *
4282: * Quick display of a NodeSet
4283: */
4284: void
4285: xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
4286: int i;
4287:
4288: if (output == NULL) output = xmlGenericErrorContext;
4289: if (obj == NULL) {
4290: fprintf(output, "NodeSet == NULL !\n");
4291: return;
4292: }
4293: if (obj->nodeNr == 0) {
4294: fprintf(output, "NodeSet is empty\n");
4295: return;
4296: }
4297: if (obj->nodeTab == NULL) {
4298: fprintf(output, " nodeTab == NULL !\n");
4299: return;
4300: }
4301: for (i = 0; i < obj->nodeNr; i++) {
4302: if (obj->nodeTab[i] == NULL) {
4303: fprintf(output, " NULL !\n");
4304: return;
4305: }
4306: if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
4307: (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
4308: fprintf(output, " /");
4309: else if (obj->nodeTab[i]->name == NULL)
4310: fprintf(output, " noname!");
4311: else fprintf(output, " %s", obj->nodeTab[i]->name);
4312: }
4313: fprintf(output, "\n");
4314: }
4315: #endif
4316:
4317: /**
4318: * xmlXPathNewNodeSet:
4319: * @val: the NodePtr value
4320: *
4321: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4322: * it with the single Node @val
4323: *
4324: * Returns the newly created object.
4325: */
4326: xmlXPathObjectPtr
4327: xmlXPathNewNodeSet(xmlNodePtr val) {
4328: xmlXPathObjectPtr ret;
4329:
4330: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4331: if (ret == NULL) {
4332: xmlXPathErrMemory(NULL, "creating nodeset\n");
4333: return(NULL);
4334: }
4335: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4336: ret->type = XPATH_NODESET;
4337: ret->boolval = 0;
4338: ret->nodesetval = xmlXPathNodeSetCreate(val);
4339: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4340: #ifdef XP_DEBUG_OBJ_USAGE
4341: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4342: #endif
4343: return(ret);
4344: }
4345:
4346: /**
4347: * xmlXPathNewValueTree:
4348: * @val: the NodePtr value
4349: *
4350: * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
4351: * it with the tree root @val
4352: *
4353: * Returns the newly created object.
4354: */
4355: xmlXPathObjectPtr
4356: xmlXPathNewValueTree(xmlNodePtr val) {
4357: xmlXPathObjectPtr ret;
4358:
4359: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4360: if (ret == NULL) {
4361: xmlXPathErrMemory(NULL, "creating result value tree\n");
4362: return(NULL);
4363: }
4364: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4365: ret->type = XPATH_XSLT_TREE;
4366: ret->boolval = 1;
4367: ret->user = (void *) val;
4368: ret->nodesetval = xmlXPathNodeSetCreate(val);
4369: #ifdef XP_DEBUG_OBJ_USAGE
4370: xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
4371: #endif
4372: return(ret);
4373: }
4374:
4375: /**
4376: * xmlXPathNewNodeSetList:
4377: * @val: an existing NodeSet
4378: *
4379: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4380: * it with the Nodeset @val
4381: *
4382: * Returns the newly created object.
4383: */
4384: xmlXPathObjectPtr
4385: xmlXPathNewNodeSetList(xmlNodeSetPtr val)
4386: {
4387: xmlXPathObjectPtr ret;
4388: int i;
4389:
4390: if (val == NULL)
4391: ret = NULL;
4392: else if (val->nodeTab == NULL)
4393: ret = xmlXPathNewNodeSet(NULL);
4394: else {
4395: ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1.1.1.3 ! misho 4396: if (ret) {
! 4397: for (i = 1; i < val->nodeNr; ++i) {
! 4398: if (xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i])
! 4399: < 0) break;
! 4400: }
! 4401: }
1.1 misho 4402: }
4403:
4404: return (ret);
4405: }
4406:
4407: /**
4408: * xmlXPathWrapNodeSet:
4409: * @val: the NodePtr value
4410: *
4411: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
4412: *
4413: * Returns the newly created object.
4414: */
4415: xmlXPathObjectPtr
4416: xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
4417: xmlXPathObjectPtr ret;
4418:
4419: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4420: if (ret == NULL) {
4421: xmlXPathErrMemory(NULL, "creating node set object\n");
4422: return(NULL);
4423: }
4424: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4425: ret->type = XPATH_NODESET;
4426: ret->nodesetval = val;
4427: #ifdef XP_DEBUG_OBJ_USAGE
4428: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4429: #endif
4430: return(ret);
4431: }
4432:
4433: /**
4434: * xmlXPathFreeNodeSetList:
4435: * @obj: an existing NodeSetList object
4436: *
4437: * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
4438: * the list contrary to xmlXPathFreeObject().
4439: */
4440: void
4441: xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
4442: if (obj == NULL) return;
4443: #ifdef XP_DEBUG_OBJ_USAGE
4444: xmlXPathDebugObjUsageReleased(NULL, obj->type);
4445: #endif
4446: xmlFree(obj);
4447: }
4448:
4449: /**
4450: * xmlXPathDifference:
4451: * @nodes1: a node-set
4452: * @nodes2: a node-set
4453: *
4454: * Implements the EXSLT - Sets difference() function:
4455: * node-set set:difference (node-set, node-set)
4456: *
4457: * Returns the difference between the two node sets, or nodes1 if
4458: * nodes2 is empty
4459: */
4460: xmlNodeSetPtr
4461: xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4462: xmlNodeSetPtr ret;
4463: int i, l1;
4464: xmlNodePtr cur;
4465:
4466: if (xmlXPathNodeSetIsEmpty(nodes2))
4467: return(nodes1);
4468:
4469: ret = xmlXPathNodeSetCreate(NULL);
4470: if (xmlXPathNodeSetIsEmpty(nodes1))
4471: return(ret);
4472:
4473: l1 = xmlXPathNodeSetGetLength(nodes1);
4474:
4475: for (i = 0; i < l1; i++) {
4476: cur = xmlXPathNodeSetItem(nodes1, i);
1.1.1.3 ! misho 4477: if (!xmlXPathNodeSetContains(nodes2, cur)) {
! 4478: if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
! 4479: break;
! 4480: }
1.1 misho 4481: }
4482: return(ret);
4483: }
4484:
4485: /**
4486: * xmlXPathIntersection:
4487: * @nodes1: a node-set
4488: * @nodes2: a node-set
4489: *
4490: * Implements the EXSLT - Sets intersection() function:
4491: * node-set set:intersection (node-set, node-set)
4492: *
4493: * Returns a node set comprising the nodes that are within both the
4494: * node sets passed as arguments
4495: */
4496: xmlNodeSetPtr
4497: xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4498: xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
4499: int i, l1;
4500: xmlNodePtr cur;
4501:
4502: if (ret == NULL)
4503: return(ret);
4504: if (xmlXPathNodeSetIsEmpty(nodes1))
4505: return(ret);
4506: if (xmlXPathNodeSetIsEmpty(nodes2))
4507: return(ret);
4508:
4509: l1 = xmlXPathNodeSetGetLength(nodes1);
4510:
4511: for (i = 0; i < l1; i++) {
4512: cur = xmlXPathNodeSetItem(nodes1, i);
1.1.1.3 ! misho 4513: if (xmlXPathNodeSetContains(nodes2, cur)) {
! 4514: if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
! 4515: break;
! 4516: }
1.1 misho 4517: }
4518: return(ret);
4519: }
4520:
4521: /**
4522: * xmlXPathDistinctSorted:
4523: * @nodes: a node-set, sorted by document order
4524: *
4525: * Implements the EXSLT - Sets distinct() function:
4526: * node-set set:distinct (node-set)
4527: *
4528: * Returns a subset of the nodes contained in @nodes, or @nodes if
4529: * it is empty
4530: */
4531: xmlNodeSetPtr
4532: xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
4533: xmlNodeSetPtr ret;
4534: xmlHashTablePtr hash;
4535: int i, l;
4536: xmlChar * strval;
4537: xmlNodePtr cur;
4538:
4539: if (xmlXPathNodeSetIsEmpty(nodes))
4540: return(nodes);
4541:
4542: ret = xmlXPathNodeSetCreate(NULL);
4543: if (ret == NULL)
4544: return(ret);
4545: l = xmlXPathNodeSetGetLength(nodes);
4546: hash = xmlHashCreate (l);
4547: for (i = 0; i < l; i++) {
4548: cur = xmlXPathNodeSetItem(nodes, i);
4549: strval = xmlXPathCastNodeToString(cur);
4550: if (xmlHashLookup(hash, strval) == NULL) {
4551: xmlHashAddEntry(hash, strval, strval);
1.1.1.3 ! misho 4552: if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
! 4553: break;
1.1 misho 4554: } else {
4555: xmlFree(strval);
4556: }
4557: }
4558: xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
4559: return(ret);
4560: }
4561:
4562: /**
4563: * xmlXPathDistinct:
4564: * @nodes: a node-set
4565: *
4566: * Implements the EXSLT - Sets distinct() function:
4567: * node-set set:distinct (node-set)
4568: * @nodes is sorted by document order, then #exslSetsDistinctSorted
4569: * is called with the sorted node-set
4570: *
4571: * Returns a subset of the nodes contained in @nodes, or @nodes if
4572: * it is empty
4573: */
4574: xmlNodeSetPtr
4575: xmlXPathDistinct (xmlNodeSetPtr nodes) {
4576: if (xmlXPathNodeSetIsEmpty(nodes))
4577: return(nodes);
4578:
4579: xmlXPathNodeSetSort(nodes);
4580: return(xmlXPathDistinctSorted(nodes));
4581: }
4582:
4583: /**
4584: * xmlXPathHasSameNodes:
4585: * @nodes1: a node-set
4586: * @nodes2: a node-set
4587: *
4588: * Implements the EXSLT - Sets has-same-nodes function:
4589: * boolean set:has-same-node(node-set, node-set)
4590: *
4591: * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
4592: * otherwise
4593: */
4594: int
4595: xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4596: int i, l;
4597: xmlNodePtr cur;
4598:
4599: if (xmlXPathNodeSetIsEmpty(nodes1) ||
4600: xmlXPathNodeSetIsEmpty(nodes2))
4601: return(0);
4602:
4603: l = xmlXPathNodeSetGetLength(nodes1);
4604: for (i = 0; i < l; i++) {
4605: cur = xmlXPathNodeSetItem(nodes1, i);
4606: if (xmlXPathNodeSetContains(nodes2, cur))
4607: return(1);
4608: }
4609: return(0);
4610: }
4611:
4612: /**
4613: * xmlXPathNodeLeadingSorted:
4614: * @nodes: a node-set, sorted by document order
4615: * @node: a node
4616: *
4617: * Implements the EXSLT - Sets leading() function:
4618: * node-set set:leading (node-set, node-set)
4619: *
4620: * Returns the nodes in @nodes that precede @node in document order,
4621: * @nodes if @node is NULL or an empty node-set if @nodes
4622: * doesn't contain @node
4623: */
4624: xmlNodeSetPtr
4625: xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4626: int i, l;
4627: xmlNodePtr cur;
4628: xmlNodeSetPtr ret;
4629:
4630: if (node == NULL)
4631: return(nodes);
4632:
4633: ret = xmlXPathNodeSetCreate(NULL);
4634: if (ret == NULL)
4635: return(ret);
4636: if (xmlXPathNodeSetIsEmpty(nodes) ||
4637: (!xmlXPathNodeSetContains(nodes, node)))
4638: return(ret);
4639:
4640: l = xmlXPathNodeSetGetLength(nodes);
4641: for (i = 0; i < l; i++) {
4642: cur = xmlXPathNodeSetItem(nodes, i);
4643: if (cur == node)
4644: break;
1.1.1.3 ! misho 4645: if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
! 4646: break;
1.1 misho 4647: }
4648: return(ret);
4649: }
4650:
4651: /**
4652: * xmlXPathNodeLeading:
4653: * @nodes: a node-set
4654: * @node: a node
4655: *
4656: * Implements the EXSLT - Sets leading() function:
4657: * node-set set:leading (node-set, node-set)
4658: * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
4659: * is called.
4660: *
4661: * Returns the nodes in @nodes that precede @node in document order,
4662: * @nodes if @node is NULL or an empty node-set if @nodes
4663: * doesn't contain @node
4664: */
4665: xmlNodeSetPtr
4666: xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
4667: xmlXPathNodeSetSort(nodes);
4668: return(xmlXPathNodeLeadingSorted(nodes, node));
4669: }
4670:
4671: /**
4672: * xmlXPathLeadingSorted:
4673: * @nodes1: a node-set, sorted by document order
4674: * @nodes2: a node-set, sorted by document order
4675: *
4676: * Implements the EXSLT - Sets leading() function:
4677: * node-set set:leading (node-set, node-set)
4678: *
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
4682: */
4683: xmlNodeSetPtr
4684: xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4685: if (xmlXPathNodeSetIsEmpty(nodes2))
4686: return(nodes1);
4687: return(xmlXPathNodeLeadingSorted(nodes1,
4688: xmlXPathNodeSetItem(nodes2, 1)));
4689: }
4690:
4691: /**
4692: * xmlXPathLeading:
4693: * @nodes1: a node-set
4694: * @nodes2: a node-set
4695: *
4696: * Implements the EXSLT - Sets leading() function:
4697: * node-set set:leading (node-set, node-set)
4698: * @nodes1 and @nodes2 are sorted by document order, then
4699: * #exslSetsLeadingSorted is called.
4700: *
4701: * Returns the nodes in @nodes1 that precede the first node in @nodes2
4702: * in document order, @nodes1 if @nodes2 is NULL or empty or
4703: * an empty node-set if @nodes1 doesn't contain @nodes2
4704: */
4705: xmlNodeSetPtr
4706: xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4707: if (xmlXPathNodeSetIsEmpty(nodes2))
4708: return(nodes1);
4709: if (xmlXPathNodeSetIsEmpty(nodes1))
4710: return(xmlXPathNodeSetCreate(NULL));
4711: xmlXPathNodeSetSort(nodes1);
4712: xmlXPathNodeSetSort(nodes2);
4713: return(xmlXPathNodeLeadingSorted(nodes1,
4714: xmlXPathNodeSetItem(nodes2, 1)));
4715: }
4716:
4717: /**
4718: * xmlXPathNodeTrailingSorted:
4719: * @nodes: a node-set, sorted by document order
4720: * @node: a node
4721: *
4722: * Implements the EXSLT - Sets trailing() function:
4723: * node-set set:trailing (node-set, node-set)
4724: *
4725: * Returns the nodes in @nodes that follow @node in document order,
4726: * @nodes if @node is NULL or an empty node-set if @nodes
4727: * doesn't contain @node
4728: */
4729: xmlNodeSetPtr
4730: xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4731: int i, l;
4732: xmlNodePtr cur;
4733: xmlNodeSetPtr ret;
4734:
4735: if (node == NULL)
4736: return(nodes);
4737:
4738: ret = xmlXPathNodeSetCreate(NULL);
4739: if (ret == NULL)
4740: return(ret);
4741: if (xmlXPathNodeSetIsEmpty(nodes) ||
4742: (!xmlXPathNodeSetContains(nodes, node)))
4743: return(ret);
4744:
4745: l = xmlXPathNodeSetGetLength(nodes);
4746: for (i = l - 1; i >= 0; i--) {
4747: cur = xmlXPathNodeSetItem(nodes, i);
4748: if (cur == node)
4749: break;
1.1.1.3 ! misho 4750: if (xmlXPathNodeSetAddUnique(ret, cur) < 0)
! 4751: break;
1.1 misho 4752: }
4753: xmlXPathNodeSetSort(ret); /* bug 413451 */
4754: return(ret);
4755: }
4756:
4757: /**
4758: * xmlXPathNodeTrailing:
4759: * @nodes: a node-set
4760: * @node: a node
4761: *
4762: * Implements the EXSLT - Sets trailing() function:
4763: * node-set set:trailing (node-set, node-set)
4764: * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
4765: * is called.
4766: *
4767: * Returns the nodes in @nodes that follow @node in document order,
4768: * @nodes if @node is NULL or an empty node-set if @nodes
4769: * doesn't contain @node
4770: */
4771: xmlNodeSetPtr
4772: xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
4773: xmlXPathNodeSetSort(nodes);
4774: return(xmlXPathNodeTrailingSorted(nodes, node));
4775: }
4776:
4777: /**
4778: * xmlXPathTrailingSorted:
4779: * @nodes1: a node-set, sorted by document order
4780: * @nodes2: a node-set, sorted by document order
4781: *
4782: * Implements the EXSLT - Sets trailing() function:
4783: * node-set set:trailing (node-set, node-set)
4784: *
4785: * Returns the nodes in @nodes1 that follow the first node in @nodes2
4786: * in document order, @nodes1 if @nodes2 is NULL or empty or
4787: * an empty node-set if @nodes1 doesn't contain @nodes2
4788: */
4789: xmlNodeSetPtr
4790: xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4791: if (xmlXPathNodeSetIsEmpty(nodes2))
4792: return(nodes1);
4793: return(xmlXPathNodeTrailingSorted(nodes1,
4794: xmlXPathNodeSetItem(nodes2, 0)));
4795: }
4796:
4797: /**
4798: * xmlXPathTrailing:
4799: * @nodes1: a node-set
4800: * @nodes2: a node-set
4801: *
4802: * Implements the EXSLT - Sets trailing() function:
4803: * node-set set:trailing (node-set, node-set)
4804: * @nodes1 and @nodes2 are sorted by document order, then
4805: * #xmlXPathTrailingSorted is called.
4806: *
4807: * Returns the nodes in @nodes1 that follow the first node in @nodes2
4808: * in document order, @nodes1 if @nodes2 is NULL or empty or
4809: * an empty node-set if @nodes1 doesn't contain @nodes2
4810: */
4811: xmlNodeSetPtr
4812: xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4813: if (xmlXPathNodeSetIsEmpty(nodes2))
4814: return(nodes1);
4815: if (xmlXPathNodeSetIsEmpty(nodes1))
4816: return(xmlXPathNodeSetCreate(NULL));
4817: xmlXPathNodeSetSort(nodes1);
4818: xmlXPathNodeSetSort(nodes2);
4819: return(xmlXPathNodeTrailingSorted(nodes1,
4820: xmlXPathNodeSetItem(nodes2, 0)));
4821: }
4822:
4823: /************************************************************************
4824: * *
4825: * Routines to handle extra functions *
4826: * *
4827: ************************************************************************/
4828:
4829: /**
4830: * xmlXPathRegisterFunc:
4831: * @ctxt: the XPath context
4832: * @name: the function name
4833: * @f: the function implementation or NULL
4834: *
4835: * Register a new function. If @f is NULL it unregisters the function
4836: *
4837: * Returns 0 in case of success, -1 in case of error
4838: */
4839: int
4840: xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
4841: xmlXPathFunction f) {
4842: return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
4843: }
4844:
4845: /**
4846: * xmlXPathRegisterFuncNS:
4847: * @ctxt: the XPath context
4848: * @name: the function name
4849: * @ns_uri: the function namespace URI
4850: * @f: the function implementation or NULL
4851: *
4852: * Register a new function. If @f is NULL it unregisters the function
4853: *
4854: * Returns 0 in case of success, -1 in case of error
4855: */
4856: int
4857: xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4858: const xmlChar *ns_uri, xmlXPathFunction f) {
4859: if (ctxt == NULL)
4860: return(-1);
4861: if (name == NULL)
4862: return(-1);
4863:
4864: if (ctxt->funcHash == NULL)
4865: ctxt->funcHash = xmlHashCreate(0);
4866: if (ctxt->funcHash == NULL)
4867: return(-1);
4868: if (f == NULL)
4869: return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
4870: return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, XML_CAST_FPTR(f)));
4871: }
4872:
4873: /**
4874: * xmlXPathRegisterFuncLookup:
4875: * @ctxt: the XPath context
4876: * @f: the lookup function
4877: * @funcCtxt: the lookup data
4878: *
4879: * Registers an external mechanism to do function lookup.
4880: */
4881: void
4882: xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
4883: xmlXPathFuncLookupFunc f,
4884: void *funcCtxt) {
4885: if (ctxt == NULL)
4886: return;
4887: ctxt->funcLookupFunc = f;
4888: ctxt->funcLookupData = funcCtxt;
4889: }
4890:
4891: /**
4892: * xmlXPathFunctionLookup:
4893: * @ctxt: the XPath context
4894: * @name: the function name
4895: *
4896: * Search in the Function array of the context for the given
4897: * function.
4898: *
4899: * Returns the xmlXPathFunction or NULL if not found
4900: */
4901: xmlXPathFunction
4902: xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
4903: if (ctxt == NULL)
4904: return (NULL);
4905:
4906: if (ctxt->funcLookupFunc != NULL) {
4907: xmlXPathFunction ret;
4908: xmlXPathFuncLookupFunc f;
4909:
4910: f = ctxt->funcLookupFunc;
4911: ret = f(ctxt->funcLookupData, name, NULL);
4912: if (ret != NULL)
4913: return(ret);
4914: }
4915: return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
4916: }
4917:
4918: /**
4919: * xmlXPathFunctionLookupNS:
4920: * @ctxt: the XPath context
4921: * @name: the function name
4922: * @ns_uri: the function namespace URI
4923: *
4924: * Search in the Function array of the context for the given
4925: * function.
4926: *
4927: * Returns the xmlXPathFunction or NULL if not found
4928: */
4929: xmlXPathFunction
4930: xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4931: const xmlChar *ns_uri) {
4932: xmlXPathFunction ret;
4933:
4934: if (ctxt == NULL)
4935: return(NULL);
4936: if (name == NULL)
4937: return(NULL);
4938:
4939: if (ctxt->funcLookupFunc != NULL) {
4940: xmlXPathFuncLookupFunc f;
4941:
4942: f = ctxt->funcLookupFunc;
4943: ret = f(ctxt->funcLookupData, name, ns_uri);
4944: if (ret != NULL)
4945: return(ret);
4946: }
4947:
4948: if (ctxt->funcHash == NULL)
4949: return(NULL);
4950:
4951: XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
4952: return(ret);
4953: }
4954:
4955: /**
4956: * xmlXPathRegisteredFuncsCleanup:
4957: * @ctxt: the XPath context
4958: *
4959: * Cleanup the XPath context data associated to registered functions
4960: */
4961: void
4962: xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
4963: if (ctxt == NULL)
4964: return;
4965:
4966: xmlHashFree(ctxt->funcHash, NULL);
4967: ctxt->funcHash = NULL;
4968: }
4969:
4970: /************************************************************************
4971: * *
4972: * Routines to handle Variables *
4973: * *
4974: ************************************************************************/
4975:
4976: /**
4977: * xmlXPathRegisterVariable:
4978: * @ctxt: the XPath context
4979: * @name: the variable name
4980: * @value: the variable value or NULL
4981: *
4982: * Register a new variable value. If @value is NULL it unregisters
4983: * the variable
4984: *
4985: * Returns 0 in case of success, -1 in case of error
4986: */
4987: int
4988: xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
4989: xmlXPathObjectPtr value) {
4990: return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
4991: }
4992:
4993: /**
4994: * xmlXPathRegisterVariableNS:
4995: * @ctxt: the XPath context
4996: * @name: the variable name
4997: * @ns_uri: the variable namespace URI
4998: * @value: the variable value or NULL
4999: *
5000: * Register a new variable value. If @value is NULL it unregisters
5001: * the variable
5002: *
5003: * Returns 0 in case of success, -1 in case of error
5004: */
5005: int
5006: xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
5007: const xmlChar *ns_uri,
5008: xmlXPathObjectPtr value) {
5009: if (ctxt == NULL)
5010: return(-1);
5011: if (name == NULL)
5012: return(-1);
5013:
5014: if (ctxt->varHash == NULL)
5015: ctxt->varHash = xmlHashCreate(0);
5016: if (ctxt->varHash == NULL)
5017: return(-1);
5018: if (value == NULL)
5019: return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
5020: (xmlHashDeallocator)xmlXPathFreeObject));
5021: return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
5022: (void *) value,
5023: (xmlHashDeallocator)xmlXPathFreeObject));
5024: }
5025:
5026: /**
5027: * xmlXPathRegisterVariableLookup:
5028: * @ctxt: the XPath context
5029: * @f: the lookup function
5030: * @data: the lookup data
5031: *
5032: * register an external mechanism to do variable lookup
5033: */
5034: void
5035: xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
5036: xmlXPathVariableLookupFunc f, void *data) {
5037: if (ctxt == NULL)
5038: return;
5039: ctxt->varLookupFunc = f;
5040: ctxt->varLookupData = data;
5041: }
5042:
5043: /**
5044: * xmlXPathVariableLookup:
5045: * @ctxt: the XPath context
5046: * @name: the variable name
5047: *
5048: * Search in the Variable array of the context for the given
5049: * variable value.
5050: *
5051: * Returns a copy of the value or NULL if not found
5052: */
5053: xmlXPathObjectPtr
5054: xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
5055: if (ctxt == NULL)
5056: return(NULL);
5057:
5058: if (ctxt->varLookupFunc != NULL) {
5059: xmlXPathObjectPtr ret;
5060:
5061: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5062: (ctxt->varLookupData, name, NULL);
5063: return(ret);
5064: }
5065: return(xmlXPathVariableLookupNS(ctxt, name, NULL));
5066: }
5067:
5068: /**
5069: * xmlXPathVariableLookupNS:
5070: * @ctxt: the XPath context
5071: * @name: the variable name
5072: * @ns_uri: the variable namespace URI
5073: *
5074: * Search in the Variable array of the context for the given
5075: * variable value.
5076: *
5077: * Returns the a copy of the value or NULL if not found
5078: */
5079: xmlXPathObjectPtr
5080: xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
5081: const xmlChar *ns_uri) {
5082: if (ctxt == NULL)
5083: return(NULL);
5084:
5085: if (ctxt->varLookupFunc != NULL) {
5086: xmlXPathObjectPtr ret;
5087:
5088: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
5089: (ctxt->varLookupData, name, ns_uri);
5090: if (ret != NULL) return(ret);
5091: }
5092:
5093: if (ctxt->varHash == NULL)
5094: return(NULL);
5095: if (name == NULL)
5096: return(NULL);
5097:
5098: return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
5099: xmlHashLookup2(ctxt->varHash, name, ns_uri)));
5100: }
5101:
5102: /**
5103: * xmlXPathRegisteredVariablesCleanup:
5104: * @ctxt: the XPath context
5105: *
5106: * Cleanup the XPath context data associated to registered variables
5107: */
5108: void
5109: xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
5110: if (ctxt == NULL)
5111: return;
5112:
5113: xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
5114: ctxt->varHash = NULL;
5115: }
5116:
5117: /**
5118: * xmlXPathRegisterNs:
5119: * @ctxt: the XPath context
5120: * @prefix: the namespace prefix cannot be NULL or empty string
5121: * @ns_uri: the namespace name
5122: *
5123: * Register a new namespace. If @ns_uri is NULL it unregisters
5124: * the namespace
5125: *
5126: * Returns 0 in case of success, -1 in case of error
5127: */
5128: int
5129: xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
5130: const xmlChar *ns_uri) {
5131: if (ctxt == NULL)
5132: return(-1);
5133: if (prefix == NULL)
5134: return(-1);
5135: if (prefix[0] == 0)
5136: return(-1);
5137:
5138: if (ctxt->nsHash == NULL)
5139: ctxt->nsHash = xmlHashCreate(10);
5140: if (ctxt->nsHash == NULL)
5141: return(-1);
5142: if (ns_uri == NULL)
5143: return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
5144: (xmlHashDeallocator)xmlFree));
5145: return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
5146: (xmlHashDeallocator)xmlFree));
5147: }
5148:
5149: /**
5150: * xmlXPathNsLookup:
5151: * @ctxt: the XPath context
5152: * @prefix: the namespace prefix value
5153: *
5154: * Search in the namespace declaration array of the context for the given
5155: * namespace name associated to the given prefix
5156: *
5157: * Returns the value or NULL if not found
5158: */
5159: const xmlChar *
5160: xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
5161: if (ctxt == NULL)
5162: return(NULL);
5163: if (prefix == NULL)
5164: return(NULL);
5165:
5166: #ifdef XML_XML_NAMESPACE
5167: if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
5168: return(XML_XML_NAMESPACE);
5169: #endif
5170:
5171: if (ctxt->namespaces != NULL) {
5172: int i;
5173:
5174: for (i = 0;i < ctxt->nsNr;i++) {
5175: if ((ctxt->namespaces[i] != NULL) &&
5176: (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
5177: return(ctxt->namespaces[i]->href);
5178: }
5179: }
5180:
5181: return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
5182: }
5183:
5184: /**
5185: * xmlXPathRegisteredNsCleanup:
5186: * @ctxt: the XPath context
5187: *
5188: * Cleanup the XPath context data associated to registered variables
5189: */
5190: void
5191: xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
5192: if (ctxt == NULL)
5193: return;
5194:
5195: xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree);
5196: ctxt->nsHash = NULL;
5197: }
5198:
5199: /************************************************************************
5200: * *
5201: * Routines to handle Values *
5202: * *
5203: ************************************************************************/
5204:
5205: /* Allocations are terrible, one needs to optimize all this !!! */
5206:
5207: /**
5208: * xmlXPathNewFloat:
5209: * @val: the double value
5210: *
5211: * Create a new xmlXPathObjectPtr of type double and of value @val
5212: *
5213: * Returns the newly created object.
5214: */
5215: xmlXPathObjectPtr
5216: xmlXPathNewFloat(double val) {
5217: xmlXPathObjectPtr ret;
5218:
5219: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5220: if (ret == NULL) {
5221: xmlXPathErrMemory(NULL, "creating float object\n");
5222: return(NULL);
5223: }
5224: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5225: ret->type = XPATH_NUMBER;
5226: ret->floatval = val;
5227: #ifdef XP_DEBUG_OBJ_USAGE
5228: xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
5229: #endif
5230: return(ret);
5231: }
5232:
5233: /**
5234: * xmlXPathNewBoolean:
5235: * @val: the boolean value
5236: *
5237: * Create a new xmlXPathObjectPtr of type boolean and of value @val
5238: *
5239: * Returns the newly created object.
5240: */
5241: xmlXPathObjectPtr
5242: xmlXPathNewBoolean(int val) {
5243: xmlXPathObjectPtr ret;
5244:
5245: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5246: if (ret == NULL) {
5247: xmlXPathErrMemory(NULL, "creating boolean object\n");
5248: return(NULL);
5249: }
5250: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5251: ret->type = XPATH_BOOLEAN;
5252: ret->boolval = (val != 0);
5253: #ifdef XP_DEBUG_OBJ_USAGE
5254: xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
5255: #endif
5256: return(ret);
5257: }
5258:
5259: /**
5260: * xmlXPathNewString:
5261: * @val: the xmlChar * value
5262: *
5263: * Create a new xmlXPathObjectPtr of type string and of value @val
5264: *
5265: * Returns the newly created object.
5266: */
5267: xmlXPathObjectPtr
5268: xmlXPathNewString(const xmlChar *val) {
5269: xmlXPathObjectPtr ret;
5270:
5271: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5272: if (ret == NULL) {
5273: xmlXPathErrMemory(NULL, "creating string object\n");
5274: return(NULL);
5275: }
5276: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5277: ret->type = XPATH_STRING;
5278: if (val != NULL)
5279: ret->stringval = xmlStrdup(val);
5280: else
5281: ret->stringval = xmlStrdup((const xmlChar *)"");
5282: #ifdef XP_DEBUG_OBJ_USAGE
5283: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5284: #endif
5285: return(ret);
5286: }
5287:
5288: /**
5289: * xmlXPathWrapString:
5290: * @val: the xmlChar * value
5291: *
5292: * Wraps the @val string into an XPath object.
5293: *
5294: * Returns the newly created object.
5295: */
5296: xmlXPathObjectPtr
5297: xmlXPathWrapString (xmlChar *val) {
5298: xmlXPathObjectPtr ret;
5299:
5300: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5301: if (ret == NULL) {
5302: xmlXPathErrMemory(NULL, "creating string object\n");
5303: return(NULL);
5304: }
5305: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5306: ret->type = XPATH_STRING;
5307: ret->stringval = val;
5308: #ifdef XP_DEBUG_OBJ_USAGE
5309: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5310: #endif
5311: return(ret);
5312: }
5313:
5314: /**
5315: * xmlXPathNewCString:
5316: * @val: the char * value
5317: *
5318: * Create a new xmlXPathObjectPtr of type string and of value @val
5319: *
5320: * Returns the newly created object.
5321: */
5322: xmlXPathObjectPtr
5323: xmlXPathNewCString(const char *val) {
5324: xmlXPathObjectPtr ret;
5325:
5326: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5327: if (ret == NULL) {
5328: xmlXPathErrMemory(NULL, "creating string object\n");
5329: return(NULL);
5330: }
5331: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5332: ret->type = XPATH_STRING;
5333: ret->stringval = xmlStrdup(BAD_CAST val);
5334: #ifdef XP_DEBUG_OBJ_USAGE
5335: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5336: #endif
5337: return(ret);
5338: }
5339:
5340: /**
5341: * xmlXPathWrapCString:
5342: * @val: the char * value
5343: *
5344: * Wraps a string into an XPath object.
5345: *
5346: * Returns the newly created object.
5347: */
5348: xmlXPathObjectPtr
5349: xmlXPathWrapCString (char * val) {
5350: return(xmlXPathWrapString((xmlChar *)(val)));
5351: }
5352:
5353: /**
5354: * xmlXPathWrapExternal:
5355: * @val: the user data
5356: *
5357: * Wraps the @val data into an XPath object.
5358: *
5359: * Returns the newly created object.
5360: */
5361: xmlXPathObjectPtr
5362: xmlXPathWrapExternal (void *val) {
5363: xmlXPathObjectPtr ret;
5364:
5365: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5366: if (ret == NULL) {
5367: xmlXPathErrMemory(NULL, "creating user object\n");
5368: return(NULL);
5369: }
5370: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5371: ret->type = XPATH_USERS;
5372: ret->user = val;
5373: #ifdef XP_DEBUG_OBJ_USAGE
5374: xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
5375: #endif
5376: return(ret);
5377: }
5378:
5379: /**
5380: * xmlXPathObjectCopy:
5381: * @val: the original object
5382: *
5383: * allocate a new copy of a given object
5384: *
5385: * Returns the newly created object.
5386: */
5387: xmlXPathObjectPtr
5388: xmlXPathObjectCopy(xmlXPathObjectPtr val) {
5389: xmlXPathObjectPtr ret;
5390:
5391: if (val == NULL)
5392: return(NULL);
5393:
5394: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5395: if (ret == NULL) {
5396: xmlXPathErrMemory(NULL, "copying object\n");
5397: return(NULL);
5398: }
5399: memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
5400: #ifdef XP_DEBUG_OBJ_USAGE
5401: xmlXPathDebugObjUsageRequested(NULL, val->type);
5402: #endif
5403: switch (val->type) {
5404: case XPATH_BOOLEAN:
5405: case XPATH_NUMBER:
5406: case XPATH_POINT:
5407: case XPATH_RANGE:
5408: break;
5409: case XPATH_STRING:
5410: ret->stringval = xmlStrdup(val->stringval);
5411: break;
5412: case XPATH_XSLT_TREE:
5413: #if 0
5414: /*
5415: Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
5416: this previous handling is no longer correct, and can cause some serious
5417: problems (ref. bug 145547)
5418: */
5419: if ((val->nodesetval != NULL) &&
5420: (val->nodesetval->nodeTab != NULL)) {
5421: xmlNodePtr cur, tmp;
5422: xmlDocPtr top;
5423:
5424: ret->boolval = 1;
5425: top = xmlNewDoc(NULL);
5426: top->name = (char *)
5427: xmlStrdup(val->nodesetval->nodeTab[0]->name);
5428: ret->user = top;
5429: if (top != NULL) {
5430: top->doc = top;
5431: cur = val->nodesetval->nodeTab[0]->children;
5432: while (cur != NULL) {
5433: tmp = xmlDocCopyNode(cur, top, 1);
5434: xmlAddChild((xmlNodePtr) top, tmp);
5435: cur = cur->next;
5436: }
5437: }
5438:
5439: ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
5440: } else
5441: ret->nodesetval = xmlXPathNodeSetCreate(NULL);
5442: /* Deallocate the copied tree value */
5443: break;
5444: #endif
5445: case XPATH_NODESET:
5446: ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
5447: /* Do not deallocate the copied tree value */
5448: ret->boolval = 0;
5449: break;
5450: case XPATH_LOCATIONSET:
5451: #ifdef LIBXML_XPTR_ENABLED
5452: {
5453: xmlLocationSetPtr loc = val->user;
5454: ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
5455: break;
5456: }
5457: #endif
5458: case XPATH_USERS:
5459: ret->user = val->user;
5460: break;
5461: case XPATH_UNDEFINED:
5462: xmlGenericError(xmlGenericErrorContext,
5463: "xmlXPathObjectCopy: unsupported type %d\n",
5464: val->type);
5465: break;
5466: }
5467: return(ret);
5468: }
5469:
5470: /**
5471: * xmlXPathFreeObject:
5472: * @obj: the object to free
5473: *
5474: * Free up an xmlXPathObjectPtr object.
5475: */
5476: void
5477: xmlXPathFreeObject(xmlXPathObjectPtr obj) {
5478: if (obj == NULL) return;
5479: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
5480: if (obj->boolval) {
5481: #if 0
5482: if (obj->user != NULL) {
5483: xmlXPathFreeNodeSet(obj->nodesetval);
5484: xmlFreeNodeList((xmlNodePtr) obj->user);
5485: } else
5486: #endif
5487: obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
5488: if (obj->nodesetval != NULL)
5489: xmlXPathFreeValueTree(obj->nodesetval);
5490: } else {
5491: if (obj->nodesetval != NULL)
5492: xmlXPathFreeNodeSet(obj->nodesetval);
5493: }
5494: #ifdef LIBXML_XPTR_ENABLED
5495: } else if (obj->type == XPATH_LOCATIONSET) {
5496: if (obj->user != NULL)
5497: xmlXPtrFreeLocationSet(obj->user);
5498: #endif
5499: } else if (obj->type == XPATH_STRING) {
5500: if (obj->stringval != NULL)
5501: xmlFree(obj->stringval);
5502: }
5503: #ifdef XP_DEBUG_OBJ_USAGE
5504: xmlXPathDebugObjUsageReleased(NULL, obj->type);
5505: #endif
5506: xmlFree(obj);
5507: }
5508:
5509: /**
5510: * xmlXPathReleaseObject:
5511: * @obj: the xmlXPathObjectPtr to free or to cache
5512: *
5513: * Depending on the state of the cache this frees the given
5514: * XPath object or stores it in the cache.
5515: */
5516: static void
5517: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
5518: {
5519: #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
5520: sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
5521: if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
5522:
5523: #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
5524:
5525: if (obj == NULL)
5526: return;
5527: if ((ctxt == NULL) || (ctxt->cache == NULL)) {
5528: xmlXPathFreeObject(obj);
5529: } else {
5530: xmlXPathContextCachePtr cache =
5531: (xmlXPathContextCachePtr) ctxt->cache;
5532:
5533: switch (obj->type) {
5534: case XPATH_NODESET:
5535: case XPATH_XSLT_TREE:
5536: if (obj->nodesetval != NULL) {
5537: if (obj->boolval) {
5538: /*
5539: * It looks like the @boolval is used for
5540: * evaluation if this an XSLT Result Tree Fragment.
5541: * TODO: Check if this assumption is correct.
5542: */
5543: obj->type = XPATH_XSLT_TREE; /* just for debugging */
5544: xmlXPathFreeValueTree(obj->nodesetval);
5545: obj->nodesetval = NULL;
5546: } else if ((obj->nodesetval->nodeMax <= 40) &&
5547: (XP_CACHE_WANTS(cache->nodesetObjs,
5548: cache->maxNodeset)))
5549: {
5550: XP_CACHE_ADD(cache->nodesetObjs, obj);
5551: goto obj_cached;
5552: } else {
5553: xmlXPathFreeNodeSet(obj->nodesetval);
5554: obj->nodesetval = NULL;
5555: }
5556: }
5557: break;
5558: case XPATH_STRING:
5559: if (obj->stringval != NULL)
5560: xmlFree(obj->stringval);
5561:
5562: if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
5563: XP_CACHE_ADD(cache->stringObjs, obj);
5564: goto obj_cached;
5565: }
5566: break;
5567: case XPATH_BOOLEAN:
5568: if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
5569: XP_CACHE_ADD(cache->booleanObjs, obj);
5570: goto obj_cached;
5571: }
5572: break;
5573: case XPATH_NUMBER:
5574: if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
5575: XP_CACHE_ADD(cache->numberObjs, obj);
5576: goto obj_cached;
5577: }
5578: break;
5579: #ifdef LIBXML_XPTR_ENABLED
5580: case XPATH_LOCATIONSET:
5581: if (obj->user != NULL) {
5582: xmlXPtrFreeLocationSet(obj->user);
5583: }
5584: goto free_obj;
5585: #endif
5586: default:
5587: goto free_obj;
5588: }
5589:
5590: /*
5591: * Fallback to adding to the misc-objects slot.
5592: */
5593: if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
5594: XP_CACHE_ADD(cache->miscObjs, obj);
5595: } else
5596: goto free_obj;
5597:
5598: obj_cached:
5599:
5600: #ifdef XP_DEBUG_OBJ_USAGE
5601: xmlXPathDebugObjUsageReleased(ctxt, obj->type);
5602: #endif
5603:
5604: if (obj->nodesetval != NULL) {
5605: xmlNodeSetPtr tmpset = obj->nodesetval;
5606:
5607: /*
5608: * TODO: Due to those nasty ns-nodes, we need to traverse
5609: * the list and free the ns-nodes.
5610: * URGENT TODO: Check if it's actually slowing things down.
5611: * Maybe we shouldn't try to preserve the list.
5612: */
5613: if (tmpset->nodeNr > 1) {
5614: int i;
5615: xmlNodePtr node;
5616:
5617: for (i = 0; i < tmpset->nodeNr; i++) {
5618: node = tmpset->nodeTab[i];
5619: if ((node != NULL) &&
5620: (node->type == XML_NAMESPACE_DECL))
5621: {
5622: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
5623: }
5624: }
5625: } else if (tmpset->nodeNr == 1) {
5626: if ((tmpset->nodeTab[0] != NULL) &&
5627: (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
5628: xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
5629: }
5630: tmpset->nodeNr = 0;
5631: memset(obj, 0, sizeof(xmlXPathObject));
5632: obj->nodesetval = tmpset;
5633: } else
5634: memset(obj, 0, sizeof(xmlXPathObject));
5635:
5636: return;
5637:
5638: free_obj:
5639: /*
5640: * Cache is full; free the object.
5641: */
5642: if (obj->nodesetval != NULL)
5643: xmlXPathFreeNodeSet(obj->nodesetval);
5644: #ifdef XP_DEBUG_OBJ_USAGE
5645: xmlXPathDebugObjUsageReleased(NULL, obj->type);
5646: #endif
5647: xmlFree(obj);
5648: }
5649: return;
5650: }
5651:
5652:
5653: /************************************************************************
5654: * *
5655: * Type Casting Routines *
5656: * *
5657: ************************************************************************/
5658:
5659: /**
5660: * xmlXPathCastBooleanToString:
5661: * @val: a boolean
5662: *
5663: * Converts a boolean to its string value.
5664: *
5665: * Returns a newly allocated string.
5666: */
5667: xmlChar *
5668: xmlXPathCastBooleanToString (int val) {
5669: xmlChar *ret;
5670: if (val)
5671: ret = xmlStrdup((const xmlChar *) "true");
5672: else
5673: ret = xmlStrdup((const xmlChar *) "false");
5674: return(ret);
5675: }
5676:
5677: /**
5678: * xmlXPathCastNumberToString:
5679: * @val: a number
5680: *
5681: * Converts a number to its string value.
5682: *
5683: * Returns a newly allocated string.
5684: */
5685: xmlChar *
5686: xmlXPathCastNumberToString (double val) {
5687: xmlChar *ret;
5688: switch (xmlXPathIsInf(val)) {
5689: case 1:
5690: ret = xmlStrdup((const xmlChar *) "Infinity");
5691: break;
5692: case -1:
5693: ret = xmlStrdup((const xmlChar *) "-Infinity");
5694: break;
5695: default:
5696: if (xmlXPathIsNaN(val)) {
5697: ret = xmlStrdup((const xmlChar *) "NaN");
5698: } else if (val == 0 && xmlXPathGetSign(val) != 0) {
5699: ret = xmlStrdup((const xmlChar *) "0");
5700: } else {
5701: /* could be improved */
5702: char buf[100];
5703: xmlXPathFormatNumber(val, buf, 99);
5704: buf[99] = 0;
5705: ret = xmlStrdup((const xmlChar *) buf);
5706: }
5707: }
5708: return(ret);
5709: }
5710:
5711: /**
5712: * xmlXPathCastNodeToString:
5713: * @node: a node
5714: *
5715: * Converts a node to its string value.
5716: *
5717: * Returns a newly allocated string.
5718: */
5719: xmlChar *
5720: xmlXPathCastNodeToString (xmlNodePtr node) {
5721: xmlChar *ret;
5722: if ((ret = xmlNodeGetContent(node)) == NULL)
5723: ret = xmlStrdup((const xmlChar *) "");
5724: return(ret);
5725: }
5726:
5727: /**
5728: * xmlXPathCastNodeSetToString:
5729: * @ns: a node-set
5730: *
5731: * Converts a node-set to its string value.
5732: *
5733: * Returns a newly allocated string.
5734: */
5735: xmlChar *
5736: xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
5737: if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
5738: return(xmlStrdup((const xmlChar *) ""));
5739:
5740: if (ns->nodeNr > 1)
5741: xmlXPathNodeSetSort(ns);
5742: return(xmlXPathCastNodeToString(ns->nodeTab[0]));
5743: }
5744:
5745: /**
5746: * xmlXPathCastToString:
5747: * @val: an XPath object
5748: *
5749: * Converts an existing object to its string() equivalent
5750: *
5751: * Returns the allocated string value of the object, NULL in case of error.
5752: * It's up to the caller to free the string memory with xmlFree().
5753: */
5754: xmlChar *
5755: xmlXPathCastToString(xmlXPathObjectPtr val) {
5756: xmlChar *ret = NULL;
5757:
5758: if (val == NULL)
5759: return(xmlStrdup((const xmlChar *) ""));
5760: switch (val->type) {
5761: case XPATH_UNDEFINED:
5762: #ifdef DEBUG_EXPR
5763: xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
5764: #endif
5765: ret = xmlStrdup((const xmlChar *) "");
5766: break;
5767: case XPATH_NODESET:
5768: case XPATH_XSLT_TREE:
5769: ret = xmlXPathCastNodeSetToString(val->nodesetval);
5770: break;
5771: case XPATH_STRING:
5772: return(xmlStrdup(val->stringval));
5773: case XPATH_BOOLEAN:
5774: ret = xmlXPathCastBooleanToString(val->boolval);
5775: break;
5776: case XPATH_NUMBER: {
5777: ret = xmlXPathCastNumberToString(val->floatval);
5778: break;
5779: }
5780: case XPATH_USERS:
5781: case XPATH_POINT:
5782: case XPATH_RANGE:
5783: case XPATH_LOCATIONSET:
5784: TODO
5785: ret = xmlStrdup((const xmlChar *) "");
5786: break;
5787: }
5788: return(ret);
5789: }
5790:
5791: /**
5792: * xmlXPathConvertString:
5793: * @val: an XPath object
5794: *
5795: * Converts an existing object to its string() equivalent
5796: *
5797: * Returns the new object, the old one is freed (or the operation
5798: * is done directly on @val)
5799: */
5800: xmlXPathObjectPtr
5801: xmlXPathConvertString(xmlXPathObjectPtr val) {
5802: xmlChar *res = NULL;
5803:
5804: if (val == NULL)
5805: return(xmlXPathNewCString(""));
5806:
5807: switch (val->type) {
5808: case XPATH_UNDEFINED:
5809: #ifdef DEBUG_EXPR
5810: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
5811: #endif
5812: break;
5813: case XPATH_NODESET:
5814: case XPATH_XSLT_TREE:
5815: res = xmlXPathCastNodeSetToString(val->nodesetval);
5816: break;
5817: case XPATH_STRING:
5818: return(val);
5819: case XPATH_BOOLEAN:
5820: res = xmlXPathCastBooleanToString(val->boolval);
5821: break;
5822: case XPATH_NUMBER:
5823: res = xmlXPathCastNumberToString(val->floatval);
5824: break;
5825: case XPATH_USERS:
5826: case XPATH_POINT:
5827: case XPATH_RANGE:
5828: case XPATH_LOCATIONSET:
5829: TODO;
5830: break;
5831: }
5832: xmlXPathFreeObject(val);
5833: if (res == NULL)
5834: return(xmlXPathNewCString(""));
5835: return(xmlXPathWrapString(res));
5836: }
5837:
5838: /**
5839: * xmlXPathCastBooleanToNumber:
5840: * @val: a boolean
5841: *
5842: * Converts a boolean to its number value
5843: *
5844: * Returns the number value
5845: */
5846: double
5847: xmlXPathCastBooleanToNumber(int val) {
5848: if (val)
5849: return(1.0);
5850: return(0.0);
5851: }
5852:
5853: /**
5854: * xmlXPathCastStringToNumber:
5855: * @val: a string
5856: *
5857: * Converts a string to its number value
5858: *
5859: * Returns the number value
5860: */
5861: double
5862: xmlXPathCastStringToNumber(const xmlChar * val) {
5863: return(xmlXPathStringEvalNumber(val));
5864: }
5865:
5866: /**
5867: * xmlXPathCastNodeToNumber:
5868: * @node: a node
5869: *
5870: * Converts a node to its number value
5871: *
5872: * Returns the number value
5873: */
5874: double
5875: xmlXPathCastNodeToNumber (xmlNodePtr node) {
5876: xmlChar *strval;
5877: double ret;
5878:
5879: if (node == NULL)
5880: return(xmlXPathNAN);
5881: strval = xmlXPathCastNodeToString(node);
5882: if (strval == NULL)
5883: return(xmlXPathNAN);
5884: ret = xmlXPathCastStringToNumber(strval);
5885: xmlFree(strval);
5886:
5887: return(ret);
5888: }
5889:
5890: /**
5891: * xmlXPathCastNodeSetToNumber:
5892: * @ns: a node-set
5893: *
5894: * Converts a node-set to its number value
5895: *
5896: * Returns the number value
5897: */
5898: double
5899: xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
5900: xmlChar *str;
5901: double ret;
5902:
5903: if (ns == NULL)
5904: return(xmlXPathNAN);
5905: str = xmlXPathCastNodeSetToString(ns);
5906: ret = xmlXPathCastStringToNumber(str);
5907: xmlFree(str);
5908: return(ret);
5909: }
5910:
5911: /**
5912: * xmlXPathCastToNumber:
5913: * @val: an XPath object
5914: *
5915: * Converts an XPath object to its number value
5916: *
5917: * Returns the number value
5918: */
5919: double
5920: xmlXPathCastToNumber(xmlXPathObjectPtr val) {
5921: double ret = 0.0;
5922:
5923: if (val == NULL)
5924: return(xmlXPathNAN);
5925: switch (val->type) {
5926: case XPATH_UNDEFINED:
5927: #ifdef DEGUB_EXPR
5928: xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5929: #endif
5930: ret = xmlXPathNAN;
5931: break;
5932: case XPATH_NODESET:
5933: case XPATH_XSLT_TREE:
5934: ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
5935: break;
5936: case XPATH_STRING:
5937: ret = xmlXPathCastStringToNumber(val->stringval);
5938: break;
5939: case XPATH_NUMBER:
5940: ret = val->floatval;
5941: break;
5942: case XPATH_BOOLEAN:
5943: ret = xmlXPathCastBooleanToNumber(val->boolval);
5944: break;
5945: case XPATH_USERS:
5946: case XPATH_POINT:
5947: case XPATH_RANGE:
5948: case XPATH_LOCATIONSET:
5949: TODO;
5950: ret = xmlXPathNAN;
5951: break;
5952: }
5953: return(ret);
5954: }
5955:
5956: /**
5957: * xmlXPathConvertNumber:
5958: * @val: an XPath object
5959: *
5960: * Converts an existing object to its number() equivalent
5961: *
5962: * Returns the new object, the old one is freed (or the operation
5963: * is done directly on @val)
5964: */
5965: xmlXPathObjectPtr
5966: xmlXPathConvertNumber(xmlXPathObjectPtr val) {
5967: xmlXPathObjectPtr ret;
5968:
5969: if (val == NULL)
5970: return(xmlXPathNewFloat(0.0));
5971: if (val->type == XPATH_NUMBER)
5972: return(val);
5973: ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
5974: xmlXPathFreeObject(val);
5975: return(ret);
5976: }
5977:
5978: /**
5979: * xmlXPathCastNumberToBoolean:
5980: * @val: a number
5981: *
5982: * Converts a number to its boolean value
5983: *
5984: * Returns the boolean value
5985: */
5986: int
5987: xmlXPathCastNumberToBoolean (double val) {
5988: if (xmlXPathIsNaN(val) || (val == 0.0))
5989: return(0);
5990: return(1);
5991: }
5992:
5993: /**
5994: * xmlXPathCastStringToBoolean:
5995: * @val: a string
5996: *
5997: * Converts a string to its boolean value
5998: *
5999: * Returns the boolean value
6000: */
6001: int
6002: xmlXPathCastStringToBoolean (const xmlChar *val) {
6003: if ((val == NULL) || (xmlStrlen(val) == 0))
6004: return(0);
6005: return(1);
6006: }
6007:
6008: /**
6009: * xmlXPathCastNodeSetToBoolean:
6010: * @ns: a node-set
6011: *
6012: * Converts a node-set to its boolean value
6013: *
6014: * Returns the boolean value
6015: */
6016: int
6017: xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
6018: if ((ns == NULL) || (ns->nodeNr == 0))
6019: return(0);
6020: return(1);
6021: }
6022:
6023: /**
6024: * xmlXPathCastToBoolean:
6025: * @val: an XPath object
6026: *
6027: * Converts an XPath object to its boolean value
6028: *
6029: * Returns the boolean value
6030: */
6031: int
6032: xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
6033: int ret = 0;
6034:
6035: if (val == NULL)
6036: return(0);
6037: switch (val->type) {
6038: case XPATH_UNDEFINED:
6039: #ifdef DEBUG_EXPR
6040: xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
6041: #endif
6042: ret = 0;
6043: break;
6044: case XPATH_NODESET:
6045: case XPATH_XSLT_TREE:
6046: ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
6047: break;
6048: case XPATH_STRING:
6049: ret = xmlXPathCastStringToBoolean(val->stringval);
6050: break;
6051: case XPATH_NUMBER:
6052: ret = xmlXPathCastNumberToBoolean(val->floatval);
6053: break;
6054: case XPATH_BOOLEAN:
6055: ret = val->boolval;
6056: break;
6057: case XPATH_USERS:
6058: case XPATH_POINT:
6059: case XPATH_RANGE:
6060: case XPATH_LOCATIONSET:
6061: TODO;
6062: ret = 0;
6063: break;
6064: }
6065: return(ret);
6066: }
6067:
6068:
6069: /**
6070: * xmlXPathConvertBoolean:
6071: * @val: an XPath object
6072: *
6073: * Converts an existing object to its boolean() equivalent
6074: *
6075: * Returns the new object, the old one is freed (or the operation
6076: * is done directly on @val)
6077: */
6078: xmlXPathObjectPtr
6079: xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
6080: xmlXPathObjectPtr ret;
6081:
6082: if (val == NULL)
6083: return(xmlXPathNewBoolean(0));
6084: if (val->type == XPATH_BOOLEAN)
6085: return(val);
6086: ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
6087: xmlXPathFreeObject(val);
6088: return(ret);
6089: }
6090:
6091: /************************************************************************
6092: * *
6093: * Routines to handle XPath contexts *
6094: * *
6095: ************************************************************************/
6096:
6097: /**
6098: * xmlXPathNewContext:
6099: * @doc: the XML document
6100: *
6101: * Create a new xmlXPathContext
6102: *
6103: * Returns the xmlXPathContext just allocated. The caller will need to free it.
6104: */
6105: xmlXPathContextPtr
6106: xmlXPathNewContext(xmlDocPtr doc) {
6107: xmlXPathContextPtr ret;
6108:
6109: ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
6110: if (ret == NULL) {
6111: xmlXPathErrMemory(NULL, "creating context\n");
6112: return(NULL);
6113: }
6114: memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
6115: ret->doc = doc;
6116: ret->node = NULL;
6117:
6118: ret->varHash = NULL;
6119:
6120: ret->nb_types = 0;
6121: ret->max_types = 0;
6122: ret->types = NULL;
6123:
6124: ret->funcHash = xmlHashCreate(0);
6125:
6126: ret->nb_axis = 0;
6127: ret->max_axis = 0;
6128: ret->axis = NULL;
6129:
6130: ret->nsHash = NULL;
6131: ret->user = NULL;
6132:
6133: ret->contextSize = -1;
6134: ret->proximityPosition = -1;
6135:
6136: #ifdef XP_DEFAULT_CACHE_ON
6137: if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
6138: xmlXPathFreeContext(ret);
6139: return(NULL);
6140: }
6141: #endif
6142:
6143: xmlXPathRegisterAllFunctions(ret);
6144:
6145: return(ret);
6146: }
6147:
6148: /**
6149: * xmlXPathFreeContext:
6150: * @ctxt: the context to free
6151: *
6152: * Free up an xmlXPathContext
6153: */
6154: void
6155: xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
6156: if (ctxt == NULL) return;
6157:
6158: if (ctxt->cache != NULL)
6159: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
6160: xmlXPathRegisteredNsCleanup(ctxt);
6161: xmlXPathRegisteredFuncsCleanup(ctxt);
6162: xmlXPathRegisteredVariablesCleanup(ctxt);
6163: xmlResetError(&ctxt->lastError);
6164: xmlFree(ctxt);
6165: }
6166:
6167: /************************************************************************
6168: * *
6169: * Routines to handle XPath parser contexts *
6170: * *
6171: ************************************************************************/
6172:
6173: #define CHECK_CTXT(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(NULL); \
6182: } \
6183:
6184: #define CHECK_CTXT_NEG(ctxt) \
6185: if (ctxt == NULL) { \
6186: __xmlRaiseError(NULL, NULL, NULL, \
6187: NULL, NULL, XML_FROM_XPATH, \
6188: XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
6189: __FILE__, __LINE__, \
6190: NULL, NULL, NULL, 0, 0, \
6191: "NULL context pointer\n"); \
6192: return(-1); \
6193: } \
6194:
6195:
6196: #define CHECK_CONTEXT(ctxt) \
6197: if ((ctxt == NULL) || (ctxt->doc == NULL) || \
6198: (ctxt->doc->children == NULL)) { \
6199: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \
6200: return(NULL); \
6201: }
6202:
6203:
6204: /**
6205: * xmlXPathNewParserContext:
6206: * @str: the XPath expression
6207: * @ctxt: the XPath context
6208: *
6209: * Create a new xmlXPathParserContext
6210: *
6211: * Returns the xmlXPathParserContext just allocated.
6212: */
6213: xmlXPathParserContextPtr
6214: xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
6215: xmlXPathParserContextPtr ret;
6216:
6217: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6218: if (ret == NULL) {
6219: xmlXPathErrMemory(ctxt, "creating parser context\n");
6220: return(NULL);
6221: }
6222: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
6223: ret->cur = ret->base = str;
6224: ret->context = ctxt;
6225:
6226: ret->comp = xmlXPathNewCompExpr();
6227: if (ret->comp == NULL) {
6228: xmlFree(ret->valueTab);
6229: xmlFree(ret);
6230: return(NULL);
6231: }
6232: if ((ctxt != NULL) && (ctxt->dict != NULL)) {
6233: ret->comp->dict = ctxt->dict;
6234: xmlDictReference(ret->comp->dict);
6235: }
6236:
6237: return(ret);
6238: }
6239:
6240: /**
6241: * xmlXPathCompParserContext:
6242: * @comp: the XPath compiled expression
6243: * @ctxt: the XPath context
6244: *
6245: * Create a new xmlXPathParserContext when processing a compiled expression
6246: *
6247: * Returns the xmlXPathParserContext just allocated.
6248: */
6249: static xmlXPathParserContextPtr
6250: xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
6251: xmlXPathParserContextPtr ret;
6252:
6253: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6254: if (ret == NULL) {
6255: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6256: return(NULL);
6257: }
6258: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
6259:
6260: /* Allocate the value stack */
6261: ret->valueTab = (xmlXPathObjectPtr *)
6262: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
6263: if (ret->valueTab == NULL) {
6264: xmlFree(ret);
6265: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6266: return(NULL);
6267: }
6268: ret->valueNr = 0;
6269: ret->valueMax = 10;
6270: ret->value = NULL;
1.1.1.2 misho 6271: ret->valueFrame = 0;
1.1 misho 6272:
6273: ret->context = ctxt;
6274: ret->comp = comp;
6275:
6276: return(ret);
6277: }
6278:
6279: /**
6280: * xmlXPathFreeParserContext:
6281: * @ctxt: the context to free
6282: *
6283: * Free up an xmlXPathParserContext
6284: */
6285: void
6286: xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
6287: if (ctxt->valueTab != NULL) {
6288: xmlFree(ctxt->valueTab);
6289: }
6290: if (ctxt->comp != NULL) {
6291: #ifdef XPATH_STREAMING
6292: if (ctxt->comp->stream != NULL) {
6293: xmlFreePatternList(ctxt->comp->stream);
6294: ctxt->comp->stream = NULL;
6295: }
6296: #endif
6297: xmlXPathFreeCompExpr(ctxt->comp);
6298: }
6299: xmlFree(ctxt);
6300: }
6301:
6302: /************************************************************************
6303: * *
6304: * The implicit core function library *
6305: * *
6306: ************************************************************************/
6307:
6308: /**
6309: * xmlXPathNodeValHash:
6310: * @node: a node pointer
6311: *
6312: * Function computing the beginning of the string value of the node,
6313: * used to speed up comparisons
6314: *
6315: * Returns an int usable as a hash
6316: */
6317: static unsigned int
6318: xmlXPathNodeValHash(xmlNodePtr node) {
6319: int len = 2;
6320: const xmlChar * string = NULL;
6321: xmlNodePtr tmp = NULL;
6322: unsigned int ret = 0;
6323:
6324: if (node == NULL)
6325: return(0);
6326:
6327: if (node->type == XML_DOCUMENT_NODE) {
6328: tmp = xmlDocGetRootElement((xmlDocPtr) node);
6329: if (tmp == NULL)
6330: node = node->children;
6331: else
6332: node = tmp;
6333:
6334: if (node == NULL)
6335: return(0);
6336: }
6337:
6338: switch (node->type) {
6339: case XML_COMMENT_NODE:
6340: case XML_PI_NODE:
6341: case XML_CDATA_SECTION_NODE:
6342: case XML_TEXT_NODE:
6343: string = node->content;
6344: if (string == NULL)
6345: return(0);
6346: if (string[0] == 0)
6347: return(0);
6348: return(((unsigned int) string[0]) +
6349: (((unsigned int) string[1]) << 8));
6350: case XML_NAMESPACE_DECL:
6351: string = ((xmlNsPtr)node)->href;
6352: if (string == NULL)
6353: return(0);
6354: if (string[0] == 0)
6355: return(0);
6356: return(((unsigned int) string[0]) +
6357: (((unsigned int) string[1]) << 8));
6358: case XML_ATTRIBUTE_NODE:
6359: tmp = ((xmlAttrPtr) node)->children;
6360: break;
6361: case XML_ELEMENT_NODE:
6362: tmp = node->children;
6363: break;
6364: default:
6365: return(0);
6366: }
6367: while (tmp != NULL) {
6368: switch (tmp->type) {
6369: case XML_COMMENT_NODE:
6370: case XML_PI_NODE:
6371: case XML_CDATA_SECTION_NODE:
6372: case XML_TEXT_NODE:
6373: string = tmp->content;
6374: break;
6375: case XML_NAMESPACE_DECL:
6376: string = ((xmlNsPtr)tmp)->href;
6377: break;
6378: default:
6379: break;
6380: }
6381: if ((string != NULL) && (string[0] != 0)) {
6382: if (len == 1) {
6383: return(ret + (((unsigned int) string[0]) << 8));
6384: }
6385: if (string[1] == 0) {
6386: len = 1;
6387: ret = (unsigned int) string[0];
6388: } else {
6389: return(((unsigned int) string[0]) +
6390: (((unsigned int) string[1]) << 8));
6391: }
6392: }
6393: /*
6394: * Skip to next node
6395: */
6396: if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
6397: if (tmp->children->type != XML_ENTITY_DECL) {
6398: tmp = tmp->children;
6399: continue;
6400: }
6401: }
6402: if (tmp == node)
6403: break;
6404:
6405: if (tmp->next != NULL) {
6406: tmp = tmp->next;
6407: continue;
6408: }
6409:
6410: do {
6411: tmp = tmp->parent;
6412: if (tmp == NULL)
6413: break;
6414: if (tmp == node) {
6415: tmp = NULL;
6416: break;
6417: }
6418: if (tmp->next != NULL) {
6419: tmp = tmp->next;
6420: break;
6421: }
6422: } while (tmp != NULL);
6423: }
6424: return(ret);
6425: }
6426:
6427: /**
6428: * xmlXPathStringHash:
6429: * @string: a string
6430: *
6431: * Function computing the beginning of the string value of the node,
6432: * used to speed up comparisons
6433: *
6434: * Returns an int usable as a hash
6435: */
6436: static unsigned int
6437: xmlXPathStringHash(const xmlChar * string) {
6438: if (string == NULL)
6439: return((unsigned int) 0);
6440: if (string[0] == 0)
6441: return(0);
6442: return(((unsigned int) string[0]) +
6443: (((unsigned int) string[1]) << 8));
6444: }
6445:
6446: /**
6447: * xmlXPathCompareNodeSetFloat:
6448: * @ctxt: the XPath Parser context
6449: * @inf: less than (1) or greater than (0)
6450: * @strict: is the comparison strict
6451: * @arg: the node set
6452: * @f: the value
6453: *
6454: * Implement the compare operation between a nodeset and a number
6455: * @ns < @val (1, 1, ...
6456: * @ns <= @val (1, 0, ...
6457: * @ns > @val (0, 1, ...
6458: * @ns >= @val (0, 0, ...
6459: *
6460: * If one object to be compared is a node-set and the other is a number,
6461: * then the comparison will be true if and only if there is a node in the
6462: * node-set such that the result of performing the comparison on the number
6463: * to be compared and on the result of converting the string-value of that
6464: * node to a number using the number function is true.
6465: *
6466: * Returns 0 or 1 depending on the results of the test.
6467: */
6468: static int
6469: xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
6470: xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
6471: int i, ret = 0;
6472: xmlNodeSetPtr ns;
6473: xmlChar *str2;
6474:
6475: if ((f == NULL) || (arg == NULL) ||
6476: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6477: xmlXPathReleaseObject(ctxt->context, arg);
6478: xmlXPathReleaseObject(ctxt->context, f);
6479: return(0);
6480: }
6481: ns = arg->nodesetval;
6482: if (ns != NULL) {
6483: for (i = 0;i < ns->nodeNr;i++) {
6484: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6485: if (str2 != NULL) {
6486: valuePush(ctxt,
6487: xmlXPathCacheNewString(ctxt->context, str2));
6488: xmlFree(str2);
6489: xmlXPathNumberFunction(ctxt, 1);
6490: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
6491: ret = xmlXPathCompareValues(ctxt, inf, strict);
6492: if (ret)
6493: break;
6494: }
6495: }
6496: }
6497: xmlXPathReleaseObject(ctxt->context, arg);
6498: xmlXPathReleaseObject(ctxt->context, f);
6499: return(ret);
6500: }
6501:
6502: /**
6503: * xmlXPathCompareNodeSetString:
6504: * @ctxt: the XPath Parser context
6505: * @inf: less than (1) or greater than (0)
6506: * @strict: is the comparison strict
6507: * @arg: the node set
6508: * @s: the value
6509: *
6510: * Implement the compare operation between a nodeset and a string
6511: * @ns < @val (1, 1, ...
6512: * @ns <= @val (1, 0, ...
6513: * @ns > @val (0, 1, ...
6514: * @ns >= @val (0, 0, ...
6515: *
6516: * If one object to be compared is a node-set and the other is a string,
6517: * then the comparison will be true if and only if there is a node in
6518: * the node-set such that the result of performing the comparison on the
6519: * string-value of the node and the other string is true.
6520: *
6521: * Returns 0 or 1 depending on the results of the test.
6522: */
6523: static int
6524: xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
6525: xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
6526: int i, ret = 0;
6527: xmlNodeSetPtr ns;
6528: xmlChar *str2;
6529:
6530: if ((s == NULL) || (arg == NULL) ||
6531: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6532: xmlXPathReleaseObject(ctxt->context, arg);
6533: xmlXPathReleaseObject(ctxt->context, s);
6534: return(0);
6535: }
6536: ns = arg->nodesetval;
6537: if (ns != NULL) {
6538: for (i = 0;i < ns->nodeNr;i++) {
6539: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6540: if (str2 != NULL) {
6541: valuePush(ctxt,
6542: xmlXPathCacheNewString(ctxt->context, str2));
6543: xmlFree(str2);
6544: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
6545: ret = xmlXPathCompareValues(ctxt, inf, strict);
6546: if (ret)
6547: break;
6548: }
6549: }
6550: }
6551: xmlXPathReleaseObject(ctxt->context, arg);
6552: xmlXPathReleaseObject(ctxt->context, s);
6553: return(ret);
6554: }
6555:
6556: /**
6557: * xmlXPathCompareNodeSets:
6558: * @inf: less than (1) or greater than (0)
6559: * @strict: is the comparison strict
6560: * @arg1: the first node set object
6561: * @arg2: the second node set object
6562: *
6563: * Implement the compare operation on nodesets:
6564: *
6565: * If both objects to be compared are node-sets, then the comparison
6566: * will be true if and only if there is a node in the first node-set
6567: * and a node in the second node-set such that the result of performing
6568: * the comparison on the string-values of the two nodes is true.
6569: * ....
6570: * When neither object to be compared is a node-set and the operator
6571: * is <=, <, >= or >, then the objects are compared by converting both
6572: * objects to numbers and comparing the numbers according to IEEE 754.
6573: * ....
6574: * The number function converts its argument to a number as follows:
6575: * - a string that consists of optional whitespace followed by an
6576: * optional minus sign followed by a Number followed by whitespace
6577: * is converted to the IEEE 754 number that is nearest (according
6578: * to the IEEE 754 round-to-nearest rule) to the mathematical value
6579: * represented by the string; any other string is converted to NaN
6580: *
6581: * Conclusion all nodes need to be converted first to their string value
6582: * and then the comparison must be done when possible
6583: */
6584: static int
6585: xmlXPathCompareNodeSets(int inf, int strict,
6586: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6587: int i, j, init = 0;
6588: double val1;
6589: double *values2;
6590: int ret = 0;
6591: xmlNodeSetPtr ns1;
6592: xmlNodeSetPtr ns2;
6593:
6594: if ((arg1 == NULL) ||
6595: ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
6596: xmlXPathFreeObject(arg2);
6597: return(0);
6598: }
6599: if ((arg2 == NULL) ||
6600: ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
6601: xmlXPathFreeObject(arg1);
6602: xmlXPathFreeObject(arg2);
6603: return(0);
6604: }
6605:
6606: ns1 = arg1->nodesetval;
6607: ns2 = arg2->nodesetval;
6608:
6609: if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
6610: xmlXPathFreeObject(arg1);
6611: xmlXPathFreeObject(arg2);
6612: return(0);
6613: }
6614: if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
6615: xmlXPathFreeObject(arg1);
6616: xmlXPathFreeObject(arg2);
6617: return(0);
6618: }
6619:
6620: values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
6621: if (values2 == NULL) {
6622: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6623: xmlXPathFreeObject(arg1);
6624: xmlXPathFreeObject(arg2);
6625: return(0);
6626: }
6627: for (i = 0;i < ns1->nodeNr;i++) {
6628: val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
6629: if (xmlXPathIsNaN(val1))
6630: continue;
6631: for (j = 0;j < ns2->nodeNr;j++) {
6632: if (init == 0) {
6633: values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
6634: }
6635: if (xmlXPathIsNaN(values2[j]))
6636: continue;
6637: if (inf && strict)
6638: ret = (val1 < values2[j]);
6639: else if (inf && !strict)
6640: ret = (val1 <= values2[j]);
6641: else if (!inf && strict)
6642: ret = (val1 > values2[j]);
6643: else if (!inf && !strict)
6644: ret = (val1 >= values2[j]);
6645: if (ret)
6646: break;
6647: }
6648: if (ret)
6649: break;
6650: init = 1;
6651: }
6652: xmlFree(values2);
6653: xmlXPathFreeObject(arg1);
6654: xmlXPathFreeObject(arg2);
6655: return(ret);
6656: }
6657:
6658: /**
6659: * xmlXPathCompareNodeSetValue:
6660: * @ctxt: the XPath Parser context
6661: * @inf: less than (1) or greater than (0)
6662: * @strict: is the comparison strict
6663: * @arg: the node set
6664: * @val: the value
6665: *
6666: * Implement the compare operation between a nodeset and a value
6667: * @ns < @val (1, 1, ...
6668: * @ns <= @val (1, 0, ...
6669: * @ns > @val (0, 1, ...
6670: * @ns >= @val (0, 0, ...
6671: *
6672: * If one object to be compared is a node-set and the other is a boolean,
6673: * then the comparison will be true if and only if the result of performing
6674: * the comparison on the boolean and on the result of converting
6675: * the node-set to a boolean using the boolean function is true.
6676: *
6677: * Returns 0 or 1 depending on the results of the test.
6678: */
6679: static int
6680: xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
6681: xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
6682: if ((val == NULL) || (arg == NULL) ||
6683: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6684: return(0);
6685:
6686: switch(val->type) {
6687: case XPATH_NUMBER:
6688: return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
6689: case XPATH_NODESET:
6690: case XPATH_XSLT_TREE:
6691: return(xmlXPathCompareNodeSets(inf, strict, arg, val));
6692: case XPATH_STRING:
6693: return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
6694: case XPATH_BOOLEAN:
6695: valuePush(ctxt, arg);
6696: xmlXPathBooleanFunction(ctxt, 1);
6697: valuePush(ctxt, val);
6698: return(xmlXPathCompareValues(ctxt, inf, strict));
6699: default:
6700: TODO
6701: }
6702: return(0);
6703: }
6704:
6705: /**
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)
6710: *
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.
6716: *
6717: * Returns 0 or 1 depending on the results of the test.
6718: */
6719: static int
6720: xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
6721: {
6722: int i;
6723: xmlNodeSetPtr ns;
6724: xmlChar *str2;
6725: unsigned int hash;
6726:
6727: if ((str == NULL) || (arg == NULL) ||
6728: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6729: return (0);
6730: ns = arg->nodesetval;
6731: /*
6732: * A NULL nodeset compared with a string is always false
6733: * (since there is no node equal, and no node not equal)
6734: */
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);
6754: }
6755: if (str2 != NULL)
6756: xmlFree(str2);
6757: } else if (neq)
6758: return (1);
6759: }
6760: return (0);
6761: }
6762:
6763: /**
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)
6768: *
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.
6775: *
6776: * Returns 0 or 1 depending on the results of the test.
6777: */
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;
6786:
6787: if ((arg == NULL) ||
6788: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6789: return(0);
6790:
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;
6809: }
6810: } else { /* NaN is unequal to any value */
6811: if (neq)
6812: ret = 1;
6813: }
6814: }
6815: }
6816: }
6817:
6818: return(ret);
6819: }
6820:
6821:
6822: /**
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)
6827: *
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.
6834: *
6835: * (needless to say, this is a costly operation)
6836: *
6837: * Returns 0 or 1 depending on the results of the test.
6838: */
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;
6849:
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);
6856:
6857: ns1 = arg1->nodesetval;
6858: ns2 = arg2->nodesetval;
6859:
6860: if ((ns1 == NULL) || (ns1->nodeNr <= 0))
6861: return(0);
6862: if ((ns2 == NULL) || (ns2->nodeNr <= 0))
6863: return(0);
6864:
6865: /*
6866: * for equal, check if there is a node pertaining to both sets
6867: */
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);
6873:
6874: values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
6875: if (values1 == NULL) {
6876: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6877: return(0);
6878: }
6879: hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
6880: if (hashs1 == NULL) {
6881: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6882: xmlFree(values1);
6883: return(0);
6884: }
6885: memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
6886: values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
6887: if (values2 == NULL) {
6888: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6889: xmlFree(hashs1);
6890: xmlFree(values1);
6891: return(0);
6892: }
6893: hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
6894: if (hashs2 == NULL) {
6895: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6896: xmlFree(hashs1);
6897: xmlFree(values1);
6898: xmlFree(values2);
6899: return(0);
6900: }
6901: memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
6902: for (i = 0;i < ns1->nodeNr;i++) {
6903: hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
6904: for (j = 0;j < ns2->nodeNr;j++) {
6905: if (i == 0)
6906: hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
6907: if (hashs1[i] != hashs2[j]) {
6908: if (neq) {
6909: ret = 1;
6910: break;
6911: }
6912: }
6913: else {
6914: if (values1[i] == NULL)
6915: values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
6916: if (values2[j] == NULL)
6917: values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
6918: ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
6919: if (ret)
6920: break;
6921: }
6922: }
6923: if (ret)
6924: break;
6925: }
6926: for (i = 0;i < ns1->nodeNr;i++)
6927: if (values1[i] != NULL)
6928: xmlFree(values1[i]);
6929: for (j = 0;j < ns2->nodeNr;j++)
6930: if (values2[j] != NULL)
6931: xmlFree(values2[j]);
6932: xmlFree(values1);
6933: xmlFree(values2);
6934: xmlFree(hashs1);
6935: xmlFree(hashs2);
6936: return(ret);
6937: }
6938:
6939: static int
6940: xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
6941: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6942: int ret = 0;
6943: /*
6944: *At this point we are assured neither arg1 nor arg2
6945: *is a nodeset, so we can just pick the appropriate routine.
6946: */
6947: switch (arg1->type) {
6948: case XPATH_UNDEFINED:
6949: #ifdef DEBUG_EXPR
6950: xmlGenericError(xmlGenericErrorContext,
6951: "Equal: undefined\n");
6952: #endif
6953: break;
6954: case XPATH_BOOLEAN:
6955: switch (arg2->type) {
6956: case XPATH_UNDEFINED:
6957: #ifdef DEBUG_EXPR
6958: xmlGenericError(xmlGenericErrorContext,
6959: "Equal: undefined\n");
6960: #endif
6961: break;
6962: case XPATH_BOOLEAN:
6963: #ifdef DEBUG_EXPR
6964: xmlGenericError(xmlGenericErrorContext,
6965: "Equal: %d boolean %d \n",
6966: arg1->boolval, arg2->boolval);
6967: #endif
6968: ret = (arg1->boolval == arg2->boolval);
6969: break;
6970: case XPATH_NUMBER:
6971: ret = (arg1->boolval ==
6972: xmlXPathCastNumberToBoolean(arg2->floatval));
6973: break;
6974: case XPATH_STRING:
6975: if ((arg2->stringval == NULL) ||
6976: (arg2->stringval[0] == 0)) ret = 0;
6977: else
6978: ret = 1;
6979: ret = (arg1->boolval == ret);
6980: break;
6981: case XPATH_USERS:
6982: case XPATH_POINT:
6983: case XPATH_RANGE:
6984: case XPATH_LOCATIONSET:
6985: TODO
6986: break;
6987: case XPATH_NODESET:
6988: case XPATH_XSLT_TREE:
6989: break;
6990: }
6991: break;
6992: case XPATH_NUMBER:
6993: switch (arg2->type) {
6994: case XPATH_UNDEFINED:
6995: #ifdef DEBUG_EXPR
6996: xmlGenericError(xmlGenericErrorContext,
6997: "Equal: undefined\n");
6998: #endif
6999: break;
7000: case XPATH_BOOLEAN:
7001: ret = (arg2->boolval==
7002: xmlXPathCastNumberToBoolean(arg1->floatval));
7003: break;
7004: case XPATH_STRING:
7005: valuePush(ctxt, arg2);
7006: xmlXPathNumberFunction(ctxt, 1);
7007: arg2 = valuePop(ctxt);
7008: /* no break on purpose */
7009: case XPATH_NUMBER:
7010: /* Hand check NaN and Infinity equalities */
7011: if (xmlXPathIsNaN(arg1->floatval) ||
7012: xmlXPathIsNaN(arg2->floatval)) {
7013: ret = 0;
7014: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
7015: if (xmlXPathIsInf(arg2->floatval) == 1)
7016: ret = 1;
7017: else
7018: ret = 0;
7019: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
7020: if (xmlXPathIsInf(arg2->floatval) == -1)
7021: ret = 1;
7022: else
7023: ret = 0;
7024: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
7025: if (xmlXPathIsInf(arg1->floatval) == 1)
7026: ret = 1;
7027: else
7028: ret = 0;
7029: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
7030: if (xmlXPathIsInf(arg1->floatval) == -1)
7031: ret = 1;
7032: else
7033: ret = 0;
7034: } else {
7035: ret = (arg1->floatval == arg2->floatval);
7036: }
7037: break;
7038: case XPATH_USERS:
7039: case XPATH_POINT:
7040: case XPATH_RANGE:
7041: case XPATH_LOCATIONSET:
7042: TODO
7043: break;
7044: case XPATH_NODESET:
7045: case XPATH_XSLT_TREE:
7046: break;
7047: }
7048: break;
7049: case XPATH_STRING:
7050: switch (arg2->type) {
7051: case XPATH_UNDEFINED:
7052: #ifdef DEBUG_EXPR
7053: xmlGenericError(xmlGenericErrorContext,
7054: "Equal: undefined\n");
7055: #endif
7056: break;
7057: case XPATH_BOOLEAN:
7058: if ((arg1->stringval == NULL) ||
7059: (arg1->stringval[0] == 0)) ret = 0;
7060: else
7061: ret = 1;
7062: ret = (arg2->boolval == ret);
7063: break;
7064: case XPATH_STRING:
7065: ret = xmlStrEqual(arg1->stringval, arg2->stringval);
7066: break;
7067: case XPATH_NUMBER:
7068: valuePush(ctxt, arg1);
7069: xmlXPathNumberFunction(ctxt, 1);
7070: arg1 = valuePop(ctxt);
7071: /* Hand check NaN and Infinity equalities */
7072: if (xmlXPathIsNaN(arg1->floatval) ||
7073: xmlXPathIsNaN(arg2->floatval)) {
7074: ret = 0;
7075: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
7076: if (xmlXPathIsInf(arg2->floatval) == 1)
7077: ret = 1;
7078: else
7079: ret = 0;
7080: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
7081: if (xmlXPathIsInf(arg2->floatval) == -1)
7082: ret = 1;
7083: else
7084: ret = 0;
7085: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
7086: if (xmlXPathIsInf(arg1->floatval) == 1)
7087: ret = 1;
7088: else
7089: ret = 0;
7090: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
7091: if (xmlXPathIsInf(arg1->floatval) == -1)
7092: ret = 1;
7093: else
7094: ret = 0;
7095: } else {
7096: ret = (arg1->floatval == arg2->floatval);
7097: }
7098: break;
7099: case XPATH_USERS:
7100: case XPATH_POINT:
7101: case XPATH_RANGE:
7102: case XPATH_LOCATIONSET:
7103: TODO
7104: break;
7105: case XPATH_NODESET:
7106: case XPATH_XSLT_TREE:
7107: break;
7108: }
7109: break;
7110: case XPATH_USERS:
7111: case XPATH_POINT:
7112: case XPATH_RANGE:
7113: case XPATH_LOCATIONSET:
7114: TODO
7115: break;
7116: case XPATH_NODESET:
7117: case XPATH_XSLT_TREE:
7118: break;
7119: }
7120: xmlXPathReleaseObject(ctxt->context, arg1);
7121: xmlXPathReleaseObject(ctxt->context, arg2);
7122: return(ret);
7123: }
7124:
7125: /**
7126: * xmlXPathEqualValues:
7127: * @ctxt: the XPath Parser context
7128: *
7129: * Implement the equal operation on XPath objects content: @arg1 == @arg2
7130: *
7131: * Returns 0 or 1 depending on the results of the test.
7132: */
7133: int
7134: xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
7135: xmlXPathObjectPtr arg1, arg2, argtmp;
7136: int ret = 0;
7137:
7138: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7139: arg2 = valuePop(ctxt);
7140: arg1 = valuePop(ctxt);
7141: if ((arg1 == NULL) || (arg2 == NULL)) {
7142: if (arg1 != NULL)
7143: xmlXPathReleaseObject(ctxt->context, arg1);
7144: else
7145: xmlXPathReleaseObject(ctxt->context, arg2);
7146: XP_ERROR0(XPATH_INVALID_OPERAND);
7147: }
7148:
7149: if (arg1 == arg2) {
7150: #ifdef DEBUG_EXPR
7151: xmlGenericError(xmlGenericErrorContext,
7152: "Equal: by pointer\n");
7153: #endif
7154: xmlXPathFreeObject(arg1);
7155: return(1);
7156: }
7157:
7158: /*
7159: *If either argument is a nodeset, it's a 'special case'
7160: */
7161: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7162: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7163: /*
7164: *Hack it to assure arg1 is the nodeset
7165: */
7166: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7167: argtmp = arg2;
7168: arg2 = arg1;
7169: arg1 = argtmp;
7170: }
7171: switch (arg2->type) {
7172: case XPATH_UNDEFINED:
7173: #ifdef DEBUG_EXPR
7174: xmlGenericError(xmlGenericErrorContext,
7175: "Equal: undefined\n");
7176: #endif
7177: break;
7178: case XPATH_NODESET:
7179: case XPATH_XSLT_TREE:
7180: ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
7181: break;
7182: case XPATH_BOOLEAN:
7183: if ((arg1->nodesetval == NULL) ||
7184: (arg1->nodesetval->nodeNr == 0)) ret = 0;
7185: else
7186: ret = 1;
7187: ret = (ret == arg2->boolval);
7188: break;
7189: case XPATH_NUMBER:
7190: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
7191: break;
7192: case XPATH_STRING:
7193: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
7194: break;
7195: case XPATH_USERS:
7196: case XPATH_POINT:
7197: case XPATH_RANGE:
7198: case XPATH_LOCATIONSET:
7199: TODO
7200: break;
7201: }
7202: xmlXPathReleaseObject(ctxt->context, arg1);
7203: xmlXPathReleaseObject(ctxt->context, arg2);
7204: return(ret);
7205: }
7206:
7207: return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7208: }
7209:
7210: /**
7211: * xmlXPathNotEqualValues:
7212: * @ctxt: the XPath Parser context
7213: *
7214: * Implement the equal operation on XPath objects content: @arg1 == @arg2
7215: *
7216: * Returns 0 or 1 depending on the results of the test.
7217: */
7218: int
7219: xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
7220: xmlXPathObjectPtr arg1, arg2, argtmp;
7221: int ret = 0;
7222:
7223: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7224: arg2 = valuePop(ctxt);
7225: arg1 = valuePop(ctxt);
7226: if ((arg1 == NULL) || (arg2 == NULL)) {
7227: if (arg1 != NULL)
7228: xmlXPathReleaseObject(ctxt->context, arg1);
7229: else
7230: xmlXPathReleaseObject(ctxt->context, arg2);
7231: XP_ERROR0(XPATH_INVALID_OPERAND);
7232: }
7233:
7234: if (arg1 == arg2) {
7235: #ifdef DEBUG_EXPR
7236: xmlGenericError(xmlGenericErrorContext,
7237: "NotEqual: by pointer\n");
7238: #endif
7239: xmlXPathReleaseObject(ctxt->context, arg1);
7240: return(0);
7241: }
7242:
7243: /*
7244: *If either argument is a nodeset, it's a 'special case'
7245: */
7246: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7247: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7248: /*
7249: *Hack it to assure arg1 is the nodeset
7250: */
7251: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7252: argtmp = arg2;
7253: arg2 = arg1;
7254: arg1 = argtmp;
7255: }
7256: switch (arg2->type) {
7257: case XPATH_UNDEFINED:
7258: #ifdef DEBUG_EXPR
7259: xmlGenericError(xmlGenericErrorContext,
7260: "NotEqual: undefined\n");
7261: #endif
7262: break;
7263: case XPATH_NODESET:
7264: case XPATH_XSLT_TREE:
7265: ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
7266: break;
7267: case XPATH_BOOLEAN:
7268: if ((arg1->nodesetval == NULL) ||
7269: (arg1->nodesetval->nodeNr == 0)) ret = 0;
7270: else
7271: ret = 1;
7272: ret = (ret != arg2->boolval);
7273: break;
7274: case XPATH_NUMBER:
7275: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
7276: break;
7277: case XPATH_STRING:
7278: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
7279: break;
7280: case XPATH_USERS:
7281: case XPATH_POINT:
7282: case XPATH_RANGE:
7283: case XPATH_LOCATIONSET:
7284: TODO
7285: break;
7286: }
7287: xmlXPathReleaseObject(ctxt->context, arg1);
7288: xmlXPathReleaseObject(ctxt->context, arg2);
7289: return(ret);
7290: }
7291:
7292: return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7293: }
7294:
7295: /**
7296: * xmlXPathCompareValues:
7297: * @ctxt: the XPath Parser context
7298: * @inf: less than (1) or greater than (0)
7299: * @strict: is the comparison strict
7300: *
7301: * Implement the compare operation on XPath objects:
7302: * @arg1 < @arg2 (1, 1, ...
7303: * @arg1 <= @arg2 (1, 0, ...
7304: * @arg1 > @arg2 (0, 1, ...
7305: * @arg1 >= @arg2 (0, 0, ...
7306: *
7307: * When neither object to be compared is a node-set and the operator is
7308: * <=, <, >=, >, then the objects are compared by converted both objects
7309: * to numbers and comparing the numbers according to IEEE 754. The <
7310: * comparison will be true if and only if the first number is less than the
7311: * second number. The <= comparison will be true if and only if the first
7312: * number is less than or equal to the second number. The > comparison
7313: * will be true if and only if the first number is greater than the second
7314: * number. The >= comparison will be true if and only if the first number
7315: * is greater than or equal to the second number.
7316: *
7317: * Returns 1 if the comparison succeeded, 0 if it failed
7318: */
7319: int
7320: xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
7321: int ret = 0, arg1i = 0, arg2i = 0;
7322: xmlXPathObjectPtr arg1, arg2;
7323:
7324: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7325: arg2 = valuePop(ctxt);
7326: arg1 = valuePop(ctxt);
7327: if ((arg1 == NULL) || (arg2 == NULL)) {
7328: if (arg1 != NULL)
7329: xmlXPathReleaseObject(ctxt->context, arg1);
7330: else
7331: xmlXPathReleaseObject(ctxt->context, arg2);
7332: XP_ERROR0(XPATH_INVALID_OPERAND);
7333: }
7334:
7335: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7336: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7337: /*
7338: * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
7339: * are not freed from within this routine; they will be freed from the
7340: * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
7341: */
7342: if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
7343: ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
7344: ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
7345: } else {
7346: if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7347: ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
7348: arg1, arg2);
7349: } else {
7350: ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
7351: arg2, arg1);
7352: }
7353: }
7354: return(ret);
7355: }
7356:
7357: if (arg1->type != XPATH_NUMBER) {
7358: valuePush(ctxt, arg1);
7359: xmlXPathNumberFunction(ctxt, 1);
7360: arg1 = valuePop(ctxt);
7361: }
7362: if (arg1->type != XPATH_NUMBER) {
7363: xmlXPathFreeObject(arg1);
7364: xmlXPathFreeObject(arg2);
7365: XP_ERROR0(XPATH_INVALID_OPERAND);
7366: }
7367: if (arg2->type != XPATH_NUMBER) {
7368: valuePush(ctxt, arg2);
7369: xmlXPathNumberFunction(ctxt, 1);
7370: arg2 = valuePop(ctxt);
7371: }
7372: if (arg2->type != XPATH_NUMBER) {
7373: xmlXPathReleaseObject(ctxt->context, arg1);
7374: xmlXPathReleaseObject(ctxt->context, arg2);
7375: XP_ERROR0(XPATH_INVALID_OPERAND);
7376: }
7377: /*
7378: * Add tests for infinity and nan
7379: * => feedback on 3.4 for Inf and NaN
7380: */
7381: /* Hand check NaN and Infinity comparisons */
7382: if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
7383: ret=0;
7384: } else {
7385: arg1i=xmlXPathIsInf(arg1->floatval);
7386: arg2i=xmlXPathIsInf(arg2->floatval);
7387: if (inf && strict) {
7388: if ((arg1i == -1 && arg2i != -1) ||
7389: (arg2i == 1 && arg1i != 1)) {
7390: ret = 1;
7391: } else if (arg1i == 0 && arg2i == 0) {
7392: ret = (arg1->floatval < arg2->floatval);
7393: } else {
7394: ret = 0;
7395: }
7396: }
7397: else if (inf && !strict) {
7398: if (arg1i == -1 || arg2i == 1) {
7399: ret = 1;
7400: } else if (arg1i == 0 && arg2i == 0) {
7401: ret = (arg1->floatval <= arg2->floatval);
7402: } else {
7403: ret = 0;
7404: }
7405: }
7406: else if (!inf && strict) {
7407: if ((arg1i == 1 && arg2i != 1) ||
7408: (arg2i == -1 && arg1i != -1)) {
7409: ret = 1;
7410: } else if (arg1i == 0 && arg2i == 0) {
7411: ret = (arg1->floatval > arg2->floatval);
7412: } else {
7413: ret = 0;
7414: }
7415: }
7416: else if (!inf && !strict) {
7417: if (arg1i == 1 || arg2i == -1) {
7418: ret = 1;
7419: } else if (arg1i == 0 && arg2i == 0) {
7420: ret = (arg1->floatval >= arg2->floatval);
7421: } else {
7422: ret = 0;
7423: }
7424: }
7425: }
7426: xmlXPathReleaseObject(ctxt->context, arg1);
7427: xmlXPathReleaseObject(ctxt->context, arg2);
7428: return(ret);
7429: }
7430:
7431: /**
7432: * xmlXPathValueFlipSign:
7433: * @ctxt: the XPath Parser context
7434: *
7435: * Implement the unary - operation on an XPath object
7436: * The numeric operators convert their operands to numbers as if
7437: * by calling the number function.
7438: */
7439: void
7440: xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
7441: if ((ctxt == NULL) || (ctxt->context == NULL)) return;
7442: CAST_TO_NUMBER;
7443: CHECK_TYPE(XPATH_NUMBER);
7444: if (xmlXPathIsNaN(ctxt->value->floatval))
7445: ctxt->value->floatval=xmlXPathNAN;
7446: else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
7447: ctxt->value->floatval=xmlXPathNINF;
7448: else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
7449: ctxt->value->floatval=xmlXPathPINF;
7450: else if (ctxt->value->floatval == 0) {
7451: if (xmlXPathGetSign(ctxt->value->floatval) == 0)
7452: ctxt->value->floatval = xmlXPathNZERO;
7453: else
7454: ctxt->value->floatval = 0;
7455: }
7456: else
7457: ctxt->value->floatval = - ctxt->value->floatval;
7458: }
7459:
7460: /**
7461: * xmlXPathAddValues:
7462: * @ctxt: the XPath Parser context
7463: *
7464: * Implement the add operation on XPath objects:
7465: * The numeric operators convert their operands to numbers as if
7466: * by calling the number function.
7467: */
7468: void
7469: xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
7470: xmlXPathObjectPtr arg;
7471: double val;
7472:
7473: arg = valuePop(ctxt);
7474: if (arg == NULL)
7475: XP_ERROR(XPATH_INVALID_OPERAND);
7476: val = xmlXPathCastToNumber(arg);
7477: xmlXPathReleaseObject(ctxt->context, arg);
7478: CAST_TO_NUMBER;
7479: CHECK_TYPE(XPATH_NUMBER);
7480: ctxt->value->floatval += val;
7481: }
7482:
7483: /**
7484: * xmlXPathSubValues:
7485: * @ctxt: the XPath Parser context
7486: *
7487: * Implement the subtraction operation on XPath objects:
7488: * The numeric operators convert their operands to numbers as if
7489: * by calling the number function.
7490: */
7491: void
7492: xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
7493: xmlXPathObjectPtr arg;
7494: double val;
7495:
7496: arg = valuePop(ctxt);
7497: if (arg == NULL)
7498: XP_ERROR(XPATH_INVALID_OPERAND);
7499: val = xmlXPathCastToNumber(arg);
7500: xmlXPathReleaseObject(ctxt->context, arg);
7501: CAST_TO_NUMBER;
7502: CHECK_TYPE(XPATH_NUMBER);
7503: ctxt->value->floatval -= val;
7504: }
7505:
7506: /**
7507: * xmlXPathMultValues:
7508: * @ctxt: the XPath Parser context
7509: *
7510: * Implement the multiply operation on XPath objects:
7511: * The numeric operators convert their operands to numbers as if
7512: * by calling the number function.
7513: */
7514: void
7515: xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
7516: xmlXPathObjectPtr arg;
7517: double val;
7518:
7519: arg = valuePop(ctxt);
7520: if (arg == NULL)
7521: XP_ERROR(XPATH_INVALID_OPERAND);
7522: val = xmlXPathCastToNumber(arg);
7523: xmlXPathReleaseObject(ctxt->context, arg);
7524: CAST_TO_NUMBER;
7525: CHECK_TYPE(XPATH_NUMBER);
7526: ctxt->value->floatval *= val;
7527: }
7528:
7529: /**
7530: * xmlXPathDivValues:
7531: * @ctxt: the XPath Parser context
7532: *
7533: * Implement the div operation on XPath objects @arg1 / @arg2:
7534: * The numeric operators convert their operands to numbers as if
7535: * by calling the number function.
7536: */
7537: void
7538: xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
7539: xmlXPathObjectPtr arg;
7540: double val;
7541:
7542: arg = valuePop(ctxt);
7543: if (arg == NULL)
7544: XP_ERROR(XPATH_INVALID_OPERAND);
7545: val = xmlXPathCastToNumber(arg);
7546: xmlXPathReleaseObject(ctxt->context, arg);
7547: CAST_TO_NUMBER;
7548: CHECK_TYPE(XPATH_NUMBER);
7549: if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
7550: ctxt->value->floatval = xmlXPathNAN;
7551: else if (val == 0 && xmlXPathGetSign(val) != 0) {
7552: if (ctxt->value->floatval == 0)
7553: ctxt->value->floatval = xmlXPathNAN;
7554: else if (ctxt->value->floatval > 0)
7555: ctxt->value->floatval = xmlXPathNINF;
7556: else if (ctxt->value->floatval < 0)
7557: ctxt->value->floatval = xmlXPathPINF;
7558: }
7559: else if (val == 0) {
7560: if (ctxt->value->floatval == 0)
7561: ctxt->value->floatval = xmlXPathNAN;
7562: else if (ctxt->value->floatval > 0)
7563: ctxt->value->floatval = xmlXPathPINF;
7564: else if (ctxt->value->floatval < 0)
7565: ctxt->value->floatval = xmlXPathNINF;
7566: } else
7567: ctxt->value->floatval /= val;
7568: }
7569:
7570: /**
7571: * xmlXPathModValues:
7572: * @ctxt: the XPath Parser context
7573: *
7574: * Implement the mod operation on XPath objects: @arg1 / @arg2
7575: * The numeric operators convert their operands to numbers as if
7576: * by calling the number function.
7577: */
7578: void
7579: xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
7580: xmlXPathObjectPtr arg;
7581: double arg1, arg2;
7582:
7583: arg = valuePop(ctxt);
7584: if (arg == NULL)
7585: XP_ERROR(XPATH_INVALID_OPERAND);
7586: arg2 = xmlXPathCastToNumber(arg);
7587: xmlXPathReleaseObject(ctxt->context, arg);
7588: CAST_TO_NUMBER;
7589: CHECK_TYPE(XPATH_NUMBER);
7590: arg1 = ctxt->value->floatval;
7591: if (arg2 == 0)
7592: ctxt->value->floatval = xmlXPathNAN;
7593: else {
7594: ctxt->value->floatval = fmod(arg1, arg2);
7595: }
7596: }
7597:
7598: /************************************************************************
7599: * *
7600: * The traversal functions *
7601: * *
7602: ************************************************************************/
7603:
7604: /*
7605: * A traversal function enumerates nodes along an axis.
7606: * Initially it must be called with NULL, and it indicates
7607: * termination on the axis by returning NULL.
7608: */
7609: typedef xmlNodePtr (*xmlXPathTraversalFunction)
7610: (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
7611:
7612: /*
7613: * xmlXPathTraversalFunctionExt:
7614: * A traversal function enumerates nodes along an axis.
7615: * Initially it must be called with NULL, and it indicates
7616: * termination on the axis by returning NULL.
7617: * The context node of the traversal is specified via @contextNode.
7618: */
7619: typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
7620: (xmlNodePtr cur, xmlNodePtr contextNode);
7621:
7622: /*
7623: * xmlXPathNodeSetMergeFunction:
7624: * Used for merging node sets in xmlXPathCollectAndTest().
7625: */
7626: typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
7627: (xmlNodeSetPtr, xmlNodeSetPtr, int);
7628:
7629:
7630: /**
7631: * xmlXPathNextSelf:
7632: * @ctxt: the XPath Parser context
7633: * @cur: the current node in the traversal
7634: *
7635: * Traversal function for the "self" direction
7636: * The self axis contains just the context node itself
7637: *
7638: * Returns the next element following that axis
7639: */
7640: xmlNodePtr
7641: xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7642: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7643: if (cur == NULL)
7644: return(ctxt->context->node);
7645: return(NULL);
7646: }
7647:
7648: /**
7649: * xmlXPathNextChild:
7650: * @ctxt: the XPath Parser context
7651: * @cur: the current node in the traversal
7652: *
7653: * Traversal function for the "child" direction
7654: * The child axis contains the children of the context node in document order.
7655: *
7656: * Returns the next element following that axis
7657: */
7658: xmlNodePtr
7659: xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7660: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7661: if (cur == NULL) {
7662: if (ctxt->context->node == NULL) return(NULL);
7663: switch (ctxt->context->node->type) {
7664: case XML_ELEMENT_NODE:
7665: case XML_TEXT_NODE:
7666: case XML_CDATA_SECTION_NODE:
7667: case XML_ENTITY_REF_NODE:
7668: case XML_ENTITY_NODE:
7669: case XML_PI_NODE:
7670: case XML_COMMENT_NODE:
7671: case XML_NOTATION_NODE:
7672: case XML_DTD_NODE:
7673: return(ctxt->context->node->children);
7674: case XML_DOCUMENT_NODE:
7675: case XML_DOCUMENT_TYPE_NODE:
7676: case XML_DOCUMENT_FRAG_NODE:
7677: case XML_HTML_DOCUMENT_NODE:
7678: #ifdef LIBXML_DOCB_ENABLED
7679: case XML_DOCB_DOCUMENT_NODE:
7680: #endif
7681: return(((xmlDocPtr) ctxt->context->node)->children);
7682: case XML_ELEMENT_DECL:
7683: case XML_ATTRIBUTE_DECL:
7684: case XML_ENTITY_DECL:
7685: case XML_ATTRIBUTE_NODE:
7686: case XML_NAMESPACE_DECL:
7687: case XML_XINCLUDE_START:
7688: case XML_XINCLUDE_END:
7689: return(NULL);
7690: }
7691: return(NULL);
7692: }
7693: if ((cur->type == XML_DOCUMENT_NODE) ||
7694: (cur->type == XML_HTML_DOCUMENT_NODE))
7695: return(NULL);
7696: return(cur->next);
7697: }
7698:
7699: /**
7700: * xmlXPathNextChildElement:
7701: * @ctxt: the XPath Parser context
7702: * @cur: the current node in the traversal
7703: *
7704: * Traversal function for the "child" direction and nodes of type element.
7705: * The child axis contains the children of the context node in document order.
7706: *
7707: * Returns the next element following that axis
7708: */
7709: static xmlNodePtr
7710: xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7711: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7712: if (cur == NULL) {
7713: cur = ctxt->context->node;
7714: if (cur == NULL) return(NULL);
7715: /*
7716: * Get the first element child.
7717: */
7718: switch (cur->type) {
7719: case XML_ELEMENT_NODE:
7720: case XML_DOCUMENT_FRAG_NODE:
7721: case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
7722: case XML_ENTITY_NODE:
7723: cur = cur->children;
7724: if (cur != NULL) {
7725: if (cur->type == XML_ELEMENT_NODE)
7726: return(cur);
7727: do {
7728: cur = cur->next;
7729: } while ((cur != NULL) &&
7730: (cur->type != XML_ELEMENT_NODE));
7731: return(cur);
7732: }
7733: return(NULL);
7734: case XML_DOCUMENT_NODE:
7735: case XML_HTML_DOCUMENT_NODE:
7736: #ifdef LIBXML_DOCB_ENABLED
7737: case XML_DOCB_DOCUMENT_NODE:
7738: #endif
7739: return(xmlDocGetRootElement((xmlDocPtr) cur));
7740: default:
7741: return(NULL);
7742: }
7743: return(NULL);
7744: }
7745: /*
7746: * Get the next sibling element node.
7747: */
7748: switch (cur->type) {
7749: case XML_ELEMENT_NODE:
7750: case XML_TEXT_NODE:
7751: case XML_ENTITY_REF_NODE:
7752: case XML_ENTITY_NODE:
7753: case XML_CDATA_SECTION_NODE:
7754: case XML_PI_NODE:
7755: case XML_COMMENT_NODE:
7756: case XML_XINCLUDE_END:
7757: break;
7758: /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
7759: default:
7760: return(NULL);
7761: }
7762: if (cur->next != NULL) {
7763: if (cur->next->type == XML_ELEMENT_NODE)
7764: return(cur->next);
7765: cur = cur->next;
7766: do {
7767: cur = cur->next;
7768: } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
7769: return(cur);
7770: }
7771: return(NULL);
7772: }
7773:
1.1.1.3 ! misho 7774: #if 0
1.1 misho 7775: /**
7776: * xmlXPathNextDescendantOrSelfElemParent:
7777: * @ctxt: the XPath Parser context
7778: * @cur: the current node in the traversal
7779: *
7780: * Traversal function for the "descendant-or-self" axis.
7781: * Additionally it returns only nodes which can be parents of
7782: * element nodes.
7783: *
7784: *
7785: * Returns the next element following that axis
7786: */
7787: static xmlNodePtr
7788: xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
7789: xmlNodePtr contextNode)
7790: {
7791: if (cur == NULL) {
7792: if (contextNode == NULL)
7793: return(NULL);
7794: switch (contextNode->type) {
7795: case XML_ELEMENT_NODE:
7796: case XML_XINCLUDE_START:
7797: case XML_DOCUMENT_FRAG_NODE:
7798: case XML_DOCUMENT_NODE:
7799: #ifdef LIBXML_DOCB_ENABLED
7800: case XML_DOCB_DOCUMENT_NODE:
7801: #endif
1.1.1.3 ! misho 7802: case XML_HTML_DOCUMENT_NODE:
1.1 misho 7803: return(contextNode);
7804: default:
7805: return(NULL);
7806: }
7807: return(NULL);
7808: } else {
7809: xmlNodePtr start = cur;
7810:
7811: while (cur != NULL) {
7812: switch (cur->type) {
7813: case XML_ELEMENT_NODE:
7814: /* TODO: OK to have XInclude here? */
7815: case XML_XINCLUDE_START:
7816: case XML_DOCUMENT_FRAG_NODE:
7817: if (cur != start)
7818: return(cur);
7819: if (cur->children != NULL) {
7820: cur = cur->children;
7821: continue;
7822: }
7823: break;
7824: /* Not sure if we need those here. */
7825: case XML_DOCUMENT_NODE:
7826: #ifdef LIBXML_DOCB_ENABLED
7827: case XML_DOCB_DOCUMENT_NODE:
7828: #endif
7829: case XML_HTML_DOCUMENT_NODE:
7830: if (cur != start)
7831: return(cur);
7832: return(xmlDocGetRootElement((xmlDocPtr) cur));
7833: default:
7834: break;
7835: }
7836:
7837: next_sibling:
7838: if ((cur == NULL) || (cur == contextNode))
7839: return(NULL);
7840: if (cur->next != NULL) {
7841: cur = cur->next;
7842: } else {
7843: cur = cur->parent;
7844: goto next_sibling;
7845: }
7846: }
7847: }
7848: return(NULL);
7849: }
1.1.1.3 ! misho 7850: #endif
1.1 misho 7851:
7852: /**
7853: * xmlXPathNextDescendant:
7854: * @ctxt: the XPath Parser context
7855: * @cur: the current node in the traversal
7856: *
7857: * Traversal function for the "descendant" direction
7858: * the descendant axis contains the descendants of the context node in document
7859: * order; a descendant is a child or a child of a child and so on.
7860: *
7861: * Returns the next element following that axis
7862: */
7863: xmlNodePtr
7864: xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7865: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7866: if (cur == NULL) {
7867: if (ctxt->context->node == NULL)
7868: return(NULL);
7869: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7870: (ctxt->context->node->type == XML_NAMESPACE_DECL))
7871: return(NULL);
7872:
7873: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
7874: return(ctxt->context->doc->children);
7875: return(ctxt->context->node->children);
7876: }
7877:
1.1.1.3 ! misho 7878: if (cur->type == XML_NAMESPACE_DECL)
! 7879: return(NULL);
1.1 misho 7880: if (cur->children != NULL) {
7881: /*
7882: * Do not descend on entities declarations
7883: */
7884: if (cur->children->type != XML_ENTITY_DECL) {
7885: cur = cur->children;
7886: /*
7887: * Skip DTDs
7888: */
7889: if (cur->type != XML_DTD_NODE)
7890: return(cur);
7891: }
7892: }
7893:
7894: if (cur == ctxt->context->node) return(NULL);
7895:
7896: while (cur->next != NULL) {
7897: cur = cur->next;
7898: if ((cur->type != XML_ENTITY_DECL) &&
7899: (cur->type != XML_DTD_NODE))
7900: return(cur);
7901: }
7902:
7903: do {
7904: cur = cur->parent;
7905: if (cur == NULL) break;
7906: if (cur == ctxt->context->node) return(NULL);
7907: if (cur->next != NULL) {
7908: cur = cur->next;
7909: return(cur);
7910: }
7911: } while (cur != NULL);
7912: return(cur);
7913: }
7914:
7915: /**
7916: * xmlXPathNextDescendantOrSelf:
7917: * @ctxt: the XPath Parser context
7918: * @cur: the current node in the traversal
7919: *
7920: * Traversal function for the "descendant-or-self" direction
7921: * the descendant-or-self axis contains the context node and the descendants
7922: * of the context node in document order; thus the context node is the first
7923: * node on the axis, and the first child of the context node is the second node
7924: * on the axis
7925: *
7926: * Returns the next element following that axis
7927: */
7928: xmlNodePtr
7929: xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7930: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7931: if (cur == NULL) {
7932: if (ctxt->context->node == NULL)
7933: return(NULL);
7934: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7935: (ctxt->context->node->type == XML_NAMESPACE_DECL))
7936: return(NULL);
7937: return(ctxt->context->node);
7938: }
7939:
7940: return(xmlXPathNextDescendant(ctxt, cur));
7941: }
7942:
7943: /**
7944: * xmlXPathNextParent:
7945: * @ctxt: the XPath Parser context
7946: * @cur: the current node in the traversal
7947: *
7948: * Traversal function for the "parent" direction
7949: * The parent axis contains the parent of the context node, if there is one.
7950: *
7951: * Returns the next element following that axis
7952: */
7953: xmlNodePtr
7954: xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7955: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7956: /*
7957: * the parent of an attribute or namespace node is the element
7958: * to which the attribute or namespace node is attached
7959: * Namespace handling !!!
7960: */
7961: if (cur == NULL) {
7962: if (ctxt->context->node == NULL) return(NULL);
7963: switch (ctxt->context->node->type) {
7964: case XML_ELEMENT_NODE:
7965: case XML_TEXT_NODE:
7966: case XML_CDATA_SECTION_NODE:
7967: case XML_ENTITY_REF_NODE:
7968: case XML_ENTITY_NODE:
7969: case XML_PI_NODE:
7970: case XML_COMMENT_NODE:
7971: case XML_NOTATION_NODE:
7972: case XML_DTD_NODE:
7973: case XML_ELEMENT_DECL:
7974: case XML_ATTRIBUTE_DECL:
7975: case XML_XINCLUDE_START:
7976: case XML_XINCLUDE_END:
7977: case XML_ENTITY_DECL:
7978: if (ctxt->context->node->parent == NULL)
7979: return((xmlNodePtr) ctxt->context->doc);
7980: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
7981: ((ctxt->context->node->parent->name[0] == ' ') ||
7982: (xmlStrEqual(ctxt->context->node->parent->name,
7983: BAD_CAST "fake node libxslt"))))
7984: return(NULL);
7985: return(ctxt->context->node->parent);
7986: case XML_ATTRIBUTE_NODE: {
7987: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
7988:
7989: return(att->parent);
7990: }
7991: case XML_DOCUMENT_NODE:
7992: case XML_DOCUMENT_TYPE_NODE:
7993: case XML_DOCUMENT_FRAG_NODE:
7994: case XML_HTML_DOCUMENT_NODE:
7995: #ifdef LIBXML_DOCB_ENABLED
7996: case XML_DOCB_DOCUMENT_NODE:
7997: #endif
7998: return(NULL);
7999: case XML_NAMESPACE_DECL: {
8000: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
8001:
8002: if ((ns->next != NULL) &&
8003: (ns->next->type != XML_NAMESPACE_DECL))
8004: return((xmlNodePtr) ns->next);
8005: return(NULL);
8006: }
8007: }
8008: }
8009: return(NULL);
8010: }
8011:
8012: /**
8013: * xmlXPathNextAncestor:
8014: * @ctxt: the XPath Parser context
8015: * @cur: the current node in the traversal
8016: *
8017: * Traversal function for the "ancestor" direction
8018: * the ancestor axis contains the ancestors of the context node; the ancestors
8019: * of the context node consist of the parent of context node and the parent's
8020: * parent and so on; the nodes are ordered in reverse document order; thus the
8021: * parent is the first node on the axis, and the parent's parent is the second
8022: * node on the axis
8023: *
8024: * Returns the next element following that axis
8025: */
8026: xmlNodePtr
8027: xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8028: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8029: /*
8030: * the parent of an attribute or namespace node is the element
8031: * to which the attribute or namespace node is attached
8032: * !!!!!!!!!!!!!
8033: */
8034: if (cur == NULL) {
8035: if (ctxt->context->node == NULL) return(NULL);
8036: switch (ctxt->context->node->type) {
8037: case XML_ELEMENT_NODE:
8038: case XML_TEXT_NODE:
8039: case XML_CDATA_SECTION_NODE:
8040: case XML_ENTITY_REF_NODE:
8041: case XML_ENTITY_NODE:
8042: case XML_PI_NODE:
8043: case XML_COMMENT_NODE:
8044: case XML_DTD_NODE:
8045: case XML_ELEMENT_DECL:
8046: case XML_ATTRIBUTE_DECL:
8047: case XML_ENTITY_DECL:
8048: case XML_NOTATION_NODE:
8049: case XML_XINCLUDE_START:
8050: case XML_XINCLUDE_END:
8051: if (ctxt->context->node->parent == NULL)
8052: return((xmlNodePtr) ctxt->context->doc);
8053: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
8054: ((ctxt->context->node->parent->name[0] == ' ') ||
8055: (xmlStrEqual(ctxt->context->node->parent->name,
8056: BAD_CAST "fake node libxslt"))))
8057: return(NULL);
8058: return(ctxt->context->node->parent);
8059: case XML_ATTRIBUTE_NODE: {
8060: xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
8061:
8062: return(tmp->parent);
8063: }
8064: case XML_DOCUMENT_NODE:
8065: case XML_DOCUMENT_TYPE_NODE:
8066: case XML_DOCUMENT_FRAG_NODE:
8067: case XML_HTML_DOCUMENT_NODE:
8068: #ifdef LIBXML_DOCB_ENABLED
8069: case XML_DOCB_DOCUMENT_NODE:
8070: #endif
8071: return(NULL);
8072: case XML_NAMESPACE_DECL: {
8073: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
8074:
8075: if ((ns->next != NULL) &&
8076: (ns->next->type != XML_NAMESPACE_DECL))
8077: return((xmlNodePtr) ns->next);
8078: /* Bad, how did that namespace end up here ? */
8079: return(NULL);
8080: }
8081: }
8082: return(NULL);
8083: }
8084: if (cur == ctxt->context->doc->children)
8085: return((xmlNodePtr) ctxt->context->doc);
8086: if (cur == (xmlNodePtr) ctxt->context->doc)
8087: return(NULL);
8088: switch (cur->type) {
8089: case XML_ELEMENT_NODE:
8090: case XML_TEXT_NODE:
8091: case XML_CDATA_SECTION_NODE:
8092: case XML_ENTITY_REF_NODE:
8093: case XML_ENTITY_NODE:
8094: case XML_PI_NODE:
8095: case XML_COMMENT_NODE:
8096: case XML_NOTATION_NODE:
8097: case XML_DTD_NODE:
8098: case XML_ELEMENT_DECL:
8099: case XML_ATTRIBUTE_DECL:
8100: case XML_ENTITY_DECL:
8101: case XML_XINCLUDE_START:
8102: case XML_XINCLUDE_END:
8103: if (cur->parent == NULL)
8104: return(NULL);
8105: if ((cur->parent->type == XML_ELEMENT_NODE) &&
8106: ((cur->parent->name[0] == ' ') ||
8107: (xmlStrEqual(cur->parent->name,
8108: BAD_CAST "fake node libxslt"))))
8109: return(NULL);
8110: return(cur->parent);
8111: case XML_ATTRIBUTE_NODE: {
8112: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
8113:
8114: return(att->parent);
8115: }
8116: case XML_NAMESPACE_DECL: {
8117: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
8118:
8119: if ((ns->next != NULL) &&
8120: (ns->next->type != XML_NAMESPACE_DECL))
8121: return((xmlNodePtr) ns->next);
8122: /* Bad, how did that namespace end up here ? */
8123: return(NULL);
8124: }
8125: case XML_DOCUMENT_NODE:
8126: case XML_DOCUMENT_TYPE_NODE:
8127: case XML_DOCUMENT_FRAG_NODE:
8128: case XML_HTML_DOCUMENT_NODE:
8129: #ifdef LIBXML_DOCB_ENABLED
8130: case XML_DOCB_DOCUMENT_NODE:
8131: #endif
8132: return(NULL);
8133: }
8134: return(NULL);
8135: }
8136:
8137: /**
8138: * xmlXPathNextAncestorOrSelf:
8139: * @ctxt: the XPath Parser context
8140: * @cur: the current node in the traversal
8141: *
8142: * Traversal function for the "ancestor-or-self" direction
8143: * he ancestor-or-self axis contains the context node and ancestors of
8144: * the context node in reverse document order; thus the context node is
8145: * the first node on the axis, and the context node's parent the second;
8146: * parent here is defined the same as with the parent axis.
8147: *
8148: * Returns the next element following that axis
8149: */
8150: xmlNodePtr
8151: xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8152: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8153: if (cur == NULL)
8154: return(ctxt->context->node);
8155: return(xmlXPathNextAncestor(ctxt, cur));
8156: }
8157:
8158: /**
8159: * xmlXPathNextFollowingSibling:
8160: * @ctxt: the XPath Parser context
8161: * @cur: the current node in the traversal
8162: *
8163: * Traversal function for the "following-sibling" direction
8164: * The following-sibling axis contains the following siblings of the context
8165: * node in document order.
8166: *
8167: * Returns the next element following that axis
8168: */
8169: xmlNodePtr
8170: xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8171: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8172: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8173: (ctxt->context->node->type == XML_NAMESPACE_DECL))
8174: return(NULL);
8175: if (cur == (xmlNodePtr) ctxt->context->doc)
8176: return(NULL);
8177: if (cur == NULL)
8178: return(ctxt->context->node->next);
8179: return(cur->next);
8180: }
8181:
8182: /**
8183: * xmlXPathNextPrecedingSibling:
8184: * @ctxt: the XPath Parser context
8185: * @cur: the current node in the traversal
8186: *
8187: * Traversal function for the "preceding-sibling" direction
8188: * The preceding-sibling axis contains the preceding siblings of the context
8189: * node in reverse document order; the first preceding sibling is first on the
8190: * axis; the sibling preceding that node is the second on the axis and so on.
8191: *
8192: * Returns the next element following that axis
8193: */
8194: xmlNodePtr
8195: xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8196: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8197: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8198: (ctxt->context->node->type == XML_NAMESPACE_DECL))
8199: return(NULL);
8200: if (cur == (xmlNodePtr) ctxt->context->doc)
8201: return(NULL);
8202: if (cur == NULL)
8203: return(ctxt->context->node->prev);
8204: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
8205: cur = cur->prev;
8206: if (cur == NULL)
8207: return(ctxt->context->node->prev);
8208: }
8209: return(cur->prev);
8210: }
8211:
8212: /**
8213: * xmlXPathNextFollowing:
8214: * @ctxt: the XPath Parser context
8215: * @cur: the current node in the traversal
8216: *
8217: * Traversal function for the "following" direction
8218: * The following axis contains all nodes in the same document as the context
8219: * node that are after the context node in document order, excluding any
8220: * descendants and excluding attribute nodes and namespace nodes; the nodes
8221: * are ordered in document order
8222: *
8223: * Returns the next element following that axis
8224: */
8225: xmlNodePtr
8226: xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8227: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8228: if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
8229: (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
8230: return(cur->children);
8231:
8232: if (cur == NULL) {
8233: cur = ctxt->context->node;
8234: if (cur->type == XML_NAMESPACE_DECL)
8235: return(NULL);
8236: if (cur->type == XML_ATTRIBUTE_NODE)
8237: cur = cur->parent;
8238: }
8239: if (cur == NULL) return(NULL) ; /* ERROR */
8240: if (cur->next != NULL) return(cur->next) ;
8241: do {
8242: cur = cur->parent;
8243: if (cur == NULL) break;
8244: if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
8245: if (cur->next != NULL) return(cur->next);
8246: } while (cur != NULL);
8247: return(cur);
8248: }
8249:
8250: /*
8251: * xmlXPathIsAncestor:
8252: * @ancestor: the ancestor node
8253: * @node: the current node
8254: *
8255: * Check that @ancestor is a @node's ancestor
8256: *
8257: * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
8258: */
8259: static int
8260: xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
8261: if ((ancestor == NULL) || (node == NULL)) return(0);
1.1.1.3 ! misho 8262: if (node->type == XML_NAMESPACE_DECL)
! 8263: return(0);
! 8264: if (ancestor->type == XML_NAMESPACE_DECL)
! 8265: return(0);
1.1 misho 8266: /* nodes need to be in the same document */
8267: if (ancestor->doc != node->doc) return(0);
8268: /* avoid searching if ancestor or node is the root node */
8269: if (ancestor == (xmlNodePtr) node->doc) return(1);
8270: if (node == (xmlNodePtr) ancestor->doc) return(0);
8271: while (node->parent != NULL) {
8272: if (node->parent == ancestor)
8273: return(1);
8274: node = node->parent;
8275: }
8276: return(0);
8277: }
8278:
8279: /**
8280: * xmlXPathNextPreceding:
8281: * @ctxt: the XPath Parser context
8282: * @cur: the current node in the traversal
8283: *
8284: * Traversal function for the "preceding" direction
8285: * the preceding axis contains all nodes in the same document as the context
8286: * node that are before the context node in document order, excluding any
8287: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8288: * ordered in reverse document order
8289: *
8290: * Returns the next element following that axis
8291: */
8292: xmlNodePtr
8293: xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
8294: {
8295: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8296: if (cur == NULL) {
8297: cur = ctxt->context->node;
8298: if (cur->type == XML_NAMESPACE_DECL)
8299: return(NULL);
8300: if (cur->type == XML_ATTRIBUTE_NODE)
8301: return(cur->parent);
8302: }
1.1.1.3 ! misho 8303: if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
1.1 misho 8304: return (NULL);
8305: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8306: cur = cur->prev;
8307: do {
8308: if (cur->prev != NULL) {
8309: for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
8310: return (cur);
8311: }
8312:
8313: cur = cur->parent;
8314: if (cur == NULL)
8315: return (NULL);
8316: if (cur == ctxt->context->doc->children)
8317: return (NULL);
8318: } while (xmlXPathIsAncestor(cur, ctxt->context->node));
8319: return (cur);
8320: }
8321:
8322: /**
8323: * xmlXPathNextPrecedingInternal:
8324: * @ctxt: the XPath Parser context
8325: * @cur: the current node in the traversal
8326: *
8327: * Traversal function for the "preceding" direction
8328: * the preceding axis contains all nodes in the same document as the context
8329: * node that are before the context node in document order, excluding any
8330: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8331: * ordered in reverse document order
8332: * This is a faster implementation but internal only since it requires a
8333: * state kept in the parser context: ctxt->ancestor.
8334: *
8335: * Returns the next element following that axis
8336: */
8337: static xmlNodePtr
8338: xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
8339: xmlNodePtr cur)
8340: {
8341: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8342: if (cur == NULL) {
8343: cur = ctxt->context->node;
8344: if (cur == NULL)
8345: return (NULL);
8346: if (cur->type == XML_NAMESPACE_DECL)
8347: return (NULL);
8348: ctxt->ancestor = cur->parent;
8349: }
1.1.1.3 ! misho 8350: if (cur->type == XML_NAMESPACE_DECL)
! 8351: return(NULL);
1.1 misho 8352: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8353: cur = cur->prev;
8354: while (cur->prev == NULL) {
8355: cur = cur->parent;
8356: if (cur == NULL)
8357: return (NULL);
8358: if (cur == ctxt->context->doc->children)
8359: return (NULL);
8360: if (cur != ctxt->ancestor)
8361: return (cur);
8362: ctxt->ancestor = cur->parent;
8363: }
8364: cur = cur->prev;
8365: while (cur->last != NULL)
8366: cur = cur->last;
8367: return (cur);
8368: }
8369:
8370: /**
8371: * xmlXPathNextNamespace:
8372: * @ctxt: the XPath Parser context
8373: * @cur: the current attribute in the traversal
8374: *
8375: * Traversal function for the "namespace" direction
8376: * the namespace axis contains the namespace nodes of the context node;
8377: * the order of nodes on this axis is implementation-defined; the axis will
8378: * be empty unless the context node is an element
8379: *
8380: * We keep the XML namespace node at the end of the list.
8381: *
8382: * Returns the next element following that axis
8383: */
8384: xmlNodePtr
8385: xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8386: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8387: if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
8388: if (ctxt->context->tmpNsList == NULL && cur != (xmlNodePtr) xmlXPathXMLNamespace) {
8389: if (ctxt->context->tmpNsList != NULL)
8390: xmlFree(ctxt->context->tmpNsList);
8391: ctxt->context->tmpNsList =
8392: xmlGetNsList(ctxt->context->doc, ctxt->context->node);
8393: ctxt->context->tmpNsNr = 0;
8394: if (ctxt->context->tmpNsList != NULL) {
8395: while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
8396: ctxt->context->tmpNsNr++;
8397: }
8398: }
8399: return((xmlNodePtr) xmlXPathXMLNamespace);
8400: }
8401: if (ctxt->context->tmpNsNr > 0) {
8402: return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
8403: } else {
8404: if (ctxt->context->tmpNsList != NULL)
8405: xmlFree(ctxt->context->tmpNsList);
8406: ctxt->context->tmpNsList = NULL;
8407: return(NULL);
8408: }
8409: }
8410:
8411: /**
8412: * xmlXPathNextAttribute:
8413: * @ctxt: the XPath Parser context
8414: * @cur: the current attribute in the traversal
8415: *
8416: * Traversal function for the "attribute" direction
8417: * TODO: support DTD inherited default attributes
8418: *
8419: * Returns the next element following that axis
8420: */
8421: xmlNodePtr
8422: xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8423: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8424: if (ctxt->context->node == NULL)
8425: return(NULL);
8426: if (ctxt->context->node->type != XML_ELEMENT_NODE)
8427: return(NULL);
8428: if (cur == NULL) {
8429: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
8430: return(NULL);
8431: return((xmlNodePtr)ctxt->context->node->properties);
8432: }
8433: return((xmlNodePtr)cur->next);
8434: }
8435:
8436: /************************************************************************
8437: * *
8438: * NodeTest Functions *
8439: * *
8440: ************************************************************************/
8441:
8442: #define IS_FUNCTION 200
8443:
8444:
8445: /************************************************************************
8446: * *
8447: * Implicit tree core function library *
8448: * *
8449: ************************************************************************/
8450:
8451: /**
8452: * xmlXPathRoot:
8453: * @ctxt: the XPath Parser context
8454: *
8455: * Initialize the context to the root of the document
8456: */
8457: void
8458: xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
8459: if ((ctxt == NULL) || (ctxt->context == NULL))
8460: return;
8461: ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
8462: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8463: ctxt->context->node));
8464: }
8465:
8466: /************************************************************************
8467: * *
8468: * The explicit core function library *
8469: *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
8470: * *
8471: ************************************************************************/
8472:
8473:
8474: /**
8475: * xmlXPathLastFunction:
8476: * @ctxt: the XPath Parser context
8477: * @nargs: the number of arguments
8478: *
8479: * Implement the last() XPath function
8480: * number last()
8481: * The last function returns the number of nodes in the context node list.
8482: */
8483: void
8484: xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8485: CHECK_ARITY(0);
8486: if (ctxt->context->contextSize >= 0) {
8487: valuePush(ctxt,
8488: xmlXPathCacheNewFloat(ctxt->context,
8489: (double) ctxt->context->contextSize));
8490: #ifdef DEBUG_EXPR
8491: xmlGenericError(xmlGenericErrorContext,
8492: "last() : %d\n", ctxt->context->contextSize);
8493: #endif
8494: } else {
8495: XP_ERROR(XPATH_INVALID_CTXT_SIZE);
8496: }
8497: }
8498:
8499: /**
8500: * xmlXPathPositionFunction:
8501: * @ctxt: the XPath Parser context
8502: * @nargs: the number of arguments
8503: *
8504: * Implement the position() XPath function
8505: * number position()
8506: * The position function returns the position of the context node in the
8507: * context node list. The first position is 1, and so the last position
8508: * will be equal to last().
8509: */
8510: void
8511: xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8512: CHECK_ARITY(0);
8513: if (ctxt->context->proximityPosition >= 0) {
8514: valuePush(ctxt,
8515: xmlXPathCacheNewFloat(ctxt->context,
8516: (double) ctxt->context->proximityPosition));
8517: #ifdef DEBUG_EXPR
8518: xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
8519: ctxt->context->proximityPosition);
8520: #endif
8521: } else {
8522: XP_ERROR(XPATH_INVALID_CTXT_POSITION);
8523: }
8524: }
8525:
8526: /**
8527: * xmlXPathCountFunction:
8528: * @ctxt: the XPath Parser context
8529: * @nargs: the number of arguments
8530: *
8531: * Implement the count() XPath function
8532: * number count(node-set)
8533: */
8534: void
8535: xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8536: xmlXPathObjectPtr cur;
8537:
8538: CHECK_ARITY(1);
8539: if ((ctxt->value == NULL) ||
8540: ((ctxt->value->type != XPATH_NODESET) &&
8541: (ctxt->value->type != XPATH_XSLT_TREE)))
8542: XP_ERROR(XPATH_INVALID_TYPE);
8543: cur = valuePop(ctxt);
8544:
8545: if ((cur == NULL) || (cur->nodesetval == NULL))
8546: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8547: else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
8548: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8549: (double) cur->nodesetval->nodeNr));
8550: } else {
8551: if ((cur->nodesetval->nodeNr != 1) ||
8552: (cur->nodesetval->nodeTab == NULL)) {
8553: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8554: } else {
8555: xmlNodePtr tmp;
8556: int i = 0;
8557:
8558: tmp = cur->nodesetval->nodeTab[0];
1.1.1.3 ! misho 8559: if ((tmp != NULL) && (tmp->type != XML_NAMESPACE_DECL)) {
1.1 misho 8560: tmp = tmp->children;
8561: while (tmp != NULL) {
8562: tmp = tmp->next;
8563: i++;
8564: }
8565: }
8566: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i));
8567: }
8568: }
8569: xmlXPathReleaseObject(ctxt->context, cur);
8570: }
8571:
8572: /**
8573: * xmlXPathGetElementsByIds:
8574: * @doc: the document
8575: * @ids: a whitespace separated list of IDs
8576: *
8577: * Selects elements by their unique ID.
8578: *
8579: * Returns a node-set of selected elements.
8580: */
8581: static xmlNodeSetPtr
8582: xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
8583: xmlNodeSetPtr ret;
8584: const xmlChar *cur = ids;
8585: xmlChar *ID;
8586: xmlAttrPtr attr;
8587: xmlNodePtr elem = NULL;
8588:
8589: if (ids == NULL) return(NULL);
8590:
8591: ret = xmlXPathNodeSetCreate(NULL);
8592: if (ret == NULL)
8593: return(ret);
8594:
8595: while (IS_BLANK_CH(*cur)) cur++;
8596: while (*cur != 0) {
8597: while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
8598: cur++;
8599:
8600: ID = xmlStrndup(ids, cur - ids);
8601: if (ID != NULL) {
8602: /*
8603: * We used to check the fact that the value passed
8604: * was an NCName, but this generated much troubles for
8605: * me and Aleksey Sanin, people blatantly violated that
8606: * constaint, like Visa3D spec.
8607: * if (xmlValidateNCName(ID, 1) == 0)
8608: */
8609: attr = xmlGetID(doc, ID);
8610: if (attr != NULL) {
8611: if (attr->type == XML_ATTRIBUTE_NODE)
8612: elem = attr->parent;
8613: else if (attr->type == XML_ELEMENT_NODE)
8614: elem = (xmlNodePtr) attr;
8615: else
8616: elem = NULL;
8617: if (elem != NULL)
8618: xmlXPathNodeSetAdd(ret, elem);
8619: }
8620: xmlFree(ID);
8621: }
8622:
8623: while (IS_BLANK_CH(*cur)) cur++;
8624: ids = cur;
8625: }
8626: return(ret);
8627: }
8628:
8629: /**
8630: * xmlXPathIdFunction:
8631: * @ctxt: the XPath Parser context
8632: * @nargs: the number of arguments
8633: *
8634: * Implement the id() XPath function
8635: * node-set id(object)
8636: * The id function selects elements by their unique ID
8637: * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
8638: * then the result is the union of the result of applying id to the
8639: * string value of each of the nodes in the argument node-set. When the
8640: * argument to id is of any other type, the argument is converted to a
8641: * string as if by a call to the string function; the string is split
8642: * into a whitespace-separated list of tokens (whitespace is any sequence
8643: * of characters matching the production S); the result is a node-set
8644: * containing the elements in the same document as the context node that
8645: * have a unique ID equal to any of the tokens in the list.
8646: */
8647: void
8648: xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8649: xmlChar *tokens;
8650: xmlNodeSetPtr ret;
8651: xmlXPathObjectPtr obj;
8652:
8653: CHECK_ARITY(1);
8654: obj = valuePop(ctxt);
8655: if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8656: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
8657: xmlNodeSetPtr ns;
8658: int i;
8659:
8660: ret = xmlXPathNodeSetCreate(NULL);
8661: /*
8662: * FIXME -- in an out-of-memory condition this will behave badly.
8663: * The solution is not clear -- we already popped an item from
8664: * ctxt, so the object is in a corrupt state.
8665: */
8666:
8667: if (obj->nodesetval != NULL) {
8668: for (i = 0; i < obj->nodesetval->nodeNr; i++) {
8669: tokens =
8670: xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
8671: ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
8672: ret = xmlXPathNodeSetMerge(ret, ns);
8673: xmlXPathFreeNodeSet(ns);
8674: if (tokens != NULL)
8675: xmlFree(tokens);
8676: }
8677: }
8678: xmlXPathReleaseObject(ctxt->context, obj);
8679: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8680: return;
8681: }
8682: obj = xmlXPathCacheConvertString(ctxt->context, obj);
8683: ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
8684: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8685: xmlXPathReleaseObject(ctxt->context, obj);
8686: return;
8687: }
8688:
8689: /**
8690: * xmlXPathLocalNameFunction:
8691: * @ctxt: the XPath Parser context
8692: * @nargs: the number of arguments
8693: *
8694: * Implement the local-name() XPath function
8695: * string local-name(node-set?)
8696: * The local-name function returns a string containing the local part
8697: * of the name of the node in the argument node-set that is first in
8698: * document order. If the node-set is empty or the first node has no
8699: * name, an empty string is returned. If the argument is omitted it
8700: * defaults to the context node.
8701: */
8702: void
8703: xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8704: xmlXPathObjectPtr cur;
8705:
8706: if (ctxt == NULL) return;
8707:
8708: if (nargs == 0) {
8709: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8710: ctxt->context->node));
8711: nargs = 1;
8712: }
8713:
8714: CHECK_ARITY(1);
8715: if ((ctxt->value == NULL) ||
8716: ((ctxt->value->type != XPATH_NODESET) &&
8717: (ctxt->value->type != XPATH_XSLT_TREE)))
8718: XP_ERROR(XPATH_INVALID_TYPE);
8719: cur = valuePop(ctxt);
8720:
8721: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8722: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8723: } else {
8724: int i = 0; /* Should be first in document order !!!!! */
8725: switch (cur->nodesetval->nodeTab[i]->type) {
8726: case XML_ELEMENT_NODE:
8727: case XML_ATTRIBUTE_NODE:
8728: case XML_PI_NODE:
8729: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8730: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8731: else
8732: valuePush(ctxt,
8733: xmlXPathCacheNewString(ctxt->context,
8734: cur->nodesetval->nodeTab[i]->name));
8735: break;
8736: case XML_NAMESPACE_DECL:
8737: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8738: ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
8739: break;
8740: default:
8741: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8742: }
8743: }
8744: xmlXPathReleaseObject(ctxt->context, cur);
8745: }
8746:
8747: /**
8748: * xmlXPathNamespaceURIFunction:
8749: * @ctxt: the XPath Parser context
8750: * @nargs: the number of arguments
8751: *
8752: * Implement the namespace-uri() XPath function
8753: * string namespace-uri(node-set?)
8754: * The namespace-uri function returns a string containing the
8755: * namespace URI of the expanded name of the node in the argument
8756: * node-set that is first in document order. If the node-set is empty,
8757: * the first node has no name, or the expanded name has no namespace
8758: * URI, an empty string is returned. If the argument is omitted it
8759: * defaults to the context node.
8760: */
8761: void
8762: xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8763: xmlXPathObjectPtr cur;
8764:
8765: if (ctxt == NULL) return;
8766:
8767: if (nargs == 0) {
8768: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8769: ctxt->context->node));
8770: nargs = 1;
8771: }
8772: CHECK_ARITY(1);
8773: if ((ctxt->value == NULL) ||
8774: ((ctxt->value->type != XPATH_NODESET) &&
8775: (ctxt->value->type != XPATH_XSLT_TREE)))
8776: XP_ERROR(XPATH_INVALID_TYPE);
8777: cur = valuePop(ctxt);
8778:
8779: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8780: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8781: } else {
8782: int i = 0; /* Should be first in document order !!!!! */
8783: switch (cur->nodesetval->nodeTab[i]->type) {
8784: case XML_ELEMENT_NODE:
8785: case XML_ATTRIBUTE_NODE:
8786: if (cur->nodesetval->nodeTab[i]->ns == NULL)
8787: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8788: else
8789: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8790: cur->nodesetval->nodeTab[i]->ns->href));
8791: break;
8792: default:
8793: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8794: }
8795: }
8796: xmlXPathReleaseObject(ctxt->context, cur);
8797: }
8798:
8799: /**
8800: * xmlXPathNameFunction:
8801: * @ctxt: the XPath Parser context
8802: * @nargs: the number of arguments
8803: *
8804: * Implement the name() XPath function
8805: * string name(node-set?)
8806: * The name function returns a string containing a QName representing
8807: * the name of the node in the argument node-set that is first in document
8808: * order. The QName must represent the name with respect to the namespace
8809: * declarations in effect on the node whose name is being represented.
8810: * Typically, this will be the form in which the name occurred in the XML
8811: * source. This need not be the case if there are namespace declarations
8812: * in effect on the node that associate multiple prefixes with the same
8813: * namespace. However, an implementation may include information about
8814: * the original prefix in its representation of nodes; in this case, an
8815: * implementation can ensure that the returned string is always the same
8816: * as the QName used in the XML source. If the argument it omitted it
8817: * defaults to the context node.
8818: * Libxml keep the original prefix so the "real qualified name" used is
8819: * returned.
8820: */
8821: static void
8822: xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
8823: {
8824: xmlXPathObjectPtr cur;
8825:
8826: if (nargs == 0) {
8827: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8828: ctxt->context->node));
8829: nargs = 1;
8830: }
8831:
8832: CHECK_ARITY(1);
8833: if ((ctxt->value == NULL) ||
8834: ((ctxt->value->type != XPATH_NODESET) &&
8835: (ctxt->value->type != XPATH_XSLT_TREE)))
8836: XP_ERROR(XPATH_INVALID_TYPE);
8837: cur = valuePop(ctxt);
8838:
8839: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8840: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8841: } else {
8842: int i = 0; /* Should be first in document order !!!!! */
8843:
8844: switch (cur->nodesetval->nodeTab[i]->type) {
8845: case XML_ELEMENT_NODE:
8846: case XML_ATTRIBUTE_NODE:
8847: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8848: valuePush(ctxt,
8849: xmlXPathCacheNewCString(ctxt->context, ""));
8850: else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
8851: (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
8852: valuePush(ctxt,
8853: xmlXPathCacheNewString(ctxt->context,
8854: cur->nodesetval->nodeTab[i]->name));
8855: } else {
8856: xmlChar *fullname;
8857:
8858: fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
8859: cur->nodesetval->nodeTab[i]->ns->prefix,
8860: NULL, 0);
8861: if (fullname == cur->nodesetval->nodeTab[i]->name)
8862: fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
8863: if (fullname == NULL) {
8864: XP_ERROR(XPATH_MEMORY_ERROR);
8865: }
8866: valuePush(ctxt, xmlXPathCacheWrapString(
8867: ctxt->context, fullname));
8868: }
8869: break;
8870: default:
8871: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8872: cur->nodesetval->nodeTab[i]));
8873: xmlXPathLocalNameFunction(ctxt, 1);
8874: }
8875: }
8876: xmlXPathReleaseObject(ctxt->context, cur);
8877: }
8878:
8879:
8880: /**
8881: * xmlXPathStringFunction:
8882: * @ctxt: the XPath Parser context
8883: * @nargs: the number of arguments
8884: *
8885: * Implement the string() XPath function
8886: * string string(object?)
8887: * The string function converts an object to a string as follows:
8888: * - A node-set is converted to a string by returning the value of
8889: * the node in the node-set that is first in document order.
8890: * If the node-set is empty, an empty string is returned.
8891: * - A number is converted to a string as follows
8892: * + NaN is converted to the string NaN
8893: * + positive zero is converted to the string 0
8894: * + negative zero is converted to the string 0
8895: * + positive infinity is converted to the string Infinity
8896: * + negative infinity is converted to the string -Infinity
8897: * + if the number is an integer, the number is represented in
8898: * decimal form as a Number with no decimal point and no leading
8899: * zeros, preceded by a minus sign (-) if the number is negative
8900: * + otherwise, the number is represented in decimal form as a
8901: * Number including a decimal point with at least one digit
8902: * before the decimal point and at least one digit after the
8903: * decimal point, preceded by a minus sign (-) if the number
8904: * is negative; there must be no leading zeros before the decimal
8905: * point apart possibly from the one required digit immediately
8906: * before the decimal point; beyond the one required digit
8907: * after the decimal point there must be as many, but only as
8908: * many, more digits as are needed to uniquely distinguish the
8909: * number from all other IEEE 754 numeric values.
8910: * - The boolean false value is converted to the string false.
8911: * The boolean true value is converted to the string true.
8912: *
8913: * If the argument is omitted, it defaults to a node-set with the
8914: * context node as its only member.
8915: */
8916: void
8917: xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8918: xmlXPathObjectPtr cur;
8919:
8920: if (ctxt == NULL) return;
8921: if (nargs == 0) {
8922: valuePush(ctxt,
8923: xmlXPathCacheWrapString(ctxt->context,
8924: xmlXPathCastNodeToString(ctxt->context->node)));
8925: return;
8926: }
8927:
8928: CHECK_ARITY(1);
8929: cur = valuePop(ctxt);
8930: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8931: valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
8932: }
8933:
8934: /**
8935: * xmlXPathStringLengthFunction:
8936: * @ctxt: the XPath Parser context
8937: * @nargs: the number of arguments
8938: *
8939: * Implement the string-length() XPath function
8940: * number string-length(string?)
8941: * The string-length returns the number of characters in the string
8942: * (see [3.6 Strings]). If the argument is omitted, it defaults to
8943: * the context node converted to a string, in other words the value
8944: * of the context node.
8945: */
8946: void
8947: xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8948: xmlXPathObjectPtr cur;
8949:
8950: if (nargs == 0) {
8951: if ((ctxt == NULL) || (ctxt->context == NULL))
8952: return;
8953: if (ctxt->context->node == NULL) {
8954: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
8955: } else {
8956: xmlChar *content;
8957:
8958: content = xmlXPathCastNodeToString(ctxt->context->node);
8959: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8960: xmlUTF8Strlen(content)));
8961: xmlFree(content);
8962: }
8963: return;
8964: }
8965: CHECK_ARITY(1);
8966: CAST_TO_STRING;
8967: CHECK_TYPE(XPATH_STRING);
8968: cur = valuePop(ctxt);
8969: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8970: xmlUTF8Strlen(cur->stringval)));
8971: xmlXPathReleaseObject(ctxt->context, cur);
8972: }
8973:
8974: /**
8975: * xmlXPathConcatFunction:
8976: * @ctxt: the XPath Parser context
8977: * @nargs: the number of arguments
8978: *
8979: * Implement the concat() XPath function
8980: * string concat(string, string, string*)
8981: * The concat function returns the concatenation of its arguments.
8982: */
8983: void
8984: xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8985: xmlXPathObjectPtr cur, newobj;
8986: xmlChar *tmp;
8987:
8988: if (ctxt == NULL) return;
8989: if (nargs < 2) {
8990: CHECK_ARITY(2);
8991: }
8992:
8993: CAST_TO_STRING;
8994: cur = valuePop(ctxt);
8995: if ((cur == NULL) || (cur->type != XPATH_STRING)) {
8996: xmlXPathReleaseObject(ctxt->context, cur);
8997: return;
8998: }
8999: nargs--;
9000:
9001: while (nargs > 0) {
9002: CAST_TO_STRING;
9003: newobj = valuePop(ctxt);
9004: if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
9005: xmlXPathReleaseObject(ctxt->context, newobj);
9006: xmlXPathReleaseObject(ctxt->context, cur);
9007: XP_ERROR(XPATH_INVALID_TYPE);
9008: }
9009: tmp = xmlStrcat(newobj->stringval, cur->stringval);
9010: newobj->stringval = cur->stringval;
9011: cur->stringval = tmp;
9012: xmlXPathReleaseObject(ctxt->context, newobj);
9013: nargs--;
9014: }
9015: valuePush(ctxt, cur);
9016: }
9017:
9018: /**
9019: * xmlXPathContainsFunction:
9020: * @ctxt: the XPath Parser context
9021: * @nargs: the number of arguments
9022: *
9023: * Implement the contains() XPath function
9024: * boolean contains(string, string)
9025: * The contains function returns true if the first argument string
9026: * contains the second argument string, and otherwise returns false.
9027: */
9028: void
9029: xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9030: xmlXPathObjectPtr hay, needle;
9031:
9032: CHECK_ARITY(2);
9033: CAST_TO_STRING;
9034: CHECK_TYPE(XPATH_STRING);
9035: needle = valuePop(ctxt);
9036: CAST_TO_STRING;
9037: hay = valuePop(ctxt);
9038:
9039: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
9040: xmlXPathReleaseObject(ctxt->context, hay);
9041: xmlXPathReleaseObject(ctxt->context, needle);
9042: XP_ERROR(XPATH_INVALID_TYPE);
9043: }
9044: if (xmlStrstr(hay->stringval, needle->stringval))
9045: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9046: else
9047: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9048: xmlXPathReleaseObject(ctxt->context, hay);
9049: xmlXPathReleaseObject(ctxt->context, needle);
9050: }
9051:
9052: /**
9053: * xmlXPathStartsWithFunction:
9054: * @ctxt: the XPath Parser context
9055: * @nargs: the number of arguments
9056: *
9057: * Implement the starts-with() XPath function
9058: * boolean starts-with(string, string)
9059: * The starts-with function returns true if the first argument string
9060: * starts with the second argument string, and otherwise returns false.
9061: */
9062: void
9063: xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9064: xmlXPathObjectPtr hay, needle;
9065: int n;
9066:
9067: CHECK_ARITY(2);
9068: CAST_TO_STRING;
9069: CHECK_TYPE(XPATH_STRING);
9070: needle = valuePop(ctxt);
9071: CAST_TO_STRING;
9072: hay = valuePop(ctxt);
9073:
9074: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
9075: xmlXPathReleaseObject(ctxt->context, hay);
9076: xmlXPathReleaseObject(ctxt->context, needle);
9077: XP_ERROR(XPATH_INVALID_TYPE);
9078: }
9079: n = xmlStrlen(needle->stringval);
9080: if (xmlStrncmp(hay->stringval, needle->stringval, n))
9081: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9082: else
9083: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9084: xmlXPathReleaseObject(ctxt->context, hay);
9085: xmlXPathReleaseObject(ctxt->context, needle);
9086: }
9087:
9088: /**
9089: * xmlXPathSubstringFunction:
9090: * @ctxt: the XPath Parser context
9091: * @nargs: the number of arguments
9092: *
9093: * Implement the substring() XPath function
9094: * string substring(string, number, number?)
9095: * The substring function returns the substring of the first argument
9096: * starting at the position specified in the second argument with
9097: * length specified in the third argument. For example,
9098: * substring("12345",2,3) returns "234". If the third argument is not
9099: * specified, it returns the substring starting at the position specified
9100: * in the second argument and continuing to the end of the string. For
9101: * example, substring("12345",2) returns "2345". More precisely, each
9102: * character in the string (see [3.6 Strings]) is considered to have a
9103: * numeric position: the position of the first character is 1, the position
9104: * of the second character is 2 and so on. The returned substring contains
9105: * those characters for which the position of the character is greater than
9106: * or equal to the second argument and, if the third argument is specified,
9107: * less than the sum of the second and third arguments; the comparisons
9108: * and addition used for the above follow the standard IEEE 754 rules. Thus:
9109: * - substring("12345", 1.5, 2.6) returns "234"
9110: * - substring("12345", 0, 3) returns "12"
9111: * - substring("12345", 0 div 0, 3) returns ""
9112: * - substring("12345", 1, 0 div 0) returns ""
9113: * - substring("12345", -42, 1 div 0) returns "12345"
9114: * - substring("12345", -1 div 0, 1 div 0) returns ""
9115: */
9116: void
9117: xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9118: xmlXPathObjectPtr str, start, len;
9119: double le=0, in;
9120: int i, l, m;
9121: xmlChar *ret;
9122:
9123: if (nargs < 2) {
9124: CHECK_ARITY(2);
9125: }
9126: if (nargs > 3) {
9127: CHECK_ARITY(3);
9128: }
9129: /*
9130: * take care of possible last (position) argument
9131: */
9132: if (nargs == 3) {
9133: CAST_TO_NUMBER;
9134: CHECK_TYPE(XPATH_NUMBER);
9135: len = valuePop(ctxt);
9136: le = len->floatval;
9137: xmlXPathReleaseObject(ctxt->context, len);
9138: }
9139:
9140: CAST_TO_NUMBER;
9141: CHECK_TYPE(XPATH_NUMBER);
9142: start = valuePop(ctxt);
9143: in = start->floatval;
9144: xmlXPathReleaseObject(ctxt->context, start);
9145: CAST_TO_STRING;
9146: CHECK_TYPE(XPATH_STRING);
9147: str = valuePop(ctxt);
9148: m = xmlUTF8Strlen((const unsigned char *)str->stringval);
9149:
9150: /*
9151: * If last pos not present, calculate last position
9152: */
9153: if (nargs != 3) {
9154: le = (double)m;
9155: if (in < 1.0)
9156: in = 1.0;
9157: }
9158:
9159: /* Need to check for the special cases where either
9160: * the index is NaN, the length is NaN, or both
9161: * arguments are infinity (relying on Inf + -Inf = NaN)
9162: */
9163: if (!xmlXPathIsInf(in) && !xmlXPathIsNaN(in + le)) {
9164: /*
9165: * To meet the requirements of the spec, the arguments
9166: * must be converted to integer format before
9167: * initial index calculations are done
9168: *
9169: * First we go to integer form, rounding up
9170: * and checking for special cases
9171: */
9172: i = (int) in;
9173: if (((double)i)+0.5 <= in) i++;
9174:
9175: if (xmlXPathIsInf(le) == 1) {
9176: l = m;
9177: if (i < 1)
9178: i = 1;
9179: }
9180: else if (xmlXPathIsInf(le) == -1 || le < 0.0)
9181: l = 0;
9182: else {
9183: l = (int) le;
9184: if (((double)l)+0.5 <= le) l++;
9185: }
9186:
9187: /* Now we normalize inidices */
9188: i -= 1;
9189: l += i;
9190: if (i < 0)
9191: i = 0;
9192: if (l > m)
9193: l = m;
9194:
9195: /* number of chars to copy */
9196: l -= i;
9197:
9198: ret = xmlUTF8Strsub(str->stringval, i, l);
9199: }
9200: else {
9201: ret = NULL;
9202: }
9203: if (ret == NULL)
9204: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
9205: else {
9206: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
9207: xmlFree(ret);
9208: }
9209: xmlXPathReleaseObject(ctxt->context, str);
9210: }
9211:
9212: /**
9213: * xmlXPathSubstringBeforeFunction:
9214: * @ctxt: the XPath Parser context
9215: * @nargs: the number of arguments
9216: *
9217: * Implement the substring-before() XPath function
9218: * string substring-before(string, string)
9219: * The substring-before function returns the substring of the first
9220: * argument string that precedes the first occurrence of the second
9221: * argument string in the first argument string, or the empty string
9222: * if the first argument string does not contain the second argument
9223: * string. For example, substring-before("1999/04/01","/") returns 1999.
9224: */
9225: void
9226: xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9227: xmlXPathObjectPtr str;
9228: xmlXPathObjectPtr find;
1.1.1.3 ! misho 9229: xmlBufPtr target;
1.1 misho 9230: const xmlChar *point;
9231: int offset;
9232:
9233: CHECK_ARITY(2);
9234: CAST_TO_STRING;
9235: find = valuePop(ctxt);
9236: CAST_TO_STRING;
9237: str = valuePop(ctxt);
9238:
1.1.1.3 ! misho 9239: target = xmlBufCreate();
1.1 misho 9240: if (target) {
9241: point = xmlStrstr(str->stringval, find->stringval);
9242: if (point) {
9243: offset = (int)(point - str->stringval);
1.1.1.3 ! misho 9244: xmlBufAdd(target, str->stringval, offset);
1.1 misho 9245: }
9246: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
1.1.1.3 ! misho 9247: xmlBufContent(target)));
! 9248: xmlBufFree(target);
1.1 misho 9249: }
9250: xmlXPathReleaseObject(ctxt->context, str);
9251: xmlXPathReleaseObject(ctxt->context, find);
9252: }
9253:
9254: /**
9255: * xmlXPathSubstringAfterFunction:
9256: * @ctxt: the XPath Parser context
9257: * @nargs: the number of arguments
9258: *
9259: * Implement the substring-after() XPath function
9260: * string substring-after(string, string)
9261: * The substring-after function returns the substring of the first
9262: * argument string that follows the first occurrence of the second
9263: * argument string in the first argument string, or the empty stringi
9264: * if the first argument string does not contain the second argument
9265: * string. For example, substring-after("1999/04/01","/") returns 04/01,
9266: * and substring-after("1999/04/01","19") returns 99/04/01.
9267: */
9268: void
9269: xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9270: xmlXPathObjectPtr str;
9271: xmlXPathObjectPtr find;
1.1.1.3 ! misho 9272: xmlBufPtr target;
1.1 misho 9273: const xmlChar *point;
9274: int offset;
9275:
9276: CHECK_ARITY(2);
9277: CAST_TO_STRING;
9278: find = valuePop(ctxt);
9279: CAST_TO_STRING;
9280: str = valuePop(ctxt);
9281:
1.1.1.3 ! misho 9282: target = xmlBufCreate();
1.1 misho 9283: if (target) {
9284: point = xmlStrstr(str->stringval, find->stringval);
9285: if (point) {
9286: offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
1.1.1.3 ! misho 9287: xmlBufAdd(target, &str->stringval[offset],
1.1 misho 9288: xmlStrlen(str->stringval) - offset);
9289: }
9290: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
1.1.1.3 ! misho 9291: xmlBufContent(target)));
! 9292: xmlBufFree(target);
1.1 misho 9293: }
9294: xmlXPathReleaseObject(ctxt->context, str);
9295: xmlXPathReleaseObject(ctxt->context, find);
9296: }
9297:
9298: /**
9299: * xmlXPathNormalizeFunction:
9300: * @ctxt: the XPath Parser context
9301: * @nargs: the number of arguments
9302: *
9303: * Implement the normalize-space() XPath function
9304: * string normalize-space(string?)
9305: * The normalize-space function returns the argument string with white
9306: * space normalized by stripping leading and trailing whitespace
9307: * and replacing sequences of whitespace characters by a single
9308: * space. Whitespace characters are the same allowed by the S production
9309: * in XML. If the argument is omitted, it defaults to the context
9310: * node converted to a string, in other words the value of the context node.
9311: */
9312: void
9313: xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9314: xmlXPathObjectPtr obj = NULL;
9315: xmlChar *source = NULL;
1.1.1.3 ! misho 9316: xmlBufPtr target;
1.1 misho 9317: xmlChar blank;
9318:
9319: if (ctxt == NULL) return;
9320: if (nargs == 0) {
9321: /* Use current context node */
9322: valuePush(ctxt,
9323: xmlXPathCacheWrapString(ctxt->context,
9324: xmlXPathCastNodeToString(ctxt->context->node)));
9325: nargs = 1;
9326: }
9327:
9328: CHECK_ARITY(1);
9329: CAST_TO_STRING;
9330: CHECK_TYPE(XPATH_STRING);
9331: obj = valuePop(ctxt);
9332: source = obj->stringval;
9333:
1.1.1.3 ! misho 9334: target = xmlBufCreate();
1.1 misho 9335: if (target && source) {
9336:
9337: /* Skip leading whitespaces */
9338: while (IS_BLANK_CH(*source))
9339: source++;
9340:
9341: /* Collapse intermediate whitespaces, and skip trailing whitespaces */
9342: blank = 0;
9343: while (*source) {
9344: if (IS_BLANK_CH(*source)) {
9345: blank = 0x20;
9346: } else {
9347: if (blank) {
1.1.1.3 ! misho 9348: xmlBufAdd(target, &blank, 1);
1.1 misho 9349: blank = 0;
9350: }
1.1.1.3 ! misho 9351: xmlBufAdd(target, source, 1);
1.1 misho 9352: }
9353: source++;
9354: }
9355: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
1.1.1.3 ! misho 9356: xmlBufContent(target)));
! 9357: xmlBufFree(target);
1.1 misho 9358: }
9359: xmlXPathReleaseObject(ctxt->context, obj);
9360: }
9361:
9362: /**
9363: * xmlXPathTranslateFunction:
9364: * @ctxt: the XPath Parser context
9365: * @nargs: the number of arguments
9366: *
9367: * Implement the translate() XPath function
9368: * string translate(string, string, string)
9369: * The translate function returns the first argument string with
9370: * occurrences of characters in the second argument string replaced
9371: * by the character at the corresponding position in the third argument
9372: * string. For example, translate("bar","abc","ABC") returns the string
9373: * BAr. If there is a character in the second argument string with no
9374: * character at a corresponding position in the third argument string
9375: * (because the second argument string is longer than the third argument
9376: * string), then occurrences of that character in the first argument
9377: * string are removed. For example, translate("--aaa--","abc-","ABC")
9378: * returns "AAA". If a character occurs more than once in second
9379: * argument string, then the first occurrence determines the replacement
9380: * character. If the third argument string is longer than the second
9381: * argument string, then excess characters are ignored.
9382: */
9383: void
9384: xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9385: xmlXPathObjectPtr str;
9386: xmlXPathObjectPtr from;
9387: xmlXPathObjectPtr to;
1.1.1.3 ! misho 9388: xmlBufPtr target;
1.1 misho 9389: int offset, max;
9390: xmlChar ch;
9391: const xmlChar *point;
9392: xmlChar *cptr;
9393:
9394: CHECK_ARITY(3);
9395:
9396: CAST_TO_STRING;
9397: to = valuePop(ctxt);
9398: CAST_TO_STRING;
9399: from = valuePop(ctxt);
9400: CAST_TO_STRING;
9401: str = valuePop(ctxt);
9402:
1.1.1.3 ! misho 9403: target = xmlBufCreate();
1.1 misho 9404: if (target) {
9405: max = xmlUTF8Strlen(to->stringval);
9406: for (cptr = str->stringval; (ch=*cptr); ) {
9407: offset = xmlUTF8Strloc(from->stringval, cptr);
9408: if (offset >= 0) {
9409: if (offset < max) {
9410: point = xmlUTF8Strpos(to->stringval, offset);
9411: if (point)
1.1.1.3 ! misho 9412: xmlBufAdd(target, point, xmlUTF8Strsize(point, 1));
1.1 misho 9413: }
9414: } else
1.1.1.3 ! misho 9415: xmlBufAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
1.1 misho 9416:
9417: /* Step to next character in input */
9418: cptr++;
9419: if ( ch & 0x80 ) {
9420: /* if not simple ascii, verify proper format */
9421: if ( (ch & 0xc0) != 0xc0 ) {
9422: xmlGenericError(xmlGenericErrorContext,
9423: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
1.1.1.2 misho 9424: /* not asserting an XPath error is probably better */
1.1 misho 9425: break;
9426: }
9427: /* then skip over remaining bytes for this char */
9428: while ( (ch <<= 1) & 0x80 )
9429: if ( (*cptr++ & 0xc0) != 0x80 ) {
9430: xmlGenericError(xmlGenericErrorContext,
9431: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
1.1.1.2 misho 9432: /* not asserting an XPath error is probably better */
1.1 misho 9433: break;
9434: }
9435: if (ch & 0x80) /* must have had error encountered */
9436: break;
9437: }
9438: }
9439: }
9440: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
1.1.1.3 ! misho 9441: xmlBufContent(target)));
! 9442: xmlBufFree(target);
1.1 misho 9443: xmlXPathReleaseObject(ctxt->context, str);
9444: xmlXPathReleaseObject(ctxt->context, from);
9445: xmlXPathReleaseObject(ctxt->context, to);
9446: }
9447:
9448: /**
9449: * xmlXPathBooleanFunction:
9450: * @ctxt: the XPath Parser context
9451: * @nargs: the number of arguments
9452: *
9453: * Implement the boolean() XPath function
9454: * boolean boolean(object)
9455: * The boolean function converts its argument to a boolean as follows:
9456: * - a number is true if and only if it is neither positive or
9457: * negative zero nor NaN
9458: * - a node-set is true if and only if it is non-empty
9459: * - a string is true if and only if its length is non-zero
9460: */
9461: void
9462: xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9463: xmlXPathObjectPtr cur;
9464:
9465: CHECK_ARITY(1);
9466: cur = valuePop(ctxt);
9467: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
9468: cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
9469: valuePush(ctxt, cur);
9470: }
9471:
9472: /**
9473: * xmlXPathNotFunction:
9474: * @ctxt: the XPath Parser context
9475: * @nargs: the number of arguments
9476: *
9477: * Implement the not() XPath function
9478: * boolean not(boolean)
9479: * The not function returns true if its argument is false,
9480: * and false otherwise.
9481: */
9482: void
9483: xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9484: CHECK_ARITY(1);
9485: CAST_TO_BOOLEAN;
9486: CHECK_TYPE(XPATH_BOOLEAN);
9487: ctxt->value->boolval = ! ctxt->value->boolval;
9488: }
9489:
9490: /**
9491: * xmlXPathTrueFunction:
9492: * @ctxt: the XPath Parser context
9493: * @nargs: the number of arguments
9494: *
9495: * Implement the true() XPath function
9496: * boolean true()
9497: */
9498: void
9499: xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9500: CHECK_ARITY(0);
9501: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9502: }
9503:
9504: /**
9505: * xmlXPathFalseFunction:
9506: * @ctxt: the XPath Parser context
9507: * @nargs: the number of arguments
9508: *
9509: * Implement the false() XPath function
9510: * boolean false()
9511: */
9512: void
9513: xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9514: CHECK_ARITY(0);
9515: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9516: }
9517:
9518: /**
9519: * xmlXPathLangFunction:
9520: * @ctxt: the XPath Parser context
9521: * @nargs: the number of arguments
9522: *
9523: * Implement the lang() XPath function
9524: * boolean lang(string)
9525: * The lang function returns true or false depending on whether the
9526: * language of the context node as specified by xml:lang attributes
9527: * is the same as or is a sublanguage of the language specified by
9528: * the argument string. The language of the context node is determined
9529: * by the value of the xml:lang attribute on the context node, or, if
9530: * the context node has no xml:lang attribute, by the value of the
9531: * xml:lang attribute on the nearest ancestor of the context node that
9532: * has an xml:lang attribute. If there is no such attribute, then lang
9533: * returns false. If there is such an attribute, then lang returns
9534: * true if the attribute value is equal to the argument ignoring case,
9535: * or if there is some suffix starting with - such that the attribute
9536: * value is equal to the argument ignoring that suffix of the attribute
9537: * value and ignoring case.
9538: */
9539: void
9540: xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9541: xmlXPathObjectPtr val = NULL;
9542: const xmlChar *theLang = NULL;
9543: const xmlChar *lang;
9544: int ret = 0;
9545: int i;
9546:
9547: CHECK_ARITY(1);
9548: CAST_TO_STRING;
9549: CHECK_TYPE(XPATH_STRING);
9550: val = valuePop(ctxt);
9551: lang = val->stringval;
9552: theLang = xmlNodeGetLang(ctxt->context->node);
9553: if ((theLang != NULL) && (lang != NULL)) {
9554: for (i = 0;lang[i] != 0;i++)
9555: if (toupper(lang[i]) != toupper(theLang[i]))
9556: goto not_equal;
9557: if ((theLang[i] == 0) || (theLang[i] == '-'))
9558: ret = 1;
9559: }
9560: not_equal:
9561: if (theLang != NULL)
9562: xmlFree((void *)theLang);
9563:
9564: xmlXPathReleaseObject(ctxt->context, val);
9565: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
9566: }
9567:
9568: /**
9569: * xmlXPathNumberFunction:
9570: * @ctxt: the XPath Parser context
9571: * @nargs: the number of arguments
9572: *
9573: * Implement the number() XPath function
9574: * number number(object?)
9575: */
9576: void
9577: xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9578: xmlXPathObjectPtr cur;
9579: double res;
9580:
9581: if (ctxt == NULL) return;
9582: if (nargs == 0) {
9583: if (ctxt->context->node == NULL) {
9584: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
9585: } else {
9586: xmlChar* content = xmlNodeGetContent(ctxt->context->node);
9587:
9588: res = xmlXPathStringEvalNumber(content);
9589: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9590: xmlFree(content);
9591: }
9592: return;
9593: }
9594:
9595: CHECK_ARITY(1);
9596: cur = valuePop(ctxt);
9597: valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
9598: }
9599:
9600: /**
9601: * xmlXPathSumFunction:
9602: * @ctxt: the XPath Parser context
9603: * @nargs: the number of arguments
9604: *
9605: * Implement the sum() XPath function
9606: * number sum(node-set)
9607: * The sum function returns the sum of the values of the nodes in
9608: * the argument node-set.
9609: */
9610: void
9611: xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9612: xmlXPathObjectPtr cur;
9613: int i;
9614: double res = 0.0;
9615:
9616: CHECK_ARITY(1);
9617: if ((ctxt->value == NULL) ||
9618: ((ctxt->value->type != XPATH_NODESET) &&
9619: (ctxt->value->type != XPATH_XSLT_TREE)))
9620: XP_ERROR(XPATH_INVALID_TYPE);
9621: cur = valuePop(ctxt);
9622:
9623: if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
9624: for (i = 0; i < cur->nodesetval->nodeNr; i++) {
9625: res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
9626: }
9627: }
9628: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9629: xmlXPathReleaseObject(ctxt->context, cur);
9630: }
9631:
9632: /*
9633: * To assure working code on multiple platforms, we want to only depend
9634: * upon the characteristic truncation of converting a floating point value
9635: * to an integer. Unfortunately, because of the different storage sizes
9636: * of our internal floating point value (double) and integer (int), we
9637: * can't directly convert (see bug 301162). This macro is a messy
9638: * 'workaround'
9639: */
9640: #define XTRUNC(f, v) \
9641: f = fmod((v), INT_MAX); \
9642: f = (v) - (f) + (double)((int)(f));
9643:
9644: /**
9645: * xmlXPathFloorFunction:
9646: * @ctxt: the XPath Parser context
9647: * @nargs: the number of arguments
9648: *
9649: * Implement the floor() XPath function
9650: * number floor(number)
9651: * The floor function returns the largest (closest to positive infinity)
9652: * number that is not greater than the argument and that is an integer.
9653: */
9654: void
9655: xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9656: double f;
9657:
9658: CHECK_ARITY(1);
9659: CAST_TO_NUMBER;
9660: CHECK_TYPE(XPATH_NUMBER);
9661:
9662: XTRUNC(f, ctxt->value->floatval);
9663: if (f != ctxt->value->floatval) {
9664: if (ctxt->value->floatval > 0)
9665: ctxt->value->floatval = f;
9666: else
9667: ctxt->value->floatval = f - 1;
9668: }
9669: }
9670:
9671: /**
9672: * xmlXPathCeilingFunction:
9673: * @ctxt: the XPath Parser context
9674: * @nargs: the number of arguments
9675: *
9676: * Implement the ceiling() XPath function
9677: * number ceiling(number)
9678: * The ceiling function returns the smallest (closest to negative infinity)
9679: * number that is not less than the argument and that is an integer.
9680: */
9681: void
9682: xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9683: double f;
9684:
9685: CHECK_ARITY(1);
9686: CAST_TO_NUMBER;
9687: CHECK_TYPE(XPATH_NUMBER);
9688:
9689: #if 0
9690: ctxt->value->floatval = ceil(ctxt->value->floatval);
9691: #else
9692: XTRUNC(f, ctxt->value->floatval);
9693: if (f != ctxt->value->floatval) {
9694: if (ctxt->value->floatval > 0)
9695: ctxt->value->floatval = f + 1;
9696: else {
9697: if (ctxt->value->floatval < 0 && f == 0)
9698: ctxt->value->floatval = xmlXPathNZERO;
9699: else
9700: ctxt->value->floatval = f;
9701: }
9702:
9703: }
9704: #endif
9705: }
9706:
9707: /**
9708: * xmlXPathRoundFunction:
9709: * @ctxt: the XPath Parser context
9710: * @nargs: the number of arguments
9711: *
9712: * Implement the round() XPath function
9713: * number round(number)
9714: * The round function returns the number that is closest to the
9715: * argument and that is an integer. If there are two such numbers,
9716: * then the one that is even is returned.
9717: */
9718: void
9719: xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9720: double f;
9721:
9722: CHECK_ARITY(1);
9723: CAST_TO_NUMBER;
9724: CHECK_TYPE(XPATH_NUMBER);
9725:
9726: if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
9727: (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
9728: (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
9729: (ctxt->value->floatval == 0.0))
9730: return;
9731:
9732: XTRUNC(f, ctxt->value->floatval);
9733: if (ctxt->value->floatval < 0) {
9734: if (ctxt->value->floatval < f - 0.5)
9735: ctxt->value->floatval = f - 1;
9736: else
9737: ctxt->value->floatval = f;
9738: if (ctxt->value->floatval == 0)
9739: ctxt->value->floatval = xmlXPathNZERO;
9740: } else {
9741: if (ctxt->value->floatval < f + 0.5)
9742: ctxt->value->floatval = f;
9743: else
9744: ctxt->value->floatval = f + 1;
9745: }
9746: }
9747:
9748: /************************************************************************
9749: * *
9750: * The Parser *
9751: * *
9752: ************************************************************************/
9753:
9754: /*
9755: * a few forward declarations since we use a recursive call based
9756: * implementation.
9757: */
9758: static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
9759: static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
9760: static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
9761: static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
9762: static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
9763: int qualified);
9764:
9765: /**
9766: * xmlXPathCurrentChar:
9767: * @ctxt: the XPath parser context
9768: * @cur: pointer to the beginning of the char
9769: * @len: pointer to the length of the char read
9770: *
9771: * The current char value, if using UTF-8 this may actually span multiple
9772: * bytes in the input buffer.
9773: *
9774: * Returns the current char value and its length
9775: */
9776:
9777: static int
9778: xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
9779: unsigned char c;
9780: unsigned int val;
9781: const xmlChar *cur;
9782:
9783: if (ctxt == NULL)
9784: return(0);
9785: cur = ctxt->cur;
9786:
9787: /*
9788: * We are supposed to handle UTF8, check it's valid
9789: * From rfc2044: encoding of the Unicode values on UTF-8:
9790: *
9791: * UCS-4 range (hex.) UTF-8 octet sequence (binary)
9792: * 0000 0000-0000 007F 0xxxxxxx
9793: * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
9794: * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
9795: *
9796: * Check for the 0x110000 limit too
9797: */
9798: c = *cur;
9799: if (c & 0x80) {
9800: if ((cur[1] & 0xc0) != 0x80)
9801: goto encoding_error;
9802: if ((c & 0xe0) == 0xe0) {
9803:
9804: if ((cur[2] & 0xc0) != 0x80)
9805: goto encoding_error;
9806: if ((c & 0xf0) == 0xf0) {
9807: if (((c & 0xf8) != 0xf0) ||
9808: ((cur[3] & 0xc0) != 0x80))
9809: goto encoding_error;
9810: /* 4-byte code */
9811: *len = 4;
9812: val = (cur[0] & 0x7) << 18;
9813: val |= (cur[1] & 0x3f) << 12;
9814: val |= (cur[2] & 0x3f) << 6;
9815: val |= cur[3] & 0x3f;
9816: } else {
9817: /* 3-byte code */
9818: *len = 3;
9819: val = (cur[0] & 0xf) << 12;
9820: val |= (cur[1] & 0x3f) << 6;
9821: val |= cur[2] & 0x3f;
9822: }
9823: } else {
9824: /* 2-byte code */
9825: *len = 2;
9826: val = (cur[0] & 0x1f) << 6;
9827: val |= cur[1] & 0x3f;
9828: }
9829: if (!IS_CHAR(val)) {
9830: XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
9831: }
9832: return(val);
9833: } else {
9834: /* 1-byte code */
9835: *len = 1;
9836: return((int) *cur);
9837: }
9838: encoding_error:
9839: /*
9840: * If we detect an UTF8 error that probably means that the
9841: * input encoding didn't get properly advertised in the
9842: * declaration header. Report the error and switch the encoding
9843: * to ISO-Latin-1 (if you don't like this policy, just declare the
9844: * encoding !)
9845: */
9846: *len = 0;
9847: XP_ERROR0(XPATH_ENCODING_ERROR);
9848: }
9849:
9850: /**
9851: * xmlXPathParseNCName:
9852: * @ctxt: the XPath Parser context
9853: *
9854: * parse an XML namespace non qualified name.
9855: *
9856: * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
9857: *
9858: * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
9859: * CombiningChar | Extender
9860: *
9861: * Returns the namespace name or NULL
9862: */
9863:
9864: xmlChar *
9865: xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
9866: const xmlChar *in;
9867: xmlChar *ret;
9868: int count = 0;
9869:
9870: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9871: /*
9872: * Accelerator for simple ASCII names
9873: */
9874: in = ctxt->cur;
9875: if (((*in >= 0x61) && (*in <= 0x7A)) ||
9876: ((*in >= 0x41) && (*in <= 0x5A)) ||
9877: (*in == '_')) {
9878: in++;
9879: while (((*in >= 0x61) && (*in <= 0x7A)) ||
9880: ((*in >= 0x41) && (*in <= 0x5A)) ||
9881: ((*in >= 0x30) && (*in <= 0x39)) ||
9882: (*in == '_') || (*in == '.') ||
9883: (*in == '-'))
9884: in++;
9885: if ((*in == ' ') || (*in == '>') || (*in == '/') ||
9886: (*in == '[') || (*in == ']') || (*in == ':') ||
9887: (*in == '@') || (*in == '*')) {
9888: count = in - ctxt->cur;
9889: if (count == 0)
9890: return(NULL);
9891: ret = xmlStrndup(ctxt->cur, count);
9892: ctxt->cur = in;
9893: return(ret);
9894: }
9895: }
9896: return(xmlXPathParseNameComplex(ctxt, 0));
9897: }
9898:
9899:
9900: /**
9901: * xmlXPathParseQName:
9902: * @ctxt: the XPath Parser context
9903: * @prefix: a xmlChar **
9904: *
9905: * parse an XML qualified name
9906: *
9907: * [NS 5] QName ::= (Prefix ':')? LocalPart
9908: *
9909: * [NS 6] Prefix ::= NCName
9910: *
9911: * [NS 7] LocalPart ::= NCName
9912: *
9913: * Returns the function returns the local part, and prefix is updated
9914: * to get the Prefix if any.
9915: */
9916:
9917: static xmlChar *
9918: xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
9919: xmlChar *ret = NULL;
9920:
9921: *prefix = NULL;
9922: ret = xmlXPathParseNCName(ctxt);
9923: if (ret && CUR == ':') {
9924: *prefix = ret;
9925: NEXT;
9926: ret = xmlXPathParseNCName(ctxt);
9927: }
9928: return(ret);
9929: }
9930:
9931: /**
9932: * xmlXPathParseName:
9933: * @ctxt: the XPath Parser context
9934: *
9935: * parse an XML name
9936: *
9937: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
9938: * CombiningChar | Extender
9939: *
9940: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
9941: *
9942: * Returns the namespace name or NULL
9943: */
9944:
9945: xmlChar *
9946: xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
9947: const xmlChar *in;
9948: xmlChar *ret;
1.1.1.3 ! misho 9949: size_t count = 0;
1.1 misho 9950:
9951: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9952: /*
9953: * Accelerator for simple ASCII names
9954: */
9955: in = ctxt->cur;
9956: if (((*in >= 0x61) && (*in <= 0x7A)) ||
9957: ((*in >= 0x41) && (*in <= 0x5A)) ||
9958: (*in == '_') || (*in == ':')) {
9959: in++;
9960: while (((*in >= 0x61) && (*in <= 0x7A)) ||
9961: ((*in >= 0x41) && (*in <= 0x5A)) ||
9962: ((*in >= 0x30) && (*in <= 0x39)) ||
9963: (*in == '_') || (*in == '-') ||
9964: (*in == ':') || (*in == '.'))
9965: in++;
9966: if ((*in > 0) && (*in < 0x80)) {
9967: count = in - ctxt->cur;
1.1.1.3 ! misho 9968: if (count > XML_MAX_NAME_LENGTH) {
! 9969: ctxt->cur = in;
! 9970: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 9971: }
1.1 misho 9972: ret = xmlStrndup(ctxt->cur, count);
9973: ctxt->cur = in;
9974: return(ret);
9975: }
9976: }
9977: return(xmlXPathParseNameComplex(ctxt, 1));
9978: }
9979:
9980: static xmlChar *
9981: xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
9982: xmlChar buf[XML_MAX_NAMELEN + 5];
9983: int len = 0, l;
9984: int c;
9985:
9986: /*
9987: * Handler for more complex cases
9988: */
9989: c = CUR_CHAR(l);
9990: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
9991: (c == '[') || (c == ']') || (c == '@') || /* accelerators */
9992: (c == '*') || /* accelerators */
9993: (!IS_LETTER(c) && (c != '_') &&
9994: ((qualified) && (c != ':')))) {
9995: return(NULL);
9996: }
9997:
9998: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
9999: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
10000: (c == '.') || (c == '-') ||
10001: (c == '_') || ((qualified) && (c == ':')) ||
10002: (IS_COMBINING(c)) ||
10003: (IS_EXTENDER(c)))) {
10004: COPY_BUF(l,buf,len,c);
10005: NEXTL(l);
10006: c = CUR_CHAR(l);
10007: if (len >= XML_MAX_NAMELEN) {
10008: /*
10009: * Okay someone managed to make a huge name, so he's ready to pay
10010: * for the processing speed.
10011: */
10012: xmlChar *buffer;
10013: int max = len * 2;
10014:
1.1.1.3 ! misho 10015: if (len > XML_MAX_NAME_LENGTH) {
! 10016: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 10017: }
1.1 misho 10018: buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar));
10019: if (buffer == NULL) {
10020: XP_ERRORNULL(XPATH_MEMORY_ERROR);
10021: }
10022: memcpy(buffer, buf, len);
10023: while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
10024: (c == '.') || (c == '-') ||
10025: (c == '_') || ((qualified) && (c == ':')) ||
10026: (IS_COMBINING(c)) ||
10027: (IS_EXTENDER(c))) {
10028: if (len + 10 > max) {
1.1.1.3 ! misho 10029: if (max > XML_MAX_NAME_LENGTH) {
! 10030: XP_ERRORNULL(XPATH_EXPR_ERROR);
! 10031: }
1.1 misho 10032: max *= 2;
10033: buffer = (xmlChar *) xmlRealloc(buffer,
10034: max * sizeof(xmlChar));
10035: if (buffer == NULL) {
10036: XP_ERRORNULL(XPATH_MEMORY_ERROR);
10037: }
10038: }
10039: COPY_BUF(l,buffer,len,c);
10040: NEXTL(l);
10041: c = CUR_CHAR(l);
10042: }
10043: buffer[len] = 0;
10044: return(buffer);
10045: }
10046: }
10047: if (len == 0)
10048: return(NULL);
10049: return(xmlStrndup(buf, len));
10050: }
10051:
10052: #define MAX_FRAC 20
10053:
10054: /*
10055: * These are used as divisors for the fractional part of a number.
10056: * Since the table includes 1.0 (representing '0' fractional digits),
10057: * it must be dimensioned at MAX_FRAC+1 (bug 133921)
10058: */
10059: static double my_pow10[MAX_FRAC+1] = {
10060: 1.0, 10.0, 100.0, 1000.0, 10000.0,
10061: 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
10062: 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
10063: 100000000000000.0,
10064: 1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
10065: 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
10066: };
10067:
10068: /**
10069: * xmlXPathStringEvalNumber:
10070: * @str: A string to scan
10071: *
10072: * [30a] Float ::= Number ('e' Digits?)?
10073: *
10074: * [30] Number ::= Digits ('.' Digits?)?
10075: * | '.' Digits
10076: * [31] Digits ::= [0-9]+
10077: *
10078: * Compile a Number in the string
10079: * In complement of the Number expression, this function also handles
10080: * negative values : '-' Number.
10081: *
10082: * Returns the double value.
10083: */
10084: double
10085: xmlXPathStringEvalNumber(const xmlChar *str) {
10086: const xmlChar *cur = str;
10087: double ret;
10088: int ok = 0;
10089: int isneg = 0;
10090: int exponent = 0;
10091: int is_exponent_negative = 0;
10092: #ifdef __GNUC__
10093: unsigned long tmp = 0;
10094: double temp;
10095: #endif
10096: if (cur == NULL) return(0);
10097: while (IS_BLANK_CH(*cur)) cur++;
10098: if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
10099: return(xmlXPathNAN);
10100: }
10101: if (*cur == '-') {
10102: isneg = 1;
10103: cur++;
10104: }
10105:
10106: #ifdef __GNUC__
10107: /*
10108: * tmp/temp is a workaround against a gcc compiler bug
10109: * http://veillard.com/gcc.bug
10110: */
10111: ret = 0;
10112: while ((*cur >= '0') && (*cur <= '9')) {
10113: ret = ret * 10;
10114: tmp = (*cur - '0');
10115: ok = 1;
10116: cur++;
10117: temp = (double) tmp;
10118: ret = ret + temp;
10119: }
10120: #else
10121: ret = 0;
10122: while ((*cur >= '0') && (*cur <= '9')) {
10123: ret = ret * 10 + (*cur - '0');
10124: ok = 1;
10125: cur++;
10126: }
10127: #endif
10128:
10129: if (*cur == '.') {
10130: int v, frac = 0;
10131: double fraction = 0;
10132:
10133: cur++;
10134: if (((*cur < '0') || (*cur > '9')) && (!ok)) {
10135: return(xmlXPathNAN);
10136: }
10137: while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) {
10138: v = (*cur - '0');
10139: fraction = fraction * 10 + v;
10140: frac = frac + 1;
10141: cur++;
10142: }
10143: fraction /= my_pow10[frac];
10144: ret = ret + fraction;
10145: while ((*cur >= '0') && (*cur <= '9'))
10146: cur++;
10147: }
10148: if ((*cur == 'e') || (*cur == 'E')) {
10149: cur++;
10150: if (*cur == '-') {
10151: is_exponent_negative = 1;
10152: cur++;
10153: } else if (*cur == '+') {
10154: cur++;
10155: }
10156: while ((*cur >= '0') && (*cur <= '9')) {
10157: exponent = exponent * 10 + (*cur - '0');
10158: cur++;
10159: }
10160: }
10161: while (IS_BLANK_CH(*cur)) cur++;
10162: if (*cur != 0) return(xmlXPathNAN);
10163: if (isneg) ret = -ret;
10164: if (is_exponent_negative) exponent = -exponent;
10165: ret *= pow(10.0, (double)exponent);
10166: return(ret);
10167: }
10168:
10169: /**
10170: * xmlXPathCompNumber:
10171: * @ctxt: the XPath Parser context
10172: *
10173: * [30] Number ::= Digits ('.' Digits?)?
10174: * | '.' Digits
10175: * [31] Digits ::= [0-9]+
10176: *
10177: * Compile a Number, then push it on the stack
10178: *
10179: */
10180: static void
10181: xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
10182: {
10183: double ret = 0.0;
10184: int ok = 0;
10185: int exponent = 0;
10186: int is_exponent_negative = 0;
10187: #ifdef __GNUC__
10188: unsigned long tmp = 0;
10189: double temp;
10190: #endif
10191:
10192: CHECK_ERROR;
10193: if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
10194: XP_ERROR(XPATH_NUMBER_ERROR);
10195: }
10196: #ifdef __GNUC__
10197: /*
10198: * tmp/temp is a workaround against a gcc compiler bug
10199: * http://veillard.com/gcc.bug
10200: */
10201: ret = 0;
10202: while ((CUR >= '0') && (CUR <= '9')) {
10203: ret = ret * 10;
10204: tmp = (CUR - '0');
10205: ok = 1;
10206: NEXT;
10207: temp = (double) tmp;
10208: ret = ret + temp;
10209: }
10210: #else
10211: ret = 0;
10212: while ((CUR >= '0') && (CUR <= '9')) {
10213: ret = ret * 10 + (CUR - '0');
10214: ok = 1;
10215: NEXT;
10216: }
10217: #endif
10218: if (CUR == '.') {
10219: int v, frac = 0;
10220: double fraction = 0;
10221:
10222: NEXT;
10223: if (((CUR < '0') || (CUR > '9')) && (!ok)) {
10224: XP_ERROR(XPATH_NUMBER_ERROR);
10225: }
10226: while ((CUR >= '0') && (CUR <= '9') && (frac < MAX_FRAC)) {
10227: v = (CUR - '0');
10228: fraction = fraction * 10 + v;
10229: frac = frac + 1;
10230: NEXT;
10231: }
10232: fraction /= my_pow10[frac];
10233: ret = ret + fraction;
10234: while ((CUR >= '0') && (CUR <= '9'))
10235: NEXT;
10236: }
10237: if ((CUR == 'e') || (CUR == 'E')) {
10238: NEXT;
10239: if (CUR == '-') {
10240: is_exponent_negative = 1;
10241: NEXT;
10242: } else if (CUR == '+') {
10243: NEXT;
10244: }
10245: while ((CUR >= '0') && (CUR <= '9')) {
10246: exponent = exponent * 10 + (CUR - '0');
10247: NEXT;
10248: }
10249: if (is_exponent_negative)
10250: exponent = -exponent;
10251: ret *= pow(10.0, (double) exponent);
10252: }
10253: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
10254: xmlXPathCacheNewFloat(ctxt->context, ret), NULL);
10255: }
10256:
10257: /**
10258: * xmlXPathParseLiteral:
10259: * @ctxt: the XPath Parser context
10260: *
10261: * Parse a Literal
10262: *
10263: * [29] Literal ::= '"' [^"]* '"'
10264: * | "'" [^']* "'"
10265: *
10266: * Returns the value found or NULL in case of error
10267: */
10268: static xmlChar *
10269: xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
10270: const xmlChar *q;
10271: xmlChar *ret = NULL;
10272:
10273: if (CUR == '"') {
10274: NEXT;
10275: q = CUR_PTR;
10276: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10277: NEXT;
10278: if (!IS_CHAR_CH(CUR)) {
10279: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10280: } else {
10281: ret = xmlStrndup(q, CUR_PTR - q);
10282: NEXT;
10283: }
10284: } else if (CUR == '\'') {
10285: NEXT;
10286: q = CUR_PTR;
10287: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10288: NEXT;
10289: if (!IS_CHAR_CH(CUR)) {
10290: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10291: } else {
10292: ret = xmlStrndup(q, CUR_PTR - q);
10293: NEXT;
10294: }
10295: } else {
10296: XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
10297: }
10298: return(ret);
10299: }
10300:
10301: /**
10302: * xmlXPathCompLiteral:
10303: * @ctxt: the XPath Parser context
10304: *
10305: * Parse a Literal and push it on the stack.
10306: *
10307: * [29] Literal ::= '"' [^"]* '"'
10308: * | "'" [^']* "'"
10309: *
10310: * TODO: xmlXPathCompLiteral memory allocation could be improved.
10311: */
10312: static void
10313: xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
10314: const xmlChar *q;
10315: xmlChar *ret = NULL;
10316:
10317: if (CUR == '"') {
10318: NEXT;
10319: q = CUR_PTR;
10320: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10321: NEXT;
10322: if (!IS_CHAR_CH(CUR)) {
10323: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10324: } else {
10325: ret = xmlStrndup(q, CUR_PTR - q);
10326: NEXT;
10327: }
10328: } else if (CUR == '\'') {
10329: NEXT;
10330: q = CUR_PTR;
10331: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10332: NEXT;
10333: if (!IS_CHAR_CH(CUR)) {
10334: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10335: } else {
10336: ret = xmlStrndup(q, CUR_PTR - q);
10337: NEXT;
10338: }
10339: } else {
10340: XP_ERROR(XPATH_START_LITERAL_ERROR);
10341: }
10342: if (ret == NULL) return;
10343: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
10344: xmlXPathCacheNewString(ctxt->context, ret), NULL);
10345: xmlFree(ret);
10346: }
10347:
10348: /**
10349: * xmlXPathCompVariableReference:
10350: * @ctxt: the XPath Parser context
10351: *
10352: * Parse a VariableReference, evaluate it and push it on the stack.
10353: *
10354: * The variable bindings consist of a mapping from variable names
10355: * to variable values. The value of a variable is an object, which can be
10356: * of any of the types that are possible for the value of an expression,
10357: * and may also be of additional types not specified here.
10358: *
10359: * Early evaluation is possible since:
10360: * The variable bindings [...] used to evaluate a subexpression are
10361: * always the same as those used to evaluate the containing expression.
10362: *
10363: * [36] VariableReference ::= '$' QName
10364: */
10365: static void
10366: xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
10367: xmlChar *name;
10368: xmlChar *prefix;
10369:
10370: SKIP_BLANKS;
10371: if (CUR != '$') {
10372: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10373: }
10374: NEXT;
10375: name = xmlXPathParseQName(ctxt, &prefix);
10376: if (name == NULL) {
10377: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10378: }
10379: ctxt->comp->last = -1;
10380: PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
10381: name, prefix);
10382: SKIP_BLANKS;
10383: if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
1.1.1.3 ! misho 10384: XP_ERROR(XPATH_FORBID_VARIABLE_ERROR);
1.1 misho 10385: }
10386: }
10387:
10388: /**
10389: * xmlXPathIsNodeType:
10390: * @name: a name string
10391: *
10392: * Is the name given a NodeType one.
10393: *
10394: * [38] NodeType ::= 'comment'
10395: * | 'text'
10396: * | 'processing-instruction'
10397: * | 'node'
10398: *
10399: * Returns 1 if true 0 otherwise
10400: */
10401: int
10402: xmlXPathIsNodeType(const xmlChar *name) {
10403: if (name == NULL)
10404: return(0);
10405:
10406: if (xmlStrEqual(name, BAD_CAST "node"))
10407: return(1);
10408: if (xmlStrEqual(name, BAD_CAST "text"))
10409: return(1);
10410: if (xmlStrEqual(name, BAD_CAST "comment"))
10411: return(1);
10412: if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
10413: return(1);
10414: return(0);
10415: }
10416:
10417: /**
10418: * xmlXPathCompFunctionCall:
10419: * @ctxt: the XPath Parser context
10420: *
10421: * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
10422: * [17] Argument ::= Expr
10423: *
10424: * Compile a function call, the evaluation of all arguments are
10425: * pushed on the stack
10426: */
10427: static void
10428: xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
10429: xmlChar *name;
10430: xmlChar *prefix;
10431: int nbargs = 0;
10432: int sort = 1;
10433:
10434: name = xmlXPathParseQName(ctxt, &prefix);
10435: if (name == NULL) {
10436: xmlFree(prefix);
10437: XP_ERROR(XPATH_EXPR_ERROR);
10438: }
10439: SKIP_BLANKS;
10440: #ifdef DEBUG_EXPR
10441: if (prefix == NULL)
10442: xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
10443: name);
10444: else
10445: xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
10446: prefix, name);
10447: #endif
10448:
10449: if (CUR != '(') {
10450: XP_ERROR(XPATH_EXPR_ERROR);
10451: }
10452: NEXT;
10453: SKIP_BLANKS;
10454:
10455: /*
10456: * Optimization for count(): we don't need the node-set to be sorted.
10457: */
10458: if ((prefix == NULL) && (name[0] == 'c') &&
10459: xmlStrEqual(name, BAD_CAST "count"))
10460: {
10461: sort = 0;
10462: }
10463: ctxt->comp->last = -1;
10464: if (CUR != ')') {
10465: while (CUR != 0) {
10466: int op1 = ctxt->comp->last;
10467: ctxt->comp->last = -1;
10468: xmlXPathCompileExpr(ctxt, sort);
10469: if (ctxt->error != XPATH_EXPRESSION_OK) {
10470: xmlFree(name);
10471: xmlFree(prefix);
10472: return;
10473: }
10474: PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
10475: nbargs++;
10476: if (CUR == ')') break;
10477: if (CUR != ',') {
10478: XP_ERROR(XPATH_EXPR_ERROR);
10479: }
10480: NEXT;
10481: SKIP_BLANKS;
10482: }
10483: }
10484: PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
10485: name, prefix);
10486: NEXT;
10487: SKIP_BLANKS;
10488: }
10489:
10490: /**
10491: * xmlXPathCompPrimaryExpr:
10492: * @ctxt: the XPath Parser context
10493: *
10494: * [15] PrimaryExpr ::= VariableReference
10495: * | '(' Expr ')'
10496: * | Literal
10497: * | Number
10498: * | FunctionCall
10499: *
10500: * Compile a primary expression.
10501: */
10502: static void
10503: xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
10504: SKIP_BLANKS;
10505: if (CUR == '$') xmlXPathCompVariableReference(ctxt);
10506: else if (CUR == '(') {
10507: NEXT;
10508: SKIP_BLANKS;
10509: xmlXPathCompileExpr(ctxt, 1);
10510: CHECK_ERROR;
10511: if (CUR != ')') {
10512: XP_ERROR(XPATH_EXPR_ERROR);
10513: }
10514: NEXT;
10515: SKIP_BLANKS;
10516: } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10517: xmlXPathCompNumber(ctxt);
10518: } else if ((CUR == '\'') || (CUR == '"')) {
10519: xmlXPathCompLiteral(ctxt);
10520: } else {
10521: xmlXPathCompFunctionCall(ctxt);
10522: }
10523: SKIP_BLANKS;
10524: }
10525:
10526: /**
10527: * xmlXPathCompFilterExpr:
10528: * @ctxt: the XPath Parser context
10529: *
10530: * [20] FilterExpr ::= PrimaryExpr
10531: * | FilterExpr Predicate
10532: *
10533: * Compile a filter expression.
10534: * Square brackets are used to filter expressions in the same way that
10535: * they are used in location paths. It is an error if the expression to
10536: * be filtered does not evaluate to a node-set. The context node list
10537: * used for evaluating the expression in square brackets is the node-set
10538: * to be filtered listed in document order.
10539: */
10540:
10541: static void
10542: xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
10543: xmlXPathCompPrimaryExpr(ctxt);
10544: CHECK_ERROR;
10545: SKIP_BLANKS;
10546:
10547: while (CUR == '[') {
10548: xmlXPathCompPredicate(ctxt, 1);
10549: SKIP_BLANKS;
10550: }
10551:
10552:
10553: }
10554:
10555: /**
10556: * xmlXPathScanName:
10557: * @ctxt: the XPath Parser context
10558: *
10559: * Trickery: parse an XML name but without consuming the input flow
10560: * Needed to avoid insanity in the parser state.
10561: *
10562: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
10563: * CombiningChar | Extender
10564: *
10565: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
10566: *
10567: * [6] Names ::= Name (S Name)*
10568: *
10569: * Returns the Name parsed or NULL
10570: */
10571:
10572: static xmlChar *
10573: xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
10574: int len = 0, l;
10575: int c;
10576: const xmlChar *cur;
10577: xmlChar *ret;
10578:
10579: cur = ctxt->cur;
10580:
10581: c = CUR_CHAR(l);
10582: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
10583: (!IS_LETTER(c) && (c != '_') &&
10584: (c != ':'))) {
10585: return(NULL);
10586: }
10587:
10588: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
10589: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
10590: (c == '.') || (c == '-') ||
10591: (c == '_') || (c == ':') ||
10592: (IS_COMBINING(c)) ||
10593: (IS_EXTENDER(c)))) {
10594: len += l;
10595: NEXTL(l);
10596: c = CUR_CHAR(l);
10597: }
10598: ret = xmlStrndup(cur, ctxt->cur - cur);
10599: ctxt->cur = cur;
10600: return(ret);
10601: }
10602:
10603: /**
10604: * xmlXPathCompPathExpr:
10605: * @ctxt: the XPath Parser context
10606: *
10607: * [19] PathExpr ::= LocationPath
10608: * | FilterExpr
10609: * | FilterExpr '/' RelativeLocationPath
10610: * | FilterExpr '//' RelativeLocationPath
10611: *
10612: * Compile a path expression.
10613: * The / operator and // operators combine an arbitrary expression
10614: * and a relative location path. It is an error if the expression
10615: * does not evaluate to a node-set.
10616: * The / operator does composition in the same way as when / is
10617: * used in a location path. As in location paths, // is short for
10618: * /descendant-or-self::node()/.
10619: */
10620:
10621: static void
10622: xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
10623: int lc = 1; /* Should we branch to LocationPath ? */
10624: xmlChar *name = NULL; /* we may have to preparse a name to find out */
10625:
10626: SKIP_BLANKS;
10627: if ((CUR == '$') || (CUR == '(') ||
10628: (IS_ASCII_DIGIT(CUR)) ||
10629: (CUR == '\'') || (CUR == '"') ||
10630: (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10631: lc = 0;
10632: } else if (CUR == '*') {
10633: /* relative or absolute location path */
10634: lc = 1;
10635: } else if (CUR == '/') {
10636: /* relative or absolute location path */
10637: lc = 1;
10638: } else if (CUR == '@') {
10639: /* relative abbreviated attribute location path */
10640: lc = 1;
10641: } else if (CUR == '.') {
10642: /* relative abbreviated attribute location path */
10643: lc = 1;
10644: } else {
10645: /*
10646: * Problem is finding if we have a name here whether it's:
10647: * - a nodetype
10648: * - a function call in which case it's followed by '('
10649: * - an axis in which case it's followed by ':'
10650: * - a element name
10651: * We do an a priori analysis here rather than having to
10652: * maintain parsed token content through the recursive function
10653: * calls. This looks uglier but makes the code easier to
10654: * read/write/debug.
10655: */
10656: SKIP_BLANKS;
10657: name = xmlXPathScanName(ctxt);
10658: if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
10659: #ifdef DEBUG_STEP
10660: xmlGenericError(xmlGenericErrorContext,
10661: "PathExpr: Axis\n");
10662: #endif
10663: lc = 1;
10664: xmlFree(name);
10665: } else if (name != NULL) {
10666: int len =xmlStrlen(name);
10667:
10668:
10669: while (NXT(len) != 0) {
10670: if (NXT(len) == '/') {
10671: /* element name */
10672: #ifdef DEBUG_STEP
10673: xmlGenericError(xmlGenericErrorContext,
10674: "PathExpr: AbbrRelLocation\n");
10675: #endif
10676: lc = 1;
10677: break;
10678: } else if (IS_BLANK_CH(NXT(len))) {
10679: /* ignore blanks */
10680: ;
10681: } else if (NXT(len) == ':') {
10682: #ifdef DEBUG_STEP
10683: xmlGenericError(xmlGenericErrorContext,
10684: "PathExpr: AbbrRelLocation\n");
10685: #endif
10686: lc = 1;
10687: break;
10688: } else if ((NXT(len) == '(')) {
10689: /* Note Type or Function */
10690: if (xmlXPathIsNodeType(name)) {
10691: #ifdef DEBUG_STEP
10692: xmlGenericError(xmlGenericErrorContext,
10693: "PathExpr: Type search\n");
10694: #endif
10695: lc = 1;
10696: } else {
10697: #ifdef DEBUG_STEP
10698: xmlGenericError(xmlGenericErrorContext,
10699: "PathExpr: function call\n");
10700: #endif
10701: lc = 0;
10702: }
10703: break;
10704: } else if ((NXT(len) == '[')) {
10705: /* element name */
10706: #ifdef DEBUG_STEP
10707: xmlGenericError(xmlGenericErrorContext,
10708: "PathExpr: AbbrRelLocation\n");
10709: #endif
10710: lc = 1;
10711: break;
10712: } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
10713: (NXT(len) == '=')) {
10714: lc = 1;
10715: break;
10716: } else {
10717: lc = 1;
10718: break;
10719: }
10720: len++;
10721: }
10722: if (NXT(len) == 0) {
10723: #ifdef DEBUG_STEP
10724: xmlGenericError(xmlGenericErrorContext,
10725: "PathExpr: AbbrRelLocation\n");
10726: #endif
10727: /* element name */
10728: lc = 1;
10729: }
10730: xmlFree(name);
10731: } else {
10732: /* make sure all cases are covered explicitly */
10733: XP_ERROR(XPATH_EXPR_ERROR);
10734: }
10735: }
10736:
10737: if (lc) {
10738: if (CUR == '/') {
10739: PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
10740: } else {
10741: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10742: }
10743: xmlXPathCompLocationPath(ctxt);
10744: } else {
10745: xmlXPathCompFilterExpr(ctxt);
10746: CHECK_ERROR;
10747: if ((CUR == '/') && (NXT(1) == '/')) {
10748: SKIP(2);
10749: SKIP_BLANKS;
10750:
10751: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
10752: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
10753: PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
10754:
10755: xmlXPathCompRelativeLocationPath(ctxt);
10756: } else if (CUR == '/') {
10757: xmlXPathCompRelativeLocationPath(ctxt);
10758: }
10759: }
10760: SKIP_BLANKS;
10761: }
10762:
10763: /**
10764: * xmlXPathCompUnionExpr:
10765: * @ctxt: the XPath Parser context
10766: *
10767: * [18] UnionExpr ::= PathExpr
10768: * | UnionExpr '|' PathExpr
10769: *
10770: * Compile an union expression.
10771: */
10772:
10773: static void
10774: xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
10775: xmlXPathCompPathExpr(ctxt);
10776: CHECK_ERROR;
10777: SKIP_BLANKS;
10778: while (CUR == '|') {
10779: int op1 = ctxt->comp->last;
10780: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10781:
10782: NEXT;
10783: SKIP_BLANKS;
10784: xmlXPathCompPathExpr(ctxt);
10785:
10786: PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
10787:
10788: SKIP_BLANKS;
10789: }
10790: }
10791:
10792: /**
10793: * xmlXPathCompUnaryExpr:
10794: * @ctxt: the XPath Parser context
10795: *
10796: * [27] UnaryExpr ::= UnionExpr
10797: * | '-' UnaryExpr
10798: *
10799: * Compile an unary expression.
10800: */
10801:
10802: static void
10803: xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
10804: int minus = 0;
10805: int found = 0;
10806:
10807: SKIP_BLANKS;
10808: while (CUR == '-') {
10809: minus = 1 - minus;
10810: found = 1;
10811: NEXT;
10812: SKIP_BLANKS;
10813: }
10814:
10815: xmlXPathCompUnionExpr(ctxt);
10816: CHECK_ERROR;
10817: if (found) {
10818: if (minus)
10819: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
10820: else
10821: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
10822: }
10823: }
10824:
10825: /**
10826: * xmlXPathCompMultiplicativeExpr:
10827: * @ctxt: the XPath Parser context
10828: *
10829: * [26] MultiplicativeExpr ::= UnaryExpr
10830: * | MultiplicativeExpr MultiplyOperator UnaryExpr
10831: * | MultiplicativeExpr 'div' UnaryExpr
10832: * | MultiplicativeExpr 'mod' UnaryExpr
10833: * [34] MultiplyOperator ::= '*'
10834: *
10835: * Compile an Additive expression.
10836: */
10837:
10838: static void
10839: xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
10840: xmlXPathCompUnaryExpr(ctxt);
10841: CHECK_ERROR;
10842: SKIP_BLANKS;
10843: while ((CUR == '*') ||
10844: ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
10845: ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
10846: int op = -1;
10847: int op1 = ctxt->comp->last;
10848:
10849: if (CUR == '*') {
10850: op = 0;
10851: NEXT;
10852: } else if (CUR == 'd') {
10853: op = 1;
10854: SKIP(3);
10855: } else if (CUR == 'm') {
10856: op = 2;
10857: SKIP(3);
10858: }
10859: SKIP_BLANKS;
10860: xmlXPathCompUnaryExpr(ctxt);
10861: CHECK_ERROR;
10862: PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
10863: SKIP_BLANKS;
10864: }
10865: }
10866:
10867: /**
10868: * xmlXPathCompAdditiveExpr:
10869: * @ctxt: the XPath Parser context
10870: *
10871: * [25] AdditiveExpr ::= MultiplicativeExpr
10872: * | AdditiveExpr '+' MultiplicativeExpr
10873: * | AdditiveExpr '-' MultiplicativeExpr
10874: *
10875: * Compile an Additive expression.
10876: */
10877:
10878: static void
10879: xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
10880:
10881: xmlXPathCompMultiplicativeExpr(ctxt);
10882: CHECK_ERROR;
10883: SKIP_BLANKS;
10884: while ((CUR == '+') || (CUR == '-')) {
10885: int plus;
10886: int op1 = ctxt->comp->last;
10887:
10888: if (CUR == '+') plus = 1;
10889: else plus = 0;
10890: NEXT;
10891: SKIP_BLANKS;
10892: xmlXPathCompMultiplicativeExpr(ctxt);
10893: CHECK_ERROR;
10894: PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
10895: SKIP_BLANKS;
10896: }
10897: }
10898:
10899: /**
10900: * xmlXPathCompRelationalExpr:
10901: * @ctxt: the XPath Parser context
10902: *
10903: * [24] RelationalExpr ::= AdditiveExpr
10904: * | RelationalExpr '<' AdditiveExpr
10905: * | RelationalExpr '>' AdditiveExpr
10906: * | RelationalExpr '<=' AdditiveExpr
10907: * | RelationalExpr '>=' AdditiveExpr
10908: *
10909: * A <= B > C is allowed ? Answer from James, yes with
10910: * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
10911: * which is basically what got implemented.
10912: *
10913: * Compile a Relational expression, then push the result
10914: * on the stack
10915: */
10916:
10917: static void
10918: xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
10919: xmlXPathCompAdditiveExpr(ctxt);
10920: CHECK_ERROR;
10921: SKIP_BLANKS;
10922: while ((CUR == '<') ||
10923: (CUR == '>') ||
10924: ((CUR == '<') && (NXT(1) == '=')) ||
10925: ((CUR == '>') && (NXT(1) == '='))) {
10926: int inf, strict;
10927: int op1 = ctxt->comp->last;
10928:
10929: if (CUR == '<') inf = 1;
10930: else inf = 0;
10931: if (NXT(1) == '=') strict = 0;
10932: else strict = 1;
10933: NEXT;
10934: if (!strict) NEXT;
10935: SKIP_BLANKS;
10936: xmlXPathCompAdditiveExpr(ctxt);
10937: CHECK_ERROR;
10938: PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
10939: SKIP_BLANKS;
10940: }
10941: }
10942:
10943: /**
10944: * xmlXPathCompEqualityExpr:
10945: * @ctxt: the XPath Parser context
10946: *
10947: * [23] EqualityExpr ::= RelationalExpr
10948: * | EqualityExpr '=' RelationalExpr
10949: * | EqualityExpr '!=' RelationalExpr
10950: *
10951: * A != B != C is allowed ? Answer from James, yes with
10952: * (RelationalExpr = RelationalExpr) = RelationalExpr
10953: * (RelationalExpr != RelationalExpr) != RelationalExpr
10954: * which is basically what got implemented.
10955: *
10956: * Compile an Equality expression.
10957: *
10958: */
10959: static void
10960: xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
10961: xmlXPathCompRelationalExpr(ctxt);
10962: CHECK_ERROR;
10963: SKIP_BLANKS;
10964: while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
10965: int eq;
10966: int op1 = ctxt->comp->last;
10967:
10968: if (CUR == '=') eq = 1;
10969: else eq = 0;
10970: NEXT;
10971: if (!eq) NEXT;
10972: SKIP_BLANKS;
10973: xmlXPathCompRelationalExpr(ctxt);
10974: CHECK_ERROR;
10975: PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
10976: SKIP_BLANKS;
10977: }
10978: }
10979:
10980: /**
10981: * xmlXPathCompAndExpr:
10982: * @ctxt: the XPath Parser context
10983: *
10984: * [22] AndExpr ::= EqualityExpr
10985: * | AndExpr 'and' EqualityExpr
10986: *
10987: * Compile an AND expression.
10988: *
10989: */
10990: static void
10991: xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
10992: xmlXPathCompEqualityExpr(ctxt);
10993: CHECK_ERROR;
10994: SKIP_BLANKS;
10995: while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
10996: int op1 = ctxt->comp->last;
10997: SKIP(3);
10998: SKIP_BLANKS;
10999: xmlXPathCompEqualityExpr(ctxt);
11000: CHECK_ERROR;
11001: PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
11002: SKIP_BLANKS;
11003: }
11004: }
11005:
11006: /**
11007: * xmlXPathCompileExpr:
11008: * @ctxt: the XPath Parser context
11009: *
11010: * [14] Expr ::= OrExpr
11011: * [21] OrExpr ::= AndExpr
11012: * | OrExpr 'or' AndExpr
11013: *
11014: * Parse and compile an expression
11015: */
11016: static void
11017: xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
11018: xmlXPathCompAndExpr(ctxt);
11019: CHECK_ERROR;
11020: SKIP_BLANKS;
11021: while ((CUR == 'o') && (NXT(1) == 'r')) {
11022: int op1 = ctxt->comp->last;
11023: SKIP(2);
11024: SKIP_BLANKS;
11025: xmlXPathCompAndExpr(ctxt);
11026: CHECK_ERROR;
11027: PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
11028: SKIP_BLANKS;
11029: }
11030: if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
11031: /* more ops could be optimized too */
11032: /*
11033: * This is the main place to eliminate sorting for
11034: * operations which don't require a sorted node-set.
11035: * E.g. count().
11036: */
11037: PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
11038: }
11039: }
11040:
11041: /**
11042: * xmlXPathCompPredicate:
11043: * @ctxt: the XPath Parser context
11044: * @filter: act as a filter
11045: *
11046: * [8] Predicate ::= '[' PredicateExpr ']'
11047: * [9] PredicateExpr ::= Expr
11048: *
11049: * Compile a predicate expression
11050: */
11051: static void
11052: xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
11053: int op1 = ctxt->comp->last;
11054:
11055: SKIP_BLANKS;
11056: if (CUR != '[') {
11057: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
11058: }
11059: NEXT;
11060: SKIP_BLANKS;
11061:
11062: ctxt->comp->last = -1;
11063: /*
11064: * This call to xmlXPathCompileExpr() will deactivate sorting
11065: * of the predicate result.
11066: * TODO: Sorting is still activated for filters, since I'm not
11067: * sure if needed. Normally sorting should not be needed, since
11068: * a filter can only diminish the number of items in a sequence,
11069: * but won't change its order; so if the initial sequence is sorted,
11070: * subsequent sorting is not needed.
11071: */
11072: if (! filter)
11073: xmlXPathCompileExpr(ctxt, 0);
11074: else
11075: xmlXPathCompileExpr(ctxt, 1);
11076: CHECK_ERROR;
11077:
11078: if (CUR != ']') {
11079: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
11080: }
11081:
11082: if (filter)
11083: PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
11084: else
11085: PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
11086:
11087: NEXT;
11088: SKIP_BLANKS;
11089: }
11090:
11091: /**
11092: * xmlXPathCompNodeTest:
11093: * @ctxt: the XPath Parser context
11094: * @test: pointer to a xmlXPathTestVal
11095: * @type: pointer to a xmlXPathTypeVal
11096: * @prefix: placeholder for a possible name prefix
11097: *
11098: * [7] NodeTest ::= NameTest
11099: * | NodeType '(' ')'
11100: * | 'processing-instruction' '(' Literal ')'
11101: *
11102: * [37] NameTest ::= '*'
11103: * | NCName ':' '*'
11104: * | QName
11105: * [38] NodeType ::= 'comment'
11106: * | 'text'
11107: * | 'processing-instruction'
11108: * | 'node'
11109: *
11110: * Returns the name found and updates @test, @type and @prefix appropriately
11111: */
11112: static xmlChar *
11113: xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
11114: xmlXPathTypeVal *type, const xmlChar **prefix,
11115: xmlChar *name) {
11116: int blanks;
11117:
11118: if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
11119: STRANGE;
11120: return(NULL);
11121: }
11122: *type = (xmlXPathTypeVal) 0;
11123: *test = (xmlXPathTestVal) 0;
11124: *prefix = NULL;
11125: SKIP_BLANKS;
11126:
11127: if ((name == NULL) && (CUR == '*')) {
11128: /*
11129: * All elements
11130: */
11131: NEXT;
11132: *test = NODE_TEST_ALL;
11133: return(NULL);
11134: }
11135:
11136: if (name == NULL)
11137: name = xmlXPathParseNCName(ctxt);
11138: if (name == NULL) {
11139: XP_ERRORNULL(XPATH_EXPR_ERROR);
11140: }
11141:
11142: blanks = IS_BLANK_CH(CUR);
11143: SKIP_BLANKS;
11144: if (CUR == '(') {
11145: NEXT;
11146: /*
11147: * NodeType or PI search
11148: */
11149: if (xmlStrEqual(name, BAD_CAST "comment"))
11150: *type = NODE_TYPE_COMMENT;
11151: else if (xmlStrEqual(name, BAD_CAST "node"))
11152: *type = NODE_TYPE_NODE;
11153: else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
11154: *type = NODE_TYPE_PI;
11155: else if (xmlStrEqual(name, BAD_CAST "text"))
11156: *type = NODE_TYPE_TEXT;
11157: else {
11158: if (name != NULL)
11159: xmlFree(name);
11160: XP_ERRORNULL(XPATH_EXPR_ERROR);
11161: }
11162:
11163: *test = NODE_TEST_TYPE;
11164:
11165: SKIP_BLANKS;
11166: if (*type == NODE_TYPE_PI) {
11167: /*
11168: * Specific case: search a PI by name.
11169: */
11170: if (name != NULL)
11171: xmlFree(name);
11172: name = NULL;
11173: if (CUR != ')') {
11174: name = xmlXPathParseLiteral(ctxt);
11175: CHECK_ERROR NULL;
11176: *test = NODE_TEST_PI;
11177: SKIP_BLANKS;
11178: }
11179: }
11180: if (CUR != ')') {
11181: if (name != NULL)
11182: xmlFree(name);
11183: XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
11184: }
11185: NEXT;
11186: return(name);
11187: }
11188: *test = NODE_TEST_NAME;
11189: if ((!blanks) && (CUR == ':')) {
11190: NEXT;
11191:
11192: /*
11193: * Since currently the parser context don't have a
11194: * namespace list associated:
11195: * The namespace name for this prefix can be computed
11196: * only at evaluation time. The compilation is done
11197: * outside of any context.
11198: */
11199: #if 0
11200: *prefix = xmlXPathNsLookup(ctxt->context, name);
11201: if (name != NULL)
11202: xmlFree(name);
11203: if (*prefix == NULL) {
11204: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
11205: }
11206: #else
11207: *prefix = name;
11208: #endif
11209:
11210: if (CUR == '*') {
11211: /*
11212: * All elements
11213: */
11214: NEXT;
11215: *test = NODE_TEST_ALL;
11216: return(NULL);
11217: }
11218:
11219: name = xmlXPathParseNCName(ctxt);
11220: if (name == NULL) {
11221: XP_ERRORNULL(XPATH_EXPR_ERROR);
11222: }
11223: }
11224: return(name);
11225: }
11226:
11227: /**
11228: * xmlXPathIsAxisName:
11229: * @name: a preparsed name token
11230: *
11231: * [6] AxisName ::= 'ancestor'
11232: * | 'ancestor-or-self'
11233: * | 'attribute'
11234: * | 'child'
11235: * | 'descendant'
11236: * | 'descendant-or-self'
11237: * | 'following'
11238: * | 'following-sibling'
11239: * | 'namespace'
11240: * | 'parent'
11241: * | 'preceding'
11242: * | 'preceding-sibling'
11243: * | 'self'
11244: *
11245: * Returns the axis or 0
11246: */
11247: static xmlXPathAxisVal
11248: xmlXPathIsAxisName(const xmlChar *name) {
11249: xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
11250: switch (name[0]) {
11251: case 'a':
11252: if (xmlStrEqual(name, BAD_CAST "ancestor"))
11253: ret = AXIS_ANCESTOR;
11254: if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
11255: ret = AXIS_ANCESTOR_OR_SELF;
11256: if (xmlStrEqual(name, BAD_CAST "attribute"))
11257: ret = AXIS_ATTRIBUTE;
11258: break;
11259: case 'c':
11260: if (xmlStrEqual(name, BAD_CAST "child"))
11261: ret = AXIS_CHILD;
11262: break;
11263: case 'd':
11264: if (xmlStrEqual(name, BAD_CAST "descendant"))
11265: ret = AXIS_DESCENDANT;
11266: if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
11267: ret = AXIS_DESCENDANT_OR_SELF;
11268: break;
11269: case 'f':
11270: if (xmlStrEqual(name, BAD_CAST "following"))
11271: ret = AXIS_FOLLOWING;
11272: if (xmlStrEqual(name, BAD_CAST "following-sibling"))
11273: ret = AXIS_FOLLOWING_SIBLING;
11274: break;
11275: case 'n':
11276: if (xmlStrEqual(name, BAD_CAST "namespace"))
11277: ret = AXIS_NAMESPACE;
11278: break;
11279: case 'p':
11280: if (xmlStrEqual(name, BAD_CAST "parent"))
11281: ret = AXIS_PARENT;
11282: if (xmlStrEqual(name, BAD_CAST "preceding"))
11283: ret = AXIS_PRECEDING;
11284: if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
11285: ret = AXIS_PRECEDING_SIBLING;
11286: break;
11287: case 's':
11288: if (xmlStrEqual(name, BAD_CAST "self"))
11289: ret = AXIS_SELF;
11290: break;
11291: }
11292: return(ret);
11293: }
11294:
11295: /**
11296: * xmlXPathCompStep:
11297: * @ctxt: the XPath Parser context
11298: *
11299: * [4] Step ::= AxisSpecifier NodeTest Predicate*
11300: * | AbbreviatedStep
11301: *
11302: * [12] AbbreviatedStep ::= '.' | '..'
11303: *
11304: * [5] AxisSpecifier ::= AxisName '::'
11305: * | AbbreviatedAxisSpecifier
11306: *
11307: * [13] AbbreviatedAxisSpecifier ::= '@'?
11308: *
11309: * Modified for XPtr range support as:
11310: *
11311: * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
11312: * | AbbreviatedStep
11313: * | 'range-to' '(' Expr ')' Predicate*
11314: *
11315: * Compile one step in a Location Path
11316: * A location step of . is short for self::node(). This is
11317: * particularly useful in conjunction with //. For example, the
11318: * location path .//para is short for
11319: * self::node()/descendant-or-self::node()/child::para
11320: * and so will select all para descendant elements of the context
11321: * node.
11322: * Similarly, a location step of .. is short for parent::node().
11323: * For example, ../title is short for parent::node()/child::title
11324: * and so will select the title children of the parent of the context
11325: * node.
11326: */
11327: static void
11328: xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
11329: #ifdef LIBXML_XPTR_ENABLED
11330: int rangeto = 0;
11331: int op2 = -1;
11332: #endif
11333:
11334: SKIP_BLANKS;
11335: if ((CUR == '.') && (NXT(1) == '.')) {
11336: SKIP(2);
11337: SKIP_BLANKS;
11338: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
11339: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11340: } else if (CUR == '.') {
11341: NEXT;
11342: SKIP_BLANKS;
11343: } else {
11344: xmlChar *name = NULL;
11345: const xmlChar *prefix = NULL;
11346: xmlXPathTestVal test = (xmlXPathTestVal) 0;
11347: xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
11348: xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
11349: int op1;
11350:
11351: /*
11352: * The modification needed for XPointer change to the production
11353: */
11354: #ifdef LIBXML_XPTR_ENABLED
11355: if (ctxt->xptr) {
11356: name = xmlXPathParseNCName(ctxt);
11357: if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
11358: op2 = ctxt->comp->last;
11359: xmlFree(name);
11360: SKIP_BLANKS;
11361: if (CUR != '(') {
11362: XP_ERROR(XPATH_EXPR_ERROR);
11363: }
11364: NEXT;
11365: SKIP_BLANKS;
11366:
11367: xmlXPathCompileExpr(ctxt, 1);
11368: /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
11369: CHECK_ERROR;
11370:
11371: SKIP_BLANKS;
11372: if (CUR != ')') {
11373: XP_ERROR(XPATH_EXPR_ERROR);
11374: }
11375: NEXT;
11376: rangeto = 1;
11377: goto eval_predicates;
11378: }
11379: }
11380: #endif
11381: if (CUR == '*') {
11382: axis = AXIS_CHILD;
11383: } else {
11384: if (name == NULL)
11385: name = xmlXPathParseNCName(ctxt);
11386: if (name != NULL) {
11387: axis = xmlXPathIsAxisName(name);
11388: if (axis != 0) {
11389: SKIP_BLANKS;
11390: if ((CUR == ':') && (NXT(1) == ':')) {
11391: SKIP(2);
11392: xmlFree(name);
11393: name = NULL;
11394: } else {
11395: /* an element name can conflict with an axis one :-\ */
11396: axis = AXIS_CHILD;
11397: }
11398: } else {
11399: axis = AXIS_CHILD;
11400: }
11401: } else if (CUR == '@') {
11402: NEXT;
11403: axis = AXIS_ATTRIBUTE;
11404: } else {
11405: axis = AXIS_CHILD;
11406: }
11407: }
11408:
11409: if (ctxt->error != XPATH_EXPRESSION_OK) {
11410: xmlFree(name);
11411: return;
11412: }
11413:
11414: name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
11415: if (test == 0)
11416: return;
11417:
11418: if ((prefix != NULL) && (ctxt->context != NULL) &&
11419: (ctxt->context->flags & XML_XPATH_CHECKNS)) {
11420: if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
11421: xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
11422: }
11423: }
11424: #ifdef DEBUG_STEP
11425: xmlGenericError(xmlGenericErrorContext,
11426: "Basis : computing new set\n");
11427: #endif
11428:
11429: #ifdef DEBUG_STEP
11430: xmlGenericError(xmlGenericErrorContext, "Basis : ");
11431: if (ctxt->value == NULL)
11432: xmlGenericError(xmlGenericErrorContext, "no value\n");
11433: else if (ctxt->value->nodesetval == NULL)
11434: xmlGenericError(xmlGenericErrorContext, "Empty\n");
11435: else
11436: xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
11437: #endif
11438:
11439: #ifdef LIBXML_XPTR_ENABLED
11440: eval_predicates:
11441: #endif
11442: op1 = ctxt->comp->last;
11443: ctxt->comp->last = -1;
11444:
11445: SKIP_BLANKS;
11446: while (CUR == '[') {
11447: xmlXPathCompPredicate(ctxt, 0);
11448: }
11449:
11450: #ifdef LIBXML_XPTR_ENABLED
11451: if (rangeto) {
11452: PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
11453: } else
11454: #endif
11455: PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
11456: test, type, (void *)prefix, (void *)name);
11457:
11458: }
11459: #ifdef DEBUG_STEP
11460: xmlGenericError(xmlGenericErrorContext, "Step : ");
11461: if (ctxt->value == NULL)
11462: xmlGenericError(xmlGenericErrorContext, "no value\n");
11463: else if (ctxt->value->nodesetval == NULL)
11464: xmlGenericError(xmlGenericErrorContext, "Empty\n");
11465: else
11466: xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
11467: ctxt->value->nodesetval);
11468: #endif
11469: }
11470:
11471: /**
11472: * xmlXPathCompRelativeLocationPath:
11473: * @ctxt: the XPath Parser context
11474: *
11475: * [3] RelativeLocationPath ::= Step
11476: * | RelativeLocationPath '/' Step
11477: * | AbbreviatedRelativeLocationPath
11478: * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
11479: *
11480: * Compile a relative location path.
11481: */
11482: static void
11483: xmlXPathCompRelativeLocationPath
11484: (xmlXPathParserContextPtr ctxt) {
11485: SKIP_BLANKS;
11486: if ((CUR == '/') && (NXT(1) == '/')) {
11487: SKIP(2);
11488: SKIP_BLANKS;
11489: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11490: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11491: } else if (CUR == '/') {
11492: NEXT;
11493: SKIP_BLANKS;
11494: }
11495: xmlXPathCompStep(ctxt);
11496: CHECK_ERROR;
11497: SKIP_BLANKS;
11498: while (CUR == '/') {
11499: if ((CUR == '/') && (NXT(1) == '/')) {
11500: SKIP(2);
11501: SKIP_BLANKS;
11502: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11503: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11504: xmlXPathCompStep(ctxt);
11505: } else if (CUR == '/') {
11506: NEXT;
11507: SKIP_BLANKS;
11508: xmlXPathCompStep(ctxt);
11509: }
11510: SKIP_BLANKS;
11511: }
11512: }
11513:
11514: /**
11515: * xmlXPathCompLocationPath:
11516: * @ctxt: the XPath Parser context
11517: *
11518: * [1] LocationPath ::= RelativeLocationPath
11519: * | AbsoluteLocationPath
11520: * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
11521: * | AbbreviatedAbsoluteLocationPath
11522: * [10] AbbreviatedAbsoluteLocationPath ::=
11523: * '//' RelativeLocationPath
11524: *
11525: * Compile a location path
11526: *
11527: * // is short for /descendant-or-self::node()/. For example,
11528: * //para is short for /descendant-or-self::node()/child::para and
11529: * so will select any para element in the document (even a para element
11530: * that is a document element will be selected by //para since the
11531: * document element node is a child of the root node); div//para is
11532: * short for div/descendant-or-self::node()/child::para and so will
11533: * select all para descendants of div children.
11534: */
11535: static void
11536: xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
11537: SKIP_BLANKS;
11538: if (CUR != '/') {
11539: xmlXPathCompRelativeLocationPath(ctxt);
11540: } else {
11541: while (CUR == '/') {
11542: if ((CUR == '/') && (NXT(1) == '/')) {
11543: SKIP(2);
11544: SKIP_BLANKS;
11545: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11546: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11547: xmlXPathCompRelativeLocationPath(ctxt);
11548: } else if (CUR == '/') {
11549: NEXT;
11550: SKIP_BLANKS;
11551: if ((CUR != 0 ) &&
11552: ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
11553: (CUR == '@') || (CUR == '*')))
11554: xmlXPathCompRelativeLocationPath(ctxt);
11555: }
11556: CHECK_ERROR;
11557: }
11558: }
11559: }
11560:
11561: /************************************************************************
11562: * *
11563: * XPath precompiled expression evaluation *
11564: * *
11565: ************************************************************************/
11566:
11567: static int
11568: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
11569:
11570: #ifdef DEBUG_STEP
11571: static void
11572: xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
11573: int nbNodes)
11574: {
11575: xmlGenericError(xmlGenericErrorContext, "new step : ");
11576: switch (op->value) {
11577: case AXIS_ANCESTOR:
11578: xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
11579: break;
11580: case AXIS_ANCESTOR_OR_SELF:
11581: xmlGenericError(xmlGenericErrorContext,
11582: "axis 'ancestors-or-self' ");
11583: break;
11584: case AXIS_ATTRIBUTE:
11585: xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
11586: break;
11587: case AXIS_CHILD:
11588: xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
11589: break;
11590: case AXIS_DESCENDANT:
11591: xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
11592: break;
11593: case AXIS_DESCENDANT_OR_SELF:
11594: xmlGenericError(xmlGenericErrorContext,
11595: "axis 'descendant-or-self' ");
11596: break;
11597: case AXIS_FOLLOWING:
11598: xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
11599: break;
11600: case AXIS_FOLLOWING_SIBLING:
11601: xmlGenericError(xmlGenericErrorContext,
11602: "axis 'following-siblings' ");
11603: break;
11604: case AXIS_NAMESPACE:
11605: xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
11606: break;
11607: case AXIS_PARENT:
11608: xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
11609: break;
11610: case AXIS_PRECEDING:
11611: xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
11612: break;
11613: case AXIS_PRECEDING_SIBLING:
11614: xmlGenericError(xmlGenericErrorContext,
11615: "axis 'preceding-sibling' ");
11616: break;
11617: case AXIS_SELF:
11618: xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
11619: break;
11620: }
11621: xmlGenericError(xmlGenericErrorContext,
11622: " context contains %d nodes\n", nbNodes);
11623: switch (op->value2) {
11624: case NODE_TEST_NONE:
11625: xmlGenericError(xmlGenericErrorContext,
11626: " searching for none !!!\n");
11627: break;
11628: case NODE_TEST_TYPE:
11629: xmlGenericError(xmlGenericErrorContext,
11630: " searching for type %d\n", op->value3);
11631: break;
11632: case NODE_TEST_PI:
11633: xmlGenericError(xmlGenericErrorContext,
11634: " searching for PI !!!\n");
11635: break;
11636: case NODE_TEST_ALL:
11637: xmlGenericError(xmlGenericErrorContext,
11638: " searching for *\n");
11639: break;
11640: case NODE_TEST_NS:
11641: xmlGenericError(xmlGenericErrorContext,
11642: " searching for namespace %s\n",
11643: op->value5);
11644: break;
11645: case NODE_TEST_NAME:
11646: xmlGenericError(xmlGenericErrorContext,
11647: " searching for name %s\n", op->value5);
11648: if (op->value4)
11649: xmlGenericError(xmlGenericErrorContext,
11650: " with namespace %s\n", op->value4);
11651: break;
11652: }
11653: xmlGenericError(xmlGenericErrorContext, "Testing : ");
11654: }
11655: #endif /* DEBUG_STEP */
11656:
11657: static int
11658: xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
11659: xmlXPathStepOpPtr op,
11660: xmlNodeSetPtr set,
11661: int contextSize,
11662: int hasNsNodes)
11663: {
11664: if (op->ch1 != -1) {
11665: xmlXPathCompExprPtr comp = ctxt->comp;
11666: /*
11667: * Process inner predicates first.
11668: */
11669: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11670: /*
11671: * TODO: raise an internal error.
11672: */
11673: }
11674: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
11675: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
11676: CHECK_ERROR0;
11677: if (contextSize <= 0)
11678: return(0);
11679: }
11680: if (op->ch2 != -1) {
11681: xmlXPathContextPtr xpctxt = ctxt->context;
11682: xmlNodePtr contextNode, oldContextNode;
11683: xmlDocPtr oldContextDoc;
11684: int i, res, contextPos = 0, newContextSize;
11685: xmlXPathStepOpPtr exprOp;
11686: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
11687:
11688: #ifdef LIBXML_XPTR_ENABLED
11689: /*
11690: * URGENT TODO: Check the following:
11691: * We don't expect location sets if evaluating prediates, right?
11692: * Only filters should expect location sets, right?
11693: */
11694: #endif
11695: /*
11696: * SPEC XPath 1.0:
11697: * "For each node in the node-set to be filtered, the
11698: * PredicateExpr is evaluated with that node as the
11699: * context node, with the number of nodes in the
11700: * node-set as the context size, and with the proximity
11701: * position of the node in the node-set with respect to
11702: * the axis as the context position;"
11703: * @oldset is the node-set" to be filtered.
11704: *
11705: * SPEC XPath 1.0:
11706: * "only predicates change the context position and
11707: * context size (see [2.4 Predicates])."
11708: * Example:
11709: * node-set context pos
11710: * nA 1
11711: * nB 2
11712: * nC 3
11713: * After applying predicate [position() > 1] :
11714: * node-set context pos
11715: * nB 1
11716: * nC 2
11717: */
11718: oldContextNode = xpctxt->node;
11719: oldContextDoc = xpctxt->doc;
11720: /*
11721: * Get the expression of this predicate.
11722: */
11723: exprOp = &ctxt->comp->steps[op->ch2];
11724: newContextSize = 0;
11725: for (i = 0; i < set->nodeNr; i++) {
11726: if (set->nodeTab[i] == NULL)
11727: continue;
11728:
11729: contextNode = set->nodeTab[i];
11730: xpctxt->node = contextNode;
11731: xpctxt->contextSize = contextSize;
11732: xpctxt->proximityPosition = ++contextPos;
11733:
11734: /*
11735: * Also set the xpath document in case things like
11736: * key() are evaluated in the predicate.
11737: */
11738: if ((contextNode->type != XML_NAMESPACE_DECL) &&
11739: (contextNode->doc != NULL))
11740: xpctxt->doc = contextNode->doc;
11741: /*
11742: * Evaluate the predicate expression with 1 context node
11743: * at a time; this node is packaged into a node set; this
11744: * node set is handed over to the evaluation mechanism.
11745: */
11746: if (contextObj == NULL)
11747: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
1.1.1.3 ! misho 11748: else {
! 11749: if (xmlXPathNodeSetAddUnique(contextObj->nodesetval,
! 11750: contextNode) < 0) {
! 11751: ctxt->error = XPATH_MEMORY_ERROR;
! 11752: goto evaluation_exit;
! 11753: }
! 11754: }
1.1 misho 11755:
11756: valuePush(ctxt, contextObj);
11757:
11758: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
11759:
11760: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
11761: xmlXPathNodeSetClear(set, hasNsNodes);
11762: newContextSize = 0;
11763: goto evaluation_exit;
11764: }
11765:
11766: if (res != 0) {
11767: newContextSize++;
11768: } else {
11769: /*
11770: * Remove the entry from the initial node set.
11771: */
11772: set->nodeTab[i] = NULL;
11773: if (contextNode->type == XML_NAMESPACE_DECL)
11774: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
11775: }
11776: if (ctxt->value == contextObj) {
11777: /*
11778: * Don't free the temporary XPath object holding the
11779: * context node, in order to avoid massive recreation
11780: * inside this loop.
11781: */
11782: valuePop(ctxt);
11783: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
11784: } else {
11785: /*
11786: * TODO: The object was lost in the evaluation machinery.
11787: * Can this happen? Maybe in internal-error cases.
11788: */
11789: contextObj = NULL;
11790: }
11791: }
11792:
11793: if (contextObj != NULL) {
11794: if (ctxt->value == contextObj)
11795: valuePop(ctxt);
11796: xmlXPathReleaseObject(xpctxt, contextObj);
11797: }
11798: evaluation_exit:
11799: if (exprRes != NULL)
11800: xmlXPathReleaseObject(ctxt->context, exprRes);
11801: /*
11802: * Reset/invalidate the context.
11803: */
11804: xpctxt->node = oldContextNode;
11805: xpctxt->doc = oldContextDoc;
11806: xpctxt->contextSize = -1;
11807: xpctxt->proximityPosition = -1;
11808: return(newContextSize);
11809: }
11810: return(contextSize);
11811: }
11812:
11813: static int
11814: xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt,
11815: xmlXPathStepOpPtr op,
11816: xmlNodeSetPtr set,
11817: int contextSize,
11818: int minPos,
11819: int maxPos,
11820: int hasNsNodes)
11821: {
11822: if (op->ch1 != -1) {
11823: xmlXPathCompExprPtr comp = ctxt->comp;
11824: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11825: /*
11826: * TODO: raise an internal error.
11827: */
11828: }
11829: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
11830: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
11831: CHECK_ERROR0;
11832: if (contextSize <= 0)
11833: return(0);
11834: }
11835: /*
11836: * Check if the node set contains a sufficient number of nodes for
11837: * the requested range.
11838: */
11839: if (contextSize < minPos) {
11840: xmlXPathNodeSetClear(set, hasNsNodes);
11841: return(0);
11842: }
11843: if (op->ch2 == -1) {
11844: /*
11845: * TODO: Can this ever happen?
11846: */
11847: return (contextSize);
11848: } else {
11849: xmlDocPtr oldContextDoc;
11850: int i, pos = 0, newContextSize = 0, contextPos = 0, res;
11851: xmlXPathStepOpPtr exprOp;
11852: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
11853: xmlNodePtr oldContextNode, contextNode = NULL;
11854: xmlXPathContextPtr xpctxt = ctxt->context;
1.1.1.2 misho 11855: int frame;
1.1 misho 11856:
11857: #ifdef LIBXML_XPTR_ENABLED
11858: /*
11859: * URGENT TODO: Check the following:
11860: * We don't expect location sets if evaluating prediates, right?
11861: * Only filters should expect location sets, right?
11862: */
11863: #endif /* LIBXML_XPTR_ENABLED */
11864:
11865: /*
11866: * Save old context.
11867: */
11868: oldContextNode = xpctxt->node;
11869: oldContextDoc = xpctxt->doc;
11870: /*
11871: * Get the expression of this predicate.
11872: */
11873: exprOp = &ctxt->comp->steps[op->ch2];
11874: for (i = 0; i < set->nodeNr; i++) {
1.1.1.2 misho 11875: xmlXPathObjectPtr tmp;
11876:
1.1 misho 11877: if (set->nodeTab[i] == NULL)
11878: continue;
11879:
11880: contextNode = set->nodeTab[i];
11881: xpctxt->node = contextNode;
11882: xpctxt->contextSize = contextSize;
11883: xpctxt->proximityPosition = ++contextPos;
11884:
11885: /*
11886: * Initialize the new set.
11887: * Also set the xpath document in case things like
11888: * key() evaluation are attempted on the predicate
11889: */
11890: if ((contextNode->type != XML_NAMESPACE_DECL) &&
11891: (contextNode->doc != NULL))
11892: xpctxt->doc = contextNode->doc;
11893: /*
11894: * Evaluate the predicate expression with 1 context node
11895: * at a time; this node is packaged into a node set; this
11896: * node set is handed over to the evaluation mechanism.
11897: */
11898: if (contextObj == NULL)
11899: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
1.1.1.3 ! misho 11900: else {
! 11901: if (xmlXPathNodeSetAddUnique(contextObj->nodesetval,
! 11902: contextNode) < 0) {
! 11903: ctxt->error = XPATH_MEMORY_ERROR;
! 11904: goto evaluation_exit;
! 11905: }
! 11906: }
1.1 misho 11907:
1.1.1.2 misho 11908: frame = xmlXPathSetFrame(ctxt);
1.1 misho 11909: valuePush(ctxt, contextObj);
11910: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
1.1.1.2 misho 11911: tmp = valuePop(ctxt);
11912: xmlXPathPopFrame(ctxt, frame);
1.1 misho 11913:
11914: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
1.1.1.2 misho 11915: while (tmp != contextObj) {
11916: /*
11917: * Free up the result
11918: * then pop off contextObj, which will be freed later
11919: */
11920: xmlXPathReleaseObject(xpctxt, tmp);
11921: tmp = valuePop(ctxt);
11922: }
1.1 misho 11923: goto evaluation_error;
11924: }
1.1.1.2 misho 11925: /* push the result back onto the stack */
11926: valuePush(ctxt, tmp);
1.1 misho 11927:
11928: if (res)
11929: pos++;
11930:
11931: if (res && (pos >= minPos) && (pos <= maxPos)) {
11932: /*
11933: * Fits in the requested range.
11934: */
11935: newContextSize++;
11936: if (minPos == maxPos) {
11937: /*
11938: * Only 1 node was requested.
11939: */
11940: if (contextNode->type == XML_NAMESPACE_DECL) {
11941: /*
11942: * As always: take care of those nasty
11943: * namespace nodes.
11944: */
11945: set->nodeTab[i] = NULL;
11946: }
11947: xmlXPathNodeSetClear(set, hasNsNodes);
11948: set->nodeNr = 1;
11949: set->nodeTab[0] = contextNode;
11950: goto evaluation_exit;
11951: }
11952: if (pos == maxPos) {
11953: /*
11954: * We are done.
11955: */
11956: xmlXPathNodeSetClearFromPos(set, i +1, hasNsNodes);
11957: goto evaluation_exit;
11958: }
11959: } else {
11960: /*
11961: * Remove the entry from the initial node set.
11962: */
11963: set->nodeTab[i] = NULL;
11964: if (contextNode->type == XML_NAMESPACE_DECL)
11965: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
11966: }
11967: if (exprRes != NULL) {
11968: xmlXPathReleaseObject(ctxt->context, exprRes);
11969: exprRes = NULL;
11970: }
11971: if (ctxt->value == contextObj) {
11972: /*
11973: * Don't free the temporary XPath object holding the
11974: * context node, in order to avoid massive recreation
11975: * inside this loop.
11976: */
11977: valuePop(ctxt);
11978: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
11979: } else {
11980: /*
11981: * The object was lost in the evaluation machinery.
11982: * Can this happen? Maybe in case of internal-errors.
11983: */
11984: contextObj = NULL;
11985: }
11986: }
11987: goto evaluation_exit;
11988:
11989: evaluation_error:
11990: xmlXPathNodeSetClear(set, hasNsNodes);
11991: newContextSize = 0;
11992:
11993: evaluation_exit:
11994: if (contextObj != NULL) {
11995: if (ctxt->value == contextObj)
11996: valuePop(ctxt);
11997: xmlXPathReleaseObject(xpctxt, contextObj);
11998: }
11999: if (exprRes != NULL)
12000: xmlXPathReleaseObject(ctxt->context, exprRes);
12001: /*
12002: * Reset/invalidate the context.
12003: */
12004: xpctxt->node = oldContextNode;
12005: xpctxt->doc = oldContextDoc;
12006: xpctxt->contextSize = -1;
12007: xpctxt->proximityPosition = -1;
12008: return(newContextSize);
12009: }
12010: return(contextSize);
12011: }
12012:
12013: static int
12014: xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
12015: xmlXPathStepOpPtr op,
12016: int *maxPos)
12017: {
12018:
12019: xmlXPathStepOpPtr exprOp;
12020:
12021: /*
12022: * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
12023: */
12024:
12025: /*
12026: * If not -1, then ch1 will point to:
12027: * 1) For predicates (XPATH_OP_PREDICATE):
12028: * - an inner predicate operator
12029: * 2) For filters (XPATH_OP_FILTER):
12030: * - an inner filter operater OR
12031: * - an expression selecting the node set.
12032: * E.g. "key('a', 'b')" or "(//foo | //bar)".
12033: */
12034: if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
12035: return(0);
12036:
12037: if (op->ch2 != -1) {
12038: exprOp = &ctxt->comp->steps[op->ch2];
12039: } else
12040: return(0);
12041:
12042: if ((exprOp != NULL) &&
12043: (exprOp->op == XPATH_OP_VALUE) &&
12044: (exprOp->value4 != NULL) &&
12045: (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
12046: {
12047: /*
12048: * We have a "[n]" predicate here.
12049: * TODO: Unfortunately this simplistic test here is not
12050: * able to detect a position() predicate in compound
12051: * expressions like "[@attr = 'a" and position() = 1],
12052: * and even not the usage of position() in
12053: * "[position() = 1]"; thus - obviously - a position-range,
12054: * like it "[position() < 5]", is also not detected.
12055: * Maybe we could rewrite the AST to ease the optimization.
12056: */
12057: *maxPos = (int) ((xmlXPathObjectPtr) exprOp->value4)->floatval;
12058:
12059: if (((xmlXPathObjectPtr) exprOp->value4)->floatval ==
12060: (float) *maxPos)
12061: {
12062: return(1);
12063: }
12064: }
12065: return(0);
12066: }
12067:
12068: static int
12069: xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
12070: xmlXPathStepOpPtr op,
12071: xmlNodePtr * first, xmlNodePtr * last,
12072: int toBool)
12073: {
12074:
12075: #define XP_TEST_HIT \
12076: if (hasAxisRange != 0) { \
12077: if (++pos == maxPos) { \
1.1.1.3 ! misho 12078: if (addNode(seq, cur) < 0) \
! 12079: ctxt->error = XPATH_MEMORY_ERROR; \
! 12080: goto axis_range_end; } \
1.1 misho 12081: } else { \
1.1.1.3 ! misho 12082: if (addNode(seq, cur) < 0) \
! 12083: ctxt->error = XPATH_MEMORY_ERROR; \
1.1 misho 12084: if (breakOnFirstHit) goto first_hit; }
12085:
12086: #define XP_TEST_HIT_NS \
12087: if (hasAxisRange != 0) { \
12088: if (++pos == maxPos) { \
12089: hasNsNodes = 1; \
1.1.1.3 ! misho 12090: if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
! 12091: ctxt->error = XPATH_MEMORY_ERROR; \
1.1 misho 12092: goto axis_range_end; } \
12093: } else { \
12094: hasNsNodes = 1; \
1.1.1.3 ! misho 12095: if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \
! 12096: ctxt->error = XPATH_MEMORY_ERROR; \
1.1 misho 12097: if (breakOnFirstHit) goto first_hit; }
12098:
12099: xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
12100: xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
12101: xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
12102: const xmlChar *prefix = op->value4;
12103: const xmlChar *name = op->value5;
12104: const xmlChar *URI = NULL;
12105:
12106: #ifdef DEBUG_STEP
12107: int nbMatches = 0, prevMatches = 0;
12108: #endif
12109: int total = 0, hasNsNodes = 0;
12110: /* The popped object holding the context nodes */
12111: xmlXPathObjectPtr obj;
12112: /* The set of context nodes for the node tests */
12113: xmlNodeSetPtr contextSeq;
12114: int contextIdx;
12115: xmlNodePtr contextNode;
12116: /* The final resulting node set wrt to all context nodes */
12117: xmlNodeSetPtr outSeq;
12118: /*
12119: * The temporary resulting node set wrt 1 context node.
12120: * Used to feed predicate evaluation.
12121: */
12122: xmlNodeSetPtr seq;
12123: xmlNodePtr cur;
12124: /* First predicate operator */
12125: xmlXPathStepOpPtr predOp;
12126: int maxPos; /* The requested position() (when a "[n]" predicate) */
12127: int hasPredicateRange, hasAxisRange, pos, size, newSize;
12128: int breakOnFirstHit;
12129:
12130: xmlXPathTraversalFunction next = NULL;
1.1.1.3 ! misho 12131: int (*addNode) (xmlNodeSetPtr, xmlNodePtr);
1.1 misho 12132: xmlXPathNodeSetMergeFunction mergeAndClear;
12133: xmlNodePtr oldContextNode;
12134: xmlXPathContextPtr xpctxt = ctxt->context;
12135:
12136:
12137: CHECK_TYPE0(XPATH_NODESET);
12138: obj = valuePop(ctxt);
12139: /*
12140: * Setup namespaces.
12141: */
12142: if (prefix != NULL) {
12143: URI = xmlXPathNsLookup(xpctxt, prefix);
12144: if (URI == NULL) {
12145: xmlXPathReleaseObject(xpctxt, obj);
12146: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
12147: }
12148: }
12149: /*
12150: * Setup axis.
12151: *
12152: * MAYBE FUTURE TODO: merging optimizations:
12153: * - If the nodes to be traversed wrt to the initial nodes and
12154: * the current axis cannot overlap, then we could avoid searching
12155: * for duplicates during the merge.
12156: * But the question is how/when to evaluate if they cannot overlap.
12157: * Example: if we know that for two initial nodes, the one is
12158: * not in the ancestor-or-self axis of the other, then we could safely
12159: * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
12160: * the descendant-or-self axis.
12161: */
12162: mergeAndClear = xmlXPathNodeSetMergeAndClear;
12163: switch (axis) {
12164: case AXIS_ANCESTOR:
12165: first = NULL;
12166: next = xmlXPathNextAncestor;
12167: break;
12168: case AXIS_ANCESTOR_OR_SELF:
12169: first = NULL;
12170: next = xmlXPathNextAncestorOrSelf;
12171: break;
12172: case AXIS_ATTRIBUTE:
12173: first = NULL;
12174: last = NULL;
12175: next = xmlXPathNextAttribute;
12176: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12177: break;
12178: case AXIS_CHILD:
12179: last = NULL;
12180: if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
12181: (type == NODE_TYPE_NODE))
12182: {
12183: /*
12184: * Optimization if an element node type is 'element'.
12185: */
12186: next = xmlXPathNextChildElement;
12187: } else
12188: next = xmlXPathNextChild;
12189: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12190: break;
12191: case AXIS_DESCENDANT:
12192: last = NULL;
12193: next = xmlXPathNextDescendant;
12194: break;
12195: case AXIS_DESCENDANT_OR_SELF:
12196: last = NULL;
12197: next = xmlXPathNextDescendantOrSelf;
12198: break;
12199: case AXIS_FOLLOWING:
12200: last = NULL;
12201: next = xmlXPathNextFollowing;
12202: break;
12203: case AXIS_FOLLOWING_SIBLING:
12204: last = NULL;
12205: next = xmlXPathNextFollowingSibling;
12206: break;
12207: case AXIS_NAMESPACE:
12208: first = NULL;
12209: last = NULL;
12210: next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
12211: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12212: break;
12213: case AXIS_PARENT:
12214: first = NULL;
12215: next = xmlXPathNextParent;
12216: break;
12217: case AXIS_PRECEDING:
12218: first = NULL;
12219: next = xmlXPathNextPrecedingInternal;
12220: break;
12221: case AXIS_PRECEDING_SIBLING:
12222: first = NULL;
12223: next = xmlXPathNextPrecedingSibling;
12224: break;
12225: case AXIS_SELF:
12226: first = NULL;
12227: last = NULL;
12228: next = xmlXPathNextSelf;
12229: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12230: break;
12231: }
12232:
12233: #ifdef DEBUG_STEP
12234: xmlXPathDebugDumpStepAxis(op,
12235: (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
12236: #endif
12237:
12238: if (next == NULL) {
12239: xmlXPathReleaseObject(xpctxt, obj);
12240: return(0);
12241: }
12242: contextSeq = obj->nodesetval;
12243: if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
12244: xmlXPathReleaseObject(xpctxt, obj);
12245: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
12246: return(0);
12247: }
12248: /*
12249: * Predicate optimization ---------------------------------------------
12250: * If this step has a last predicate, which contains a position(),
12251: * then we'll optimize (although not exactly "position()", but only
12252: * the short-hand form, i.e., "[n]".
12253: *
12254: * Example - expression "/foo[parent::bar][1]":
12255: *
12256: * COLLECT 'child' 'name' 'node' foo -- op (we are here)
12257: * ROOT -- op->ch1
12258: * PREDICATE -- op->ch2 (predOp)
12259: * PREDICATE -- predOp->ch1 = [parent::bar]
12260: * SORT
12261: * COLLECT 'parent' 'name' 'node' bar
12262: * NODE
12263: * ELEM Object is a number : 1 -- predOp->ch2 = [1]
12264: *
12265: */
12266: maxPos = 0;
12267: predOp = NULL;
12268: hasPredicateRange = 0;
12269: hasAxisRange = 0;
12270: if (op->ch2 != -1) {
12271: /*
12272: * There's at least one predicate. 16 == XPATH_OP_PREDICATE
12273: */
12274: predOp = &ctxt->comp->steps[op->ch2];
12275: if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
12276: if (predOp->ch1 != -1) {
12277: /*
12278: * Use the next inner predicate operator.
12279: */
12280: predOp = &ctxt->comp->steps[predOp->ch1];
12281: hasPredicateRange = 1;
12282: } else {
12283: /*
12284: * There's no other predicate than the [n] predicate.
12285: */
12286: predOp = NULL;
12287: hasAxisRange = 1;
12288: }
12289: }
12290: }
12291: breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
12292: /*
12293: * Axis traversal -----------------------------------------------------
12294: */
12295: /*
12296: * 2.3 Node Tests
12297: * - For the attribute axis, the principal node type is attribute.
12298: * - For the namespace axis, the principal node type is namespace.
12299: * - For other axes, the principal node type is element.
12300: *
12301: * A node test * is true for any node of the
12302: * principal node type. For example, child::* will
12303: * select all element children of the context node
12304: */
12305: oldContextNode = xpctxt->node;
12306: addNode = xmlXPathNodeSetAddUnique;
12307: outSeq = NULL;
12308: seq = NULL;
12309: contextNode = NULL;
12310: contextIdx = 0;
12311:
12312:
1.1.1.3 ! misho 12313: while (((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) &&
! 12314: (ctxt->error == XPATH_EXPRESSION_OK)) {
! 12315: xpctxt->node = contextSeq->nodeTab[contextIdx++];
1.1 misho 12316:
12317: if (seq == NULL) {
12318: seq = xmlXPathNodeSetCreate(NULL);
12319: if (seq == NULL) {
12320: total = 0;
12321: goto error;
12322: }
12323: }
12324: /*
12325: * Traverse the axis and test the nodes.
12326: */
12327: pos = 0;
12328: cur = NULL;
12329: hasNsNodes = 0;
12330: do {
12331: cur = next(ctxt, cur);
12332: if (cur == NULL)
12333: break;
12334:
12335: /*
12336: * QUESTION TODO: What does the "first" and "last" stuff do?
12337: */
12338: if ((first != NULL) && (*first != NULL)) {
12339: if (*first == cur)
12340: break;
12341: if (((total % 256) == 0) &&
12342: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12343: (xmlXPathCmpNodesExt(*first, cur) >= 0))
12344: #else
12345: (xmlXPathCmpNodes(*first, cur) >= 0))
12346: #endif
12347: {
12348: break;
12349: }
12350: }
12351: if ((last != NULL) && (*last != NULL)) {
12352: if (*last == cur)
12353: break;
12354: if (((total % 256) == 0) &&
12355: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12356: (xmlXPathCmpNodesExt(cur, *last) >= 0))
12357: #else
12358: (xmlXPathCmpNodes(cur, *last) >= 0))
12359: #endif
12360: {
12361: break;
12362: }
12363: }
12364:
12365: total++;
12366:
12367: #ifdef DEBUG_STEP
12368: xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
12369: #endif
12370:
12371: switch (test) {
12372: case NODE_TEST_NONE:
12373: total = 0;
12374: STRANGE
12375: goto error;
12376: case NODE_TEST_TYPE:
12377: /*
12378: * TODO: Don't we need to use
12379: * xmlXPathNodeSetAddNs() for namespace nodes here?
12380: * Surprisingly, some c14n tests fail, if we do this.
12381: */
12382: if (type == NODE_TYPE_NODE) {
12383: switch (cur->type) {
12384: case XML_DOCUMENT_NODE:
12385: case XML_HTML_DOCUMENT_NODE:
12386: #ifdef LIBXML_DOCB_ENABLED
12387: case XML_DOCB_DOCUMENT_NODE:
12388: #endif
12389: case XML_ELEMENT_NODE:
12390: case XML_ATTRIBUTE_NODE:
12391: case XML_PI_NODE:
12392: case XML_COMMENT_NODE:
12393: case XML_CDATA_SECTION_NODE:
12394: case XML_TEXT_NODE:
12395: case XML_NAMESPACE_DECL:
12396: XP_TEST_HIT
12397: break;
12398: default:
12399: break;
12400: }
12401: } else if (cur->type == type) {
1.1.1.3 ! misho 12402: if (cur->type == XML_NAMESPACE_DECL)
1.1 misho 12403: XP_TEST_HIT_NS
12404: else
12405: XP_TEST_HIT
12406: } else if ((type == NODE_TYPE_TEXT) &&
12407: (cur->type == XML_CDATA_SECTION_NODE))
12408: {
12409: XP_TEST_HIT
12410: }
12411: break;
12412: case NODE_TEST_PI:
12413: if ((cur->type == XML_PI_NODE) &&
12414: ((name == NULL) || xmlStrEqual(name, cur->name)))
12415: {
12416: XP_TEST_HIT
12417: }
12418: break;
12419: case NODE_TEST_ALL:
12420: if (axis == AXIS_ATTRIBUTE) {
12421: if (cur->type == XML_ATTRIBUTE_NODE)
12422: {
12423: XP_TEST_HIT
12424: }
12425: } else if (axis == AXIS_NAMESPACE) {
12426: if (cur->type == XML_NAMESPACE_DECL)
12427: {
12428: XP_TEST_HIT_NS
12429: }
12430: } else {
12431: if (cur->type == XML_ELEMENT_NODE) {
12432: if (prefix == NULL)
12433: {
12434: XP_TEST_HIT
12435:
12436: } else if ((cur->ns != NULL) &&
12437: (xmlStrEqual(URI, cur->ns->href)))
12438: {
12439: XP_TEST_HIT
12440: }
12441: }
12442: }
12443: break;
12444: case NODE_TEST_NS:{
12445: TODO;
12446: break;
12447: }
12448: case NODE_TEST_NAME:
12449: if (axis == AXIS_ATTRIBUTE) {
12450: if (cur->type != XML_ATTRIBUTE_NODE)
12451: break;
12452: } else if (axis == AXIS_NAMESPACE) {
12453: if (cur->type != XML_NAMESPACE_DECL)
12454: break;
12455: } else {
12456: if (cur->type != XML_ELEMENT_NODE)
12457: break;
12458: }
12459: switch (cur->type) {
12460: case XML_ELEMENT_NODE:
12461: if (xmlStrEqual(name, cur->name)) {
12462: if (prefix == NULL) {
12463: if (cur->ns == NULL)
12464: {
12465: XP_TEST_HIT
12466: }
12467: } else {
12468: if ((cur->ns != NULL) &&
12469: (xmlStrEqual(URI, cur->ns->href)))
12470: {
12471: XP_TEST_HIT
12472: }
12473: }
12474: }
12475: break;
12476: case XML_ATTRIBUTE_NODE:{
12477: xmlAttrPtr attr = (xmlAttrPtr) cur;
12478:
12479: if (xmlStrEqual(name, attr->name)) {
12480: if (prefix == NULL) {
12481: if ((attr->ns == NULL) ||
12482: (attr->ns->prefix == NULL))
12483: {
12484: XP_TEST_HIT
12485: }
12486: } else {
12487: if ((attr->ns != NULL) &&
12488: (xmlStrEqual(URI,
12489: attr->ns->href)))
12490: {
12491: XP_TEST_HIT
12492: }
12493: }
12494: }
12495: break;
12496: }
12497: case XML_NAMESPACE_DECL:
12498: if (cur->type == XML_NAMESPACE_DECL) {
12499: xmlNsPtr ns = (xmlNsPtr) cur;
12500:
12501: if ((ns->prefix != NULL) && (name != NULL)
12502: && (xmlStrEqual(ns->prefix, name)))
12503: {
12504: XP_TEST_HIT_NS
12505: }
12506: }
12507: break;
12508: default:
12509: break;
12510: }
12511: break;
12512: } /* switch(test) */
1.1.1.3 ! misho 12513: } while ((cur != NULL) && (ctxt->error == XPATH_EXPRESSION_OK));
1.1 misho 12514:
12515: goto apply_predicates;
12516:
12517: axis_range_end: /* ----------------------------------------------------- */
12518: /*
12519: * We have a "/foo[n]", and position() = n was reached.
12520: * Note that we can have as well "/foo/::parent::foo[1]", so
12521: * a duplicate-aware merge is still needed.
12522: * Merge with the result.
12523: */
12524: if (outSeq == NULL) {
12525: outSeq = seq;
12526: seq = NULL;
12527: } else
12528: outSeq = mergeAndClear(outSeq, seq, 0);
12529: /*
12530: * Break if only a true/false result was requested.
12531: */
12532: if (toBool)
12533: break;
12534: continue;
12535:
12536: first_hit: /* ---------------------------------------------------------- */
12537: /*
12538: * Break if only a true/false result was requested and
12539: * no predicates existed and a node test succeeded.
12540: */
12541: if (outSeq == NULL) {
12542: outSeq = seq;
12543: seq = NULL;
12544: } else
12545: outSeq = mergeAndClear(outSeq, seq, 0);
12546: break;
12547:
12548: #ifdef DEBUG_STEP
12549: if (seq != NULL)
12550: nbMatches += seq->nodeNr;
12551: #endif
12552:
12553: apply_predicates: /* --------------------------------------------------- */
1.1.1.3 ! misho 12554: if (ctxt->error != XPATH_EXPRESSION_OK)
! 12555: goto error;
! 12556:
1.1 misho 12557: /*
12558: * Apply predicates.
12559: */
12560: if ((predOp != NULL) && (seq->nodeNr > 0)) {
12561: /*
12562: * E.g. when we have a "/foo[some expression][n]".
1.1.1.3 ! misho 12563: */
1.1 misho 12564: /*
12565: * QUESTION TODO: The old predicate evaluation took into
12566: * account location-sets.
12567: * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
12568: * Do we expect such a set here?
12569: * All what I learned now from the evaluation semantics
12570: * does not indicate that a location-set will be processed
12571: * here, so this looks OK.
1.1.1.3 ! misho 12572: */
1.1 misho 12573: /*
12574: * Iterate over all predicates, starting with the outermost
12575: * predicate.
12576: * TODO: Problem: we cannot execute the inner predicates first
12577: * since we cannot go back *up* the operator tree!
12578: * Options we have:
12579: * 1) Use of recursive functions (like is it currently done
12580: * via xmlXPathCompOpEval())
12581: * 2) Add a predicate evaluation information stack to the
12582: * context struct
12583: * 3) Change the way the operators are linked; we need a
12584: * "parent" field on xmlXPathStepOp
12585: *
12586: * For the moment, I'll try to solve this with a recursive
12587: * function: xmlXPathCompOpEvalPredicate().
12588: */
12589: size = seq->nodeNr;
12590: if (hasPredicateRange != 0)
12591: newSize = xmlXPathCompOpEvalPositionalPredicate(ctxt,
12592: predOp, seq, size, maxPos, maxPos, hasNsNodes);
12593: else
12594: newSize = xmlXPathCompOpEvalPredicate(ctxt,
12595: predOp, seq, size, hasNsNodes);
12596:
12597: if (ctxt->error != XPATH_EXPRESSION_OK) {
12598: total = 0;
12599: goto error;
12600: }
12601: /*
12602: * Add the filtered set of nodes to the result node set.
12603: */
12604: if (newSize == 0) {
12605: /*
12606: * The predicates filtered all nodes out.
12607: */
12608: xmlXPathNodeSetClear(seq, hasNsNodes);
12609: } else if (seq->nodeNr > 0) {
12610: /*
12611: * Add to result set.
12612: */
12613: if (outSeq == NULL) {
12614: if (size != newSize) {
12615: /*
12616: * We need to merge and clear here, since
12617: * the sequence will contained NULLed entries.
12618: */
12619: outSeq = mergeAndClear(NULL, seq, 1);
12620: } else {
12621: outSeq = seq;
12622: seq = NULL;
12623: }
12624: } else
12625: outSeq = mergeAndClear(outSeq, seq,
12626: (size != newSize) ? 1: 0);
12627: /*
12628: * Break if only a true/false result was requested.
12629: */
12630: if (toBool)
12631: break;
12632: }
12633: } else if (seq->nodeNr > 0) {
12634: /*
12635: * Add to result set.
12636: */
12637: if (outSeq == NULL) {
12638: outSeq = seq;
12639: seq = NULL;
12640: } else {
12641: outSeq = mergeAndClear(outSeq, seq, 0);
12642: }
12643: }
12644: }
12645:
12646: error:
12647: if ((obj->boolval) && (obj->user != NULL)) {
12648: /*
12649: * QUESTION TODO: What does this do and why?
12650: * TODO: Do we have to do this also for the "error"
12651: * cleanup further down?
12652: */
12653: ctxt->value->boolval = 1;
12654: ctxt->value->user = obj->user;
12655: obj->user = NULL;
12656: obj->boolval = 0;
12657: }
12658: xmlXPathReleaseObject(xpctxt, obj);
12659:
12660: /*
12661: * Ensure we return at least an emtpy set.
12662: */
12663: if (outSeq == NULL) {
12664: if ((seq != NULL) && (seq->nodeNr == 0))
12665: outSeq = seq;
12666: else
12667: outSeq = xmlXPathNodeSetCreate(NULL);
12668: /* XXX what if xmlXPathNodeSetCreate returned NULL here? */
12669: }
12670: if ((seq != NULL) && (seq != outSeq)) {
12671: xmlXPathFreeNodeSet(seq);
12672: }
12673: /*
12674: * Hand over the result. Better to push the set also in
12675: * case of errors.
12676: */
12677: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
12678: /*
12679: * Reset the context node.
12680: */
12681: xpctxt->node = oldContextNode;
12682:
12683: #ifdef DEBUG_STEP
12684: xmlGenericError(xmlGenericErrorContext,
12685: "\nExamined %d nodes, found %d nodes at that step\n",
12686: total, nbMatches);
12687: #endif
12688:
12689: return(total);
12690: }
12691:
12692: static int
12693: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12694: xmlXPathStepOpPtr op, xmlNodePtr * first);
12695:
12696: /**
12697: * xmlXPathCompOpEvalFirst:
12698: * @ctxt: the XPath parser context with the compiled expression
12699: * @op: an XPath compiled operation
12700: * @first: the first elem found so far
12701: *
12702: * Evaluate the Precompiled XPath operation searching only the first
12703: * element in document order
12704: *
12705: * Returns the number of examined objects.
12706: */
12707: static int
12708: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
12709: xmlXPathStepOpPtr op, xmlNodePtr * first)
12710: {
12711: int total = 0, cur;
12712: xmlXPathCompExprPtr comp;
12713: xmlXPathObjectPtr arg1, arg2;
12714:
12715: CHECK_ERROR0;
12716: comp = ctxt->comp;
12717: switch (op->op) {
12718: case XPATH_OP_END:
12719: return (0);
12720: case XPATH_OP_UNION:
12721: total =
12722: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12723: first);
12724: CHECK_ERROR0;
12725: if ((ctxt->value != NULL)
12726: && (ctxt->value->type == XPATH_NODESET)
12727: && (ctxt->value->nodesetval != NULL)
12728: && (ctxt->value->nodesetval->nodeNr >= 1)) {
12729: /*
12730: * limit tree traversing to first node in the result
12731: */
12732: /*
12733: * OPTIMIZE TODO: This implicitely sorts
12734: * the result, even if not needed. E.g. if the argument
12735: * of the count() function, no sorting is needed.
12736: * OPTIMIZE TODO: How do we know if the node-list wasn't
12737: * aready sorted?
12738: */
12739: if (ctxt->value->nodesetval->nodeNr > 1)
12740: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12741: *first = ctxt->value->nodesetval->nodeTab[0];
12742: }
12743: cur =
12744: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
12745: first);
12746: CHECK_ERROR0;
12747: CHECK_TYPE0(XPATH_NODESET);
12748: arg2 = valuePop(ctxt);
12749:
12750: CHECK_TYPE0(XPATH_NODESET);
12751: arg1 = valuePop(ctxt);
12752:
12753: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12754: arg2->nodesetval);
12755: valuePush(ctxt, arg1);
12756: xmlXPathReleaseObject(ctxt->context, arg2);
12757: /* optimizer */
12758: if (total > cur)
12759: xmlXPathCompSwap(op);
12760: return (total + cur);
12761: case XPATH_OP_ROOT:
12762: xmlXPathRoot(ctxt);
12763: return (0);
12764: case XPATH_OP_NODE:
12765: if (op->ch1 != -1)
12766: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12767: CHECK_ERROR0;
12768: if (op->ch2 != -1)
12769: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12770: CHECK_ERROR0;
12771: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12772: ctxt->context->node));
12773: return (total);
12774: case XPATH_OP_RESET:
12775: if (op->ch1 != -1)
12776: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12777: CHECK_ERROR0;
12778: if (op->ch2 != -1)
12779: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12780: CHECK_ERROR0;
12781: ctxt->context->node = NULL;
12782: return (total);
12783: case XPATH_OP_COLLECT:{
12784: if (op->ch1 == -1)
12785: return (total);
12786:
12787: total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12788: CHECK_ERROR0;
12789:
12790: total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
12791: return (total);
12792: }
12793: case XPATH_OP_VALUE:
12794: valuePush(ctxt,
12795: xmlXPathCacheObjectCopy(ctxt->context,
12796: (xmlXPathObjectPtr) op->value4));
12797: return (0);
12798: case XPATH_OP_SORT:
12799: if (op->ch1 != -1)
12800: total +=
12801: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12802: first);
12803: CHECK_ERROR0;
12804: if ((ctxt->value != NULL)
12805: && (ctxt->value->type == XPATH_NODESET)
12806: && (ctxt->value->nodesetval != NULL)
12807: && (ctxt->value->nodesetval->nodeNr > 1))
12808: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12809: return (total);
12810: #ifdef XP_OPTIMIZED_FILTER_FIRST
12811: case XPATH_OP_FILTER:
12812: total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
12813: return (total);
12814: #endif
12815: default:
12816: return (xmlXPathCompOpEval(ctxt, op));
12817: }
12818: }
12819:
12820: /**
12821: * xmlXPathCompOpEvalLast:
12822: * @ctxt: the XPath parser context with the compiled expression
12823: * @op: an XPath compiled operation
12824: * @last: the last elem found so far
12825: *
12826: * Evaluate the Precompiled XPath operation searching only the last
12827: * element in document order
12828: *
12829: * Returns the number of nodes traversed
12830: */
12831: static int
12832: xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
12833: xmlNodePtr * last)
12834: {
12835: int total = 0, cur;
12836: xmlXPathCompExprPtr comp;
12837: xmlXPathObjectPtr arg1, arg2;
12838: xmlNodePtr bak;
12839: xmlDocPtr bakd;
12840: int pp;
12841: int cs;
12842:
12843: CHECK_ERROR0;
12844: comp = ctxt->comp;
12845: switch (op->op) {
12846: case XPATH_OP_END:
12847: return (0);
12848: case XPATH_OP_UNION:
12849: bakd = ctxt->context->doc;
12850: bak = ctxt->context->node;
12851: pp = ctxt->context->proximityPosition;
12852: cs = ctxt->context->contextSize;
12853: total =
12854: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
12855: CHECK_ERROR0;
12856: if ((ctxt->value != NULL)
12857: && (ctxt->value->type == XPATH_NODESET)
12858: && (ctxt->value->nodesetval != NULL)
12859: && (ctxt->value->nodesetval->nodeNr >= 1)) {
12860: /*
12861: * limit tree traversing to first node in the result
12862: */
12863: if (ctxt->value->nodesetval->nodeNr > 1)
12864: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12865: *last =
12866: ctxt->value->nodesetval->nodeTab[ctxt->value->
12867: nodesetval->nodeNr -
12868: 1];
12869: }
12870: ctxt->context->doc = bakd;
12871: ctxt->context->node = bak;
12872: ctxt->context->proximityPosition = pp;
12873: ctxt->context->contextSize = cs;
12874: cur =
12875: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
12876: CHECK_ERROR0;
12877: if ((ctxt->value != NULL)
12878: && (ctxt->value->type == XPATH_NODESET)
12879: && (ctxt->value->nodesetval != NULL)
12880: && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
12881: }
12882: CHECK_TYPE0(XPATH_NODESET);
12883: arg2 = valuePop(ctxt);
12884:
12885: CHECK_TYPE0(XPATH_NODESET);
12886: arg1 = valuePop(ctxt);
12887:
12888: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12889: arg2->nodesetval);
12890: valuePush(ctxt, arg1);
12891: xmlXPathReleaseObject(ctxt->context, arg2);
12892: /* optimizer */
12893: if (total > cur)
12894: xmlXPathCompSwap(op);
12895: return (total + cur);
12896: case XPATH_OP_ROOT:
12897: xmlXPathRoot(ctxt);
12898: return (0);
12899: case XPATH_OP_NODE:
12900: if (op->ch1 != -1)
12901: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12902: CHECK_ERROR0;
12903: if (op->ch2 != -1)
12904: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12905: CHECK_ERROR0;
12906: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12907: ctxt->context->node));
12908: return (total);
12909: case XPATH_OP_RESET:
12910: if (op->ch1 != -1)
12911: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12912: CHECK_ERROR0;
12913: if (op->ch2 != -1)
12914: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12915: CHECK_ERROR0;
12916: ctxt->context->node = NULL;
12917: return (total);
12918: case XPATH_OP_COLLECT:{
12919: if (op->ch1 == -1)
12920: return (0);
12921:
12922: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12923: CHECK_ERROR0;
12924:
12925: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
12926: return (total);
12927: }
12928: case XPATH_OP_VALUE:
12929: valuePush(ctxt,
12930: xmlXPathCacheObjectCopy(ctxt->context,
12931: (xmlXPathObjectPtr) op->value4));
12932: return (0);
12933: case XPATH_OP_SORT:
12934: if (op->ch1 != -1)
12935: total +=
12936: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
12937: last);
12938: CHECK_ERROR0;
12939: if ((ctxt->value != NULL)
12940: && (ctxt->value->type == XPATH_NODESET)
12941: && (ctxt->value->nodesetval != NULL)
12942: && (ctxt->value->nodesetval->nodeNr > 1))
12943: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12944: return (total);
12945: default:
12946: return (xmlXPathCompOpEval(ctxt, op));
12947: }
12948: }
12949:
12950: #ifdef XP_OPTIMIZED_FILTER_FIRST
12951: static int
12952: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12953: xmlXPathStepOpPtr op, xmlNodePtr * first)
12954: {
12955: int total = 0;
12956: xmlXPathCompExprPtr comp;
12957: xmlXPathObjectPtr res;
12958: xmlXPathObjectPtr obj;
12959: xmlNodeSetPtr oldset;
12960: xmlNodePtr oldnode;
12961: xmlDocPtr oldDoc;
12962: int i;
12963:
12964: CHECK_ERROR0;
12965: comp = ctxt->comp;
12966: /*
12967: * Optimization for ()[last()] selection i.e. the last elem
12968: */
12969: if ((op->ch1 != -1) && (op->ch2 != -1) &&
12970: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
12971: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
12972: int f = comp->steps[op->ch2].ch1;
12973:
12974: if ((f != -1) &&
12975: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
12976: (comp->steps[f].value5 == NULL) &&
12977: (comp->steps[f].value == 0) &&
12978: (comp->steps[f].value4 != NULL) &&
12979: (xmlStrEqual
12980: (comp->steps[f].value4, BAD_CAST "last"))) {
12981: xmlNodePtr last = NULL;
12982:
12983: total +=
12984: xmlXPathCompOpEvalLast(ctxt,
12985: &comp->steps[op->ch1],
12986: &last);
12987: CHECK_ERROR0;
12988: /*
12989: * The nodeset should be in document order,
12990: * Keep only the last value
12991: */
12992: if ((ctxt->value != NULL) &&
12993: (ctxt->value->type == XPATH_NODESET) &&
12994: (ctxt->value->nodesetval != NULL) &&
12995: (ctxt->value->nodesetval->nodeTab != NULL) &&
12996: (ctxt->value->nodesetval->nodeNr > 1)) {
12997: ctxt->value->nodesetval->nodeTab[0] =
12998: ctxt->value->nodesetval->nodeTab[ctxt->
12999: value->
13000: nodesetval->
13001: nodeNr -
13002: 1];
13003: ctxt->value->nodesetval->nodeNr = 1;
13004: *first = *(ctxt->value->nodesetval->nodeTab);
13005: }
13006: return (total);
13007: }
13008: }
13009:
13010: if (op->ch1 != -1)
13011: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13012: CHECK_ERROR0;
13013: if (op->ch2 == -1)
13014: return (total);
13015: if (ctxt->value == NULL)
13016: return (total);
13017:
13018: #ifdef LIBXML_XPTR_ENABLED
13019: oldnode = ctxt->context->node;
13020: /*
13021: * Hum are we filtering the result of an XPointer expression
13022: */
13023: if (ctxt->value->type == XPATH_LOCATIONSET) {
13024: xmlXPathObjectPtr tmp = NULL;
13025: xmlLocationSetPtr newlocset = NULL;
13026: xmlLocationSetPtr oldlocset;
13027:
13028: /*
13029: * Extract the old locset, and then evaluate the result of the
13030: * expression for all the element in the locset. use it to grow
13031: * up a new locset.
13032: */
13033: CHECK_TYPE0(XPATH_LOCATIONSET);
13034: obj = valuePop(ctxt);
13035: oldlocset = obj->user;
13036: ctxt->context->node = NULL;
13037:
13038: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
13039: ctxt->context->contextSize = 0;
13040: ctxt->context->proximityPosition = 0;
13041: if (op->ch2 != -1)
13042: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13043: res = valuePop(ctxt);
13044: if (res != NULL) {
13045: xmlXPathReleaseObject(ctxt->context, res);
13046: }
13047: valuePush(ctxt, obj);
13048: CHECK_ERROR0;
13049: return (total);
13050: }
13051: newlocset = xmlXPtrLocationSetCreate(NULL);
13052:
13053: for (i = 0; i < oldlocset->locNr; i++) {
13054: /*
13055: * Run the evaluation with a node list made of a
13056: * single item in the nodelocset.
13057: */
13058: ctxt->context->node = oldlocset->locTab[i]->user;
13059: ctxt->context->contextSize = oldlocset->locNr;
13060: ctxt->context->proximityPosition = i + 1;
13061: if (tmp == NULL) {
13062: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13063: ctxt->context->node);
13064: } else {
1.1.1.3 ! misho 13065: if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 13066: ctxt->context->node) < 0) {
! 13067: ctxt->error = XPATH_MEMORY_ERROR;
! 13068: }
1.1 misho 13069: }
13070: valuePush(ctxt, tmp);
13071: if (op->ch2 != -1)
13072: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13073: if (ctxt->error != XPATH_EXPRESSION_OK) {
13074: xmlXPathFreeObject(obj);
13075: return(0);
13076: }
13077: /*
13078: * The result of the evaluation need to be tested to
13079: * decided whether the filter succeeded or not
13080: */
13081: res = valuePop(ctxt);
13082: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
13083: xmlXPtrLocationSetAdd(newlocset,
13084: xmlXPathCacheObjectCopy(ctxt->context,
13085: oldlocset->locTab[i]));
13086: }
13087: /*
13088: * Cleanup
13089: */
13090: if (res != NULL) {
13091: xmlXPathReleaseObject(ctxt->context, res);
13092: }
13093: if (ctxt->value == tmp) {
13094: valuePop(ctxt);
13095: xmlXPathNodeSetClear(tmp->nodesetval, 1);
13096: /*
13097: * REVISIT TODO: Don't create a temporary nodeset
13098: * for everly iteration.
13099: */
13100: /* OLD: xmlXPathFreeObject(res); */
13101: } else
13102: tmp = NULL;
13103: ctxt->context->node = NULL;
13104: /*
13105: * Only put the first node in the result, then leave.
13106: */
13107: if (newlocset->locNr > 0) {
13108: *first = (xmlNodePtr) oldlocset->locTab[i]->user;
13109: break;
13110: }
13111: }
13112: if (tmp != NULL) {
13113: xmlXPathReleaseObject(ctxt->context, tmp);
13114: }
13115: /*
13116: * The result is used as the new evaluation locset.
13117: */
13118: xmlXPathReleaseObject(ctxt->context, obj);
13119: ctxt->context->node = NULL;
13120: ctxt->context->contextSize = -1;
13121: ctxt->context->proximityPosition = -1;
13122: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13123: ctxt->context->node = oldnode;
13124: return (total);
13125: }
13126: #endif /* LIBXML_XPTR_ENABLED */
13127:
13128: /*
13129: * Extract the old set, and then evaluate the result of the
13130: * expression for all the element in the set. use it to grow
13131: * up a new set.
13132: */
13133: CHECK_TYPE0(XPATH_NODESET);
13134: obj = valuePop(ctxt);
13135: oldset = obj->nodesetval;
13136:
13137: oldnode = ctxt->context->node;
13138: oldDoc = ctxt->context->doc;
13139: ctxt->context->node = NULL;
13140:
13141: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
13142: ctxt->context->contextSize = 0;
13143: ctxt->context->proximityPosition = 0;
13144: /* QUESTION TODO: Why was this code commented out?
13145: if (op->ch2 != -1)
13146: total +=
13147: xmlXPathCompOpEval(ctxt,
13148: &comp->steps[op->ch2]);
13149: CHECK_ERROR0;
13150: res = valuePop(ctxt);
13151: if (res != NULL)
13152: xmlXPathFreeObject(res);
13153: */
13154: valuePush(ctxt, obj);
13155: ctxt->context->node = oldnode;
13156: CHECK_ERROR0;
13157: } else {
13158: xmlNodeSetPtr newset;
13159: xmlXPathObjectPtr tmp = NULL;
13160: /*
13161: * Initialize the new set.
13162: * Also set the xpath document in case things like
13163: * key() evaluation are attempted on the predicate
13164: */
13165: newset = xmlXPathNodeSetCreate(NULL);
13166: /* XXX what if xmlXPathNodeSetCreate returned NULL? */
13167:
13168: for (i = 0; i < oldset->nodeNr; i++) {
13169: /*
13170: * Run the evaluation with a node list made of
13171: * a single item in the nodeset.
13172: */
13173: ctxt->context->node = oldset->nodeTab[i];
13174: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
13175: (oldset->nodeTab[i]->doc != NULL))
13176: ctxt->context->doc = oldset->nodeTab[i]->doc;
13177: if (tmp == NULL) {
13178: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13179: ctxt->context->node);
13180: } else {
1.1.1.3 ! misho 13181: if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 13182: ctxt->context->node) < 0) {
! 13183: ctxt->error = XPATH_MEMORY_ERROR;
! 13184: }
1.1 misho 13185: }
13186: valuePush(ctxt, tmp);
13187: ctxt->context->contextSize = oldset->nodeNr;
13188: ctxt->context->proximityPosition = i + 1;
13189: if (op->ch2 != -1)
13190: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13191: if (ctxt->error != XPATH_EXPRESSION_OK) {
13192: xmlXPathFreeNodeSet(newset);
13193: xmlXPathFreeObject(obj);
13194: return(0);
13195: }
13196: /*
13197: * The result of the evaluation needs to be tested to
13198: * decide whether the filter succeeded or not
13199: */
13200: res = valuePop(ctxt);
13201: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
1.1.1.3 ! misho 13202: if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]) < 0)
! 13203: ctxt->error = XPATH_MEMORY_ERROR;
1.1 misho 13204: }
13205: /*
13206: * Cleanup
13207: */
13208: if (res != NULL) {
13209: xmlXPathReleaseObject(ctxt->context, res);
13210: }
13211: if (ctxt->value == tmp) {
13212: valuePop(ctxt);
13213: /*
13214: * Don't free the temporary nodeset
13215: * in order to avoid massive recreation inside this
13216: * loop.
13217: */
13218: xmlXPathNodeSetClear(tmp->nodesetval, 1);
13219: } else
13220: tmp = NULL;
13221: ctxt->context->node = NULL;
13222: /*
13223: * Only put the first node in the result, then leave.
13224: */
13225: if (newset->nodeNr > 0) {
13226: *first = *(newset->nodeTab);
13227: break;
13228: }
13229: }
13230: if (tmp != NULL) {
13231: xmlXPathReleaseObject(ctxt->context, tmp);
13232: }
13233: /*
13234: * The result is used as the new evaluation set.
13235: */
13236: xmlXPathReleaseObject(ctxt->context, obj);
13237: ctxt->context->node = NULL;
13238: ctxt->context->contextSize = -1;
13239: ctxt->context->proximityPosition = -1;
13240: /* may want to move this past the '}' later */
13241: ctxt->context->doc = oldDoc;
13242: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset));
13243: }
13244: ctxt->context->node = oldnode;
13245: return(total);
13246: }
13247: #endif /* XP_OPTIMIZED_FILTER_FIRST */
13248:
13249: /**
13250: * xmlXPathCompOpEval:
13251: * @ctxt: the XPath parser context with the compiled expression
13252: * @op: an XPath compiled operation
13253: *
13254: * Evaluate the Precompiled XPath operation
13255: * Returns the number of nodes traversed
13256: */
13257: static int
13258: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
13259: {
13260: int total = 0;
13261: int equal, ret;
13262: xmlXPathCompExprPtr comp;
13263: xmlXPathObjectPtr arg1, arg2;
13264: xmlNodePtr bak;
13265: xmlDocPtr bakd;
13266: int pp;
13267: int cs;
13268:
13269: CHECK_ERROR0;
13270: comp = ctxt->comp;
13271: switch (op->op) {
13272: case XPATH_OP_END:
13273: return (0);
13274: case XPATH_OP_AND:
13275: bakd = ctxt->context->doc;
13276: bak = ctxt->context->node;
13277: pp = ctxt->context->proximityPosition;
13278: cs = ctxt->context->contextSize;
13279: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13280: CHECK_ERROR0;
13281: xmlXPathBooleanFunction(ctxt, 1);
13282: if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
13283: return (total);
13284: arg2 = valuePop(ctxt);
13285: ctxt->context->doc = bakd;
13286: ctxt->context->node = bak;
13287: ctxt->context->proximityPosition = pp;
13288: ctxt->context->contextSize = cs;
13289: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13290: if (ctxt->error) {
13291: xmlXPathFreeObject(arg2);
13292: return(0);
13293: }
13294: xmlXPathBooleanFunction(ctxt, 1);
13295: arg1 = valuePop(ctxt);
13296: arg1->boolval &= arg2->boolval;
13297: valuePush(ctxt, arg1);
13298: xmlXPathReleaseObject(ctxt->context, arg2);
13299: return (total);
13300: case XPATH_OP_OR:
13301: bakd = ctxt->context->doc;
13302: bak = ctxt->context->node;
13303: pp = ctxt->context->proximityPosition;
13304: cs = ctxt->context->contextSize;
13305: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13306: CHECK_ERROR0;
13307: xmlXPathBooleanFunction(ctxt, 1);
13308: if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
13309: return (total);
13310: arg2 = valuePop(ctxt);
13311: ctxt->context->doc = bakd;
13312: ctxt->context->node = bak;
13313: ctxt->context->proximityPosition = pp;
13314: ctxt->context->contextSize = cs;
13315: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13316: if (ctxt->error) {
13317: xmlXPathFreeObject(arg2);
13318: return(0);
13319: }
13320: xmlXPathBooleanFunction(ctxt, 1);
13321: arg1 = valuePop(ctxt);
13322: arg1->boolval |= arg2->boolval;
13323: valuePush(ctxt, arg1);
13324: xmlXPathReleaseObject(ctxt->context, arg2);
13325: return (total);
13326: case XPATH_OP_EQUAL:
13327: bakd = ctxt->context->doc;
13328: bak = ctxt->context->node;
13329: pp = ctxt->context->proximityPosition;
13330: cs = ctxt->context->contextSize;
13331: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13332: CHECK_ERROR0;
13333: ctxt->context->doc = bakd;
13334: ctxt->context->node = bak;
13335: ctxt->context->proximityPosition = pp;
13336: ctxt->context->contextSize = cs;
13337: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13338: CHECK_ERROR0;
13339: if (op->value)
13340: equal = xmlXPathEqualValues(ctxt);
13341: else
13342: equal = xmlXPathNotEqualValues(ctxt);
13343: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
13344: return (total);
13345: case XPATH_OP_CMP:
13346: bakd = ctxt->context->doc;
13347: bak = ctxt->context->node;
13348: pp = ctxt->context->proximityPosition;
13349: cs = ctxt->context->contextSize;
13350: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13351: CHECK_ERROR0;
13352: ctxt->context->doc = bakd;
13353: ctxt->context->node = bak;
13354: ctxt->context->proximityPosition = pp;
13355: ctxt->context->contextSize = cs;
13356: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13357: CHECK_ERROR0;
13358: ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
13359: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
13360: return (total);
13361: case XPATH_OP_PLUS:
13362: bakd = ctxt->context->doc;
13363: bak = ctxt->context->node;
13364: pp = ctxt->context->proximityPosition;
13365: cs = ctxt->context->contextSize;
13366: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13367: CHECK_ERROR0;
13368: if (op->ch2 != -1) {
13369: ctxt->context->doc = bakd;
13370: ctxt->context->node = bak;
13371: ctxt->context->proximityPosition = pp;
13372: ctxt->context->contextSize = cs;
13373: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13374: }
13375: CHECK_ERROR0;
13376: if (op->value == 0)
13377: xmlXPathSubValues(ctxt);
13378: else if (op->value == 1)
13379: xmlXPathAddValues(ctxt);
13380: else if (op->value == 2)
13381: xmlXPathValueFlipSign(ctxt);
13382: else if (op->value == 3) {
13383: CAST_TO_NUMBER;
13384: CHECK_TYPE0(XPATH_NUMBER);
13385: }
13386: return (total);
13387: case XPATH_OP_MULT:
13388: bakd = ctxt->context->doc;
13389: bak = ctxt->context->node;
13390: pp = ctxt->context->proximityPosition;
13391: cs = ctxt->context->contextSize;
13392: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13393: CHECK_ERROR0;
13394: ctxt->context->doc = bakd;
13395: ctxt->context->node = bak;
13396: ctxt->context->proximityPosition = pp;
13397: ctxt->context->contextSize = cs;
13398: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13399: CHECK_ERROR0;
13400: if (op->value == 0)
13401: xmlXPathMultValues(ctxt);
13402: else if (op->value == 1)
13403: xmlXPathDivValues(ctxt);
13404: else if (op->value == 2)
13405: xmlXPathModValues(ctxt);
13406: return (total);
13407: case XPATH_OP_UNION:
13408: bakd = ctxt->context->doc;
13409: bak = ctxt->context->node;
13410: pp = ctxt->context->proximityPosition;
13411: cs = ctxt->context->contextSize;
13412: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13413: CHECK_ERROR0;
13414: ctxt->context->doc = bakd;
13415: ctxt->context->node = bak;
13416: ctxt->context->proximityPosition = pp;
13417: ctxt->context->contextSize = cs;
13418: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13419: CHECK_ERROR0;
13420: CHECK_TYPE0(XPATH_NODESET);
13421: arg2 = valuePop(ctxt);
13422:
13423: CHECK_TYPE0(XPATH_NODESET);
13424: arg1 = valuePop(ctxt);
13425:
13426: if ((arg1->nodesetval == NULL) ||
13427: ((arg2->nodesetval != NULL) &&
13428: (arg2->nodesetval->nodeNr != 0)))
13429: {
13430: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
13431: arg2->nodesetval);
13432: }
13433:
13434: valuePush(ctxt, arg1);
13435: xmlXPathReleaseObject(ctxt->context, arg2);
13436: return (total);
13437: case XPATH_OP_ROOT:
13438: xmlXPathRoot(ctxt);
13439: return (total);
13440: case XPATH_OP_NODE:
13441: if (op->ch1 != -1)
13442: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13443: CHECK_ERROR0;
13444: if (op->ch2 != -1)
13445: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13446: CHECK_ERROR0;
13447: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
13448: ctxt->context->node));
13449: return (total);
13450: case XPATH_OP_RESET:
13451: if (op->ch1 != -1)
13452: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13453: CHECK_ERROR0;
13454: if (op->ch2 != -1)
13455: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13456: CHECK_ERROR0;
13457: ctxt->context->node = NULL;
13458: return (total);
13459: case XPATH_OP_COLLECT:{
13460: if (op->ch1 == -1)
13461: return (total);
13462:
13463: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13464: CHECK_ERROR0;
13465:
13466: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
13467: return (total);
13468: }
13469: case XPATH_OP_VALUE:
13470: valuePush(ctxt,
13471: xmlXPathCacheObjectCopy(ctxt->context,
13472: (xmlXPathObjectPtr) op->value4));
13473: return (total);
13474: case XPATH_OP_VARIABLE:{
13475: xmlXPathObjectPtr val;
13476:
13477: if (op->ch1 != -1)
13478: total +=
13479: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13480: if (op->value5 == NULL) {
13481: val = xmlXPathVariableLookup(ctxt->context, op->value4);
13482: if (val == NULL) {
13483: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
13484: return(0);
13485: }
13486: valuePush(ctxt, val);
13487: } else {
13488: const xmlChar *URI;
13489:
13490: URI = xmlXPathNsLookup(ctxt->context, op->value5);
13491: if (URI == NULL) {
13492: xmlGenericError(xmlGenericErrorContext,
13493: "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
13494: (char *) op->value4, (char *)op->value5);
1.1.1.2 misho 13495: ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
1.1 misho 13496: return (total);
13497: }
13498: val = xmlXPathVariableLookupNS(ctxt->context,
13499: op->value4, URI);
13500: if (val == NULL) {
13501: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
13502: return(0);
13503: }
13504: valuePush(ctxt, val);
13505: }
13506: return (total);
13507: }
13508: case XPATH_OP_FUNCTION:{
13509: xmlXPathFunction func;
13510: const xmlChar *oldFunc, *oldFuncURI;
13511: int i;
1.1.1.2 misho 13512: int frame;
1.1 misho 13513:
1.1.1.2 misho 13514: frame = xmlXPathSetFrame(ctxt);
1.1 misho 13515: if (op->ch1 != -1)
13516: total +=
13517: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13518: if (ctxt->valueNr < op->value) {
13519: xmlGenericError(xmlGenericErrorContext,
13520: "xmlXPathCompOpEval: parameter error\n");
13521: ctxt->error = XPATH_INVALID_OPERAND;
1.1.1.2 misho 13522: xmlXPathPopFrame(ctxt, frame);
1.1 misho 13523: return (total);
13524: }
1.1.1.2 misho 13525: for (i = 0; i < op->value; i++) {
1.1 misho 13526: if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
13527: xmlGenericError(xmlGenericErrorContext,
13528: "xmlXPathCompOpEval: parameter error\n");
13529: ctxt->error = XPATH_INVALID_OPERAND;
1.1.1.2 misho 13530: xmlXPathPopFrame(ctxt, frame);
1.1 misho 13531: return (total);
13532: }
1.1.1.2 misho 13533: }
1.1 misho 13534: if (op->cache != NULL)
13535: XML_CAST_FPTR(func) = op->cache;
13536: else {
13537: const xmlChar *URI = NULL;
13538:
13539: if (op->value5 == NULL)
13540: func =
13541: xmlXPathFunctionLookup(ctxt->context,
13542: op->value4);
13543: else {
13544: URI = xmlXPathNsLookup(ctxt->context, op->value5);
13545: if (URI == NULL) {
13546: xmlGenericError(xmlGenericErrorContext,
13547: "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
13548: (char *)op->value4, (char *)op->value5);
1.1.1.2 misho 13549: xmlXPathPopFrame(ctxt, frame);
13550: ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
1.1 misho 13551: return (total);
13552: }
13553: func = xmlXPathFunctionLookupNS(ctxt->context,
13554: op->value4, URI);
13555: }
13556: if (func == NULL) {
13557: xmlGenericError(xmlGenericErrorContext,
13558: "xmlXPathCompOpEval: function %s not found\n",
13559: (char *)op->value4);
13560: XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
13561: }
13562: op->cache = XML_CAST_FPTR(func);
13563: op->cacheURI = (void *) URI;
13564: }
13565: oldFunc = ctxt->context->function;
13566: oldFuncURI = ctxt->context->functionURI;
13567: ctxt->context->function = op->value4;
13568: ctxt->context->functionURI = op->cacheURI;
13569: func(ctxt, op->value);
13570: ctxt->context->function = oldFunc;
13571: ctxt->context->functionURI = oldFuncURI;
1.1.1.2 misho 13572: xmlXPathPopFrame(ctxt, frame);
1.1 misho 13573: return (total);
13574: }
13575: case XPATH_OP_ARG:
13576: bakd = ctxt->context->doc;
13577: bak = ctxt->context->node;
13578: pp = ctxt->context->proximityPosition;
13579: cs = ctxt->context->contextSize;
13580: if (op->ch1 != -1)
13581: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13582: ctxt->context->contextSize = cs;
13583: ctxt->context->proximityPosition = pp;
13584: ctxt->context->node = bak;
13585: ctxt->context->doc = bakd;
13586: CHECK_ERROR0;
13587: if (op->ch2 != -1) {
13588: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13589: ctxt->context->doc = bakd;
13590: ctxt->context->node = bak;
13591: CHECK_ERROR0;
13592: }
13593: return (total);
13594: case XPATH_OP_PREDICATE:
13595: case XPATH_OP_FILTER:{
13596: xmlXPathObjectPtr res;
13597: xmlXPathObjectPtr obj, tmp;
13598: xmlNodeSetPtr newset = NULL;
13599: xmlNodeSetPtr oldset;
13600: xmlNodePtr oldnode;
13601: xmlDocPtr oldDoc;
13602: int i;
13603:
13604: /*
13605: * Optimization for ()[1] selection i.e. the first elem
13606: */
13607: if ((op->ch1 != -1) && (op->ch2 != -1) &&
13608: #ifdef XP_OPTIMIZED_FILTER_FIRST
13609: /*
13610: * FILTER TODO: Can we assume that the inner processing
13611: * will result in an ordered list if we have an
13612: * XPATH_OP_FILTER?
13613: * What about an additional field or flag on
13614: * xmlXPathObject like @sorted ? This way we wouln'd need
13615: * to assume anything, so it would be more robust and
13616: * easier to optimize.
13617: */
13618: ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
13619: (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
13620: #else
13621: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13622: #endif
13623: (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
13624: xmlXPathObjectPtr val;
13625:
13626: val = comp->steps[op->ch2].value4;
13627: if ((val != NULL) && (val->type == XPATH_NUMBER) &&
13628: (val->floatval == 1.0)) {
13629: xmlNodePtr first = NULL;
13630:
13631: total +=
13632: xmlXPathCompOpEvalFirst(ctxt,
13633: &comp->steps[op->ch1],
13634: &first);
13635: CHECK_ERROR0;
13636: /*
13637: * The nodeset should be in document order,
13638: * Keep only the first value
13639: */
13640: if ((ctxt->value != NULL) &&
13641: (ctxt->value->type == XPATH_NODESET) &&
13642: (ctxt->value->nodesetval != NULL) &&
13643: (ctxt->value->nodesetval->nodeNr > 1))
13644: ctxt->value->nodesetval->nodeNr = 1;
13645: return (total);
13646: }
13647: }
13648: /*
13649: * Optimization for ()[last()] selection i.e. the last elem
13650: */
13651: if ((op->ch1 != -1) && (op->ch2 != -1) &&
13652: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13653: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
13654: int f = comp->steps[op->ch2].ch1;
13655:
13656: if ((f != -1) &&
13657: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
13658: (comp->steps[f].value5 == NULL) &&
13659: (comp->steps[f].value == 0) &&
13660: (comp->steps[f].value4 != NULL) &&
13661: (xmlStrEqual
13662: (comp->steps[f].value4, BAD_CAST "last"))) {
13663: xmlNodePtr last = NULL;
13664:
13665: total +=
13666: xmlXPathCompOpEvalLast(ctxt,
13667: &comp->steps[op->ch1],
13668: &last);
13669: CHECK_ERROR0;
13670: /*
13671: * The nodeset should be in document order,
13672: * Keep only the last value
13673: */
13674: if ((ctxt->value != NULL) &&
13675: (ctxt->value->type == XPATH_NODESET) &&
13676: (ctxt->value->nodesetval != NULL) &&
13677: (ctxt->value->nodesetval->nodeTab != NULL) &&
13678: (ctxt->value->nodesetval->nodeNr > 1)) {
13679: ctxt->value->nodesetval->nodeTab[0] =
13680: ctxt->value->nodesetval->nodeTab[ctxt->
13681: value->
13682: nodesetval->
13683: nodeNr -
13684: 1];
13685: ctxt->value->nodesetval->nodeNr = 1;
13686: }
13687: return (total);
13688: }
13689: }
13690: /*
13691: * Process inner predicates first.
13692: * Example "index[parent::book][1]":
13693: * ...
13694: * PREDICATE <-- we are here "[1]"
13695: * PREDICATE <-- process "[parent::book]" first
13696: * SORT
13697: * COLLECT 'parent' 'name' 'node' book
13698: * NODE
13699: * ELEM Object is a number : 1
13700: */
13701: if (op->ch1 != -1)
13702: total +=
13703: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13704: CHECK_ERROR0;
13705: if (op->ch2 == -1)
13706: return (total);
13707: if (ctxt->value == NULL)
13708: return (total);
13709:
13710: oldnode = ctxt->context->node;
13711:
13712: #ifdef LIBXML_XPTR_ENABLED
13713: /*
13714: * Hum are we filtering the result of an XPointer expression
13715: */
13716: if (ctxt->value->type == XPATH_LOCATIONSET) {
13717: xmlLocationSetPtr newlocset = NULL;
13718: xmlLocationSetPtr oldlocset;
13719:
13720: /*
13721: * Extract the old locset, and then evaluate the result of the
13722: * expression for all the element in the locset. use it to grow
13723: * up a new locset.
13724: */
13725: CHECK_TYPE0(XPATH_LOCATIONSET);
13726: obj = valuePop(ctxt);
13727: oldlocset = obj->user;
13728: ctxt->context->node = NULL;
13729:
13730: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
13731: ctxt->context->contextSize = 0;
13732: ctxt->context->proximityPosition = 0;
13733: if (op->ch2 != -1)
13734: total +=
13735: xmlXPathCompOpEval(ctxt,
13736: &comp->steps[op->ch2]);
13737: res = valuePop(ctxt);
13738: if (res != NULL) {
13739: xmlXPathReleaseObject(ctxt->context, res);
13740: }
13741: valuePush(ctxt, obj);
13742: CHECK_ERROR0;
13743: return (total);
13744: }
13745: newlocset = xmlXPtrLocationSetCreate(NULL);
13746:
13747: for (i = 0; i < oldlocset->locNr; i++) {
13748: /*
13749: * Run the evaluation with a node list made of a
13750: * single item in the nodelocset.
13751: */
13752: ctxt->context->node = oldlocset->locTab[i]->user;
13753: ctxt->context->contextSize = oldlocset->locNr;
13754: ctxt->context->proximityPosition = i + 1;
13755: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13756: ctxt->context->node);
13757: valuePush(ctxt, tmp);
13758:
13759: if (op->ch2 != -1)
13760: total +=
13761: xmlXPathCompOpEval(ctxt,
13762: &comp->steps[op->ch2]);
13763: if (ctxt->error != XPATH_EXPRESSION_OK) {
13764: xmlXPathFreeObject(obj);
13765: return(0);
13766: }
13767:
13768: /*
13769: * The result of the evaluation need to be tested to
13770: * decided whether the filter succeeded or not
13771: */
13772: res = valuePop(ctxt);
13773: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
13774: xmlXPtrLocationSetAdd(newlocset,
13775: xmlXPathObjectCopy
13776: (oldlocset->locTab[i]));
13777: }
13778:
13779: /*
13780: * Cleanup
13781: */
13782: if (res != NULL) {
13783: xmlXPathReleaseObject(ctxt->context, res);
13784: }
13785: if (ctxt->value == tmp) {
13786: res = valuePop(ctxt);
13787: xmlXPathReleaseObject(ctxt->context, res);
13788: }
13789:
13790: ctxt->context->node = NULL;
13791: }
13792:
13793: /*
13794: * The result is used as the new evaluation locset.
13795: */
13796: xmlXPathReleaseObject(ctxt->context, obj);
13797: ctxt->context->node = NULL;
13798: ctxt->context->contextSize = -1;
13799: ctxt->context->proximityPosition = -1;
13800: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13801: ctxt->context->node = oldnode;
13802: return (total);
13803: }
13804: #endif /* LIBXML_XPTR_ENABLED */
13805:
13806: /*
13807: * Extract the old set, and then evaluate the result of the
13808: * expression for all the element in the set. use it to grow
13809: * up a new set.
13810: */
13811: CHECK_TYPE0(XPATH_NODESET);
13812: obj = valuePop(ctxt);
13813: oldset = obj->nodesetval;
13814:
13815: oldnode = ctxt->context->node;
13816: oldDoc = ctxt->context->doc;
13817: ctxt->context->node = NULL;
13818:
13819: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
13820: ctxt->context->contextSize = 0;
13821: ctxt->context->proximityPosition = 0;
13822: /*
13823: if (op->ch2 != -1)
13824: total +=
13825: xmlXPathCompOpEval(ctxt,
13826: &comp->steps[op->ch2]);
13827: CHECK_ERROR0;
13828: res = valuePop(ctxt);
13829: if (res != NULL)
13830: xmlXPathFreeObject(res);
13831: */
13832: valuePush(ctxt, obj);
13833: ctxt->context->node = oldnode;
13834: CHECK_ERROR0;
13835: } else {
13836: tmp = NULL;
13837: /*
13838: * Initialize the new set.
13839: * Also set the xpath document in case things like
13840: * key() evaluation are attempted on the predicate
13841: */
13842: newset = xmlXPathNodeSetCreate(NULL);
13843: /*
13844: * SPEC XPath 1.0:
13845: * "For each node in the node-set to be filtered, the
13846: * PredicateExpr is evaluated with that node as the
13847: * context node, with the number of nodes in the
13848: * node-set as the context size, and with the proximity
13849: * position of the node in the node-set with respect to
13850: * the axis as the context position;"
13851: * @oldset is the node-set" to be filtered.
13852: *
13853: * SPEC XPath 1.0:
13854: * "only predicates change the context position and
13855: * context size (see [2.4 Predicates])."
13856: * Example:
13857: * node-set context pos
13858: * nA 1
13859: * nB 2
13860: * nC 3
13861: * After applying predicate [position() > 1] :
13862: * node-set context pos
13863: * nB 1
13864: * nC 2
13865: *
13866: * removed the first node in the node-set, then
13867: * the context position of the
13868: */
13869: for (i = 0; i < oldset->nodeNr; i++) {
13870: /*
13871: * Run the evaluation with a node list made of
13872: * a single item in the nodeset.
13873: */
13874: ctxt->context->node = oldset->nodeTab[i];
13875: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
13876: (oldset->nodeTab[i]->doc != NULL))
13877: ctxt->context->doc = oldset->nodeTab[i]->doc;
13878: if (tmp == NULL) {
13879: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13880: ctxt->context->node);
13881: } else {
1.1.1.3 ! misho 13882: if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
! 13883: ctxt->context->node) < 0) {
! 13884: ctxt->error = XPATH_MEMORY_ERROR;
! 13885: }
1.1 misho 13886: }
13887: valuePush(ctxt, tmp);
13888: ctxt->context->contextSize = oldset->nodeNr;
13889: ctxt->context->proximityPosition = i + 1;
13890: /*
13891: * Evaluate the predicate against the context node.
13892: * Can/should we optimize position() predicates
13893: * here (e.g. "[1]")?
13894: */
13895: if (op->ch2 != -1)
13896: total +=
13897: xmlXPathCompOpEval(ctxt,
13898: &comp->steps[op->ch2]);
13899: if (ctxt->error != XPATH_EXPRESSION_OK) {
13900: xmlXPathFreeNodeSet(newset);
13901: xmlXPathFreeObject(obj);
13902: return(0);
13903: }
13904:
13905: /*
13906: * The result of the evaluation needs to be tested to
13907: * decide whether the filter succeeded or not
13908: */
13909: /*
13910: * OPTIMIZE TODO: Can we use
13911: * xmlXPathNodeSetAdd*Unique()* instead?
13912: */
13913: res = valuePop(ctxt);
13914: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
1.1.1.3 ! misho 13915: if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i])
! 13916: < 0)
! 13917: ctxt->error = XPATH_MEMORY_ERROR;
1.1 misho 13918: }
13919:
13920: /*
13921: * Cleanup
13922: */
13923: if (res != NULL) {
13924: xmlXPathReleaseObject(ctxt->context, res);
13925: }
13926: if (ctxt->value == tmp) {
13927: valuePop(ctxt);
13928: xmlXPathNodeSetClear(tmp->nodesetval, 1);
13929: /*
13930: * Don't free the temporary nodeset
13931: * in order to avoid massive recreation inside this
13932: * loop.
13933: */
13934: } else
13935: tmp = NULL;
13936: ctxt->context->node = NULL;
13937: }
13938: if (tmp != NULL)
13939: xmlXPathReleaseObject(ctxt->context, tmp);
13940: /*
13941: * The result is used as the new evaluation set.
13942: */
13943: xmlXPathReleaseObject(ctxt->context, obj);
13944: ctxt->context->node = NULL;
13945: ctxt->context->contextSize = -1;
13946: ctxt->context->proximityPosition = -1;
13947: /* may want to move this past the '}' later */
13948: ctxt->context->doc = oldDoc;
13949: valuePush(ctxt,
13950: xmlXPathCacheWrapNodeSet(ctxt->context, newset));
13951: }
13952: ctxt->context->node = oldnode;
13953: return (total);
13954: }
13955: case XPATH_OP_SORT:
13956: if (op->ch1 != -1)
13957: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13958: CHECK_ERROR0;
13959: if ((ctxt->value != NULL) &&
13960: (ctxt->value->type == XPATH_NODESET) &&
13961: (ctxt->value->nodesetval != NULL) &&
13962: (ctxt->value->nodesetval->nodeNr > 1))
13963: {
13964: xmlXPathNodeSetSort(ctxt->value->nodesetval);
13965: }
13966: return (total);
13967: #ifdef LIBXML_XPTR_ENABLED
13968: case XPATH_OP_RANGETO:{
13969: xmlXPathObjectPtr range;
13970: xmlXPathObjectPtr res, obj;
13971: xmlXPathObjectPtr tmp;
13972: xmlLocationSetPtr newlocset = NULL;
13973: xmlLocationSetPtr oldlocset;
13974: xmlNodeSetPtr oldset;
13975: int i, j;
13976:
13977: if (op->ch1 != -1)
13978: total +=
13979: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13980: if (op->ch2 == -1)
13981: return (total);
13982:
13983: if (ctxt->value->type == XPATH_LOCATIONSET) {
13984: /*
13985: * Extract the old locset, and then evaluate the result of the
13986: * expression for all the element in the locset. use it to grow
13987: * up a new locset.
13988: */
13989: CHECK_TYPE0(XPATH_LOCATIONSET);
13990: obj = valuePop(ctxt);
13991: oldlocset = obj->user;
13992:
13993: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
13994: ctxt->context->node = NULL;
13995: ctxt->context->contextSize = 0;
13996: ctxt->context->proximityPosition = 0;
13997: total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
13998: res = valuePop(ctxt);
13999: if (res != NULL) {
14000: xmlXPathReleaseObject(ctxt->context, res);
14001: }
14002: valuePush(ctxt, obj);
14003: CHECK_ERROR0;
14004: return (total);
14005: }
14006: newlocset = xmlXPtrLocationSetCreate(NULL);
14007:
14008: for (i = 0; i < oldlocset->locNr; i++) {
14009: /*
14010: * Run the evaluation with a node list made of a
14011: * single item in the nodelocset.
14012: */
14013: ctxt->context->node = oldlocset->locTab[i]->user;
14014: ctxt->context->contextSize = oldlocset->locNr;
14015: ctxt->context->proximityPosition = i + 1;
14016: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
14017: ctxt->context->node);
14018: valuePush(ctxt, tmp);
14019:
14020: if (op->ch2 != -1)
14021: total +=
14022: xmlXPathCompOpEval(ctxt,
14023: &comp->steps[op->ch2]);
14024: if (ctxt->error != XPATH_EXPRESSION_OK) {
14025: xmlXPathFreeObject(obj);
14026: return(0);
14027: }
14028:
14029: res = valuePop(ctxt);
14030: if (res->type == XPATH_LOCATIONSET) {
14031: xmlLocationSetPtr rloc =
14032: (xmlLocationSetPtr)res->user;
14033: for (j=0; j<rloc->locNr; j++) {
14034: range = xmlXPtrNewRange(
14035: oldlocset->locTab[i]->user,
14036: oldlocset->locTab[i]->index,
14037: rloc->locTab[j]->user2,
14038: rloc->locTab[j]->index2);
14039: if (range != NULL) {
14040: xmlXPtrLocationSetAdd(newlocset, range);
14041: }
14042: }
14043: } else {
14044: range = xmlXPtrNewRangeNodeObject(
14045: (xmlNodePtr)oldlocset->locTab[i]->user, res);
14046: if (range != NULL) {
14047: xmlXPtrLocationSetAdd(newlocset,range);
14048: }
14049: }
14050:
14051: /*
14052: * Cleanup
14053: */
14054: if (res != NULL) {
14055: xmlXPathReleaseObject(ctxt->context, res);
14056: }
14057: if (ctxt->value == tmp) {
14058: res = valuePop(ctxt);
14059: xmlXPathReleaseObject(ctxt->context, res);
14060: }
14061:
14062: ctxt->context->node = NULL;
14063: }
14064: } else { /* Not a location set */
14065: CHECK_TYPE0(XPATH_NODESET);
14066: obj = valuePop(ctxt);
14067: oldset = obj->nodesetval;
14068: ctxt->context->node = NULL;
14069:
14070: newlocset = xmlXPtrLocationSetCreate(NULL);
14071:
14072: if (oldset != NULL) {
14073: for (i = 0; i < oldset->nodeNr; i++) {
14074: /*
14075: * Run the evaluation with a node list made of a single item
14076: * in the nodeset.
14077: */
14078: ctxt->context->node = oldset->nodeTab[i];
14079: /*
14080: * OPTIMIZE TODO: Avoid recreation for every iteration.
14081: */
14082: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
14083: ctxt->context->node);
14084: valuePush(ctxt, tmp);
14085:
14086: if (op->ch2 != -1)
14087: total +=
14088: xmlXPathCompOpEval(ctxt,
14089: &comp->steps[op->ch2]);
14090: if (ctxt->error != XPATH_EXPRESSION_OK) {
14091: xmlXPathFreeObject(obj);
14092: return(0);
14093: }
14094:
14095: res = valuePop(ctxt);
14096: range =
14097: xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
14098: res);
14099: if (range != NULL) {
14100: xmlXPtrLocationSetAdd(newlocset, range);
14101: }
14102:
14103: /*
14104: * Cleanup
14105: */
14106: if (res != NULL) {
14107: xmlXPathReleaseObject(ctxt->context, res);
14108: }
14109: if (ctxt->value == tmp) {
14110: res = valuePop(ctxt);
14111: xmlXPathReleaseObject(ctxt->context, res);
14112: }
14113:
14114: ctxt->context->node = NULL;
14115: }
14116: }
14117: }
14118:
14119: /*
14120: * The result is used as the new evaluation set.
14121: */
14122: xmlXPathReleaseObject(ctxt->context, obj);
14123: ctxt->context->node = NULL;
14124: ctxt->context->contextSize = -1;
14125: ctxt->context->proximityPosition = -1;
14126: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
14127: return (total);
14128: }
14129: #endif /* LIBXML_XPTR_ENABLED */
14130: }
14131: xmlGenericError(xmlGenericErrorContext,
14132: "XPath: unknown precompiled operation %d\n", op->op);
1.1.1.2 misho 14133: ctxt->error = XPATH_INVALID_OPERAND;
1.1 misho 14134: return (total);
14135: }
14136:
14137: /**
14138: * xmlXPathCompOpEvalToBoolean:
14139: * @ctxt: the XPath parser context
14140: *
14141: * Evaluates if the expression evaluates to true.
14142: *
14143: * Returns 1 if true, 0 if false and -1 on API or internal errors.
14144: */
14145: static int
14146: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
14147: xmlXPathStepOpPtr op,
14148: int isPredicate)
14149: {
14150: xmlXPathObjectPtr resObj = NULL;
14151:
14152: start:
14153: /* comp = ctxt->comp; */
14154: switch (op->op) {
14155: case XPATH_OP_END:
14156: return (0);
14157: case XPATH_OP_VALUE:
14158: resObj = (xmlXPathObjectPtr) op->value4;
14159: if (isPredicate)
14160: return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
14161: return(xmlXPathCastToBoolean(resObj));
14162: case XPATH_OP_SORT:
14163: /*
14164: * We don't need sorting for boolean results. Skip this one.
14165: */
14166: if (op->ch1 != -1) {
14167: op = &ctxt->comp->steps[op->ch1];
14168: goto start;
14169: }
14170: return(0);
14171: case XPATH_OP_COLLECT:
14172: if (op->ch1 == -1)
14173: return(0);
14174:
14175: xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
14176: if (ctxt->error != XPATH_EXPRESSION_OK)
14177: return(-1);
14178:
14179: xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
14180: if (ctxt->error != XPATH_EXPRESSION_OK)
14181: return(-1);
14182:
14183: resObj = valuePop(ctxt);
14184: if (resObj == NULL)
14185: return(-1);
14186: break;
14187: default:
14188: /*
14189: * Fallback to call xmlXPathCompOpEval().
14190: */
14191: xmlXPathCompOpEval(ctxt, op);
14192: if (ctxt->error != XPATH_EXPRESSION_OK)
14193: return(-1);
14194:
14195: resObj = valuePop(ctxt);
14196: if (resObj == NULL)
14197: return(-1);
14198: break;
14199: }
14200:
14201: if (resObj) {
14202: int res;
14203:
14204: if (resObj->type == XPATH_BOOLEAN) {
14205: res = resObj->boolval;
14206: } else if (isPredicate) {
14207: /*
14208: * For predicates a result of type "number" is handled
14209: * differently:
14210: * SPEC XPath 1.0:
14211: * "If the result is a number, the result will be converted
14212: * to true if the number is equal to the context position
14213: * and will be converted to false otherwise;"
14214: */
14215: res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
14216: } else {
14217: res = xmlXPathCastToBoolean(resObj);
14218: }
14219: xmlXPathReleaseObject(ctxt->context, resObj);
14220: return(res);
14221: }
14222:
14223: return(0);
14224: }
14225:
14226: #ifdef XPATH_STREAMING
14227: /**
14228: * xmlXPathRunStreamEval:
14229: * @ctxt: the XPath parser context with the compiled expression
14230: *
14231: * Evaluate the Precompiled Streamable XPath expression in the given context.
14232: */
14233: static int
14234: xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
14235: xmlXPathObjectPtr *resultSeq, int toBool)
14236: {
14237: int max_depth, min_depth;
14238: int from_root;
14239: int ret, depth;
14240: int eval_all_nodes;
14241: xmlNodePtr cur = NULL, limit = NULL;
14242: xmlStreamCtxtPtr patstream = NULL;
14243:
14244: int nb_nodes = 0;
14245:
14246: if ((ctxt == NULL) || (comp == NULL))
14247: return(-1);
14248: max_depth = xmlPatternMaxDepth(comp);
14249: if (max_depth == -1)
14250: return(-1);
14251: if (max_depth == -2)
14252: max_depth = 10000;
14253: min_depth = xmlPatternMinDepth(comp);
14254: if (min_depth == -1)
14255: return(-1);
14256: from_root = xmlPatternFromRoot(comp);
14257: if (from_root < 0)
14258: return(-1);
14259: #if 0
14260: printf("stream eval: depth %d from root %d\n", max_depth, from_root);
14261: #endif
14262:
14263: if (! toBool) {
14264: if (resultSeq == NULL)
14265: return(-1);
14266: *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
14267: if (*resultSeq == NULL)
14268: return(-1);
14269: }
14270:
14271: /*
14272: * handle the special cases of "/" amd "." being matched
14273: */
14274: if (min_depth == 0) {
14275: if (from_root) {
14276: /* Select "/" */
14277: if (toBool)
14278: return(1);
14279: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
1.1.1.3 ! misho 14280: (xmlNodePtr) ctxt->doc);
1.1 misho 14281: } else {
14282: /* Select "self::node()" */
14283: if (toBool)
14284: return(1);
14285: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
14286: }
14287: }
14288: if (max_depth == 0) {
14289: return(0);
14290: }
14291:
14292: if (from_root) {
14293: cur = (xmlNodePtr)ctxt->doc;
14294: } else if (ctxt->node != NULL) {
14295: switch (ctxt->node->type) {
14296: case XML_ELEMENT_NODE:
14297: case XML_DOCUMENT_NODE:
14298: case XML_DOCUMENT_FRAG_NODE:
14299: case XML_HTML_DOCUMENT_NODE:
14300: #ifdef LIBXML_DOCB_ENABLED
14301: case XML_DOCB_DOCUMENT_NODE:
14302: #endif
14303: cur = ctxt->node;
14304: break;
14305: case XML_ATTRIBUTE_NODE:
14306: case XML_TEXT_NODE:
14307: case XML_CDATA_SECTION_NODE:
14308: case XML_ENTITY_REF_NODE:
14309: case XML_ENTITY_NODE:
14310: case XML_PI_NODE:
14311: case XML_COMMENT_NODE:
14312: case XML_NOTATION_NODE:
14313: case XML_DTD_NODE:
14314: case XML_DOCUMENT_TYPE_NODE:
14315: case XML_ELEMENT_DECL:
14316: case XML_ATTRIBUTE_DECL:
14317: case XML_ENTITY_DECL:
14318: case XML_NAMESPACE_DECL:
14319: case XML_XINCLUDE_START:
14320: case XML_XINCLUDE_END:
14321: break;
14322: }
14323: limit = cur;
14324: }
14325: if (cur == NULL) {
14326: return(0);
14327: }
14328:
14329: patstream = xmlPatternGetStreamCtxt(comp);
14330: if (patstream == NULL) {
14331: /*
14332: * QUESTION TODO: Is this an error?
14333: */
14334: return(0);
14335: }
14336:
14337: eval_all_nodes = xmlStreamWantsAnyNode(patstream);
14338:
14339: if (from_root) {
14340: ret = xmlStreamPush(patstream, NULL, NULL);
14341: if (ret < 0) {
14342: } else if (ret == 1) {
14343: if (toBool)
14344: goto return_1;
14345: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
14346: }
14347: }
14348: depth = 0;
14349: goto scan_children;
14350: next_node:
14351: do {
14352: nb_nodes++;
14353:
14354: switch (cur->type) {
14355: case XML_ELEMENT_NODE:
14356: case XML_TEXT_NODE:
14357: case XML_CDATA_SECTION_NODE:
14358: case XML_COMMENT_NODE:
14359: case XML_PI_NODE:
14360: if (cur->type == XML_ELEMENT_NODE) {
14361: ret = xmlStreamPush(patstream, cur->name,
14362: (cur->ns ? cur->ns->href : NULL));
14363: } else if (eval_all_nodes)
14364: ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
14365: else
14366: break;
14367:
14368: if (ret < 0) {
14369: /* NOP. */
14370: } else if (ret == 1) {
14371: if (toBool)
14372: goto return_1;
1.1.1.3 ! misho 14373: if (xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur)
! 14374: < 0) {
! 14375: ctxt->lastError.domain = XML_FROM_XPATH;
! 14376: ctxt->lastError.code = XML_ERR_NO_MEMORY;
! 14377: }
1.1 misho 14378: }
14379: if ((cur->children == NULL) || (depth >= max_depth)) {
14380: ret = xmlStreamPop(patstream);
14381: while (cur->next != NULL) {
14382: cur = cur->next;
14383: if ((cur->type != XML_ENTITY_DECL) &&
14384: (cur->type != XML_DTD_NODE))
14385: goto next_node;
14386: }
14387: }
14388: default:
14389: break;
14390: }
14391:
14392: scan_children:
1.1.1.3 ! misho 14393: if (cur->type == XML_NAMESPACE_DECL) break;
1.1 misho 14394: if ((cur->children != NULL) && (depth < max_depth)) {
14395: /*
14396: * Do not descend on entities declarations
14397: */
14398: if (cur->children->type != XML_ENTITY_DECL) {
14399: cur = cur->children;
14400: depth++;
14401: /*
14402: * Skip DTDs
14403: */
14404: if (cur->type != XML_DTD_NODE)
14405: continue;
14406: }
14407: }
14408:
14409: if (cur == limit)
14410: break;
14411:
14412: while (cur->next != NULL) {
14413: cur = cur->next;
14414: if ((cur->type != XML_ENTITY_DECL) &&
14415: (cur->type != XML_DTD_NODE))
14416: goto next_node;
14417: }
14418:
14419: do {
14420: cur = cur->parent;
14421: depth--;
14422: if ((cur == NULL) || (cur == limit))
14423: goto done;
14424: if (cur->type == XML_ELEMENT_NODE) {
14425: ret = xmlStreamPop(patstream);
14426: } else if ((eval_all_nodes) &&
14427: ((cur->type == XML_TEXT_NODE) ||
14428: (cur->type == XML_CDATA_SECTION_NODE) ||
14429: (cur->type == XML_COMMENT_NODE) ||
14430: (cur->type == XML_PI_NODE)))
14431: {
14432: ret = xmlStreamPop(patstream);
14433: }
14434: if (cur->next != NULL) {
14435: cur = cur->next;
14436: break;
14437: }
14438: } while (cur != NULL);
14439:
14440: } while ((cur != NULL) && (depth >= 0));
14441:
14442: done:
14443:
14444: #if 0
14445: printf("stream eval: checked %d nodes selected %d\n",
14446: nb_nodes, retObj->nodesetval->nodeNr);
14447: #endif
14448:
14449: if (patstream)
14450: xmlFreeStreamCtxt(patstream);
14451: return(0);
14452:
14453: return_1:
14454: if (patstream)
14455: xmlFreeStreamCtxt(patstream);
14456: return(1);
14457: }
14458: #endif /* XPATH_STREAMING */
14459:
14460: /**
14461: * xmlXPathRunEval:
14462: * @ctxt: the XPath parser context with the compiled expression
14463: * @toBool: evaluate to a boolean result
14464: *
14465: * Evaluate the Precompiled XPath expression in the given context.
14466: */
14467: static int
14468: xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
14469: {
14470: xmlXPathCompExprPtr comp;
14471:
14472: if ((ctxt == NULL) || (ctxt->comp == NULL))
14473: return(-1);
14474:
14475: if (ctxt->valueTab == NULL) {
14476: /* Allocate the value stack */
14477: ctxt->valueTab = (xmlXPathObjectPtr *)
14478: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
14479: if (ctxt->valueTab == NULL) {
14480: xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
14481: xmlFree(ctxt);
14482: }
14483: ctxt->valueNr = 0;
14484: ctxt->valueMax = 10;
14485: ctxt->value = NULL;
1.1.1.2 misho 14486: ctxt->valueFrame = 0;
1.1 misho 14487: }
14488: #ifdef XPATH_STREAMING
14489: if (ctxt->comp->stream) {
14490: int res;
14491:
14492: if (toBool) {
14493: /*
14494: * Evaluation to boolean result.
14495: */
14496: res = xmlXPathRunStreamEval(ctxt->context,
14497: ctxt->comp->stream, NULL, 1);
14498: if (res != -1)
14499: return(res);
14500: } else {
14501: xmlXPathObjectPtr resObj = NULL;
14502:
14503: /*
14504: * Evaluation to a sequence.
14505: */
14506: res = xmlXPathRunStreamEval(ctxt->context,
14507: ctxt->comp->stream, &resObj, 0);
14508:
14509: if ((res != -1) && (resObj != NULL)) {
14510: valuePush(ctxt, resObj);
14511: return(0);
14512: }
14513: if (resObj != NULL)
14514: xmlXPathReleaseObject(ctxt->context, resObj);
14515: }
14516: /*
14517: * QUESTION TODO: This falls back to normal XPath evaluation
14518: * if res == -1. Is this intended?
14519: */
14520: }
14521: #endif
14522: comp = ctxt->comp;
14523: if (comp->last < 0) {
14524: xmlGenericError(xmlGenericErrorContext,
14525: "xmlXPathRunEval: last is less than zero\n");
14526: return(-1);
14527: }
14528: if (toBool)
14529: return(xmlXPathCompOpEvalToBoolean(ctxt,
14530: &comp->steps[comp->last], 0));
14531: else
14532: xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
14533:
14534: return(0);
14535: }
14536:
14537: /************************************************************************
14538: * *
14539: * Public interfaces *
14540: * *
14541: ************************************************************************/
14542:
14543: /**
14544: * xmlXPathEvalPredicate:
14545: * @ctxt: the XPath context
14546: * @res: the Predicate Expression evaluation result
14547: *
14548: * Evaluate a predicate result for the current node.
14549: * A PredicateExpr is evaluated by evaluating the Expr and converting
14550: * the result to a boolean. If the result is a number, the result will
14551: * be converted to true if the number is equal to the position of the
14552: * context node in the context node list (as returned by the position
14553: * function) and will be converted to false otherwise; if the result
14554: * is not a number, then the result will be converted as if by a call
14555: * to the boolean function.
14556: *
14557: * Returns 1 if predicate is true, 0 otherwise
14558: */
14559: int
14560: xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
14561: if ((ctxt == NULL) || (res == NULL)) return(0);
14562: switch (res->type) {
14563: case XPATH_BOOLEAN:
14564: return(res->boolval);
14565: case XPATH_NUMBER:
14566: return(res->floatval == ctxt->proximityPosition);
14567: case XPATH_NODESET:
14568: case XPATH_XSLT_TREE:
14569: if (res->nodesetval == NULL)
14570: return(0);
14571: return(res->nodesetval->nodeNr != 0);
14572: case XPATH_STRING:
14573: return((res->stringval != NULL) &&
14574: (xmlStrlen(res->stringval) != 0));
14575: default:
14576: STRANGE
14577: }
14578: return(0);
14579: }
14580:
14581: /**
14582: * xmlXPathEvaluatePredicateResult:
14583: * @ctxt: the XPath Parser context
14584: * @res: the Predicate Expression evaluation result
14585: *
14586: * Evaluate a predicate result for the current node.
14587: * A PredicateExpr is evaluated by evaluating the Expr and converting
14588: * the result to a boolean. If the result is a number, the result will
14589: * be converted to true if the number is equal to the position of the
14590: * context node in the context node list (as returned by the position
14591: * function) and will be converted to false otherwise; if the result
14592: * is not a number, then the result will be converted as if by a call
14593: * to the boolean function.
14594: *
14595: * Returns 1 if predicate is true, 0 otherwise
14596: */
14597: int
14598: xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
14599: xmlXPathObjectPtr res) {
14600: if ((ctxt == NULL) || (res == NULL)) return(0);
14601: switch (res->type) {
14602: case XPATH_BOOLEAN:
14603: return(res->boolval);
14604: case XPATH_NUMBER:
14605: #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
14606: return((res->floatval == ctxt->context->proximityPosition) &&
14607: (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
14608: #else
14609: return(res->floatval == ctxt->context->proximityPosition);
14610: #endif
14611: case XPATH_NODESET:
14612: case XPATH_XSLT_TREE:
14613: if (res->nodesetval == NULL)
14614: return(0);
14615: return(res->nodesetval->nodeNr != 0);
14616: case XPATH_STRING:
14617: return((res->stringval != NULL) && (res->stringval[0] != 0));
14618: #ifdef LIBXML_XPTR_ENABLED
14619: case XPATH_LOCATIONSET:{
14620: xmlLocationSetPtr ptr = res->user;
14621: if (ptr == NULL)
14622: return(0);
14623: return (ptr->locNr != 0);
14624: }
14625: #endif
14626: default:
14627: STRANGE
14628: }
14629: return(0);
14630: }
14631:
14632: #ifdef XPATH_STREAMING
14633: /**
14634: * xmlXPathTryStreamCompile:
14635: * @ctxt: an XPath context
14636: * @str: the XPath expression
14637: *
14638: * Try to compile the XPath expression as a streamable subset.
14639: *
14640: * Returns the compiled expression or NULL if failed to compile.
14641: */
14642: static xmlXPathCompExprPtr
14643: xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14644: /*
14645: * Optimization: use streaming patterns when the XPath expression can
14646: * be compiled to a stream lookup
14647: */
14648: xmlPatternPtr stream;
14649: xmlXPathCompExprPtr comp;
14650: xmlDictPtr dict = NULL;
14651: const xmlChar **namespaces = NULL;
14652: xmlNsPtr ns;
14653: int i, j;
14654:
14655: if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
14656: (!xmlStrchr(str, '@'))) {
14657: const xmlChar *tmp;
14658:
14659: /*
14660: * We don't try to handle expressions using the verbose axis
14661: * specifiers ("::"), just the simplied form at this point.
14662: * Additionally, if there is no list of namespaces available and
14663: * there's a ":" in the expression, indicating a prefixed QName,
14664: * then we won't try to compile either. xmlPatterncompile() needs
14665: * to have a list of namespaces at compilation time in order to
14666: * compile prefixed name tests.
14667: */
14668: tmp = xmlStrchr(str, ':');
14669: if ((tmp != NULL) &&
14670: ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
14671: return(NULL);
14672:
14673: if (ctxt != NULL) {
14674: dict = ctxt->dict;
14675: if (ctxt->nsNr > 0) {
14676: namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
14677: if (namespaces == NULL) {
14678: xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
14679: return(NULL);
14680: }
14681: for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
14682: ns = ctxt->namespaces[j];
14683: namespaces[i++] = ns->href;
14684: namespaces[i++] = ns->prefix;
14685: }
14686: namespaces[i++] = NULL;
14687: namespaces[i] = NULL;
14688: }
14689: }
14690:
14691: stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH,
14692: &namespaces[0]);
14693: if (namespaces != NULL) {
14694: xmlFree((xmlChar **)namespaces);
14695: }
14696: if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
14697: comp = xmlXPathNewCompExpr();
14698: if (comp == NULL) {
14699: xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
14700: return(NULL);
14701: }
14702: comp->stream = stream;
14703: comp->dict = dict;
14704: if (comp->dict)
14705: xmlDictReference(comp->dict);
14706: return(comp);
14707: }
14708: xmlFreePattern(stream);
14709: }
14710: return(NULL);
14711: }
14712: #endif /* XPATH_STREAMING */
14713:
14714: static void
1.1.1.3 ! misho 14715: xmlXPathOptimizeExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
1.1 misho 14716: {
14717: /*
14718: * Try to rewrite "descendant-or-self::node()/foo" to an optimized
14719: * internal representation.
14720: */
14721:
1.1.1.3 ! misho 14722: if ((op->ch1 != -1) &&
! 14723: (op->op == XPATH_OP_COLLECT /* 11 */))
! 14724: {
! 14725: xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
! 14726:
! 14727: if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
! 14728: ((xmlXPathAxisVal) prevop->value ==
! 14729: AXIS_DESCENDANT_OR_SELF) &&
! 14730: (prevop->ch2 == -1) &&
! 14731: ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
! 14732: ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE))
! 14733: {
! 14734: /*
! 14735: * This is a "descendant-or-self::node()" without predicates.
! 14736: * Try to eliminate it.
! 14737: */
! 14738:
! 14739: switch ((xmlXPathAxisVal) op->value) {
! 14740: case AXIS_CHILD:
! 14741: case AXIS_DESCENDANT:
! 14742: /*
! 14743: * Convert "descendant-or-self::node()/child::" or
! 14744: * "descendant-or-self::node()/descendant::" to
! 14745: * "descendant::"
! 14746: */
! 14747: op->ch1 = prevop->ch1;
! 14748: op->value = AXIS_DESCENDANT;
! 14749: break;
! 14750: case AXIS_SELF:
! 14751: case AXIS_DESCENDANT_OR_SELF:
! 14752: /*
! 14753: * Convert "descendant-or-self::node()/self::" or
! 14754: * "descendant-or-self::node()/descendant-or-self::" to
! 14755: * to "descendant-or-self::"
! 14756: */
! 14757: op->ch1 = prevop->ch1;
! 14758: op->value = AXIS_DESCENDANT_OR_SELF;
! 14759: break;
! 14760: default:
! 14761: break;
! 14762: }
1.1 misho 14763: }
14764: }
1.1.1.3 ! misho 14765:
! 14766: /* Recurse */
! 14767: if (op->ch1 != -1)
! 14768: xmlXPathOptimizeExpression(comp, &comp->steps[op->ch1]);
1.1 misho 14769: if (op->ch2 != -1)
1.1.1.3 ! misho 14770: xmlXPathOptimizeExpression(comp, &comp->steps[op->ch2]);
1.1 misho 14771: }
14772:
14773: /**
14774: * xmlXPathCtxtCompile:
14775: * @ctxt: an XPath context
14776: * @str: the XPath expression
14777: *
14778: * Compile an XPath expression
14779: *
14780: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14781: * the caller has to free the object.
14782: */
14783: xmlXPathCompExprPtr
14784: xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14785: xmlXPathParserContextPtr pctxt;
14786: xmlXPathCompExprPtr comp;
14787:
14788: #ifdef XPATH_STREAMING
14789: comp = xmlXPathTryStreamCompile(ctxt, str);
14790: if (comp != NULL)
14791: return(comp);
14792: #endif
14793:
14794: xmlXPathInit();
14795:
14796: pctxt = xmlXPathNewParserContext(str, ctxt);
14797: if (pctxt == NULL)
14798: return NULL;
14799: xmlXPathCompileExpr(pctxt, 1);
14800:
14801: if( pctxt->error != XPATH_EXPRESSION_OK )
14802: {
14803: xmlXPathFreeParserContext(pctxt);
14804: return(NULL);
14805: }
14806:
14807: if (*pctxt->cur != 0) {
14808: /*
14809: * aleksey: in some cases this line prints *second* error message
14810: * (see bug #78858) and probably this should be fixed.
14811: * However, we are not sure that all error messages are printed
14812: * out in other places. It's not critical so we leave it as-is for now
14813: */
14814: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14815: comp = NULL;
14816: } else {
14817: comp = pctxt->comp;
14818: pctxt->comp = NULL;
14819: }
14820: xmlXPathFreeParserContext(pctxt);
14821:
14822: if (comp != NULL) {
14823: comp->expr = xmlStrdup(str);
14824: #ifdef DEBUG_EVAL_COUNTS
14825: comp->string = xmlStrdup(str);
14826: comp->nb = 0;
14827: #endif
1.1.1.3 ! misho 14828: if ((comp->nbStep > 1) && (comp->last >= 0)) {
! 14829: xmlXPathOptimizeExpression(comp, &comp->steps[comp->last]);
1.1 misho 14830: }
14831: }
14832: return(comp);
14833: }
14834:
14835: /**
14836: * xmlXPathCompile:
14837: * @str: the XPath expression
14838: *
14839: * Compile an XPath expression
14840: *
14841: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14842: * the caller has to free the object.
14843: */
14844: xmlXPathCompExprPtr
14845: xmlXPathCompile(const xmlChar *str) {
14846: return(xmlXPathCtxtCompile(NULL, str));
14847: }
14848:
14849: /**
14850: * xmlXPathCompiledEvalInternal:
14851: * @comp: the compiled XPath expression
14852: * @ctxt: the XPath context
14853: * @resObj: the resulting XPath object or NULL
14854: * @toBool: 1 if only a boolean result is requested
14855: *
14856: * Evaluate the Precompiled XPath expression in the given context.
14857: * The caller has to free @resObj.
14858: *
14859: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14860: * the caller has to free the object.
14861: */
14862: static int
14863: xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
14864: xmlXPathContextPtr ctxt,
14865: xmlXPathObjectPtr *resObj,
14866: int toBool)
14867: {
14868: xmlXPathParserContextPtr pctxt;
14869: #ifndef LIBXML_THREAD_ENABLED
14870: static int reentance = 0;
14871: #endif
14872: int res;
14873:
14874: CHECK_CTXT_NEG(ctxt)
14875:
14876: if (comp == NULL)
14877: return(-1);
14878: xmlXPathInit();
14879:
14880: #ifndef LIBXML_THREAD_ENABLED
14881: reentance++;
14882: if (reentance > 1)
14883: xmlXPathDisableOptimizer = 1;
14884: #endif
14885:
14886: #ifdef DEBUG_EVAL_COUNTS
14887: comp->nb++;
14888: if ((comp->string != NULL) && (comp->nb > 100)) {
14889: fprintf(stderr, "100 x %s\n", comp->string);
14890: comp->nb = 0;
14891: }
14892: #endif
14893: pctxt = xmlXPathCompParserContext(comp, ctxt);
14894: res = xmlXPathRunEval(pctxt, toBool);
14895:
14896: if (resObj) {
14897: if (pctxt->value == NULL) {
14898: xmlGenericError(xmlGenericErrorContext,
14899: "xmlXPathCompiledEval: evaluation failed\n");
14900: *resObj = NULL;
14901: } else {
14902: *resObj = valuePop(pctxt);
14903: }
14904: }
14905:
14906: /*
14907: * Pop all remaining objects from the stack.
14908: */
14909: if (pctxt->valueNr > 0) {
14910: xmlXPathObjectPtr tmp;
14911: int stack = 0;
14912:
14913: do {
14914: tmp = valuePop(pctxt);
14915: if (tmp != NULL) {
14916: stack++;
14917: xmlXPathReleaseObject(ctxt, tmp);
14918: }
14919: } while (tmp != NULL);
14920: if ((stack != 0) &&
14921: ((toBool) || ((resObj) && (*resObj))))
14922: {
14923: xmlGenericError(xmlGenericErrorContext,
14924: "xmlXPathCompiledEval: %d objects left on the stack.\n",
14925: stack);
14926: }
14927: }
14928:
14929: if ((pctxt->error != XPATH_EXPRESSION_OK) && (resObj) && (*resObj)) {
14930: xmlXPathFreeObject(*resObj);
14931: *resObj = NULL;
14932: }
14933: pctxt->comp = NULL;
14934: xmlXPathFreeParserContext(pctxt);
14935: #ifndef LIBXML_THREAD_ENABLED
14936: reentance--;
14937: #endif
14938:
14939: return(res);
14940: }
14941:
14942: /**
14943: * xmlXPathCompiledEval:
14944: * @comp: the compiled XPath expression
14945: * @ctx: the XPath context
14946: *
14947: * Evaluate the Precompiled XPath expression in the given context.
14948: *
14949: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14950: * the caller has to free the object.
14951: */
14952: xmlXPathObjectPtr
14953: xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
14954: {
14955: xmlXPathObjectPtr res = NULL;
14956:
14957: xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
14958: return(res);
14959: }
14960:
14961: /**
14962: * xmlXPathCompiledEvalToBoolean:
14963: * @comp: the compiled XPath expression
14964: * @ctxt: the XPath context
14965: *
14966: * Applies the XPath boolean() function on the result of the given
14967: * compiled expression.
14968: *
14969: * Returns 1 if the expression evaluated to true, 0 if to false and
14970: * -1 in API and internal errors.
14971: */
14972: int
14973: xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
14974: xmlXPathContextPtr ctxt)
14975: {
14976: return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
14977: }
14978:
14979: /**
14980: * xmlXPathEvalExpr:
14981: * @ctxt: the XPath Parser context
14982: *
14983: * Parse and evaluate an XPath expression in the given context,
14984: * then push the result on the context stack
14985: */
14986: void
14987: xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
14988: #ifdef XPATH_STREAMING
14989: xmlXPathCompExprPtr comp;
14990: #endif
14991:
14992: if (ctxt == NULL) return;
14993:
14994: #ifdef XPATH_STREAMING
14995: comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
14996: if (comp != NULL) {
14997: if (ctxt->comp != NULL)
14998: xmlXPathFreeCompExpr(ctxt->comp);
14999: ctxt->comp = comp;
15000: if (ctxt->cur != NULL)
15001: while (*ctxt->cur != 0) ctxt->cur++;
15002: } else
15003: #endif
15004: {
15005: xmlXPathCompileExpr(ctxt, 1);
15006: if ((ctxt->error == XPATH_EXPRESSION_OK) &&
15007: (ctxt->comp != NULL) &&
1.1.1.3 ! misho 15008: (ctxt->comp->nbStep > 1) &&
! 15009: (ctxt->comp->last >= 0))
1.1 misho 15010: {
1.1.1.3 ! misho 15011: xmlXPathOptimizeExpression(ctxt->comp,
1.1 misho 15012: &ctxt->comp->steps[ctxt->comp->last]);
15013: }
15014: }
15015: CHECK_ERROR;
15016: xmlXPathRunEval(ctxt, 0);
15017: }
15018:
15019: /**
15020: * xmlXPathEval:
15021: * @str: the XPath expression
15022: * @ctx: the XPath context
15023: *
15024: * Evaluate the XPath Location Path in the given context.
15025: *
15026: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
15027: * the caller has to free the object.
15028: */
15029: xmlXPathObjectPtr
15030: xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
15031: xmlXPathParserContextPtr ctxt;
15032: xmlXPathObjectPtr res, tmp, init = NULL;
15033: int stack = 0;
15034:
15035: CHECK_CTXT(ctx)
15036:
15037: xmlXPathInit();
15038:
15039: ctxt = xmlXPathNewParserContext(str, ctx);
15040: if (ctxt == NULL)
15041: return NULL;
15042: xmlXPathEvalExpr(ctxt);
15043:
15044: if (ctxt->value == NULL) {
15045: xmlGenericError(xmlGenericErrorContext,
15046: "xmlXPathEval: evaluation failed\n");
15047: res = NULL;
15048: } else if ((*ctxt->cur != 0) && (ctxt->comp != NULL)
15049: #ifdef XPATH_STREAMING
15050: && (ctxt->comp->stream == NULL)
15051: #endif
15052: ) {
15053: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
15054: res = NULL;
15055: } else {
15056: res = valuePop(ctxt);
15057: }
15058:
15059: do {
15060: tmp = valuePop(ctxt);
15061: if (tmp != NULL) {
15062: if (tmp != init)
15063: stack++;
15064: xmlXPathReleaseObject(ctx, tmp);
15065: }
15066: } while (tmp != NULL);
15067: if ((stack != 0) && (res != NULL)) {
15068: xmlGenericError(xmlGenericErrorContext,
15069: "xmlXPathEval: %d object left on the stack\n",
15070: stack);
15071: }
15072: if (ctxt->error != XPATH_EXPRESSION_OK) {
15073: xmlXPathFreeObject(res);
15074: res = NULL;
15075: }
15076:
15077: xmlXPathFreeParserContext(ctxt);
15078: return(res);
15079: }
15080:
15081: /**
1.1.1.3 ! misho 15082: * xmlXPathSetContextNode:
! 15083: * @node: the node to to use as the context node
! 15084: * @ctx: the XPath context
! 15085: *
! 15086: * Sets 'node' as the context node. The node must be in the same
! 15087: * document as that associated with the context.
! 15088: *
! 15089: * Returns -1 in case of error or 0 if successful
! 15090: */
! 15091: int
! 15092: xmlXPathSetContextNode(xmlNodePtr node, xmlXPathContextPtr ctx) {
! 15093: if ((node == NULL) || (ctx == NULL))
! 15094: return(-1);
! 15095:
! 15096: if (node->doc == ctx->doc) {
! 15097: ctx->node = node;
! 15098: return(0);
! 15099: }
! 15100: return(-1);
! 15101: }
! 15102:
! 15103: /**
! 15104: * xmlXPathNodeEval:
! 15105: * @node: the node to to use as the context node
! 15106: * @str: the XPath expression
! 15107: * @ctx: the XPath context
! 15108: *
! 15109: * Evaluate the XPath Location Path in the given context. The node 'node'
! 15110: * is set as the context node. The context node is not restored.
! 15111: *
! 15112: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
! 15113: * the caller has to free the object.
! 15114: */
! 15115: xmlXPathObjectPtr
! 15116: xmlXPathNodeEval(xmlNodePtr node, const xmlChar *str, xmlXPathContextPtr ctx) {
! 15117: if (str == NULL)
! 15118: return(NULL);
! 15119: if (xmlXPathSetContextNode(node, ctx) < 0)
! 15120: return(NULL);
! 15121: return(xmlXPathEval(str, ctx));
! 15122: }
! 15123:
! 15124: /**
1.1 misho 15125: * xmlXPathEvalExpression:
15126: * @str: the XPath expression
15127: * @ctxt: the XPath context
15128: *
15129: * Evaluate the XPath expression in the given context.
15130: *
15131: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
15132: * the caller has to free the object.
15133: */
15134: xmlXPathObjectPtr
15135: xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
15136: xmlXPathParserContextPtr pctxt;
15137: xmlXPathObjectPtr res, tmp;
15138: int stack = 0;
15139:
15140: CHECK_CTXT(ctxt)
15141:
15142: xmlXPathInit();
15143:
15144: pctxt = xmlXPathNewParserContext(str, ctxt);
15145: if (pctxt == NULL)
15146: return NULL;
15147: xmlXPathEvalExpr(pctxt);
15148:
15149: if ((*pctxt->cur != 0) || (pctxt->error != XPATH_EXPRESSION_OK)) {
15150: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
15151: res = NULL;
15152: } else {
15153: res = valuePop(pctxt);
15154: }
15155: do {
15156: tmp = valuePop(pctxt);
15157: if (tmp != NULL) {
15158: xmlXPathReleaseObject(ctxt, tmp);
15159: stack++;
15160: }
15161: } while (tmp != NULL);
15162: if ((stack != 0) && (res != NULL)) {
15163: xmlGenericError(xmlGenericErrorContext,
15164: "xmlXPathEvalExpression: %d object left on the stack\n",
15165: stack);
15166: }
15167: xmlXPathFreeParserContext(pctxt);
15168: return(res);
15169: }
15170:
15171: /************************************************************************
15172: * *
15173: * Extra functions not pertaining to the XPath spec *
15174: * *
15175: ************************************************************************/
15176: /**
15177: * xmlXPathEscapeUriFunction:
15178: * @ctxt: the XPath Parser context
15179: * @nargs: the number of arguments
15180: *
15181: * Implement the escape-uri() XPath function
15182: * string escape-uri(string $str, bool $escape-reserved)
15183: *
15184: * This function applies the URI escaping rules defined in section 2 of [RFC
15185: * 2396] to the string supplied as $uri-part, which typically represents all
15186: * or part of a URI. The effect of the function is to replace any special
15187: * character in the string by an escape sequence of the form %xx%yy...,
15188: * where xxyy... is the hexadecimal representation of the octets used to
15189: * represent the character in UTF-8.
15190: *
15191: * The set of characters that are escaped depends on the setting of the
15192: * boolean argument $escape-reserved.
15193: *
15194: * If $escape-reserved is true, all characters are escaped other than lower
15195: * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
15196: * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
15197: * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
15198: * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
15199: * A-F).
15200: *
15201: * If $escape-reserved is false, the behavior differs in that characters
15202: * referred to in [RFC 2396] as reserved characters are not escaped. These
15203: * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
15204: *
15205: * [RFC 2396] does not define whether escaped URIs should use lower case or
15206: * upper case for hexadecimal digits. To ensure that escaped URIs can be
15207: * compared using string comparison functions, this function must always use
15208: * the upper-case letters A-F.
15209: *
15210: * Generally, $escape-reserved should be set to true when escaping a string
15211: * that is to form a single part of a URI, and to false when escaping an
15212: * entire URI or URI reference.
15213: *
15214: * In the case of non-ascii characters, the string is encoded according to
15215: * utf-8 and then converted according to RFC 2396.
15216: *
15217: * Examples
15218: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
15219: * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
15220: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
15221: * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
15222: *
15223: */
15224: static void
15225: xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
15226: xmlXPathObjectPtr str;
15227: int escape_reserved;
1.1.1.3 ! misho 15228: xmlBufPtr target;
1.1 misho 15229: xmlChar *cptr;
15230: xmlChar escape[4];
15231:
15232: CHECK_ARITY(2);
15233:
15234: escape_reserved = xmlXPathPopBoolean(ctxt);
15235:
15236: CAST_TO_STRING;
15237: str = valuePop(ctxt);
15238:
1.1.1.3 ! misho 15239: target = xmlBufCreate();
1.1 misho 15240:
15241: escape[0] = '%';
15242: escape[3] = 0;
15243:
15244: if (target) {
15245: for (cptr = str->stringval; *cptr; cptr++) {
15246: if ((*cptr >= 'A' && *cptr <= 'Z') ||
15247: (*cptr >= 'a' && *cptr <= 'z') ||
15248: (*cptr >= '0' && *cptr <= '9') ||
15249: *cptr == '-' || *cptr == '_' || *cptr == '.' ||
15250: *cptr == '!' || *cptr == '~' || *cptr == '*' ||
15251: *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
15252: (*cptr == '%' &&
15253: ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
15254: (cptr[1] >= 'a' && cptr[1] <= 'f') ||
15255: (cptr[1] >= '0' && cptr[1] <= '9')) &&
15256: ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
15257: (cptr[2] >= 'a' && cptr[2] <= 'f') ||
15258: (cptr[2] >= '0' && cptr[2] <= '9'))) ||
15259: (!escape_reserved &&
15260: (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
15261: *cptr == ':' || *cptr == '@' || *cptr == '&' ||
15262: *cptr == '=' || *cptr == '+' || *cptr == '$' ||
15263: *cptr == ','))) {
1.1.1.3 ! misho 15264: xmlBufAdd(target, cptr, 1);
1.1 misho 15265: } else {
15266: if ((*cptr >> 4) < 10)
15267: escape[1] = '0' + (*cptr >> 4);
15268: else
15269: escape[1] = 'A' - 10 + (*cptr >> 4);
15270: if ((*cptr & 0xF) < 10)
15271: escape[2] = '0' + (*cptr & 0xF);
15272: else
15273: escape[2] = 'A' - 10 + (*cptr & 0xF);
15274:
1.1.1.3 ! misho 15275: xmlBufAdd(target, &escape[0], 3);
1.1 misho 15276: }
15277: }
15278: }
15279: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
1.1.1.3 ! misho 15280: xmlBufContent(target)));
! 15281: xmlBufFree(target);
1.1 misho 15282: xmlXPathReleaseObject(ctxt->context, str);
15283: }
15284:
15285: /**
15286: * xmlXPathRegisterAllFunctions:
15287: * @ctxt: the XPath context
15288: *
15289: * Registers all default XPath functions in this context
15290: */
15291: void
15292: xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
15293: {
15294: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
15295: xmlXPathBooleanFunction);
15296: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
15297: xmlXPathCeilingFunction);
15298: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
15299: xmlXPathCountFunction);
15300: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
15301: xmlXPathConcatFunction);
15302: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
15303: xmlXPathContainsFunction);
15304: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
15305: xmlXPathIdFunction);
15306: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
15307: xmlXPathFalseFunction);
15308: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
15309: xmlXPathFloorFunction);
15310: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
15311: xmlXPathLastFunction);
15312: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
15313: xmlXPathLangFunction);
15314: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
15315: xmlXPathLocalNameFunction);
15316: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
15317: xmlXPathNotFunction);
15318: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
15319: xmlXPathNameFunction);
15320: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
15321: xmlXPathNamespaceURIFunction);
15322: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
15323: xmlXPathNormalizeFunction);
15324: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
15325: xmlXPathNumberFunction);
15326: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
15327: xmlXPathPositionFunction);
15328: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
15329: xmlXPathRoundFunction);
15330: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
15331: xmlXPathStringFunction);
15332: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
15333: xmlXPathStringLengthFunction);
15334: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
15335: xmlXPathStartsWithFunction);
15336: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
15337: xmlXPathSubstringFunction);
15338: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
15339: xmlXPathSubstringBeforeFunction);
15340: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
15341: xmlXPathSubstringAfterFunction);
15342: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
15343: xmlXPathSumFunction);
15344: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
15345: xmlXPathTrueFunction);
15346: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
15347: xmlXPathTranslateFunction);
15348:
15349: xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
15350: (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
15351: xmlXPathEscapeUriFunction);
15352: }
15353:
15354: #endif /* LIBXML_XPATH_ENABLED */
15355: #define bottom_xpath
15356: #include "elfgcchack.h"
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>