Annotation of embedaddon/libxml2/xpath.c, revision 1.1.1.1
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:
58: #ifdef LIBXML_PATTERN_ENABLED
59: #define XPATH_STREAMING
60: #endif
61:
62: #define TODO \
63: xmlGenericError(xmlGenericErrorContext, \
64: "Unimplemented block at %s:%d\n", \
65: __FILE__, __LINE__);
66:
67: /*
68: * XP_OPTIMIZED_NON_ELEM_COMPARISON:
69: * If defined, this will use xmlXPathCmpNodesExt() instead of
70: * xmlXPathCmpNodes(). The new function is optimized comparison of
71: * non-element nodes; actually it will speed up comparison only if
72: * xmlXPathOrderDocElems() was called in order to index the elements of
73: * a tree in document order; Libxslt does such an indexing, thus it will
74: * benefit from this optimization.
75: */
76: #define XP_OPTIMIZED_NON_ELEM_COMPARISON
77:
78: /*
79: * XP_OPTIMIZED_FILTER_FIRST:
80: * If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
81: * in a way, that it stop evaluation at the first node.
82: */
83: #define XP_OPTIMIZED_FILTER_FIRST
84:
85: /*
86: * XP_DEBUG_OBJ_USAGE:
87: * Internal flag to enable tracking of how much XPath objects have been
88: * created.
89: */
90: /* #define XP_DEBUG_OBJ_USAGE */
91:
92: /*
93: * TODO:
94: * There are a few spots where some tests are done which depend upon ascii
95: * data. These should be enhanced for full UTF8 support (see particularly
96: * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT)
97: */
98:
99: #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
100:
101: /************************************************************************
102: * *
103: * Floating point stuff *
104: * *
105: ************************************************************************/
106:
107: #ifndef TRIO_REPLACE_STDIO
108: #define TRIO_PUBLIC static
109: #endif
110: #include "trionan.c"
111:
112: /*
113: * The lack of portability of this section of the libc is annoying !
114: */
115: double xmlXPathNAN = 0;
116: double xmlXPathPINF = 1;
117: double xmlXPathNINF = -1;
118: static double xmlXPathNZERO = 0; /* not exported from headers */
119: static int xmlXPathInitialized = 0;
120:
121: /**
122: * xmlXPathInit:
123: *
124: * Initialize the XPath environment
125: */
126: void
127: xmlXPathInit(void) {
128: if (xmlXPathInitialized) return;
129:
130: xmlXPathPINF = trio_pinf();
131: xmlXPathNINF = trio_ninf();
132: xmlXPathNAN = trio_nan();
133: xmlXPathNZERO = trio_nzero();
134:
135: xmlXPathInitialized = 1;
136: }
137:
138: /**
139: * xmlXPathIsNaN:
140: * @val: a double value
141: *
142: * Provides a portable isnan() function to detect whether a double
143: * is a NotaNumber. Based on trio code
144: * http://sourceforge.net/projects/ctrio/
145: *
146: * Returns 1 if the value is a NaN, 0 otherwise
147: */
148: int
149: xmlXPathIsNaN(double val) {
150: return(trio_isnan(val));
151: }
152:
153: /**
154: * xmlXPathIsInf:
155: * @val: a double value
156: *
157: * Provides a portable isinf() function to detect whether a double
158: * is a +Infinite or -Infinite. Based on trio code
159: * http://sourceforge.net/projects/ctrio/
160: *
161: * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
162: */
163: int
164: xmlXPathIsInf(double val) {
165: return(trio_isinf(val));
166: }
167:
168: #endif /* SCHEMAS or XPATH */
169: #ifdef LIBXML_XPATH_ENABLED
170: /**
171: * xmlXPathGetSign:
172: * @val: a double value
173: *
174: * Provides a portable function to detect the sign of a double
175: * Modified from trio code
176: * http://sourceforge.net/projects/ctrio/
177: *
178: * Returns 1 if the value is Negative, 0 if positive
179: */
180: static int
181: xmlXPathGetSign(double val) {
182: return(trio_signbit(val));
183: }
184:
185:
186: /*
187: * TODO: when compatibility allows remove all "fake node libxslt" strings
188: * the test should just be name[0] = ' '
189: */
190: #ifdef DEBUG_XPATH_EXPRESSION
191: #define DEBUG_STEP
192: #define DEBUG_EXPR
193: #define DEBUG_EVAL_COUNTS
194: #endif
195:
196: static xmlNs xmlXPathXMLNamespaceStruct = {
197: NULL,
198: XML_NAMESPACE_DECL,
199: XML_XML_NAMESPACE,
200: BAD_CAST "xml",
201: NULL,
202: NULL
203: };
204: static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
205: #ifndef LIBXML_THREAD_ENABLED
206: /*
207: * Optimizer is disabled only when threaded apps are detected while
208: * the library ain't compiled for thread safety.
209: */
210: static int xmlXPathDisableOptimizer = 0;
211: #endif
212:
213: /************************************************************************
214: * *
215: * Error handling routines *
216: * *
217: ************************************************************************/
218:
219: /**
220: * XP_ERRORNULL:
221: * @X: the error code
222: *
223: * Macro to raise an XPath error and return NULL.
224: */
225: #define XP_ERRORNULL(X) \
226: { xmlXPathErr(ctxt, X); return(NULL); }
227:
228: /*
229: * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
230: */
231: static const char *xmlXPathErrorMessages[] = {
232: "Ok\n",
233: "Number encoding\n",
234: "Unfinished literal\n",
235: "Start of literal\n",
236: "Expected $ for variable reference\n",
237: "Undefined variable\n",
238: "Invalid predicate\n",
239: "Invalid expression\n",
240: "Missing closing curly brace\n",
241: "Unregistered function\n",
242: "Invalid operand\n",
243: "Invalid type\n",
244: "Invalid number of arguments\n",
245: "Invalid context size\n",
246: "Invalid context position\n",
247: "Memory allocation error\n",
248: "Syntax error\n",
249: "Resource error\n",
250: "Sub resource error\n",
251: "Undefined namespace prefix\n",
252: "Encoding error\n",
253: "Char out of XML range\n",
254: "Invalid or incomplete context\n",
255: "?? Unknown error ??\n" /* Must be last in the list! */
256: };
257: #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
258: sizeof(xmlXPathErrorMessages[0])) - 1)
259: /**
260: * xmlXPathErrMemory:
261: * @ctxt: an XPath context
262: * @extra: extra informations
263: *
264: * Handle a redefinition of attribute error
265: */
266: static void
267: xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
268: {
269: if (ctxt != NULL) {
270: if (extra) {
271: xmlChar buf[200];
272:
273: xmlStrPrintf(buf, 200,
274: BAD_CAST "Memory allocation failed : %s\n",
275: extra);
276: ctxt->lastError.message = (char *) xmlStrdup(buf);
277: } else {
278: ctxt->lastError.message = (char *)
279: xmlStrdup(BAD_CAST "Memory allocation failed\n");
280: }
281: ctxt->lastError.domain = XML_FROM_XPATH;
282: ctxt->lastError.code = XML_ERR_NO_MEMORY;
283: if (ctxt->error != NULL)
284: ctxt->error(ctxt->userData, &ctxt->lastError);
285: } else {
286: if (extra)
287: __xmlRaiseError(NULL, NULL, NULL,
288: NULL, NULL, XML_FROM_XPATH,
289: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
290: extra, NULL, NULL, 0, 0,
291: "Memory allocation failed : %s\n", extra);
292: else
293: __xmlRaiseError(NULL, NULL, NULL,
294: NULL, NULL, XML_FROM_XPATH,
295: XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
296: NULL, NULL, NULL, 0, 0,
297: "Memory allocation failed\n");
298: }
299: }
300:
301: /**
302: * xmlXPathPErrMemory:
303: * @ctxt: an XPath parser context
304: * @extra: extra informations
305: *
306: * Handle a redefinition of attribute error
307: */
308: static void
309: xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
310: {
311: if (ctxt == NULL)
312: xmlXPathErrMemory(NULL, extra);
313: else {
314: ctxt->error = XPATH_MEMORY_ERROR;
315: xmlXPathErrMemory(ctxt->context, extra);
316: }
317: }
318:
319: /**
320: * xmlXPathErr:
321: * @ctxt: a XPath parser context
322: * @error: the error code
323: *
324: * Handle an XPath error
325: */
326: void
327: xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
328: {
329: if ((error < 0) || (error > MAXERRNO))
330: error = MAXERRNO;
331: if (ctxt == NULL) {
332: __xmlRaiseError(NULL, NULL, NULL,
333: NULL, NULL, XML_FROM_XPATH,
334: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
335: XML_ERR_ERROR, NULL, 0,
336: NULL, NULL, NULL, 0, 0,
337: "%s", xmlXPathErrorMessages[error]);
338: return;
339: }
340: ctxt->error = error;
341: if (ctxt->context == NULL) {
342: __xmlRaiseError(NULL, NULL, NULL,
343: NULL, NULL, XML_FROM_XPATH,
344: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
345: XML_ERR_ERROR, NULL, 0,
346: (const char *) ctxt->base, NULL, NULL,
347: ctxt->cur - ctxt->base, 0,
348: "%s", xmlXPathErrorMessages[error]);
349: return;
350: }
351:
352: /* cleanup current last error */
353: xmlResetError(&ctxt->context->lastError);
354:
355: ctxt->context->lastError.domain = XML_FROM_XPATH;
356: ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
357: XPATH_EXPRESSION_OK;
358: ctxt->context->lastError.level = XML_ERR_ERROR;
359: ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
360: ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
361: ctxt->context->lastError.node = ctxt->context->debugNode;
362: if (ctxt->context->error != NULL) {
363: ctxt->context->error(ctxt->context->userData,
364: &ctxt->context->lastError);
365: } else {
366: __xmlRaiseError(NULL, NULL, NULL,
367: NULL, ctxt->context->debugNode, XML_FROM_XPATH,
368: error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
369: XML_ERR_ERROR, NULL, 0,
370: (const char *) ctxt->base, NULL, NULL,
371: ctxt->cur - ctxt->base, 0,
372: "%s", xmlXPathErrorMessages[error]);
373: }
374:
375: }
376:
377: /**
378: * xmlXPatherror:
379: * @ctxt: the XPath Parser context
380: * @file: the file name
381: * @line: the line number
382: * @no: the error number
383: *
384: * Formats an error message.
385: */
386: void
387: xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
388: int line ATTRIBUTE_UNUSED, int no) {
389: xmlXPathErr(ctxt, no);
390: }
391:
392: /************************************************************************
393: * *
394: * Utilities *
395: * *
396: ************************************************************************/
397:
398: /**
399: * xsltPointerList:
400: *
401: * Pointer-list for various purposes.
402: */
403: typedef struct _xmlPointerList xmlPointerList;
404: typedef xmlPointerList *xmlPointerListPtr;
405: struct _xmlPointerList {
406: void **items;
407: int number;
408: int size;
409: };
410: /*
411: * TODO: Since such a list-handling is used in xmlschemas.c and libxslt
412: * and here, we should make the functions public.
413: */
414: static int
415: xmlPointerListAddSize(xmlPointerListPtr list,
416: void *item,
417: int initialSize)
418: {
419: if (list->items == NULL) {
420: if (initialSize <= 0)
421: initialSize = 1;
422: list->items = (void **) xmlMalloc(
423: initialSize * sizeof(void *));
424: if (list->items == NULL) {
425: xmlXPathErrMemory(NULL,
426: "xmlPointerListCreate: allocating item\n");
427: return(-1);
428: }
429: list->number = 0;
430: list->size = initialSize;
431: } else if (list->size <= list->number) {
432: list->size *= 2;
433: list->items = (void **) xmlRealloc(list->items,
434: list->size * sizeof(void *));
435: if (list->items == NULL) {
436: xmlXPathErrMemory(NULL,
437: "xmlPointerListCreate: re-allocating item\n");
438: list->size = 0;
439: return(-1);
440: }
441: }
442: list->items[list->number++] = item;
443: return(0);
444: }
445:
446: /**
447: * xsltPointerListCreate:
448: *
449: * Creates an xsltPointerList structure.
450: *
451: * Returns a xsltPointerList structure or NULL in case of an error.
452: */
453: static xmlPointerListPtr
454: xmlPointerListCreate(int initialSize)
455: {
456: xmlPointerListPtr ret;
457:
458: ret = xmlMalloc(sizeof(xmlPointerList));
459: if (ret == NULL) {
460: xmlXPathErrMemory(NULL,
461: "xmlPointerListCreate: allocating item\n");
462: return (NULL);
463: }
464: memset(ret, 0, sizeof(xmlPointerList));
465: if (initialSize > 0) {
466: xmlPointerListAddSize(ret, NULL, initialSize);
467: ret->number = 0;
468: }
469: return (ret);
470: }
471:
472: /**
473: * xsltPointerListFree:
474: *
475: * Frees the xsltPointerList structure. This does not free
476: * the content of the list.
477: */
478: static void
479: xmlPointerListFree(xmlPointerListPtr list)
480: {
481: if (list == NULL)
482: return;
483: if (list->items != NULL)
484: xmlFree(list->items);
485: xmlFree(list);
486: }
487:
488: /************************************************************************
489: * *
490: * Parser Types *
491: * *
492: ************************************************************************/
493:
494: /*
495: * Types are private:
496: */
497:
498: typedef enum {
499: XPATH_OP_END=0,
500: XPATH_OP_AND,
501: XPATH_OP_OR,
502: XPATH_OP_EQUAL,
503: XPATH_OP_CMP,
504: XPATH_OP_PLUS,
505: XPATH_OP_MULT,
506: XPATH_OP_UNION,
507: XPATH_OP_ROOT,
508: XPATH_OP_NODE,
509: XPATH_OP_RESET, /* 10 */
510: XPATH_OP_COLLECT,
511: XPATH_OP_VALUE, /* 12 */
512: XPATH_OP_VARIABLE,
513: XPATH_OP_FUNCTION,
514: XPATH_OP_ARG,
515: XPATH_OP_PREDICATE,
516: XPATH_OP_FILTER, /* 17 */
517: XPATH_OP_SORT /* 18 */
518: #ifdef LIBXML_XPTR_ENABLED
519: ,XPATH_OP_RANGETO
520: #endif
521: } xmlXPathOp;
522:
523: typedef enum {
524: AXIS_ANCESTOR = 1,
525: AXIS_ANCESTOR_OR_SELF,
526: AXIS_ATTRIBUTE,
527: AXIS_CHILD,
528: AXIS_DESCENDANT,
529: AXIS_DESCENDANT_OR_SELF,
530: AXIS_FOLLOWING,
531: AXIS_FOLLOWING_SIBLING,
532: AXIS_NAMESPACE,
533: AXIS_PARENT,
534: AXIS_PRECEDING,
535: AXIS_PRECEDING_SIBLING,
536: AXIS_SELF
537: } xmlXPathAxisVal;
538:
539: typedef enum {
540: NODE_TEST_NONE = 0,
541: NODE_TEST_TYPE = 1,
542: NODE_TEST_PI = 2,
543: NODE_TEST_ALL = 3,
544: NODE_TEST_NS = 4,
545: NODE_TEST_NAME = 5
546: } xmlXPathTestVal;
547:
548: typedef enum {
549: NODE_TYPE_NODE = 0,
550: NODE_TYPE_COMMENT = XML_COMMENT_NODE,
551: NODE_TYPE_TEXT = XML_TEXT_NODE,
552: NODE_TYPE_PI = XML_PI_NODE
553: } xmlXPathTypeVal;
554:
555: #define XP_REWRITE_DOS_CHILD_ELEM 1
556:
557: typedef struct _xmlXPathStepOp xmlXPathStepOp;
558: typedef xmlXPathStepOp *xmlXPathStepOpPtr;
559: struct _xmlXPathStepOp {
560: xmlXPathOp op; /* The identifier of the operation */
561: int ch1; /* First child */
562: int ch2; /* Second child */
563: int value;
564: int value2;
565: int value3;
566: void *value4;
567: void *value5;
568: void *cache;
569: void *cacheURI;
570: int rewriteType;
571: };
572:
573: struct _xmlXPathCompExpr {
574: int nbStep; /* Number of steps in this expression */
575: int maxStep; /* Maximum number of steps allocated */
576: xmlXPathStepOp *steps; /* ops for computation of this expression */
577: int last; /* index of last step in expression */
578: xmlChar *expr; /* the expression being computed */
579: xmlDictPtr dict; /* the dictionnary to use if any */
580: #ifdef DEBUG_EVAL_COUNTS
581: int nb;
582: xmlChar *string;
583: #endif
584: #ifdef XPATH_STREAMING
585: xmlPatternPtr stream;
586: #endif
587: };
588:
589: /************************************************************************
590: * *
591: * Forward declarations *
592: * *
593: ************************************************************************/
594: static void
595: xmlXPathFreeValueTree(xmlNodeSetPtr obj);
596: static void
597: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
598: static int
599: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
600: xmlXPathStepOpPtr op, xmlNodePtr *first);
601: static int
602: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
603: xmlXPathStepOpPtr op,
604: int isPredicate);
605:
606: /************************************************************************
607: * *
608: * Parser Type functions *
609: * *
610: ************************************************************************/
611:
612: /**
613: * xmlXPathNewCompExpr:
614: *
615: * Create a new Xpath component
616: *
617: * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
618: */
619: static xmlXPathCompExprPtr
620: xmlXPathNewCompExpr(void) {
621: xmlXPathCompExprPtr cur;
622:
623: cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
624: if (cur == NULL) {
625: xmlXPathErrMemory(NULL, "allocating component\n");
626: return(NULL);
627: }
628: memset(cur, 0, sizeof(xmlXPathCompExpr));
629: cur->maxStep = 10;
630: cur->nbStep = 0;
631: cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
632: sizeof(xmlXPathStepOp));
633: if (cur->steps == NULL) {
634: xmlXPathErrMemory(NULL, "allocating steps\n");
635: xmlFree(cur);
636: return(NULL);
637: }
638: memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
639: cur->last = -1;
640: #ifdef DEBUG_EVAL_COUNTS
641: cur->nb = 0;
642: #endif
643: return(cur);
644: }
645:
646: /**
647: * xmlXPathFreeCompExpr:
648: * @comp: an XPATH comp
649: *
650: * Free up the memory allocated by @comp
651: */
652: void
653: xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
654: {
655: xmlXPathStepOpPtr op;
656: int i;
657:
658: if (comp == NULL)
659: return;
660: if (comp->dict == NULL) {
661: for (i = 0; i < comp->nbStep; i++) {
662: op = &comp->steps[i];
663: if (op->value4 != NULL) {
664: if (op->op == XPATH_OP_VALUE)
665: xmlXPathFreeObject(op->value4);
666: else
667: xmlFree(op->value4);
668: }
669: if (op->value5 != NULL)
670: xmlFree(op->value5);
671: }
672: } else {
673: for (i = 0; i < comp->nbStep; i++) {
674: op = &comp->steps[i];
675: if (op->value4 != NULL) {
676: if (op->op == XPATH_OP_VALUE)
677: xmlXPathFreeObject(op->value4);
678: }
679: }
680: xmlDictFree(comp->dict);
681: }
682: if (comp->steps != NULL) {
683: xmlFree(comp->steps);
684: }
685: #ifdef DEBUG_EVAL_COUNTS
686: if (comp->string != NULL) {
687: xmlFree(comp->string);
688: }
689: #endif
690: #ifdef XPATH_STREAMING
691: if (comp->stream != NULL) {
692: xmlFreePatternList(comp->stream);
693: }
694: #endif
695: if (comp->expr != NULL) {
696: xmlFree(comp->expr);
697: }
698:
699: xmlFree(comp);
700: }
701:
702: /**
703: * xmlXPathCompExprAdd:
704: * @comp: the compiled expression
705: * @ch1: first child index
706: * @ch2: second child index
707: * @op: an op
708: * @value: the first int value
709: * @value2: the second int value
710: * @value3: the third int value
711: * @value4: the first string value
712: * @value5: the second string value
713: *
714: * Add a step to an XPath Compiled Expression
715: *
716: * Returns -1 in case of failure, the index otherwise
717: */
718: static int
719: xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
720: xmlXPathOp op, int value,
721: int value2, int value3, void *value4, void *value5) {
722: if (comp->nbStep >= comp->maxStep) {
723: xmlXPathStepOp *real;
724:
725: comp->maxStep *= 2;
726: real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
727: comp->maxStep * sizeof(xmlXPathStepOp));
728: if (real == NULL) {
729: comp->maxStep /= 2;
730: xmlXPathErrMemory(NULL, "adding step\n");
731: return(-1);
732: }
733: comp->steps = real;
734: }
735: comp->last = comp->nbStep;
736: comp->steps[comp->nbStep].rewriteType = 0;
737: comp->steps[comp->nbStep].ch1 = ch1;
738: comp->steps[comp->nbStep].ch2 = ch2;
739: comp->steps[comp->nbStep].op = op;
740: comp->steps[comp->nbStep].value = value;
741: comp->steps[comp->nbStep].value2 = value2;
742: comp->steps[comp->nbStep].value3 = value3;
743: if ((comp->dict != NULL) &&
744: ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
745: (op == XPATH_OP_COLLECT))) {
746: if (value4 != NULL) {
747: comp->steps[comp->nbStep].value4 = (xmlChar *)
748: (void *)xmlDictLookup(comp->dict, value4, -1);
749: xmlFree(value4);
750: } else
751: comp->steps[comp->nbStep].value4 = NULL;
752: if (value5 != NULL) {
753: comp->steps[comp->nbStep].value5 = (xmlChar *)
754: (void *)xmlDictLookup(comp->dict, value5, -1);
755: xmlFree(value5);
756: } else
757: comp->steps[comp->nbStep].value5 = NULL;
758: } else {
759: comp->steps[comp->nbStep].value4 = value4;
760: comp->steps[comp->nbStep].value5 = value5;
761: }
762: comp->steps[comp->nbStep].cache = NULL;
763: return(comp->nbStep++);
764: }
765:
766: /**
767: * xmlXPathCompSwap:
768: * @comp: the compiled expression
769: * @op: operation index
770: *
771: * Swaps 2 operations in the compiled expression
772: */
773: static void
774: xmlXPathCompSwap(xmlXPathStepOpPtr op) {
775: int tmp;
776:
777: #ifndef LIBXML_THREAD_ENABLED
778: /*
779: * Since this manipulates possibly shared variables, this is
780: * disabled if one detects that the library is used in a multithreaded
781: * application
782: */
783: if (xmlXPathDisableOptimizer)
784: return;
785: #endif
786:
787: tmp = op->ch1;
788: op->ch1 = op->ch2;
789: op->ch2 = tmp;
790: }
791:
792: #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
793: xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
794: (op), (val), (val2), (val3), (val4), (val5))
795: #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
796: xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
797: (op), (val), (val2), (val3), (val4), (val5))
798:
799: #define PUSH_LEAVE_EXPR(op, val, val2) \
800: xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
801:
802: #define PUSH_UNARY_EXPR(op, ch, val, val2) \
803: xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
804:
805: #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
806: xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \
807: (val), (val2), 0 ,NULL ,NULL)
808:
809: /************************************************************************
810: * *
811: * XPath object cache structures *
812: * *
813: ************************************************************************/
814:
815: /* #define XP_DEFAULT_CACHE_ON */
816:
817: #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
818:
819: typedef struct _xmlXPathContextCache xmlXPathContextCache;
820: typedef xmlXPathContextCache *xmlXPathContextCachePtr;
821: struct _xmlXPathContextCache {
822: xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
823: xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
824: xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
825: xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
826: xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
827: int maxNodeset;
828: int maxString;
829: int maxBoolean;
830: int maxNumber;
831: int maxMisc;
832: #ifdef XP_DEBUG_OBJ_USAGE
833: int dbgCachedAll;
834: int dbgCachedNodeset;
835: int dbgCachedString;
836: int dbgCachedBool;
837: int dbgCachedNumber;
838: int dbgCachedPoint;
839: int dbgCachedRange;
840: int dbgCachedLocset;
841: int dbgCachedUsers;
842: int dbgCachedXSLTTree;
843: int dbgCachedUndefined;
844:
845:
846: int dbgReusedAll;
847: int dbgReusedNodeset;
848: int dbgReusedString;
849: int dbgReusedBool;
850: int dbgReusedNumber;
851: int dbgReusedPoint;
852: int dbgReusedRange;
853: int dbgReusedLocset;
854: int dbgReusedUsers;
855: int dbgReusedXSLTTree;
856: int dbgReusedUndefined;
857:
858: #endif
859: };
860:
861: /************************************************************************
862: * *
863: * Debugging related functions *
864: * *
865: ************************************************************************/
866:
867: #define STRANGE \
868: xmlGenericError(xmlGenericErrorContext, \
869: "Internal error at %s:%d\n", \
870: __FILE__, __LINE__);
871:
872: #ifdef LIBXML_DEBUG_ENABLED
873: static void
874: xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
875: int i;
876: char shift[100];
877:
878: for (i = 0;((i < depth) && (i < 25));i++)
879: shift[2 * i] = shift[2 * i + 1] = ' ';
880: shift[2 * i] = shift[2 * i + 1] = 0;
881: if (cur == NULL) {
882: fprintf(output, "%s", shift);
883: fprintf(output, "Node is NULL !\n");
884: return;
885:
886: }
887:
888: if ((cur->type == XML_DOCUMENT_NODE) ||
889: (cur->type == XML_HTML_DOCUMENT_NODE)) {
890: fprintf(output, "%s", shift);
891: fprintf(output, " /\n");
892: } else if (cur->type == XML_ATTRIBUTE_NODE)
893: xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
894: else
895: xmlDebugDumpOneNode(output, cur, depth);
896: }
897: static void
898: xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
899: xmlNodePtr tmp;
900: int i;
901: char shift[100];
902:
903: for (i = 0;((i < depth) && (i < 25));i++)
904: shift[2 * i] = shift[2 * i + 1] = ' ';
905: shift[2 * i] = shift[2 * i + 1] = 0;
906: if (cur == NULL) {
907: fprintf(output, "%s", shift);
908: fprintf(output, "Node is NULL !\n");
909: return;
910:
911: }
912:
913: while (cur != NULL) {
914: tmp = cur;
915: cur = cur->next;
916: xmlDebugDumpOneNode(output, tmp, depth);
917: }
918: }
919:
920: static void
921: xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
922: int i;
923: char shift[100];
924:
925: for (i = 0;((i < depth) && (i < 25));i++)
926: shift[2 * i] = shift[2 * i + 1] = ' ';
927: shift[2 * i] = shift[2 * i + 1] = 0;
928:
929: if (cur == NULL) {
930: fprintf(output, "%s", shift);
931: fprintf(output, "NodeSet is NULL !\n");
932: return;
933:
934: }
935:
936: if (cur != NULL) {
937: fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
938: for (i = 0;i < cur->nodeNr;i++) {
939: fprintf(output, "%s", shift);
940: fprintf(output, "%d", i + 1);
941: xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
942: }
943: }
944: }
945:
946: static void
947: xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
948: int i;
949: char shift[100];
950:
951: for (i = 0;((i < depth) && (i < 25));i++)
952: shift[2 * i] = shift[2 * i + 1] = ' ';
953: shift[2 * i] = shift[2 * i + 1] = 0;
954:
955: if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
956: fprintf(output, "%s", shift);
957: fprintf(output, "Value Tree is NULL !\n");
958: return;
959:
960: }
961:
962: fprintf(output, "%s", shift);
963: fprintf(output, "%d", i + 1);
964: xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
965: }
966: #if defined(LIBXML_XPTR_ENABLED)
967: static void
968: xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
969: int i;
970: char shift[100];
971:
972: for (i = 0;((i < depth) && (i < 25));i++)
973: shift[2 * i] = shift[2 * i + 1] = ' ';
974: shift[2 * i] = shift[2 * i + 1] = 0;
975:
976: if (cur == NULL) {
977: fprintf(output, "%s", shift);
978: fprintf(output, "LocationSet is NULL !\n");
979: return;
980:
981: }
982:
983: for (i = 0;i < cur->locNr;i++) {
984: fprintf(output, "%s", shift);
985: fprintf(output, "%d : ", i + 1);
986: xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
987: }
988: }
989: #endif /* LIBXML_XPTR_ENABLED */
990:
991: /**
992: * xmlXPathDebugDumpObject:
993: * @output: the FILE * to dump the output
994: * @cur: the object to inspect
995: * @depth: indentation level
996: *
997: * Dump the content of the object for debugging purposes
998: */
999: void
1000: xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
1001: int i;
1002: char shift[100];
1003:
1004: if (output == NULL) return;
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:
1011: fprintf(output, "%s", shift);
1012:
1013: if (cur == NULL) {
1014: fprintf(output, "Object is empty (NULL)\n");
1015: return;
1016: }
1017: switch(cur->type) {
1018: case XPATH_UNDEFINED:
1019: fprintf(output, "Object is uninitialized\n");
1020: break;
1021: case XPATH_NODESET:
1022: fprintf(output, "Object is a Node Set :\n");
1023: xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
1024: break;
1025: case XPATH_XSLT_TREE:
1026: fprintf(output, "Object is an XSLT value tree :\n");
1027: xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
1028: break;
1029: case XPATH_BOOLEAN:
1030: fprintf(output, "Object is a Boolean : ");
1031: if (cur->boolval) fprintf(output, "true\n");
1032: else fprintf(output, "false\n");
1033: break;
1034: case XPATH_NUMBER:
1035: switch (xmlXPathIsInf(cur->floatval)) {
1036: case 1:
1037: fprintf(output, "Object is a number : Infinity\n");
1038: break;
1039: case -1:
1040: fprintf(output, "Object is a number : -Infinity\n");
1041: break;
1042: default:
1043: if (xmlXPathIsNaN(cur->floatval)) {
1044: fprintf(output, "Object is a number : NaN\n");
1045: } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) {
1046: fprintf(output, "Object is a number : 0\n");
1047: } else {
1048: fprintf(output, "Object is a number : %0g\n", cur->floatval);
1049: }
1050: }
1051: break;
1052: case XPATH_STRING:
1053: fprintf(output, "Object is a string : ");
1054: xmlDebugDumpString(output, cur->stringval);
1055: fprintf(output, "\n");
1056: break;
1057: case XPATH_POINT:
1058: fprintf(output, "Object is a point : index %d in node", cur->index);
1059: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
1060: fprintf(output, "\n");
1061: break;
1062: case XPATH_RANGE:
1063: if ((cur->user2 == NULL) ||
1064: ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
1065: fprintf(output, "Object is a collapsed range :\n");
1066: fprintf(output, "%s", shift);
1067: if (cur->index >= 0)
1068: fprintf(output, "index %d in ", cur->index);
1069: fprintf(output, "node\n");
1070: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1071: depth + 1);
1072: } else {
1073: fprintf(output, "Object is a range :\n");
1074: fprintf(output, "%s", shift);
1075: fprintf(output, "From ");
1076: if (cur->index >= 0)
1077: fprintf(output, "index %d in ", cur->index);
1078: fprintf(output, "node\n");
1079: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
1080: depth + 1);
1081: fprintf(output, "%s", shift);
1082: fprintf(output, "To ");
1083: if (cur->index2 >= 0)
1084: fprintf(output, "index %d in ", cur->index2);
1085: fprintf(output, "node\n");
1086: xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
1087: depth + 1);
1088: fprintf(output, "\n");
1089: }
1090: break;
1091: case XPATH_LOCATIONSET:
1092: #if defined(LIBXML_XPTR_ENABLED)
1093: fprintf(output, "Object is a Location Set:\n");
1094: xmlXPathDebugDumpLocationSet(output,
1095: (xmlLocationSetPtr) cur->user, depth);
1096: #endif
1097: break;
1098: case XPATH_USERS:
1099: fprintf(output, "Object is user defined\n");
1100: break;
1101: }
1102: }
1103:
1104: static void
1105: xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
1106: xmlXPathStepOpPtr op, int depth) {
1107: int i;
1108: char shift[100];
1109:
1110: for (i = 0;((i < depth) && (i < 25));i++)
1111: shift[2 * i] = shift[2 * i + 1] = ' ';
1112: shift[2 * i] = shift[2 * i + 1] = 0;
1113:
1114: fprintf(output, "%s", shift);
1115: if (op == NULL) {
1116: fprintf(output, "Step is NULL\n");
1117: return;
1118: }
1119: switch (op->op) {
1120: case XPATH_OP_END:
1121: fprintf(output, "END"); break;
1122: case XPATH_OP_AND:
1123: fprintf(output, "AND"); break;
1124: case XPATH_OP_OR:
1125: fprintf(output, "OR"); break;
1126: case XPATH_OP_EQUAL:
1127: if (op->value)
1128: fprintf(output, "EQUAL =");
1129: else
1130: fprintf(output, "EQUAL !=");
1131: break;
1132: case XPATH_OP_CMP:
1133: if (op->value)
1134: fprintf(output, "CMP <");
1135: else
1136: fprintf(output, "CMP >");
1137: if (!op->value2)
1138: fprintf(output, "=");
1139: break;
1140: case XPATH_OP_PLUS:
1141: if (op->value == 0)
1142: fprintf(output, "PLUS -");
1143: else if (op->value == 1)
1144: fprintf(output, "PLUS +");
1145: else if (op->value == 2)
1146: fprintf(output, "PLUS unary -");
1147: else if (op->value == 3)
1148: fprintf(output, "PLUS unary - -");
1149: break;
1150: case XPATH_OP_MULT:
1151: if (op->value == 0)
1152: fprintf(output, "MULT *");
1153: else if (op->value == 1)
1154: fprintf(output, "MULT div");
1155: else
1156: fprintf(output, "MULT mod");
1157: break;
1158: case XPATH_OP_UNION:
1159: fprintf(output, "UNION"); break;
1160: case XPATH_OP_ROOT:
1161: fprintf(output, "ROOT"); break;
1162: case XPATH_OP_NODE:
1163: fprintf(output, "NODE"); break;
1164: case XPATH_OP_RESET:
1165: fprintf(output, "RESET"); break;
1166: case XPATH_OP_SORT:
1167: fprintf(output, "SORT"); break;
1168: case XPATH_OP_COLLECT: {
1169: xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value;
1170: xmlXPathTestVal test = (xmlXPathTestVal)op->value2;
1171: xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3;
1172: const xmlChar *prefix = op->value4;
1173: const xmlChar *name = op->value5;
1174:
1175: fprintf(output, "COLLECT ");
1176: switch (axis) {
1177: case AXIS_ANCESTOR:
1178: fprintf(output, " 'ancestors' "); break;
1179: case AXIS_ANCESTOR_OR_SELF:
1180: fprintf(output, " 'ancestors-or-self' "); break;
1181: case AXIS_ATTRIBUTE:
1182: fprintf(output, " 'attributes' "); break;
1183: case AXIS_CHILD:
1184: fprintf(output, " 'child' "); break;
1185: case AXIS_DESCENDANT:
1186: fprintf(output, " 'descendant' "); break;
1187: case AXIS_DESCENDANT_OR_SELF:
1188: fprintf(output, " 'descendant-or-self' "); break;
1189: case AXIS_FOLLOWING:
1190: fprintf(output, " 'following' "); break;
1191: case AXIS_FOLLOWING_SIBLING:
1192: fprintf(output, " 'following-siblings' "); break;
1193: case AXIS_NAMESPACE:
1194: fprintf(output, " 'namespace' "); break;
1195: case AXIS_PARENT:
1196: fprintf(output, " 'parent' "); break;
1197: case AXIS_PRECEDING:
1198: fprintf(output, " 'preceding' "); break;
1199: case AXIS_PRECEDING_SIBLING:
1200: fprintf(output, " 'preceding-sibling' "); break;
1201: case AXIS_SELF:
1202: fprintf(output, " 'self' "); break;
1203: }
1204: switch (test) {
1205: case NODE_TEST_NONE:
1206: fprintf(output, "'none' "); break;
1207: case NODE_TEST_TYPE:
1208: fprintf(output, "'type' "); break;
1209: case NODE_TEST_PI:
1210: fprintf(output, "'PI' "); break;
1211: case NODE_TEST_ALL:
1212: fprintf(output, "'all' "); break;
1213: case NODE_TEST_NS:
1214: fprintf(output, "'namespace' "); break;
1215: case NODE_TEST_NAME:
1216: fprintf(output, "'name' "); break;
1217: }
1218: switch (type) {
1219: case NODE_TYPE_NODE:
1220: fprintf(output, "'node' "); break;
1221: case NODE_TYPE_COMMENT:
1222: fprintf(output, "'comment' "); break;
1223: case NODE_TYPE_TEXT:
1224: fprintf(output, "'text' "); break;
1225: case NODE_TYPE_PI:
1226: fprintf(output, "'PI' "); break;
1227: }
1228: if (prefix != NULL)
1229: fprintf(output, "%s:", prefix);
1230: if (name != NULL)
1231: fprintf(output, "%s", (const char *) name);
1232: break;
1233:
1234: }
1235: case XPATH_OP_VALUE: {
1236: xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
1237:
1238: fprintf(output, "ELEM ");
1239: xmlXPathDebugDumpObject(output, object, 0);
1240: goto finish;
1241: }
1242: case XPATH_OP_VARIABLE: {
1243: const xmlChar *prefix = op->value5;
1244: const xmlChar *name = op->value4;
1245:
1246: if (prefix != NULL)
1247: fprintf(output, "VARIABLE %s:%s", prefix, name);
1248: else
1249: fprintf(output, "VARIABLE %s", name);
1250: break;
1251: }
1252: case XPATH_OP_FUNCTION: {
1253: int nbargs = op->value;
1254: const xmlChar *prefix = op->value5;
1255: const xmlChar *name = op->value4;
1256:
1257: if (prefix != NULL)
1258: fprintf(output, "FUNCTION %s:%s(%d args)",
1259: prefix, name, nbargs);
1260: else
1261: fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
1262: break;
1263: }
1264: case XPATH_OP_ARG: fprintf(output, "ARG"); break;
1265: case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
1266: case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
1267: #ifdef LIBXML_XPTR_ENABLED
1268: case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
1269: #endif
1270: default:
1271: fprintf(output, "UNKNOWN %d\n", op->op); return;
1272: }
1273: fprintf(output, "\n");
1274: finish:
1275: if (op->ch1 >= 0)
1276: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
1277: if (op->ch2 >= 0)
1278: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
1279: }
1280:
1281: /**
1282: * xmlXPathDebugDumpCompExpr:
1283: * @output: the FILE * for the output
1284: * @comp: the precompiled XPath expression
1285: * @depth: the indentation level.
1286: *
1287: * Dumps the tree of the compiled XPath expression.
1288: */
1289: void
1290: xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
1291: int depth) {
1292: int i;
1293: char shift[100];
1294:
1295: if ((output == NULL) || (comp == NULL)) return;
1296:
1297: for (i = 0;((i < depth) && (i < 25));i++)
1298: shift[2 * i] = shift[2 * i + 1] = ' ';
1299: shift[2 * i] = shift[2 * i + 1] = 0;
1300:
1301: fprintf(output, "%s", shift);
1302:
1303: fprintf(output, "Compiled Expression : %d elements\n",
1304: comp->nbStep);
1305: i = comp->last;
1306: xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
1307: }
1308:
1309: #ifdef XP_DEBUG_OBJ_USAGE
1310:
1311: /*
1312: * XPath object usage related debugging variables.
1313: */
1314: static int xmlXPathDebugObjCounterUndefined = 0;
1315: static int xmlXPathDebugObjCounterNodeset = 0;
1316: static int xmlXPathDebugObjCounterBool = 0;
1317: static int xmlXPathDebugObjCounterNumber = 0;
1318: static int xmlXPathDebugObjCounterString = 0;
1319: static int xmlXPathDebugObjCounterPoint = 0;
1320: static int xmlXPathDebugObjCounterRange = 0;
1321: static int xmlXPathDebugObjCounterLocset = 0;
1322: static int xmlXPathDebugObjCounterUsers = 0;
1323: static int xmlXPathDebugObjCounterXSLTTree = 0;
1324: static int xmlXPathDebugObjCounterAll = 0;
1325:
1326: static int xmlXPathDebugObjTotalUndefined = 0;
1327: static int xmlXPathDebugObjTotalNodeset = 0;
1328: static int xmlXPathDebugObjTotalBool = 0;
1329: static int xmlXPathDebugObjTotalNumber = 0;
1330: static int xmlXPathDebugObjTotalString = 0;
1331: static int xmlXPathDebugObjTotalPoint = 0;
1332: static int xmlXPathDebugObjTotalRange = 0;
1333: static int xmlXPathDebugObjTotalLocset = 0;
1334: static int xmlXPathDebugObjTotalUsers = 0;
1335: static int xmlXPathDebugObjTotalXSLTTree = 0;
1336: static int xmlXPathDebugObjTotalAll = 0;
1337:
1338: static int xmlXPathDebugObjMaxUndefined = 0;
1339: static int xmlXPathDebugObjMaxNodeset = 0;
1340: static int xmlXPathDebugObjMaxBool = 0;
1341: static int xmlXPathDebugObjMaxNumber = 0;
1342: static int xmlXPathDebugObjMaxString = 0;
1343: static int xmlXPathDebugObjMaxPoint = 0;
1344: static int xmlXPathDebugObjMaxRange = 0;
1345: static int xmlXPathDebugObjMaxLocset = 0;
1346: static int xmlXPathDebugObjMaxUsers = 0;
1347: static int xmlXPathDebugObjMaxXSLTTree = 0;
1348: static int xmlXPathDebugObjMaxAll = 0;
1349:
1350: /* REVISIT TODO: Make this static when committing */
1351: static void
1352: xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
1353: {
1354: if (ctxt != NULL) {
1355: if (ctxt->cache != NULL) {
1356: xmlXPathContextCachePtr cache =
1357: (xmlXPathContextCachePtr) ctxt->cache;
1358:
1359: cache->dbgCachedAll = 0;
1360: cache->dbgCachedNodeset = 0;
1361: cache->dbgCachedString = 0;
1362: cache->dbgCachedBool = 0;
1363: cache->dbgCachedNumber = 0;
1364: cache->dbgCachedPoint = 0;
1365: cache->dbgCachedRange = 0;
1366: cache->dbgCachedLocset = 0;
1367: cache->dbgCachedUsers = 0;
1368: cache->dbgCachedXSLTTree = 0;
1369: cache->dbgCachedUndefined = 0;
1370:
1371: cache->dbgReusedAll = 0;
1372: cache->dbgReusedNodeset = 0;
1373: cache->dbgReusedString = 0;
1374: cache->dbgReusedBool = 0;
1375: cache->dbgReusedNumber = 0;
1376: cache->dbgReusedPoint = 0;
1377: cache->dbgReusedRange = 0;
1378: cache->dbgReusedLocset = 0;
1379: cache->dbgReusedUsers = 0;
1380: cache->dbgReusedXSLTTree = 0;
1381: cache->dbgReusedUndefined = 0;
1382: }
1383: }
1384:
1385: xmlXPathDebugObjCounterUndefined = 0;
1386: xmlXPathDebugObjCounterNodeset = 0;
1387: xmlXPathDebugObjCounterBool = 0;
1388: xmlXPathDebugObjCounterNumber = 0;
1389: xmlXPathDebugObjCounterString = 0;
1390: xmlXPathDebugObjCounterPoint = 0;
1391: xmlXPathDebugObjCounterRange = 0;
1392: xmlXPathDebugObjCounterLocset = 0;
1393: xmlXPathDebugObjCounterUsers = 0;
1394: xmlXPathDebugObjCounterXSLTTree = 0;
1395: xmlXPathDebugObjCounterAll = 0;
1396:
1397: xmlXPathDebugObjTotalUndefined = 0;
1398: xmlXPathDebugObjTotalNodeset = 0;
1399: xmlXPathDebugObjTotalBool = 0;
1400: xmlXPathDebugObjTotalNumber = 0;
1401: xmlXPathDebugObjTotalString = 0;
1402: xmlXPathDebugObjTotalPoint = 0;
1403: xmlXPathDebugObjTotalRange = 0;
1404: xmlXPathDebugObjTotalLocset = 0;
1405: xmlXPathDebugObjTotalUsers = 0;
1406: xmlXPathDebugObjTotalXSLTTree = 0;
1407: xmlXPathDebugObjTotalAll = 0;
1408:
1409: xmlXPathDebugObjMaxUndefined = 0;
1410: xmlXPathDebugObjMaxNodeset = 0;
1411: xmlXPathDebugObjMaxBool = 0;
1412: xmlXPathDebugObjMaxNumber = 0;
1413: xmlXPathDebugObjMaxString = 0;
1414: xmlXPathDebugObjMaxPoint = 0;
1415: xmlXPathDebugObjMaxRange = 0;
1416: xmlXPathDebugObjMaxLocset = 0;
1417: xmlXPathDebugObjMaxUsers = 0;
1418: xmlXPathDebugObjMaxXSLTTree = 0;
1419: xmlXPathDebugObjMaxAll = 0;
1420:
1421: }
1422:
1423: static void
1424: xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
1425: xmlXPathObjectType objType)
1426: {
1427: int isCached = 0;
1428:
1429: if (ctxt != NULL) {
1430: if (ctxt->cache != NULL) {
1431: xmlXPathContextCachePtr cache =
1432: (xmlXPathContextCachePtr) ctxt->cache;
1433:
1434: isCached = 1;
1435:
1436: cache->dbgReusedAll++;
1437: switch (objType) {
1438: case XPATH_UNDEFINED:
1439: cache->dbgReusedUndefined++;
1440: break;
1441: case XPATH_NODESET:
1442: cache->dbgReusedNodeset++;
1443: break;
1444: case XPATH_BOOLEAN:
1445: cache->dbgReusedBool++;
1446: break;
1447: case XPATH_NUMBER:
1448: cache->dbgReusedNumber++;
1449: break;
1450: case XPATH_STRING:
1451: cache->dbgReusedString++;
1452: break;
1453: case XPATH_POINT:
1454: cache->dbgReusedPoint++;
1455: break;
1456: case XPATH_RANGE:
1457: cache->dbgReusedRange++;
1458: break;
1459: case XPATH_LOCATIONSET:
1460: cache->dbgReusedLocset++;
1461: break;
1462: case XPATH_USERS:
1463: cache->dbgReusedUsers++;
1464: break;
1465: case XPATH_XSLT_TREE:
1466: cache->dbgReusedXSLTTree++;
1467: break;
1468: default:
1469: break;
1470: }
1471: }
1472: }
1473:
1474: switch (objType) {
1475: case XPATH_UNDEFINED:
1476: if (! isCached)
1477: xmlXPathDebugObjTotalUndefined++;
1478: xmlXPathDebugObjCounterUndefined++;
1479: if (xmlXPathDebugObjCounterUndefined >
1480: xmlXPathDebugObjMaxUndefined)
1481: xmlXPathDebugObjMaxUndefined =
1482: xmlXPathDebugObjCounterUndefined;
1483: break;
1484: case XPATH_NODESET:
1485: if (! isCached)
1486: xmlXPathDebugObjTotalNodeset++;
1487: xmlXPathDebugObjCounterNodeset++;
1488: if (xmlXPathDebugObjCounterNodeset >
1489: xmlXPathDebugObjMaxNodeset)
1490: xmlXPathDebugObjMaxNodeset =
1491: xmlXPathDebugObjCounterNodeset;
1492: break;
1493: case XPATH_BOOLEAN:
1494: if (! isCached)
1495: xmlXPathDebugObjTotalBool++;
1496: xmlXPathDebugObjCounterBool++;
1497: if (xmlXPathDebugObjCounterBool >
1498: xmlXPathDebugObjMaxBool)
1499: xmlXPathDebugObjMaxBool =
1500: xmlXPathDebugObjCounterBool;
1501: break;
1502: case XPATH_NUMBER:
1503: if (! isCached)
1504: xmlXPathDebugObjTotalNumber++;
1505: xmlXPathDebugObjCounterNumber++;
1506: if (xmlXPathDebugObjCounterNumber >
1507: xmlXPathDebugObjMaxNumber)
1508: xmlXPathDebugObjMaxNumber =
1509: xmlXPathDebugObjCounterNumber;
1510: break;
1511: case XPATH_STRING:
1512: if (! isCached)
1513: xmlXPathDebugObjTotalString++;
1514: xmlXPathDebugObjCounterString++;
1515: if (xmlXPathDebugObjCounterString >
1516: xmlXPathDebugObjMaxString)
1517: xmlXPathDebugObjMaxString =
1518: xmlXPathDebugObjCounterString;
1519: break;
1520: case XPATH_POINT:
1521: if (! isCached)
1522: xmlXPathDebugObjTotalPoint++;
1523: xmlXPathDebugObjCounterPoint++;
1524: if (xmlXPathDebugObjCounterPoint >
1525: xmlXPathDebugObjMaxPoint)
1526: xmlXPathDebugObjMaxPoint =
1527: xmlXPathDebugObjCounterPoint;
1528: break;
1529: case XPATH_RANGE:
1530: if (! isCached)
1531: xmlXPathDebugObjTotalRange++;
1532: xmlXPathDebugObjCounterRange++;
1533: if (xmlXPathDebugObjCounterRange >
1534: xmlXPathDebugObjMaxRange)
1535: xmlXPathDebugObjMaxRange =
1536: xmlXPathDebugObjCounterRange;
1537: break;
1538: case XPATH_LOCATIONSET:
1539: if (! isCached)
1540: xmlXPathDebugObjTotalLocset++;
1541: xmlXPathDebugObjCounterLocset++;
1542: if (xmlXPathDebugObjCounterLocset >
1543: xmlXPathDebugObjMaxLocset)
1544: xmlXPathDebugObjMaxLocset =
1545: xmlXPathDebugObjCounterLocset;
1546: break;
1547: case XPATH_USERS:
1548: if (! isCached)
1549: xmlXPathDebugObjTotalUsers++;
1550: xmlXPathDebugObjCounterUsers++;
1551: if (xmlXPathDebugObjCounterUsers >
1552: xmlXPathDebugObjMaxUsers)
1553: xmlXPathDebugObjMaxUsers =
1554: xmlXPathDebugObjCounterUsers;
1555: break;
1556: case XPATH_XSLT_TREE:
1557: if (! isCached)
1558: xmlXPathDebugObjTotalXSLTTree++;
1559: xmlXPathDebugObjCounterXSLTTree++;
1560: if (xmlXPathDebugObjCounterXSLTTree >
1561: xmlXPathDebugObjMaxXSLTTree)
1562: xmlXPathDebugObjMaxXSLTTree =
1563: xmlXPathDebugObjCounterXSLTTree;
1564: break;
1565: default:
1566: break;
1567: }
1568: if (! isCached)
1569: xmlXPathDebugObjTotalAll++;
1570: xmlXPathDebugObjCounterAll++;
1571: if (xmlXPathDebugObjCounterAll >
1572: xmlXPathDebugObjMaxAll)
1573: xmlXPathDebugObjMaxAll =
1574: xmlXPathDebugObjCounterAll;
1575: }
1576:
1577: static void
1578: xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
1579: xmlXPathObjectType objType)
1580: {
1581: int isCached = 0;
1582:
1583: if (ctxt != NULL) {
1584: if (ctxt->cache != NULL) {
1585: xmlXPathContextCachePtr cache =
1586: (xmlXPathContextCachePtr) ctxt->cache;
1587:
1588: isCached = 1;
1589:
1590: cache->dbgCachedAll++;
1591: switch (objType) {
1592: case XPATH_UNDEFINED:
1593: cache->dbgCachedUndefined++;
1594: break;
1595: case XPATH_NODESET:
1596: cache->dbgCachedNodeset++;
1597: break;
1598: case XPATH_BOOLEAN:
1599: cache->dbgCachedBool++;
1600: break;
1601: case XPATH_NUMBER:
1602: cache->dbgCachedNumber++;
1603: break;
1604: case XPATH_STRING:
1605: cache->dbgCachedString++;
1606: break;
1607: case XPATH_POINT:
1608: cache->dbgCachedPoint++;
1609: break;
1610: case XPATH_RANGE:
1611: cache->dbgCachedRange++;
1612: break;
1613: case XPATH_LOCATIONSET:
1614: cache->dbgCachedLocset++;
1615: break;
1616: case XPATH_USERS:
1617: cache->dbgCachedUsers++;
1618: break;
1619: case XPATH_XSLT_TREE:
1620: cache->dbgCachedXSLTTree++;
1621: break;
1622: default:
1623: break;
1624: }
1625:
1626: }
1627: }
1628: switch (objType) {
1629: case XPATH_UNDEFINED:
1630: xmlXPathDebugObjCounterUndefined--;
1631: break;
1632: case XPATH_NODESET:
1633: xmlXPathDebugObjCounterNodeset--;
1634: break;
1635: case XPATH_BOOLEAN:
1636: xmlXPathDebugObjCounterBool--;
1637: break;
1638: case XPATH_NUMBER:
1639: xmlXPathDebugObjCounterNumber--;
1640: break;
1641: case XPATH_STRING:
1642: xmlXPathDebugObjCounterString--;
1643: break;
1644: case XPATH_POINT:
1645: xmlXPathDebugObjCounterPoint--;
1646: break;
1647: case XPATH_RANGE:
1648: xmlXPathDebugObjCounterRange--;
1649: break;
1650: case XPATH_LOCATIONSET:
1651: xmlXPathDebugObjCounterLocset--;
1652: break;
1653: case XPATH_USERS:
1654: xmlXPathDebugObjCounterUsers--;
1655: break;
1656: case XPATH_XSLT_TREE:
1657: xmlXPathDebugObjCounterXSLTTree--;
1658: break;
1659: default:
1660: break;
1661: }
1662: xmlXPathDebugObjCounterAll--;
1663: }
1664:
1665: /* REVISIT TODO: Make this static when committing */
1666: static void
1667: xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
1668: {
1669: int reqAll, reqNodeset, reqString, reqBool, reqNumber,
1670: reqXSLTTree, reqUndefined;
1671: int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
1672: caNumber = 0, caXSLTTree = 0, caUndefined = 0;
1673: int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
1674: reNumber = 0, reXSLTTree = 0, reUndefined = 0;
1675: int leftObjs = xmlXPathDebugObjCounterAll;
1676:
1677: reqAll = xmlXPathDebugObjTotalAll;
1678: reqNodeset = xmlXPathDebugObjTotalNodeset;
1679: reqString = xmlXPathDebugObjTotalString;
1680: reqBool = xmlXPathDebugObjTotalBool;
1681: reqNumber = xmlXPathDebugObjTotalNumber;
1682: reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
1683: reqUndefined = xmlXPathDebugObjTotalUndefined;
1684:
1685: printf("# XPath object usage:\n");
1686:
1687: if (ctxt != NULL) {
1688: if (ctxt->cache != NULL) {
1689: xmlXPathContextCachePtr cache =
1690: (xmlXPathContextCachePtr) ctxt->cache;
1691:
1692: reAll = cache->dbgReusedAll;
1693: reqAll += reAll;
1694: reNodeset = cache->dbgReusedNodeset;
1695: reqNodeset += reNodeset;
1696: reString = cache->dbgReusedString;
1697: reqString += reString;
1698: reBool = cache->dbgReusedBool;
1699: reqBool += reBool;
1700: reNumber = cache->dbgReusedNumber;
1701: reqNumber += reNumber;
1702: reXSLTTree = cache->dbgReusedXSLTTree;
1703: reqXSLTTree += reXSLTTree;
1704: reUndefined = cache->dbgReusedUndefined;
1705: reqUndefined += reUndefined;
1706:
1707: caAll = cache->dbgCachedAll;
1708: caBool = cache->dbgCachedBool;
1709: caNodeset = cache->dbgCachedNodeset;
1710: caString = cache->dbgCachedString;
1711: caNumber = cache->dbgCachedNumber;
1712: caXSLTTree = cache->dbgCachedXSLTTree;
1713: caUndefined = cache->dbgCachedUndefined;
1714:
1715: if (cache->nodesetObjs)
1716: leftObjs -= cache->nodesetObjs->number;
1717: if (cache->stringObjs)
1718: leftObjs -= cache->stringObjs->number;
1719: if (cache->booleanObjs)
1720: leftObjs -= cache->booleanObjs->number;
1721: if (cache->numberObjs)
1722: leftObjs -= cache->numberObjs->number;
1723: if (cache->miscObjs)
1724: leftObjs -= cache->miscObjs->number;
1725: }
1726: }
1727:
1728: printf("# all\n");
1729: printf("# total : %d\n", reqAll);
1730: printf("# left : %d\n", leftObjs);
1731: printf("# created: %d\n", xmlXPathDebugObjTotalAll);
1732: printf("# reused : %d\n", reAll);
1733: printf("# max : %d\n", xmlXPathDebugObjMaxAll);
1734:
1735: printf("# node-sets\n");
1736: printf("# total : %d\n", reqNodeset);
1737: printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
1738: printf("# reused : %d\n", reNodeset);
1739: printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
1740:
1741: printf("# strings\n");
1742: printf("# total : %d\n", reqString);
1743: printf("# created: %d\n", xmlXPathDebugObjTotalString);
1744: printf("# reused : %d\n", reString);
1745: printf("# max : %d\n", xmlXPathDebugObjMaxString);
1746:
1747: printf("# booleans\n");
1748: printf("# total : %d\n", reqBool);
1749: printf("# created: %d\n", xmlXPathDebugObjTotalBool);
1750: printf("# reused : %d\n", reBool);
1751: printf("# max : %d\n", xmlXPathDebugObjMaxBool);
1752:
1753: printf("# numbers\n");
1754: printf("# total : %d\n", reqNumber);
1755: printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
1756: printf("# reused : %d\n", reNumber);
1757: printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
1758:
1759: printf("# XSLT result tree fragments\n");
1760: printf("# total : %d\n", reqXSLTTree);
1761: printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
1762: printf("# reused : %d\n", reXSLTTree);
1763: printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
1764:
1765: printf("# undefined\n");
1766: printf("# total : %d\n", reqUndefined);
1767: printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
1768: printf("# reused : %d\n", reUndefined);
1769: printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
1770:
1771: }
1772:
1773: #endif /* XP_DEBUG_OBJ_USAGE */
1774:
1775: #endif /* LIBXML_DEBUG_ENABLED */
1776:
1777: /************************************************************************
1778: * *
1779: * XPath object caching *
1780: * *
1781: ************************************************************************/
1782:
1783: /**
1784: * xmlXPathNewCache:
1785: *
1786: * Create a new object cache
1787: *
1788: * Returns the xmlXPathCache just allocated.
1789: */
1790: static xmlXPathContextCachePtr
1791: xmlXPathNewCache(void)
1792: {
1793: xmlXPathContextCachePtr ret;
1794:
1795: ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
1796: if (ret == NULL) {
1797: xmlXPathErrMemory(NULL, "creating object cache\n");
1798: return(NULL);
1799: }
1800: memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
1801: ret->maxNodeset = 100;
1802: ret->maxString = 100;
1803: ret->maxBoolean = 100;
1804: ret->maxNumber = 100;
1805: ret->maxMisc = 100;
1806: return(ret);
1807: }
1808:
1809: static void
1810: xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
1811: {
1812: int i;
1813: xmlXPathObjectPtr obj;
1814:
1815: if (list == NULL)
1816: return;
1817:
1818: for (i = 0; i < list->number; i++) {
1819: obj = list->items[i];
1820: /*
1821: * Note that it is already assured that we don't need to
1822: * look out for namespace nodes in the node-set.
1823: */
1824: if (obj->nodesetval != NULL) {
1825: if (obj->nodesetval->nodeTab != NULL)
1826: xmlFree(obj->nodesetval->nodeTab);
1827: xmlFree(obj->nodesetval);
1828: }
1829: xmlFree(obj);
1830: #ifdef XP_DEBUG_OBJ_USAGE
1831: xmlXPathDebugObjCounterAll--;
1832: #endif
1833: }
1834: xmlPointerListFree(list);
1835: }
1836:
1837: static void
1838: xmlXPathFreeCache(xmlXPathContextCachePtr cache)
1839: {
1840: if (cache == NULL)
1841: return;
1842: if (cache->nodesetObjs)
1843: xmlXPathCacheFreeObjectList(cache->nodesetObjs);
1844: if (cache->stringObjs)
1845: xmlXPathCacheFreeObjectList(cache->stringObjs);
1846: if (cache->booleanObjs)
1847: xmlXPathCacheFreeObjectList(cache->booleanObjs);
1848: if (cache->numberObjs)
1849: xmlXPathCacheFreeObjectList(cache->numberObjs);
1850: if (cache->miscObjs)
1851: xmlXPathCacheFreeObjectList(cache->miscObjs);
1852: xmlFree(cache);
1853: }
1854:
1855: /**
1856: * xmlXPathContextSetCache:
1857: *
1858: * @ctxt: the XPath context
1859: * @active: enables/disables (creates/frees) the cache
1860: * @value: a value with semantics dependant on @options
1861: * @options: options (currently only the value 0 is used)
1862: *
1863: * Creates/frees an object cache on the XPath context.
1864: * If activates XPath objects (xmlXPathObject) will be cached internally
1865: * to be reused.
1866: * @options:
1867: * 0: This will set the XPath object caching:
1868: * @value:
1869: * This will set the maximum number of XPath objects
1870: * to be cached per slot
1871: * There are 5 slots for: node-set, string, number, boolean, and
1872: * misc objects. Use <0 for the default number (100).
1873: * Other values for @options have currently no effect.
1874: *
1875: * Returns 0 if the setting succeeded, and -1 on API or internal errors.
1876: */
1877: int
1878: xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
1879: int active,
1880: int value,
1881: int options)
1882: {
1883: if (ctxt == NULL)
1884: return(-1);
1885: if (active) {
1886: xmlXPathContextCachePtr cache;
1887:
1888: if (ctxt->cache == NULL) {
1889: ctxt->cache = xmlXPathNewCache();
1890: if (ctxt->cache == NULL)
1891: return(-1);
1892: }
1893: cache = (xmlXPathContextCachePtr) ctxt->cache;
1894: if (options == 0) {
1895: if (value < 0)
1896: value = 100;
1897: cache->maxNodeset = value;
1898: cache->maxString = value;
1899: cache->maxNumber = value;
1900: cache->maxBoolean = value;
1901: cache->maxMisc = value;
1902: }
1903: } else if (ctxt->cache != NULL) {
1904: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
1905: ctxt->cache = NULL;
1906: }
1907: return(0);
1908: }
1909:
1910: /**
1911: * xmlXPathCacheWrapNodeSet:
1912: * @ctxt: the XPath context
1913: * @val: the NodePtr value
1914: *
1915: * This is the cached version of xmlXPathWrapNodeSet().
1916: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1917: *
1918: * Returns the created or reused object.
1919: */
1920: static xmlXPathObjectPtr
1921: xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
1922: {
1923: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
1924: xmlXPathContextCachePtr cache =
1925: (xmlXPathContextCachePtr) ctxt->cache;
1926:
1927: if ((cache->miscObjs != NULL) &&
1928: (cache->miscObjs->number != 0))
1929: {
1930: xmlXPathObjectPtr ret;
1931:
1932: ret = (xmlXPathObjectPtr)
1933: cache->miscObjs->items[--cache->miscObjs->number];
1934: ret->type = XPATH_NODESET;
1935: ret->nodesetval = val;
1936: #ifdef XP_DEBUG_OBJ_USAGE
1937: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
1938: #endif
1939: return(ret);
1940: }
1941: }
1942:
1943: return(xmlXPathWrapNodeSet(val));
1944:
1945: }
1946:
1947: /**
1948: * xmlXPathCacheWrapString:
1949: * @ctxt: the XPath context
1950: * @val: the xmlChar * value
1951: *
1952: * This is the cached version of xmlXPathWrapString().
1953: * Wraps the @val string into an XPath object.
1954: *
1955: * Returns the created or reused object.
1956: */
1957: static xmlXPathObjectPtr
1958: xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
1959: {
1960: if ((ctxt != NULL) && (ctxt->cache != NULL)) {
1961: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
1962:
1963: if ((cache->stringObjs != NULL) &&
1964: (cache->stringObjs->number != 0))
1965: {
1966:
1967: xmlXPathObjectPtr ret;
1968:
1969: ret = (xmlXPathObjectPtr)
1970: cache->stringObjs->items[--cache->stringObjs->number];
1971: ret->type = XPATH_STRING;
1972: ret->stringval = val;
1973: #ifdef XP_DEBUG_OBJ_USAGE
1974: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
1975: #endif
1976: return(ret);
1977: } else if ((cache->miscObjs != NULL) &&
1978: (cache->miscObjs->number != 0))
1979: {
1980: xmlXPathObjectPtr ret;
1981: /*
1982: * Fallback to misc-cache.
1983: */
1984: ret = (xmlXPathObjectPtr)
1985: cache->miscObjs->items[--cache->miscObjs->number];
1986:
1987: ret->type = XPATH_STRING;
1988: ret->stringval = val;
1989: #ifdef XP_DEBUG_OBJ_USAGE
1990: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
1991: #endif
1992: return(ret);
1993: }
1994: }
1995: return(xmlXPathWrapString(val));
1996: }
1997:
1998: /**
1999: * xmlXPathCacheNewNodeSet:
2000: * @ctxt: the XPath context
2001: * @val: the NodePtr value
2002: *
2003: * This is the cached version of xmlXPathNewNodeSet().
2004: * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
2005: * it with the single Node @val
2006: *
2007: * Returns the created or reused object.
2008: */
2009: static xmlXPathObjectPtr
2010: xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
2011: {
2012: if ((ctxt != NULL) && (ctxt->cache)) {
2013: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2014:
2015: if ((cache->nodesetObjs != NULL) &&
2016: (cache->nodesetObjs->number != 0))
2017: {
2018: xmlXPathObjectPtr ret;
2019: /*
2020: * Use the nodset-cache.
2021: */
2022: ret = (xmlXPathObjectPtr)
2023: cache->nodesetObjs->items[--cache->nodesetObjs->number];
2024: ret->type = XPATH_NODESET;
2025: ret->boolval = 0;
2026: if (val) {
2027: if ((ret->nodesetval->nodeMax == 0) ||
2028: (val->type == XML_NAMESPACE_DECL))
2029: {
2030: xmlXPathNodeSetAddUnique(ret->nodesetval, val);
2031: } else {
2032: ret->nodesetval->nodeTab[0] = val;
2033: ret->nodesetval->nodeNr = 1;
2034: }
2035: }
2036: #ifdef XP_DEBUG_OBJ_USAGE
2037: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2038: #endif
2039: return(ret);
2040: } else if ((cache->miscObjs != NULL) &&
2041: (cache->miscObjs->number != 0))
2042: {
2043: xmlXPathObjectPtr ret;
2044: /*
2045: * Fallback to misc-cache.
2046: */
2047:
2048: ret = (xmlXPathObjectPtr)
2049: cache->miscObjs->items[--cache->miscObjs->number];
2050:
2051: ret->type = XPATH_NODESET;
2052: ret->boolval = 0;
2053: ret->nodesetval = xmlXPathNodeSetCreate(val);
2054: #ifdef XP_DEBUG_OBJ_USAGE
2055: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
2056: #endif
2057: return(ret);
2058: }
2059: }
2060: return(xmlXPathNewNodeSet(val));
2061: }
2062:
2063: /**
2064: * xmlXPathCacheNewCString:
2065: * @ctxt: the XPath context
2066: * @val: the char * value
2067: *
2068: * This is the cached version of xmlXPathNewCString().
2069: * Acquire an xmlXPathObjectPtr of type string and of value @val
2070: *
2071: * Returns the created or reused object.
2072: */
2073: static xmlXPathObjectPtr
2074: xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
2075: {
2076: if ((ctxt != NULL) && (ctxt->cache)) {
2077: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2078:
2079: if ((cache->stringObjs != NULL) &&
2080: (cache->stringObjs->number != 0))
2081: {
2082: xmlXPathObjectPtr ret;
2083:
2084: ret = (xmlXPathObjectPtr)
2085: cache->stringObjs->items[--cache->stringObjs->number];
2086:
2087: ret->type = XPATH_STRING;
2088: ret->stringval = xmlStrdup(BAD_CAST val);
2089: #ifdef XP_DEBUG_OBJ_USAGE
2090: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2091: #endif
2092: return(ret);
2093: } else if ((cache->miscObjs != NULL) &&
2094: (cache->miscObjs->number != 0))
2095: {
2096: xmlXPathObjectPtr ret;
2097:
2098: ret = (xmlXPathObjectPtr)
2099: cache->miscObjs->items[--cache->miscObjs->number];
2100:
2101: ret->type = XPATH_STRING;
2102: ret->stringval = xmlStrdup(BAD_CAST val);
2103: #ifdef XP_DEBUG_OBJ_USAGE
2104: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2105: #endif
2106: return(ret);
2107: }
2108: }
2109: return(xmlXPathNewCString(val));
2110: }
2111:
2112: /**
2113: * xmlXPathCacheNewString:
2114: * @ctxt: the XPath context
2115: * @val: the xmlChar * value
2116: *
2117: * This is the cached version of xmlXPathNewString().
2118: * Acquire an xmlXPathObjectPtr of type string and of value @val
2119: *
2120: * Returns the created or reused object.
2121: */
2122: static xmlXPathObjectPtr
2123: xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
2124: {
2125: if ((ctxt != NULL) && (ctxt->cache)) {
2126: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2127:
2128: if ((cache->stringObjs != NULL) &&
2129: (cache->stringObjs->number != 0))
2130: {
2131: xmlXPathObjectPtr ret;
2132:
2133: ret = (xmlXPathObjectPtr)
2134: cache->stringObjs->items[--cache->stringObjs->number];
2135: ret->type = XPATH_STRING;
2136: if (val != NULL)
2137: ret->stringval = xmlStrdup(val);
2138: else
2139: ret->stringval = xmlStrdup((const xmlChar *)"");
2140: #ifdef XP_DEBUG_OBJ_USAGE
2141: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2142: #endif
2143: return(ret);
2144: } else if ((cache->miscObjs != NULL) &&
2145: (cache->miscObjs->number != 0))
2146: {
2147: xmlXPathObjectPtr ret;
2148:
2149: ret = (xmlXPathObjectPtr)
2150: cache->miscObjs->items[--cache->miscObjs->number];
2151:
2152: ret->type = XPATH_STRING;
2153: if (val != NULL)
2154: ret->stringval = xmlStrdup(val);
2155: else
2156: ret->stringval = xmlStrdup((const xmlChar *)"");
2157: #ifdef XP_DEBUG_OBJ_USAGE
2158: xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
2159: #endif
2160: return(ret);
2161: }
2162: }
2163: return(xmlXPathNewString(val));
2164: }
2165:
2166: /**
2167: * xmlXPathCacheNewBoolean:
2168: * @ctxt: the XPath context
2169: * @val: the boolean value
2170: *
2171: * This is the cached version of xmlXPathNewBoolean().
2172: * Acquires an xmlXPathObjectPtr of type boolean and of value @val
2173: *
2174: * Returns the created or reused object.
2175: */
2176: static xmlXPathObjectPtr
2177: xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
2178: {
2179: if ((ctxt != NULL) && (ctxt->cache)) {
2180: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2181:
2182: if ((cache->booleanObjs != NULL) &&
2183: (cache->booleanObjs->number != 0))
2184: {
2185: xmlXPathObjectPtr ret;
2186:
2187: ret = (xmlXPathObjectPtr)
2188: cache->booleanObjs->items[--cache->booleanObjs->number];
2189: ret->type = XPATH_BOOLEAN;
2190: ret->boolval = (val != 0);
2191: #ifdef XP_DEBUG_OBJ_USAGE
2192: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2193: #endif
2194: return(ret);
2195: } else if ((cache->miscObjs != NULL) &&
2196: (cache->miscObjs->number != 0))
2197: {
2198: xmlXPathObjectPtr ret;
2199:
2200: ret = (xmlXPathObjectPtr)
2201: cache->miscObjs->items[--cache->miscObjs->number];
2202:
2203: ret->type = XPATH_BOOLEAN;
2204: ret->boolval = (val != 0);
2205: #ifdef XP_DEBUG_OBJ_USAGE
2206: xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
2207: #endif
2208: return(ret);
2209: }
2210: }
2211: return(xmlXPathNewBoolean(val));
2212: }
2213:
2214: /**
2215: * xmlXPathCacheNewFloat:
2216: * @ctxt: the XPath context
2217: * @val: the double value
2218: *
2219: * This is the cached version of xmlXPathNewFloat().
2220: * Acquires an xmlXPathObjectPtr of type double and of value @val
2221: *
2222: * Returns the created or reused object.
2223: */
2224: static xmlXPathObjectPtr
2225: xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
2226: {
2227: if ((ctxt != NULL) && (ctxt->cache)) {
2228: xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
2229:
2230: if ((cache->numberObjs != NULL) &&
2231: (cache->numberObjs->number != 0))
2232: {
2233: xmlXPathObjectPtr ret;
2234:
2235: ret = (xmlXPathObjectPtr)
2236: cache->numberObjs->items[--cache->numberObjs->number];
2237: ret->type = XPATH_NUMBER;
2238: ret->floatval = val;
2239: #ifdef XP_DEBUG_OBJ_USAGE
2240: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2241: #endif
2242: return(ret);
2243: } else if ((cache->miscObjs != NULL) &&
2244: (cache->miscObjs->number != 0))
2245: {
2246: xmlXPathObjectPtr ret;
2247:
2248: ret = (xmlXPathObjectPtr)
2249: cache->miscObjs->items[--cache->miscObjs->number];
2250:
2251: ret->type = XPATH_NUMBER;
2252: ret->floatval = val;
2253: #ifdef XP_DEBUG_OBJ_USAGE
2254: xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
2255: #endif
2256: return(ret);
2257: }
2258: }
2259: return(xmlXPathNewFloat(val));
2260: }
2261:
2262: /**
2263: * xmlXPathCacheConvertString:
2264: * @ctxt: the XPath context
2265: * @val: an XPath object
2266: *
2267: * This is the cached version of xmlXPathConvertString().
2268: * Converts an existing object to its string() equivalent
2269: *
2270: * Returns a created or reused object, the old one is freed (cached)
2271: * (or the operation is done directly on @val)
2272: */
2273:
2274: static xmlXPathObjectPtr
2275: xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2276: xmlChar *res = NULL;
2277:
2278: if (val == NULL)
2279: return(xmlXPathCacheNewCString(ctxt, ""));
2280:
2281: switch (val->type) {
2282: case XPATH_UNDEFINED:
2283: #ifdef DEBUG_EXPR
2284: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2285: #endif
2286: break;
2287: case XPATH_NODESET:
2288: case XPATH_XSLT_TREE:
2289: res = xmlXPathCastNodeSetToString(val->nodesetval);
2290: break;
2291: case XPATH_STRING:
2292: return(val);
2293: case XPATH_BOOLEAN:
2294: res = xmlXPathCastBooleanToString(val->boolval);
2295: break;
2296: case XPATH_NUMBER:
2297: res = xmlXPathCastNumberToString(val->floatval);
2298: break;
2299: case XPATH_USERS:
2300: case XPATH_POINT:
2301: case XPATH_RANGE:
2302: case XPATH_LOCATIONSET:
2303: TODO;
2304: break;
2305: }
2306: xmlXPathReleaseObject(ctxt, val);
2307: if (res == NULL)
2308: return(xmlXPathCacheNewCString(ctxt, ""));
2309: return(xmlXPathCacheWrapString(ctxt, res));
2310: }
2311:
2312: /**
2313: * xmlXPathCacheObjectCopy:
2314: * @ctxt: the XPath context
2315: * @val: the original object
2316: *
2317: * This is the cached version of xmlXPathObjectCopy().
2318: * Acquire a copy of a given object
2319: *
2320: * Returns a created or reused created object.
2321: */
2322: static xmlXPathObjectPtr
2323: xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
2324: {
2325: if (val == NULL)
2326: return(NULL);
2327:
2328: if (XP_HAS_CACHE(ctxt)) {
2329: switch (val->type) {
2330: case XPATH_NODESET:
2331: return(xmlXPathCacheWrapNodeSet(ctxt,
2332: xmlXPathNodeSetMerge(NULL, val->nodesetval)));
2333: case XPATH_STRING:
2334: return(xmlXPathCacheNewString(ctxt, val->stringval));
2335: case XPATH_BOOLEAN:
2336: return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
2337: case XPATH_NUMBER:
2338: return(xmlXPathCacheNewFloat(ctxt, val->floatval));
2339: default:
2340: break;
2341: }
2342: }
2343: return(xmlXPathObjectCopy(val));
2344: }
2345:
2346: /**
2347: * xmlXPathCacheConvertBoolean:
2348: * @ctxt: the XPath context
2349: * @val: an XPath object
2350: *
2351: * This is the cached version of xmlXPathConvertBoolean().
2352: * Converts an existing object to its boolean() equivalent
2353: *
2354: * Returns a created or reused object, the old one is freed (or the operation
2355: * is done directly on @val)
2356: */
2357: static xmlXPathObjectPtr
2358: xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2359: xmlXPathObjectPtr ret;
2360:
2361: if (val == NULL)
2362: return(xmlXPathCacheNewBoolean(ctxt, 0));
2363: if (val->type == XPATH_BOOLEAN)
2364: return(val);
2365: ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
2366: xmlXPathReleaseObject(ctxt, val);
2367: return(ret);
2368: }
2369:
2370: /**
2371: * xmlXPathCacheConvertNumber:
2372: * @ctxt: the XPath context
2373: * @val: an XPath object
2374: *
2375: * This is the cached version of xmlXPathConvertNumber().
2376: * Converts an existing object to its number() equivalent
2377: *
2378: * Returns a created or reused object, the old one is freed (or the operation
2379: * is done directly on @val)
2380: */
2381: static xmlXPathObjectPtr
2382: xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
2383: xmlXPathObjectPtr ret;
2384:
2385: if (val == NULL)
2386: return(xmlXPathCacheNewFloat(ctxt, 0.0));
2387: if (val->type == XPATH_NUMBER)
2388: return(val);
2389: ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
2390: xmlXPathReleaseObject(ctxt, val);
2391: return(ret);
2392: }
2393:
2394: /************************************************************************
2395: * *
2396: * Parser stacks related functions and macros *
2397: * *
2398: ************************************************************************/
2399:
2400: /**
2401: * valuePop:
2402: * @ctxt: an XPath evaluation context
2403: *
2404: * Pops the top XPath object from the value stack
2405: *
2406: * Returns the XPath object just removed
2407: */
2408: xmlXPathObjectPtr
2409: valuePop(xmlXPathParserContextPtr ctxt)
2410: {
2411: xmlXPathObjectPtr ret;
2412:
2413: if ((ctxt == NULL) || (ctxt->valueNr <= 0))
2414: return (NULL);
2415: ctxt->valueNr--;
2416: if (ctxt->valueNr > 0)
2417: ctxt->value = ctxt->valueTab[ctxt->valueNr - 1];
2418: else
2419: ctxt->value = NULL;
2420: ret = ctxt->valueTab[ctxt->valueNr];
2421: ctxt->valueTab[ctxt->valueNr] = NULL;
2422: return (ret);
2423: }
2424: /**
2425: * valuePush:
2426: * @ctxt: an XPath evaluation context
2427: * @value: the XPath object
2428: *
2429: * Pushes a new XPath object on top of the value stack
2430: *
2431: * returns the number of items on the value stack
2432: */
2433: int
2434: valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
2435: {
2436: if ((ctxt == NULL) || (value == NULL)) return(-1);
2437: if (ctxt->valueNr >= ctxt->valueMax) {
2438: xmlXPathObjectPtr *tmp;
2439:
2440: tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
2441: 2 * ctxt->valueMax *
2442: sizeof(ctxt->valueTab[0]));
2443: if (tmp == NULL) {
2444: xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
2445: return (0);
2446: }
2447: ctxt->valueMax *= 2;
2448: ctxt->valueTab = tmp;
2449: }
2450: ctxt->valueTab[ctxt->valueNr] = value;
2451: ctxt->value = value;
2452: return (ctxt->valueNr++);
2453: }
2454:
2455: /**
2456: * xmlXPathPopBoolean:
2457: * @ctxt: an XPath parser context
2458: *
2459: * Pops a boolean from the stack, handling conversion if needed.
2460: * Check error with #xmlXPathCheckError.
2461: *
2462: * Returns the boolean
2463: */
2464: int
2465: xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
2466: xmlXPathObjectPtr obj;
2467: int ret;
2468:
2469: obj = valuePop(ctxt);
2470: if (obj == NULL) {
2471: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2472: return(0);
2473: }
2474: if (obj->type != XPATH_BOOLEAN)
2475: ret = xmlXPathCastToBoolean(obj);
2476: else
2477: ret = obj->boolval;
2478: xmlXPathReleaseObject(ctxt->context, obj);
2479: return(ret);
2480: }
2481:
2482: /**
2483: * xmlXPathPopNumber:
2484: * @ctxt: an XPath parser context
2485: *
2486: * Pops a number from the stack, handling conversion if needed.
2487: * Check error with #xmlXPathCheckError.
2488: *
2489: * Returns the number
2490: */
2491: double
2492: xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
2493: xmlXPathObjectPtr obj;
2494: double ret;
2495:
2496: obj = valuePop(ctxt);
2497: if (obj == NULL) {
2498: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2499: return(0);
2500: }
2501: if (obj->type != XPATH_NUMBER)
2502: ret = xmlXPathCastToNumber(obj);
2503: else
2504: ret = obj->floatval;
2505: xmlXPathReleaseObject(ctxt->context, obj);
2506: return(ret);
2507: }
2508:
2509: /**
2510: * xmlXPathPopString:
2511: * @ctxt: an XPath parser context
2512: *
2513: * Pops a string from the stack, handling conversion if needed.
2514: * Check error with #xmlXPathCheckError.
2515: *
2516: * Returns the string
2517: */
2518: xmlChar *
2519: xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
2520: xmlXPathObjectPtr obj;
2521: xmlChar * ret;
2522:
2523: obj = valuePop(ctxt);
2524: if (obj == NULL) {
2525: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2526: return(NULL);
2527: }
2528: ret = xmlXPathCastToString(obj); /* this does required strdup */
2529: /* TODO: needs refactoring somewhere else */
2530: if (obj->stringval == ret)
2531: obj->stringval = NULL;
2532: xmlXPathReleaseObject(ctxt->context, obj);
2533: return(ret);
2534: }
2535:
2536: /**
2537: * xmlXPathPopNodeSet:
2538: * @ctxt: an XPath parser context
2539: *
2540: * Pops a node-set from the stack, handling conversion if needed.
2541: * Check error with #xmlXPathCheckError.
2542: *
2543: * Returns the node-set
2544: */
2545: xmlNodeSetPtr
2546: xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
2547: xmlXPathObjectPtr obj;
2548: xmlNodeSetPtr ret;
2549:
2550: if (ctxt == NULL) return(NULL);
2551: if (ctxt->value == NULL) {
2552: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2553: return(NULL);
2554: }
2555: if (!xmlXPathStackIsNodeSet(ctxt)) {
2556: xmlXPathSetTypeError(ctxt);
2557: return(NULL);
2558: }
2559: obj = valuePop(ctxt);
2560: ret = obj->nodesetval;
2561: #if 0
2562: /* to fix memory leak of not clearing obj->user */
2563: if (obj->boolval && obj->user != NULL)
2564: xmlFreeNodeList((xmlNodePtr) obj->user);
2565: #endif
2566: obj->nodesetval = NULL;
2567: xmlXPathReleaseObject(ctxt->context, obj);
2568: return(ret);
2569: }
2570:
2571: /**
2572: * xmlXPathPopExternal:
2573: * @ctxt: an XPath parser context
2574: *
2575: * Pops an external object from the stack, handling conversion if needed.
2576: * Check error with #xmlXPathCheckError.
2577: *
2578: * Returns the object
2579: */
2580: void *
2581: xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
2582: xmlXPathObjectPtr obj;
2583: void * ret;
2584:
2585: if ((ctxt == NULL) || (ctxt->value == NULL)) {
2586: xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
2587: return(NULL);
2588: }
2589: if (ctxt->value->type != XPATH_USERS) {
2590: xmlXPathSetTypeError(ctxt);
2591: return(NULL);
2592: }
2593: obj = valuePop(ctxt);
2594: ret = obj->user;
2595: obj->user = NULL;
2596: xmlXPathReleaseObject(ctxt->context, obj);
2597: return(ret);
2598: }
2599:
2600: /*
2601: * Macros for accessing the content. Those should be used only by the parser,
2602: * and not exported.
2603: *
2604: * Dirty macros, i.e. one need to make assumption on the context to use them
2605: *
2606: * CUR_PTR return the current pointer to the xmlChar to be parsed.
2607: * CUR returns the current xmlChar value, i.e. a 8 bit value
2608: * in ISO-Latin or UTF-8.
2609: * This should be used internally by the parser
2610: * only to compare to ASCII values otherwise it would break when
2611: * running with UTF-8 encoding.
2612: * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
2613: * to compare on ASCII based substring.
2614: * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
2615: * strings within the parser.
2616: * CURRENT Returns the current char value, with the full decoding of
2617: * UTF-8 if we are using this mode. It returns an int.
2618: * NEXT Skip to the next character, this does the proper decoding
2619: * in UTF-8 mode. It also pop-up unfinished entities on the fly.
2620: * It returns the pointer to the current xmlChar.
2621: */
2622:
2623: #define CUR (*ctxt->cur)
2624: #define SKIP(val) ctxt->cur += (val)
2625: #define NXT(val) ctxt->cur[(val)]
2626: #define CUR_PTR ctxt->cur
2627: #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
2628:
2629: #define COPY_BUF(l,b,i,v) \
2630: if (l == 1) b[i++] = (xmlChar) v; \
2631: else i += xmlCopyChar(l,&b[i],v)
2632:
2633: #define NEXTL(l) ctxt->cur += l
2634:
2635: #define SKIP_BLANKS \
2636: while (IS_BLANK_CH(*(ctxt->cur))) NEXT
2637:
2638: #define CURRENT (*ctxt->cur)
2639: #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
2640:
2641:
2642: #ifndef DBL_DIG
2643: #define DBL_DIG 16
2644: #endif
2645: #ifndef DBL_EPSILON
2646: #define DBL_EPSILON 1E-9
2647: #endif
2648:
2649: #define UPPER_DOUBLE 1E9
2650: #define LOWER_DOUBLE 1E-5
2651: #define LOWER_DOUBLE_EXP 5
2652:
2653: #define INTEGER_DIGITS DBL_DIG
2654: #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP))
2655: #define EXPONENT_DIGITS (3 + 2)
2656:
2657: /**
2658: * xmlXPathFormatNumber:
2659: * @number: number to format
2660: * @buffer: output buffer
2661: * @buffersize: size of output buffer
2662: *
2663: * Convert the number into a string representation.
2664: */
2665: static void
2666: xmlXPathFormatNumber(double number, char buffer[], int buffersize)
2667: {
2668: switch (xmlXPathIsInf(number)) {
2669: case 1:
2670: if (buffersize > (int)sizeof("Infinity"))
2671: snprintf(buffer, buffersize, "Infinity");
2672: break;
2673: case -1:
2674: if (buffersize > (int)sizeof("-Infinity"))
2675: snprintf(buffer, buffersize, "-Infinity");
2676: break;
2677: default:
2678: if (xmlXPathIsNaN(number)) {
2679: if (buffersize > (int)sizeof("NaN"))
2680: snprintf(buffer, buffersize, "NaN");
2681: } else if (number == 0 && xmlXPathGetSign(number) != 0) {
2682: snprintf(buffer, buffersize, "0");
2683: } else if (number == ((int) number)) {
2684: char work[30];
2685: char *ptr, *cur;
2686: int value = (int) number;
2687:
2688: ptr = &buffer[0];
2689: if (value == 0) {
2690: *ptr++ = '0';
2691: } else {
2692: snprintf(work, 29, "%d", value);
2693: cur = &work[0];
2694: while ((*cur) && (ptr - buffer < buffersize)) {
2695: *ptr++ = *cur++;
2696: }
2697: }
2698: if (ptr - buffer < buffersize) {
2699: *ptr = 0;
2700: } else if (buffersize > 0) {
2701: ptr--;
2702: *ptr = 0;
2703: }
2704: } else {
2705: /*
2706: For the dimension of work,
2707: DBL_DIG is number of significant digits
2708: EXPONENT is only needed for "scientific notation"
2709: 3 is sign, decimal point, and terminating zero
2710: LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
2711: Note that this dimension is slightly (a few characters)
2712: larger than actually necessary.
2713: */
2714: char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
2715: int integer_place, fraction_place;
2716: char *ptr;
2717: char *after_fraction;
2718: double absolute_value;
2719: int size;
2720:
2721: absolute_value = fabs(number);
2722:
2723: /*
2724: * First choose format - scientific or regular floating point.
2725: * In either case, result is in work, and after_fraction points
2726: * just past the fractional part.
2727: */
2728: if ( ((absolute_value > UPPER_DOUBLE) ||
2729: (absolute_value < LOWER_DOUBLE)) &&
2730: (absolute_value != 0.0) ) {
2731: /* Use scientific notation */
2732: integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
2733: fraction_place = DBL_DIG - 1;
2734: size = snprintf(work, sizeof(work),"%*.*e",
2735: integer_place, fraction_place, number);
2736: while ((size > 0) && (work[size] != 'e')) size--;
2737:
2738: }
2739: else {
2740: /* Use regular notation */
2741: if (absolute_value > 0.0) {
2742: integer_place = (int)log10(absolute_value);
2743: if (integer_place > 0)
2744: fraction_place = DBL_DIG - integer_place - 1;
2745: else
2746: fraction_place = DBL_DIG - integer_place;
2747: } else {
2748: fraction_place = 1;
2749: }
2750: size = snprintf(work, sizeof(work), "%0.*f",
2751: fraction_place, number);
2752: }
2753:
2754: /* Remove fractional trailing zeroes */
2755: after_fraction = work + size;
2756: ptr = after_fraction;
2757: while (*(--ptr) == '0')
2758: ;
2759: if (*ptr != '.')
2760: ptr++;
2761: while ((*ptr++ = *after_fraction++) != 0);
2762:
2763: /* Finally copy result back to caller */
2764: size = strlen(work) + 1;
2765: if (size > buffersize) {
2766: work[buffersize - 1] = 0;
2767: size = buffersize;
2768: }
2769: memmove(buffer, work, size);
2770: }
2771: break;
2772: }
2773: }
2774:
2775:
2776: /************************************************************************
2777: * *
2778: * Routines to handle NodeSets *
2779: * *
2780: ************************************************************************/
2781:
2782: /**
2783: * xmlXPathOrderDocElems:
2784: * @doc: an input document
2785: *
2786: * Call this routine to speed up XPath computation on static documents.
2787: * This stamps all the element nodes with the document order
2788: * Like for line information, the order is kept in the element->content
2789: * field, the value stored is actually - the node number (starting at -1)
2790: * to be able to differentiate from line numbers.
2791: *
2792: * Returns the number of elements found in the document or -1 in case
2793: * of error.
2794: */
2795: long
2796: xmlXPathOrderDocElems(xmlDocPtr doc) {
2797: long count = 0;
2798: xmlNodePtr cur;
2799:
2800: if (doc == NULL)
2801: return(-1);
2802: cur = doc->children;
2803: while (cur != NULL) {
2804: if (cur->type == XML_ELEMENT_NODE) {
2805: cur->content = (void *) (-(++count));
2806: if (cur->children != NULL) {
2807: cur = cur->children;
2808: continue;
2809: }
2810: }
2811: if (cur->next != NULL) {
2812: cur = cur->next;
2813: continue;
2814: }
2815: do {
2816: cur = cur->parent;
2817: if (cur == NULL)
2818: break;
2819: if (cur == (xmlNodePtr) doc) {
2820: cur = NULL;
2821: break;
2822: }
2823: if (cur->next != NULL) {
2824: cur = cur->next;
2825: break;
2826: }
2827: } while (cur != NULL);
2828: }
2829: return(count);
2830: }
2831:
2832: /**
2833: * xmlXPathCmpNodes:
2834: * @node1: the first node
2835: * @node2: the second node
2836: *
2837: * Compare two nodes w.r.t document order
2838: *
2839: * Returns -2 in case of error 1 if first point < second point, 0 if
2840: * it's the same node, -1 otherwise
2841: */
2842: int
2843: xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
2844: int depth1, depth2;
2845: int attr1 = 0, attr2 = 0;
2846: xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
2847: xmlNodePtr cur, root;
2848:
2849: if ((node1 == NULL) || (node2 == NULL))
2850: return(-2);
2851: /*
2852: * a couple of optimizations which will avoid computations in most cases
2853: */
2854: if (node1 == node2) /* trivial case */
2855: return(0);
2856: if (node1->type == XML_ATTRIBUTE_NODE) {
2857: attr1 = 1;
2858: attrNode1 = node1;
2859: node1 = node1->parent;
2860: }
2861: if (node2->type == XML_ATTRIBUTE_NODE) {
2862: attr2 = 1;
2863: attrNode2 = node2;
2864: node2 = node2->parent;
2865: }
2866: if (node1 == node2) {
2867: if (attr1 == attr2) {
2868: /* not required, but we keep attributes in order */
2869: if (attr1 != 0) {
2870: cur = attrNode2->prev;
2871: while (cur != NULL) {
2872: if (cur == attrNode1)
2873: return (1);
2874: cur = cur->prev;
2875: }
2876: return (-1);
2877: }
2878: return(0);
2879: }
2880: if (attr2 == 1)
2881: return(1);
2882: return(-1);
2883: }
2884: if ((node1->type == XML_NAMESPACE_DECL) ||
2885: (node2->type == XML_NAMESPACE_DECL))
2886: return(1);
2887: if (node1 == node2->prev)
2888: return(1);
2889: if (node1 == node2->next)
2890: return(-1);
2891:
2892: /*
2893: * Speedup using document order if availble.
2894: */
2895: if ((node1->type == XML_ELEMENT_NODE) &&
2896: (node2->type == XML_ELEMENT_NODE) &&
2897: (0 > (long) node1->content) &&
2898: (0 > (long) node2->content) &&
2899: (node1->doc == node2->doc)) {
2900: long l1, l2;
2901:
2902: l1 = -((long) node1->content);
2903: l2 = -((long) node2->content);
2904: if (l1 < l2)
2905: return(1);
2906: if (l1 > l2)
2907: return(-1);
2908: }
2909:
2910: /*
2911: * compute depth to root
2912: */
2913: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
2914: if (cur == node1)
2915: return(1);
2916: depth2++;
2917: }
2918: root = cur;
2919: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
2920: if (cur == node2)
2921: return(-1);
2922: depth1++;
2923: }
2924: /*
2925: * Distinct document (or distinct entities :-( ) case.
2926: */
2927: if (root != cur) {
2928: return(-2);
2929: }
2930: /*
2931: * get the nearest common ancestor.
2932: */
2933: while (depth1 > depth2) {
2934: depth1--;
2935: node1 = node1->parent;
2936: }
2937: while (depth2 > depth1) {
2938: depth2--;
2939: node2 = node2->parent;
2940: }
2941: while (node1->parent != node2->parent) {
2942: node1 = node1->parent;
2943: node2 = node2->parent;
2944: /* should not happen but just in case ... */
2945: if ((node1 == NULL) || (node2 == NULL))
2946: return(-2);
2947: }
2948: /*
2949: * Find who's first.
2950: */
2951: if (node1 == node2->prev)
2952: return(1);
2953: if (node1 == node2->next)
2954: return(-1);
2955: /*
2956: * Speedup using document order if availble.
2957: */
2958: if ((node1->type == XML_ELEMENT_NODE) &&
2959: (node2->type == XML_ELEMENT_NODE) &&
2960: (0 > (long) node1->content) &&
2961: (0 > (long) node2->content) &&
2962: (node1->doc == node2->doc)) {
2963: long l1, l2;
2964:
2965: l1 = -((long) node1->content);
2966: l2 = -((long) node2->content);
2967: if (l1 < l2)
2968: return(1);
2969: if (l1 > l2)
2970: return(-1);
2971: }
2972:
2973: for (cur = node1->next;cur != NULL;cur = cur->next)
2974: if (cur == node2)
2975: return(1);
2976: return(-1); /* assume there is no sibling list corruption */
2977: }
2978:
2979: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
2980: /**
2981: * xmlXPathCmpNodesExt:
2982: * @node1: the first node
2983: * @node2: the second node
2984: *
2985: * Compare two nodes w.r.t document order.
2986: * This one is optimized for handling of non-element nodes.
2987: *
2988: * Returns -2 in case of error 1 if first point < second point, 0 if
2989: * it's the same node, -1 otherwise
2990: */
2991: static int
2992: xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
2993: int depth1, depth2;
2994: int misc = 0, precedence1 = 0, precedence2 = 0;
2995: xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
2996: xmlNodePtr cur, root;
2997: long l1, l2;
2998:
2999: if ((node1 == NULL) || (node2 == NULL))
3000: return(-2);
3001:
3002: if (node1 == node2)
3003: return(0);
3004:
3005: /*
3006: * a couple of optimizations which will avoid computations in most cases
3007: */
3008: switch (node1->type) {
3009: case XML_ELEMENT_NODE:
3010: if (node2->type == XML_ELEMENT_NODE) {
3011: if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */
3012: (0 > (long) node2->content) &&
3013: (node1->doc == node2->doc))
3014: {
3015: l1 = -((long) node1->content);
3016: l2 = -((long) node2->content);
3017: if (l1 < l2)
3018: return(1);
3019: if (l1 > l2)
3020: return(-1);
3021: } else
3022: goto turtle_comparison;
3023: }
3024: break;
3025: case XML_ATTRIBUTE_NODE:
3026: precedence1 = 1; /* element is owner */
3027: miscNode1 = node1;
3028: node1 = node1->parent;
3029: misc = 1;
3030: break;
3031: case XML_TEXT_NODE:
3032: case XML_CDATA_SECTION_NODE:
3033: case XML_COMMENT_NODE:
3034: case XML_PI_NODE: {
3035: miscNode1 = node1;
3036: /*
3037: * Find nearest element node.
3038: */
3039: if (node1->prev != NULL) {
3040: do {
3041: node1 = node1->prev;
3042: if (node1->type == XML_ELEMENT_NODE) {
3043: precedence1 = 3; /* element in prev-sibl axis */
3044: break;
3045: }
3046: if (node1->prev == NULL) {
3047: precedence1 = 2; /* element is parent */
3048: /*
3049: * URGENT TODO: Are there any cases, where the
3050: * parent of such a node is not an element node?
3051: */
3052: node1 = node1->parent;
3053: break;
3054: }
3055: } while (1);
3056: } else {
3057: precedence1 = 2; /* element is parent */
3058: node1 = node1->parent;
3059: }
3060: if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
3061: (0 <= (long) node1->content)) {
3062: /*
3063: * Fallback for whatever case.
3064: */
3065: node1 = miscNode1;
3066: precedence1 = 0;
3067: } else
3068: misc = 1;
3069: }
3070: break;
3071: case XML_NAMESPACE_DECL:
3072: /*
3073: * TODO: why do we return 1 for namespace nodes?
3074: */
3075: return(1);
3076: default:
3077: break;
3078: }
3079: switch (node2->type) {
3080: case XML_ELEMENT_NODE:
3081: break;
3082: case XML_ATTRIBUTE_NODE:
3083: precedence2 = 1; /* element is owner */
3084: miscNode2 = node2;
3085: node2 = node2->parent;
3086: misc = 1;
3087: break;
3088: case XML_TEXT_NODE:
3089: case XML_CDATA_SECTION_NODE:
3090: case XML_COMMENT_NODE:
3091: case XML_PI_NODE: {
3092: miscNode2 = node2;
3093: if (node2->prev != NULL) {
3094: do {
3095: node2 = node2->prev;
3096: if (node2->type == XML_ELEMENT_NODE) {
3097: precedence2 = 3; /* element in prev-sibl axis */
3098: break;
3099: }
3100: if (node2->prev == NULL) {
3101: precedence2 = 2; /* element is parent */
3102: node2 = node2->parent;
3103: break;
3104: }
3105: } while (1);
3106: } else {
3107: precedence2 = 2; /* element is parent */
3108: node2 = node2->parent;
3109: }
3110: if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
3111: (0 <= (long) node1->content))
3112: {
3113: node2 = miscNode2;
3114: precedence2 = 0;
3115: } else
3116: misc = 1;
3117: }
3118: break;
3119: case XML_NAMESPACE_DECL:
3120: return(1);
3121: default:
3122: break;
3123: }
3124: if (misc) {
3125: if (node1 == node2) {
3126: if (precedence1 == precedence2) {
3127: /*
3128: * The ugly case; but normally there aren't many
3129: * adjacent non-element nodes around.
3130: */
3131: cur = miscNode2->prev;
3132: while (cur != NULL) {
3133: if (cur == miscNode1)
3134: return(1);
3135: if (cur->type == XML_ELEMENT_NODE)
3136: return(-1);
3137: cur = cur->prev;
3138: }
3139: return (-1);
3140: } else {
3141: /*
3142: * Evaluate based on higher precedence wrt to the element.
3143: * TODO: This assumes attributes are sorted before content.
3144: * Is this 100% correct?
3145: */
3146: if (precedence1 < precedence2)
3147: return(1);
3148: else
3149: return(-1);
3150: }
3151: }
3152: /*
3153: * Special case: One of the helper-elements is contained by the other.
3154: * <foo>
3155: * <node2>
3156: * <node1>Text-1(precedence1 == 2)</node1>
3157: * </node2>
3158: * Text-6(precedence2 == 3)
3159: * </foo>
3160: */
3161: if ((precedence2 == 3) && (precedence1 > 1)) {
3162: cur = node1->parent;
3163: while (cur) {
3164: if (cur == node2)
3165: return(1);
3166: cur = cur->parent;
3167: }
3168: }
3169: if ((precedence1 == 3) && (precedence2 > 1)) {
3170: cur = node2->parent;
3171: while (cur) {
3172: if (cur == node1)
3173: return(-1);
3174: cur = cur->parent;
3175: }
3176: }
3177: }
3178:
3179: /*
3180: * Speedup using document order if availble.
3181: */
3182: if ((node1->type == XML_ELEMENT_NODE) &&
3183: (node2->type == XML_ELEMENT_NODE) &&
3184: (0 > (long) node1->content) &&
3185: (0 > (long) node2->content) &&
3186: (node1->doc == node2->doc)) {
3187:
3188: l1 = -((long) node1->content);
3189: l2 = -((long) node2->content);
3190: if (l1 < l2)
3191: return(1);
3192: if (l1 > l2)
3193: return(-1);
3194: }
3195:
3196: turtle_comparison:
3197:
3198: if (node1 == node2->prev)
3199: return(1);
3200: if (node1 == node2->next)
3201: return(-1);
3202: /*
3203: * compute depth to root
3204: */
3205: for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
3206: if (cur == node1)
3207: return(1);
3208: depth2++;
3209: }
3210: root = cur;
3211: for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
3212: if (cur == node2)
3213: return(-1);
3214: depth1++;
3215: }
3216: /*
3217: * Distinct document (or distinct entities :-( ) case.
3218: */
3219: if (root != cur) {
3220: return(-2);
3221: }
3222: /*
3223: * get the nearest common ancestor.
3224: */
3225: while (depth1 > depth2) {
3226: depth1--;
3227: node1 = node1->parent;
3228: }
3229: while (depth2 > depth1) {
3230: depth2--;
3231: node2 = node2->parent;
3232: }
3233: while (node1->parent != node2->parent) {
3234: node1 = node1->parent;
3235: node2 = node2->parent;
3236: /* should not happen but just in case ... */
3237: if ((node1 == NULL) || (node2 == NULL))
3238: return(-2);
3239: }
3240: /*
3241: * Find who's first.
3242: */
3243: if (node1 == node2->prev)
3244: return(1);
3245: if (node1 == node2->next)
3246: return(-1);
3247: /*
3248: * Speedup using document order if availble.
3249: */
3250: if ((node1->type == XML_ELEMENT_NODE) &&
3251: (node2->type == XML_ELEMENT_NODE) &&
3252: (0 > (long) node1->content) &&
3253: (0 > (long) node2->content) &&
3254: (node1->doc == node2->doc)) {
3255:
3256: l1 = -((long) node1->content);
3257: l2 = -((long) node2->content);
3258: if (l1 < l2)
3259: return(1);
3260: if (l1 > l2)
3261: return(-1);
3262: }
3263:
3264: for (cur = node1->next;cur != NULL;cur = cur->next)
3265: if (cur == node2)
3266: return(1);
3267: return(-1); /* assume there is no sibling list corruption */
3268: }
3269: #endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
3270:
3271: /**
3272: * xmlXPathNodeSetSort:
3273: * @set: the node set
3274: *
3275: * Sort the node set in document order
3276: */
3277: void
3278: xmlXPathNodeSetSort(xmlNodeSetPtr set) {
3279: int i, j, incr, len;
3280: xmlNodePtr tmp;
3281:
3282: if (set == NULL)
3283: return;
3284:
3285: /* Use Shell's sort to sort the node-set */
3286: len = set->nodeNr;
3287: for (incr = len / 2; incr > 0; incr /= 2) {
3288: for (i = incr; i < len; i++) {
3289: j = i - incr;
3290: while (j >= 0) {
3291: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
3292: if (xmlXPathCmpNodesExt(set->nodeTab[j],
3293: set->nodeTab[j + incr]) == -1)
3294: #else
3295: if (xmlXPathCmpNodes(set->nodeTab[j],
3296: set->nodeTab[j + incr]) == -1)
3297: #endif
3298: {
3299: tmp = set->nodeTab[j];
3300: set->nodeTab[j] = set->nodeTab[j + incr];
3301: set->nodeTab[j + incr] = tmp;
3302: j -= incr;
3303: } else
3304: break;
3305: }
3306: }
3307: }
3308: }
3309:
3310: #define XML_NODESET_DEFAULT 10
3311: /**
3312: * xmlXPathNodeSetDupNs:
3313: * @node: the parent node of the namespace XPath node
3314: * @ns: the libxml namespace declaration node.
3315: *
3316: * Namespace node in libxml don't match the XPath semantic. In a node set
3317: * the namespace nodes are duplicated and the next pointer is set to the
3318: * parent node in the XPath semantic.
3319: *
3320: * Returns the newly created object.
3321: */
3322: static xmlNodePtr
3323: xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
3324: xmlNsPtr cur;
3325:
3326: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3327: return(NULL);
3328: if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
3329: return((xmlNodePtr) ns);
3330:
3331: /*
3332: * Allocate a new Namespace and fill the fields.
3333: */
3334: cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
3335: if (cur == NULL) {
3336: xmlXPathErrMemory(NULL, "duplicating namespace\n");
3337: return(NULL);
3338: }
3339: memset(cur, 0, sizeof(xmlNs));
3340: cur->type = XML_NAMESPACE_DECL;
3341: if (ns->href != NULL)
3342: cur->href = xmlStrdup(ns->href);
3343: if (ns->prefix != NULL)
3344: cur->prefix = xmlStrdup(ns->prefix);
3345: cur->next = (xmlNsPtr) node;
3346: return((xmlNodePtr) cur);
3347: }
3348:
3349: /**
3350: * xmlXPathNodeSetFreeNs:
3351: * @ns: the XPath namespace node found in a nodeset.
3352: *
3353: * Namespace nodes in libxml don't match the XPath semantic. In a node set
3354: * the namespace nodes are duplicated and the next pointer is set to the
3355: * parent node in the XPath semantic. Check if such a node needs to be freed
3356: */
3357: void
3358: xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
3359: if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
3360: return;
3361:
3362: if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
3363: if (ns->href != NULL)
3364: xmlFree((xmlChar *)ns->href);
3365: if (ns->prefix != NULL)
3366: xmlFree((xmlChar *)ns->prefix);
3367: xmlFree(ns);
3368: }
3369: }
3370:
3371: /**
3372: * xmlXPathNodeSetCreate:
3373: * @val: an initial xmlNodePtr, or NULL
3374: *
3375: * Create a new xmlNodeSetPtr of type double and of value @val
3376: *
3377: * Returns the newly created object.
3378: */
3379: xmlNodeSetPtr
3380: xmlXPathNodeSetCreate(xmlNodePtr val) {
3381: xmlNodeSetPtr ret;
3382:
3383: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3384: if (ret == NULL) {
3385: xmlXPathErrMemory(NULL, "creating nodeset\n");
3386: return(NULL);
3387: }
3388: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
3389: if (val != NULL) {
3390: ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3391: sizeof(xmlNodePtr));
3392: if (ret->nodeTab == NULL) {
3393: xmlXPathErrMemory(NULL, "creating nodeset\n");
3394: xmlFree(ret);
3395: return(NULL);
3396: }
3397: memset(ret->nodeTab, 0 ,
3398: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3399: ret->nodeMax = XML_NODESET_DEFAULT;
3400: if (val->type == XML_NAMESPACE_DECL) {
3401: xmlNsPtr ns = (xmlNsPtr) val;
3402:
3403: ret->nodeTab[ret->nodeNr++] =
3404: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3405: } else
3406: ret->nodeTab[ret->nodeNr++] = val;
3407: }
3408: return(ret);
3409: }
3410:
3411: /**
3412: * xmlXPathNodeSetCreateSize:
3413: * @size: the initial size of the set
3414: *
3415: * Create a new xmlNodeSetPtr of type double and of value @val
3416: *
3417: * Returns the newly created object.
3418: */
3419: static xmlNodeSetPtr
3420: xmlXPathNodeSetCreateSize(int size) {
3421: xmlNodeSetPtr ret;
3422:
3423: ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
3424: if (ret == NULL) {
3425: xmlXPathErrMemory(NULL, "creating nodeset\n");
3426: return(NULL);
3427: }
3428: memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
3429: if (size < XML_NODESET_DEFAULT)
3430: size = XML_NODESET_DEFAULT;
3431: ret->nodeTab = (xmlNodePtr *) xmlMalloc(size * sizeof(xmlNodePtr));
3432: if (ret->nodeTab == NULL) {
3433: xmlXPathErrMemory(NULL, "creating nodeset\n");
3434: xmlFree(ret);
3435: return(NULL);
3436: }
3437: memset(ret->nodeTab, 0 , size * (size_t) sizeof(xmlNodePtr));
3438: ret->nodeMax = size;
3439: return(ret);
3440: }
3441:
3442: /**
3443: * xmlXPathNodeSetContains:
3444: * @cur: the node-set
3445: * @val: the node
3446: *
3447: * checks whether @cur contains @val
3448: *
3449: * Returns true (1) if @cur contains @val, false (0) otherwise
3450: */
3451: int
3452: xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
3453: int i;
3454:
3455: if ((cur == NULL) || (val == NULL)) return(0);
3456: if (val->type == XML_NAMESPACE_DECL) {
3457: for (i = 0; i < cur->nodeNr; i++) {
3458: if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
3459: xmlNsPtr ns1, ns2;
3460:
3461: ns1 = (xmlNsPtr) val;
3462: ns2 = (xmlNsPtr) cur->nodeTab[i];
3463: if (ns1 == ns2)
3464: return(1);
3465: if ((ns1->next != NULL) && (ns2->next == ns1->next) &&
3466: (xmlStrEqual(ns1->prefix, ns2->prefix)))
3467: return(1);
3468: }
3469: }
3470: } else {
3471: for (i = 0; i < cur->nodeNr; i++) {
3472: if (cur->nodeTab[i] == val)
3473: return(1);
3474: }
3475: }
3476: return(0);
3477: }
3478:
3479: /**
3480: * xmlXPathNodeSetAddNs:
3481: * @cur: the initial node set
3482: * @node: the hosting node
3483: * @ns: a the namespace node
3484: *
3485: * add a new namespace node to an existing NodeSet
3486: */
3487: void
3488: xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
3489: int i;
3490:
3491:
3492: if ((cur == NULL) || (ns == NULL) || (node == NULL) ||
3493: (ns->type != XML_NAMESPACE_DECL) ||
3494: (node->type != XML_ELEMENT_NODE))
3495: return;
3496:
3497: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3498: /*
3499: * prevent duplicates
3500: */
3501: for (i = 0;i < cur->nodeNr;i++) {
3502: if ((cur->nodeTab[i] != NULL) &&
3503: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
3504: (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
3505: (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
3506: return;
3507: }
3508:
3509: /*
3510: * grow the nodeTab if needed
3511: */
3512: if (cur->nodeMax == 0) {
3513: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3514: sizeof(xmlNodePtr));
3515: if (cur->nodeTab == NULL) {
3516: xmlXPathErrMemory(NULL, "growing nodeset\n");
3517: return;
3518: }
3519: memset(cur->nodeTab, 0 ,
3520: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3521: cur->nodeMax = XML_NODESET_DEFAULT;
3522: } else if (cur->nodeNr == cur->nodeMax) {
3523: xmlNodePtr *temp;
3524:
3525: cur->nodeMax *= 2;
3526: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
3527: sizeof(xmlNodePtr));
3528: if (temp == NULL) {
3529: xmlXPathErrMemory(NULL, "growing nodeset\n");
3530: return;
3531: }
3532: cur->nodeTab = temp;
3533: }
3534: cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
3535: }
3536:
3537: /**
3538: * xmlXPathNodeSetAdd:
3539: * @cur: the initial node set
3540: * @val: a new xmlNodePtr
3541: *
3542: * add a new xmlNodePtr to an existing NodeSet
3543: */
3544: void
3545: xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
3546: int i;
3547:
3548: if ((cur == NULL) || (val == NULL)) return;
3549:
3550: #if 0
3551: if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
3552: return; /* an XSLT fake node */
3553: #endif
3554:
3555: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3556: /*
3557: * prevent duplcates
3558: */
3559: for (i = 0;i < cur->nodeNr;i++)
3560: if (cur->nodeTab[i] == val) return;
3561:
3562: /*
3563: * grow the nodeTab if needed
3564: */
3565: if (cur->nodeMax == 0) {
3566: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3567: sizeof(xmlNodePtr));
3568: if (cur->nodeTab == NULL) {
3569: xmlXPathErrMemory(NULL, "growing nodeset\n");
3570: return;
3571: }
3572: memset(cur->nodeTab, 0 ,
3573: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3574: cur->nodeMax = XML_NODESET_DEFAULT;
3575: } else if (cur->nodeNr == cur->nodeMax) {
3576: xmlNodePtr *temp;
3577:
3578: cur->nodeMax *= 2;
3579: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
3580: sizeof(xmlNodePtr));
3581: if (temp == NULL) {
3582: xmlXPathErrMemory(NULL, "growing nodeset\n");
3583: return;
3584: }
3585: cur->nodeTab = temp;
3586: }
3587: if (val->type == XML_NAMESPACE_DECL) {
3588: xmlNsPtr ns = (xmlNsPtr) val;
3589:
3590: cur->nodeTab[cur->nodeNr++] =
3591: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3592: } else
3593: cur->nodeTab[cur->nodeNr++] = val;
3594: }
3595:
3596: /**
3597: * xmlXPathNodeSetAddUnique:
3598: * @cur: the initial node set
3599: * @val: a new xmlNodePtr
3600: *
3601: * add a new xmlNodePtr to an existing NodeSet, optimized version
3602: * when we are sure the node is not already in the set.
3603: */
3604: void
3605: xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
3606: if ((cur == NULL) || (val == NULL)) return;
3607:
3608: #if 0
3609: if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
3610: return; /* an XSLT fake node */
3611: #endif
3612:
3613: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3614: /*
3615: * grow the nodeTab if needed
3616: */
3617: if (cur->nodeMax == 0) {
3618: cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3619: sizeof(xmlNodePtr));
3620: if (cur->nodeTab == NULL) {
3621: xmlXPathErrMemory(NULL, "growing nodeset\n");
3622: return;
3623: }
3624: memset(cur->nodeTab, 0 ,
3625: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3626: cur->nodeMax = XML_NODESET_DEFAULT;
3627: } else if (cur->nodeNr == cur->nodeMax) {
3628: xmlNodePtr *temp;
3629:
3630: cur->nodeMax *= 2;
3631: temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
3632: sizeof(xmlNodePtr));
3633: if (temp == NULL) {
3634: xmlXPathErrMemory(NULL, "growing nodeset\n");
3635: return;
3636: }
3637: cur->nodeTab = temp;
3638: }
3639: if (val->type == XML_NAMESPACE_DECL) {
3640: xmlNsPtr ns = (xmlNsPtr) val;
3641:
3642: cur->nodeTab[cur->nodeNr++] =
3643: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3644: } else
3645: cur->nodeTab[cur->nodeNr++] = val;
3646: }
3647:
3648: /**
3649: * xmlXPathNodeSetMerge:
3650: * @val1: the first NodeSet or NULL
3651: * @val2: the second NodeSet
3652: *
3653: * Merges two nodesets, all nodes from @val2 are added to @val1
3654: * if @val1 is NULL, a new set is created and copied from @val2
3655: *
3656: * Returns @val1 once extended or NULL in case of error.
3657: */
3658: xmlNodeSetPtr
3659: xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
3660: int i, j, initNr, skip;
3661: xmlNodePtr n1, n2;
3662:
3663: if (val2 == NULL) return(val1);
3664: if (val1 == NULL) {
3665: val1 = xmlXPathNodeSetCreate(NULL);
3666: if (val1 == NULL)
3667: return (NULL);
3668: #if 0
3669: /*
3670: * TODO: The optimization won't work in every case, since
3671: * those nasty namespace nodes need to be added with
3672: * xmlXPathNodeSetDupNs() to the set; thus a pure
3673: * memcpy is not possible.
3674: * If there was a flag on the nodesetval, indicating that
3675: * some temporary nodes are in, that would be helpfull.
3676: */
3677: /*
3678: * Optimization: Create an equally sized node-set
3679: * and memcpy the content.
3680: */
3681: val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
3682: if (val1 == NULL)
3683: return(NULL);
3684: if (val2->nodeNr != 0) {
3685: if (val2->nodeNr == 1)
3686: *(val1->nodeTab) = *(val2->nodeTab);
3687: else {
3688: memcpy(val1->nodeTab, val2->nodeTab,
3689: val2->nodeNr * sizeof(xmlNodePtr));
3690: }
3691: val1->nodeNr = val2->nodeNr;
3692: }
3693: return(val1);
3694: #endif
3695: }
3696:
3697: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3698: initNr = val1->nodeNr;
3699:
3700: for (i = 0;i < val2->nodeNr;i++) {
3701: n2 = val2->nodeTab[i];
3702: /*
3703: * check against duplicates
3704: */
3705: skip = 0;
3706: for (j = 0; j < initNr; j++) {
3707: n1 = val1->nodeTab[j];
3708: if (n1 == n2) {
3709: skip = 1;
3710: break;
3711: } else if ((n1->type == XML_NAMESPACE_DECL) &&
3712: (n2->type == XML_NAMESPACE_DECL)) {
3713: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3714: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3715: ((xmlNsPtr) n2)->prefix)))
3716: {
3717: skip = 1;
3718: break;
3719: }
3720: }
3721: }
3722: if (skip)
3723: continue;
3724:
3725: /*
3726: * grow the nodeTab if needed
3727: */
3728: if (val1->nodeMax == 0) {
3729: val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3730: sizeof(xmlNodePtr));
3731: if (val1->nodeTab == NULL) {
3732: xmlXPathErrMemory(NULL, "merging nodeset\n");
3733: return(NULL);
3734: }
3735: memset(val1->nodeTab, 0 ,
3736: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3737: val1->nodeMax = XML_NODESET_DEFAULT;
3738: } else if (val1->nodeNr == val1->nodeMax) {
3739: xmlNodePtr *temp;
3740:
3741: val1->nodeMax *= 2;
3742: temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
3743: sizeof(xmlNodePtr));
3744: if (temp == NULL) {
3745: xmlXPathErrMemory(NULL, "merging nodeset\n");
3746: return(NULL);
3747: }
3748: val1->nodeTab = temp;
3749: }
3750: if (n2->type == XML_NAMESPACE_DECL) {
3751: xmlNsPtr ns = (xmlNsPtr) n2;
3752:
3753: val1->nodeTab[val1->nodeNr++] =
3754: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3755: } else
3756: val1->nodeTab[val1->nodeNr++] = n2;
3757: }
3758:
3759: return(val1);
3760: }
3761:
3762: #if 0 /* xmlXPathNodeSetMergeUnique() is currently not used anymore */
3763: /**
3764: * xmlXPathNodeSetMergeUnique:
3765: * @val1: the first NodeSet or NULL
3766: * @val2: the second NodeSet
3767: *
3768: * Merges two nodesets, all nodes from @val2 are added to @val1
3769: * if @val1 is NULL, a new set is created and copied from @val2
3770: *
3771: * Returns @val1 once extended or NULL in case of error.
3772: */
3773: static xmlNodeSetPtr
3774: xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
3775: int i;
3776:
3777: if (val2 == NULL) return(val1);
3778: if (val1 == NULL) {
3779: val1 = xmlXPathNodeSetCreate(NULL);
3780: }
3781: if (val1 == NULL)
3782: return (NULL);
3783:
3784: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
3785:
3786: for (i = 0;i < val2->nodeNr;i++) {
3787: /*
3788: * grow the nodeTab if needed
3789: */
3790: if (val1->nodeMax == 0) {
3791: val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
3792: sizeof(xmlNodePtr));
3793: if (val1->nodeTab == NULL) {
3794: xmlXPathErrMemory(NULL, "merging nodeset\n");
3795: return(NULL);
3796: }
3797: memset(val1->nodeTab, 0 ,
3798: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3799: val1->nodeMax = XML_NODESET_DEFAULT;
3800: } else if (val1->nodeNr == val1->nodeMax) {
3801: xmlNodePtr *temp;
3802:
3803: val1->nodeMax *= 2;
3804: temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
3805: sizeof(xmlNodePtr));
3806: if (temp == NULL) {
3807: xmlXPathErrMemory(NULL, "merging nodeset\n");
3808: return(NULL);
3809: }
3810: val1->nodeTab = temp;
3811: }
3812: if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
3813: xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
3814:
3815: val1->nodeTab[val1->nodeNr++] =
3816: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3817: } else
3818: val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
3819: }
3820:
3821: return(val1);
3822: }
3823: #endif /* xmlXPathNodeSetMergeUnique() is currently not used anymore */
3824:
3825: /**
3826: * xmlXPathNodeSetMergeAndClear:
3827: * @set1: the first NodeSet or NULL
3828: * @set2: the second NodeSet
3829: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
3830: *
3831: * Merges two nodesets, all nodes from @set2 are added to @set1
3832: * if @set1 is NULL, a new set is created and copied from @set2.
3833: * Checks for duplicate nodes. Clears set2.
3834: *
3835: * Returns @set1 once extended or NULL in case of error.
3836: */
3837: static xmlNodeSetPtr
3838: xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
3839: int hasNullEntries)
3840: {
3841: if ((set1 == NULL) && (hasNullEntries == 0)) {
3842: /*
3843: * Note that doing a memcpy of the list, namespace nodes are
3844: * just assigned to set1, since set2 is cleared anyway.
3845: */
3846: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
3847: if (set1 == NULL)
3848: return(NULL);
3849: if (set2->nodeNr != 0) {
3850: memcpy(set1->nodeTab, set2->nodeTab,
3851: set2->nodeNr * sizeof(xmlNodePtr));
3852: set1->nodeNr = set2->nodeNr;
3853: }
3854: } else {
3855: int i, j, initNbSet1;
3856: xmlNodePtr n1, n2;
3857:
3858: if (set1 == NULL)
3859: set1 = xmlXPathNodeSetCreate(NULL);
3860: if (set1 == NULL)
3861: return (NULL);
3862:
3863: initNbSet1 = set1->nodeNr;
3864: for (i = 0;i < set2->nodeNr;i++) {
3865: n2 = set2->nodeTab[i];
3866: /*
3867: * Skip NULLed entries.
3868: */
3869: if (n2 == NULL)
3870: continue;
3871: /*
3872: * Skip duplicates.
3873: */
3874: for (j = 0; j < initNbSet1; j++) {
3875: n1 = set1->nodeTab[j];
3876: if (n1 == n2) {
3877: goto skip_node;
3878: } else if ((n1->type == XML_NAMESPACE_DECL) &&
3879: (n2->type == XML_NAMESPACE_DECL))
3880: {
3881: if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
3882: (xmlStrEqual(((xmlNsPtr) n1)->prefix,
3883: ((xmlNsPtr) n2)->prefix)))
3884: {
3885: /*
3886: * Free the namespace node.
3887: */
3888: set2->nodeTab[i] = NULL;
3889: xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
3890: goto skip_node;
3891: }
3892: }
3893: }
3894: /*
3895: * grow the nodeTab if needed
3896: */
3897: if (set1->nodeMax == 0) {
3898: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
3899: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3900: if (set1->nodeTab == NULL) {
3901: xmlXPathErrMemory(NULL, "merging nodeset\n");
3902: return(NULL);
3903: }
3904: memset(set1->nodeTab, 0,
3905: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3906: set1->nodeMax = XML_NODESET_DEFAULT;
3907: } else if (set1->nodeNr >= set1->nodeMax) {
3908: xmlNodePtr *temp;
3909:
3910: set1->nodeMax *= 2;
3911: temp = (xmlNodePtr *) xmlRealloc(
3912: set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr));
3913: if (temp == NULL) {
3914: xmlXPathErrMemory(NULL, "merging nodeset\n");
3915: return(NULL);
3916: }
3917: set1->nodeTab = temp;
3918: }
3919: if (n2->type == XML_NAMESPACE_DECL) {
3920: xmlNsPtr ns = (xmlNsPtr) n2;
3921:
3922: set1->nodeTab[set1->nodeNr++] =
3923: xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
3924: } else
3925: set1->nodeTab[set1->nodeNr++] = n2;
3926: skip_node:
3927: {}
3928: }
3929: }
3930: set2->nodeNr = 0;
3931: return(set1);
3932: }
3933:
3934: /**
3935: * xmlXPathNodeSetMergeAndClearNoDupls:
3936: * @set1: the first NodeSet or NULL
3937: * @set2: the second NodeSet
3938: * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
3939: *
3940: * Merges two nodesets, all nodes from @set2 are added to @set1
3941: * if @set1 is NULL, a new set is created and copied from @set2.
3942: * Doesn't chack for duplicate nodes. Clears set2.
3943: *
3944: * Returns @set1 once extended or NULL in case of error.
3945: */
3946: static xmlNodeSetPtr
3947: xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
3948: int hasNullEntries)
3949: {
3950: if (set2 == NULL)
3951: return(set1);
3952: if ((set1 == NULL) && (hasNullEntries == 0)) {
3953: /*
3954: * Note that doing a memcpy of the list, namespace nodes are
3955: * just assigned to set1, since set2 is cleared anyway.
3956: */
3957: set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
3958: if (set1 == NULL)
3959: return(NULL);
3960: if (set2->nodeNr != 0) {
3961: memcpy(set1->nodeTab, set2->nodeTab,
3962: set2->nodeNr * sizeof(xmlNodePtr));
3963: set1->nodeNr = set2->nodeNr;
3964: }
3965: } else {
3966: int i;
3967: xmlNodePtr n2;
3968:
3969: if (set1 == NULL)
3970: set1 = xmlXPathNodeSetCreate(NULL);
3971: if (set1 == NULL)
3972: return (NULL);
3973:
3974: for (i = 0;i < set2->nodeNr;i++) {
3975: n2 = set2->nodeTab[i];
3976: /*
3977: * Skip NULLed entries.
3978: */
3979: if (n2 == NULL)
3980: continue;
3981: if (set1->nodeMax == 0) {
3982: set1->nodeTab = (xmlNodePtr *) xmlMalloc(
3983: XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
3984: if (set1->nodeTab == NULL) {
3985: xmlXPathErrMemory(NULL, "merging nodeset\n");
3986: return(NULL);
3987: }
3988: memset(set1->nodeTab, 0,
3989: XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
3990: set1->nodeMax = XML_NODESET_DEFAULT;
3991: } else if (set1->nodeNr >= set1->nodeMax) {
3992: xmlNodePtr *temp;
3993:
3994: set1->nodeMax *= 2;
3995: temp = (xmlNodePtr *) xmlRealloc(
3996: set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr));
3997: if (temp == NULL) {
3998: xmlXPathErrMemory(NULL, "merging nodeset\n");
3999: return(NULL);
4000: }
4001: set1->nodeTab = temp;
4002: }
4003: set1->nodeTab[set1->nodeNr++] = n2;
4004: }
4005: }
4006: set2->nodeNr = 0;
4007: return(set1);
4008: }
4009:
4010: /**
4011: * xmlXPathNodeSetDel:
4012: * @cur: the initial node set
4013: * @val: an xmlNodePtr
4014: *
4015: * Removes an xmlNodePtr from an existing NodeSet
4016: */
4017: void
4018: xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
4019: int i;
4020:
4021: if (cur == NULL) return;
4022: if (val == NULL) return;
4023:
4024: /*
4025: * find node in nodeTab
4026: */
4027: for (i = 0;i < cur->nodeNr;i++)
4028: if (cur->nodeTab[i] == val) break;
4029:
4030: if (i >= cur->nodeNr) { /* not found */
4031: #ifdef DEBUG
4032: xmlGenericError(xmlGenericErrorContext,
4033: "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
4034: val->name);
4035: #endif
4036: return;
4037: }
4038: if ((cur->nodeTab[i] != NULL) &&
4039: (cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
4040: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
4041: cur->nodeNr--;
4042: for (;i < cur->nodeNr;i++)
4043: cur->nodeTab[i] = cur->nodeTab[i + 1];
4044: cur->nodeTab[cur->nodeNr] = NULL;
4045: }
4046:
4047: /**
4048: * xmlXPathNodeSetRemove:
4049: * @cur: the initial node set
4050: * @val: the index to remove
4051: *
4052: * Removes an entry from an existing NodeSet list.
4053: */
4054: void
4055: xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
4056: if (cur == NULL) return;
4057: if (val >= cur->nodeNr) return;
4058: if ((cur->nodeTab[val] != NULL) &&
4059: (cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
4060: xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
4061: cur->nodeNr--;
4062: for (;val < cur->nodeNr;val++)
4063: cur->nodeTab[val] = cur->nodeTab[val + 1];
4064: cur->nodeTab[cur->nodeNr] = NULL;
4065: }
4066:
4067: /**
4068: * xmlXPathFreeNodeSet:
4069: * @obj: the xmlNodeSetPtr to free
4070: *
4071: * Free the NodeSet compound (not the actual nodes !).
4072: */
4073: void
4074: xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
4075: if (obj == NULL) return;
4076: if (obj->nodeTab != NULL) {
4077: int i;
4078:
4079: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4080: for (i = 0;i < obj->nodeNr;i++)
4081: if ((obj->nodeTab[i] != NULL) &&
4082: (obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
4083: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4084: xmlFree(obj->nodeTab);
4085: }
4086: xmlFree(obj);
4087: }
4088:
4089: /**
4090: * xmlXPathNodeSetClear:
4091: * @set: the node set to clear
4092: *
4093: * Clears the list from all temporary XPath objects (e.g. namespace nodes
4094: * are feed), but does *not* free the list itself. Sets the length of the
4095: * list to 0.
4096: */
4097: static void
4098: xmlXPathNodeSetClear(xmlNodeSetPtr set, int hasNsNodes)
4099: {
4100: if ((set == NULL) || (set->nodeNr <= 0))
4101: return;
4102: else if (hasNsNodes) {
4103: int i;
4104: xmlNodePtr node;
4105:
4106: for (i = 0; i < set->nodeNr; i++) {
4107: node = set->nodeTab[i];
4108: if ((node != NULL) &&
4109: (node->type == XML_NAMESPACE_DECL))
4110: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4111: }
4112: }
4113: set->nodeNr = 0;
4114: }
4115:
4116: /**
4117: * xmlXPathNodeSetClearFromPos:
4118: * @set: the node set to be cleared
4119: * @pos: the start position to clear from
4120: *
4121: * Clears the list from temporary XPath objects (e.g. namespace nodes
4122: * are feed) starting with the entry at @pos, but does *not* free the list
4123: * itself. Sets the length of the list to @pos.
4124: */
4125: static void
4126: xmlXPathNodeSetClearFromPos(xmlNodeSetPtr set, int pos, int hasNsNodes)
4127: {
4128: if ((set == NULL) || (set->nodeNr <= 0) || (pos >= set->nodeNr))
4129: return;
4130: else if ((hasNsNodes)) {
4131: int i;
4132: xmlNodePtr node;
4133:
4134: for (i = pos; i < set->nodeNr; i++) {
4135: node = set->nodeTab[i];
4136: if ((node != NULL) &&
4137: (node->type == XML_NAMESPACE_DECL))
4138: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
4139: }
4140: }
4141: set->nodeNr = pos;
4142: }
4143:
4144: /**
4145: * xmlXPathFreeValueTree:
4146: * @obj: the xmlNodeSetPtr to free
4147: *
4148: * Free the NodeSet compound and the actual tree, this is different
4149: * from xmlXPathFreeNodeSet()
4150: */
4151: static void
4152: xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
4153: int i;
4154:
4155: if (obj == NULL) return;
4156:
4157: if (obj->nodeTab != NULL) {
4158: for (i = 0;i < obj->nodeNr;i++) {
4159: if (obj->nodeTab[i] != NULL) {
4160: if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
4161: xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
4162: } else {
4163: xmlFreeNodeList(obj->nodeTab[i]);
4164: }
4165: }
4166: }
4167: xmlFree(obj->nodeTab);
4168: }
4169: xmlFree(obj);
4170: }
4171:
4172: #if defined(DEBUG) || defined(DEBUG_STEP)
4173: /**
4174: * xmlGenericErrorContextNodeSet:
4175: * @output: a FILE * for the output
4176: * @obj: the xmlNodeSetPtr to display
4177: *
4178: * Quick display of a NodeSet
4179: */
4180: void
4181: xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
4182: int i;
4183:
4184: if (output == NULL) output = xmlGenericErrorContext;
4185: if (obj == NULL) {
4186: fprintf(output, "NodeSet == NULL !\n");
4187: return;
4188: }
4189: if (obj->nodeNr == 0) {
4190: fprintf(output, "NodeSet is empty\n");
4191: return;
4192: }
4193: if (obj->nodeTab == NULL) {
4194: fprintf(output, " nodeTab == NULL !\n");
4195: return;
4196: }
4197: for (i = 0; i < obj->nodeNr; i++) {
4198: if (obj->nodeTab[i] == NULL) {
4199: fprintf(output, " NULL !\n");
4200: return;
4201: }
4202: if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
4203: (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
4204: fprintf(output, " /");
4205: else if (obj->nodeTab[i]->name == NULL)
4206: fprintf(output, " noname!");
4207: else fprintf(output, " %s", obj->nodeTab[i]->name);
4208: }
4209: fprintf(output, "\n");
4210: }
4211: #endif
4212:
4213: /**
4214: * xmlXPathNewNodeSet:
4215: * @val: the NodePtr value
4216: *
4217: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4218: * it with the single Node @val
4219: *
4220: * Returns the newly created object.
4221: */
4222: xmlXPathObjectPtr
4223: xmlXPathNewNodeSet(xmlNodePtr val) {
4224: xmlXPathObjectPtr ret;
4225:
4226: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4227: if (ret == NULL) {
4228: xmlXPathErrMemory(NULL, "creating nodeset\n");
4229: return(NULL);
4230: }
4231: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4232: ret->type = XPATH_NODESET;
4233: ret->boolval = 0;
4234: ret->nodesetval = xmlXPathNodeSetCreate(val);
4235: /* @@ with_ns to check whether namespace nodes should be looked at @@ */
4236: #ifdef XP_DEBUG_OBJ_USAGE
4237: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4238: #endif
4239: return(ret);
4240: }
4241:
4242: /**
4243: * xmlXPathNewValueTree:
4244: * @val: the NodePtr value
4245: *
4246: * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
4247: * it with the tree root @val
4248: *
4249: * Returns the newly created object.
4250: */
4251: xmlXPathObjectPtr
4252: xmlXPathNewValueTree(xmlNodePtr val) {
4253: xmlXPathObjectPtr ret;
4254:
4255: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4256: if (ret == NULL) {
4257: xmlXPathErrMemory(NULL, "creating result value tree\n");
4258: return(NULL);
4259: }
4260: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4261: ret->type = XPATH_XSLT_TREE;
4262: ret->boolval = 1;
4263: ret->user = (void *) val;
4264: ret->nodesetval = xmlXPathNodeSetCreate(val);
4265: #ifdef XP_DEBUG_OBJ_USAGE
4266: xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
4267: #endif
4268: return(ret);
4269: }
4270:
4271: /**
4272: * xmlXPathNewNodeSetList:
4273: * @val: an existing NodeSet
4274: *
4275: * Create a new xmlXPathObjectPtr of type NodeSet and initialize
4276: * it with the Nodeset @val
4277: *
4278: * Returns the newly created object.
4279: */
4280: xmlXPathObjectPtr
4281: xmlXPathNewNodeSetList(xmlNodeSetPtr val)
4282: {
4283: xmlXPathObjectPtr ret;
4284: int i;
4285:
4286: if (val == NULL)
4287: ret = NULL;
4288: else if (val->nodeTab == NULL)
4289: ret = xmlXPathNewNodeSet(NULL);
4290: else {
4291: ret = xmlXPathNewNodeSet(val->nodeTab[0]);
4292: if (ret)
4293: for (i = 1; i < val->nodeNr; ++i)
4294: xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
4295: }
4296:
4297: return (ret);
4298: }
4299:
4300: /**
4301: * xmlXPathWrapNodeSet:
4302: * @val: the NodePtr value
4303: *
4304: * Wrap the Nodeset @val in a new xmlXPathObjectPtr
4305: *
4306: * Returns the newly created object.
4307: */
4308: xmlXPathObjectPtr
4309: xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
4310: xmlXPathObjectPtr ret;
4311:
4312: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
4313: if (ret == NULL) {
4314: xmlXPathErrMemory(NULL, "creating node set object\n");
4315: return(NULL);
4316: }
4317: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
4318: ret->type = XPATH_NODESET;
4319: ret->nodesetval = val;
4320: #ifdef XP_DEBUG_OBJ_USAGE
4321: xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
4322: #endif
4323: return(ret);
4324: }
4325:
4326: /**
4327: * xmlXPathFreeNodeSetList:
4328: * @obj: an existing NodeSetList object
4329: *
4330: * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
4331: * the list contrary to xmlXPathFreeObject().
4332: */
4333: void
4334: xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
4335: if (obj == NULL) return;
4336: #ifdef XP_DEBUG_OBJ_USAGE
4337: xmlXPathDebugObjUsageReleased(NULL, obj->type);
4338: #endif
4339: xmlFree(obj);
4340: }
4341:
4342: /**
4343: * xmlXPathDifference:
4344: * @nodes1: a node-set
4345: * @nodes2: a node-set
4346: *
4347: * Implements the EXSLT - Sets difference() function:
4348: * node-set set:difference (node-set, node-set)
4349: *
4350: * Returns the difference between the two node sets, or nodes1 if
4351: * nodes2 is empty
4352: */
4353: xmlNodeSetPtr
4354: xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4355: xmlNodeSetPtr ret;
4356: int i, l1;
4357: xmlNodePtr cur;
4358:
4359: if (xmlXPathNodeSetIsEmpty(nodes2))
4360: return(nodes1);
4361:
4362: ret = xmlXPathNodeSetCreate(NULL);
4363: if (xmlXPathNodeSetIsEmpty(nodes1))
4364: return(ret);
4365:
4366: l1 = xmlXPathNodeSetGetLength(nodes1);
4367:
4368: for (i = 0; i < l1; i++) {
4369: cur = xmlXPathNodeSetItem(nodes1, i);
4370: if (!xmlXPathNodeSetContains(nodes2, cur))
4371: xmlXPathNodeSetAddUnique(ret, cur);
4372: }
4373: return(ret);
4374: }
4375:
4376: /**
4377: * xmlXPathIntersection:
4378: * @nodes1: a node-set
4379: * @nodes2: a node-set
4380: *
4381: * Implements the EXSLT - Sets intersection() function:
4382: * node-set set:intersection (node-set, node-set)
4383: *
4384: * Returns a node set comprising the nodes that are within both the
4385: * node sets passed as arguments
4386: */
4387: xmlNodeSetPtr
4388: xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4389: xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
4390: int i, l1;
4391: xmlNodePtr cur;
4392:
4393: if (ret == NULL)
4394: return(ret);
4395: if (xmlXPathNodeSetIsEmpty(nodes1))
4396: return(ret);
4397: if (xmlXPathNodeSetIsEmpty(nodes2))
4398: return(ret);
4399:
4400: l1 = xmlXPathNodeSetGetLength(nodes1);
4401:
4402: for (i = 0; i < l1; i++) {
4403: cur = xmlXPathNodeSetItem(nodes1, i);
4404: if (xmlXPathNodeSetContains(nodes2, cur))
4405: xmlXPathNodeSetAddUnique(ret, cur);
4406: }
4407: return(ret);
4408: }
4409:
4410: /**
4411: * xmlXPathDistinctSorted:
4412: * @nodes: a node-set, sorted by document order
4413: *
4414: * Implements the EXSLT - Sets distinct() function:
4415: * node-set set:distinct (node-set)
4416: *
4417: * Returns a subset of the nodes contained in @nodes, or @nodes if
4418: * it is empty
4419: */
4420: xmlNodeSetPtr
4421: xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
4422: xmlNodeSetPtr ret;
4423: xmlHashTablePtr hash;
4424: int i, l;
4425: xmlChar * strval;
4426: xmlNodePtr cur;
4427:
4428: if (xmlXPathNodeSetIsEmpty(nodes))
4429: return(nodes);
4430:
4431: ret = xmlXPathNodeSetCreate(NULL);
4432: if (ret == NULL)
4433: return(ret);
4434: l = xmlXPathNodeSetGetLength(nodes);
4435: hash = xmlHashCreate (l);
4436: for (i = 0; i < l; i++) {
4437: cur = xmlXPathNodeSetItem(nodes, i);
4438: strval = xmlXPathCastNodeToString(cur);
4439: if (xmlHashLookup(hash, strval) == NULL) {
4440: xmlHashAddEntry(hash, strval, strval);
4441: xmlXPathNodeSetAddUnique(ret, cur);
4442: } else {
4443: xmlFree(strval);
4444: }
4445: }
4446: xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
4447: return(ret);
4448: }
4449:
4450: /**
4451: * xmlXPathDistinct:
4452: * @nodes: a node-set
4453: *
4454: * Implements the EXSLT - Sets distinct() function:
4455: * node-set set:distinct (node-set)
4456: * @nodes is sorted by document order, then #exslSetsDistinctSorted
4457: * is called with the sorted node-set
4458: *
4459: * Returns a subset of the nodes contained in @nodes, or @nodes if
4460: * it is empty
4461: */
4462: xmlNodeSetPtr
4463: xmlXPathDistinct (xmlNodeSetPtr nodes) {
4464: if (xmlXPathNodeSetIsEmpty(nodes))
4465: return(nodes);
4466:
4467: xmlXPathNodeSetSort(nodes);
4468: return(xmlXPathDistinctSorted(nodes));
4469: }
4470:
4471: /**
4472: * xmlXPathHasSameNodes:
4473: * @nodes1: a node-set
4474: * @nodes2: a node-set
4475: *
4476: * Implements the EXSLT - Sets has-same-nodes function:
4477: * boolean set:has-same-node(node-set, node-set)
4478: *
4479: * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
4480: * otherwise
4481: */
4482: int
4483: xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4484: int i, l;
4485: xmlNodePtr cur;
4486:
4487: if (xmlXPathNodeSetIsEmpty(nodes1) ||
4488: xmlXPathNodeSetIsEmpty(nodes2))
4489: return(0);
4490:
4491: l = xmlXPathNodeSetGetLength(nodes1);
4492: for (i = 0; i < l; i++) {
4493: cur = xmlXPathNodeSetItem(nodes1, i);
4494: if (xmlXPathNodeSetContains(nodes2, cur))
4495: return(1);
4496: }
4497: return(0);
4498: }
4499:
4500: /**
4501: * xmlXPathNodeLeadingSorted:
4502: * @nodes: a node-set, sorted by document order
4503: * @node: a node
4504: *
4505: * Implements the EXSLT - Sets leading() function:
4506: * node-set set:leading (node-set, node-set)
4507: *
4508: * Returns the nodes in @nodes that precede @node in document order,
4509: * @nodes if @node is NULL or an empty node-set if @nodes
4510: * doesn't contain @node
4511: */
4512: xmlNodeSetPtr
4513: xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4514: int i, l;
4515: xmlNodePtr cur;
4516: xmlNodeSetPtr ret;
4517:
4518: if (node == NULL)
4519: return(nodes);
4520:
4521: ret = xmlXPathNodeSetCreate(NULL);
4522: if (ret == NULL)
4523: return(ret);
4524: if (xmlXPathNodeSetIsEmpty(nodes) ||
4525: (!xmlXPathNodeSetContains(nodes, node)))
4526: return(ret);
4527:
4528: l = xmlXPathNodeSetGetLength(nodes);
4529: for (i = 0; i < l; i++) {
4530: cur = xmlXPathNodeSetItem(nodes, i);
4531: if (cur == node)
4532: break;
4533: xmlXPathNodeSetAddUnique(ret, cur);
4534: }
4535: return(ret);
4536: }
4537:
4538: /**
4539: * xmlXPathNodeLeading:
4540: * @nodes: a node-set
4541: * @node: a node
4542: *
4543: * Implements the EXSLT - Sets leading() function:
4544: * node-set set:leading (node-set, node-set)
4545: * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
4546: * is called.
4547: *
4548: * Returns the nodes in @nodes that precede @node in document order,
4549: * @nodes if @node is NULL or an empty node-set if @nodes
4550: * doesn't contain @node
4551: */
4552: xmlNodeSetPtr
4553: xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
4554: xmlXPathNodeSetSort(nodes);
4555: return(xmlXPathNodeLeadingSorted(nodes, node));
4556: }
4557:
4558: /**
4559: * xmlXPathLeadingSorted:
4560: * @nodes1: a node-set, sorted by document order
4561: * @nodes2: a node-set, sorted by document order
4562: *
4563: * Implements the EXSLT - Sets leading() function:
4564: * node-set set:leading (node-set, node-set)
4565: *
4566: * Returns the nodes in @nodes1 that precede the first node in @nodes2
4567: * in document order, @nodes1 if @nodes2 is NULL or empty or
4568: * an empty node-set if @nodes1 doesn't contain @nodes2
4569: */
4570: xmlNodeSetPtr
4571: xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4572: if (xmlXPathNodeSetIsEmpty(nodes2))
4573: return(nodes1);
4574: return(xmlXPathNodeLeadingSorted(nodes1,
4575: xmlXPathNodeSetItem(nodes2, 1)));
4576: }
4577:
4578: /**
4579: * xmlXPathLeading:
4580: * @nodes1: a node-set
4581: * @nodes2: a node-set
4582: *
4583: * Implements the EXSLT - Sets leading() function:
4584: * node-set set:leading (node-set, node-set)
4585: * @nodes1 and @nodes2 are sorted by document order, then
4586: * #exslSetsLeadingSorted is called.
4587: *
4588: * Returns the nodes in @nodes1 that precede the first node in @nodes2
4589: * in document order, @nodes1 if @nodes2 is NULL or empty or
4590: * an empty node-set if @nodes1 doesn't contain @nodes2
4591: */
4592: xmlNodeSetPtr
4593: xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4594: if (xmlXPathNodeSetIsEmpty(nodes2))
4595: return(nodes1);
4596: if (xmlXPathNodeSetIsEmpty(nodes1))
4597: return(xmlXPathNodeSetCreate(NULL));
4598: xmlXPathNodeSetSort(nodes1);
4599: xmlXPathNodeSetSort(nodes2);
4600: return(xmlXPathNodeLeadingSorted(nodes1,
4601: xmlXPathNodeSetItem(nodes2, 1)));
4602: }
4603:
4604: /**
4605: * xmlXPathNodeTrailingSorted:
4606: * @nodes: a node-set, sorted by document order
4607: * @node: a node
4608: *
4609: * Implements the EXSLT - Sets trailing() function:
4610: * node-set set:trailing (node-set, node-set)
4611: *
4612: * Returns the nodes in @nodes that follow @node in document order,
4613: * @nodes if @node is NULL or an empty node-set if @nodes
4614: * doesn't contain @node
4615: */
4616: xmlNodeSetPtr
4617: xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
4618: int i, l;
4619: xmlNodePtr cur;
4620: xmlNodeSetPtr ret;
4621:
4622: if (node == NULL)
4623: return(nodes);
4624:
4625: ret = xmlXPathNodeSetCreate(NULL);
4626: if (ret == NULL)
4627: return(ret);
4628: if (xmlXPathNodeSetIsEmpty(nodes) ||
4629: (!xmlXPathNodeSetContains(nodes, node)))
4630: return(ret);
4631:
4632: l = xmlXPathNodeSetGetLength(nodes);
4633: for (i = l - 1; i >= 0; i--) {
4634: cur = xmlXPathNodeSetItem(nodes, i);
4635: if (cur == node)
4636: break;
4637: xmlXPathNodeSetAddUnique(ret, cur);
4638: }
4639: xmlXPathNodeSetSort(ret); /* bug 413451 */
4640: return(ret);
4641: }
4642:
4643: /**
4644: * xmlXPathNodeTrailing:
4645: * @nodes: a node-set
4646: * @node: a node
4647: *
4648: * Implements the EXSLT - Sets trailing() function:
4649: * node-set set:trailing (node-set, node-set)
4650: * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
4651: * is called.
4652: *
4653: * Returns the nodes in @nodes that follow @node in document order,
4654: * @nodes if @node is NULL or an empty node-set if @nodes
4655: * doesn't contain @node
4656: */
4657: xmlNodeSetPtr
4658: xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
4659: xmlXPathNodeSetSort(nodes);
4660: return(xmlXPathNodeTrailingSorted(nodes, node));
4661: }
4662:
4663: /**
4664: * xmlXPathTrailingSorted:
4665: * @nodes1: a node-set, sorted by document order
4666: * @nodes2: a node-set, sorted by document order
4667: *
4668: * Implements the EXSLT - Sets trailing() function:
4669: * node-set set:trailing (node-set, node-set)
4670: *
4671: * Returns the nodes in @nodes1 that follow the first node in @nodes2
4672: * in document order, @nodes1 if @nodes2 is NULL or empty or
4673: * an empty node-set if @nodes1 doesn't contain @nodes2
4674: */
4675: xmlNodeSetPtr
4676: xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4677: if (xmlXPathNodeSetIsEmpty(nodes2))
4678: return(nodes1);
4679: return(xmlXPathNodeTrailingSorted(nodes1,
4680: xmlXPathNodeSetItem(nodes2, 0)));
4681: }
4682:
4683: /**
4684: * xmlXPathTrailing:
4685: * @nodes1: a node-set
4686: * @nodes2: a node-set
4687: *
4688: * Implements the EXSLT - Sets trailing() function:
4689: * node-set set:trailing (node-set, node-set)
4690: * @nodes1 and @nodes2 are sorted by document order, then
4691: * #xmlXPathTrailingSorted is called.
4692: *
4693: * Returns the nodes in @nodes1 that follow the first node in @nodes2
4694: * in document order, @nodes1 if @nodes2 is NULL or empty or
4695: * an empty node-set if @nodes1 doesn't contain @nodes2
4696: */
4697: xmlNodeSetPtr
4698: xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
4699: if (xmlXPathNodeSetIsEmpty(nodes2))
4700: return(nodes1);
4701: if (xmlXPathNodeSetIsEmpty(nodes1))
4702: return(xmlXPathNodeSetCreate(NULL));
4703: xmlXPathNodeSetSort(nodes1);
4704: xmlXPathNodeSetSort(nodes2);
4705: return(xmlXPathNodeTrailingSorted(nodes1,
4706: xmlXPathNodeSetItem(nodes2, 0)));
4707: }
4708:
4709: /************************************************************************
4710: * *
4711: * Routines to handle extra functions *
4712: * *
4713: ************************************************************************/
4714:
4715: /**
4716: * xmlXPathRegisterFunc:
4717: * @ctxt: the XPath context
4718: * @name: the function name
4719: * @f: the function implementation or NULL
4720: *
4721: * Register a new function. If @f is NULL it unregisters the function
4722: *
4723: * Returns 0 in case of success, -1 in case of error
4724: */
4725: int
4726: xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
4727: xmlXPathFunction f) {
4728: return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
4729: }
4730:
4731: /**
4732: * xmlXPathRegisterFuncNS:
4733: * @ctxt: the XPath context
4734: * @name: the function name
4735: * @ns_uri: the function namespace URI
4736: * @f: the function implementation or NULL
4737: *
4738: * Register a new function. If @f is NULL it unregisters the function
4739: *
4740: * Returns 0 in case of success, -1 in case of error
4741: */
4742: int
4743: xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4744: const xmlChar *ns_uri, xmlXPathFunction f) {
4745: if (ctxt == NULL)
4746: return(-1);
4747: if (name == NULL)
4748: return(-1);
4749:
4750: if (ctxt->funcHash == NULL)
4751: ctxt->funcHash = xmlHashCreate(0);
4752: if (ctxt->funcHash == NULL)
4753: return(-1);
4754: if (f == NULL)
4755: return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
4756: return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, XML_CAST_FPTR(f)));
4757: }
4758:
4759: /**
4760: * xmlXPathRegisterFuncLookup:
4761: * @ctxt: the XPath context
4762: * @f: the lookup function
4763: * @funcCtxt: the lookup data
4764: *
4765: * Registers an external mechanism to do function lookup.
4766: */
4767: void
4768: xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
4769: xmlXPathFuncLookupFunc f,
4770: void *funcCtxt) {
4771: if (ctxt == NULL)
4772: return;
4773: ctxt->funcLookupFunc = f;
4774: ctxt->funcLookupData = funcCtxt;
4775: }
4776:
4777: /**
4778: * xmlXPathFunctionLookup:
4779: * @ctxt: the XPath context
4780: * @name: the function name
4781: *
4782: * Search in the Function array of the context for the given
4783: * function.
4784: *
4785: * Returns the xmlXPathFunction or NULL if not found
4786: */
4787: xmlXPathFunction
4788: xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
4789: if (ctxt == NULL)
4790: return (NULL);
4791:
4792: if (ctxt->funcLookupFunc != NULL) {
4793: xmlXPathFunction ret;
4794: xmlXPathFuncLookupFunc f;
4795:
4796: f = ctxt->funcLookupFunc;
4797: ret = f(ctxt->funcLookupData, name, NULL);
4798: if (ret != NULL)
4799: return(ret);
4800: }
4801: return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
4802: }
4803:
4804: /**
4805: * xmlXPathFunctionLookupNS:
4806: * @ctxt: the XPath context
4807: * @name: the function name
4808: * @ns_uri: the function namespace URI
4809: *
4810: * Search in the Function array of the context for the given
4811: * function.
4812: *
4813: * Returns the xmlXPathFunction or NULL if not found
4814: */
4815: xmlXPathFunction
4816: xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4817: const xmlChar *ns_uri) {
4818: xmlXPathFunction ret;
4819:
4820: if (ctxt == NULL)
4821: return(NULL);
4822: if (name == NULL)
4823: return(NULL);
4824:
4825: if (ctxt->funcLookupFunc != NULL) {
4826: xmlXPathFuncLookupFunc f;
4827:
4828: f = ctxt->funcLookupFunc;
4829: ret = f(ctxt->funcLookupData, name, ns_uri);
4830: if (ret != NULL)
4831: return(ret);
4832: }
4833:
4834: if (ctxt->funcHash == NULL)
4835: return(NULL);
4836:
4837: XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
4838: return(ret);
4839: }
4840:
4841: /**
4842: * xmlXPathRegisteredFuncsCleanup:
4843: * @ctxt: the XPath context
4844: *
4845: * Cleanup the XPath context data associated to registered functions
4846: */
4847: void
4848: xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
4849: if (ctxt == NULL)
4850: return;
4851:
4852: xmlHashFree(ctxt->funcHash, NULL);
4853: ctxt->funcHash = NULL;
4854: }
4855:
4856: /************************************************************************
4857: * *
4858: * Routines to handle Variables *
4859: * *
4860: ************************************************************************/
4861:
4862: /**
4863: * xmlXPathRegisterVariable:
4864: * @ctxt: the XPath context
4865: * @name: the variable name
4866: * @value: the variable value or NULL
4867: *
4868: * Register a new variable value. If @value is NULL it unregisters
4869: * the variable
4870: *
4871: * Returns 0 in case of success, -1 in case of error
4872: */
4873: int
4874: xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
4875: xmlXPathObjectPtr value) {
4876: return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
4877: }
4878:
4879: /**
4880: * xmlXPathRegisterVariableNS:
4881: * @ctxt: the XPath context
4882: * @name: the variable name
4883: * @ns_uri: the variable namespace URI
4884: * @value: the variable value or NULL
4885: *
4886: * Register a new variable value. If @value is NULL it unregisters
4887: * the variable
4888: *
4889: * Returns 0 in case of success, -1 in case of error
4890: */
4891: int
4892: xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4893: const xmlChar *ns_uri,
4894: xmlXPathObjectPtr value) {
4895: if (ctxt == NULL)
4896: return(-1);
4897: if (name == NULL)
4898: return(-1);
4899:
4900: if (ctxt->varHash == NULL)
4901: ctxt->varHash = xmlHashCreate(0);
4902: if (ctxt->varHash == NULL)
4903: return(-1);
4904: if (value == NULL)
4905: return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
4906: (xmlHashDeallocator)xmlXPathFreeObject));
4907: return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
4908: (void *) value,
4909: (xmlHashDeallocator)xmlXPathFreeObject));
4910: }
4911:
4912: /**
4913: * xmlXPathRegisterVariableLookup:
4914: * @ctxt: the XPath context
4915: * @f: the lookup function
4916: * @data: the lookup data
4917: *
4918: * register an external mechanism to do variable lookup
4919: */
4920: void
4921: xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
4922: xmlXPathVariableLookupFunc f, void *data) {
4923: if (ctxt == NULL)
4924: return;
4925: ctxt->varLookupFunc = f;
4926: ctxt->varLookupData = data;
4927: }
4928:
4929: /**
4930: * xmlXPathVariableLookup:
4931: * @ctxt: the XPath context
4932: * @name: the variable name
4933: *
4934: * Search in the Variable array of the context for the given
4935: * variable value.
4936: *
4937: * Returns a copy of the value or NULL if not found
4938: */
4939: xmlXPathObjectPtr
4940: xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
4941: if (ctxt == NULL)
4942: return(NULL);
4943:
4944: if (ctxt->varLookupFunc != NULL) {
4945: xmlXPathObjectPtr ret;
4946:
4947: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
4948: (ctxt->varLookupData, name, NULL);
4949: return(ret);
4950: }
4951: return(xmlXPathVariableLookupNS(ctxt, name, NULL));
4952: }
4953:
4954: /**
4955: * xmlXPathVariableLookupNS:
4956: * @ctxt: the XPath context
4957: * @name: the variable name
4958: * @ns_uri: the variable namespace URI
4959: *
4960: * Search in the Variable array of the context for the given
4961: * variable value.
4962: *
4963: * Returns the a copy of the value or NULL if not found
4964: */
4965: xmlXPathObjectPtr
4966: xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
4967: const xmlChar *ns_uri) {
4968: if (ctxt == NULL)
4969: return(NULL);
4970:
4971: if (ctxt->varLookupFunc != NULL) {
4972: xmlXPathObjectPtr ret;
4973:
4974: ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
4975: (ctxt->varLookupData, name, ns_uri);
4976: if (ret != NULL) return(ret);
4977: }
4978:
4979: if (ctxt->varHash == NULL)
4980: return(NULL);
4981: if (name == NULL)
4982: return(NULL);
4983:
4984: return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
4985: xmlHashLookup2(ctxt->varHash, name, ns_uri)));
4986: }
4987:
4988: /**
4989: * xmlXPathRegisteredVariablesCleanup:
4990: * @ctxt: the XPath context
4991: *
4992: * Cleanup the XPath context data associated to registered variables
4993: */
4994: void
4995: xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
4996: if (ctxt == NULL)
4997: return;
4998:
4999: xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
5000: ctxt->varHash = NULL;
5001: }
5002:
5003: /**
5004: * xmlXPathRegisterNs:
5005: * @ctxt: the XPath context
5006: * @prefix: the namespace prefix cannot be NULL or empty string
5007: * @ns_uri: the namespace name
5008: *
5009: * Register a new namespace. If @ns_uri is NULL it unregisters
5010: * the namespace
5011: *
5012: * Returns 0 in case of success, -1 in case of error
5013: */
5014: int
5015: xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
5016: const xmlChar *ns_uri) {
5017: if (ctxt == NULL)
5018: return(-1);
5019: if (prefix == NULL)
5020: return(-1);
5021: if (prefix[0] == 0)
5022: return(-1);
5023:
5024: if (ctxt->nsHash == NULL)
5025: ctxt->nsHash = xmlHashCreate(10);
5026: if (ctxt->nsHash == NULL)
5027: return(-1);
5028: if (ns_uri == NULL)
5029: return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
5030: (xmlHashDeallocator)xmlFree));
5031: return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
5032: (xmlHashDeallocator)xmlFree));
5033: }
5034:
5035: /**
5036: * xmlXPathNsLookup:
5037: * @ctxt: the XPath context
5038: * @prefix: the namespace prefix value
5039: *
5040: * Search in the namespace declaration array of the context for the given
5041: * namespace name associated to the given prefix
5042: *
5043: * Returns the value or NULL if not found
5044: */
5045: const xmlChar *
5046: xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
5047: if (ctxt == NULL)
5048: return(NULL);
5049: if (prefix == NULL)
5050: return(NULL);
5051:
5052: #ifdef XML_XML_NAMESPACE
5053: if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
5054: return(XML_XML_NAMESPACE);
5055: #endif
5056:
5057: if (ctxt->namespaces != NULL) {
5058: int i;
5059:
5060: for (i = 0;i < ctxt->nsNr;i++) {
5061: if ((ctxt->namespaces[i] != NULL) &&
5062: (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
5063: return(ctxt->namespaces[i]->href);
5064: }
5065: }
5066:
5067: return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
5068: }
5069:
5070: /**
5071: * xmlXPathRegisteredNsCleanup:
5072: * @ctxt: the XPath context
5073: *
5074: * Cleanup the XPath context data associated to registered variables
5075: */
5076: void
5077: xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
5078: if (ctxt == NULL)
5079: return;
5080:
5081: xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree);
5082: ctxt->nsHash = NULL;
5083: }
5084:
5085: /************************************************************************
5086: * *
5087: * Routines to handle Values *
5088: * *
5089: ************************************************************************/
5090:
5091: /* Allocations are terrible, one needs to optimize all this !!! */
5092:
5093: /**
5094: * xmlXPathNewFloat:
5095: * @val: the double value
5096: *
5097: * Create a new xmlXPathObjectPtr of type double and of value @val
5098: *
5099: * Returns the newly created object.
5100: */
5101: xmlXPathObjectPtr
5102: xmlXPathNewFloat(double val) {
5103: xmlXPathObjectPtr ret;
5104:
5105: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5106: if (ret == NULL) {
5107: xmlXPathErrMemory(NULL, "creating float object\n");
5108: return(NULL);
5109: }
5110: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5111: ret->type = XPATH_NUMBER;
5112: ret->floatval = val;
5113: #ifdef XP_DEBUG_OBJ_USAGE
5114: xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
5115: #endif
5116: return(ret);
5117: }
5118:
5119: /**
5120: * xmlXPathNewBoolean:
5121: * @val: the boolean value
5122: *
5123: * Create a new xmlXPathObjectPtr of type boolean and of value @val
5124: *
5125: * Returns the newly created object.
5126: */
5127: xmlXPathObjectPtr
5128: xmlXPathNewBoolean(int val) {
5129: xmlXPathObjectPtr ret;
5130:
5131: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5132: if (ret == NULL) {
5133: xmlXPathErrMemory(NULL, "creating boolean object\n");
5134: return(NULL);
5135: }
5136: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5137: ret->type = XPATH_BOOLEAN;
5138: ret->boolval = (val != 0);
5139: #ifdef XP_DEBUG_OBJ_USAGE
5140: xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
5141: #endif
5142: return(ret);
5143: }
5144:
5145: /**
5146: * xmlXPathNewString:
5147: * @val: the xmlChar * value
5148: *
5149: * Create a new xmlXPathObjectPtr of type string and of value @val
5150: *
5151: * Returns the newly created object.
5152: */
5153: xmlXPathObjectPtr
5154: xmlXPathNewString(const xmlChar *val) {
5155: xmlXPathObjectPtr ret;
5156:
5157: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5158: if (ret == NULL) {
5159: xmlXPathErrMemory(NULL, "creating string object\n");
5160: return(NULL);
5161: }
5162: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5163: ret->type = XPATH_STRING;
5164: if (val != NULL)
5165: ret->stringval = xmlStrdup(val);
5166: else
5167: ret->stringval = xmlStrdup((const xmlChar *)"");
5168: #ifdef XP_DEBUG_OBJ_USAGE
5169: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5170: #endif
5171: return(ret);
5172: }
5173:
5174: /**
5175: * xmlXPathWrapString:
5176: * @val: the xmlChar * value
5177: *
5178: * Wraps the @val string into an XPath object.
5179: *
5180: * Returns the newly created object.
5181: */
5182: xmlXPathObjectPtr
5183: xmlXPathWrapString (xmlChar *val) {
5184: xmlXPathObjectPtr ret;
5185:
5186: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5187: if (ret == NULL) {
5188: xmlXPathErrMemory(NULL, "creating string object\n");
5189: return(NULL);
5190: }
5191: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5192: ret->type = XPATH_STRING;
5193: ret->stringval = val;
5194: #ifdef XP_DEBUG_OBJ_USAGE
5195: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5196: #endif
5197: return(ret);
5198: }
5199:
5200: /**
5201: * xmlXPathNewCString:
5202: * @val: the char * value
5203: *
5204: * Create a new xmlXPathObjectPtr of type string and of value @val
5205: *
5206: * Returns the newly created object.
5207: */
5208: xmlXPathObjectPtr
5209: xmlXPathNewCString(const char *val) {
5210: xmlXPathObjectPtr ret;
5211:
5212: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5213: if (ret == NULL) {
5214: xmlXPathErrMemory(NULL, "creating string object\n");
5215: return(NULL);
5216: }
5217: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5218: ret->type = XPATH_STRING;
5219: ret->stringval = xmlStrdup(BAD_CAST val);
5220: #ifdef XP_DEBUG_OBJ_USAGE
5221: xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
5222: #endif
5223: return(ret);
5224: }
5225:
5226: /**
5227: * xmlXPathWrapCString:
5228: * @val: the char * value
5229: *
5230: * Wraps a string into an XPath object.
5231: *
5232: * Returns the newly created object.
5233: */
5234: xmlXPathObjectPtr
5235: xmlXPathWrapCString (char * val) {
5236: return(xmlXPathWrapString((xmlChar *)(val)));
5237: }
5238:
5239: /**
5240: * xmlXPathWrapExternal:
5241: * @val: the user data
5242: *
5243: * Wraps the @val data into an XPath object.
5244: *
5245: * Returns the newly created object.
5246: */
5247: xmlXPathObjectPtr
5248: xmlXPathWrapExternal (void *val) {
5249: xmlXPathObjectPtr ret;
5250:
5251: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5252: if (ret == NULL) {
5253: xmlXPathErrMemory(NULL, "creating user object\n");
5254: return(NULL);
5255: }
5256: memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
5257: ret->type = XPATH_USERS;
5258: ret->user = val;
5259: #ifdef XP_DEBUG_OBJ_USAGE
5260: xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
5261: #endif
5262: return(ret);
5263: }
5264:
5265: /**
5266: * xmlXPathObjectCopy:
5267: * @val: the original object
5268: *
5269: * allocate a new copy of a given object
5270: *
5271: * Returns the newly created object.
5272: */
5273: xmlXPathObjectPtr
5274: xmlXPathObjectCopy(xmlXPathObjectPtr val) {
5275: xmlXPathObjectPtr ret;
5276:
5277: if (val == NULL)
5278: return(NULL);
5279:
5280: ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
5281: if (ret == NULL) {
5282: xmlXPathErrMemory(NULL, "copying object\n");
5283: return(NULL);
5284: }
5285: memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
5286: #ifdef XP_DEBUG_OBJ_USAGE
5287: xmlXPathDebugObjUsageRequested(NULL, val->type);
5288: #endif
5289: switch (val->type) {
5290: case XPATH_BOOLEAN:
5291: case XPATH_NUMBER:
5292: case XPATH_POINT:
5293: case XPATH_RANGE:
5294: break;
5295: case XPATH_STRING:
5296: ret->stringval = xmlStrdup(val->stringval);
5297: break;
5298: case XPATH_XSLT_TREE:
5299: #if 0
5300: /*
5301: Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that
5302: this previous handling is no longer correct, and can cause some serious
5303: problems (ref. bug 145547)
5304: */
5305: if ((val->nodesetval != NULL) &&
5306: (val->nodesetval->nodeTab != NULL)) {
5307: xmlNodePtr cur, tmp;
5308: xmlDocPtr top;
5309:
5310: ret->boolval = 1;
5311: top = xmlNewDoc(NULL);
5312: top->name = (char *)
5313: xmlStrdup(val->nodesetval->nodeTab[0]->name);
5314: ret->user = top;
5315: if (top != NULL) {
5316: top->doc = top;
5317: cur = val->nodesetval->nodeTab[0]->children;
5318: while (cur != NULL) {
5319: tmp = xmlDocCopyNode(cur, top, 1);
5320: xmlAddChild((xmlNodePtr) top, tmp);
5321: cur = cur->next;
5322: }
5323: }
5324:
5325: ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
5326: } else
5327: ret->nodesetval = xmlXPathNodeSetCreate(NULL);
5328: /* Deallocate the copied tree value */
5329: break;
5330: #endif
5331: case XPATH_NODESET:
5332: ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
5333: /* Do not deallocate the copied tree value */
5334: ret->boolval = 0;
5335: break;
5336: case XPATH_LOCATIONSET:
5337: #ifdef LIBXML_XPTR_ENABLED
5338: {
5339: xmlLocationSetPtr loc = val->user;
5340: ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
5341: break;
5342: }
5343: #endif
5344: case XPATH_USERS:
5345: ret->user = val->user;
5346: break;
5347: case XPATH_UNDEFINED:
5348: xmlGenericError(xmlGenericErrorContext,
5349: "xmlXPathObjectCopy: unsupported type %d\n",
5350: val->type);
5351: break;
5352: }
5353: return(ret);
5354: }
5355:
5356: /**
5357: * xmlXPathFreeObject:
5358: * @obj: the object to free
5359: *
5360: * Free up an xmlXPathObjectPtr object.
5361: */
5362: void
5363: xmlXPathFreeObject(xmlXPathObjectPtr obj) {
5364: if (obj == NULL) return;
5365: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
5366: if (obj->boolval) {
5367: #if 0
5368: if (obj->user != NULL) {
5369: xmlXPathFreeNodeSet(obj->nodesetval);
5370: xmlFreeNodeList((xmlNodePtr) obj->user);
5371: } else
5372: #endif
5373: obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
5374: if (obj->nodesetval != NULL)
5375: xmlXPathFreeValueTree(obj->nodesetval);
5376: } else {
5377: if (obj->nodesetval != NULL)
5378: xmlXPathFreeNodeSet(obj->nodesetval);
5379: }
5380: #ifdef LIBXML_XPTR_ENABLED
5381: } else if (obj->type == XPATH_LOCATIONSET) {
5382: if (obj->user != NULL)
5383: xmlXPtrFreeLocationSet(obj->user);
5384: #endif
5385: } else if (obj->type == XPATH_STRING) {
5386: if (obj->stringval != NULL)
5387: xmlFree(obj->stringval);
5388: }
5389: #ifdef XP_DEBUG_OBJ_USAGE
5390: xmlXPathDebugObjUsageReleased(NULL, obj->type);
5391: #endif
5392: xmlFree(obj);
5393: }
5394:
5395: /**
5396: * xmlXPathReleaseObject:
5397: * @obj: the xmlXPathObjectPtr to free or to cache
5398: *
5399: * Depending on the state of the cache this frees the given
5400: * XPath object or stores it in the cache.
5401: */
5402: static void
5403: xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
5404: {
5405: #define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
5406: sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
5407: if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
5408:
5409: #define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
5410:
5411: if (obj == NULL)
5412: return;
5413: if ((ctxt == NULL) || (ctxt->cache == NULL)) {
5414: xmlXPathFreeObject(obj);
5415: } else {
5416: xmlXPathContextCachePtr cache =
5417: (xmlXPathContextCachePtr) ctxt->cache;
5418:
5419: switch (obj->type) {
5420: case XPATH_NODESET:
5421: case XPATH_XSLT_TREE:
5422: if (obj->nodesetval != NULL) {
5423: if (obj->boolval) {
5424: /*
5425: * It looks like the @boolval is used for
5426: * evaluation if this an XSLT Result Tree Fragment.
5427: * TODO: Check if this assumption is correct.
5428: */
5429: obj->type = XPATH_XSLT_TREE; /* just for debugging */
5430: xmlXPathFreeValueTree(obj->nodesetval);
5431: obj->nodesetval = NULL;
5432: } else if ((obj->nodesetval->nodeMax <= 40) &&
5433: (XP_CACHE_WANTS(cache->nodesetObjs,
5434: cache->maxNodeset)))
5435: {
5436: XP_CACHE_ADD(cache->nodesetObjs, obj);
5437: goto obj_cached;
5438: } else {
5439: xmlXPathFreeNodeSet(obj->nodesetval);
5440: obj->nodesetval = NULL;
5441: }
5442: }
5443: break;
5444: case XPATH_STRING:
5445: if (obj->stringval != NULL)
5446: xmlFree(obj->stringval);
5447:
5448: if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
5449: XP_CACHE_ADD(cache->stringObjs, obj);
5450: goto obj_cached;
5451: }
5452: break;
5453: case XPATH_BOOLEAN:
5454: if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
5455: XP_CACHE_ADD(cache->booleanObjs, obj);
5456: goto obj_cached;
5457: }
5458: break;
5459: case XPATH_NUMBER:
5460: if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
5461: XP_CACHE_ADD(cache->numberObjs, obj);
5462: goto obj_cached;
5463: }
5464: break;
5465: #ifdef LIBXML_XPTR_ENABLED
5466: case XPATH_LOCATIONSET:
5467: if (obj->user != NULL) {
5468: xmlXPtrFreeLocationSet(obj->user);
5469: }
5470: goto free_obj;
5471: #endif
5472: default:
5473: goto free_obj;
5474: }
5475:
5476: /*
5477: * Fallback to adding to the misc-objects slot.
5478: */
5479: if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
5480: XP_CACHE_ADD(cache->miscObjs, obj);
5481: } else
5482: goto free_obj;
5483:
5484: obj_cached:
5485:
5486: #ifdef XP_DEBUG_OBJ_USAGE
5487: xmlXPathDebugObjUsageReleased(ctxt, obj->type);
5488: #endif
5489:
5490: if (obj->nodesetval != NULL) {
5491: xmlNodeSetPtr tmpset = obj->nodesetval;
5492:
5493: /*
5494: * TODO: Due to those nasty ns-nodes, we need to traverse
5495: * the list and free the ns-nodes.
5496: * URGENT TODO: Check if it's actually slowing things down.
5497: * Maybe we shouldn't try to preserve the list.
5498: */
5499: if (tmpset->nodeNr > 1) {
5500: int i;
5501: xmlNodePtr node;
5502:
5503: for (i = 0; i < tmpset->nodeNr; i++) {
5504: node = tmpset->nodeTab[i];
5505: if ((node != NULL) &&
5506: (node->type == XML_NAMESPACE_DECL))
5507: {
5508: xmlXPathNodeSetFreeNs((xmlNsPtr) node);
5509: }
5510: }
5511: } else if (tmpset->nodeNr == 1) {
5512: if ((tmpset->nodeTab[0] != NULL) &&
5513: (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
5514: xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
5515: }
5516: tmpset->nodeNr = 0;
5517: memset(obj, 0, sizeof(xmlXPathObject));
5518: obj->nodesetval = tmpset;
5519: } else
5520: memset(obj, 0, sizeof(xmlXPathObject));
5521:
5522: return;
5523:
5524: free_obj:
5525: /*
5526: * Cache is full; free the object.
5527: */
5528: if (obj->nodesetval != NULL)
5529: xmlXPathFreeNodeSet(obj->nodesetval);
5530: #ifdef XP_DEBUG_OBJ_USAGE
5531: xmlXPathDebugObjUsageReleased(NULL, obj->type);
5532: #endif
5533: xmlFree(obj);
5534: }
5535: return;
5536: }
5537:
5538:
5539: /************************************************************************
5540: * *
5541: * Type Casting Routines *
5542: * *
5543: ************************************************************************/
5544:
5545: /**
5546: * xmlXPathCastBooleanToString:
5547: * @val: a boolean
5548: *
5549: * Converts a boolean to its string value.
5550: *
5551: * Returns a newly allocated string.
5552: */
5553: xmlChar *
5554: xmlXPathCastBooleanToString (int val) {
5555: xmlChar *ret;
5556: if (val)
5557: ret = xmlStrdup((const xmlChar *) "true");
5558: else
5559: ret = xmlStrdup((const xmlChar *) "false");
5560: return(ret);
5561: }
5562:
5563: /**
5564: * xmlXPathCastNumberToString:
5565: * @val: a number
5566: *
5567: * Converts a number to its string value.
5568: *
5569: * Returns a newly allocated string.
5570: */
5571: xmlChar *
5572: xmlXPathCastNumberToString (double val) {
5573: xmlChar *ret;
5574: switch (xmlXPathIsInf(val)) {
5575: case 1:
5576: ret = xmlStrdup((const xmlChar *) "Infinity");
5577: break;
5578: case -1:
5579: ret = xmlStrdup((const xmlChar *) "-Infinity");
5580: break;
5581: default:
5582: if (xmlXPathIsNaN(val)) {
5583: ret = xmlStrdup((const xmlChar *) "NaN");
5584: } else if (val == 0 && xmlXPathGetSign(val) != 0) {
5585: ret = xmlStrdup((const xmlChar *) "0");
5586: } else {
5587: /* could be improved */
5588: char buf[100];
5589: xmlXPathFormatNumber(val, buf, 99);
5590: buf[99] = 0;
5591: ret = xmlStrdup((const xmlChar *) buf);
5592: }
5593: }
5594: return(ret);
5595: }
5596:
5597: /**
5598: * xmlXPathCastNodeToString:
5599: * @node: a node
5600: *
5601: * Converts a node to its string value.
5602: *
5603: * Returns a newly allocated string.
5604: */
5605: xmlChar *
5606: xmlXPathCastNodeToString (xmlNodePtr node) {
5607: xmlChar *ret;
5608: if ((ret = xmlNodeGetContent(node)) == NULL)
5609: ret = xmlStrdup((const xmlChar *) "");
5610: return(ret);
5611: }
5612:
5613: /**
5614: * xmlXPathCastNodeSetToString:
5615: * @ns: a node-set
5616: *
5617: * Converts a node-set to its string value.
5618: *
5619: * Returns a newly allocated string.
5620: */
5621: xmlChar *
5622: xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
5623: if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
5624: return(xmlStrdup((const xmlChar *) ""));
5625:
5626: if (ns->nodeNr > 1)
5627: xmlXPathNodeSetSort(ns);
5628: return(xmlXPathCastNodeToString(ns->nodeTab[0]));
5629: }
5630:
5631: /**
5632: * xmlXPathCastToString:
5633: * @val: an XPath object
5634: *
5635: * Converts an existing object to its string() equivalent
5636: *
5637: * Returns the allocated string value of the object, NULL in case of error.
5638: * It's up to the caller to free the string memory with xmlFree().
5639: */
5640: xmlChar *
5641: xmlXPathCastToString(xmlXPathObjectPtr val) {
5642: xmlChar *ret = NULL;
5643:
5644: if (val == NULL)
5645: return(xmlStrdup((const xmlChar *) ""));
5646: switch (val->type) {
5647: case XPATH_UNDEFINED:
5648: #ifdef DEBUG_EXPR
5649: xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
5650: #endif
5651: ret = xmlStrdup((const xmlChar *) "");
5652: break;
5653: case XPATH_NODESET:
5654: case XPATH_XSLT_TREE:
5655: ret = xmlXPathCastNodeSetToString(val->nodesetval);
5656: break;
5657: case XPATH_STRING:
5658: return(xmlStrdup(val->stringval));
5659: case XPATH_BOOLEAN:
5660: ret = xmlXPathCastBooleanToString(val->boolval);
5661: break;
5662: case XPATH_NUMBER: {
5663: ret = xmlXPathCastNumberToString(val->floatval);
5664: break;
5665: }
5666: case XPATH_USERS:
5667: case XPATH_POINT:
5668: case XPATH_RANGE:
5669: case XPATH_LOCATIONSET:
5670: TODO
5671: ret = xmlStrdup((const xmlChar *) "");
5672: break;
5673: }
5674: return(ret);
5675: }
5676:
5677: /**
5678: * xmlXPathConvertString:
5679: * @val: an XPath object
5680: *
5681: * Converts an existing object to its string() equivalent
5682: *
5683: * Returns the new object, the old one is freed (or the operation
5684: * is done directly on @val)
5685: */
5686: xmlXPathObjectPtr
5687: xmlXPathConvertString(xmlXPathObjectPtr val) {
5688: xmlChar *res = NULL;
5689:
5690: if (val == NULL)
5691: return(xmlXPathNewCString(""));
5692:
5693: switch (val->type) {
5694: case XPATH_UNDEFINED:
5695: #ifdef DEBUG_EXPR
5696: xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
5697: #endif
5698: break;
5699: case XPATH_NODESET:
5700: case XPATH_XSLT_TREE:
5701: res = xmlXPathCastNodeSetToString(val->nodesetval);
5702: break;
5703: case XPATH_STRING:
5704: return(val);
5705: case XPATH_BOOLEAN:
5706: res = xmlXPathCastBooleanToString(val->boolval);
5707: break;
5708: case XPATH_NUMBER:
5709: res = xmlXPathCastNumberToString(val->floatval);
5710: break;
5711: case XPATH_USERS:
5712: case XPATH_POINT:
5713: case XPATH_RANGE:
5714: case XPATH_LOCATIONSET:
5715: TODO;
5716: break;
5717: }
5718: xmlXPathFreeObject(val);
5719: if (res == NULL)
5720: return(xmlXPathNewCString(""));
5721: return(xmlXPathWrapString(res));
5722: }
5723:
5724: /**
5725: * xmlXPathCastBooleanToNumber:
5726: * @val: a boolean
5727: *
5728: * Converts a boolean to its number value
5729: *
5730: * Returns the number value
5731: */
5732: double
5733: xmlXPathCastBooleanToNumber(int val) {
5734: if (val)
5735: return(1.0);
5736: return(0.0);
5737: }
5738:
5739: /**
5740: * xmlXPathCastStringToNumber:
5741: * @val: a string
5742: *
5743: * Converts a string to its number value
5744: *
5745: * Returns the number value
5746: */
5747: double
5748: xmlXPathCastStringToNumber(const xmlChar * val) {
5749: return(xmlXPathStringEvalNumber(val));
5750: }
5751:
5752: /**
5753: * xmlXPathCastNodeToNumber:
5754: * @node: a node
5755: *
5756: * Converts a node to its number value
5757: *
5758: * Returns the number value
5759: */
5760: double
5761: xmlXPathCastNodeToNumber (xmlNodePtr node) {
5762: xmlChar *strval;
5763: double ret;
5764:
5765: if (node == NULL)
5766: return(xmlXPathNAN);
5767: strval = xmlXPathCastNodeToString(node);
5768: if (strval == NULL)
5769: return(xmlXPathNAN);
5770: ret = xmlXPathCastStringToNumber(strval);
5771: xmlFree(strval);
5772:
5773: return(ret);
5774: }
5775:
5776: /**
5777: * xmlXPathCastNodeSetToNumber:
5778: * @ns: a node-set
5779: *
5780: * Converts a node-set to its number value
5781: *
5782: * Returns the number value
5783: */
5784: double
5785: xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
5786: xmlChar *str;
5787: double ret;
5788:
5789: if (ns == NULL)
5790: return(xmlXPathNAN);
5791: str = xmlXPathCastNodeSetToString(ns);
5792: ret = xmlXPathCastStringToNumber(str);
5793: xmlFree(str);
5794: return(ret);
5795: }
5796:
5797: /**
5798: * xmlXPathCastToNumber:
5799: * @val: an XPath object
5800: *
5801: * Converts an XPath object to its number value
5802: *
5803: * Returns the number value
5804: */
5805: double
5806: xmlXPathCastToNumber(xmlXPathObjectPtr val) {
5807: double ret = 0.0;
5808:
5809: if (val == NULL)
5810: return(xmlXPathNAN);
5811: switch (val->type) {
5812: case XPATH_UNDEFINED:
5813: #ifdef DEGUB_EXPR
5814: xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
5815: #endif
5816: ret = xmlXPathNAN;
5817: break;
5818: case XPATH_NODESET:
5819: case XPATH_XSLT_TREE:
5820: ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
5821: break;
5822: case XPATH_STRING:
5823: ret = xmlXPathCastStringToNumber(val->stringval);
5824: break;
5825: case XPATH_NUMBER:
5826: ret = val->floatval;
5827: break;
5828: case XPATH_BOOLEAN:
5829: ret = xmlXPathCastBooleanToNumber(val->boolval);
5830: break;
5831: case XPATH_USERS:
5832: case XPATH_POINT:
5833: case XPATH_RANGE:
5834: case XPATH_LOCATIONSET:
5835: TODO;
5836: ret = xmlXPathNAN;
5837: break;
5838: }
5839: return(ret);
5840: }
5841:
5842: /**
5843: * xmlXPathConvertNumber:
5844: * @val: an XPath object
5845: *
5846: * Converts an existing object to its number() equivalent
5847: *
5848: * Returns the new object, the old one is freed (or the operation
5849: * is done directly on @val)
5850: */
5851: xmlXPathObjectPtr
5852: xmlXPathConvertNumber(xmlXPathObjectPtr val) {
5853: xmlXPathObjectPtr ret;
5854:
5855: if (val == NULL)
5856: return(xmlXPathNewFloat(0.0));
5857: if (val->type == XPATH_NUMBER)
5858: return(val);
5859: ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
5860: xmlXPathFreeObject(val);
5861: return(ret);
5862: }
5863:
5864: /**
5865: * xmlXPathCastNumberToBoolean:
5866: * @val: a number
5867: *
5868: * Converts a number to its boolean value
5869: *
5870: * Returns the boolean value
5871: */
5872: int
5873: xmlXPathCastNumberToBoolean (double val) {
5874: if (xmlXPathIsNaN(val) || (val == 0.0))
5875: return(0);
5876: return(1);
5877: }
5878:
5879: /**
5880: * xmlXPathCastStringToBoolean:
5881: * @val: a string
5882: *
5883: * Converts a string to its boolean value
5884: *
5885: * Returns the boolean value
5886: */
5887: int
5888: xmlXPathCastStringToBoolean (const xmlChar *val) {
5889: if ((val == NULL) || (xmlStrlen(val) == 0))
5890: return(0);
5891: return(1);
5892: }
5893:
5894: /**
5895: * xmlXPathCastNodeSetToBoolean:
5896: * @ns: a node-set
5897: *
5898: * Converts a node-set to its boolean value
5899: *
5900: * Returns the boolean value
5901: */
5902: int
5903: xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
5904: if ((ns == NULL) || (ns->nodeNr == 0))
5905: return(0);
5906: return(1);
5907: }
5908:
5909: /**
5910: * xmlXPathCastToBoolean:
5911: * @val: an XPath object
5912: *
5913: * Converts an XPath object to its boolean value
5914: *
5915: * Returns the boolean value
5916: */
5917: int
5918: xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
5919: int ret = 0;
5920:
5921: if (val == NULL)
5922: return(0);
5923: switch (val->type) {
5924: case XPATH_UNDEFINED:
5925: #ifdef DEBUG_EXPR
5926: xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
5927: #endif
5928: ret = 0;
5929: break;
5930: case XPATH_NODESET:
5931: case XPATH_XSLT_TREE:
5932: ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
5933: break;
5934: case XPATH_STRING:
5935: ret = xmlXPathCastStringToBoolean(val->stringval);
5936: break;
5937: case XPATH_NUMBER:
5938: ret = xmlXPathCastNumberToBoolean(val->floatval);
5939: break;
5940: case XPATH_BOOLEAN:
5941: ret = val->boolval;
5942: break;
5943: case XPATH_USERS:
5944: case XPATH_POINT:
5945: case XPATH_RANGE:
5946: case XPATH_LOCATIONSET:
5947: TODO;
5948: ret = 0;
5949: break;
5950: }
5951: return(ret);
5952: }
5953:
5954:
5955: /**
5956: * xmlXPathConvertBoolean:
5957: * @val: an XPath object
5958: *
5959: * Converts an existing object to its boolean() equivalent
5960: *
5961: * Returns the new object, the old one is freed (or the operation
5962: * is done directly on @val)
5963: */
5964: xmlXPathObjectPtr
5965: xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
5966: xmlXPathObjectPtr ret;
5967:
5968: if (val == NULL)
5969: return(xmlXPathNewBoolean(0));
5970: if (val->type == XPATH_BOOLEAN)
5971: return(val);
5972: ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
5973: xmlXPathFreeObject(val);
5974: return(ret);
5975: }
5976:
5977: /************************************************************************
5978: * *
5979: * Routines to handle XPath contexts *
5980: * *
5981: ************************************************************************/
5982:
5983: /**
5984: * xmlXPathNewContext:
5985: * @doc: the XML document
5986: *
5987: * Create a new xmlXPathContext
5988: *
5989: * Returns the xmlXPathContext just allocated. The caller will need to free it.
5990: */
5991: xmlXPathContextPtr
5992: xmlXPathNewContext(xmlDocPtr doc) {
5993: xmlXPathContextPtr ret;
5994:
5995: ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
5996: if (ret == NULL) {
5997: xmlXPathErrMemory(NULL, "creating context\n");
5998: return(NULL);
5999: }
6000: memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
6001: ret->doc = doc;
6002: ret->node = NULL;
6003:
6004: ret->varHash = NULL;
6005:
6006: ret->nb_types = 0;
6007: ret->max_types = 0;
6008: ret->types = NULL;
6009:
6010: ret->funcHash = xmlHashCreate(0);
6011:
6012: ret->nb_axis = 0;
6013: ret->max_axis = 0;
6014: ret->axis = NULL;
6015:
6016: ret->nsHash = NULL;
6017: ret->user = NULL;
6018:
6019: ret->contextSize = -1;
6020: ret->proximityPosition = -1;
6021:
6022: #ifdef XP_DEFAULT_CACHE_ON
6023: if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
6024: xmlXPathFreeContext(ret);
6025: return(NULL);
6026: }
6027: #endif
6028:
6029: xmlXPathRegisterAllFunctions(ret);
6030:
6031: return(ret);
6032: }
6033:
6034: /**
6035: * xmlXPathFreeContext:
6036: * @ctxt: the context to free
6037: *
6038: * Free up an xmlXPathContext
6039: */
6040: void
6041: xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
6042: if (ctxt == NULL) return;
6043:
6044: if (ctxt->cache != NULL)
6045: xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
6046: xmlXPathRegisteredNsCleanup(ctxt);
6047: xmlXPathRegisteredFuncsCleanup(ctxt);
6048: xmlXPathRegisteredVariablesCleanup(ctxt);
6049: xmlResetError(&ctxt->lastError);
6050: xmlFree(ctxt);
6051: }
6052:
6053: /************************************************************************
6054: * *
6055: * Routines to handle XPath parser contexts *
6056: * *
6057: ************************************************************************/
6058:
6059: #define CHECK_CTXT(ctxt) \
6060: if (ctxt == NULL) { \
6061: __xmlRaiseError(NULL, NULL, NULL, \
6062: NULL, NULL, XML_FROM_XPATH, \
6063: XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
6064: __FILE__, __LINE__, \
6065: NULL, NULL, NULL, 0, 0, \
6066: "NULL context pointer\n"); \
6067: return(NULL); \
6068: } \
6069:
6070: #define CHECK_CTXT_NEG(ctxt) \
6071: if (ctxt == NULL) { \
6072: __xmlRaiseError(NULL, NULL, NULL, \
6073: NULL, NULL, XML_FROM_XPATH, \
6074: XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \
6075: __FILE__, __LINE__, \
6076: NULL, NULL, NULL, 0, 0, \
6077: "NULL context pointer\n"); \
6078: return(-1); \
6079: } \
6080:
6081:
6082: #define CHECK_CONTEXT(ctxt) \
6083: if ((ctxt == NULL) || (ctxt->doc == NULL) || \
6084: (ctxt->doc->children == NULL)) { \
6085: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \
6086: return(NULL); \
6087: }
6088:
6089:
6090: /**
6091: * xmlXPathNewParserContext:
6092: * @str: the XPath expression
6093: * @ctxt: the XPath context
6094: *
6095: * Create a new xmlXPathParserContext
6096: *
6097: * Returns the xmlXPathParserContext just allocated.
6098: */
6099: xmlXPathParserContextPtr
6100: xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
6101: xmlXPathParserContextPtr ret;
6102:
6103: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6104: if (ret == NULL) {
6105: xmlXPathErrMemory(ctxt, "creating parser context\n");
6106: return(NULL);
6107: }
6108: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
6109: ret->cur = ret->base = str;
6110: ret->context = ctxt;
6111:
6112: ret->comp = xmlXPathNewCompExpr();
6113: if (ret->comp == NULL) {
6114: xmlFree(ret->valueTab);
6115: xmlFree(ret);
6116: return(NULL);
6117: }
6118: if ((ctxt != NULL) && (ctxt->dict != NULL)) {
6119: ret->comp->dict = ctxt->dict;
6120: xmlDictReference(ret->comp->dict);
6121: }
6122:
6123: return(ret);
6124: }
6125:
6126: /**
6127: * xmlXPathCompParserContext:
6128: * @comp: the XPath compiled expression
6129: * @ctxt: the XPath context
6130: *
6131: * Create a new xmlXPathParserContext when processing a compiled expression
6132: *
6133: * Returns the xmlXPathParserContext just allocated.
6134: */
6135: static xmlXPathParserContextPtr
6136: xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
6137: xmlXPathParserContextPtr ret;
6138:
6139: ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
6140: if (ret == NULL) {
6141: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6142: return(NULL);
6143: }
6144: memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
6145:
6146: /* Allocate the value stack */
6147: ret->valueTab = (xmlXPathObjectPtr *)
6148: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
6149: if (ret->valueTab == NULL) {
6150: xmlFree(ret);
6151: xmlXPathErrMemory(ctxt, "creating evaluation context\n");
6152: return(NULL);
6153: }
6154: ret->valueNr = 0;
6155: ret->valueMax = 10;
6156: ret->value = NULL;
6157:
6158: ret->context = ctxt;
6159: ret->comp = comp;
6160:
6161: return(ret);
6162: }
6163:
6164: /**
6165: * xmlXPathFreeParserContext:
6166: * @ctxt: the context to free
6167: *
6168: * Free up an xmlXPathParserContext
6169: */
6170: void
6171: xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
6172: if (ctxt->valueTab != NULL) {
6173: xmlFree(ctxt->valueTab);
6174: }
6175: if (ctxt->comp != NULL) {
6176: #ifdef XPATH_STREAMING
6177: if (ctxt->comp->stream != NULL) {
6178: xmlFreePatternList(ctxt->comp->stream);
6179: ctxt->comp->stream = NULL;
6180: }
6181: #endif
6182: xmlXPathFreeCompExpr(ctxt->comp);
6183: }
6184: xmlFree(ctxt);
6185: }
6186:
6187: /************************************************************************
6188: * *
6189: * The implicit core function library *
6190: * *
6191: ************************************************************************/
6192:
6193: /**
6194: * xmlXPathNodeValHash:
6195: * @node: a node pointer
6196: *
6197: * Function computing the beginning of the string value of the node,
6198: * used to speed up comparisons
6199: *
6200: * Returns an int usable as a hash
6201: */
6202: static unsigned int
6203: xmlXPathNodeValHash(xmlNodePtr node) {
6204: int len = 2;
6205: const xmlChar * string = NULL;
6206: xmlNodePtr tmp = NULL;
6207: unsigned int ret = 0;
6208:
6209: if (node == NULL)
6210: return(0);
6211:
6212: if (node->type == XML_DOCUMENT_NODE) {
6213: tmp = xmlDocGetRootElement((xmlDocPtr) node);
6214: if (tmp == NULL)
6215: node = node->children;
6216: else
6217: node = tmp;
6218:
6219: if (node == NULL)
6220: return(0);
6221: }
6222:
6223: switch (node->type) {
6224: case XML_COMMENT_NODE:
6225: case XML_PI_NODE:
6226: case XML_CDATA_SECTION_NODE:
6227: case XML_TEXT_NODE:
6228: string = node->content;
6229: if (string == NULL)
6230: return(0);
6231: if (string[0] == 0)
6232: return(0);
6233: return(((unsigned int) string[0]) +
6234: (((unsigned int) string[1]) << 8));
6235: case XML_NAMESPACE_DECL:
6236: string = ((xmlNsPtr)node)->href;
6237: if (string == NULL)
6238: return(0);
6239: if (string[0] == 0)
6240: return(0);
6241: return(((unsigned int) string[0]) +
6242: (((unsigned int) string[1]) << 8));
6243: case XML_ATTRIBUTE_NODE:
6244: tmp = ((xmlAttrPtr) node)->children;
6245: break;
6246: case XML_ELEMENT_NODE:
6247: tmp = node->children;
6248: break;
6249: default:
6250: return(0);
6251: }
6252: while (tmp != NULL) {
6253: switch (tmp->type) {
6254: case XML_COMMENT_NODE:
6255: case XML_PI_NODE:
6256: case XML_CDATA_SECTION_NODE:
6257: case XML_TEXT_NODE:
6258: string = tmp->content;
6259: break;
6260: case XML_NAMESPACE_DECL:
6261: string = ((xmlNsPtr)tmp)->href;
6262: break;
6263: default:
6264: break;
6265: }
6266: if ((string != NULL) && (string[0] != 0)) {
6267: if (len == 1) {
6268: return(ret + (((unsigned int) string[0]) << 8));
6269: }
6270: if (string[1] == 0) {
6271: len = 1;
6272: ret = (unsigned int) string[0];
6273: } else {
6274: return(((unsigned int) string[0]) +
6275: (((unsigned int) string[1]) << 8));
6276: }
6277: }
6278: /*
6279: * Skip to next node
6280: */
6281: if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
6282: if (tmp->children->type != XML_ENTITY_DECL) {
6283: tmp = tmp->children;
6284: continue;
6285: }
6286: }
6287: if (tmp == node)
6288: break;
6289:
6290: if (tmp->next != NULL) {
6291: tmp = tmp->next;
6292: continue;
6293: }
6294:
6295: do {
6296: tmp = tmp->parent;
6297: if (tmp == NULL)
6298: break;
6299: if (tmp == node) {
6300: tmp = NULL;
6301: break;
6302: }
6303: if (tmp->next != NULL) {
6304: tmp = tmp->next;
6305: break;
6306: }
6307: } while (tmp != NULL);
6308: }
6309: return(ret);
6310: }
6311:
6312: /**
6313: * xmlXPathStringHash:
6314: * @string: a string
6315: *
6316: * Function computing the beginning of the string value of the node,
6317: * used to speed up comparisons
6318: *
6319: * Returns an int usable as a hash
6320: */
6321: static unsigned int
6322: xmlXPathStringHash(const xmlChar * string) {
6323: if (string == NULL)
6324: return((unsigned int) 0);
6325: if (string[0] == 0)
6326: return(0);
6327: return(((unsigned int) string[0]) +
6328: (((unsigned int) string[1]) << 8));
6329: }
6330:
6331: /**
6332: * xmlXPathCompareNodeSetFloat:
6333: * @ctxt: the XPath Parser context
6334: * @inf: less than (1) or greater than (0)
6335: * @strict: is the comparison strict
6336: * @arg: the node set
6337: * @f: the value
6338: *
6339: * Implement the compare operation between a nodeset and a number
6340: * @ns < @val (1, 1, ...
6341: * @ns <= @val (1, 0, ...
6342: * @ns > @val (0, 1, ...
6343: * @ns >= @val (0, 0, ...
6344: *
6345: * If one object to be compared is a node-set and the other is a number,
6346: * then the comparison will be true if and only if there is a node in the
6347: * node-set such that the result of performing the comparison on the number
6348: * to be compared and on the result of converting the string-value of that
6349: * node to a number using the number function is true.
6350: *
6351: * Returns 0 or 1 depending on the results of the test.
6352: */
6353: static int
6354: xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
6355: xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
6356: int i, ret = 0;
6357: xmlNodeSetPtr ns;
6358: xmlChar *str2;
6359:
6360: if ((f == NULL) || (arg == NULL) ||
6361: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6362: xmlXPathReleaseObject(ctxt->context, arg);
6363: xmlXPathReleaseObject(ctxt->context, f);
6364: return(0);
6365: }
6366: ns = arg->nodesetval;
6367: if (ns != NULL) {
6368: for (i = 0;i < ns->nodeNr;i++) {
6369: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6370: if (str2 != NULL) {
6371: valuePush(ctxt,
6372: xmlXPathCacheNewString(ctxt->context, str2));
6373: xmlFree(str2);
6374: xmlXPathNumberFunction(ctxt, 1);
6375: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
6376: ret = xmlXPathCompareValues(ctxt, inf, strict);
6377: if (ret)
6378: break;
6379: }
6380: }
6381: }
6382: xmlXPathReleaseObject(ctxt->context, arg);
6383: xmlXPathReleaseObject(ctxt->context, f);
6384: return(ret);
6385: }
6386:
6387: /**
6388: * xmlXPathCompareNodeSetString:
6389: * @ctxt: the XPath Parser context
6390: * @inf: less than (1) or greater than (0)
6391: * @strict: is the comparison strict
6392: * @arg: the node set
6393: * @s: the value
6394: *
6395: * Implement the compare operation between a nodeset and a string
6396: * @ns < @val (1, 1, ...
6397: * @ns <= @val (1, 0, ...
6398: * @ns > @val (0, 1, ...
6399: * @ns >= @val (0, 0, ...
6400: *
6401: * If one object to be compared is a node-set and the other is a string,
6402: * then the comparison will be true if and only if there is a node in
6403: * the node-set such that the result of performing the comparison on the
6404: * string-value of the node and the other string is true.
6405: *
6406: * Returns 0 or 1 depending on the results of the test.
6407: */
6408: static int
6409: xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
6410: xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
6411: int i, ret = 0;
6412: xmlNodeSetPtr ns;
6413: xmlChar *str2;
6414:
6415: if ((s == NULL) || (arg == NULL) ||
6416: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
6417: xmlXPathReleaseObject(ctxt->context, arg);
6418: xmlXPathReleaseObject(ctxt->context, s);
6419: return(0);
6420: }
6421: ns = arg->nodesetval;
6422: if (ns != NULL) {
6423: for (i = 0;i < ns->nodeNr;i++) {
6424: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6425: if (str2 != NULL) {
6426: valuePush(ctxt,
6427: xmlXPathCacheNewString(ctxt->context, str2));
6428: xmlFree(str2);
6429: valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
6430: ret = xmlXPathCompareValues(ctxt, inf, strict);
6431: if (ret)
6432: break;
6433: }
6434: }
6435: }
6436: xmlXPathReleaseObject(ctxt->context, arg);
6437: xmlXPathReleaseObject(ctxt->context, s);
6438: return(ret);
6439: }
6440:
6441: /**
6442: * xmlXPathCompareNodeSets:
6443: * @inf: less than (1) or greater than (0)
6444: * @strict: is the comparison strict
6445: * @arg1: the first node set object
6446: * @arg2: the second node set object
6447: *
6448: * Implement the compare operation on nodesets:
6449: *
6450: * If both objects to be compared are node-sets, then the comparison
6451: * will be true if and only if there is a node in the first node-set
6452: * and a node in the second node-set such that the result of performing
6453: * the comparison on the string-values of the two nodes is true.
6454: * ....
6455: * When neither object to be compared is a node-set and the operator
6456: * is <=, <, >= or >, then the objects are compared by converting both
6457: * objects to numbers and comparing the numbers according to IEEE 754.
6458: * ....
6459: * The number function converts its argument to a number as follows:
6460: * - a string that consists of optional whitespace followed by an
6461: * optional minus sign followed by a Number followed by whitespace
6462: * is converted to the IEEE 754 number that is nearest (according
6463: * to the IEEE 754 round-to-nearest rule) to the mathematical value
6464: * represented by the string; any other string is converted to NaN
6465: *
6466: * Conclusion all nodes need to be converted first to their string value
6467: * and then the comparison must be done when possible
6468: */
6469: static int
6470: xmlXPathCompareNodeSets(int inf, int strict,
6471: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6472: int i, j, init = 0;
6473: double val1;
6474: double *values2;
6475: int ret = 0;
6476: xmlNodeSetPtr ns1;
6477: xmlNodeSetPtr ns2;
6478:
6479: if ((arg1 == NULL) ||
6480: ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
6481: xmlXPathFreeObject(arg2);
6482: return(0);
6483: }
6484: if ((arg2 == NULL) ||
6485: ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
6486: xmlXPathFreeObject(arg1);
6487: xmlXPathFreeObject(arg2);
6488: return(0);
6489: }
6490:
6491: ns1 = arg1->nodesetval;
6492: ns2 = arg2->nodesetval;
6493:
6494: if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
6495: xmlXPathFreeObject(arg1);
6496: xmlXPathFreeObject(arg2);
6497: return(0);
6498: }
6499: if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
6500: xmlXPathFreeObject(arg1);
6501: xmlXPathFreeObject(arg2);
6502: return(0);
6503: }
6504:
6505: values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
6506: if (values2 == NULL) {
6507: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6508: xmlXPathFreeObject(arg1);
6509: xmlXPathFreeObject(arg2);
6510: return(0);
6511: }
6512: for (i = 0;i < ns1->nodeNr;i++) {
6513: val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
6514: if (xmlXPathIsNaN(val1))
6515: continue;
6516: for (j = 0;j < ns2->nodeNr;j++) {
6517: if (init == 0) {
6518: values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
6519: }
6520: if (xmlXPathIsNaN(values2[j]))
6521: continue;
6522: if (inf && strict)
6523: ret = (val1 < values2[j]);
6524: else if (inf && !strict)
6525: ret = (val1 <= values2[j]);
6526: else if (!inf && strict)
6527: ret = (val1 > values2[j]);
6528: else if (!inf && !strict)
6529: ret = (val1 >= values2[j]);
6530: if (ret)
6531: break;
6532: }
6533: if (ret)
6534: break;
6535: init = 1;
6536: }
6537: xmlFree(values2);
6538: xmlXPathFreeObject(arg1);
6539: xmlXPathFreeObject(arg2);
6540: return(ret);
6541: }
6542:
6543: /**
6544: * xmlXPathCompareNodeSetValue:
6545: * @ctxt: the XPath Parser context
6546: * @inf: less than (1) or greater than (0)
6547: * @strict: is the comparison strict
6548: * @arg: the node set
6549: * @val: the value
6550: *
6551: * Implement the compare operation between a nodeset and a value
6552: * @ns < @val (1, 1, ...
6553: * @ns <= @val (1, 0, ...
6554: * @ns > @val (0, 1, ...
6555: * @ns >= @val (0, 0, ...
6556: *
6557: * If one object to be compared is a node-set and the other is a boolean,
6558: * then the comparison will be true if and only if the result of performing
6559: * the comparison on the boolean and on the result of converting
6560: * the node-set to a boolean using the boolean function is true.
6561: *
6562: * Returns 0 or 1 depending on the results of the test.
6563: */
6564: static int
6565: xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
6566: xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
6567: if ((val == NULL) || (arg == NULL) ||
6568: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6569: return(0);
6570:
6571: switch(val->type) {
6572: case XPATH_NUMBER:
6573: return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
6574: case XPATH_NODESET:
6575: case XPATH_XSLT_TREE:
6576: return(xmlXPathCompareNodeSets(inf, strict, arg, val));
6577: case XPATH_STRING:
6578: return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
6579: case XPATH_BOOLEAN:
6580: valuePush(ctxt, arg);
6581: xmlXPathBooleanFunction(ctxt, 1);
6582: valuePush(ctxt, val);
6583: return(xmlXPathCompareValues(ctxt, inf, strict));
6584: default:
6585: TODO
6586: }
6587: return(0);
6588: }
6589:
6590: /**
6591: * xmlXPathEqualNodeSetString:
6592: * @arg: the nodeset object argument
6593: * @str: the string to compare to.
6594: * @neq: flag to show whether for '=' (0) or '!=' (1)
6595: *
6596: * Implement the equal operation on XPath objects content: @arg1 == @arg2
6597: * If one object to be compared is a node-set and the other is a string,
6598: * then the comparison will be true if and only if there is a node in
6599: * the node-set such that the result of performing the comparison on the
6600: * string-value of the node and the other string is true.
6601: *
6602: * Returns 0 or 1 depending on the results of the test.
6603: */
6604: static int
6605: xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq)
6606: {
6607: int i;
6608: xmlNodeSetPtr ns;
6609: xmlChar *str2;
6610: unsigned int hash;
6611:
6612: if ((str == NULL) || (arg == NULL) ||
6613: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6614: return (0);
6615: ns = arg->nodesetval;
6616: /*
6617: * A NULL nodeset compared with a string is always false
6618: * (since there is no node equal, and no node not equal)
6619: */
6620: if ((ns == NULL) || (ns->nodeNr <= 0) )
6621: return (0);
6622: hash = xmlXPathStringHash(str);
6623: for (i = 0; i < ns->nodeNr; i++) {
6624: if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
6625: str2 = xmlNodeGetContent(ns->nodeTab[i]);
6626: if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
6627: xmlFree(str2);
6628: if (neq)
6629: continue;
6630: return (1);
6631: } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
6632: if (neq)
6633: continue;
6634: return (1);
6635: } else if (neq) {
6636: if (str2 != NULL)
6637: xmlFree(str2);
6638: return (1);
6639: }
6640: if (str2 != NULL)
6641: xmlFree(str2);
6642: } else if (neq)
6643: return (1);
6644: }
6645: return (0);
6646: }
6647:
6648: /**
6649: * xmlXPathEqualNodeSetFloat:
6650: * @arg: the nodeset object argument
6651: * @f: the float to compare to
6652: * @neq: flag to show whether to compare '=' (0) or '!=' (1)
6653: *
6654: * Implement the equal operation on XPath objects content: @arg1 == @arg2
6655: * If one object to be compared is a node-set and the other is a number,
6656: * then the comparison will be true if and only if there is a node in
6657: * the node-set such that the result of performing the comparison on the
6658: * number to be compared and on the result of converting the string-value
6659: * of that node to a number using the number function is true.
6660: *
6661: * Returns 0 or 1 depending on the results of the test.
6662: */
6663: static int
6664: xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
6665: xmlXPathObjectPtr arg, double f, int neq) {
6666: int i, ret=0;
6667: xmlNodeSetPtr ns;
6668: xmlChar *str2;
6669: xmlXPathObjectPtr val;
6670: double v;
6671:
6672: if ((arg == NULL) ||
6673: ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
6674: return(0);
6675:
6676: ns = arg->nodesetval;
6677: if (ns != NULL) {
6678: for (i=0;i<ns->nodeNr;i++) {
6679: str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
6680: if (str2 != NULL) {
6681: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
6682: xmlFree(str2);
6683: xmlXPathNumberFunction(ctxt, 1);
6684: val = valuePop(ctxt);
6685: v = val->floatval;
6686: xmlXPathReleaseObject(ctxt->context, val);
6687: if (!xmlXPathIsNaN(v)) {
6688: if ((!neq) && (v==f)) {
6689: ret = 1;
6690: break;
6691: } else if ((neq) && (v!=f)) {
6692: ret = 1;
6693: break;
6694: }
6695: } else { /* NaN is unequal to any value */
6696: if (neq)
6697: ret = 1;
6698: }
6699: }
6700: }
6701: }
6702:
6703: return(ret);
6704: }
6705:
6706:
6707: /**
6708: * xmlXPathEqualNodeSets:
6709: * @arg1: first nodeset object argument
6710: * @arg2: second nodeset object argument
6711: * @neq: flag to show whether to test '=' (0) or '!=' (1)
6712: *
6713: * Implement the equal / not equal operation on XPath nodesets:
6714: * @arg1 == @arg2 or @arg1 != @arg2
6715: * If both objects to be compared are node-sets, then the comparison
6716: * will be true if and only if there is a node in the first node-set and
6717: * a node in the second node-set such that the result of performing the
6718: * comparison on the string-values of the two nodes is true.
6719: *
6720: * (needless to say, this is a costly operation)
6721: *
6722: * Returns 0 or 1 depending on the results of the test.
6723: */
6724: static int
6725: xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) {
6726: int i, j;
6727: unsigned int *hashs1;
6728: unsigned int *hashs2;
6729: xmlChar **values1;
6730: xmlChar **values2;
6731: int ret = 0;
6732: xmlNodeSetPtr ns1;
6733: xmlNodeSetPtr ns2;
6734:
6735: if ((arg1 == NULL) ||
6736: ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
6737: return(0);
6738: if ((arg2 == NULL) ||
6739: ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
6740: return(0);
6741:
6742: ns1 = arg1->nodesetval;
6743: ns2 = arg2->nodesetval;
6744:
6745: if ((ns1 == NULL) || (ns1->nodeNr <= 0))
6746: return(0);
6747: if ((ns2 == NULL) || (ns2->nodeNr <= 0))
6748: return(0);
6749:
6750: /*
6751: * for equal, check if there is a node pertaining to both sets
6752: */
6753: if (neq == 0)
6754: for (i = 0;i < ns1->nodeNr;i++)
6755: for (j = 0;j < ns2->nodeNr;j++)
6756: if (ns1->nodeTab[i] == ns2->nodeTab[j])
6757: return(1);
6758:
6759: values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
6760: if (values1 == NULL) {
6761: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6762: return(0);
6763: }
6764: hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
6765: if (hashs1 == NULL) {
6766: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6767: xmlFree(values1);
6768: return(0);
6769: }
6770: memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
6771: values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
6772: if (values2 == NULL) {
6773: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6774: xmlFree(hashs1);
6775: xmlFree(values1);
6776: return(0);
6777: }
6778: hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
6779: if (hashs2 == NULL) {
6780: xmlXPathErrMemory(NULL, "comparing nodesets\n");
6781: xmlFree(hashs1);
6782: xmlFree(values1);
6783: xmlFree(values2);
6784: return(0);
6785: }
6786: memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
6787: for (i = 0;i < ns1->nodeNr;i++) {
6788: hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
6789: for (j = 0;j < ns2->nodeNr;j++) {
6790: if (i == 0)
6791: hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
6792: if (hashs1[i] != hashs2[j]) {
6793: if (neq) {
6794: ret = 1;
6795: break;
6796: }
6797: }
6798: else {
6799: if (values1[i] == NULL)
6800: values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
6801: if (values2[j] == NULL)
6802: values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
6803: ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
6804: if (ret)
6805: break;
6806: }
6807: }
6808: if (ret)
6809: break;
6810: }
6811: for (i = 0;i < ns1->nodeNr;i++)
6812: if (values1[i] != NULL)
6813: xmlFree(values1[i]);
6814: for (j = 0;j < ns2->nodeNr;j++)
6815: if (values2[j] != NULL)
6816: xmlFree(values2[j]);
6817: xmlFree(values1);
6818: xmlFree(values2);
6819: xmlFree(hashs1);
6820: xmlFree(hashs2);
6821: return(ret);
6822: }
6823:
6824: static int
6825: xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
6826: xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
6827: int ret = 0;
6828: /*
6829: *At this point we are assured neither arg1 nor arg2
6830: *is a nodeset, so we can just pick the appropriate routine.
6831: */
6832: switch (arg1->type) {
6833: case XPATH_UNDEFINED:
6834: #ifdef DEBUG_EXPR
6835: xmlGenericError(xmlGenericErrorContext,
6836: "Equal: undefined\n");
6837: #endif
6838: break;
6839: case XPATH_BOOLEAN:
6840: switch (arg2->type) {
6841: case XPATH_UNDEFINED:
6842: #ifdef DEBUG_EXPR
6843: xmlGenericError(xmlGenericErrorContext,
6844: "Equal: undefined\n");
6845: #endif
6846: break;
6847: case XPATH_BOOLEAN:
6848: #ifdef DEBUG_EXPR
6849: xmlGenericError(xmlGenericErrorContext,
6850: "Equal: %d boolean %d \n",
6851: arg1->boolval, arg2->boolval);
6852: #endif
6853: ret = (arg1->boolval == arg2->boolval);
6854: break;
6855: case XPATH_NUMBER:
6856: ret = (arg1->boolval ==
6857: xmlXPathCastNumberToBoolean(arg2->floatval));
6858: break;
6859: case XPATH_STRING:
6860: if ((arg2->stringval == NULL) ||
6861: (arg2->stringval[0] == 0)) ret = 0;
6862: else
6863: ret = 1;
6864: ret = (arg1->boolval == ret);
6865: break;
6866: case XPATH_USERS:
6867: case XPATH_POINT:
6868: case XPATH_RANGE:
6869: case XPATH_LOCATIONSET:
6870: TODO
6871: break;
6872: case XPATH_NODESET:
6873: case XPATH_XSLT_TREE:
6874: break;
6875: }
6876: break;
6877: case XPATH_NUMBER:
6878: switch (arg2->type) {
6879: case XPATH_UNDEFINED:
6880: #ifdef DEBUG_EXPR
6881: xmlGenericError(xmlGenericErrorContext,
6882: "Equal: undefined\n");
6883: #endif
6884: break;
6885: case XPATH_BOOLEAN:
6886: ret = (arg2->boolval==
6887: xmlXPathCastNumberToBoolean(arg1->floatval));
6888: break;
6889: case XPATH_STRING:
6890: valuePush(ctxt, arg2);
6891: xmlXPathNumberFunction(ctxt, 1);
6892: arg2 = valuePop(ctxt);
6893: /* no break on purpose */
6894: case XPATH_NUMBER:
6895: /* Hand check NaN and Infinity equalities */
6896: if (xmlXPathIsNaN(arg1->floatval) ||
6897: xmlXPathIsNaN(arg2->floatval)) {
6898: ret = 0;
6899: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
6900: if (xmlXPathIsInf(arg2->floatval) == 1)
6901: ret = 1;
6902: else
6903: ret = 0;
6904: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
6905: if (xmlXPathIsInf(arg2->floatval) == -1)
6906: ret = 1;
6907: else
6908: ret = 0;
6909: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
6910: if (xmlXPathIsInf(arg1->floatval) == 1)
6911: ret = 1;
6912: else
6913: ret = 0;
6914: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
6915: if (xmlXPathIsInf(arg1->floatval) == -1)
6916: ret = 1;
6917: else
6918: ret = 0;
6919: } else {
6920: ret = (arg1->floatval == arg2->floatval);
6921: }
6922: break;
6923: case XPATH_USERS:
6924: case XPATH_POINT:
6925: case XPATH_RANGE:
6926: case XPATH_LOCATIONSET:
6927: TODO
6928: break;
6929: case XPATH_NODESET:
6930: case XPATH_XSLT_TREE:
6931: break;
6932: }
6933: break;
6934: case XPATH_STRING:
6935: switch (arg2->type) {
6936: case XPATH_UNDEFINED:
6937: #ifdef DEBUG_EXPR
6938: xmlGenericError(xmlGenericErrorContext,
6939: "Equal: undefined\n");
6940: #endif
6941: break;
6942: case XPATH_BOOLEAN:
6943: if ((arg1->stringval == NULL) ||
6944: (arg1->stringval[0] == 0)) ret = 0;
6945: else
6946: ret = 1;
6947: ret = (arg2->boolval == ret);
6948: break;
6949: case XPATH_STRING:
6950: ret = xmlStrEqual(arg1->stringval, arg2->stringval);
6951: break;
6952: case XPATH_NUMBER:
6953: valuePush(ctxt, arg1);
6954: xmlXPathNumberFunction(ctxt, 1);
6955: arg1 = valuePop(ctxt);
6956: /* Hand check NaN and Infinity equalities */
6957: if (xmlXPathIsNaN(arg1->floatval) ||
6958: xmlXPathIsNaN(arg2->floatval)) {
6959: ret = 0;
6960: } else if (xmlXPathIsInf(arg1->floatval) == 1) {
6961: if (xmlXPathIsInf(arg2->floatval) == 1)
6962: ret = 1;
6963: else
6964: ret = 0;
6965: } else if (xmlXPathIsInf(arg1->floatval) == -1) {
6966: if (xmlXPathIsInf(arg2->floatval) == -1)
6967: ret = 1;
6968: else
6969: ret = 0;
6970: } else if (xmlXPathIsInf(arg2->floatval) == 1) {
6971: if (xmlXPathIsInf(arg1->floatval) == 1)
6972: ret = 1;
6973: else
6974: ret = 0;
6975: } else if (xmlXPathIsInf(arg2->floatval) == -1) {
6976: if (xmlXPathIsInf(arg1->floatval) == -1)
6977: ret = 1;
6978: else
6979: ret = 0;
6980: } else {
6981: ret = (arg1->floatval == arg2->floatval);
6982: }
6983: break;
6984: case XPATH_USERS:
6985: case XPATH_POINT:
6986: case XPATH_RANGE:
6987: case XPATH_LOCATIONSET:
6988: TODO
6989: break;
6990: case XPATH_NODESET:
6991: case XPATH_XSLT_TREE:
6992: break;
6993: }
6994: break;
6995: case XPATH_USERS:
6996: case XPATH_POINT:
6997: case XPATH_RANGE:
6998: case XPATH_LOCATIONSET:
6999: TODO
7000: break;
7001: case XPATH_NODESET:
7002: case XPATH_XSLT_TREE:
7003: break;
7004: }
7005: xmlXPathReleaseObject(ctxt->context, arg1);
7006: xmlXPathReleaseObject(ctxt->context, arg2);
7007: return(ret);
7008: }
7009:
7010: /**
7011: * xmlXPathEqualValues:
7012: * @ctxt: the XPath Parser context
7013: *
7014: * Implement the equal operation on XPath objects content: @arg1 == @arg2
7015: *
7016: * Returns 0 or 1 depending on the results of the test.
7017: */
7018: int
7019: xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
7020: xmlXPathObjectPtr arg1, arg2, argtmp;
7021: int ret = 0;
7022:
7023: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7024: arg2 = valuePop(ctxt);
7025: arg1 = valuePop(ctxt);
7026: if ((arg1 == NULL) || (arg2 == NULL)) {
7027: if (arg1 != NULL)
7028: xmlXPathReleaseObject(ctxt->context, arg1);
7029: else
7030: xmlXPathReleaseObject(ctxt->context, arg2);
7031: XP_ERROR0(XPATH_INVALID_OPERAND);
7032: }
7033:
7034: if (arg1 == arg2) {
7035: #ifdef DEBUG_EXPR
7036: xmlGenericError(xmlGenericErrorContext,
7037: "Equal: by pointer\n");
7038: #endif
7039: xmlXPathFreeObject(arg1);
7040: return(1);
7041: }
7042:
7043: /*
7044: *If either argument is a nodeset, it's a 'special case'
7045: */
7046: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7047: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7048: /*
7049: *Hack it to assure arg1 is the nodeset
7050: */
7051: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7052: argtmp = arg2;
7053: arg2 = arg1;
7054: arg1 = argtmp;
7055: }
7056: switch (arg2->type) {
7057: case XPATH_UNDEFINED:
7058: #ifdef DEBUG_EXPR
7059: xmlGenericError(xmlGenericErrorContext,
7060: "Equal: undefined\n");
7061: #endif
7062: break;
7063: case XPATH_NODESET:
7064: case XPATH_XSLT_TREE:
7065: ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
7066: break;
7067: case XPATH_BOOLEAN:
7068: if ((arg1->nodesetval == NULL) ||
7069: (arg1->nodesetval->nodeNr == 0)) ret = 0;
7070: else
7071: ret = 1;
7072: ret = (ret == arg2->boolval);
7073: break;
7074: case XPATH_NUMBER:
7075: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0);
7076: break;
7077: case XPATH_STRING:
7078: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
7079: break;
7080: case XPATH_USERS:
7081: case XPATH_POINT:
7082: case XPATH_RANGE:
7083: case XPATH_LOCATIONSET:
7084: TODO
7085: break;
7086: }
7087: xmlXPathReleaseObject(ctxt->context, arg1);
7088: xmlXPathReleaseObject(ctxt->context, arg2);
7089: return(ret);
7090: }
7091:
7092: return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7093: }
7094:
7095: /**
7096: * xmlXPathNotEqualValues:
7097: * @ctxt: the XPath Parser context
7098: *
7099: * Implement the equal operation on XPath objects content: @arg1 == @arg2
7100: *
7101: * Returns 0 or 1 depending on the results of the test.
7102: */
7103: int
7104: xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
7105: xmlXPathObjectPtr arg1, arg2, argtmp;
7106: int ret = 0;
7107:
7108: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7109: arg2 = valuePop(ctxt);
7110: arg1 = valuePop(ctxt);
7111: if ((arg1 == NULL) || (arg2 == NULL)) {
7112: if (arg1 != NULL)
7113: xmlXPathReleaseObject(ctxt->context, arg1);
7114: else
7115: xmlXPathReleaseObject(ctxt->context, arg2);
7116: XP_ERROR0(XPATH_INVALID_OPERAND);
7117: }
7118:
7119: if (arg1 == arg2) {
7120: #ifdef DEBUG_EXPR
7121: xmlGenericError(xmlGenericErrorContext,
7122: "NotEqual: by pointer\n");
7123: #endif
7124: xmlXPathReleaseObject(ctxt->context, arg1);
7125: return(0);
7126: }
7127:
7128: /*
7129: *If either argument is a nodeset, it's a 'special case'
7130: */
7131: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7132: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7133: /*
7134: *Hack it to assure arg1 is the nodeset
7135: */
7136: if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
7137: argtmp = arg2;
7138: arg2 = arg1;
7139: arg1 = argtmp;
7140: }
7141: switch (arg2->type) {
7142: case XPATH_UNDEFINED:
7143: #ifdef DEBUG_EXPR
7144: xmlGenericError(xmlGenericErrorContext,
7145: "NotEqual: undefined\n");
7146: #endif
7147: break;
7148: case XPATH_NODESET:
7149: case XPATH_XSLT_TREE:
7150: ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
7151: break;
7152: case XPATH_BOOLEAN:
7153: if ((arg1->nodesetval == NULL) ||
7154: (arg1->nodesetval->nodeNr == 0)) ret = 0;
7155: else
7156: ret = 1;
7157: ret = (ret != arg2->boolval);
7158: break;
7159: case XPATH_NUMBER:
7160: ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1);
7161: break;
7162: case XPATH_STRING:
7163: ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
7164: break;
7165: case XPATH_USERS:
7166: case XPATH_POINT:
7167: case XPATH_RANGE:
7168: case XPATH_LOCATIONSET:
7169: TODO
7170: break;
7171: }
7172: xmlXPathReleaseObject(ctxt->context, arg1);
7173: xmlXPathReleaseObject(ctxt->context, arg2);
7174: return(ret);
7175: }
7176:
7177: return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
7178: }
7179:
7180: /**
7181: * xmlXPathCompareValues:
7182: * @ctxt: the XPath Parser context
7183: * @inf: less than (1) or greater than (0)
7184: * @strict: is the comparison strict
7185: *
7186: * Implement the compare operation on XPath objects:
7187: * @arg1 < @arg2 (1, 1, ...
7188: * @arg1 <= @arg2 (1, 0, ...
7189: * @arg1 > @arg2 (0, 1, ...
7190: * @arg1 >= @arg2 (0, 0, ...
7191: *
7192: * When neither object to be compared is a node-set and the operator is
7193: * <=, <, >=, >, then the objects are compared by converted both objects
7194: * to numbers and comparing the numbers according to IEEE 754. The <
7195: * comparison will be true if and only if the first number is less than the
7196: * second number. The <= comparison will be true if and only if the first
7197: * number is less than or equal to the second number. The > comparison
7198: * will be true if and only if the first number is greater than the second
7199: * number. The >= comparison will be true if and only if the first number
7200: * is greater than or equal to the second number.
7201: *
7202: * Returns 1 if the comparison succeeded, 0 if it failed
7203: */
7204: int
7205: xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
7206: int ret = 0, arg1i = 0, arg2i = 0;
7207: xmlXPathObjectPtr arg1, arg2;
7208:
7209: if ((ctxt == NULL) || (ctxt->context == NULL)) return(0);
7210: arg2 = valuePop(ctxt);
7211: arg1 = valuePop(ctxt);
7212: if ((arg1 == NULL) || (arg2 == NULL)) {
7213: if (arg1 != NULL)
7214: xmlXPathReleaseObject(ctxt->context, arg1);
7215: else
7216: xmlXPathReleaseObject(ctxt->context, arg2);
7217: XP_ERROR0(XPATH_INVALID_OPERAND);
7218: }
7219:
7220: if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
7221: (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7222: /*
7223: * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments
7224: * are not freed from within this routine; they will be freed from the
7225: * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue
7226: */
7227: if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
7228: ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
7229: ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
7230: } else {
7231: if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
7232: ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
7233: arg1, arg2);
7234: } else {
7235: ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
7236: arg2, arg1);
7237: }
7238: }
7239: return(ret);
7240: }
7241:
7242: if (arg1->type != XPATH_NUMBER) {
7243: valuePush(ctxt, arg1);
7244: xmlXPathNumberFunction(ctxt, 1);
7245: arg1 = valuePop(ctxt);
7246: }
7247: if (arg1->type != XPATH_NUMBER) {
7248: xmlXPathFreeObject(arg1);
7249: xmlXPathFreeObject(arg2);
7250: XP_ERROR0(XPATH_INVALID_OPERAND);
7251: }
7252: if (arg2->type != XPATH_NUMBER) {
7253: valuePush(ctxt, arg2);
7254: xmlXPathNumberFunction(ctxt, 1);
7255: arg2 = valuePop(ctxt);
7256: }
7257: if (arg2->type != XPATH_NUMBER) {
7258: xmlXPathReleaseObject(ctxt->context, arg1);
7259: xmlXPathReleaseObject(ctxt->context, arg2);
7260: XP_ERROR0(XPATH_INVALID_OPERAND);
7261: }
7262: /*
7263: * Add tests for infinity and nan
7264: * => feedback on 3.4 for Inf and NaN
7265: */
7266: /* Hand check NaN and Infinity comparisons */
7267: if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
7268: ret=0;
7269: } else {
7270: arg1i=xmlXPathIsInf(arg1->floatval);
7271: arg2i=xmlXPathIsInf(arg2->floatval);
7272: if (inf && strict) {
7273: if ((arg1i == -1 && arg2i != -1) ||
7274: (arg2i == 1 && arg1i != 1)) {
7275: ret = 1;
7276: } else if (arg1i == 0 && arg2i == 0) {
7277: ret = (arg1->floatval < arg2->floatval);
7278: } else {
7279: ret = 0;
7280: }
7281: }
7282: else if (inf && !strict) {
7283: if (arg1i == -1 || arg2i == 1) {
7284: ret = 1;
7285: } else if (arg1i == 0 && arg2i == 0) {
7286: ret = (arg1->floatval <= arg2->floatval);
7287: } else {
7288: ret = 0;
7289: }
7290: }
7291: else if (!inf && strict) {
7292: if ((arg1i == 1 && arg2i != 1) ||
7293: (arg2i == -1 && arg1i != -1)) {
7294: ret = 1;
7295: } else if (arg1i == 0 && arg2i == 0) {
7296: ret = (arg1->floatval > arg2->floatval);
7297: } else {
7298: ret = 0;
7299: }
7300: }
7301: else if (!inf && !strict) {
7302: if (arg1i == 1 || arg2i == -1) {
7303: ret = 1;
7304: } else if (arg1i == 0 && arg2i == 0) {
7305: ret = (arg1->floatval >= arg2->floatval);
7306: } else {
7307: ret = 0;
7308: }
7309: }
7310: }
7311: xmlXPathReleaseObject(ctxt->context, arg1);
7312: xmlXPathReleaseObject(ctxt->context, arg2);
7313: return(ret);
7314: }
7315:
7316: /**
7317: * xmlXPathValueFlipSign:
7318: * @ctxt: the XPath Parser context
7319: *
7320: * Implement the unary - operation on an XPath object
7321: * The numeric operators convert their operands to numbers as if
7322: * by calling the number function.
7323: */
7324: void
7325: xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
7326: if ((ctxt == NULL) || (ctxt->context == NULL)) return;
7327: CAST_TO_NUMBER;
7328: CHECK_TYPE(XPATH_NUMBER);
7329: if (xmlXPathIsNaN(ctxt->value->floatval))
7330: ctxt->value->floatval=xmlXPathNAN;
7331: else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
7332: ctxt->value->floatval=xmlXPathNINF;
7333: else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
7334: ctxt->value->floatval=xmlXPathPINF;
7335: else if (ctxt->value->floatval == 0) {
7336: if (xmlXPathGetSign(ctxt->value->floatval) == 0)
7337: ctxt->value->floatval = xmlXPathNZERO;
7338: else
7339: ctxt->value->floatval = 0;
7340: }
7341: else
7342: ctxt->value->floatval = - ctxt->value->floatval;
7343: }
7344:
7345: /**
7346: * xmlXPathAddValues:
7347: * @ctxt: the XPath Parser context
7348: *
7349: * Implement the add operation on XPath objects:
7350: * The numeric operators convert their operands to numbers as if
7351: * by calling the number function.
7352: */
7353: void
7354: xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
7355: xmlXPathObjectPtr arg;
7356: double val;
7357:
7358: arg = valuePop(ctxt);
7359: if (arg == NULL)
7360: XP_ERROR(XPATH_INVALID_OPERAND);
7361: val = xmlXPathCastToNumber(arg);
7362: xmlXPathReleaseObject(ctxt->context, arg);
7363: CAST_TO_NUMBER;
7364: CHECK_TYPE(XPATH_NUMBER);
7365: ctxt->value->floatval += val;
7366: }
7367:
7368: /**
7369: * xmlXPathSubValues:
7370: * @ctxt: the XPath Parser context
7371: *
7372: * Implement the subtraction operation on XPath objects:
7373: * The numeric operators convert their operands to numbers as if
7374: * by calling the number function.
7375: */
7376: void
7377: xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
7378: xmlXPathObjectPtr arg;
7379: double val;
7380:
7381: arg = valuePop(ctxt);
7382: if (arg == NULL)
7383: XP_ERROR(XPATH_INVALID_OPERAND);
7384: val = xmlXPathCastToNumber(arg);
7385: xmlXPathReleaseObject(ctxt->context, arg);
7386: CAST_TO_NUMBER;
7387: CHECK_TYPE(XPATH_NUMBER);
7388: ctxt->value->floatval -= val;
7389: }
7390:
7391: /**
7392: * xmlXPathMultValues:
7393: * @ctxt: the XPath Parser context
7394: *
7395: * Implement the multiply operation on XPath objects:
7396: * The numeric operators convert their operands to numbers as if
7397: * by calling the number function.
7398: */
7399: void
7400: xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
7401: xmlXPathObjectPtr arg;
7402: double val;
7403:
7404: arg = valuePop(ctxt);
7405: if (arg == NULL)
7406: XP_ERROR(XPATH_INVALID_OPERAND);
7407: val = xmlXPathCastToNumber(arg);
7408: xmlXPathReleaseObject(ctxt->context, arg);
7409: CAST_TO_NUMBER;
7410: CHECK_TYPE(XPATH_NUMBER);
7411: ctxt->value->floatval *= val;
7412: }
7413:
7414: /**
7415: * xmlXPathDivValues:
7416: * @ctxt: the XPath Parser context
7417: *
7418: * Implement the div operation on XPath objects @arg1 / @arg2:
7419: * The numeric operators convert their operands to numbers as if
7420: * by calling the number function.
7421: */
7422: void
7423: xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
7424: xmlXPathObjectPtr arg;
7425: double val;
7426:
7427: arg = valuePop(ctxt);
7428: if (arg == NULL)
7429: XP_ERROR(XPATH_INVALID_OPERAND);
7430: val = xmlXPathCastToNumber(arg);
7431: xmlXPathReleaseObject(ctxt->context, arg);
7432: CAST_TO_NUMBER;
7433: CHECK_TYPE(XPATH_NUMBER);
7434: if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
7435: ctxt->value->floatval = xmlXPathNAN;
7436: else if (val == 0 && xmlXPathGetSign(val) != 0) {
7437: if (ctxt->value->floatval == 0)
7438: ctxt->value->floatval = xmlXPathNAN;
7439: else if (ctxt->value->floatval > 0)
7440: ctxt->value->floatval = xmlXPathNINF;
7441: else if (ctxt->value->floatval < 0)
7442: ctxt->value->floatval = xmlXPathPINF;
7443: }
7444: else if (val == 0) {
7445: if (ctxt->value->floatval == 0)
7446: ctxt->value->floatval = xmlXPathNAN;
7447: else if (ctxt->value->floatval > 0)
7448: ctxt->value->floatval = xmlXPathPINF;
7449: else if (ctxt->value->floatval < 0)
7450: ctxt->value->floatval = xmlXPathNINF;
7451: } else
7452: ctxt->value->floatval /= val;
7453: }
7454:
7455: /**
7456: * xmlXPathModValues:
7457: * @ctxt: the XPath Parser context
7458: *
7459: * Implement the mod operation on XPath objects: @arg1 / @arg2
7460: * The numeric operators convert their operands to numbers as if
7461: * by calling the number function.
7462: */
7463: void
7464: xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
7465: xmlXPathObjectPtr arg;
7466: double arg1, arg2;
7467:
7468: arg = valuePop(ctxt);
7469: if (arg == NULL)
7470: XP_ERROR(XPATH_INVALID_OPERAND);
7471: arg2 = xmlXPathCastToNumber(arg);
7472: xmlXPathReleaseObject(ctxt->context, arg);
7473: CAST_TO_NUMBER;
7474: CHECK_TYPE(XPATH_NUMBER);
7475: arg1 = ctxt->value->floatval;
7476: if (arg2 == 0)
7477: ctxt->value->floatval = xmlXPathNAN;
7478: else {
7479: ctxt->value->floatval = fmod(arg1, arg2);
7480: }
7481: }
7482:
7483: /************************************************************************
7484: * *
7485: * The traversal functions *
7486: * *
7487: ************************************************************************/
7488:
7489: /*
7490: * A traversal function enumerates nodes along an axis.
7491: * Initially it must be called with NULL, and it indicates
7492: * termination on the axis by returning NULL.
7493: */
7494: typedef xmlNodePtr (*xmlXPathTraversalFunction)
7495: (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
7496:
7497: /*
7498: * xmlXPathTraversalFunctionExt:
7499: * A traversal function enumerates nodes along an axis.
7500: * Initially it must be called with NULL, and it indicates
7501: * termination on the axis by returning NULL.
7502: * The context node of the traversal is specified via @contextNode.
7503: */
7504: typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
7505: (xmlNodePtr cur, xmlNodePtr contextNode);
7506:
7507: /*
7508: * xmlXPathNodeSetMergeFunction:
7509: * Used for merging node sets in xmlXPathCollectAndTest().
7510: */
7511: typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
7512: (xmlNodeSetPtr, xmlNodeSetPtr, int);
7513:
7514:
7515: /**
7516: * xmlXPathNextSelf:
7517: * @ctxt: the XPath Parser context
7518: * @cur: the current node in the traversal
7519: *
7520: * Traversal function for the "self" direction
7521: * The self axis contains just the context node itself
7522: *
7523: * Returns the next element following that axis
7524: */
7525: xmlNodePtr
7526: xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7527: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7528: if (cur == NULL)
7529: return(ctxt->context->node);
7530: return(NULL);
7531: }
7532:
7533: /**
7534: * xmlXPathNextChild:
7535: * @ctxt: the XPath Parser context
7536: * @cur: the current node in the traversal
7537: *
7538: * Traversal function for the "child" direction
7539: * The child axis contains the children of the context node in document order.
7540: *
7541: * Returns the next element following that axis
7542: */
7543: xmlNodePtr
7544: xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7545: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7546: if (cur == NULL) {
7547: if (ctxt->context->node == NULL) return(NULL);
7548: switch (ctxt->context->node->type) {
7549: case XML_ELEMENT_NODE:
7550: case XML_TEXT_NODE:
7551: case XML_CDATA_SECTION_NODE:
7552: case XML_ENTITY_REF_NODE:
7553: case XML_ENTITY_NODE:
7554: case XML_PI_NODE:
7555: case XML_COMMENT_NODE:
7556: case XML_NOTATION_NODE:
7557: case XML_DTD_NODE:
7558: return(ctxt->context->node->children);
7559: case XML_DOCUMENT_NODE:
7560: case XML_DOCUMENT_TYPE_NODE:
7561: case XML_DOCUMENT_FRAG_NODE:
7562: case XML_HTML_DOCUMENT_NODE:
7563: #ifdef LIBXML_DOCB_ENABLED
7564: case XML_DOCB_DOCUMENT_NODE:
7565: #endif
7566: return(((xmlDocPtr) ctxt->context->node)->children);
7567: case XML_ELEMENT_DECL:
7568: case XML_ATTRIBUTE_DECL:
7569: case XML_ENTITY_DECL:
7570: case XML_ATTRIBUTE_NODE:
7571: case XML_NAMESPACE_DECL:
7572: case XML_XINCLUDE_START:
7573: case XML_XINCLUDE_END:
7574: return(NULL);
7575: }
7576: return(NULL);
7577: }
7578: if ((cur->type == XML_DOCUMENT_NODE) ||
7579: (cur->type == XML_HTML_DOCUMENT_NODE))
7580: return(NULL);
7581: return(cur->next);
7582: }
7583:
7584: /**
7585: * xmlXPathNextChildElement:
7586: * @ctxt: the XPath Parser context
7587: * @cur: the current node in the traversal
7588: *
7589: * Traversal function for the "child" direction and nodes of type element.
7590: * The child axis contains the children of the context node in document order.
7591: *
7592: * Returns the next element following that axis
7593: */
7594: static xmlNodePtr
7595: xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7596: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7597: if (cur == NULL) {
7598: cur = ctxt->context->node;
7599: if (cur == NULL) return(NULL);
7600: /*
7601: * Get the first element child.
7602: */
7603: switch (cur->type) {
7604: case XML_ELEMENT_NODE:
7605: case XML_DOCUMENT_FRAG_NODE:
7606: case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
7607: case XML_ENTITY_NODE:
7608: cur = cur->children;
7609: if (cur != NULL) {
7610: if (cur->type == XML_ELEMENT_NODE)
7611: return(cur);
7612: do {
7613: cur = cur->next;
7614: } while ((cur != NULL) &&
7615: (cur->type != XML_ELEMENT_NODE));
7616: return(cur);
7617: }
7618: return(NULL);
7619: case XML_DOCUMENT_NODE:
7620: case XML_HTML_DOCUMENT_NODE:
7621: #ifdef LIBXML_DOCB_ENABLED
7622: case XML_DOCB_DOCUMENT_NODE:
7623: #endif
7624: return(xmlDocGetRootElement((xmlDocPtr) cur));
7625: default:
7626: return(NULL);
7627: }
7628: return(NULL);
7629: }
7630: /*
7631: * Get the next sibling element node.
7632: */
7633: switch (cur->type) {
7634: case XML_ELEMENT_NODE:
7635: case XML_TEXT_NODE:
7636: case XML_ENTITY_REF_NODE:
7637: case XML_ENTITY_NODE:
7638: case XML_CDATA_SECTION_NODE:
7639: case XML_PI_NODE:
7640: case XML_COMMENT_NODE:
7641: case XML_XINCLUDE_END:
7642: break;
7643: /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
7644: default:
7645: return(NULL);
7646: }
7647: if (cur->next != NULL) {
7648: if (cur->next->type == XML_ELEMENT_NODE)
7649: return(cur->next);
7650: cur = cur->next;
7651: do {
7652: cur = cur->next;
7653: } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
7654: return(cur);
7655: }
7656: return(NULL);
7657: }
7658:
7659: /**
7660: * xmlXPathNextDescendantOrSelfElemParent:
7661: * @ctxt: the XPath Parser context
7662: * @cur: the current node in the traversal
7663: *
7664: * Traversal function for the "descendant-or-self" axis.
7665: * Additionally it returns only nodes which can be parents of
7666: * element nodes.
7667: *
7668: *
7669: * Returns the next element following that axis
7670: */
7671: static xmlNodePtr
7672: xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
7673: xmlNodePtr contextNode)
7674: {
7675: if (cur == NULL) {
7676: if (contextNode == NULL)
7677: return(NULL);
7678: switch (contextNode->type) {
7679: case XML_ELEMENT_NODE:
7680: case XML_XINCLUDE_START:
7681: case XML_DOCUMENT_FRAG_NODE:
7682: case XML_DOCUMENT_NODE:
7683: #ifdef LIBXML_DOCB_ENABLED
7684: case XML_DOCB_DOCUMENT_NODE:
7685: #endif
7686: case XML_HTML_DOCUMENT_NODE:
7687: return(contextNode);
7688: default:
7689: return(NULL);
7690: }
7691: return(NULL);
7692: } else {
7693: xmlNodePtr start = cur;
7694:
7695: while (cur != NULL) {
7696: switch (cur->type) {
7697: case XML_ELEMENT_NODE:
7698: /* TODO: OK to have XInclude here? */
7699: case XML_XINCLUDE_START:
7700: case XML_DOCUMENT_FRAG_NODE:
7701: if (cur != start)
7702: return(cur);
7703: if (cur->children != NULL) {
7704: cur = cur->children;
7705: continue;
7706: }
7707: break;
7708: /* Not sure if we need those here. */
7709: case XML_DOCUMENT_NODE:
7710: #ifdef LIBXML_DOCB_ENABLED
7711: case XML_DOCB_DOCUMENT_NODE:
7712: #endif
7713: case XML_HTML_DOCUMENT_NODE:
7714: if (cur != start)
7715: return(cur);
7716: return(xmlDocGetRootElement((xmlDocPtr) cur));
7717: default:
7718: break;
7719: }
7720:
7721: next_sibling:
7722: if ((cur == NULL) || (cur == contextNode))
7723: return(NULL);
7724: if (cur->next != NULL) {
7725: cur = cur->next;
7726: } else {
7727: cur = cur->parent;
7728: goto next_sibling;
7729: }
7730: }
7731: }
7732: return(NULL);
7733: }
7734:
7735: /**
7736: * xmlXPathNextDescendant:
7737: * @ctxt: the XPath Parser context
7738: * @cur: the current node in the traversal
7739: *
7740: * Traversal function for the "descendant" direction
7741: * the descendant axis contains the descendants of the context node in document
7742: * order; a descendant is a child or a child of a child and so on.
7743: *
7744: * Returns the next element following that axis
7745: */
7746: xmlNodePtr
7747: xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7748: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7749: if (cur == NULL) {
7750: if (ctxt->context->node == NULL)
7751: return(NULL);
7752: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7753: (ctxt->context->node->type == XML_NAMESPACE_DECL))
7754: return(NULL);
7755:
7756: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
7757: return(ctxt->context->doc->children);
7758: return(ctxt->context->node->children);
7759: }
7760:
7761: if (cur->children != NULL) {
7762: /*
7763: * Do not descend on entities declarations
7764: */
7765: if (cur->children->type != XML_ENTITY_DECL) {
7766: cur = cur->children;
7767: /*
7768: * Skip DTDs
7769: */
7770: if (cur->type != XML_DTD_NODE)
7771: return(cur);
7772: }
7773: }
7774:
7775: if (cur == ctxt->context->node) return(NULL);
7776:
7777: while (cur->next != NULL) {
7778: cur = cur->next;
7779: if ((cur->type != XML_ENTITY_DECL) &&
7780: (cur->type != XML_DTD_NODE))
7781: return(cur);
7782: }
7783:
7784: do {
7785: cur = cur->parent;
7786: if (cur == NULL) break;
7787: if (cur == ctxt->context->node) return(NULL);
7788: if (cur->next != NULL) {
7789: cur = cur->next;
7790: return(cur);
7791: }
7792: } while (cur != NULL);
7793: return(cur);
7794: }
7795:
7796: /**
7797: * xmlXPathNextDescendantOrSelf:
7798: * @ctxt: the XPath Parser context
7799: * @cur: the current node in the traversal
7800: *
7801: * Traversal function for the "descendant-or-self" direction
7802: * the descendant-or-self axis contains the context node and the descendants
7803: * of the context node in document order; thus the context node is the first
7804: * node on the axis, and the first child of the context node is the second node
7805: * on the axis
7806: *
7807: * Returns the next element following that axis
7808: */
7809: xmlNodePtr
7810: xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7811: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7812: if (cur == NULL) {
7813: if (ctxt->context->node == NULL)
7814: return(NULL);
7815: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
7816: (ctxt->context->node->type == XML_NAMESPACE_DECL))
7817: return(NULL);
7818: return(ctxt->context->node);
7819: }
7820:
7821: return(xmlXPathNextDescendant(ctxt, cur));
7822: }
7823:
7824: /**
7825: * xmlXPathNextParent:
7826: * @ctxt: the XPath Parser context
7827: * @cur: the current node in the traversal
7828: *
7829: * Traversal function for the "parent" direction
7830: * The parent axis contains the parent of the context node, if there is one.
7831: *
7832: * Returns the next element following that axis
7833: */
7834: xmlNodePtr
7835: xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7836: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7837: /*
7838: * the parent of an attribute or namespace node is the element
7839: * to which the attribute or namespace node is attached
7840: * Namespace handling !!!
7841: */
7842: if (cur == NULL) {
7843: if (ctxt->context->node == NULL) return(NULL);
7844: switch (ctxt->context->node->type) {
7845: case XML_ELEMENT_NODE:
7846: case XML_TEXT_NODE:
7847: case XML_CDATA_SECTION_NODE:
7848: case XML_ENTITY_REF_NODE:
7849: case XML_ENTITY_NODE:
7850: case XML_PI_NODE:
7851: case XML_COMMENT_NODE:
7852: case XML_NOTATION_NODE:
7853: case XML_DTD_NODE:
7854: case XML_ELEMENT_DECL:
7855: case XML_ATTRIBUTE_DECL:
7856: case XML_XINCLUDE_START:
7857: case XML_XINCLUDE_END:
7858: case XML_ENTITY_DECL:
7859: if (ctxt->context->node->parent == NULL)
7860: return((xmlNodePtr) ctxt->context->doc);
7861: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
7862: ((ctxt->context->node->parent->name[0] == ' ') ||
7863: (xmlStrEqual(ctxt->context->node->parent->name,
7864: BAD_CAST "fake node libxslt"))))
7865: return(NULL);
7866: return(ctxt->context->node->parent);
7867: case XML_ATTRIBUTE_NODE: {
7868: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
7869:
7870: return(att->parent);
7871: }
7872: case XML_DOCUMENT_NODE:
7873: case XML_DOCUMENT_TYPE_NODE:
7874: case XML_DOCUMENT_FRAG_NODE:
7875: case XML_HTML_DOCUMENT_NODE:
7876: #ifdef LIBXML_DOCB_ENABLED
7877: case XML_DOCB_DOCUMENT_NODE:
7878: #endif
7879: return(NULL);
7880: case XML_NAMESPACE_DECL: {
7881: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
7882:
7883: if ((ns->next != NULL) &&
7884: (ns->next->type != XML_NAMESPACE_DECL))
7885: return((xmlNodePtr) ns->next);
7886: return(NULL);
7887: }
7888: }
7889: }
7890: return(NULL);
7891: }
7892:
7893: /**
7894: * xmlXPathNextAncestor:
7895: * @ctxt: the XPath Parser context
7896: * @cur: the current node in the traversal
7897: *
7898: * Traversal function for the "ancestor" direction
7899: * the ancestor axis contains the ancestors of the context node; the ancestors
7900: * of the context node consist of the parent of context node and the parent's
7901: * parent and so on; the nodes are ordered in reverse document order; thus the
7902: * parent is the first node on the axis, and the parent's parent is the second
7903: * node on the axis
7904: *
7905: * Returns the next element following that axis
7906: */
7907: xmlNodePtr
7908: xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
7909: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
7910: /*
7911: * the parent of an attribute or namespace node is the element
7912: * to which the attribute or namespace node is attached
7913: * !!!!!!!!!!!!!
7914: */
7915: if (cur == NULL) {
7916: if (ctxt->context->node == NULL) return(NULL);
7917: switch (ctxt->context->node->type) {
7918: case XML_ELEMENT_NODE:
7919: case XML_TEXT_NODE:
7920: case XML_CDATA_SECTION_NODE:
7921: case XML_ENTITY_REF_NODE:
7922: case XML_ENTITY_NODE:
7923: case XML_PI_NODE:
7924: case XML_COMMENT_NODE:
7925: case XML_DTD_NODE:
7926: case XML_ELEMENT_DECL:
7927: case XML_ATTRIBUTE_DECL:
7928: case XML_ENTITY_DECL:
7929: case XML_NOTATION_NODE:
7930: case XML_XINCLUDE_START:
7931: case XML_XINCLUDE_END:
7932: if (ctxt->context->node->parent == NULL)
7933: return((xmlNodePtr) ctxt->context->doc);
7934: if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
7935: ((ctxt->context->node->parent->name[0] == ' ') ||
7936: (xmlStrEqual(ctxt->context->node->parent->name,
7937: BAD_CAST "fake node libxslt"))))
7938: return(NULL);
7939: return(ctxt->context->node->parent);
7940: case XML_ATTRIBUTE_NODE: {
7941: xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
7942:
7943: return(tmp->parent);
7944: }
7945: case XML_DOCUMENT_NODE:
7946: case XML_DOCUMENT_TYPE_NODE:
7947: case XML_DOCUMENT_FRAG_NODE:
7948: case XML_HTML_DOCUMENT_NODE:
7949: #ifdef LIBXML_DOCB_ENABLED
7950: case XML_DOCB_DOCUMENT_NODE:
7951: #endif
7952: return(NULL);
7953: case XML_NAMESPACE_DECL: {
7954: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
7955:
7956: if ((ns->next != NULL) &&
7957: (ns->next->type != XML_NAMESPACE_DECL))
7958: return((xmlNodePtr) ns->next);
7959: /* Bad, how did that namespace end up here ? */
7960: return(NULL);
7961: }
7962: }
7963: return(NULL);
7964: }
7965: if (cur == ctxt->context->doc->children)
7966: return((xmlNodePtr) ctxt->context->doc);
7967: if (cur == (xmlNodePtr) ctxt->context->doc)
7968: return(NULL);
7969: switch (cur->type) {
7970: case XML_ELEMENT_NODE:
7971: case XML_TEXT_NODE:
7972: case XML_CDATA_SECTION_NODE:
7973: case XML_ENTITY_REF_NODE:
7974: case XML_ENTITY_NODE:
7975: case XML_PI_NODE:
7976: case XML_COMMENT_NODE:
7977: case XML_NOTATION_NODE:
7978: case XML_DTD_NODE:
7979: case XML_ELEMENT_DECL:
7980: case XML_ATTRIBUTE_DECL:
7981: case XML_ENTITY_DECL:
7982: case XML_XINCLUDE_START:
7983: case XML_XINCLUDE_END:
7984: if (cur->parent == NULL)
7985: return(NULL);
7986: if ((cur->parent->type == XML_ELEMENT_NODE) &&
7987: ((cur->parent->name[0] == ' ') ||
7988: (xmlStrEqual(cur->parent->name,
7989: BAD_CAST "fake node libxslt"))))
7990: return(NULL);
7991: return(cur->parent);
7992: case XML_ATTRIBUTE_NODE: {
7993: xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
7994:
7995: return(att->parent);
7996: }
7997: case XML_NAMESPACE_DECL: {
7998: xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
7999:
8000: if ((ns->next != NULL) &&
8001: (ns->next->type != XML_NAMESPACE_DECL))
8002: return((xmlNodePtr) ns->next);
8003: /* Bad, how did that namespace end up here ? */
8004: return(NULL);
8005: }
8006: case XML_DOCUMENT_NODE:
8007: case XML_DOCUMENT_TYPE_NODE:
8008: case XML_DOCUMENT_FRAG_NODE:
8009: case XML_HTML_DOCUMENT_NODE:
8010: #ifdef LIBXML_DOCB_ENABLED
8011: case XML_DOCB_DOCUMENT_NODE:
8012: #endif
8013: return(NULL);
8014: }
8015: return(NULL);
8016: }
8017:
8018: /**
8019: * xmlXPathNextAncestorOrSelf:
8020: * @ctxt: the XPath Parser context
8021: * @cur: the current node in the traversal
8022: *
8023: * Traversal function for the "ancestor-or-self" direction
8024: * he ancestor-or-self axis contains the context node and ancestors of
8025: * the context node in reverse document order; thus the context node is
8026: * the first node on the axis, and the context node's parent the second;
8027: * parent here is defined the same as with the parent axis.
8028: *
8029: * Returns the next element following that axis
8030: */
8031: xmlNodePtr
8032: xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8033: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8034: if (cur == NULL)
8035: return(ctxt->context->node);
8036: return(xmlXPathNextAncestor(ctxt, cur));
8037: }
8038:
8039: /**
8040: * xmlXPathNextFollowingSibling:
8041: * @ctxt: the XPath Parser context
8042: * @cur: the current node in the traversal
8043: *
8044: * Traversal function for the "following-sibling" direction
8045: * The following-sibling axis contains the following siblings of the context
8046: * node in document order.
8047: *
8048: * Returns the next element following that axis
8049: */
8050: xmlNodePtr
8051: xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8052: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8053: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8054: (ctxt->context->node->type == XML_NAMESPACE_DECL))
8055: return(NULL);
8056: if (cur == (xmlNodePtr) ctxt->context->doc)
8057: return(NULL);
8058: if (cur == NULL)
8059: return(ctxt->context->node->next);
8060: return(cur->next);
8061: }
8062:
8063: /**
8064: * xmlXPathNextPrecedingSibling:
8065: * @ctxt: the XPath Parser context
8066: * @cur: the current node in the traversal
8067: *
8068: * Traversal function for the "preceding-sibling" direction
8069: * The preceding-sibling axis contains the preceding siblings of the context
8070: * node in reverse document order; the first preceding sibling is first on the
8071: * axis; the sibling preceding that node is the second on the axis and so on.
8072: *
8073: * Returns the next element following that axis
8074: */
8075: xmlNodePtr
8076: xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8077: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8078: if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
8079: (ctxt->context->node->type == XML_NAMESPACE_DECL))
8080: return(NULL);
8081: if (cur == (xmlNodePtr) ctxt->context->doc)
8082: return(NULL);
8083: if (cur == NULL)
8084: return(ctxt->context->node->prev);
8085: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
8086: cur = cur->prev;
8087: if (cur == NULL)
8088: return(ctxt->context->node->prev);
8089: }
8090: return(cur->prev);
8091: }
8092:
8093: /**
8094: * xmlXPathNextFollowing:
8095: * @ctxt: the XPath Parser context
8096: * @cur: the current node in the traversal
8097: *
8098: * Traversal function for the "following" direction
8099: * The following axis contains all nodes in the same document as the context
8100: * node that are after the context node in document order, excluding any
8101: * descendants and excluding attribute nodes and namespace nodes; the nodes
8102: * are ordered in document order
8103: *
8104: * Returns the next element following that axis
8105: */
8106: xmlNodePtr
8107: xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8108: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8109: if ((cur != NULL) && (cur->type != XML_ATTRIBUTE_NODE) &&
8110: (cur->type != XML_NAMESPACE_DECL) && (cur->children != NULL))
8111: return(cur->children);
8112:
8113: if (cur == NULL) {
8114: cur = ctxt->context->node;
8115: if (cur->type == XML_NAMESPACE_DECL)
8116: return(NULL);
8117: if (cur->type == XML_ATTRIBUTE_NODE)
8118: cur = cur->parent;
8119: }
8120: if (cur == NULL) return(NULL) ; /* ERROR */
8121: if (cur->next != NULL) return(cur->next) ;
8122: do {
8123: cur = cur->parent;
8124: if (cur == NULL) break;
8125: if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
8126: if (cur->next != NULL) return(cur->next);
8127: } while (cur != NULL);
8128: return(cur);
8129: }
8130:
8131: /*
8132: * xmlXPathIsAncestor:
8133: * @ancestor: the ancestor node
8134: * @node: the current node
8135: *
8136: * Check that @ancestor is a @node's ancestor
8137: *
8138: * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
8139: */
8140: static int
8141: xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
8142: if ((ancestor == NULL) || (node == NULL)) return(0);
8143: /* nodes need to be in the same document */
8144: if (ancestor->doc != node->doc) return(0);
8145: /* avoid searching if ancestor or node is the root node */
8146: if (ancestor == (xmlNodePtr) node->doc) return(1);
8147: if (node == (xmlNodePtr) ancestor->doc) return(0);
8148: while (node->parent != NULL) {
8149: if (node->parent == ancestor)
8150: return(1);
8151: node = node->parent;
8152: }
8153: return(0);
8154: }
8155:
8156: /**
8157: * xmlXPathNextPreceding:
8158: * @ctxt: the XPath Parser context
8159: * @cur: the current node in the traversal
8160: *
8161: * Traversal function for the "preceding" direction
8162: * the preceding axis contains all nodes in the same document as the context
8163: * node that are before the context node in document order, excluding any
8164: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8165: * ordered in reverse document order
8166: *
8167: * Returns the next element following that axis
8168: */
8169: xmlNodePtr
8170: xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
8171: {
8172: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8173: if (cur == NULL) {
8174: cur = ctxt->context->node;
8175: if (cur->type == XML_NAMESPACE_DECL)
8176: return(NULL);
8177: if (cur->type == XML_ATTRIBUTE_NODE)
8178: return(cur->parent);
8179: }
8180: if (cur == NULL)
8181: return (NULL);
8182: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8183: cur = cur->prev;
8184: do {
8185: if (cur->prev != NULL) {
8186: for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
8187: return (cur);
8188: }
8189:
8190: cur = cur->parent;
8191: if (cur == NULL)
8192: return (NULL);
8193: if (cur == ctxt->context->doc->children)
8194: return (NULL);
8195: } while (xmlXPathIsAncestor(cur, ctxt->context->node));
8196: return (cur);
8197: }
8198:
8199: /**
8200: * xmlXPathNextPrecedingInternal:
8201: * @ctxt: the XPath Parser context
8202: * @cur: the current node in the traversal
8203: *
8204: * Traversal function for the "preceding" direction
8205: * the preceding axis contains all nodes in the same document as the context
8206: * node that are before the context node in document order, excluding any
8207: * ancestors and excluding attribute nodes and namespace nodes; the nodes are
8208: * ordered in reverse document order
8209: * This is a faster implementation but internal only since it requires a
8210: * state kept in the parser context: ctxt->ancestor.
8211: *
8212: * Returns the next element following that axis
8213: */
8214: static xmlNodePtr
8215: xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
8216: xmlNodePtr cur)
8217: {
8218: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8219: if (cur == NULL) {
8220: cur = ctxt->context->node;
8221: if (cur == NULL)
8222: return (NULL);
8223: if (cur->type == XML_NAMESPACE_DECL)
8224: return (NULL);
8225: ctxt->ancestor = cur->parent;
8226: }
8227: if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
8228: cur = cur->prev;
8229: while (cur->prev == NULL) {
8230: cur = cur->parent;
8231: if (cur == NULL)
8232: return (NULL);
8233: if (cur == ctxt->context->doc->children)
8234: return (NULL);
8235: if (cur != ctxt->ancestor)
8236: return (cur);
8237: ctxt->ancestor = cur->parent;
8238: }
8239: cur = cur->prev;
8240: while (cur->last != NULL)
8241: cur = cur->last;
8242: return (cur);
8243: }
8244:
8245: /**
8246: * xmlXPathNextNamespace:
8247: * @ctxt: the XPath Parser context
8248: * @cur: the current attribute in the traversal
8249: *
8250: * Traversal function for the "namespace" direction
8251: * the namespace axis contains the namespace nodes of the context node;
8252: * the order of nodes on this axis is implementation-defined; the axis will
8253: * be empty unless the context node is an element
8254: *
8255: * We keep the XML namespace node at the end of the list.
8256: *
8257: * Returns the next element following that axis
8258: */
8259: xmlNodePtr
8260: xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8261: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8262: if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
8263: if (ctxt->context->tmpNsList == NULL && cur != (xmlNodePtr) xmlXPathXMLNamespace) {
8264: if (ctxt->context->tmpNsList != NULL)
8265: xmlFree(ctxt->context->tmpNsList);
8266: ctxt->context->tmpNsList =
8267: xmlGetNsList(ctxt->context->doc, ctxt->context->node);
8268: ctxt->context->tmpNsNr = 0;
8269: if (ctxt->context->tmpNsList != NULL) {
8270: while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
8271: ctxt->context->tmpNsNr++;
8272: }
8273: }
8274: return((xmlNodePtr) xmlXPathXMLNamespace);
8275: }
8276: if (ctxt->context->tmpNsNr > 0) {
8277: return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
8278: } else {
8279: if (ctxt->context->tmpNsList != NULL)
8280: xmlFree(ctxt->context->tmpNsList);
8281: ctxt->context->tmpNsList = NULL;
8282: return(NULL);
8283: }
8284: }
8285:
8286: /**
8287: * xmlXPathNextAttribute:
8288: * @ctxt: the XPath Parser context
8289: * @cur: the current attribute in the traversal
8290: *
8291: * Traversal function for the "attribute" direction
8292: * TODO: support DTD inherited default attributes
8293: *
8294: * Returns the next element following that axis
8295: */
8296: xmlNodePtr
8297: xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
8298: if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
8299: if (ctxt->context->node == NULL)
8300: return(NULL);
8301: if (ctxt->context->node->type != XML_ELEMENT_NODE)
8302: return(NULL);
8303: if (cur == NULL) {
8304: if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
8305: return(NULL);
8306: return((xmlNodePtr)ctxt->context->node->properties);
8307: }
8308: return((xmlNodePtr)cur->next);
8309: }
8310:
8311: /************************************************************************
8312: * *
8313: * NodeTest Functions *
8314: * *
8315: ************************************************************************/
8316:
8317: #define IS_FUNCTION 200
8318:
8319:
8320: /************************************************************************
8321: * *
8322: * Implicit tree core function library *
8323: * *
8324: ************************************************************************/
8325:
8326: /**
8327: * xmlXPathRoot:
8328: * @ctxt: the XPath Parser context
8329: *
8330: * Initialize the context to the root of the document
8331: */
8332: void
8333: xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
8334: if ((ctxt == NULL) || (ctxt->context == NULL))
8335: return;
8336: ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
8337: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8338: ctxt->context->node));
8339: }
8340:
8341: /************************************************************************
8342: * *
8343: * The explicit core function library *
8344: *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
8345: * *
8346: ************************************************************************/
8347:
8348:
8349: /**
8350: * xmlXPathLastFunction:
8351: * @ctxt: the XPath Parser context
8352: * @nargs: the number of arguments
8353: *
8354: * Implement the last() XPath function
8355: * number last()
8356: * The last function returns the number of nodes in the context node list.
8357: */
8358: void
8359: xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8360: CHECK_ARITY(0);
8361: if (ctxt->context->contextSize >= 0) {
8362: valuePush(ctxt,
8363: xmlXPathCacheNewFloat(ctxt->context,
8364: (double) ctxt->context->contextSize));
8365: #ifdef DEBUG_EXPR
8366: xmlGenericError(xmlGenericErrorContext,
8367: "last() : %d\n", ctxt->context->contextSize);
8368: #endif
8369: } else {
8370: XP_ERROR(XPATH_INVALID_CTXT_SIZE);
8371: }
8372: }
8373:
8374: /**
8375: * xmlXPathPositionFunction:
8376: * @ctxt: the XPath Parser context
8377: * @nargs: the number of arguments
8378: *
8379: * Implement the position() XPath function
8380: * number position()
8381: * The position function returns the position of the context node in the
8382: * context node list. The first position is 1, and so the last position
8383: * will be equal to last().
8384: */
8385: void
8386: xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8387: CHECK_ARITY(0);
8388: if (ctxt->context->proximityPosition >= 0) {
8389: valuePush(ctxt,
8390: xmlXPathCacheNewFloat(ctxt->context,
8391: (double) ctxt->context->proximityPosition));
8392: #ifdef DEBUG_EXPR
8393: xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
8394: ctxt->context->proximityPosition);
8395: #endif
8396: } else {
8397: XP_ERROR(XPATH_INVALID_CTXT_POSITION);
8398: }
8399: }
8400:
8401: /**
8402: * xmlXPathCountFunction:
8403: * @ctxt: the XPath Parser context
8404: * @nargs: the number of arguments
8405: *
8406: * Implement the count() XPath function
8407: * number count(node-set)
8408: */
8409: void
8410: xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8411: xmlXPathObjectPtr cur;
8412:
8413: CHECK_ARITY(1);
8414: if ((ctxt->value == NULL) ||
8415: ((ctxt->value->type != XPATH_NODESET) &&
8416: (ctxt->value->type != XPATH_XSLT_TREE)))
8417: XP_ERROR(XPATH_INVALID_TYPE);
8418: cur = valuePop(ctxt);
8419:
8420: if ((cur == NULL) || (cur->nodesetval == NULL))
8421: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8422: else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
8423: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8424: (double) cur->nodesetval->nodeNr));
8425: } else {
8426: if ((cur->nodesetval->nodeNr != 1) ||
8427: (cur->nodesetval->nodeTab == NULL)) {
8428: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
8429: } else {
8430: xmlNodePtr tmp;
8431: int i = 0;
8432:
8433: tmp = cur->nodesetval->nodeTab[0];
8434: if (tmp != NULL) {
8435: tmp = tmp->children;
8436: while (tmp != NULL) {
8437: tmp = tmp->next;
8438: i++;
8439: }
8440: }
8441: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i));
8442: }
8443: }
8444: xmlXPathReleaseObject(ctxt->context, cur);
8445: }
8446:
8447: /**
8448: * xmlXPathGetElementsByIds:
8449: * @doc: the document
8450: * @ids: a whitespace separated list of IDs
8451: *
8452: * Selects elements by their unique ID.
8453: *
8454: * Returns a node-set of selected elements.
8455: */
8456: static xmlNodeSetPtr
8457: xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
8458: xmlNodeSetPtr ret;
8459: const xmlChar *cur = ids;
8460: xmlChar *ID;
8461: xmlAttrPtr attr;
8462: xmlNodePtr elem = NULL;
8463:
8464: if (ids == NULL) return(NULL);
8465:
8466: ret = xmlXPathNodeSetCreate(NULL);
8467: if (ret == NULL)
8468: return(ret);
8469:
8470: while (IS_BLANK_CH(*cur)) cur++;
8471: while (*cur != 0) {
8472: while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
8473: cur++;
8474:
8475: ID = xmlStrndup(ids, cur - ids);
8476: if (ID != NULL) {
8477: /*
8478: * We used to check the fact that the value passed
8479: * was an NCName, but this generated much troubles for
8480: * me and Aleksey Sanin, people blatantly violated that
8481: * constaint, like Visa3D spec.
8482: * if (xmlValidateNCName(ID, 1) == 0)
8483: */
8484: attr = xmlGetID(doc, ID);
8485: if (attr != NULL) {
8486: if (attr->type == XML_ATTRIBUTE_NODE)
8487: elem = attr->parent;
8488: else if (attr->type == XML_ELEMENT_NODE)
8489: elem = (xmlNodePtr) attr;
8490: else
8491: elem = NULL;
8492: if (elem != NULL)
8493: xmlXPathNodeSetAdd(ret, elem);
8494: }
8495: xmlFree(ID);
8496: }
8497:
8498: while (IS_BLANK_CH(*cur)) cur++;
8499: ids = cur;
8500: }
8501: return(ret);
8502: }
8503:
8504: /**
8505: * xmlXPathIdFunction:
8506: * @ctxt: the XPath Parser context
8507: * @nargs: the number of arguments
8508: *
8509: * Implement the id() XPath function
8510: * node-set id(object)
8511: * The id function selects elements by their unique ID
8512: * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
8513: * then the result is the union of the result of applying id to the
8514: * string value of each of the nodes in the argument node-set. When the
8515: * argument to id is of any other type, the argument is converted to a
8516: * string as if by a call to the string function; the string is split
8517: * into a whitespace-separated list of tokens (whitespace is any sequence
8518: * of characters matching the production S); the result is a node-set
8519: * containing the elements in the same document as the context node that
8520: * have a unique ID equal to any of the tokens in the list.
8521: */
8522: void
8523: xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8524: xmlChar *tokens;
8525: xmlNodeSetPtr ret;
8526: xmlXPathObjectPtr obj;
8527:
8528: CHECK_ARITY(1);
8529: obj = valuePop(ctxt);
8530: if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8531: if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
8532: xmlNodeSetPtr ns;
8533: int i;
8534:
8535: ret = xmlXPathNodeSetCreate(NULL);
8536: /*
8537: * FIXME -- in an out-of-memory condition this will behave badly.
8538: * The solution is not clear -- we already popped an item from
8539: * ctxt, so the object is in a corrupt state.
8540: */
8541:
8542: if (obj->nodesetval != NULL) {
8543: for (i = 0; i < obj->nodesetval->nodeNr; i++) {
8544: tokens =
8545: xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
8546: ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
8547: ret = xmlXPathNodeSetMerge(ret, ns);
8548: xmlXPathFreeNodeSet(ns);
8549: if (tokens != NULL)
8550: xmlFree(tokens);
8551: }
8552: }
8553: xmlXPathReleaseObject(ctxt->context, obj);
8554: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8555: return;
8556: }
8557: obj = xmlXPathCacheConvertString(ctxt->context, obj);
8558: ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
8559: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
8560: xmlXPathReleaseObject(ctxt->context, obj);
8561: return;
8562: }
8563:
8564: /**
8565: * xmlXPathLocalNameFunction:
8566: * @ctxt: the XPath Parser context
8567: * @nargs: the number of arguments
8568: *
8569: * Implement the local-name() XPath function
8570: * string local-name(node-set?)
8571: * The local-name function returns a string containing the local part
8572: * of the name of the node in the argument node-set that is first in
8573: * document order. If the node-set is empty or the first node has no
8574: * name, an empty string is returned. If the argument is omitted it
8575: * defaults to the context node.
8576: */
8577: void
8578: xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8579: xmlXPathObjectPtr cur;
8580:
8581: if (ctxt == NULL) return;
8582:
8583: if (nargs == 0) {
8584: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8585: ctxt->context->node));
8586: nargs = 1;
8587: }
8588:
8589: CHECK_ARITY(1);
8590: if ((ctxt->value == NULL) ||
8591: ((ctxt->value->type != XPATH_NODESET) &&
8592: (ctxt->value->type != XPATH_XSLT_TREE)))
8593: XP_ERROR(XPATH_INVALID_TYPE);
8594: cur = valuePop(ctxt);
8595:
8596: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8597: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8598: } else {
8599: int i = 0; /* Should be first in document order !!!!! */
8600: switch (cur->nodesetval->nodeTab[i]->type) {
8601: case XML_ELEMENT_NODE:
8602: case XML_ATTRIBUTE_NODE:
8603: case XML_PI_NODE:
8604: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8605: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8606: else
8607: valuePush(ctxt,
8608: xmlXPathCacheNewString(ctxt->context,
8609: cur->nodesetval->nodeTab[i]->name));
8610: break;
8611: case XML_NAMESPACE_DECL:
8612: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8613: ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
8614: break;
8615: default:
8616: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8617: }
8618: }
8619: xmlXPathReleaseObject(ctxt->context, cur);
8620: }
8621:
8622: /**
8623: * xmlXPathNamespaceURIFunction:
8624: * @ctxt: the XPath Parser context
8625: * @nargs: the number of arguments
8626: *
8627: * Implement the namespace-uri() XPath function
8628: * string namespace-uri(node-set?)
8629: * The namespace-uri function returns a string containing the
8630: * namespace URI of the expanded name of the node in the argument
8631: * node-set that is first in document order. If the node-set is empty,
8632: * the first node has no name, or the expanded name has no namespace
8633: * URI, an empty string is returned. If the argument is omitted it
8634: * defaults to the context node.
8635: */
8636: void
8637: xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8638: xmlXPathObjectPtr cur;
8639:
8640: if (ctxt == NULL) return;
8641:
8642: if (nargs == 0) {
8643: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8644: ctxt->context->node));
8645: nargs = 1;
8646: }
8647: CHECK_ARITY(1);
8648: if ((ctxt->value == NULL) ||
8649: ((ctxt->value->type != XPATH_NODESET) &&
8650: (ctxt->value->type != XPATH_XSLT_TREE)))
8651: XP_ERROR(XPATH_INVALID_TYPE);
8652: cur = valuePop(ctxt);
8653:
8654: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8655: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8656: } else {
8657: int i = 0; /* Should be first in document order !!!!! */
8658: switch (cur->nodesetval->nodeTab[i]->type) {
8659: case XML_ELEMENT_NODE:
8660: case XML_ATTRIBUTE_NODE:
8661: if (cur->nodesetval->nodeTab[i]->ns == NULL)
8662: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8663: else
8664: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
8665: cur->nodesetval->nodeTab[i]->ns->href));
8666: break;
8667: default:
8668: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8669: }
8670: }
8671: xmlXPathReleaseObject(ctxt->context, cur);
8672: }
8673:
8674: /**
8675: * xmlXPathNameFunction:
8676: * @ctxt: the XPath Parser context
8677: * @nargs: the number of arguments
8678: *
8679: * Implement the name() XPath function
8680: * string name(node-set?)
8681: * The name function returns a string containing a QName representing
8682: * the name of the node in the argument node-set that is first in document
8683: * order. The QName must represent the name with respect to the namespace
8684: * declarations in effect on the node whose name is being represented.
8685: * Typically, this will be the form in which the name occurred in the XML
8686: * source. This need not be the case if there are namespace declarations
8687: * in effect on the node that associate multiple prefixes with the same
8688: * namespace. However, an implementation may include information about
8689: * the original prefix in its representation of nodes; in this case, an
8690: * implementation can ensure that the returned string is always the same
8691: * as the QName used in the XML source. If the argument it omitted it
8692: * defaults to the context node.
8693: * Libxml keep the original prefix so the "real qualified name" used is
8694: * returned.
8695: */
8696: static void
8697: xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
8698: {
8699: xmlXPathObjectPtr cur;
8700:
8701: if (nargs == 0) {
8702: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8703: ctxt->context->node));
8704: nargs = 1;
8705: }
8706:
8707: CHECK_ARITY(1);
8708: if ((ctxt->value == NULL) ||
8709: ((ctxt->value->type != XPATH_NODESET) &&
8710: (ctxt->value->type != XPATH_XSLT_TREE)))
8711: XP_ERROR(XPATH_INVALID_TYPE);
8712: cur = valuePop(ctxt);
8713:
8714: if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
8715: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
8716: } else {
8717: int i = 0; /* Should be first in document order !!!!! */
8718:
8719: switch (cur->nodesetval->nodeTab[i]->type) {
8720: case XML_ELEMENT_NODE:
8721: case XML_ATTRIBUTE_NODE:
8722: if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
8723: valuePush(ctxt,
8724: xmlXPathCacheNewCString(ctxt->context, ""));
8725: else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
8726: (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
8727: valuePush(ctxt,
8728: xmlXPathCacheNewString(ctxt->context,
8729: cur->nodesetval->nodeTab[i]->name));
8730: } else {
8731: xmlChar *fullname;
8732:
8733: fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
8734: cur->nodesetval->nodeTab[i]->ns->prefix,
8735: NULL, 0);
8736: if (fullname == cur->nodesetval->nodeTab[i]->name)
8737: fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
8738: if (fullname == NULL) {
8739: XP_ERROR(XPATH_MEMORY_ERROR);
8740: }
8741: valuePush(ctxt, xmlXPathCacheWrapString(
8742: ctxt->context, fullname));
8743: }
8744: break;
8745: default:
8746: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
8747: cur->nodesetval->nodeTab[i]));
8748: xmlXPathLocalNameFunction(ctxt, 1);
8749: }
8750: }
8751: xmlXPathReleaseObject(ctxt->context, cur);
8752: }
8753:
8754:
8755: /**
8756: * xmlXPathStringFunction:
8757: * @ctxt: the XPath Parser context
8758: * @nargs: the number of arguments
8759: *
8760: * Implement the string() XPath function
8761: * string string(object?)
8762: * The string function converts an object to a string as follows:
8763: * - A node-set is converted to a string by returning the value of
8764: * the node in the node-set that is first in document order.
8765: * If the node-set is empty, an empty string is returned.
8766: * - A number is converted to a string as follows
8767: * + NaN is converted to the string NaN
8768: * + positive zero is converted to the string 0
8769: * + negative zero is converted to the string 0
8770: * + positive infinity is converted to the string Infinity
8771: * + negative infinity is converted to the string -Infinity
8772: * + if the number is an integer, the number is represented in
8773: * decimal form as a Number with no decimal point and no leading
8774: * zeros, preceded by a minus sign (-) if the number is negative
8775: * + otherwise, the number is represented in decimal form as a
8776: * Number including a decimal point with at least one digit
8777: * before the decimal point and at least one digit after the
8778: * decimal point, preceded by a minus sign (-) if the number
8779: * is negative; there must be no leading zeros before the decimal
8780: * point apart possibly from the one required digit immediately
8781: * before the decimal point; beyond the one required digit
8782: * after the decimal point there must be as many, but only as
8783: * many, more digits as are needed to uniquely distinguish the
8784: * number from all other IEEE 754 numeric values.
8785: * - The boolean false value is converted to the string false.
8786: * The boolean true value is converted to the string true.
8787: *
8788: * If the argument is omitted, it defaults to a node-set with the
8789: * context node as its only member.
8790: */
8791: void
8792: xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8793: xmlXPathObjectPtr cur;
8794:
8795: if (ctxt == NULL) return;
8796: if (nargs == 0) {
8797: valuePush(ctxt,
8798: xmlXPathCacheWrapString(ctxt->context,
8799: xmlXPathCastNodeToString(ctxt->context->node)));
8800: return;
8801: }
8802:
8803: CHECK_ARITY(1);
8804: cur = valuePop(ctxt);
8805: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
8806: valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
8807: }
8808:
8809: /**
8810: * xmlXPathStringLengthFunction:
8811: * @ctxt: the XPath Parser context
8812: * @nargs: the number of arguments
8813: *
8814: * Implement the string-length() XPath function
8815: * number string-length(string?)
8816: * The string-length returns the number of characters in the string
8817: * (see [3.6 Strings]). If the argument is omitted, it defaults to
8818: * the context node converted to a string, in other words the value
8819: * of the context node.
8820: */
8821: void
8822: xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8823: xmlXPathObjectPtr cur;
8824:
8825: if (nargs == 0) {
8826: if ((ctxt == NULL) || (ctxt->context == NULL))
8827: return;
8828: if (ctxt->context->node == NULL) {
8829: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
8830: } else {
8831: xmlChar *content;
8832:
8833: content = xmlXPathCastNodeToString(ctxt->context->node);
8834: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8835: xmlUTF8Strlen(content)));
8836: xmlFree(content);
8837: }
8838: return;
8839: }
8840: CHECK_ARITY(1);
8841: CAST_TO_STRING;
8842: CHECK_TYPE(XPATH_STRING);
8843: cur = valuePop(ctxt);
8844: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
8845: xmlUTF8Strlen(cur->stringval)));
8846: xmlXPathReleaseObject(ctxt->context, cur);
8847: }
8848:
8849: /**
8850: * xmlXPathConcatFunction:
8851: * @ctxt: the XPath Parser context
8852: * @nargs: the number of arguments
8853: *
8854: * Implement the concat() XPath function
8855: * string concat(string, string, string*)
8856: * The concat function returns the concatenation of its arguments.
8857: */
8858: void
8859: xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8860: xmlXPathObjectPtr cur, newobj;
8861: xmlChar *tmp;
8862:
8863: if (ctxt == NULL) return;
8864: if (nargs < 2) {
8865: CHECK_ARITY(2);
8866: }
8867:
8868: CAST_TO_STRING;
8869: cur = valuePop(ctxt);
8870: if ((cur == NULL) || (cur->type != XPATH_STRING)) {
8871: xmlXPathReleaseObject(ctxt->context, cur);
8872: return;
8873: }
8874: nargs--;
8875:
8876: while (nargs > 0) {
8877: CAST_TO_STRING;
8878: newobj = valuePop(ctxt);
8879: if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
8880: xmlXPathReleaseObject(ctxt->context, newobj);
8881: xmlXPathReleaseObject(ctxt->context, cur);
8882: XP_ERROR(XPATH_INVALID_TYPE);
8883: }
8884: tmp = xmlStrcat(newobj->stringval, cur->stringval);
8885: newobj->stringval = cur->stringval;
8886: cur->stringval = tmp;
8887: xmlXPathReleaseObject(ctxt->context, newobj);
8888: nargs--;
8889: }
8890: valuePush(ctxt, cur);
8891: }
8892:
8893: /**
8894: * xmlXPathContainsFunction:
8895: * @ctxt: the XPath Parser context
8896: * @nargs: the number of arguments
8897: *
8898: * Implement the contains() XPath function
8899: * boolean contains(string, string)
8900: * The contains function returns true if the first argument string
8901: * contains the second argument string, and otherwise returns false.
8902: */
8903: void
8904: xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8905: xmlXPathObjectPtr hay, needle;
8906:
8907: CHECK_ARITY(2);
8908: CAST_TO_STRING;
8909: CHECK_TYPE(XPATH_STRING);
8910: needle = valuePop(ctxt);
8911: CAST_TO_STRING;
8912: hay = valuePop(ctxt);
8913:
8914: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
8915: xmlXPathReleaseObject(ctxt->context, hay);
8916: xmlXPathReleaseObject(ctxt->context, needle);
8917: XP_ERROR(XPATH_INVALID_TYPE);
8918: }
8919: if (xmlStrstr(hay->stringval, needle->stringval))
8920: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
8921: else
8922: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
8923: xmlXPathReleaseObject(ctxt->context, hay);
8924: xmlXPathReleaseObject(ctxt->context, needle);
8925: }
8926:
8927: /**
8928: * xmlXPathStartsWithFunction:
8929: * @ctxt: the XPath Parser context
8930: * @nargs: the number of arguments
8931: *
8932: * Implement the starts-with() XPath function
8933: * boolean starts-with(string, string)
8934: * The starts-with function returns true if the first argument string
8935: * starts with the second argument string, and otherwise returns false.
8936: */
8937: void
8938: xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8939: xmlXPathObjectPtr hay, needle;
8940: int n;
8941:
8942: CHECK_ARITY(2);
8943: CAST_TO_STRING;
8944: CHECK_TYPE(XPATH_STRING);
8945: needle = valuePop(ctxt);
8946: CAST_TO_STRING;
8947: hay = valuePop(ctxt);
8948:
8949: if ((hay == NULL) || (hay->type != XPATH_STRING)) {
8950: xmlXPathReleaseObject(ctxt->context, hay);
8951: xmlXPathReleaseObject(ctxt->context, needle);
8952: XP_ERROR(XPATH_INVALID_TYPE);
8953: }
8954: n = xmlStrlen(needle->stringval);
8955: if (xmlStrncmp(hay->stringval, needle->stringval, n))
8956: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
8957: else
8958: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
8959: xmlXPathReleaseObject(ctxt->context, hay);
8960: xmlXPathReleaseObject(ctxt->context, needle);
8961: }
8962:
8963: /**
8964: * xmlXPathSubstringFunction:
8965: * @ctxt: the XPath Parser context
8966: * @nargs: the number of arguments
8967: *
8968: * Implement the substring() XPath function
8969: * string substring(string, number, number?)
8970: * The substring function returns the substring of the first argument
8971: * starting at the position specified in the second argument with
8972: * length specified in the third argument. For example,
8973: * substring("12345",2,3) returns "234". If the third argument is not
8974: * specified, it returns the substring starting at the position specified
8975: * in the second argument and continuing to the end of the string. For
8976: * example, substring("12345",2) returns "2345". More precisely, each
8977: * character in the string (see [3.6 Strings]) is considered to have a
8978: * numeric position: the position of the first character is 1, the position
8979: * of the second character is 2 and so on. The returned substring contains
8980: * those characters for which the position of the character is greater than
8981: * or equal to the second argument and, if the third argument is specified,
8982: * less than the sum of the second and third arguments; the comparisons
8983: * and addition used for the above follow the standard IEEE 754 rules. Thus:
8984: * - substring("12345", 1.5, 2.6) returns "234"
8985: * - substring("12345", 0, 3) returns "12"
8986: * - substring("12345", 0 div 0, 3) returns ""
8987: * - substring("12345", 1, 0 div 0) returns ""
8988: * - substring("12345", -42, 1 div 0) returns "12345"
8989: * - substring("12345", -1 div 0, 1 div 0) returns ""
8990: */
8991: void
8992: xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
8993: xmlXPathObjectPtr str, start, len;
8994: double le=0, in;
8995: int i, l, m;
8996: xmlChar *ret;
8997:
8998: if (nargs < 2) {
8999: CHECK_ARITY(2);
9000: }
9001: if (nargs > 3) {
9002: CHECK_ARITY(3);
9003: }
9004: /*
9005: * take care of possible last (position) argument
9006: */
9007: if (nargs == 3) {
9008: CAST_TO_NUMBER;
9009: CHECK_TYPE(XPATH_NUMBER);
9010: len = valuePop(ctxt);
9011: le = len->floatval;
9012: xmlXPathReleaseObject(ctxt->context, len);
9013: }
9014:
9015: CAST_TO_NUMBER;
9016: CHECK_TYPE(XPATH_NUMBER);
9017: start = valuePop(ctxt);
9018: in = start->floatval;
9019: xmlXPathReleaseObject(ctxt->context, start);
9020: CAST_TO_STRING;
9021: CHECK_TYPE(XPATH_STRING);
9022: str = valuePop(ctxt);
9023: m = xmlUTF8Strlen((const unsigned char *)str->stringval);
9024:
9025: /*
9026: * If last pos not present, calculate last position
9027: */
9028: if (nargs != 3) {
9029: le = (double)m;
9030: if (in < 1.0)
9031: in = 1.0;
9032: }
9033:
9034: /* Need to check for the special cases where either
9035: * the index is NaN, the length is NaN, or both
9036: * arguments are infinity (relying on Inf + -Inf = NaN)
9037: */
9038: if (!xmlXPathIsInf(in) && !xmlXPathIsNaN(in + le)) {
9039: /*
9040: * To meet the requirements of the spec, the arguments
9041: * must be converted to integer format before
9042: * initial index calculations are done
9043: *
9044: * First we go to integer form, rounding up
9045: * and checking for special cases
9046: */
9047: i = (int) in;
9048: if (((double)i)+0.5 <= in) i++;
9049:
9050: if (xmlXPathIsInf(le) == 1) {
9051: l = m;
9052: if (i < 1)
9053: i = 1;
9054: }
9055: else if (xmlXPathIsInf(le) == -1 || le < 0.0)
9056: l = 0;
9057: else {
9058: l = (int) le;
9059: if (((double)l)+0.5 <= le) l++;
9060: }
9061:
9062: /* Now we normalize inidices */
9063: i -= 1;
9064: l += i;
9065: if (i < 0)
9066: i = 0;
9067: if (l > m)
9068: l = m;
9069:
9070: /* number of chars to copy */
9071: l -= i;
9072:
9073: ret = xmlUTF8Strsub(str->stringval, i, l);
9074: }
9075: else {
9076: ret = NULL;
9077: }
9078: if (ret == NULL)
9079: valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
9080: else {
9081: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
9082: xmlFree(ret);
9083: }
9084: xmlXPathReleaseObject(ctxt->context, str);
9085: }
9086:
9087: /**
9088: * xmlXPathSubstringBeforeFunction:
9089: * @ctxt: the XPath Parser context
9090: * @nargs: the number of arguments
9091: *
9092: * Implement the substring-before() XPath function
9093: * string substring-before(string, string)
9094: * The substring-before function returns the substring of the first
9095: * argument string that precedes the first occurrence of the second
9096: * argument string in the first argument string, or the empty string
9097: * if the first argument string does not contain the second argument
9098: * string. For example, substring-before("1999/04/01","/") returns 1999.
9099: */
9100: void
9101: xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9102: xmlXPathObjectPtr str;
9103: xmlXPathObjectPtr find;
9104: xmlBufferPtr target;
9105: const xmlChar *point;
9106: int offset;
9107:
9108: CHECK_ARITY(2);
9109: CAST_TO_STRING;
9110: find = valuePop(ctxt);
9111: CAST_TO_STRING;
9112: str = valuePop(ctxt);
9113:
9114: target = xmlBufferCreate();
9115: if (target) {
9116: point = xmlStrstr(str->stringval, find->stringval);
9117: if (point) {
9118: offset = (int)(point - str->stringval);
9119: xmlBufferAdd(target, str->stringval, offset);
9120: }
9121: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9122: xmlBufferContent(target)));
9123: xmlBufferFree(target);
9124: }
9125: xmlXPathReleaseObject(ctxt->context, str);
9126: xmlXPathReleaseObject(ctxt->context, find);
9127: }
9128:
9129: /**
9130: * xmlXPathSubstringAfterFunction:
9131: * @ctxt: the XPath Parser context
9132: * @nargs: the number of arguments
9133: *
9134: * Implement the substring-after() XPath function
9135: * string substring-after(string, string)
9136: * The substring-after function returns the substring of the first
9137: * argument string that follows the first occurrence of the second
9138: * argument string in the first argument string, or the empty stringi
9139: * if the first argument string does not contain the second argument
9140: * string. For example, substring-after("1999/04/01","/") returns 04/01,
9141: * and substring-after("1999/04/01","19") returns 99/04/01.
9142: */
9143: void
9144: xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9145: xmlXPathObjectPtr str;
9146: xmlXPathObjectPtr find;
9147: xmlBufferPtr target;
9148: const xmlChar *point;
9149: int offset;
9150:
9151: CHECK_ARITY(2);
9152: CAST_TO_STRING;
9153: find = valuePop(ctxt);
9154: CAST_TO_STRING;
9155: str = valuePop(ctxt);
9156:
9157: target = xmlBufferCreate();
9158: if (target) {
9159: point = xmlStrstr(str->stringval, find->stringval);
9160: if (point) {
9161: offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
9162: xmlBufferAdd(target, &str->stringval[offset],
9163: xmlStrlen(str->stringval) - offset);
9164: }
9165: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9166: xmlBufferContent(target)));
9167: xmlBufferFree(target);
9168: }
9169: xmlXPathReleaseObject(ctxt->context, str);
9170: xmlXPathReleaseObject(ctxt->context, find);
9171: }
9172:
9173: /**
9174: * xmlXPathNormalizeFunction:
9175: * @ctxt: the XPath Parser context
9176: * @nargs: the number of arguments
9177: *
9178: * Implement the normalize-space() XPath function
9179: * string normalize-space(string?)
9180: * The normalize-space function returns the argument string with white
9181: * space normalized by stripping leading and trailing whitespace
9182: * and replacing sequences of whitespace characters by a single
9183: * space. Whitespace characters are the same allowed by the S production
9184: * in XML. If the argument is omitted, it defaults to the context
9185: * node converted to a string, in other words the value of the context node.
9186: */
9187: void
9188: xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9189: xmlXPathObjectPtr obj = NULL;
9190: xmlChar *source = NULL;
9191: xmlBufferPtr target;
9192: xmlChar blank;
9193:
9194: if (ctxt == NULL) return;
9195: if (nargs == 0) {
9196: /* Use current context node */
9197: valuePush(ctxt,
9198: xmlXPathCacheWrapString(ctxt->context,
9199: xmlXPathCastNodeToString(ctxt->context->node)));
9200: nargs = 1;
9201: }
9202:
9203: CHECK_ARITY(1);
9204: CAST_TO_STRING;
9205: CHECK_TYPE(XPATH_STRING);
9206: obj = valuePop(ctxt);
9207: source = obj->stringval;
9208:
9209: target = xmlBufferCreate();
9210: if (target && source) {
9211:
9212: /* Skip leading whitespaces */
9213: while (IS_BLANK_CH(*source))
9214: source++;
9215:
9216: /* Collapse intermediate whitespaces, and skip trailing whitespaces */
9217: blank = 0;
9218: while (*source) {
9219: if (IS_BLANK_CH(*source)) {
9220: blank = 0x20;
9221: } else {
9222: if (blank) {
9223: xmlBufferAdd(target, &blank, 1);
9224: blank = 0;
9225: }
9226: xmlBufferAdd(target, source, 1);
9227: }
9228: source++;
9229: }
9230: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9231: xmlBufferContent(target)));
9232: xmlBufferFree(target);
9233: }
9234: xmlXPathReleaseObject(ctxt->context, obj);
9235: }
9236:
9237: /**
9238: * xmlXPathTranslateFunction:
9239: * @ctxt: the XPath Parser context
9240: * @nargs: the number of arguments
9241: *
9242: * Implement the translate() XPath function
9243: * string translate(string, string, string)
9244: * The translate function returns the first argument string with
9245: * occurrences of characters in the second argument string replaced
9246: * by the character at the corresponding position in the third argument
9247: * string. For example, translate("bar","abc","ABC") returns the string
9248: * BAr. If there is a character in the second argument string with no
9249: * character at a corresponding position in the third argument string
9250: * (because the second argument string is longer than the third argument
9251: * string), then occurrences of that character in the first argument
9252: * string are removed. For example, translate("--aaa--","abc-","ABC")
9253: * returns "AAA". If a character occurs more than once in second
9254: * argument string, then the first occurrence determines the replacement
9255: * character. If the third argument string is longer than the second
9256: * argument string, then excess characters are ignored.
9257: */
9258: void
9259: xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9260: xmlXPathObjectPtr str;
9261: xmlXPathObjectPtr from;
9262: xmlXPathObjectPtr to;
9263: xmlBufferPtr target;
9264: int offset, max;
9265: xmlChar ch;
9266: const xmlChar *point;
9267: xmlChar *cptr;
9268:
9269: CHECK_ARITY(3);
9270:
9271: CAST_TO_STRING;
9272: to = valuePop(ctxt);
9273: CAST_TO_STRING;
9274: from = valuePop(ctxt);
9275: CAST_TO_STRING;
9276: str = valuePop(ctxt);
9277:
9278: target = xmlBufferCreate();
9279: if (target) {
9280: max = xmlUTF8Strlen(to->stringval);
9281: for (cptr = str->stringval; (ch=*cptr); ) {
9282: offset = xmlUTF8Strloc(from->stringval, cptr);
9283: if (offset >= 0) {
9284: if (offset < max) {
9285: point = xmlUTF8Strpos(to->stringval, offset);
9286: if (point)
9287: xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
9288: }
9289: } else
9290: xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
9291:
9292: /* Step to next character in input */
9293: cptr++;
9294: if ( ch & 0x80 ) {
9295: /* if not simple ascii, verify proper format */
9296: if ( (ch & 0xc0) != 0xc0 ) {
9297: xmlGenericError(xmlGenericErrorContext,
9298: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9299: break;
9300: }
9301: /* then skip over remaining bytes for this char */
9302: while ( (ch <<= 1) & 0x80 )
9303: if ( (*cptr++ & 0xc0) != 0x80 ) {
9304: xmlGenericError(xmlGenericErrorContext,
9305: "xmlXPathTranslateFunction: Invalid UTF8 string\n");
9306: break;
9307: }
9308: if (ch & 0x80) /* must have had error encountered */
9309: break;
9310: }
9311: }
9312: }
9313: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
9314: xmlBufferContent(target)));
9315: xmlBufferFree(target);
9316: xmlXPathReleaseObject(ctxt->context, str);
9317: xmlXPathReleaseObject(ctxt->context, from);
9318: xmlXPathReleaseObject(ctxt->context, to);
9319: }
9320:
9321: /**
9322: * xmlXPathBooleanFunction:
9323: * @ctxt: the XPath Parser context
9324: * @nargs: the number of arguments
9325: *
9326: * Implement the boolean() XPath function
9327: * boolean boolean(object)
9328: * The boolean function converts its argument to a boolean as follows:
9329: * - a number is true if and only if it is neither positive or
9330: * negative zero nor NaN
9331: * - a node-set is true if and only if it is non-empty
9332: * - a string is true if and only if its length is non-zero
9333: */
9334: void
9335: xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9336: xmlXPathObjectPtr cur;
9337:
9338: CHECK_ARITY(1);
9339: cur = valuePop(ctxt);
9340: if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
9341: cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
9342: valuePush(ctxt, cur);
9343: }
9344:
9345: /**
9346: * xmlXPathNotFunction:
9347: * @ctxt: the XPath Parser context
9348: * @nargs: the number of arguments
9349: *
9350: * Implement the not() XPath function
9351: * boolean not(boolean)
9352: * The not function returns true if its argument is false,
9353: * and false otherwise.
9354: */
9355: void
9356: xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9357: CHECK_ARITY(1);
9358: CAST_TO_BOOLEAN;
9359: CHECK_TYPE(XPATH_BOOLEAN);
9360: ctxt->value->boolval = ! ctxt->value->boolval;
9361: }
9362:
9363: /**
9364: * xmlXPathTrueFunction:
9365: * @ctxt: the XPath Parser context
9366: * @nargs: the number of arguments
9367: *
9368: * Implement the true() XPath function
9369: * boolean true()
9370: */
9371: void
9372: xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9373: CHECK_ARITY(0);
9374: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
9375: }
9376:
9377: /**
9378: * xmlXPathFalseFunction:
9379: * @ctxt: the XPath Parser context
9380: * @nargs: the number of arguments
9381: *
9382: * Implement the false() XPath function
9383: * boolean false()
9384: */
9385: void
9386: xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9387: CHECK_ARITY(0);
9388: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
9389: }
9390:
9391: /**
9392: * xmlXPathLangFunction:
9393: * @ctxt: the XPath Parser context
9394: * @nargs: the number of arguments
9395: *
9396: * Implement the lang() XPath function
9397: * boolean lang(string)
9398: * The lang function returns true or false depending on whether the
9399: * language of the context node as specified by xml:lang attributes
9400: * is the same as or is a sublanguage of the language specified by
9401: * the argument string. The language of the context node is determined
9402: * by the value of the xml:lang attribute on the context node, or, if
9403: * the context node has no xml:lang attribute, by the value of the
9404: * xml:lang attribute on the nearest ancestor of the context node that
9405: * has an xml:lang attribute. If there is no such attribute, then lang
9406: * returns false. If there is such an attribute, then lang returns
9407: * true if the attribute value is equal to the argument ignoring case,
9408: * or if there is some suffix starting with - such that the attribute
9409: * value is equal to the argument ignoring that suffix of the attribute
9410: * value and ignoring case.
9411: */
9412: void
9413: xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9414: xmlXPathObjectPtr val = NULL;
9415: const xmlChar *theLang = NULL;
9416: const xmlChar *lang;
9417: int ret = 0;
9418: int i;
9419:
9420: CHECK_ARITY(1);
9421: CAST_TO_STRING;
9422: CHECK_TYPE(XPATH_STRING);
9423: val = valuePop(ctxt);
9424: lang = val->stringval;
9425: theLang = xmlNodeGetLang(ctxt->context->node);
9426: if ((theLang != NULL) && (lang != NULL)) {
9427: for (i = 0;lang[i] != 0;i++)
9428: if (toupper(lang[i]) != toupper(theLang[i]))
9429: goto not_equal;
9430: if ((theLang[i] == 0) || (theLang[i] == '-'))
9431: ret = 1;
9432: }
9433: not_equal:
9434: if (theLang != NULL)
9435: xmlFree((void *)theLang);
9436:
9437: xmlXPathReleaseObject(ctxt->context, val);
9438: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
9439: }
9440:
9441: /**
9442: * xmlXPathNumberFunction:
9443: * @ctxt: the XPath Parser context
9444: * @nargs: the number of arguments
9445: *
9446: * Implement the number() XPath function
9447: * number number(object?)
9448: */
9449: void
9450: xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9451: xmlXPathObjectPtr cur;
9452: double res;
9453:
9454: if (ctxt == NULL) return;
9455: if (nargs == 0) {
9456: if (ctxt->context->node == NULL) {
9457: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
9458: } else {
9459: xmlChar* content = xmlNodeGetContent(ctxt->context->node);
9460:
9461: res = xmlXPathStringEvalNumber(content);
9462: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9463: xmlFree(content);
9464: }
9465: return;
9466: }
9467:
9468: CHECK_ARITY(1);
9469: cur = valuePop(ctxt);
9470: valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
9471: }
9472:
9473: /**
9474: * xmlXPathSumFunction:
9475: * @ctxt: the XPath Parser context
9476: * @nargs: the number of arguments
9477: *
9478: * Implement the sum() XPath function
9479: * number sum(node-set)
9480: * The sum function returns the sum of the values of the nodes in
9481: * the argument node-set.
9482: */
9483: void
9484: xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9485: xmlXPathObjectPtr cur;
9486: int i;
9487: double res = 0.0;
9488:
9489: CHECK_ARITY(1);
9490: if ((ctxt->value == NULL) ||
9491: ((ctxt->value->type != XPATH_NODESET) &&
9492: (ctxt->value->type != XPATH_XSLT_TREE)))
9493: XP_ERROR(XPATH_INVALID_TYPE);
9494: cur = valuePop(ctxt);
9495:
9496: if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
9497: for (i = 0; i < cur->nodesetval->nodeNr; i++) {
9498: res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
9499: }
9500: }
9501: valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
9502: xmlXPathReleaseObject(ctxt->context, cur);
9503: }
9504:
9505: /*
9506: * To assure working code on multiple platforms, we want to only depend
9507: * upon the characteristic truncation of converting a floating point value
9508: * to an integer. Unfortunately, because of the different storage sizes
9509: * of our internal floating point value (double) and integer (int), we
9510: * can't directly convert (see bug 301162). This macro is a messy
9511: * 'workaround'
9512: */
9513: #define XTRUNC(f, v) \
9514: f = fmod((v), INT_MAX); \
9515: f = (v) - (f) + (double)((int)(f));
9516:
9517: /**
9518: * xmlXPathFloorFunction:
9519: * @ctxt: the XPath Parser context
9520: * @nargs: the number of arguments
9521: *
9522: * Implement the floor() XPath function
9523: * number floor(number)
9524: * The floor function returns the largest (closest to positive infinity)
9525: * number that is not greater than the argument and that is an integer.
9526: */
9527: void
9528: xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9529: double f;
9530:
9531: CHECK_ARITY(1);
9532: CAST_TO_NUMBER;
9533: CHECK_TYPE(XPATH_NUMBER);
9534:
9535: XTRUNC(f, ctxt->value->floatval);
9536: if (f != ctxt->value->floatval) {
9537: if (ctxt->value->floatval > 0)
9538: ctxt->value->floatval = f;
9539: else
9540: ctxt->value->floatval = f - 1;
9541: }
9542: }
9543:
9544: /**
9545: * xmlXPathCeilingFunction:
9546: * @ctxt: the XPath Parser context
9547: * @nargs: the number of arguments
9548: *
9549: * Implement the ceiling() XPath function
9550: * number ceiling(number)
9551: * The ceiling function returns the smallest (closest to negative infinity)
9552: * number that is not less than the argument and that is an integer.
9553: */
9554: void
9555: xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9556: double f;
9557:
9558: CHECK_ARITY(1);
9559: CAST_TO_NUMBER;
9560: CHECK_TYPE(XPATH_NUMBER);
9561:
9562: #if 0
9563: ctxt->value->floatval = ceil(ctxt->value->floatval);
9564: #else
9565: XTRUNC(f, ctxt->value->floatval);
9566: if (f != ctxt->value->floatval) {
9567: if (ctxt->value->floatval > 0)
9568: ctxt->value->floatval = f + 1;
9569: else {
9570: if (ctxt->value->floatval < 0 && f == 0)
9571: ctxt->value->floatval = xmlXPathNZERO;
9572: else
9573: ctxt->value->floatval = f;
9574: }
9575:
9576: }
9577: #endif
9578: }
9579:
9580: /**
9581: * xmlXPathRoundFunction:
9582: * @ctxt: the XPath Parser context
9583: * @nargs: the number of arguments
9584: *
9585: * Implement the round() XPath function
9586: * number round(number)
9587: * The round function returns the number that is closest to the
9588: * argument and that is an integer. If there are two such numbers,
9589: * then the one that is even is returned.
9590: */
9591: void
9592: xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
9593: double f;
9594:
9595: CHECK_ARITY(1);
9596: CAST_TO_NUMBER;
9597: CHECK_TYPE(XPATH_NUMBER);
9598:
9599: if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
9600: (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
9601: (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
9602: (ctxt->value->floatval == 0.0))
9603: return;
9604:
9605: XTRUNC(f, ctxt->value->floatval);
9606: if (ctxt->value->floatval < 0) {
9607: if (ctxt->value->floatval < f - 0.5)
9608: ctxt->value->floatval = f - 1;
9609: else
9610: ctxt->value->floatval = f;
9611: if (ctxt->value->floatval == 0)
9612: ctxt->value->floatval = xmlXPathNZERO;
9613: } else {
9614: if (ctxt->value->floatval < f + 0.5)
9615: ctxt->value->floatval = f;
9616: else
9617: ctxt->value->floatval = f + 1;
9618: }
9619: }
9620:
9621: /************************************************************************
9622: * *
9623: * The Parser *
9624: * *
9625: ************************************************************************/
9626:
9627: /*
9628: * a few forward declarations since we use a recursive call based
9629: * implementation.
9630: */
9631: static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
9632: static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
9633: static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
9634: static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
9635: static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
9636: int qualified);
9637:
9638: /**
9639: * xmlXPathCurrentChar:
9640: * @ctxt: the XPath parser context
9641: * @cur: pointer to the beginning of the char
9642: * @len: pointer to the length of the char read
9643: *
9644: * The current char value, if using UTF-8 this may actually span multiple
9645: * bytes in the input buffer.
9646: *
9647: * Returns the current char value and its length
9648: */
9649:
9650: static int
9651: xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
9652: unsigned char c;
9653: unsigned int val;
9654: const xmlChar *cur;
9655:
9656: if (ctxt == NULL)
9657: return(0);
9658: cur = ctxt->cur;
9659:
9660: /*
9661: * We are supposed to handle UTF8, check it's valid
9662: * From rfc2044: encoding of the Unicode values on UTF-8:
9663: *
9664: * UCS-4 range (hex.) UTF-8 octet sequence (binary)
9665: * 0000 0000-0000 007F 0xxxxxxx
9666: * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
9667: * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
9668: *
9669: * Check for the 0x110000 limit too
9670: */
9671: c = *cur;
9672: if (c & 0x80) {
9673: if ((cur[1] & 0xc0) != 0x80)
9674: goto encoding_error;
9675: if ((c & 0xe0) == 0xe0) {
9676:
9677: if ((cur[2] & 0xc0) != 0x80)
9678: goto encoding_error;
9679: if ((c & 0xf0) == 0xf0) {
9680: if (((c & 0xf8) != 0xf0) ||
9681: ((cur[3] & 0xc0) != 0x80))
9682: goto encoding_error;
9683: /* 4-byte code */
9684: *len = 4;
9685: val = (cur[0] & 0x7) << 18;
9686: val |= (cur[1] & 0x3f) << 12;
9687: val |= (cur[2] & 0x3f) << 6;
9688: val |= cur[3] & 0x3f;
9689: } else {
9690: /* 3-byte code */
9691: *len = 3;
9692: val = (cur[0] & 0xf) << 12;
9693: val |= (cur[1] & 0x3f) << 6;
9694: val |= cur[2] & 0x3f;
9695: }
9696: } else {
9697: /* 2-byte code */
9698: *len = 2;
9699: val = (cur[0] & 0x1f) << 6;
9700: val |= cur[1] & 0x3f;
9701: }
9702: if (!IS_CHAR(val)) {
9703: XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
9704: }
9705: return(val);
9706: } else {
9707: /* 1-byte code */
9708: *len = 1;
9709: return((int) *cur);
9710: }
9711: encoding_error:
9712: /*
9713: * If we detect an UTF8 error that probably means that the
9714: * input encoding didn't get properly advertised in the
9715: * declaration header. Report the error and switch the encoding
9716: * to ISO-Latin-1 (if you don't like this policy, just declare the
9717: * encoding !)
9718: */
9719: *len = 0;
9720: XP_ERROR0(XPATH_ENCODING_ERROR);
9721: }
9722:
9723: /**
9724: * xmlXPathParseNCName:
9725: * @ctxt: the XPath Parser context
9726: *
9727: * parse an XML namespace non qualified name.
9728: *
9729: * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
9730: *
9731: * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
9732: * CombiningChar | Extender
9733: *
9734: * Returns the namespace name or NULL
9735: */
9736:
9737: xmlChar *
9738: xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
9739: const xmlChar *in;
9740: xmlChar *ret;
9741: int count = 0;
9742:
9743: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9744: /*
9745: * Accelerator for simple ASCII names
9746: */
9747: in = ctxt->cur;
9748: if (((*in >= 0x61) && (*in <= 0x7A)) ||
9749: ((*in >= 0x41) && (*in <= 0x5A)) ||
9750: (*in == '_')) {
9751: in++;
9752: while (((*in >= 0x61) && (*in <= 0x7A)) ||
9753: ((*in >= 0x41) && (*in <= 0x5A)) ||
9754: ((*in >= 0x30) && (*in <= 0x39)) ||
9755: (*in == '_') || (*in == '.') ||
9756: (*in == '-'))
9757: in++;
9758: if ((*in == ' ') || (*in == '>') || (*in == '/') ||
9759: (*in == '[') || (*in == ']') || (*in == ':') ||
9760: (*in == '@') || (*in == '*')) {
9761: count = in - ctxt->cur;
9762: if (count == 0)
9763: return(NULL);
9764: ret = xmlStrndup(ctxt->cur, count);
9765: ctxt->cur = in;
9766: return(ret);
9767: }
9768: }
9769: return(xmlXPathParseNameComplex(ctxt, 0));
9770: }
9771:
9772:
9773: /**
9774: * xmlXPathParseQName:
9775: * @ctxt: the XPath Parser context
9776: * @prefix: a xmlChar **
9777: *
9778: * parse an XML qualified name
9779: *
9780: * [NS 5] QName ::= (Prefix ':')? LocalPart
9781: *
9782: * [NS 6] Prefix ::= NCName
9783: *
9784: * [NS 7] LocalPart ::= NCName
9785: *
9786: * Returns the function returns the local part, and prefix is updated
9787: * to get the Prefix if any.
9788: */
9789:
9790: static xmlChar *
9791: xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
9792: xmlChar *ret = NULL;
9793:
9794: *prefix = NULL;
9795: ret = xmlXPathParseNCName(ctxt);
9796: if (ret && CUR == ':') {
9797: *prefix = ret;
9798: NEXT;
9799: ret = xmlXPathParseNCName(ctxt);
9800: }
9801: return(ret);
9802: }
9803:
9804: /**
9805: * xmlXPathParseName:
9806: * @ctxt: the XPath Parser context
9807: *
9808: * parse an XML name
9809: *
9810: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
9811: * CombiningChar | Extender
9812: *
9813: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
9814: *
9815: * Returns the namespace name or NULL
9816: */
9817:
9818: xmlChar *
9819: xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
9820: const xmlChar *in;
9821: xmlChar *ret;
9822: int count = 0;
9823:
9824: if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
9825: /*
9826: * Accelerator for simple ASCII names
9827: */
9828: in = ctxt->cur;
9829: if (((*in >= 0x61) && (*in <= 0x7A)) ||
9830: ((*in >= 0x41) && (*in <= 0x5A)) ||
9831: (*in == '_') || (*in == ':')) {
9832: in++;
9833: while (((*in >= 0x61) && (*in <= 0x7A)) ||
9834: ((*in >= 0x41) && (*in <= 0x5A)) ||
9835: ((*in >= 0x30) && (*in <= 0x39)) ||
9836: (*in == '_') || (*in == '-') ||
9837: (*in == ':') || (*in == '.'))
9838: in++;
9839: if ((*in > 0) && (*in < 0x80)) {
9840: count = in - ctxt->cur;
9841: ret = xmlStrndup(ctxt->cur, count);
9842: ctxt->cur = in;
9843: return(ret);
9844: }
9845: }
9846: return(xmlXPathParseNameComplex(ctxt, 1));
9847: }
9848:
9849: static xmlChar *
9850: xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
9851: xmlChar buf[XML_MAX_NAMELEN + 5];
9852: int len = 0, l;
9853: int c;
9854:
9855: /*
9856: * Handler for more complex cases
9857: */
9858: c = CUR_CHAR(l);
9859: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
9860: (c == '[') || (c == ']') || (c == '@') || /* accelerators */
9861: (c == '*') || /* accelerators */
9862: (!IS_LETTER(c) && (c != '_') &&
9863: ((qualified) && (c != ':')))) {
9864: return(NULL);
9865: }
9866:
9867: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
9868: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
9869: (c == '.') || (c == '-') ||
9870: (c == '_') || ((qualified) && (c == ':')) ||
9871: (IS_COMBINING(c)) ||
9872: (IS_EXTENDER(c)))) {
9873: COPY_BUF(l,buf,len,c);
9874: NEXTL(l);
9875: c = CUR_CHAR(l);
9876: if (len >= XML_MAX_NAMELEN) {
9877: /*
9878: * Okay someone managed to make a huge name, so he's ready to pay
9879: * for the processing speed.
9880: */
9881: xmlChar *buffer;
9882: int max = len * 2;
9883:
9884: buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar));
9885: if (buffer == NULL) {
9886: XP_ERRORNULL(XPATH_MEMORY_ERROR);
9887: }
9888: memcpy(buffer, buf, len);
9889: while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
9890: (c == '.') || (c == '-') ||
9891: (c == '_') || ((qualified) && (c == ':')) ||
9892: (IS_COMBINING(c)) ||
9893: (IS_EXTENDER(c))) {
9894: if (len + 10 > max) {
9895: max *= 2;
9896: buffer = (xmlChar *) xmlRealloc(buffer,
9897: max * sizeof(xmlChar));
9898: if (buffer == NULL) {
9899: XP_ERRORNULL(XPATH_MEMORY_ERROR);
9900: }
9901: }
9902: COPY_BUF(l,buffer,len,c);
9903: NEXTL(l);
9904: c = CUR_CHAR(l);
9905: }
9906: buffer[len] = 0;
9907: return(buffer);
9908: }
9909: }
9910: if (len == 0)
9911: return(NULL);
9912: return(xmlStrndup(buf, len));
9913: }
9914:
9915: #define MAX_FRAC 20
9916:
9917: /*
9918: * These are used as divisors for the fractional part of a number.
9919: * Since the table includes 1.0 (representing '0' fractional digits),
9920: * it must be dimensioned at MAX_FRAC+1 (bug 133921)
9921: */
9922: static double my_pow10[MAX_FRAC+1] = {
9923: 1.0, 10.0, 100.0, 1000.0, 10000.0,
9924: 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
9925: 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
9926: 100000000000000.0,
9927: 1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
9928: 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
9929: };
9930:
9931: /**
9932: * xmlXPathStringEvalNumber:
9933: * @str: A string to scan
9934: *
9935: * [30a] Float ::= Number ('e' Digits?)?
9936: *
9937: * [30] Number ::= Digits ('.' Digits?)?
9938: * | '.' Digits
9939: * [31] Digits ::= [0-9]+
9940: *
9941: * Compile a Number in the string
9942: * In complement of the Number expression, this function also handles
9943: * negative values : '-' Number.
9944: *
9945: * Returns the double value.
9946: */
9947: double
9948: xmlXPathStringEvalNumber(const xmlChar *str) {
9949: const xmlChar *cur = str;
9950: double ret;
9951: int ok = 0;
9952: int isneg = 0;
9953: int exponent = 0;
9954: int is_exponent_negative = 0;
9955: #ifdef __GNUC__
9956: unsigned long tmp = 0;
9957: double temp;
9958: #endif
9959: if (cur == NULL) return(0);
9960: while (IS_BLANK_CH(*cur)) cur++;
9961: if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
9962: return(xmlXPathNAN);
9963: }
9964: if (*cur == '-') {
9965: isneg = 1;
9966: cur++;
9967: }
9968:
9969: #ifdef __GNUC__
9970: /*
9971: * tmp/temp is a workaround against a gcc compiler bug
9972: * http://veillard.com/gcc.bug
9973: */
9974: ret = 0;
9975: while ((*cur >= '0') && (*cur <= '9')) {
9976: ret = ret * 10;
9977: tmp = (*cur - '0');
9978: ok = 1;
9979: cur++;
9980: temp = (double) tmp;
9981: ret = ret + temp;
9982: }
9983: #else
9984: ret = 0;
9985: while ((*cur >= '0') && (*cur <= '9')) {
9986: ret = ret * 10 + (*cur - '0');
9987: ok = 1;
9988: cur++;
9989: }
9990: #endif
9991:
9992: if (*cur == '.') {
9993: int v, frac = 0;
9994: double fraction = 0;
9995:
9996: cur++;
9997: if (((*cur < '0') || (*cur > '9')) && (!ok)) {
9998: return(xmlXPathNAN);
9999: }
10000: while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) {
10001: v = (*cur - '0');
10002: fraction = fraction * 10 + v;
10003: frac = frac + 1;
10004: cur++;
10005: }
10006: fraction /= my_pow10[frac];
10007: ret = ret + fraction;
10008: while ((*cur >= '0') && (*cur <= '9'))
10009: cur++;
10010: }
10011: if ((*cur == 'e') || (*cur == 'E')) {
10012: cur++;
10013: if (*cur == '-') {
10014: is_exponent_negative = 1;
10015: cur++;
10016: } else if (*cur == '+') {
10017: cur++;
10018: }
10019: while ((*cur >= '0') && (*cur <= '9')) {
10020: exponent = exponent * 10 + (*cur - '0');
10021: cur++;
10022: }
10023: }
10024: while (IS_BLANK_CH(*cur)) cur++;
10025: if (*cur != 0) return(xmlXPathNAN);
10026: if (isneg) ret = -ret;
10027: if (is_exponent_negative) exponent = -exponent;
10028: ret *= pow(10.0, (double)exponent);
10029: return(ret);
10030: }
10031:
10032: /**
10033: * xmlXPathCompNumber:
10034: * @ctxt: the XPath Parser context
10035: *
10036: * [30] Number ::= Digits ('.' Digits?)?
10037: * | '.' Digits
10038: * [31] Digits ::= [0-9]+
10039: *
10040: * Compile a Number, then push it on the stack
10041: *
10042: */
10043: static void
10044: xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
10045: {
10046: double ret = 0.0;
10047: double mult = 1;
10048: int ok = 0;
10049: int exponent = 0;
10050: int is_exponent_negative = 0;
10051: #ifdef __GNUC__
10052: unsigned long tmp = 0;
10053: double temp;
10054: #endif
10055:
10056: CHECK_ERROR;
10057: if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
10058: XP_ERROR(XPATH_NUMBER_ERROR);
10059: }
10060: #ifdef __GNUC__
10061: /*
10062: * tmp/temp is a workaround against a gcc compiler bug
10063: * http://veillard.com/gcc.bug
10064: */
10065: ret = 0;
10066: while ((CUR >= '0') && (CUR <= '9')) {
10067: ret = ret * 10;
10068: tmp = (CUR - '0');
10069: ok = 1;
10070: NEXT;
10071: temp = (double) tmp;
10072: ret = ret + temp;
10073: }
10074: #else
10075: ret = 0;
10076: while ((CUR >= '0') && (CUR <= '9')) {
10077: ret = ret * 10 + (CUR - '0');
10078: ok = 1;
10079: NEXT;
10080: }
10081: #endif
10082: if (CUR == '.') {
10083: int v, frac = 0;
10084: double fraction = 0;
10085:
10086: NEXT;
10087: if (((CUR < '0') || (CUR > '9')) && (!ok)) {
10088: XP_ERROR(XPATH_NUMBER_ERROR);
10089: }
10090: while ((CUR >= '0') && (CUR <= '9') && (frac < MAX_FRAC)) {
10091: v = (CUR - '0');
10092: fraction = fraction * 10 + v;
10093: frac = frac + 1;
10094: NEXT;
10095: }
10096: fraction /= my_pow10[frac];
10097: ret = ret + fraction;
10098: while ((CUR >= '0') && (CUR <= '9'))
10099: NEXT;
10100: }
10101: if ((CUR == 'e') || (CUR == 'E')) {
10102: NEXT;
10103: if (CUR == '-') {
10104: is_exponent_negative = 1;
10105: NEXT;
10106: } else if (CUR == '+') {
10107: NEXT;
10108: }
10109: while ((CUR >= '0') && (CUR <= '9')) {
10110: exponent = exponent * 10 + (CUR - '0');
10111: NEXT;
10112: }
10113: if (is_exponent_negative)
10114: exponent = -exponent;
10115: ret *= pow(10.0, (double) exponent);
10116: }
10117: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
10118: xmlXPathCacheNewFloat(ctxt->context, ret), NULL);
10119: }
10120:
10121: /**
10122: * xmlXPathParseLiteral:
10123: * @ctxt: the XPath Parser context
10124: *
10125: * Parse a Literal
10126: *
10127: * [29] Literal ::= '"' [^"]* '"'
10128: * | "'" [^']* "'"
10129: *
10130: * Returns the value found or NULL in case of error
10131: */
10132: static xmlChar *
10133: xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
10134: const xmlChar *q;
10135: xmlChar *ret = NULL;
10136:
10137: if (CUR == '"') {
10138: NEXT;
10139: q = CUR_PTR;
10140: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10141: NEXT;
10142: if (!IS_CHAR_CH(CUR)) {
10143: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10144: } else {
10145: ret = xmlStrndup(q, CUR_PTR - q);
10146: NEXT;
10147: }
10148: } else if (CUR == '\'') {
10149: NEXT;
10150: q = CUR_PTR;
10151: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10152: NEXT;
10153: if (!IS_CHAR_CH(CUR)) {
10154: XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR);
10155: } else {
10156: ret = xmlStrndup(q, CUR_PTR - q);
10157: NEXT;
10158: }
10159: } else {
10160: XP_ERRORNULL(XPATH_START_LITERAL_ERROR);
10161: }
10162: return(ret);
10163: }
10164:
10165: /**
10166: * xmlXPathCompLiteral:
10167: * @ctxt: the XPath Parser context
10168: *
10169: * Parse a Literal and push it on the stack.
10170: *
10171: * [29] Literal ::= '"' [^"]* '"'
10172: * | "'" [^']* "'"
10173: *
10174: * TODO: xmlXPathCompLiteral memory allocation could be improved.
10175: */
10176: static void
10177: xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
10178: const xmlChar *q;
10179: xmlChar *ret = NULL;
10180:
10181: if (CUR == '"') {
10182: NEXT;
10183: q = CUR_PTR;
10184: while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
10185: NEXT;
10186: if (!IS_CHAR_CH(CUR)) {
10187: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10188: } else {
10189: ret = xmlStrndup(q, CUR_PTR - q);
10190: NEXT;
10191: }
10192: } else if (CUR == '\'') {
10193: NEXT;
10194: q = CUR_PTR;
10195: while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
10196: NEXT;
10197: if (!IS_CHAR_CH(CUR)) {
10198: XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
10199: } else {
10200: ret = xmlStrndup(q, CUR_PTR - q);
10201: NEXT;
10202: }
10203: } else {
10204: XP_ERROR(XPATH_START_LITERAL_ERROR);
10205: }
10206: if (ret == NULL) return;
10207: PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
10208: xmlXPathCacheNewString(ctxt->context, ret), NULL);
10209: xmlFree(ret);
10210: }
10211:
10212: /**
10213: * xmlXPathCompVariableReference:
10214: * @ctxt: the XPath Parser context
10215: *
10216: * Parse a VariableReference, evaluate it and push it on the stack.
10217: *
10218: * The variable bindings consist of a mapping from variable names
10219: * to variable values. The value of a variable is an object, which can be
10220: * of any of the types that are possible for the value of an expression,
10221: * and may also be of additional types not specified here.
10222: *
10223: * Early evaluation is possible since:
10224: * The variable bindings [...] used to evaluate a subexpression are
10225: * always the same as those used to evaluate the containing expression.
10226: *
10227: * [36] VariableReference ::= '$' QName
10228: */
10229: static void
10230: xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
10231: xmlChar *name;
10232: xmlChar *prefix;
10233:
10234: SKIP_BLANKS;
10235: if (CUR != '$') {
10236: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10237: }
10238: NEXT;
10239: name = xmlXPathParseQName(ctxt, &prefix);
10240: if (name == NULL) {
10241: XP_ERROR(XPATH_VARIABLE_REF_ERROR);
10242: }
10243: ctxt->comp->last = -1;
10244: PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
10245: name, prefix);
10246: SKIP_BLANKS;
10247: if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
10248: XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR);
10249: }
10250: }
10251:
10252: /**
10253: * xmlXPathIsNodeType:
10254: * @name: a name string
10255: *
10256: * Is the name given a NodeType one.
10257: *
10258: * [38] NodeType ::= 'comment'
10259: * | 'text'
10260: * | 'processing-instruction'
10261: * | 'node'
10262: *
10263: * Returns 1 if true 0 otherwise
10264: */
10265: int
10266: xmlXPathIsNodeType(const xmlChar *name) {
10267: if (name == NULL)
10268: return(0);
10269:
10270: if (xmlStrEqual(name, BAD_CAST "node"))
10271: return(1);
10272: if (xmlStrEqual(name, BAD_CAST "text"))
10273: return(1);
10274: if (xmlStrEqual(name, BAD_CAST "comment"))
10275: return(1);
10276: if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
10277: return(1);
10278: return(0);
10279: }
10280:
10281: /**
10282: * xmlXPathCompFunctionCall:
10283: * @ctxt: the XPath Parser context
10284: *
10285: * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
10286: * [17] Argument ::= Expr
10287: *
10288: * Compile a function call, the evaluation of all arguments are
10289: * pushed on the stack
10290: */
10291: static void
10292: xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
10293: xmlChar *name;
10294: xmlChar *prefix;
10295: int nbargs = 0;
10296: int sort = 1;
10297:
10298: name = xmlXPathParseQName(ctxt, &prefix);
10299: if (name == NULL) {
10300: xmlFree(prefix);
10301: XP_ERROR(XPATH_EXPR_ERROR);
10302: }
10303: SKIP_BLANKS;
10304: #ifdef DEBUG_EXPR
10305: if (prefix == NULL)
10306: xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
10307: name);
10308: else
10309: xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
10310: prefix, name);
10311: #endif
10312:
10313: if (CUR != '(') {
10314: XP_ERROR(XPATH_EXPR_ERROR);
10315: }
10316: NEXT;
10317: SKIP_BLANKS;
10318:
10319: /*
10320: * Optimization for count(): we don't need the node-set to be sorted.
10321: */
10322: if ((prefix == NULL) && (name[0] == 'c') &&
10323: xmlStrEqual(name, BAD_CAST "count"))
10324: {
10325: sort = 0;
10326: }
10327: ctxt->comp->last = -1;
10328: if (CUR != ')') {
10329: while (CUR != 0) {
10330: int op1 = ctxt->comp->last;
10331: ctxt->comp->last = -1;
10332: xmlXPathCompileExpr(ctxt, sort);
10333: if (ctxt->error != XPATH_EXPRESSION_OK) {
10334: xmlFree(name);
10335: xmlFree(prefix);
10336: return;
10337: }
10338: PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
10339: nbargs++;
10340: if (CUR == ')') break;
10341: if (CUR != ',') {
10342: XP_ERROR(XPATH_EXPR_ERROR);
10343: }
10344: NEXT;
10345: SKIP_BLANKS;
10346: }
10347: }
10348: PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
10349: name, prefix);
10350: NEXT;
10351: SKIP_BLANKS;
10352: }
10353:
10354: /**
10355: * xmlXPathCompPrimaryExpr:
10356: * @ctxt: the XPath Parser context
10357: *
10358: * [15] PrimaryExpr ::= VariableReference
10359: * | '(' Expr ')'
10360: * | Literal
10361: * | Number
10362: * | FunctionCall
10363: *
10364: * Compile a primary expression.
10365: */
10366: static void
10367: xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
10368: SKIP_BLANKS;
10369: if (CUR == '$') xmlXPathCompVariableReference(ctxt);
10370: else if (CUR == '(') {
10371: NEXT;
10372: SKIP_BLANKS;
10373: xmlXPathCompileExpr(ctxt, 1);
10374: CHECK_ERROR;
10375: if (CUR != ')') {
10376: XP_ERROR(XPATH_EXPR_ERROR);
10377: }
10378: NEXT;
10379: SKIP_BLANKS;
10380: } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10381: xmlXPathCompNumber(ctxt);
10382: } else if ((CUR == '\'') || (CUR == '"')) {
10383: xmlXPathCompLiteral(ctxt);
10384: } else {
10385: xmlXPathCompFunctionCall(ctxt);
10386: }
10387: SKIP_BLANKS;
10388: }
10389:
10390: /**
10391: * xmlXPathCompFilterExpr:
10392: * @ctxt: the XPath Parser context
10393: *
10394: * [20] FilterExpr ::= PrimaryExpr
10395: * | FilterExpr Predicate
10396: *
10397: * Compile a filter expression.
10398: * Square brackets are used to filter expressions in the same way that
10399: * they are used in location paths. It is an error if the expression to
10400: * be filtered does not evaluate to a node-set. The context node list
10401: * used for evaluating the expression in square brackets is the node-set
10402: * to be filtered listed in document order.
10403: */
10404:
10405: static void
10406: xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
10407: xmlXPathCompPrimaryExpr(ctxt);
10408: CHECK_ERROR;
10409: SKIP_BLANKS;
10410:
10411: while (CUR == '[') {
10412: xmlXPathCompPredicate(ctxt, 1);
10413: SKIP_BLANKS;
10414: }
10415:
10416:
10417: }
10418:
10419: /**
10420: * xmlXPathScanName:
10421: * @ctxt: the XPath Parser context
10422: *
10423: * Trickery: parse an XML name but without consuming the input flow
10424: * Needed to avoid insanity in the parser state.
10425: *
10426: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
10427: * CombiningChar | Extender
10428: *
10429: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
10430: *
10431: * [6] Names ::= Name (S Name)*
10432: *
10433: * Returns the Name parsed or NULL
10434: */
10435:
10436: static xmlChar *
10437: xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
10438: int len = 0, l;
10439: int c;
10440: const xmlChar *cur;
10441: xmlChar *ret;
10442:
10443: cur = ctxt->cur;
10444:
10445: c = CUR_CHAR(l);
10446: if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
10447: (!IS_LETTER(c) && (c != '_') &&
10448: (c != ':'))) {
10449: return(NULL);
10450: }
10451:
10452: while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
10453: ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
10454: (c == '.') || (c == '-') ||
10455: (c == '_') || (c == ':') ||
10456: (IS_COMBINING(c)) ||
10457: (IS_EXTENDER(c)))) {
10458: len += l;
10459: NEXTL(l);
10460: c = CUR_CHAR(l);
10461: }
10462: ret = xmlStrndup(cur, ctxt->cur - cur);
10463: ctxt->cur = cur;
10464: return(ret);
10465: }
10466:
10467: /**
10468: * xmlXPathCompPathExpr:
10469: * @ctxt: the XPath Parser context
10470: *
10471: * [19] PathExpr ::= LocationPath
10472: * | FilterExpr
10473: * | FilterExpr '/' RelativeLocationPath
10474: * | FilterExpr '//' RelativeLocationPath
10475: *
10476: * Compile a path expression.
10477: * The / operator and // operators combine an arbitrary expression
10478: * and a relative location path. It is an error if the expression
10479: * does not evaluate to a node-set.
10480: * The / operator does composition in the same way as when / is
10481: * used in a location path. As in location paths, // is short for
10482: * /descendant-or-self::node()/.
10483: */
10484:
10485: static void
10486: xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
10487: int lc = 1; /* Should we branch to LocationPath ? */
10488: xmlChar *name = NULL; /* we may have to preparse a name to find out */
10489:
10490: SKIP_BLANKS;
10491: if ((CUR == '$') || (CUR == '(') ||
10492: (IS_ASCII_DIGIT(CUR)) ||
10493: (CUR == '\'') || (CUR == '"') ||
10494: (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) {
10495: lc = 0;
10496: } else if (CUR == '*') {
10497: /* relative or absolute location path */
10498: lc = 1;
10499: } else if (CUR == '/') {
10500: /* relative or absolute location path */
10501: lc = 1;
10502: } else if (CUR == '@') {
10503: /* relative abbreviated attribute location path */
10504: lc = 1;
10505: } else if (CUR == '.') {
10506: /* relative abbreviated attribute location path */
10507: lc = 1;
10508: } else {
10509: /*
10510: * Problem is finding if we have a name here whether it's:
10511: * - a nodetype
10512: * - a function call in which case it's followed by '('
10513: * - an axis in which case it's followed by ':'
10514: * - a element name
10515: * We do an a priori analysis here rather than having to
10516: * maintain parsed token content through the recursive function
10517: * calls. This looks uglier but makes the code easier to
10518: * read/write/debug.
10519: */
10520: SKIP_BLANKS;
10521: name = xmlXPathScanName(ctxt);
10522: if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
10523: #ifdef DEBUG_STEP
10524: xmlGenericError(xmlGenericErrorContext,
10525: "PathExpr: Axis\n");
10526: #endif
10527: lc = 1;
10528: xmlFree(name);
10529: } else if (name != NULL) {
10530: int len =xmlStrlen(name);
10531:
10532:
10533: while (NXT(len) != 0) {
10534: if (NXT(len) == '/') {
10535: /* element name */
10536: #ifdef DEBUG_STEP
10537: xmlGenericError(xmlGenericErrorContext,
10538: "PathExpr: AbbrRelLocation\n");
10539: #endif
10540: lc = 1;
10541: break;
10542: } else if (IS_BLANK_CH(NXT(len))) {
10543: /* ignore blanks */
10544: ;
10545: } else if (NXT(len) == ':') {
10546: #ifdef DEBUG_STEP
10547: xmlGenericError(xmlGenericErrorContext,
10548: "PathExpr: AbbrRelLocation\n");
10549: #endif
10550: lc = 1;
10551: break;
10552: } else if ((NXT(len) == '(')) {
10553: /* Note Type or Function */
10554: if (xmlXPathIsNodeType(name)) {
10555: #ifdef DEBUG_STEP
10556: xmlGenericError(xmlGenericErrorContext,
10557: "PathExpr: Type search\n");
10558: #endif
10559: lc = 1;
10560: } else {
10561: #ifdef DEBUG_STEP
10562: xmlGenericError(xmlGenericErrorContext,
10563: "PathExpr: function call\n");
10564: #endif
10565: lc = 0;
10566: }
10567: break;
10568: } else if ((NXT(len) == '[')) {
10569: /* element name */
10570: #ifdef DEBUG_STEP
10571: xmlGenericError(xmlGenericErrorContext,
10572: "PathExpr: AbbrRelLocation\n");
10573: #endif
10574: lc = 1;
10575: break;
10576: } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
10577: (NXT(len) == '=')) {
10578: lc = 1;
10579: break;
10580: } else {
10581: lc = 1;
10582: break;
10583: }
10584: len++;
10585: }
10586: if (NXT(len) == 0) {
10587: #ifdef DEBUG_STEP
10588: xmlGenericError(xmlGenericErrorContext,
10589: "PathExpr: AbbrRelLocation\n");
10590: #endif
10591: /* element name */
10592: lc = 1;
10593: }
10594: xmlFree(name);
10595: } else {
10596: /* make sure all cases are covered explicitly */
10597: XP_ERROR(XPATH_EXPR_ERROR);
10598: }
10599: }
10600:
10601: if (lc) {
10602: if (CUR == '/') {
10603: PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
10604: } else {
10605: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10606: }
10607: xmlXPathCompLocationPath(ctxt);
10608: } else {
10609: xmlXPathCompFilterExpr(ctxt);
10610: CHECK_ERROR;
10611: if ((CUR == '/') && (NXT(1) == '/')) {
10612: SKIP(2);
10613: SKIP_BLANKS;
10614:
10615: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
10616: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
10617: PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
10618:
10619: xmlXPathCompRelativeLocationPath(ctxt);
10620: } else if (CUR == '/') {
10621: xmlXPathCompRelativeLocationPath(ctxt);
10622: }
10623: }
10624: SKIP_BLANKS;
10625: }
10626:
10627: /**
10628: * xmlXPathCompUnionExpr:
10629: * @ctxt: the XPath Parser context
10630: *
10631: * [18] UnionExpr ::= PathExpr
10632: * | UnionExpr '|' PathExpr
10633: *
10634: * Compile an union expression.
10635: */
10636:
10637: static void
10638: xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
10639: xmlXPathCompPathExpr(ctxt);
10640: CHECK_ERROR;
10641: SKIP_BLANKS;
10642: while (CUR == '|') {
10643: int op1 = ctxt->comp->last;
10644: PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
10645:
10646: NEXT;
10647: SKIP_BLANKS;
10648: xmlXPathCompPathExpr(ctxt);
10649:
10650: PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
10651:
10652: SKIP_BLANKS;
10653: }
10654: }
10655:
10656: /**
10657: * xmlXPathCompUnaryExpr:
10658: * @ctxt: the XPath Parser context
10659: *
10660: * [27] UnaryExpr ::= UnionExpr
10661: * | '-' UnaryExpr
10662: *
10663: * Compile an unary expression.
10664: */
10665:
10666: static void
10667: xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
10668: int minus = 0;
10669: int found = 0;
10670:
10671: SKIP_BLANKS;
10672: while (CUR == '-') {
10673: minus = 1 - minus;
10674: found = 1;
10675: NEXT;
10676: SKIP_BLANKS;
10677: }
10678:
10679: xmlXPathCompUnionExpr(ctxt);
10680: CHECK_ERROR;
10681: if (found) {
10682: if (minus)
10683: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
10684: else
10685: PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
10686: }
10687: }
10688:
10689: /**
10690: * xmlXPathCompMultiplicativeExpr:
10691: * @ctxt: the XPath Parser context
10692: *
10693: * [26] MultiplicativeExpr ::= UnaryExpr
10694: * | MultiplicativeExpr MultiplyOperator UnaryExpr
10695: * | MultiplicativeExpr 'div' UnaryExpr
10696: * | MultiplicativeExpr 'mod' UnaryExpr
10697: * [34] MultiplyOperator ::= '*'
10698: *
10699: * Compile an Additive expression.
10700: */
10701:
10702: static void
10703: xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
10704: xmlXPathCompUnaryExpr(ctxt);
10705: CHECK_ERROR;
10706: SKIP_BLANKS;
10707: while ((CUR == '*') ||
10708: ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
10709: ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
10710: int op = -1;
10711: int op1 = ctxt->comp->last;
10712:
10713: if (CUR == '*') {
10714: op = 0;
10715: NEXT;
10716: } else if (CUR == 'd') {
10717: op = 1;
10718: SKIP(3);
10719: } else if (CUR == 'm') {
10720: op = 2;
10721: SKIP(3);
10722: }
10723: SKIP_BLANKS;
10724: xmlXPathCompUnaryExpr(ctxt);
10725: CHECK_ERROR;
10726: PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
10727: SKIP_BLANKS;
10728: }
10729: }
10730:
10731: /**
10732: * xmlXPathCompAdditiveExpr:
10733: * @ctxt: the XPath Parser context
10734: *
10735: * [25] AdditiveExpr ::= MultiplicativeExpr
10736: * | AdditiveExpr '+' MultiplicativeExpr
10737: * | AdditiveExpr '-' MultiplicativeExpr
10738: *
10739: * Compile an Additive expression.
10740: */
10741:
10742: static void
10743: xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
10744:
10745: xmlXPathCompMultiplicativeExpr(ctxt);
10746: CHECK_ERROR;
10747: SKIP_BLANKS;
10748: while ((CUR == '+') || (CUR == '-')) {
10749: int plus;
10750: int op1 = ctxt->comp->last;
10751:
10752: if (CUR == '+') plus = 1;
10753: else plus = 0;
10754: NEXT;
10755: SKIP_BLANKS;
10756: xmlXPathCompMultiplicativeExpr(ctxt);
10757: CHECK_ERROR;
10758: PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
10759: SKIP_BLANKS;
10760: }
10761: }
10762:
10763: /**
10764: * xmlXPathCompRelationalExpr:
10765: * @ctxt: the XPath Parser context
10766: *
10767: * [24] RelationalExpr ::= AdditiveExpr
10768: * | RelationalExpr '<' AdditiveExpr
10769: * | RelationalExpr '>' AdditiveExpr
10770: * | RelationalExpr '<=' AdditiveExpr
10771: * | RelationalExpr '>=' AdditiveExpr
10772: *
10773: * A <= B > C is allowed ? Answer from James, yes with
10774: * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
10775: * which is basically what got implemented.
10776: *
10777: * Compile a Relational expression, then push the result
10778: * on the stack
10779: */
10780:
10781: static void
10782: xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
10783: xmlXPathCompAdditiveExpr(ctxt);
10784: CHECK_ERROR;
10785: SKIP_BLANKS;
10786: while ((CUR == '<') ||
10787: (CUR == '>') ||
10788: ((CUR == '<') && (NXT(1) == '=')) ||
10789: ((CUR == '>') && (NXT(1) == '='))) {
10790: int inf, strict;
10791: int op1 = ctxt->comp->last;
10792:
10793: if (CUR == '<') inf = 1;
10794: else inf = 0;
10795: if (NXT(1) == '=') strict = 0;
10796: else strict = 1;
10797: NEXT;
10798: if (!strict) NEXT;
10799: SKIP_BLANKS;
10800: xmlXPathCompAdditiveExpr(ctxt);
10801: CHECK_ERROR;
10802: PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
10803: SKIP_BLANKS;
10804: }
10805: }
10806:
10807: /**
10808: * xmlXPathCompEqualityExpr:
10809: * @ctxt: the XPath Parser context
10810: *
10811: * [23] EqualityExpr ::= RelationalExpr
10812: * | EqualityExpr '=' RelationalExpr
10813: * | EqualityExpr '!=' RelationalExpr
10814: *
10815: * A != B != C is allowed ? Answer from James, yes with
10816: * (RelationalExpr = RelationalExpr) = RelationalExpr
10817: * (RelationalExpr != RelationalExpr) != RelationalExpr
10818: * which is basically what got implemented.
10819: *
10820: * Compile an Equality expression.
10821: *
10822: */
10823: static void
10824: xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
10825: xmlXPathCompRelationalExpr(ctxt);
10826: CHECK_ERROR;
10827: SKIP_BLANKS;
10828: while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
10829: int eq;
10830: int op1 = ctxt->comp->last;
10831:
10832: if (CUR == '=') eq = 1;
10833: else eq = 0;
10834: NEXT;
10835: if (!eq) NEXT;
10836: SKIP_BLANKS;
10837: xmlXPathCompRelationalExpr(ctxt);
10838: CHECK_ERROR;
10839: PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
10840: SKIP_BLANKS;
10841: }
10842: }
10843:
10844: /**
10845: * xmlXPathCompAndExpr:
10846: * @ctxt: the XPath Parser context
10847: *
10848: * [22] AndExpr ::= EqualityExpr
10849: * | AndExpr 'and' EqualityExpr
10850: *
10851: * Compile an AND expression.
10852: *
10853: */
10854: static void
10855: xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
10856: xmlXPathCompEqualityExpr(ctxt);
10857: CHECK_ERROR;
10858: SKIP_BLANKS;
10859: while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
10860: int op1 = ctxt->comp->last;
10861: SKIP(3);
10862: SKIP_BLANKS;
10863: xmlXPathCompEqualityExpr(ctxt);
10864: CHECK_ERROR;
10865: PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
10866: SKIP_BLANKS;
10867: }
10868: }
10869:
10870: /**
10871: * xmlXPathCompileExpr:
10872: * @ctxt: the XPath Parser context
10873: *
10874: * [14] Expr ::= OrExpr
10875: * [21] OrExpr ::= AndExpr
10876: * | OrExpr 'or' AndExpr
10877: *
10878: * Parse and compile an expression
10879: */
10880: static void
10881: xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
10882: xmlXPathCompAndExpr(ctxt);
10883: CHECK_ERROR;
10884: SKIP_BLANKS;
10885: while ((CUR == 'o') && (NXT(1) == 'r')) {
10886: int op1 = ctxt->comp->last;
10887: SKIP(2);
10888: SKIP_BLANKS;
10889: xmlXPathCompAndExpr(ctxt);
10890: CHECK_ERROR;
10891: PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
10892: SKIP_BLANKS;
10893: }
10894: if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
10895: /* more ops could be optimized too */
10896: /*
10897: * This is the main place to eliminate sorting for
10898: * operations which don't require a sorted node-set.
10899: * E.g. count().
10900: */
10901: PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
10902: }
10903: }
10904:
10905: /**
10906: * xmlXPathCompPredicate:
10907: * @ctxt: the XPath Parser context
10908: * @filter: act as a filter
10909: *
10910: * [8] Predicate ::= '[' PredicateExpr ']'
10911: * [9] PredicateExpr ::= Expr
10912: *
10913: * Compile a predicate expression
10914: */
10915: static void
10916: xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
10917: int op1 = ctxt->comp->last;
10918:
10919: SKIP_BLANKS;
10920: if (CUR != '[') {
10921: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
10922: }
10923: NEXT;
10924: SKIP_BLANKS;
10925:
10926: ctxt->comp->last = -1;
10927: /*
10928: * This call to xmlXPathCompileExpr() will deactivate sorting
10929: * of the predicate result.
10930: * TODO: Sorting is still activated for filters, since I'm not
10931: * sure if needed. Normally sorting should not be needed, since
10932: * a filter can only diminish the number of items in a sequence,
10933: * but won't change its order; so if the initial sequence is sorted,
10934: * subsequent sorting is not needed.
10935: */
10936: if (! filter)
10937: xmlXPathCompileExpr(ctxt, 0);
10938: else
10939: xmlXPathCompileExpr(ctxt, 1);
10940: CHECK_ERROR;
10941:
10942: if (CUR != ']') {
10943: XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
10944: }
10945:
10946: if (filter)
10947: PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
10948: else
10949: PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
10950:
10951: NEXT;
10952: SKIP_BLANKS;
10953: }
10954:
10955: /**
10956: * xmlXPathCompNodeTest:
10957: * @ctxt: the XPath Parser context
10958: * @test: pointer to a xmlXPathTestVal
10959: * @type: pointer to a xmlXPathTypeVal
10960: * @prefix: placeholder for a possible name prefix
10961: *
10962: * [7] NodeTest ::= NameTest
10963: * | NodeType '(' ')'
10964: * | 'processing-instruction' '(' Literal ')'
10965: *
10966: * [37] NameTest ::= '*'
10967: * | NCName ':' '*'
10968: * | QName
10969: * [38] NodeType ::= 'comment'
10970: * | 'text'
10971: * | 'processing-instruction'
10972: * | 'node'
10973: *
10974: * Returns the name found and updates @test, @type and @prefix appropriately
10975: */
10976: static xmlChar *
10977: xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
10978: xmlXPathTypeVal *type, const xmlChar **prefix,
10979: xmlChar *name) {
10980: int blanks;
10981:
10982: if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
10983: STRANGE;
10984: return(NULL);
10985: }
10986: *type = (xmlXPathTypeVal) 0;
10987: *test = (xmlXPathTestVal) 0;
10988: *prefix = NULL;
10989: SKIP_BLANKS;
10990:
10991: if ((name == NULL) && (CUR == '*')) {
10992: /*
10993: * All elements
10994: */
10995: NEXT;
10996: *test = NODE_TEST_ALL;
10997: return(NULL);
10998: }
10999:
11000: if (name == NULL)
11001: name = xmlXPathParseNCName(ctxt);
11002: if (name == NULL) {
11003: XP_ERRORNULL(XPATH_EXPR_ERROR);
11004: }
11005:
11006: blanks = IS_BLANK_CH(CUR);
11007: SKIP_BLANKS;
11008: if (CUR == '(') {
11009: NEXT;
11010: /*
11011: * NodeType or PI search
11012: */
11013: if (xmlStrEqual(name, BAD_CAST "comment"))
11014: *type = NODE_TYPE_COMMENT;
11015: else if (xmlStrEqual(name, BAD_CAST "node"))
11016: *type = NODE_TYPE_NODE;
11017: else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
11018: *type = NODE_TYPE_PI;
11019: else if (xmlStrEqual(name, BAD_CAST "text"))
11020: *type = NODE_TYPE_TEXT;
11021: else {
11022: if (name != NULL)
11023: xmlFree(name);
11024: XP_ERRORNULL(XPATH_EXPR_ERROR);
11025: }
11026:
11027: *test = NODE_TEST_TYPE;
11028:
11029: SKIP_BLANKS;
11030: if (*type == NODE_TYPE_PI) {
11031: /*
11032: * Specific case: search a PI by name.
11033: */
11034: if (name != NULL)
11035: xmlFree(name);
11036: name = NULL;
11037: if (CUR != ')') {
11038: name = xmlXPathParseLiteral(ctxt);
11039: CHECK_ERROR NULL;
11040: *test = NODE_TEST_PI;
11041: SKIP_BLANKS;
11042: }
11043: }
11044: if (CUR != ')') {
11045: if (name != NULL)
11046: xmlFree(name);
11047: XP_ERRORNULL(XPATH_UNCLOSED_ERROR);
11048: }
11049: NEXT;
11050: return(name);
11051: }
11052: *test = NODE_TEST_NAME;
11053: if ((!blanks) && (CUR == ':')) {
11054: NEXT;
11055:
11056: /*
11057: * Since currently the parser context don't have a
11058: * namespace list associated:
11059: * The namespace name for this prefix can be computed
11060: * only at evaluation time. The compilation is done
11061: * outside of any context.
11062: */
11063: #if 0
11064: *prefix = xmlXPathNsLookup(ctxt->context, name);
11065: if (name != NULL)
11066: xmlFree(name);
11067: if (*prefix == NULL) {
11068: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
11069: }
11070: #else
11071: *prefix = name;
11072: #endif
11073:
11074: if (CUR == '*') {
11075: /*
11076: * All elements
11077: */
11078: NEXT;
11079: *test = NODE_TEST_ALL;
11080: return(NULL);
11081: }
11082:
11083: name = xmlXPathParseNCName(ctxt);
11084: if (name == NULL) {
11085: XP_ERRORNULL(XPATH_EXPR_ERROR);
11086: }
11087: }
11088: return(name);
11089: }
11090:
11091: /**
11092: * xmlXPathIsAxisName:
11093: * @name: a preparsed name token
11094: *
11095: * [6] AxisName ::= 'ancestor'
11096: * | 'ancestor-or-self'
11097: * | 'attribute'
11098: * | 'child'
11099: * | 'descendant'
11100: * | 'descendant-or-self'
11101: * | 'following'
11102: * | 'following-sibling'
11103: * | 'namespace'
11104: * | 'parent'
11105: * | 'preceding'
11106: * | 'preceding-sibling'
11107: * | 'self'
11108: *
11109: * Returns the axis or 0
11110: */
11111: static xmlXPathAxisVal
11112: xmlXPathIsAxisName(const xmlChar *name) {
11113: xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
11114: switch (name[0]) {
11115: case 'a':
11116: if (xmlStrEqual(name, BAD_CAST "ancestor"))
11117: ret = AXIS_ANCESTOR;
11118: if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
11119: ret = AXIS_ANCESTOR_OR_SELF;
11120: if (xmlStrEqual(name, BAD_CAST "attribute"))
11121: ret = AXIS_ATTRIBUTE;
11122: break;
11123: case 'c':
11124: if (xmlStrEqual(name, BAD_CAST "child"))
11125: ret = AXIS_CHILD;
11126: break;
11127: case 'd':
11128: if (xmlStrEqual(name, BAD_CAST "descendant"))
11129: ret = AXIS_DESCENDANT;
11130: if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
11131: ret = AXIS_DESCENDANT_OR_SELF;
11132: break;
11133: case 'f':
11134: if (xmlStrEqual(name, BAD_CAST "following"))
11135: ret = AXIS_FOLLOWING;
11136: if (xmlStrEqual(name, BAD_CAST "following-sibling"))
11137: ret = AXIS_FOLLOWING_SIBLING;
11138: break;
11139: case 'n':
11140: if (xmlStrEqual(name, BAD_CAST "namespace"))
11141: ret = AXIS_NAMESPACE;
11142: break;
11143: case 'p':
11144: if (xmlStrEqual(name, BAD_CAST "parent"))
11145: ret = AXIS_PARENT;
11146: if (xmlStrEqual(name, BAD_CAST "preceding"))
11147: ret = AXIS_PRECEDING;
11148: if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
11149: ret = AXIS_PRECEDING_SIBLING;
11150: break;
11151: case 's':
11152: if (xmlStrEqual(name, BAD_CAST "self"))
11153: ret = AXIS_SELF;
11154: break;
11155: }
11156: return(ret);
11157: }
11158:
11159: /**
11160: * xmlXPathCompStep:
11161: * @ctxt: the XPath Parser context
11162: *
11163: * [4] Step ::= AxisSpecifier NodeTest Predicate*
11164: * | AbbreviatedStep
11165: *
11166: * [12] AbbreviatedStep ::= '.' | '..'
11167: *
11168: * [5] AxisSpecifier ::= AxisName '::'
11169: * | AbbreviatedAxisSpecifier
11170: *
11171: * [13] AbbreviatedAxisSpecifier ::= '@'?
11172: *
11173: * Modified for XPtr range support as:
11174: *
11175: * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
11176: * | AbbreviatedStep
11177: * | 'range-to' '(' Expr ')' Predicate*
11178: *
11179: * Compile one step in a Location Path
11180: * A location step of . is short for self::node(). This is
11181: * particularly useful in conjunction with //. For example, the
11182: * location path .//para is short for
11183: * self::node()/descendant-or-self::node()/child::para
11184: * and so will select all para descendant elements of the context
11185: * node.
11186: * Similarly, a location step of .. is short for parent::node().
11187: * For example, ../title is short for parent::node()/child::title
11188: * and so will select the title children of the parent of the context
11189: * node.
11190: */
11191: static void
11192: xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
11193: #ifdef LIBXML_XPTR_ENABLED
11194: int rangeto = 0;
11195: int op2 = -1;
11196: #endif
11197:
11198: SKIP_BLANKS;
11199: if ((CUR == '.') && (NXT(1) == '.')) {
11200: SKIP(2);
11201: SKIP_BLANKS;
11202: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
11203: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11204: } else if (CUR == '.') {
11205: NEXT;
11206: SKIP_BLANKS;
11207: } else {
11208: xmlChar *name = NULL;
11209: const xmlChar *prefix = NULL;
11210: xmlXPathTestVal test = (xmlXPathTestVal) 0;
11211: xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
11212: xmlXPathTypeVal type = (xmlXPathTypeVal) 0;
11213: int op1;
11214:
11215: /*
11216: * The modification needed for XPointer change to the production
11217: */
11218: #ifdef LIBXML_XPTR_ENABLED
11219: if (ctxt->xptr) {
11220: name = xmlXPathParseNCName(ctxt);
11221: if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
11222: op2 = ctxt->comp->last;
11223: xmlFree(name);
11224: SKIP_BLANKS;
11225: if (CUR != '(') {
11226: XP_ERROR(XPATH_EXPR_ERROR);
11227: }
11228: NEXT;
11229: SKIP_BLANKS;
11230:
11231: xmlXPathCompileExpr(ctxt, 1);
11232: /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
11233: CHECK_ERROR;
11234:
11235: SKIP_BLANKS;
11236: if (CUR != ')') {
11237: XP_ERROR(XPATH_EXPR_ERROR);
11238: }
11239: NEXT;
11240: rangeto = 1;
11241: goto eval_predicates;
11242: }
11243: }
11244: #endif
11245: if (CUR == '*') {
11246: axis = AXIS_CHILD;
11247: } else {
11248: if (name == NULL)
11249: name = xmlXPathParseNCName(ctxt);
11250: if (name != NULL) {
11251: axis = xmlXPathIsAxisName(name);
11252: if (axis != 0) {
11253: SKIP_BLANKS;
11254: if ((CUR == ':') && (NXT(1) == ':')) {
11255: SKIP(2);
11256: xmlFree(name);
11257: name = NULL;
11258: } else {
11259: /* an element name can conflict with an axis one :-\ */
11260: axis = AXIS_CHILD;
11261: }
11262: } else {
11263: axis = AXIS_CHILD;
11264: }
11265: } else if (CUR == '@') {
11266: NEXT;
11267: axis = AXIS_ATTRIBUTE;
11268: } else {
11269: axis = AXIS_CHILD;
11270: }
11271: }
11272:
11273: if (ctxt->error != XPATH_EXPRESSION_OK) {
11274: xmlFree(name);
11275: return;
11276: }
11277:
11278: name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
11279: if (test == 0)
11280: return;
11281:
11282: if ((prefix != NULL) && (ctxt->context != NULL) &&
11283: (ctxt->context->flags & XML_XPATH_CHECKNS)) {
11284: if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) {
11285: xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
11286: }
11287: }
11288: #ifdef DEBUG_STEP
11289: xmlGenericError(xmlGenericErrorContext,
11290: "Basis : computing new set\n");
11291: #endif
11292:
11293: #ifdef DEBUG_STEP
11294: xmlGenericError(xmlGenericErrorContext, "Basis : ");
11295: if (ctxt->value == NULL)
11296: xmlGenericError(xmlGenericErrorContext, "no value\n");
11297: else if (ctxt->value->nodesetval == NULL)
11298: xmlGenericError(xmlGenericErrorContext, "Empty\n");
11299: else
11300: xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
11301: #endif
11302:
11303: #ifdef LIBXML_XPTR_ENABLED
11304: eval_predicates:
11305: #endif
11306: op1 = ctxt->comp->last;
11307: ctxt->comp->last = -1;
11308:
11309: SKIP_BLANKS;
11310: while (CUR == '[') {
11311: xmlXPathCompPredicate(ctxt, 0);
11312: }
11313:
11314: #ifdef LIBXML_XPTR_ENABLED
11315: if (rangeto) {
11316: PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
11317: } else
11318: #endif
11319: PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
11320: test, type, (void *)prefix, (void *)name);
11321:
11322: }
11323: #ifdef DEBUG_STEP
11324: xmlGenericError(xmlGenericErrorContext, "Step : ");
11325: if (ctxt->value == NULL)
11326: xmlGenericError(xmlGenericErrorContext, "no value\n");
11327: else if (ctxt->value->nodesetval == NULL)
11328: xmlGenericError(xmlGenericErrorContext, "Empty\n");
11329: else
11330: xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
11331: ctxt->value->nodesetval);
11332: #endif
11333: }
11334:
11335: /**
11336: * xmlXPathCompRelativeLocationPath:
11337: * @ctxt: the XPath Parser context
11338: *
11339: * [3] RelativeLocationPath ::= Step
11340: * | RelativeLocationPath '/' Step
11341: * | AbbreviatedRelativeLocationPath
11342: * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
11343: *
11344: * Compile a relative location path.
11345: */
11346: static void
11347: xmlXPathCompRelativeLocationPath
11348: (xmlXPathParserContextPtr ctxt) {
11349: SKIP_BLANKS;
11350: if ((CUR == '/') && (NXT(1) == '/')) {
11351: SKIP(2);
11352: SKIP_BLANKS;
11353: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11354: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11355: } else if (CUR == '/') {
11356: NEXT;
11357: SKIP_BLANKS;
11358: }
11359: xmlXPathCompStep(ctxt);
11360: CHECK_ERROR;
11361: SKIP_BLANKS;
11362: while (CUR == '/') {
11363: if ((CUR == '/') && (NXT(1) == '/')) {
11364: SKIP(2);
11365: SKIP_BLANKS;
11366: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11367: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11368: xmlXPathCompStep(ctxt);
11369: } else if (CUR == '/') {
11370: NEXT;
11371: SKIP_BLANKS;
11372: xmlXPathCompStep(ctxt);
11373: }
11374: SKIP_BLANKS;
11375: }
11376: }
11377:
11378: /**
11379: * xmlXPathCompLocationPath:
11380: * @ctxt: the XPath Parser context
11381: *
11382: * [1] LocationPath ::= RelativeLocationPath
11383: * | AbsoluteLocationPath
11384: * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
11385: * | AbbreviatedAbsoluteLocationPath
11386: * [10] AbbreviatedAbsoluteLocationPath ::=
11387: * '//' RelativeLocationPath
11388: *
11389: * Compile a location path
11390: *
11391: * // is short for /descendant-or-self::node()/. For example,
11392: * //para is short for /descendant-or-self::node()/child::para and
11393: * so will select any para element in the document (even a para element
11394: * that is a document element will be selected by //para since the
11395: * document element node is a child of the root node); div//para is
11396: * short for div/descendant-or-self::node()/child::para and so will
11397: * select all para descendants of div children.
11398: */
11399: static void
11400: xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
11401: SKIP_BLANKS;
11402: if (CUR != '/') {
11403: xmlXPathCompRelativeLocationPath(ctxt);
11404: } else {
11405: while (CUR == '/') {
11406: if ((CUR == '/') && (NXT(1) == '/')) {
11407: SKIP(2);
11408: SKIP_BLANKS;
11409: PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
11410: NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
11411: xmlXPathCompRelativeLocationPath(ctxt);
11412: } else if (CUR == '/') {
11413: NEXT;
11414: SKIP_BLANKS;
11415: if ((CUR != 0 ) &&
11416: ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
11417: (CUR == '@') || (CUR == '*')))
11418: xmlXPathCompRelativeLocationPath(ctxt);
11419: }
11420: CHECK_ERROR;
11421: }
11422: }
11423: }
11424:
11425: /************************************************************************
11426: * *
11427: * XPath precompiled expression evaluation *
11428: * *
11429: ************************************************************************/
11430:
11431: static int
11432: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
11433:
11434: #ifdef DEBUG_STEP
11435: static void
11436: xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
11437: int nbNodes)
11438: {
11439: xmlGenericError(xmlGenericErrorContext, "new step : ");
11440: switch (op->value) {
11441: case AXIS_ANCESTOR:
11442: xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
11443: break;
11444: case AXIS_ANCESTOR_OR_SELF:
11445: xmlGenericError(xmlGenericErrorContext,
11446: "axis 'ancestors-or-self' ");
11447: break;
11448: case AXIS_ATTRIBUTE:
11449: xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
11450: break;
11451: case AXIS_CHILD:
11452: xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
11453: break;
11454: case AXIS_DESCENDANT:
11455: xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
11456: break;
11457: case AXIS_DESCENDANT_OR_SELF:
11458: xmlGenericError(xmlGenericErrorContext,
11459: "axis 'descendant-or-self' ");
11460: break;
11461: case AXIS_FOLLOWING:
11462: xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
11463: break;
11464: case AXIS_FOLLOWING_SIBLING:
11465: xmlGenericError(xmlGenericErrorContext,
11466: "axis 'following-siblings' ");
11467: break;
11468: case AXIS_NAMESPACE:
11469: xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
11470: break;
11471: case AXIS_PARENT:
11472: xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
11473: break;
11474: case AXIS_PRECEDING:
11475: xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
11476: break;
11477: case AXIS_PRECEDING_SIBLING:
11478: xmlGenericError(xmlGenericErrorContext,
11479: "axis 'preceding-sibling' ");
11480: break;
11481: case AXIS_SELF:
11482: xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
11483: break;
11484: }
11485: xmlGenericError(xmlGenericErrorContext,
11486: " context contains %d nodes\n", nbNodes);
11487: switch (op->value2) {
11488: case NODE_TEST_NONE:
11489: xmlGenericError(xmlGenericErrorContext,
11490: " searching for none !!!\n");
11491: break;
11492: case NODE_TEST_TYPE:
11493: xmlGenericError(xmlGenericErrorContext,
11494: " searching for type %d\n", op->value3);
11495: break;
11496: case NODE_TEST_PI:
11497: xmlGenericError(xmlGenericErrorContext,
11498: " searching for PI !!!\n");
11499: break;
11500: case NODE_TEST_ALL:
11501: xmlGenericError(xmlGenericErrorContext,
11502: " searching for *\n");
11503: break;
11504: case NODE_TEST_NS:
11505: xmlGenericError(xmlGenericErrorContext,
11506: " searching for namespace %s\n",
11507: op->value5);
11508: break;
11509: case NODE_TEST_NAME:
11510: xmlGenericError(xmlGenericErrorContext,
11511: " searching for name %s\n", op->value5);
11512: if (op->value4)
11513: xmlGenericError(xmlGenericErrorContext,
11514: " with namespace %s\n", op->value4);
11515: break;
11516: }
11517: xmlGenericError(xmlGenericErrorContext, "Testing : ");
11518: }
11519: #endif /* DEBUG_STEP */
11520:
11521: static int
11522: xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
11523: xmlXPathStepOpPtr op,
11524: xmlNodeSetPtr set,
11525: int contextSize,
11526: int hasNsNodes)
11527: {
11528: if (op->ch1 != -1) {
11529: xmlXPathCompExprPtr comp = ctxt->comp;
11530: /*
11531: * Process inner predicates first.
11532: */
11533: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11534: /*
11535: * TODO: raise an internal error.
11536: */
11537: }
11538: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
11539: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
11540: CHECK_ERROR0;
11541: if (contextSize <= 0)
11542: return(0);
11543: }
11544: if (op->ch2 != -1) {
11545: xmlXPathContextPtr xpctxt = ctxt->context;
11546: xmlNodePtr contextNode, oldContextNode;
11547: xmlDocPtr oldContextDoc;
11548: int i, res, contextPos = 0, newContextSize;
11549: xmlXPathStepOpPtr exprOp;
11550: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
11551:
11552: #ifdef LIBXML_XPTR_ENABLED
11553: /*
11554: * URGENT TODO: Check the following:
11555: * We don't expect location sets if evaluating prediates, right?
11556: * Only filters should expect location sets, right?
11557: */
11558: #endif
11559: /*
11560: * SPEC XPath 1.0:
11561: * "For each node in the node-set to be filtered, the
11562: * PredicateExpr is evaluated with that node as the
11563: * context node, with the number of nodes in the
11564: * node-set as the context size, and with the proximity
11565: * position of the node in the node-set with respect to
11566: * the axis as the context position;"
11567: * @oldset is the node-set" to be filtered.
11568: *
11569: * SPEC XPath 1.0:
11570: * "only predicates change the context position and
11571: * context size (see [2.4 Predicates])."
11572: * Example:
11573: * node-set context pos
11574: * nA 1
11575: * nB 2
11576: * nC 3
11577: * After applying predicate [position() > 1] :
11578: * node-set context pos
11579: * nB 1
11580: * nC 2
11581: */
11582: oldContextNode = xpctxt->node;
11583: oldContextDoc = xpctxt->doc;
11584: /*
11585: * Get the expression of this predicate.
11586: */
11587: exprOp = &ctxt->comp->steps[op->ch2];
11588: newContextSize = 0;
11589: for (i = 0; i < set->nodeNr; i++) {
11590: if (set->nodeTab[i] == NULL)
11591: continue;
11592:
11593: contextNode = set->nodeTab[i];
11594: xpctxt->node = contextNode;
11595: xpctxt->contextSize = contextSize;
11596: xpctxt->proximityPosition = ++contextPos;
11597:
11598: /*
11599: * Also set the xpath document in case things like
11600: * key() are evaluated in the predicate.
11601: */
11602: if ((contextNode->type != XML_NAMESPACE_DECL) &&
11603: (contextNode->doc != NULL))
11604: xpctxt->doc = contextNode->doc;
11605: /*
11606: * Evaluate the predicate expression with 1 context node
11607: * at a time; this node is packaged into a node set; this
11608: * node set is handed over to the evaluation mechanism.
11609: */
11610: if (contextObj == NULL)
11611: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
11612: else
11613: xmlXPathNodeSetAddUnique(contextObj->nodesetval,
11614: contextNode);
11615:
11616: valuePush(ctxt, contextObj);
11617:
11618: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
11619:
11620: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
11621: xmlXPathNodeSetClear(set, hasNsNodes);
11622: newContextSize = 0;
11623: goto evaluation_exit;
11624: }
11625:
11626: if (res != 0) {
11627: newContextSize++;
11628: } else {
11629: /*
11630: * Remove the entry from the initial node set.
11631: */
11632: set->nodeTab[i] = NULL;
11633: if (contextNode->type == XML_NAMESPACE_DECL)
11634: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
11635: }
11636: if (ctxt->value == contextObj) {
11637: /*
11638: * Don't free the temporary XPath object holding the
11639: * context node, in order to avoid massive recreation
11640: * inside this loop.
11641: */
11642: valuePop(ctxt);
11643: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
11644: } else {
11645: /*
11646: * TODO: The object was lost in the evaluation machinery.
11647: * Can this happen? Maybe in internal-error cases.
11648: */
11649: contextObj = NULL;
11650: }
11651: }
11652:
11653: if (contextObj != NULL) {
11654: if (ctxt->value == contextObj)
11655: valuePop(ctxt);
11656: xmlXPathReleaseObject(xpctxt, contextObj);
11657: }
11658: evaluation_exit:
11659: if (exprRes != NULL)
11660: xmlXPathReleaseObject(ctxt->context, exprRes);
11661: /*
11662: * Reset/invalidate the context.
11663: */
11664: xpctxt->node = oldContextNode;
11665: xpctxt->doc = oldContextDoc;
11666: xpctxt->contextSize = -1;
11667: xpctxt->proximityPosition = -1;
11668: return(newContextSize);
11669: }
11670: return(contextSize);
11671: }
11672:
11673: static int
11674: xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt,
11675: xmlXPathStepOpPtr op,
11676: xmlNodeSetPtr set,
11677: int contextSize,
11678: int minPos,
11679: int maxPos,
11680: int hasNsNodes)
11681: {
11682: if (op->ch1 != -1) {
11683: xmlXPathCompExprPtr comp = ctxt->comp;
11684: if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
11685: /*
11686: * TODO: raise an internal error.
11687: */
11688: }
11689: contextSize = xmlXPathCompOpEvalPredicate(ctxt,
11690: &comp->steps[op->ch1], set, contextSize, hasNsNodes);
11691: CHECK_ERROR0;
11692: if (contextSize <= 0)
11693: return(0);
11694: }
11695: /*
11696: * Check if the node set contains a sufficient number of nodes for
11697: * the requested range.
11698: */
11699: if (contextSize < minPos) {
11700: xmlXPathNodeSetClear(set, hasNsNodes);
11701: return(0);
11702: }
11703: if (op->ch2 == -1) {
11704: /*
11705: * TODO: Can this ever happen?
11706: */
11707: return (contextSize);
11708: } else {
11709: xmlDocPtr oldContextDoc;
11710: int i, pos = 0, newContextSize = 0, contextPos = 0, res;
11711: xmlXPathStepOpPtr exprOp;
11712: xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
11713: xmlNodePtr oldContextNode, contextNode = NULL;
11714: xmlXPathContextPtr xpctxt = ctxt->context;
11715:
11716: #ifdef LIBXML_XPTR_ENABLED
11717: /*
11718: * URGENT TODO: Check the following:
11719: * We don't expect location sets if evaluating prediates, right?
11720: * Only filters should expect location sets, right?
11721: */
11722: #endif /* LIBXML_XPTR_ENABLED */
11723:
11724: /*
11725: * Save old context.
11726: */
11727: oldContextNode = xpctxt->node;
11728: oldContextDoc = xpctxt->doc;
11729: /*
11730: * Get the expression of this predicate.
11731: */
11732: exprOp = &ctxt->comp->steps[op->ch2];
11733: for (i = 0; i < set->nodeNr; i++) {
11734: if (set->nodeTab[i] == NULL)
11735: continue;
11736:
11737: contextNode = set->nodeTab[i];
11738: xpctxt->node = contextNode;
11739: xpctxt->contextSize = contextSize;
11740: xpctxt->proximityPosition = ++contextPos;
11741:
11742: /*
11743: * Initialize the new set.
11744: * Also set the xpath document in case things like
11745: * key() evaluation are attempted on the predicate
11746: */
11747: if ((contextNode->type != XML_NAMESPACE_DECL) &&
11748: (contextNode->doc != NULL))
11749: xpctxt->doc = contextNode->doc;
11750: /*
11751: * Evaluate the predicate expression with 1 context node
11752: * at a time; this node is packaged into a node set; this
11753: * node set is handed over to the evaluation mechanism.
11754: */
11755: if (contextObj == NULL)
11756: contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
11757: else
11758: xmlXPathNodeSetAddUnique(contextObj->nodesetval,
11759: contextNode);
11760:
11761: valuePush(ctxt, contextObj);
11762: res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
11763:
11764: if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
11765: xmlXPathObjectPtr tmp;
11766: /* pop the result */
11767: tmp = valuePop(ctxt);
11768: xmlXPathReleaseObject(xpctxt, tmp);
11769: /* then pop off contextObj, which will be freed later */
11770: valuePop(ctxt);
11771: goto evaluation_error;
11772: }
11773:
11774: if (res)
11775: pos++;
11776:
11777: if (res && (pos >= minPos) && (pos <= maxPos)) {
11778: /*
11779: * Fits in the requested range.
11780: */
11781: newContextSize++;
11782: if (minPos == maxPos) {
11783: /*
11784: * Only 1 node was requested.
11785: */
11786: if (contextNode->type == XML_NAMESPACE_DECL) {
11787: /*
11788: * As always: take care of those nasty
11789: * namespace nodes.
11790: */
11791: set->nodeTab[i] = NULL;
11792: }
11793: xmlXPathNodeSetClear(set, hasNsNodes);
11794: set->nodeNr = 1;
11795: set->nodeTab[0] = contextNode;
11796: goto evaluation_exit;
11797: }
11798: if (pos == maxPos) {
11799: /*
11800: * We are done.
11801: */
11802: xmlXPathNodeSetClearFromPos(set, i +1, hasNsNodes);
11803: goto evaluation_exit;
11804: }
11805: } else {
11806: /*
11807: * Remove the entry from the initial node set.
11808: */
11809: set->nodeTab[i] = NULL;
11810: if (contextNode->type == XML_NAMESPACE_DECL)
11811: xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
11812: }
11813: if (exprRes != NULL) {
11814: xmlXPathReleaseObject(ctxt->context, exprRes);
11815: exprRes = NULL;
11816: }
11817: if (ctxt->value == contextObj) {
11818: /*
11819: * Don't free the temporary XPath object holding the
11820: * context node, in order to avoid massive recreation
11821: * inside this loop.
11822: */
11823: valuePop(ctxt);
11824: xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
11825: } else {
11826: /*
11827: * The object was lost in the evaluation machinery.
11828: * Can this happen? Maybe in case of internal-errors.
11829: */
11830: contextObj = NULL;
11831: }
11832: }
11833: goto evaluation_exit;
11834:
11835: evaluation_error:
11836: xmlXPathNodeSetClear(set, hasNsNodes);
11837: newContextSize = 0;
11838:
11839: evaluation_exit:
11840: if (contextObj != NULL) {
11841: if (ctxt->value == contextObj)
11842: valuePop(ctxt);
11843: xmlXPathReleaseObject(xpctxt, contextObj);
11844: }
11845: if (exprRes != NULL)
11846: xmlXPathReleaseObject(ctxt->context, exprRes);
11847: /*
11848: * Reset/invalidate the context.
11849: */
11850: xpctxt->node = oldContextNode;
11851: xpctxt->doc = oldContextDoc;
11852: xpctxt->contextSize = -1;
11853: xpctxt->proximityPosition = -1;
11854: return(newContextSize);
11855: }
11856: return(contextSize);
11857: }
11858:
11859: static int
11860: xmlXPathIsPositionalPredicate(xmlXPathParserContextPtr ctxt,
11861: xmlXPathStepOpPtr op,
11862: int *maxPos)
11863: {
11864:
11865: xmlXPathStepOpPtr exprOp;
11866:
11867: /*
11868: * BIG NOTE: This is not intended for XPATH_OP_FILTER yet!
11869: */
11870:
11871: /*
11872: * If not -1, then ch1 will point to:
11873: * 1) For predicates (XPATH_OP_PREDICATE):
11874: * - an inner predicate operator
11875: * 2) For filters (XPATH_OP_FILTER):
11876: * - an inner filter operater OR
11877: * - an expression selecting the node set.
11878: * E.g. "key('a', 'b')" or "(//foo | //bar)".
11879: */
11880: if ((op->op != XPATH_OP_PREDICATE) && (op->op != XPATH_OP_FILTER))
11881: return(0);
11882:
11883: if (op->ch2 != -1) {
11884: exprOp = &ctxt->comp->steps[op->ch2];
11885: } else
11886: return(0);
11887:
11888: if ((exprOp != NULL) &&
11889: (exprOp->op == XPATH_OP_VALUE) &&
11890: (exprOp->value4 != NULL) &&
11891: (((xmlXPathObjectPtr) exprOp->value4)->type == XPATH_NUMBER))
11892: {
11893: /*
11894: * We have a "[n]" predicate here.
11895: * TODO: Unfortunately this simplistic test here is not
11896: * able to detect a position() predicate in compound
11897: * expressions like "[@attr = 'a" and position() = 1],
11898: * and even not the usage of position() in
11899: * "[position() = 1]"; thus - obviously - a position-range,
11900: * like it "[position() < 5]", is also not detected.
11901: * Maybe we could rewrite the AST to ease the optimization.
11902: */
11903: *maxPos = (int) ((xmlXPathObjectPtr) exprOp->value4)->floatval;
11904:
11905: if (((xmlXPathObjectPtr) exprOp->value4)->floatval ==
11906: (float) *maxPos)
11907: {
11908: return(1);
11909: }
11910: }
11911: return(0);
11912: }
11913:
11914: static int
11915: xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
11916: xmlXPathStepOpPtr op,
11917: xmlNodePtr * first, xmlNodePtr * last,
11918: int toBool)
11919: {
11920:
11921: #define XP_TEST_HIT \
11922: if (hasAxisRange != 0) { \
11923: if (++pos == maxPos) { \
11924: addNode(seq, cur); \
11925: goto axis_range_end; } \
11926: } else { \
11927: addNode(seq, cur); \
11928: if (breakOnFirstHit) goto first_hit; }
11929:
11930: #define XP_TEST_HIT_NS \
11931: if (hasAxisRange != 0) { \
11932: if (++pos == maxPos) { \
11933: hasNsNodes = 1; \
11934: xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur); \
11935: goto axis_range_end; } \
11936: } else { \
11937: hasNsNodes = 1; \
11938: xmlXPathNodeSetAddNs(seq, \
11939: xpctxt->node, (xmlNsPtr) cur); \
11940: if (breakOnFirstHit) goto first_hit; }
11941:
11942: xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
11943: xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
11944: xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
11945: const xmlChar *prefix = op->value4;
11946: const xmlChar *name = op->value5;
11947: const xmlChar *URI = NULL;
11948:
11949: #ifdef DEBUG_STEP
11950: int nbMatches = 0, prevMatches = 0;
11951: #endif
11952: int total = 0, hasNsNodes = 0;
11953: /* The popped object holding the context nodes */
11954: xmlXPathObjectPtr obj;
11955: /* The set of context nodes for the node tests */
11956: xmlNodeSetPtr contextSeq;
11957: int contextIdx;
11958: xmlNodePtr contextNode;
11959: /* The context node for a compound traversal */
11960: xmlNodePtr outerContextNode;
11961: /* The final resulting node set wrt to all context nodes */
11962: xmlNodeSetPtr outSeq;
11963: /*
11964: * The temporary resulting node set wrt 1 context node.
11965: * Used to feed predicate evaluation.
11966: */
11967: xmlNodeSetPtr seq;
11968: xmlNodePtr cur;
11969: /* First predicate operator */
11970: xmlXPathStepOpPtr predOp;
11971: int maxPos; /* The requested position() (when a "[n]" predicate) */
11972: int hasPredicateRange, hasAxisRange, pos, size, newSize;
11973: int breakOnFirstHit;
11974:
11975: xmlXPathTraversalFunction next = NULL;
11976: /* compound axis traversal */
11977: xmlXPathTraversalFunctionExt outerNext = NULL;
11978: void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
11979: xmlXPathNodeSetMergeFunction mergeAndClear;
11980: xmlNodePtr oldContextNode;
11981: xmlXPathContextPtr xpctxt = ctxt->context;
11982:
11983:
11984: CHECK_TYPE0(XPATH_NODESET);
11985: obj = valuePop(ctxt);
11986: /*
11987: * Setup namespaces.
11988: */
11989: if (prefix != NULL) {
11990: URI = xmlXPathNsLookup(xpctxt, prefix);
11991: if (URI == NULL) {
11992: xmlXPathReleaseObject(xpctxt, obj);
11993: XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
11994: }
11995: }
11996: /*
11997: * Setup axis.
11998: *
11999: * MAYBE FUTURE TODO: merging optimizations:
12000: * - If the nodes to be traversed wrt to the initial nodes and
12001: * the current axis cannot overlap, then we could avoid searching
12002: * for duplicates during the merge.
12003: * But the question is how/when to evaluate if they cannot overlap.
12004: * Example: if we know that for two initial nodes, the one is
12005: * not in the ancestor-or-self axis of the other, then we could safely
12006: * avoid a duplicate-aware merge, if the axis to be traversed is e.g.
12007: * the descendant-or-self axis.
12008: */
12009: mergeAndClear = xmlXPathNodeSetMergeAndClear;
12010: switch (axis) {
12011: case AXIS_ANCESTOR:
12012: first = NULL;
12013: next = xmlXPathNextAncestor;
12014: break;
12015: case AXIS_ANCESTOR_OR_SELF:
12016: first = NULL;
12017: next = xmlXPathNextAncestorOrSelf;
12018: break;
12019: case AXIS_ATTRIBUTE:
12020: first = NULL;
12021: last = NULL;
12022: next = xmlXPathNextAttribute;
12023: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12024: break;
12025: case AXIS_CHILD:
12026: last = NULL;
12027: if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) {
12028: /*
12029: * This iterator will give us only nodes which can
12030: * hold element nodes.
12031: */
12032: outerNext = xmlXPathNextDescendantOrSelfElemParent;
12033: }
12034: if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) &&
12035: (type == NODE_TYPE_NODE))
12036: {
12037: /*
12038: * Optimization if an element node type is 'element'.
12039: */
12040: next = xmlXPathNextChildElement;
12041: } else
12042: next = xmlXPathNextChild;
12043: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12044: break;
12045: case AXIS_DESCENDANT:
12046: last = NULL;
12047: next = xmlXPathNextDescendant;
12048: break;
12049: case AXIS_DESCENDANT_OR_SELF:
12050: last = NULL;
12051: next = xmlXPathNextDescendantOrSelf;
12052: break;
12053: case AXIS_FOLLOWING:
12054: last = NULL;
12055: next = xmlXPathNextFollowing;
12056: break;
12057: case AXIS_FOLLOWING_SIBLING:
12058: last = NULL;
12059: next = xmlXPathNextFollowingSibling;
12060: break;
12061: case AXIS_NAMESPACE:
12062: first = NULL;
12063: last = NULL;
12064: next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
12065: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12066: break;
12067: case AXIS_PARENT:
12068: first = NULL;
12069: next = xmlXPathNextParent;
12070: break;
12071: case AXIS_PRECEDING:
12072: first = NULL;
12073: next = xmlXPathNextPrecedingInternal;
12074: break;
12075: case AXIS_PRECEDING_SIBLING:
12076: first = NULL;
12077: next = xmlXPathNextPrecedingSibling;
12078: break;
12079: case AXIS_SELF:
12080: first = NULL;
12081: last = NULL;
12082: next = xmlXPathNextSelf;
12083: mergeAndClear = xmlXPathNodeSetMergeAndClearNoDupls;
12084: break;
12085: }
12086:
12087: #ifdef DEBUG_STEP
12088: xmlXPathDebugDumpStepAxis(op,
12089: (obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
12090: #endif
12091:
12092: if (next == NULL) {
12093: xmlXPathReleaseObject(xpctxt, obj);
12094: return(0);
12095: }
12096: contextSeq = obj->nodesetval;
12097: if ((contextSeq == NULL) || (contextSeq->nodeNr <= 0)) {
12098: xmlXPathReleaseObject(xpctxt, obj);
12099: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
12100: return(0);
12101: }
12102: /*
12103: * Predicate optimization ---------------------------------------------
12104: * If this step has a last predicate, which contains a position(),
12105: * then we'll optimize (although not exactly "position()", but only
12106: * the short-hand form, i.e., "[n]".
12107: *
12108: * Example - expression "/foo[parent::bar][1]":
12109: *
12110: * COLLECT 'child' 'name' 'node' foo -- op (we are here)
12111: * ROOT -- op->ch1
12112: * PREDICATE -- op->ch2 (predOp)
12113: * PREDICATE -- predOp->ch1 = [parent::bar]
12114: * SORT
12115: * COLLECT 'parent' 'name' 'node' bar
12116: * NODE
12117: * ELEM Object is a number : 1 -- predOp->ch2 = [1]
12118: *
12119: */
12120: maxPos = 0;
12121: predOp = NULL;
12122: hasPredicateRange = 0;
12123: hasAxisRange = 0;
12124: if (op->ch2 != -1) {
12125: /*
12126: * There's at least one predicate. 16 == XPATH_OP_PREDICATE
12127: */
12128: predOp = &ctxt->comp->steps[op->ch2];
12129: if (xmlXPathIsPositionalPredicate(ctxt, predOp, &maxPos)) {
12130: if (predOp->ch1 != -1) {
12131: /*
12132: * Use the next inner predicate operator.
12133: */
12134: predOp = &ctxt->comp->steps[predOp->ch1];
12135: hasPredicateRange = 1;
12136: } else {
12137: /*
12138: * There's no other predicate than the [n] predicate.
12139: */
12140: predOp = NULL;
12141: hasAxisRange = 1;
12142: }
12143: }
12144: }
12145: breakOnFirstHit = ((toBool) && (predOp == NULL)) ? 1 : 0;
12146: /*
12147: * Axis traversal -----------------------------------------------------
12148: */
12149: /*
12150: * 2.3 Node Tests
12151: * - For the attribute axis, the principal node type is attribute.
12152: * - For the namespace axis, the principal node type is namespace.
12153: * - For other axes, the principal node type is element.
12154: *
12155: * A node test * is true for any node of the
12156: * principal node type. For example, child::* will
12157: * select all element children of the context node
12158: */
12159: oldContextNode = xpctxt->node;
12160: addNode = xmlXPathNodeSetAddUnique;
12161: outSeq = NULL;
12162: seq = NULL;
12163: outerContextNode = NULL;
12164: contextNode = NULL;
12165: contextIdx = 0;
12166:
12167:
12168: while ((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) {
12169: if (outerNext != NULL) {
12170: /*
12171: * This is a compound traversal.
12172: */
12173: if (contextNode == NULL) {
12174: /*
12175: * Set the context for the outer traversal.
12176: */
12177: outerContextNode = contextSeq->nodeTab[contextIdx++];
12178: contextNode = outerNext(NULL, outerContextNode);
12179: } else
12180: contextNode = outerNext(contextNode, outerContextNode);
12181: if (contextNode == NULL)
12182: continue;
12183: /*
12184: * Set the context for the main traversal.
12185: */
12186: xpctxt->node = contextNode;
12187: } else
12188: xpctxt->node = contextSeq->nodeTab[contextIdx++];
12189:
12190: if (seq == NULL) {
12191: seq = xmlXPathNodeSetCreate(NULL);
12192: if (seq == NULL) {
12193: total = 0;
12194: goto error;
12195: }
12196: }
12197: /*
12198: * Traverse the axis and test the nodes.
12199: */
12200: pos = 0;
12201: cur = NULL;
12202: hasNsNodes = 0;
12203: do {
12204: cur = next(ctxt, cur);
12205: if (cur == NULL)
12206: break;
12207:
12208: /*
12209: * QUESTION TODO: What does the "first" and "last" stuff do?
12210: */
12211: if ((first != NULL) && (*first != NULL)) {
12212: if (*first == cur)
12213: break;
12214: if (((total % 256) == 0) &&
12215: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12216: (xmlXPathCmpNodesExt(*first, cur) >= 0))
12217: #else
12218: (xmlXPathCmpNodes(*first, cur) >= 0))
12219: #endif
12220: {
12221: break;
12222: }
12223: }
12224: if ((last != NULL) && (*last != NULL)) {
12225: if (*last == cur)
12226: break;
12227: if (((total % 256) == 0) &&
12228: #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
12229: (xmlXPathCmpNodesExt(cur, *last) >= 0))
12230: #else
12231: (xmlXPathCmpNodes(cur, *last) >= 0))
12232: #endif
12233: {
12234: break;
12235: }
12236: }
12237:
12238: total++;
12239:
12240: #ifdef DEBUG_STEP
12241: xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
12242: #endif
12243:
12244: switch (test) {
12245: case NODE_TEST_NONE:
12246: total = 0;
12247: STRANGE
12248: goto error;
12249: case NODE_TEST_TYPE:
12250: /*
12251: * TODO: Don't we need to use
12252: * xmlXPathNodeSetAddNs() for namespace nodes here?
12253: * Surprisingly, some c14n tests fail, if we do this.
12254: */
12255: if (type == NODE_TYPE_NODE) {
12256: switch (cur->type) {
12257: case XML_DOCUMENT_NODE:
12258: case XML_HTML_DOCUMENT_NODE:
12259: #ifdef LIBXML_DOCB_ENABLED
12260: case XML_DOCB_DOCUMENT_NODE:
12261: #endif
12262: case XML_ELEMENT_NODE:
12263: case XML_ATTRIBUTE_NODE:
12264: case XML_PI_NODE:
12265: case XML_COMMENT_NODE:
12266: case XML_CDATA_SECTION_NODE:
12267: case XML_TEXT_NODE:
12268: case XML_NAMESPACE_DECL:
12269: XP_TEST_HIT
12270: break;
12271: default:
12272: break;
12273: }
12274: } else if (cur->type == type) {
12275: if (type == XML_NAMESPACE_DECL)
12276: XP_TEST_HIT_NS
12277: else
12278: XP_TEST_HIT
12279: } else if ((type == NODE_TYPE_TEXT) &&
12280: (cur->type == XML_CDATA_SECTION_NODE))
12281: {
12282: XP_TEST_HIT
12283: }
12284: break;
12285: case NODE_TEST_PI:
12286: if ((cur->type == XML_PI_NODE) &&
12287: ((name == NULL) || xmlStrEqual(name, cur->name)))
12288: {
12289: XP_TEST_HIT
12290: }
12291: break;
12292: case NODE_TEST_ALL:
12293: if (axis == AXIS_ATTRIBUTE) {
12294: if (cur->type == XML_ATTRIBUTE_NODE)
12295: {
12296: XP_TEST_HIT
12297: }
12298: } else if (axis == AXIS_NAMESPACE) {
12299: if (cur->type == XML_NAMESPACE_DECL)
12300: {
12301: XP_TEST_HIT_NS
12302: }
12303: } else {
12304: if (cur->type == XML_ELEMENT_NODE) {
12305: if (prefix == NULL)
12306: {
12307: XP_TEST_HIT
12308:
12309: } else if ((cur->ns != NULL) &&
12310: (xmlStrEqual(URI, cur->ns->href)))
12311: {
12312: XP_TEST_HIT
12313: }
12314: }
12315: }
12316: break;
12317: case NODE_TEST_NS:{
12318: TODO;
12319: break;
12320: }
12321: case NODE_TEST_NAME:
12322: if (axis == AXIS_ATTRIBUTE) {
12323: if (cur->type != XML_ATTRIBUTE_NODE)
12324: break;
12325: } else if (axis == AXIS_NAMESPACE) {
12326: if (cur->type != XML_NAMESPACE_DECL)
12327: break;
12328: } else {
12329: if (cur->type != XML_ELEMENT_NODE)
12330: break;
12331: }
12332: switch (cur->type) {
12333: case XML_ELEMENT_NODE:
12334: if (xmlStrEqual(name, cur->name)) {
12335: if (prefix == NULL) {
12336: if (cur->ns == NULL)
12337: {
12338: XP_TEST_HIT
12339: }
12340: } else {
12341: if ((cur->ns != NULL) &&
12342: (xmlStrEqual(URI, cur->ns->href)))
12343: {
12344: XP_TEST_HIT
12345: }
12346: }
12347: }
12348: break;
12349: case XML_ATTRIBUTE_NODE:{
12350: xmlAttrPtr attr = (xmlAttrPtr) cur;
12351:
12352: if (xmlStrEqual(name, attr->name)) {
12353: if (prefix == NULL) {
12354: if ((attr->ns == NULL) ||
12355: (attr->ns->prefix == NULL))
12356: {
12357: XP_TEST_HIT
12358: }
12359: } else {
12360: if ((attr->ns != NULL) &&
12361: (xmlStrEqual(URI,
12362: attr->ns->href)))
12363: {
12364: XP_TEST_HIT
12365: }
12366: }
12367: }
12368: break;
12369: }
12370: case XML_NAMESPACE_DECL:
12371: if (cur->type == XML_NAMESPACE_DECL) {
12372: xmlNsPtr ns = (xmlNsPtr) cur;
12373:
12374: if ((ns->prefix != NULL) && (name != NULL)
12375: && (xmlStrEqual(ns->prefix, name)))
12376: {
12377: XP_TEST_HIT_NS
12378: }
12379: }
12380: break;
12381: default:
12382: break;
12383: }
12384: break;
12385: } /* switch(test) */
12386: } while (cur != NULL);
12387:
12388: goto apply_predicates;
12389:
12390: axis_range_end: /* ----------------------------------------------------- */
12391: /*
12392: * We have a "/foo[n]", and position() = n was reached.
12393: * Note that we can have as well "/foo/::parent::foo[1]", so
12394: * a duplicate-aware merge is still needed.
12395: * Merge with the result.
12396: */
12397: if (outSeq == NULL) {
12398: outSeq = seq;
12399: seq = NULL;
12400: } else
12401: outSeq = mergeAndClear(outSeq, seq, 0);
12402: /*
12403: * Break if only a true/false result was requested.
12404: */
12405: if (toBool)
12406: break;
12407: continue;
12408:
12409: first_hit: /* ---------------------------------------------------------- */
12410: /*
12411: * Break if only a true/false result was requested and
12412: * no predicates existed and a node test succeeded.
12413: */
12414: if (outSeq == NULL) {
12415: outSeq = seq;
12416: seq = NULL;
12417: } else
12418: outSeq = mergeAndClear(outSeq, seq, 0);
12419: break;
12420:
12421: #ifdef DEBUG_STEP
12422: if (seq != NULL)
12423: nbMatches += seq->nodeNr;
12424: #endif
12425:
12426: apply_predicates: /* --------------------------------------------------- */
12427: /*
12428: * Apply predicates.
12429: */
12430: if ((predOp != NULL) && (seq->nodeNr > 0)) {
12431: /*
12432: * E.g. when we have a "/foo[some expression][n]".
12433: */
12434: /*
12435: * QUESTION TODO: The old predicate evaluation took into
12436: * account location-sets.
12437: * (E.g. ctxt->value->type == XPATH_LOCATIONSET)
12438: * Do we expect such a set here?
12439: * All what I learned now from the evaluation semantics
12440: * does not indicate that a location-set will be processed
12441: * here, so this looks OK.
12442: */
12443: /*
12444: * Iterate over all predicates, starting with the outermost
12445: * predicate.
12446: * TODO: Problem: we cannot execute the inner predicates first
12447: * since we cannot go back *up* the operator tree!
12448: * Options we have:
12449: * 1) Use of recursive functions (like is it currently done
12450: * via xmlXPathCompOpEval())
12451: * 2) Add a predicate evaluation information stack to the
12452: * context struct
12453: * 3) Change the way the operators are linked; we need a
12454: * "parent" field on xmlXPathStepOp
12455: *
12456: * For the moment, I'll try to solve this with a recursive
12457: * function: xmlXPathCompOpEvalPredicate().
12458: */
12459: size = seq->nodeNr;
12460: if (hasPredicateRange != 0)
12461: newSize = xmlXPathCompOpEvalPositionalPredicate(ctxt,
12462: predOp, seq, size, maxPos, maxPos, hasNsNodes);
12463: else
12464: newSize = xmlXPathCompOpEvalPredicate(ctxt,
12465: predOp, seq, size, hasNsNodes);
12466:
12467: if (ctxt->error != XPATH_EXPRESSION_OK) {
12468: total = 0;
12469: goto error;
12470: }
12471: /*
12472: * Add the filtered set of nodes to the result node set.
12473: */
12474: if (newSize == 0) {
12475: /*
12476: * The predicates filtered all nodes out.
12477: */
12478: xmlXPathNodeSetClear(seq, hasNsNodes);
12479: } else if (seq->nodeNr > 0) {
12480: /*
12481: * Add to result set.
12482: */
12483: if (outSeq == NULL) {
12484: if (size != newSize) {
12485: /*
12486: * We need to merge and clear here, since
12487: * the sequence will contained NULLed entries.
12488: */
12489: outSeq = mergeAndClear(NULL, seq, 1);
12490: } else {
12491: outSeq = seq;
12492: seq = NULL;
12493: }
12494: } else
12495: outSeq = mergeAndClear(outSeq, seq,
12496: (size != newSize) ? 1: 0);
12497: /*
12498: * Break if only a true/false result was requested.
12499: */
12500: if (toBool)
12501: break;
12502: }
12503: } else if (seq->nodeNr > 0) {
12504: /*
12505: * Add to result set.
12506: */
12507: if (outSeq == NULL) {
12508: outSeq = seq;
12509: seq = NULL;
12510: } else {
12511: outSeq = mergeAndClear(outSeq, seq, 0);
12512: }
12513: }
12514: }
12515:
12516: error:
12517: if ((obj->boolval) && (obj->user != NULL)) {
12518: /*
12519: * QUESTION TODO: What does this do and why?
12520: * TODO: Do we have to do this also for the "error"
12521: * cleanup further down?
12522: */
12523: ctxt->value->boolval = 1;
12524: ctxt->value->user = obj->user;
12525: obj->user = NULL;
12526: obj->boolval = 0;
12527: }
12528: xmlXPathReleaseObject(xpctxt, obj);
12529:
12530: /*
12531: * Ensure we return at least an emtpy set.
12532: */
12533: if (outSeq == NULL) {
12534: if ((seq != NULL) && (seq->nodeNr == 0))
12535: outSeq = seq;
12536: else
12537: outSeq = xmlXPathNodeSetCreate(NULL);
12538: /* XXX what if xmlXPathNodeSetCreate returned NULL here? */
12539: }
12540: if ((seq != NULL) && (seq != outSeq)) {
12541: xmlXPathFreeNodeSet(seq);
12542: }
12543: /*
12544: * Hand over the result. Better to push the set also in
12545: * case of errors.
12546: */
12547: valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, outSeq));
12548: /*
12549: * Reset the context node.
12550: */
12551: xpctxt->node = oldContextNode;
12552:
12553: #ifdef DEBUG_STEP
12554: xmlGenericError(xmlGenericErrorContext,
12555: "\nExamined %d nodes, found %d nodes at that step\n",
12556: total, nbMatches);
12557: #endif
12558:
12559: return(total);
12560: }
12561:
12562: static int
12563: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12564: xmlXPathStepOpPtr op, xmlNodePtr * first);
12565:
12566: /**
12567: * xmlXPathCompOpEvalFirst:
12568: * @ctxt: the XPath parser context with the compiled expression
12569: * @op: an XPath compiled operation
12570: * @first: the first elem found so far
12571: *
12572: * Evaluate the Precompiled XPath operation searching only the first
12573: * element in document order
12574: *
12575: * Returns the number of examined objects.
12576: */
12577: static int
12578: xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
12579: xmlXPathStepOpPtr op, xmlNodePtr * first)
12580: {
12581: int total = 0, cur;
12582: xmlXPathCompExprPtr comp;
12583: xmlXPathObjectPtr arg1, arg2;
12584:
12585: CHECK_ERROR0;
12586: comp = ctxt->comp;
12587: switch (op->op) {
12588: case XPATH_OP_END:
12589: return (0);
12590: case XPATH_OP_UNION:
12591: total =
12592: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12593: first);
12594: CHECK_ERROR0;
12595: if ((ctxt->value != NULL)
12596: && (ctxt->value->type == XPATH_NODESET)
12597: && (ctxt->value->nodesetval != NULL)
12598: && (ctxt->value->nodesetval->nodeNr >= 1)) {
12599: /*
12600: * limit tree traversing to first node in the result
12601: */
12602: /*
12603: * OPTIMIZE TODO: This implicitely sorts
12604: * the result, even if not needed. E.g. if the argument
12605: * of the count() function, no sorting is needed.
12606: * OPTIMIZE TODO: How do we know if the node-list wasn't
12607: * aready sorted?
12608: */
12609: if (ctxt->value->nodesetval->nodeNr > 1)
12610: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12611: *first = ctxt->value->nodesetval->nodeTab[0];
12612: }
12613: cur =
12614: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
12615: first);
12616: CHECK_ERROR0;
12617: CHECK_TYPE0(XPATH_NODESET);
12618: arg2 = valuePop(ctxt);
12619:
12620: CHECK_TYPE0(XPATH_NODESET);
12621: arg1 = valuePop(ctxt);
12622:
12623: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12624: arg2->nodesetval);
12625: valuePush(ctxt, arg1);
12626: xmlXPathReleaseObject(ctxt->context, arg2);
12627: /* optimizer */
12628: if (total > cur)
12629: xmlXPathCompSwap(op);
12630: return (total + cur);
12631: case XPATH_OP_ROOT:
12632: xmlXPathRoot(ctxt);
12633: return (0);
12634: case XPATH_OP_NODE:
12635: if (op->ch1 != -1)
12636: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12637: CHECK_ERROR0;
12638: if (op->ch2 != -1)
12639: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12640: CHECK_ERROR0;
12641: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12642: ctxt->context->node));
12643: return (total);
12644: case XPATH_OP_RESET:
12645: if (op->ch1 != -1)
12646: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12647: CHECK_ERROR0;
12648: if (op->ch2 != -1)
12649: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12650: CHECK_ERROR0;
12651: ctxt->context->node = NULL;
12652: return (total);
12653: case XPATH_OP_COLLECT:{
12654: if (op->ch1 == -1)
12655: return (total);
12656:
12657: total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12658: CHECK_ERROR0;
12659:
12660: total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
12661: return (total);
12662: }
12663: case XPATH_OP_VALUE:
12664: valuePush(ctxt,
12665: xmlXPathCacheObjectCopy(ctxt->context,
12666: (xmlXPathObjectPtr) op->value4));
12667: return (0);
12668: case XPATH_OP_SORT:
12669: if (op->ch1 != -1)
12670: total +=
12671: xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
12672: first);
12673: CHECK_ERROR0;
12674: if ((ctxt->value != NULL)
12675: && (ctxt->value->type == XPATH_NODESET)
12676: && (ctxt->value->nodesetval != NULL)
12677: && (ctxt->value->nodesetval->nodeNr > 1))
12678: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12679: return (total);
12680: #ifdef XP_OPTIMIZED_FILTER_FIRST
12681: case XPATH_OP_FILTER:
12682: total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
12683: return (total);
12684: #endif
12685: default:
12686: return (xmlXPathCompOpEval(ctxt, op));
12687: }
12688: }
12689:
12690: /**
12691: * xmlXPathCompOpEvalLast:
12692: * @ctxt: the XPath parser context with the compiled expression
12693: * @op: an XPath compiled operation
12694: * @last: the last elem found so far
12695: *
12696: * Evaluate the Precompiled XPath operation searching only the last
12697: * element in document order
12698: *
12699: * Returns the number of nodes traversed
12700: */
12701: static int
12702: xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
12703: xmlNodePtr * last)
12704: {
12705: int total = 0, cur;
12706: xmlXPathCompExprPtr comp;
12707: xmlXPathObjectPtr arg1, arg2;
12708: xmlNodePtr bak;
12709: xmlDocPtr bakd;
12710: int pp;
12711: int cs;
12712:
12713: CHECK_ERROR0;
12714: comp = ctxt->comp;
12715: switch (op->op) {
12716: case XPATH_OP_END:
12717: return (0);
12718: case XPATH_OP_UNION:
12719: bakd = ctxt->context->doc;
12720: bak = ctxt->context->node;
12721: pp = ctxt->context->proximityPosition;
12722: cs = ctxt->context->contextSize;
12723: total =
12724: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
12725: CHECK_ERROR0;
12726: if ((ctxt->value != NULL)
12727: && (ctxt->value->type == XPATH_NODESET)
12728: && (ctxt->value->nodesetval != NULL)
12729: && (ctxt->value->nodesetval->nodeNr >= 1)) {
12730: /*
12731: * limit tree traversing to first node in the result
12732: */
12733: if (ctxt->value->nodesetval->nodeNr > 1)
12734: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12735: *last =
12736: ctxt->value->nodesetval->nodeTab[ctxt->value->
12737: nodesetval->nodeNr -
12738: 1];
12739: }
12740: ctxt->context->doc = bakd;
12741: ctxt->context->node = bak;
12742: ctxt->context->proximityPosition = pp;
12743: ctxt->context->contextSize = cs;
12744: cur =
12745: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
12746: CHECK_ERROR0;
12747: if ((ctxt->value != NULL)
12748: && (ctxt->value->type == XPATH_NODESET)
12749: && (ctxt->value->nodesetval != NULL)
12750: && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
12751: }
12752: CHECK_TYPE0(XPATH_NODESET);
12753: arg2 = valuePop(ctxt);
12754:
12755: CHECK_TYPE0(XPATH_NODESET);
12756: arg1 = valuePop(ctxt);
12757:
12758: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
12759: arg2->nodesetval);
12760: valuePush(ctxt, arg1);
12761: xmlXPathReleaseObject(ctxt->context, arg2);
12762: /* optimizer */
12763: if (total > cur)
12764: xmlXPathCompSwap(op);
12765: return (total + cur);
12766: case XPATH_OP_ROOT:
12767: xmlXPathRoot(ctxt);
12768: return (0);
12769: case XPATH_OP_NODE:
12770: if (op->ch1 != -1)
12771: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12772: CHECK_ERROR0;
12773: if (op->ch2 != -1)
12774: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12775: CHECK_ERROR0;
12776: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
12777: ctxt->context->node));
12778: return (total);
12779: case XPATH_OP_RESET:
12780: if (op->ch1 != -1)
12781: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12782: CHECK_ERROR0;
12783: if (op->ch2 != -1)
12784: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12785: CHECK_ERROR0;
12786: ctxt->context->node = NULL;
12787: return (total);
12788: case XPATH_OP_COLLECT:{
12789: if (op->ch1 == -1)
12790: return (0);
12791:
12792: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12793: CHECK_ERROR0;
12794:
12795: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
12796: return (total);
12797: }
12798: case XPATH_OP_VALUE:
12799: valuePush(ctxt,
12800: xmlXPathCacheObjectCopy(ctxt->context,
12801: (xmlXPathObjectPtr) op->value4));
12802: return (0);
12803: case XPATH_OP_SORT:
12804: if (op->ch1 != -1)
12805: total +=
12806: xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
12807: last);
12808: CHECK_ERROR0;
12809: if ((ctxt->value != NULL)
12810: && (ctxt->value->type == XPATH_NODESET)
12811: && (ctxt->value->nodesetval != NULL)
12812: && (ctxt->value->nodesetval->nodeNr > 1))
12813: xmlXPathNodeSetSort(ctxt->value->nodesetval);
12814: return (total);
12815: default:
12816: return (xmlXPathCompOpEval(ctxt, op));
12817: }
12818: }
12819:
12820: #ifdef XP_OPTIMIZED_FILTER_FIRST
12821: static int
12822: xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
12823: xmlXPathStepOpPtr op, xmlNodePtr * first)
12824: {
12825: int total = 0;
12826: xmlXPathCompExprPtr comp;
12827: xmlXPathObjectPtr res;
12828: xmlXPathObjectPtr obj;
12829: xmlNodeSetPtr oldset;
12830: xmlNodePtr oldnode;
12831: xmlDocPtr oldDoc;
12832: int i;
12833:
12834: CHECK_ERROR0;
12835: comp = ctxt->comp;
12836: /*
12837: * Optimization for ()[last()] selection i.e. the last elem
12838: */
12839: if ((op->ch1 != -1) && (op->ch2 != -1) &&
12840: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
12841: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
12842: int f = comp->steps[op->ch2].ch1;
12843:
12844: if ((f != -1) &&
12845: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
12846: (comp->steps[f].value5 == NULL) &&
12847: (comp->steps[f].value == 0) &&
12848: (comp->steps[f].value4 != NULL) &&
12849: (xmlStrEqual
12850: (comp->steps[f].value4, BAD_CAST "last"))) {
12851: xmlNodePtr last = NULL;
12852:
12853: total +=
12854: xmlXPathCompOpEvalLast(ctxt,
12855: &comp->steps[op->ch1],
12856: &last);
12857: CHECK_ERROR0;
12858: /*
12859: * The nodeset should be in document order,
12860: * Keep only the last value
12861: */
12862: if ((ctxt->value != NULL) &&
12863: (ctxt->value->type == XPATH_NODESET) &&
12864: (ctxt->value->nodesetval != NULL) &&
12865: (ctxt->value->nodesetval->nodeTab != NULL) &&
12866: (ctxt->value->nodesetval->nodeNr > 1)) {
12867: ctxt->value->nodesetval->nodeTab[0] =
12868: ctxt->value->nodesetval->nodeTab[ctxt->
12869: value->
12870: nodesetval->
12871: nodeNr -
12872: 1];
12873: ctxt->value->nodesetval->nodeNr = 1;
12874: *first = *(ctxt->value->nodesetval->nodeTab);
12875: }
12876: return (total);
12877: }
12878: }
12879:
12880: if (op->ch1 != -1)
12881: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
12882: CHECK_ERROR0;
12883: if (op->ch2 == -1)
12884: return (total);
12885: if (ctxt->value == NULL)
12886: return (total);
12887:
12888: #ifdef LIBXML_XPTR_ENABLED
12889: oldnode = ctxt->context->node;
12890: /*
12891: * Hum are we filtering the result of an XPointer expression
12892: */
12893: if (ctxt->value->type == XPATH_LOCATIONSET) {
12894: xmlXPathObjectPtr tmp = NULL;
12895: xmlLocationSetPtr newlocset = NULL;
12896: xmlLocationSetPtr oldlocset;
12897:
12898: /*
12899: * Extract the old locset, and then evaluate the result of the
12900: * expression for all the element in the locset. use it to grow
12901: * up a new locset.
12902: */
12903: CHECK_TYPE0(XPATH_LOCATIONSET);
12904: obj = valuePop(ctxt);
12905: oldlocset = obj->user;
12906: ctxt->context->node = NULL;
12907:
12908: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
12909: ctxt->context->contextSize = 0;
12910: ctxt->context->proximityPosition = 0;
12911: if (op->ch2 != -1)
12912: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12913: res = valuePop(ctxt);
12914: if (res != NULL) {
12915: xmlXPathReleaseObject(ctxt->context, res);
12916: }
12917: valuePush(ctxt, obj);
12918: CHECK_ERROR0;
12919: return (total);
12920: }
12921: newlocset = xmlXPtrLocationSetCreate(NULL);
12922:
12923: for (i = 0; i < oldlocset->locNr; i++) {
12924: /*
12925: * Run the evaluation with a node list made of a
12926: * single item in the nodelocset.
12927: */
12928: ctxt->context->node = oldlocset->locTab[i]->user;
12929: ctxt->context->contextSize = oldlocset->locNr;
12930: ctxt->context->proximityPosition = i + 1;
12931: if (tmp == NULL) {
12932: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
12933: ctxt->context->node);
12934: } else {
12935: xmlXPathNodeSetAddUnique(tmp->nodesetval,
12936: ctxt->context->node);
12937: }
12938: valuePush(ctxt, tmp);
12939: if (op->ch2 != -1)
12940: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
12941: if (ctxt->error != XPATH_EXPRESSION_OK) {
12942: xmlXPathFreeObject(obj);
12943: return(0);
12944: }
12945: /*
12946: * The result of the evaluation need to be tested to
12947: * decided whether the filter succeeded or not
12948: */
12949: res = valuePop(ctxt);
12950: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
12951: xmlXPtrLocationSetAdd(newlocset,
12952: xmlXPathCacheObjectCopy(ctxt->context,
12953: oldlocset->locTab[i]));
12954: }
12955: /*
12956: * Cleanup
12957: */
12958: if (res != NULL) {
12959: xmlXPathReleaseObject(ctxt->context, res);
12960: }
12961: if (ctxt->value == tmp) {
12962: valuePop(ctxt);
12963: xmlXPathNodeSetClear(tmp->nodesetval, 1);
12964: /*
12965: * REVISIT TODO: Don't create a temporary nodeset
12966: * for everly iteration.
12967: */
12968: /* OLD: xmlXPathFreeObject(res); */
12969: } else
12970: tmp = NULL;
12971: ctxt->context->node = NULL;
12972: /*
12973: * Only put the first node in the result, then leave.
12974: */
12975: if (newlocset->locNr > 0) {
12976: *first = (xmlNodePtr) oldlocset->locTab[i]->user;
12977: break;
12978: }
12979: }
12980: if (tmp != NULL) {
12981: xmlXPathReleaseObject(ctxt->context, tmp);
12982: }
12983: /*
12984: * The result is used as the new evaluation locset.
12985: */
12986: xmlXPathReleaseObject(ctxt->context, obj);
12987: ctxt->context->node = NULL;
12988: ctxt->context->contextSize = -1;
12989: ctxt->context->proximityPosition = -1;
12990: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
12991: ctxt->context->node = oldnode;
12992: return (total);
12993: }
12994: #endif /* LIBXML_XPTR_ENABLED */
12995:
12996: /*
12997: * Extract the old set, and then evaluate the result of the
12998: * expression for all the element in the set. use it to grow
12999: * up a new set.
13000: */
13001: CHECK_TYPE0(XPATH_NODESET);
13002: obj = valuePop(ctxt);
13003: oldset = obj->nodesetval;
13004:
13005: oldnode = ctxt->context->node;
13006: oldDoc = ctxt->context->doc;
13007: ctxt->context->node = NULL;
13008:
13009: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
13010: ctxt->context->contextSize = 0;
13011: ctxt->context->proximityPosition = 0;
13012: /* QUESTION TODO: Why was this code commented out?
13013: if (op->ch2 != -1)
13014: total +=
13015: xmlXPathCompOpEval(ctxt,
13016: &comp->steps[op->ch2]);
13017: CHECK_ERROR0;
13018: res = valuePop(ctxt);
13019: if (res != NULL)
13020: xmlXPathFreeObject(res);
13021: */
13022: valuePush(ctxt, obj);
13023: ctxt->context->node = oldnode;
13024: CHECK_ERROR0;
13025: } else {
13026: xmlNodeSetPtr newset;
13027: xmlXPathObjectPtr tmp = NULL;
13028: /*
13029: * Initialize the new set.
13030: * Also set the xpath document in case things like
13031: * key() evaluation are attempted on the predicate
13032: */
13033: newset = xmlXPathNodeSetCreate(NULL);
13034: /* XXX what if xmlXPathNodeSetCreate returned NULL? */
13035:
13036: for (i = 0; i < oldset->nodeNr; i++) {
13037: /*
13038: * Run the evaluation with a node list made of
13039: * a single item in the nodeset.
13040: */
13041: ctxt->context->node = oldset->nodeTab[i];
13042: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
13043: (oldset->nodeTab[i]->doc != NULL))
13044: ctxt->context->doc = oldset->nodeTab[i]->doc;
13045: if (tmp == NULL) {
13046: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13047: ctxt->context->node);
13048: } else {
13049: xmlXPathNodeSetAddUnique(tmp->nodesetval,
13050: ctxt->context->node);
13051: }
13052: valuePush(ctxt, tmp);
13053: ctxt->context->contextSize = oldset->nodeNr;
13054: ctxt->context->proximityPosition = i + 1;
13055: if (op->ch2 != -1)
13056: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13057: if (ctxt->error != XPATH_EXPRESSION_OK) {
13058: xmlXPathFreeNodeSet(newset);
13059: xmlXPathFreeObject(obj);
13060: return(0);
13061: }
13062: /*
13063: * The result of the evaluation needs to be tested to
13064: * decide whether the filter succeeded or not
13065: */
13066: res = valuePop(ctxt);
13067: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
13068: xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
13069: }
13070: /*
13071: * Cleanup
13072: */
13073: if (res != NULL) {
13074: xmlXPathReleaseObject(ctxt->context, res);
13075: }
13076: if (ctxt->value == tmp) {
13077: valuePop(ctxt);
13078: /*
13079: * Don't free the temporary nodeset
13080: * in order to avoid massive recreation inside this
13081: * loop.
13082: */
13083: xmlXPathNodeSetClear(tmp->nodesetval, 1);
13084: } else
13085: tmp = NULL;
13086: ctxt->context->node = NULL;
13087: /*
13088: * Only put the first node in the result, then leave.
13089: */
13090: if (newset->nodeNr > 0) {
13091: *first = *(newset->nodeTab);
13092: break;
13093: }
13094: }
13095: if (tmp != NULL) {
13096: xmlXPathReleaseObject(ctxt->context, tmp);
13097: }
13098: /*
13099: * The result is used as the new evaluation set.
13100: */
13101: xmlXPathReleaseObject(ctxt->context, obj);
13102: ctxt->context->node = NULL;
13103: ctxt->context->contextSize = -1;
13104: ctxt->context->proximityPosition = -1;
13105: /* may want to move this past the '}' later */
13106: ctxt->context->doc = oldDoc;
13107: valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset));
13108: }
13109: ctxt->context->node = oldnode;
13110: return(total);
13111: }
13112: #endif /* XP_OPTIMIZED_FILTER_FIRST */
13113:
13114: /**
13115: * xmlXPathCompOpEval:
13116: * @ctxt: the XPath parser context with the compiled expression
13117: * @op: an XPath compiled operation
13118: *
13119: * Evaluate the Precompiled XPath operation
13120: * Returns the number of nodes traversed
13121: */
13122: static int
13123: xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
13124: {
13125: int total = 0;
13126: int equal, ret;
13127: xmlXPathCompExprPtr comp;
13128: xmlXPathObjectPtr arg1, arg2;
13129: xmlNodePtr bak;
13130: xmlDocPtr bakd;
13131: int pp;
13132: int cs;
13133:
13134: CHECK_ERROR0;
13135: comp = ctxt->comp;
13136: switch (op->op) {
13137: case XPATH_OP_END:
13138: return (0);
13139: case XPATH_OP_AND:
13140: bakd = ctxt->context->doc;
13141: bak = ctxt->context->node;
13142: pp = ctxt->context->proximityPosition;
13143: cs = ctxt->context->contextSize;
13144: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13145: CHECK_ERROR0;
13146: xmlXPathBooleanFunction(ctxt, 1);
13147: if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
13148: return (total);
13149: arg2 = valuePop(ctxt);
13150: ctxt->context->doc = bakd;
13151: ctxt->context->node = bak;
13152: ctxt->context->proximityPosition = pp;
13153: ctxt->context->contextSize = cs;
13154: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13155: if (ctxt->error) {
13156: xmlXPathFreeObject(arg2);
13157: return(0);
13158: }
13159: xmlXPathBooleanFunction(ctxt, 1);
13160: arg1 = valuePop(ctxt);
13161: arg1->boolval &= arg2->boolval;
13162: valuePush(ctxt, arg1);
13163: xmlXPathReleaseObject(ctxt->context, arg2);
13164: return (total);
13165: case XPATH_OP_OR:
13166: bakd = ctxt->context->doc;
13167: bak = ctxt->context->node;
13168: pp = ctxt->context->proximityPosition;
13169: cs = ctxt->context->contextSize;
13170: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13171: CHECK_ERROR0;
13172: xmlXPathBooleanFunction(ctxt, 1);
13173: if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
13174: return (total);
13175: arg2 = valuePop(ctxt);
13176: ctxt->context->doc = bakd;
13177: ctxt->context->node = bak;
13178: ctxt->context->proximityPosition = pp;
13179: ctxt->context->contextSize = cs;
13180: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13181: if (ctxt->error) {
13182: xmlXPathFreeObject(arg2);
13183: return(0);
13184: }
13185: xmlXPathBooleanFunction(ctxt, 1);
13186: arg1 = valuePop(ctxt);
13187: arg1->boolval |= arg2->boolval;
13188: valuePush(ctxt, arg1);
13189: xmlXPathReleaseObject(ctxt->context, arg2);
13190: return (total);
13191: case XPATH_OP_EQUAL:
13192: bakd = ctxt->context->doc;
13193: bak = ctxt->context->node;
13194: pp = ctxt->context->proximityPosition;
13195: cs = ctxt->context->contextSize;
13196: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13197: CHECK_ERROR0;
13198: ctxt->context->doc = bakd;
13199: ctxt->context->node = bak;
13200: ctxt->context->proximityPosition = pp;
13201: ctxt->context->contextSize = cs;
13202: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13203: CHECK_ERROR0;
13204: if (op->value)
13205: equal = xmlXPathEqualValues(ctxt);
13206: else
13207: equal = xmlXPathNotEqualValues(ctxt);
13208: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
13209: return (total);
13210: case XPATH_OP_CMP:
13211: bakd = ctxt->context->doc;
13212: bak = ctxt->context->node;
13213: pp = ctxt->context->proximityPosition;
13214: cs = ctxt->context->contextSize;
13215: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13216: CHECK_ERROR0;
13217: ctxt->context->doc = bakd;
13218: ctxt->context->node = bak;
13219: ctxt->context->proximityPosition = pp;
13220: ctxt->context->contextSize = cs;
13221: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13222: CHECK_ERROR0;
13223: ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
13224: valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
13225: return (total);
13226: case XPATH_OP_PLUS:
13227: bakd = ctxt->context->doc;
13228: bak = ctxt->context->node;
13229: pp = ctxt->context->proximityPosition;
13230: cs = ctxt->context->contextSize;
13231: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13232: CHECK_ERROR0;
13233: if (op->ch2 != -1) {
13234: ctxt->context->doc = bakd;
13235: ctxt->context->node = bak;
13236: ctxt->context->proximityPosition = pp;
13237: ctxt->context->contextSize = cs;
13238: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13239: }
13240: CHECK_ERROR0;
13241: if (op->value == 0)
13242: xmlXPathSubValues(ctxt);
13243: else if (op->value == 1)
13244: xmlXPathAddValues(ctxt);
13245: else if (op->value == 2)
13246: xmlXPathValueFlipSign(ctxt);
13247: else if (op->value == 3) {
13248: CAST_TO_NUMBER;
13249: CHECK_TYPE0(XPATH_NUMBER);
13250: }
13251: return (total);
13252: case XPATH_OP_MULT:
13253: bakd = ctxt->context->doc;
13254: bak = ctxt->context->node;
13255: pp = ctxt->context->proximityPosition;
13256: cs = ctxt->context->contextSize;
13257: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13258: CHECK_ERROR0;
13259: ctxt->context->doc = bakd;
13260: ctxt->context->node = bak;
13261: ctxt->context->proximityPosition = pp;
13262: ctxt->context->contextSize = cs;
13263: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13264: CHECK_ERROR0;
13265: if (op->value == 0)
13266: xmlXPathMultValues(ctxt);
13267: else if (op->value == 1)
13268: xmlXPathDivValues(ctxt);
13269: else if (op->value == 2)
13270: xmlXPathModValues(ctxt);
13271: return (total);
13272: case XPATH_OP_UNION:
13273: bakd = ctxt->context->doc;
13274: bak = ctxt->context->node;
13275: pp = ctxt->context->proximityPosition;
13276: cs = ctxt->context->contextSize;
13277: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13278: CHECK_ERROR0;
13279: ctxt->context->doc = bakd;
13280: ctxt->context->node = bak;
13281: ctxt->context->proximityPosition = pp;
13282: ctxt->context->contextSize = cs;
13283: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13284: CHECK_ERROR0;
13285: CHECK_TYPE0(XPATH_NODESET);
13286: arg2 = valuePop(ctxt);
13287:
13288: CHECK_TYPE0(XPATH_NODESET);
13289: arg1 = valuePop(ctxt);
13290:
13291: if ((arg1->nodesetval == NULL) ||
13292: ((arg2->nodesetval != NULL) &&
13293: (arg2->nodesetval->nodeNr != 0)))
13294: {
13295: arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
13296: arg2->nodesetval);
13297: }
13298:
13299: valuePush(ctxt, arg1);
13300: xmlXPathReleaseObject(ctxt->context, arg2);
13301: return (total);
13302: case XPATH_OP_ROOT:
13303: xmlXPathRoot(ctxt);
13304: return (total);
13305: case XPATH_OP_NODE:
13306: if (op->ch1 != -1)
13307: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13308: CHECK_ERROR0;
13309: if (op->ch2 != -1)
13310: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13311: CHECK_ERROR0;
13312: valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
13313: ctxt->context->node));
13314: return (total);
13315: case XPATH_OP_RESET:
13316: if (op->ch1 != -1)
13317: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13318: CHECK_ERROR0;
13319: if (op->ch2 != -1)
13320: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13321: CHECK_ERROR0;
13322: ctxt->context->node = NULL;
13323: return (total);
13324: case XPATH_OP_COLLECT:{
13325: if (op->ch1 == -1)
13326: return (total);
13327:
13328: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13329: CHECK_ERROR0;
13330:
13331: total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
13332: return (total);
13333: }
13334: case XPATH_OP_VALUE:
13335: valuePush(ctxt,
13336: xmlXPathCacheObjectCopy(ctxt->context,
13337: (xmlXPathObjectPtr) op->value4));
13338: return (total);
13339: case XPATH_OP_VARIABLE:{
13340: xmlXPathObjectPtr val;
13341:
13342: if (op->ch1 != -1)
13343: total +=
13344: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13345: if (op->value5 == NULL) {
13346: val = xmlXPathVariableLookup(ctxt->context, op->value4);
13347: if (val == NULL) {
13348: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
13349: return(0);
13350: }
13351: valuePush(ctxt, val);
13352: } else {
13353: const xmlChar *URI;
13354:
13355: URI = xmlXPathNsLookup(ctxt->context, op->value5);
13356: if (URI == NULL) {
13357: xmlGenericError(xmlGenericErrorContext,
13358: "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
13359: (char *) op->value4, (char *)op->value5);
13360: return (total);
13361: }
13362: val = xmlXPathVariableLookupNS(ctxt->context,
13363: op->value4, URI);
13364: if (val == NULL) {
13365: ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
13366: return(0);
13367: }
13368: valuePush(ctxt, val);
13369: }
13370: return (total);
13371: }
13372: case XPATH_OP_FUNCTION:{
13373: xmlXPathFunction func;
13374: const xmlChar *oldFunc, *oldFuncURI;
13375: int i;
13376:
13377: if (op->ch1 != -1)
13378: total +=
13379: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13380: if (ctxt->valueNr < op->value) {
13381: xmlGenericError(xmlGenericErrorContext,
13382: "xmlXPathCompOpEval: parameter error\n");
13383: ctxt->error = XPATH_INVALID_OPERAND;
13384: return (total);
13385: }
13386: for (i = 0; i < op->value; i++)
13387: if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
13388: xmlGenericError(xmlGenericErrorContext,
13389: "xmlXPathCompOpEval: parameter error\n");
13390: ctxt->error = XPATH_INVALID_OPERAND;
13391: return (total);
13392: }
13393: if (op->cache != NULL)
13394: XML_CAST_FPTR(func) = op->cache;
13395: else {
13396: const xmlChar *URI = NULL;
13397:
13398: if (op->value5 == NULL)
13399: func =
13400: xmlXPathFunctionLookup(ctxt->context,
13401: op->value4);
13402: else {
13403: URI = xmlXPathNsLookup(ctxt->context, op->value5);
13404: if (URI == NULL) {
13405: xmlGenericError(xmlGenericErrorContext,
13406: "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
13407: (char *)op->value4, (char *)op->value5);
13408: return (total);
13409: }
13410: func = xmlXPathFunctionLookupNS(ctxt->context,
13411: op->value4, URI);
13412: }
13413: if (func == NULL) {
13414: xmlGenericError(xmlGenericErrorContext,
13415: "xmlXPathCompOpEval: function %s not found\n",
13416: (char *)op->value4);
13417: XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
13418: }
13419: op->cache = XML_CAST_FPTR(func);
13420: op->cacheURI = (void *) URI;
13421: }
13422: oldFunc = ctxt->context->function;
13423: oldFuncURI = ctxt->context->functionURI;
13424: ctxt->context->function = op->value4;
13425: ctxt->context->functionURI = op->cacheURI;
13426: func(ctxt, op->value);
13427: ctxt->context->function = oldFunc;
13428: ctxt->context->functionURI = oldFuncURI;
13429: return (total);
13430: }
13431: case XPATH_OP_ARG:
13432: bakd = ctxt->context->doc;
13433: bak = ctxt->context->node;
13434: pp = ctxt->context->proximityPosition;
13435: cs = ctxt->context->contextSize;
13436: if (op->ch1 != -1)
13437: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13438: ctxt->context->contextSize = cs;
13439: ctxt->context->proximityPosition = pp;
13440: ctxt->context->node = bak;
13441: ctxt->context->doc = bakd;
13442: CHECK_ERROR0;
13443: if (op->ch2 != -1) {
13444: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
13445: ctxt->context->doc = bakd;
13446: ctxt->context->node = bak;
13447: CHECK_ERROR0;
13448: }
13449: return (total);
13450: case XPATH_OP_PREDICATE:
13451: case XPATH_OP_FILTER:{
13452: xmlXPathObjectPtr res;
13453: xmlXPathObjectPtr obj, tmp;
13454: xmlNodeSetPtr newset = NULL;
13455: xmlNodeSetPtr oldset;
13456: xmlNodePtr oldnode;
13457: xmlDocPtr oldDoc;
13458: int i;
13459:
13460: /*
13461: * Optimization for ()[1] selection i.e. the first elem
13462: */
13463: if ((op->ch1 != -1) && (op->ch2 != -1) &&
13464: #ifdef XP_OPTIMIZED_FILTER_FIRST
13465: /*
13466: * FILTER TODO: Can we assume that the inner processing
13467: * will result in an ordered list if we have an
13468: * XPATH_OP_FILTER?
13469: * What about an additional field or flag on
13470: * xmlXPathObject like @sorted ? This way we wouln'd need
13471: * to assume anything, so it would be more robust and
13472: * easier to optimize.
13473: */
13474: ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
13475: (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
13476: #else
13477: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13478: #endif
13479: (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
13480: xmlXPathObjectPtr val;
13481:
13482: val = comp->steps[op->ch2].value4;
13483: if ((val != NULL) && (val->type == XPATH_NUMBER) &&
13484: (val->floatval == 1.0)) {
13485: xmlNodePtr first = NULL;
13486:
13487: total +=
13488: xmlXPathCompOpEvalFirst(ctxt,
13489: &comp->steps[op->ch1],
13490: &first);
13491: CHECK_ERROR0;
13492: /*
13493: * The nodeset should be in document order,
13494: * Keep only the first value
13495: */
13496: if ((ctxt->value != NULL) &&
13497: (ctxt->value->type == XPATH_NODESET) &&
13498: (ctxt->value->nodesetval != NULL) &&
13499: (ctxt->value->nodesetval->nodeNr > 1))
13500: ctxt->value->nodesetval->nodeNr = 1;
13501: return (total);
13502: }
13503: }
13504: /*
13505: * Optimization for ()[last()] selection i.e. the last elem
13506: */
13507: if ((op->ch1 != -1) && (op->ch2 != -1) &&
13508: (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
13509: (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
13510: int f = comp->steps[op->ch2].ch1;
13511:
13512: if ((f != -1) &&
13513: (comp->steps[f].op == XPATH_OP_FUNCTION) &&
13514: (comp->steps[f].value5 == NULL) &&
13515: (comp->steps[f].value == 0) &&
13516: (comp->steps[f].value4 != NULL) &&
13517: (xmlStrEqual
13518: (comp->steps[f].value4, BAD_CAST "last"))) {
13519: xmlNodePtr last = NULL;
13520:
13521: total +=
13522: xmlXPathCompOpEvalLast(ctxt,
13523: &comp->steps[op->ch1],
13524: &last);
13525: CHECK_ERROR0;
13526: /*
13527: * The nodeset should be in document order,
13528: * Keep only the last value
13529: */
13530: if ((ctxt->value != NULL) &&
13531: (ctxt->value->type == XPATH_NODESET) &&
13532: (ctxt->value->nodesetval != NULL) &&
13533: (ctxt->value->nodesetval->nodeTab != NULL) &&
13534: (ctxt->value->nodesetval->nodeNr > 1)) {
13535: ctxt->value->nodesetval->nodeTab[0] =
13536: ctxt->value->nodesetval->nodeTab[ctxt->
13537: value->
13538: nodesetval->
13539: nodeNr -
13540: 1];
13541: ctxt->value->nodesetval->nodeNr = 1;
13542: }
13543: return (total);
13544: }
13545: }
13546: /*
13547: * Process inner predicates first.
13548: * Example "index[parent::book][1]":
13549: * ...
13550: * PREDICATE <-- we are here "[1]"
13551: * PREDICATE <-- process "[parent::book]" first
13552: * SORT
13553: * COLLECT 'parent' 'name' 'node' book
13554: * NODE
13555: * ELEM Object is a number : 1
13556: */
13557: if (op->ch1 != -1)
13558: total +=
13559: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13560: CHECK_ERROR0;
13561: if (op->ch2 == -1)
13562: return (total);
13563: if (ctxt->value == NULL)
13564: return (total);
13565:
13566: oldnode = ctxt->context->node;
13567:
13568: #ifdef LIBXML_XPTR_ENABLED
13569: /*
13570: * Hum are we filtering the result of an XPointer expression
13571: */
13572: if (ctxt->value->type == XPATH_LOCATIONSET) {
13573: xmlLocationSetPtr newlocset = NULL;
13574: xmlLocationSetPtr oldlocset;
13575:
13576: /*
13577: * Extract the old locset, and then evaluate the result of the
13578: * expression for all the element in the locset. use it to grow
13579: * up a new locset.
13580: */
13581: CHECK_TYPE0(XPATH_LOCATIONSET);
13582: obj = valuePop(ctxt);
13583: oldlocset = obj->user;
13584: ctxt->context->node = NULL;
13585:
13586: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
13587: ctxt->context->contextSize = 0;
13588: ctxt->context->proximityPosition = 0;
13589: if (op->ch2 != -1)
13590: total +=
13591: xmlXPathCompOpEval(ctxt,
13592: &comp->steps[op->ch2]);
13593: res = valuePop(ctxt);
13594: if (res != NULL) {
13595: xmlXPathReleaseObject(ctxt->context, res);
13596: }
13597: valuePush(ctxt, obj);
13598: CHECK_ERROR0;
13599: return (total);
13600: }
13601: newlocset = xmlXPtrLocationSetCreate(NULL);
13602:
13603: for (i = 0; i < oldlocset->locNr; i++) {
13604: /*
13605: * Run the evaluation with a node list made of a
13606: * single item in the nodelocset.
13607: */
13608: ctxt->context->node = oldlocset->locTab[i]->user;
13609: ctxt->context->contextSize = oldlocset->locNr;
13610: ctxt->context->proximityPosition = i + 1;
13611: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13612: ctxt->context->node);
13613: valuePush(ctxt, tmp);
13614:
13615: if (op->ch2 != -1)
13616: total +=
13617: xmlXPathCompOpEval(ctxt,
13618: &comp->steps[op->ch2]);
13619: if (ctxt->error != XPATH_EXPRESSION_OK) {
13620: xmlXPathFreeObject(obj);
13621: return(0);
13622: }
13623:
13624: /*
13625: * The result of the evaluation need to be tested to
13626: * decided whether the filter succeeded or not
13627: */
13628: res = valuePop(ctxt);
13629: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
13630: xmlXPtrLocationSetAdd(newlocset,
13631: xmlXPathObjectCopy
13632: (oldlocset->locTab[i]));
13633: }
13634:
13635: /*
13636: * Cleanup
13637: */
13638: if (res != NULL) {
13639: xmlXPathReleaseObject(ctxt->context, res);
13640: }
13641: if (ctxt->value == tmp) {
13642: res = valuePop(ctxt);
13643: xmlXPathReleaseObject(ctxt->context, res);
13644: }
13645:
13646: ctxt->context->node = NULL;
13647: }
13648:
13649: /*
13650: * The result is used as the new evaluation locset.
13651: */
13652: xmlXPathReleaseObject(ctxt->context, obj);
13653: ctxt->context->node = NULL;
13654: ctxt->context->contextSize = -1;
13655: ctxt->context->proximityPosition = -1;
13656: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13657: ctxt->context->node = oldnode;
13658: return (total);
13659: }
13660: #endif /* LIBXML_XPTR_ENABLED */
13661:
13662: /*
13663: * Extract the old set, and then evaluate the result of the
13664: * expression for all the element in the set. use it to grow
13665: * up a new set.
13666: */
13667: CHECK_TYPE0(XPATH_NODESET);
13668: obj = valuePop(ctxt);
13669: oldset = obj->nodesetval;
13670:
13671: oldnode = ctxt->context->node;
13672: oldDoc = ctxt->context->doc;
13673: ctxt->context->node = NULL;
13674:
13675: if ((oldset == NULL) || (oldset->nodeNr == 0)) {
13676: ctxt->context->contextSize = 0;
13677: ctxt->context->proximityPosition = 0;
13678: /*
13679: if (op->ch2 != -1)
13680: total +=
13681: xmlXPathCompOpEval(ctxt,
13682: &comp->steps[op->ch2]);
13683: CHECK_ERROR0;
13684: res = valuePop(ctxt);
13685: if (res != NULL)
13686: xmlXPathFreeObject(res);
13687: */
13688: valuePush(ctxt, obj);
13689: ctxt->context->node = oldnode;
13690: CHECK_ERROR0;
13691: } else {
13692: tmp = NULL;
13693: /*
13694: * Initialize the new set.
13695: * Also set the xpath document in case things like
13696: * key() evaluation are attempted on the predicate
13697: */
13698: newset = xmlXPathNodeSetCreate(NULL);
13699: /*
13700: * SPEC XPath 1.0:
13701: * "For each node in the node-set to be filtered, the
13702: * PredicateExpr is evaluated with that node as the
13703: * context node, with the number of nodes in the
13704: * node-set as the context size, and with the proximity
13705: * position of the node in the node-set with respect to
13706: * the axis as the context position;"
13707: * @oldset is the node-set" to be filtered.
13708: *
13709: * SPEC XPath 1.0:
13710: * "only predicates change the context position and
13711: * context size (see [2.4 Predicates])."
13712: * Example:
13713: * node-set context pos
13714: * nA 1
13715: * nB 2
13716: * nC 3
13717: * After applying predicate [position() > 1] :
13718: * node-set context pos
13719: * nB 1
13720: * nC 2
13721: *
13722: * removed the first node in the node-set, then
13723: * the context position of the
13724: */
13725: for (i = 0; i < oldset->nodeNr; i++) {
13726: /*
13727: * Run the evaluation with a node list made of
13728: * a single item in the nodeset.
13729: */
13730: ctxt->context->node = oldset->nodeTab[i];
13731: if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
13732: (oldset->nodeTab[i]->doc != NULL))
13733: ctxt->context->doc = oldset->nodeTab[i]->doc;
13734: if (tmp == NULL) {
13735: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13736: ctxt->context->node);
13737: } else {
13738: xmlXPathNodeSetAddUnique(tmp->nodesetval,
13739: ctxt->context->node);
13740: }
13741: valuePush(ctxt, tmp);
13742: ctxt->context->contextSize = oldset->nodeNr;
13743: ctxt->context->proximityPosition = i + 1;
13744: /*
13745: * Evaluate the predicate against the context node.
13746: * Can/should we optimize position() predicates
13747: * here (e.g. "[1]")?
13748: */
13749: if (op->ch2 != -1)
13750: total +=
13751: xmlXPathCompOpEval(ctxt,
13752: &comp->steps[op->ch2]);
13753: if (ctxt->error != XPATH_EXPRESSION_OK) {
13754: xmlXPathFreeNodeSet(newset);
13755: xmlXPathFreeObject(obj);
13756: return(0);
13757: }
13758:
13759: /*
13760: * The result of the evaluation needs to be tested to
13761: * decide whether the filter succeeded or not
13762: */
13763: /*
13764: * OPTIMIZE TODO: Can we use
13765: * xmlXPathNodeSetAdd*Unique()* instead?
13766: */
13767: res = valuePop(ctxt);
13768: if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
13769: xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
13770: }
13771:
13772: /*
13773: * Cleanup
13774: */
13775: if (res != NULL) {
13776: xmlXPathReleaseObject(ctxt->context, res);
13777: }
13778: if (ctxt->value == tmp) {
13779: valuePop(ctxt);
13780: xmlXPathNodeSetClear(tmp->nodesetval, 1);
13781: /*
13782: * Don't free the temporary nodeset
13783: * in order to avoid massive recreation inside this
13784: * loop.
13785: */
13786: } else
13787: tmp = NULL;
13788: ctxt->context->node = NULL;
13789: }
13790: if (tmp != NULL)
13791: xmlXPathReleaseObject(ctxt->context, tmp);
13792: /*
13793: * The result is used as the new evaluation set.
13794: */
13795: xmlXPathReleaseObject(ctxt->context, obj);
13796: ctxt->context->node = NULL;
13797: ctxt->context->contextSize = -1;
13798: ctxt->context->proximityPosition = -1;
13799: /* may want to move this past the '}' later */
13800: ctxt->context->doc = oldDoc;
13801: valuePush(ctxt,
13802: xmlXPathCacheWrapNodeSet(ctxt->context, newset));
13803: }
13804: ctxt->context->node = oldnode;
13805: return (total);
13806: }
13807: case XPATH_OP_SORT:
13808: if (op->ch1 != -1)
13809: total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13810: CHECK_ERROR0;
13811: if ((ctxt->value != NULL) &&
13812: (ctxt->value->type == XPATH_NODESET) &&
13813: (ctxt->value->nodesetval != NULL) &&
13814: (ctxt->value->nodesetval->nodeNr > 1))
13815: {
13816: xmlXPathNodeSetSort(ctxt->value->nodesetval);
13817: }
13818: return (total);
13819: #ifdef LIBXML_XPTR_ENABLED
13820: case XPATH_OP_RANGETO:{
13821: xmlXPathObjectPtr range;
13822: xmlXPathObjectPtr res, obj;
13823: xmlXPathObjectPtr tmp;
13824: xmlLocationSetPtr newlocset = NULL;
13825: xmlLocationSetPtr oldlocset;
13826: xmlNodeSetPtr oldset;
13827: int i, j;
13828:
13829: if (op->ch1 != -1)
13830: total +=
13831: xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
13832: if (op->ch2 == -1)
13833: return (total);
13834:
13835: if (ctxt->value->type == XPATH_LOCATIONSET) {
13836: /*
13837: * Extract the old locset, and then evaluate the result of the
13838: * expression for all the element in the locset. use it to grow
13839: * up a new locset.
13840: */
13841: CHECK_TYPE0(XPATH_LOCATIONSET);
13842: obj = valuePop(ctxt);
13843: oldlocset = obj->user;
13844:
13845: if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
13846: ctxt->context->node = NULL;
13847: ctxt->context->contextSize = 0;
13848: ctxt->context->proximityPosition = 0;
13849: total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
13850: res = valuePop(ctxt);
13851: if (res != NULL) {
13852: xmlXPathReleaseObject(ctxt->context, res);
13853: }
13854: valuePush(ctxt, obj);
13855: CHECK_ERROR0;
13856: return (total);
13857: }
13858: newlocset = xmlXPtrLocationSetCreate(NULL);
13859:
13860: for (i = 0; i < oldlocset->locNr; i++) {
13861: /*
13862: * Run the evaluation with a node list made of a
13863: * single item in the nodelocset.
13864: */
13865: ctxt->context->node = oldlocset->locTab[i]->user;
13866: ctxt->context->contextSize = oldlocset->locNr;
13867: ctxt->context->proximityPosition = i + 1;
13868: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13869: ctxt->context->node);
13870: valuePush(ctxt, tmp);
13871:
13872: if (op->ch2 != -1)
13873: total +=
13874: xmlXPathCompOpEval(ctxt,
13875: &comp->steps[op->ch2]);
13876: if (ctxt->error != XPATH_EXPRESSION_OK) {
13877: xmlXPathFreeObject(obj);
13878: return(0);
13879: }
13880:
13881: res = valuePop(ctxt);
13882: if (res->type == XPATH_LOCATIONSET) {
13883: xmlLocationSetPtr rloc =
13884: (xmlLocationSetPtr)res->user;
13885: for (j=0; j<rloc->locNr; j++) {
13886: range = xmlXPtrNewRange(
13887: oldlocset->locTab[i]->user,
13888: oldlocset->locTab[i]->index,
13889: rloc->locTab[j]->user2,
13890: rloc->locTab[j]->index2);
13891: if (range != NULL) {
13892: xmlXPtrLocationSetAdd(newlocset, range);
13893: }
13894: }
13895: } else {
13896: range = xmlXPtrNewRangeNodeObject(
13897: (xmlNodePtr)oldlocset->locTab[i]->user, res);
13898: if (range != NULL) {
13899: xmlXPtrLocationSetAdd(newlocset,range);
13900: }
13901: }
13902:
13903: /*
13904: * Cleanup
13905: */
13906: if (res != NULL) {
13907: xmlXPathReleaseObject(ctxt->context, res);
13908: }
13909: if (ctxt->value == tmp) {
13910: res = valuePop(ctxt);
13911: xmlXPathReleaseObject(ctxt->context, res);
13912: }
13913:
13914: ctxt->context->node = NULL;
13915: }
13916: } else { /* Not a location set */
13917: CHECK_TYPE0(XPATH_NODESET);
13918: obj = valuePop(ctxt);
13919: oldset = obj->nodesetval;
13920: ctxt->context->node = NULL;
13921:
13922: newlocset = xmlXPtrLocationSetCreate(NULL);
13923:
13924: if (oldset != NULL) {
13925: for (i = 0; i < oldset->nodeNr; i++) {
13926: /*
13927: * Run the evaluation with a node list made of a single item
13928: * in the nodeset.
13929: */
13930: ctxt->context->node = oldset->nodeTab[i];
13931: /*
13932: * OPTIMIZE TODO: Avoid recreation for every iteration.
13933: */
13934: tmp = xmlXPathCacheNewNodeSet(ctxt->context,
13935: ctxt->context->node);
13936: valuePush(ctxt, tmp);
13937:
13938: if (op->ch2 != -1)
13939: total +=
13940: xmlXPathCompOpEval(ctxt,
13941: &comp->steps[op->ch2]);
13942: if (ctxt->error != XPATH_EXPRESSION_OK) {
13943: xmlXPathFreeObject(obj);
13944: return(0);
13945: }
13946:
13947: res = valuePop(ctxt);
13948: range =
13949: xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
13950: res);
13951: if (range != NULL) {
13952: xmlXPtrLocationSetAdd(newlocset, range);
13953: }
13954:
13955: /*
13956: * Cleanup
13957: */
13958: if (res != NULL) {
13959: xmlXPathReleaseObject(ctxt->context, res);
13960: }
13961: if (ctxt->value == tmp) {
13962: res = valuePop(ctxt);
13963: xmlXPathReleaseObject(ctxt->context, res);
13964: }
13965:
13966: ctxt->context->node = NULL;
13967: }
13968: }
13969: }
13970:
13971: /*
13972: * The result is used as the new evaluation set.
13973: */
13974: xmlXPathReleaseObject(ctxt->context, obj);
13975: ctxt->context->node = NULL;
13976: ctxt->context->contextSize = -1;
13977: ctxt->context->proximityPosition = -1;
13978: valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
13979: return (total);
13980: }
13981: #endif /* LIBXML_XPTR_ENABLED */
13982: }
13983: xmlGenericError(xmlGenericErrorContext,
13984: "XPath: unknown precompiled operation %d\n", op->op);
13985: return (total);
13986: }
13987:
13988: /**
13989: * xmlXPathCompOpEvalToBoolean:
13990: * @ctxt: the XPath parser context
13991: *
13992: * Evaluates if the expression evaluates to true.
13993: *
13994: * Returns 1 if true, 0 if false and -1 on API or internal errors.
13995: */
13996: static int
13997: xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
13998: xmlXPathStepOpPtr op,
13999: int isPredicate)
14000: {
14001: xmlXPathObjectPtr resObj = NULL;
14002:
14003: start:
14004: /* comp = ctxt->comp; */
14005: switch (op->op) {
14006: case XPATH_OP_END:
14007: return (0);
14008: case XPATH_OP_VALUE:
14009: resObj = (xmlXPathObjectPtr) op->value4;
14010: if (isPredicate)
14011: return(xmlXPathEvaluatePredicateResult(ctxt, resObj));
14012: return(xmlXPathCastToBoolean(resObj));
14013: case XPATH_OP_SORT:
14014: /*
14015: * We don't need sorting for boolean results. Skip this one.
14016: */
14017: if (op->ch1 != -1) {
14018: op = &ctxt->comp->steps[op->ch1];
14019: goto start;
14020: }
14021: return(0);
14022: case XPATH_OP_COLLECT:
14023: if (op->ch1 == -1)
14024: return(0);
14025:
14026: xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch1]);
14027: if (ctxt->error != XPATH_EXPRESSION_OK)
14028: return(-1);
14029:
14030: xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 1);
14031: if (ctxt->error != XPATH_EXPRESSION_OK)
14032: return(-1);
14033:
14034: resObj = valuePop(ctxt);
14035: if (resObj == NULL)
14036: return(-1);
14037: break;
14038: default:
14039: /*
14040: * Fallback to call xmlXPathCompOpEval().
14041: */
14042: xmlXPathCompOpEval(ctxt, op);
14043: if (ctxt->error != XPATH_EXPRESSION_OK)
14044: return(-1);
14045:
14046: resObj = valuePop(ctxt);
14047: if (resObj == NULL)
14048: return(-1);
14049: break;
14050: }
14051:
14052: if (resObj) {
14053: int res;
14054:
14055: if (resObj->type == XPATH_BOOLEAN) {
14056: res = resObj->boolval;
14057: } else if (isPredicate) {
14058: /*
14059: * For predicates a result of type "number" is handled
14060: * differently:
14061: * SPEC XPath 1.0:
14062: * "If the result is a number, the result will be converted
14063: * to true if the number is equal to the context position
14064: * and will be converted to false otherwise;"
14065: */
14066: res = xmlXPathEvaluatePredicateResult(ctxt, resObj);
14067: } else {
14068: res = xmlXPathCastToBoolean(resObj);
14069: }
14070: xmlXPathReleaseObject(ctxt->context, resObj);
14071: return(res);
14072: }
14073:
14074: return(0);
14075: }
14076:
14077: #ifdef XPATH_STREAMING
14078: /**
14079: * xmlXPathRunStreamEval:
14080: * @ctxt: the XPath parser context with the compiled expression
14081: *
14082: * Evaluate the Precompiled Streamable XPath expression in the given context.
14083: */
14084: static int
14085: xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
14086: xmlXPathObjectPtr *resultSeq, int toBool)
14087: {
14088: int max_depth, min_depth;
14089: int from_root;
14090: int ret, depth;
14091: int eval_all_nodes;
14092: xmlNodePtr cur = NULL, limit = NULL;
14093: xmlStreamCtxtPtr patstream = NULL;
14094:
14095: int nb_nodes = 0;
14096:
14097: if ((ctxt == NULL) || (comp == NULL))
14098: return(-1);
14099: max_depth = xmlPatternMaxDepth(comp);
14100: if (max_depth == -1)
14101: return(-1);
14102: if (max_depth == -2)
14103: max_depth = 10000;
14104: min_depth = xmlPatternMinDepth(comp);
14105: if (min_depth == -1)
14106: return(-1);
14107: from_root = xmlPatternFromRoot(comp);
14108: if (from_root < 0)
14109: return(-1);
14110: #if 0
14111: printf("stream eval: depth %d from root %d\n", max_depth, from_root);
14112: #endif
14113:
14114: if (! toBool) {
14115: if (resultSeq == NULL)
14116: return(-1);
14117: *resultSeq = xmlXPathCacheNewNodeSet(ctxt, NULL);
14118: if (*resultSeq == NULL)
14119: return(-1);
14120: }
14121:
14122: /*
14123: * handle the special cases of "/" amd "." being matched
14124: */
14125: if (min_depth == 0) {
14126: if (from_root) {
14127: /* Select "/" */
14128: if (toBool)
14129: return(1);
14130: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval,
14131: (xmlNodePtr) ctxt->doc);
14132: } else {
14133: /* Select "self::node()" */
14134: if (toBool)
14135: return(1);
14136: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, ctxt->node);
14137: }
14138: }
14139: if (max_depth == 0) {
14140: return(0);
14141: }
14142:
14143: if (from_root) {
14144: cur = (xmlNodePtr)ctxt->doc;
14145: } else if (ctxt->node != NULL) {
14146: switch (ctxt->node->type) {
14147: case XML_ELEMENT_NODE:
14148: case XML_DOCUMENT_NODE:
14149: case XML_DOCUMENT_FRAG_NODE:
14150: case XML_HTML_DOCUMENT_NODE:
14151: #ifdef LIBXML_DOCB_ENABLED
14152: case XML_DOCB_DOCUMENT_NODE:
14153: #endif
14154: cur = ctxt->node;
14155: break;
14156: case XML_ATTRIBUTE_NODE:
14157: case XML_TEXT_NODE:
14158: case XML_CDATA_SECTION_NODE:
14159: case XML_ENTITY_REF_NODE:
14160: case XML_ENTITY_NODE:
14161: case XML_PI_NODE:
14162: case XML_COMMENT_NODE:
14163: case XML_NOTATION_NODE:
14164: case XML_DTD_NODE:
14165: case XML_DOCUMENT_TYPE_NODE:
14166: case XML_ELEMENT_DECL:
14167: case XML_ATTRIBUTE_DECL:
14168: case XML_ENTITY_DECL:
14169: case XML_NAMESPACE_DECL:
14170: case XML_XINCLUDE_START:
14171: case XML_XINCLUDE_END:
14172: break;
14173: }
14174: limit = cur;
14175: }
14176: if (cur == NULL) {
14177: return(0);
14178: }
14179:
14180: patstream = xmlPatternGetStreamCtxt(comp);
14181: if (patstream == NULL) {
14182: /*
14183: * QUESTION TODO: Is this an error?
14184: */
14185: return(0);
14186: }
14187:
14188: eval_all_nodes = xmlStreamWantsAnyNode(patstream);
14189:
14190: if (from_root) {
14191: ret = xmlStreamPush(patstream, NULL, NULL);
14192: if (ret < 0) {
14193: } else if (ret == 1) {
14194: if (toBool)
14195: goto return_1;
14196: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
14197: }
14198: }
14199: depth = 0;
14200: goto scan_children;
14201: next_node:
14202: do {
14203: nb_nodes++;
14204:
14205: switch (cur->type) {
14206: case XML_ELEMENT_NODE:
14207: case XML_TEXT_NODE:
14208: case XML_CDATA_SECTION_NODE:
14209: case XML_COMMENT_NODE:
14210: case XML_PI_NODE:
14211: if (cur->type == XML_ELEMENT_NODE) {
14212: ret = xmlStreamPush(patstream, cur->name,
14213: (cur->ns ? cur->ns->href : NULL));
14214: } else if (eval_all_nodes)
14215: ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type);
14216: else
14217: break;
14218:
14219: if (ret < 0) {
14220: /* NOP. */
14221: } else if (ret == 1) {
14222: if (toBool)
14223: goto return_1;
14224: xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur);
14225: }
14226: if ((cur->children == NULL) || (depth >= max_depth)) {
14227: ret = xmlStreamPop(patstream);
14228: while (cur->next != NULL) {
14229: cur = cur->next;
14230: if ((cur->type != XML_ENTITY_DECL) &&
14231: (cur->type != XML_DTD_NODE))
14232: goto next_node;
14233: }
14234: }
14235: default:
14236: break;
14237: }
14238:
14239: scan_children:
14240: if ((cur->children != NULL) && (depth < max_depth)) {
14241: /*
14242: * Do not descend on entities declarations
14243: */
14244: if (cur->children->type != XML_ENTITY_DECL) {
14245: cur = cur->children;
14246: depth++;
14247: /*
14248: * Skip DTDs
14249: */
14250: if (cur->type != XML_DTD_NODE)
14251: continue;
14252: }
14253: }
14254:
14255: if (cur == limit)
14256: break;
14257:
14258: while (cur->next != NULL) {
14259: cur = cur->next;
14260: if ((cur->type != XML_ENTITY_DECL) &&
14261: (cur->type != XML_DTD_NODE))
14262: goto next_node;
14263: }
14264:
14265: do {
14266: cur = cur->parent;
14267: depth--;
14268: if ((cur == NULL) || (cur == limit))
14269: goto done;
14270: if (cur->type == XML_ELEMENT_NODE) {
14271: ret = xmlStreamPop(patstream);
14272: } else if ((eval_all_nodes) &&
14273: ((cur->type == XML_TEXT_NODE) ||
14274: (cur->type == XML_CDATA_SECTION_NODE) ||
14275: (cur->type == XML_COMMENT_NODE) ||
14276: (cur->type == XML_PI_NODE)))
14277: {
14278: ret = xmlStreamPop(patstream);
14279: }
14280: if (cur->next != NULL) {
14281: cur = cur->next;
14282: break;
14283: }
14284: } while (cur != NULL);
14285:
14286: } while ((cur != NULL) && (depth >= 0));
14287:
14288: done:
14289:
14290: #if 0
14291: printf("stream eval: checked %d nodes selected %d\n",
14292: nb_nodes, retObj->nodesetval->nodeNr);
14293: #endif
14294:
14295: if (patstream)
14296: xmlFreeStreamCtxt(patstream);
14297: return(0);
14298:
14299: return_1:
14300: if (patstream)
14301: xmlFreeStreamCtxt(patstream);
14302: return(1);
14303: }
14304: #endif /* XPATH_STREAMING */
14305:
14306: /**
14307: * xmlXPathRunEval:
14308: * @ctxt: the XPath parser context with the compiled expression
14309: * @toBool: evaluate to a boolean result
14310: *
14311: * Evaluate the Precompiled XPath expression in the given context.
14312: */
14313: static int
14314: xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
14315: {
14316: xmlXPathCompExprPtr comp;
14317:
14318: if ((ctxt == NULL) || (ctxt->comp == NULL))
14319: return(-1);
14320:
14321: if (ctxt->valueTab == NULL) {
14322: /* Allocate the value stack */
14323: ctxt->valueTab = (xmlXPathObjectPtr *)
14324: xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
14325: if (ctxt->valueTab == NULL) {
14326: xmlXPathPErrMemory(ctxt, "creating evaluation context\n");
14327: xmlFree(ctxt);
14328: }
14329: ctxt->valueNr = 0;
14330: ctxt->valueMax = 10;
14331: ctxt->value = NULL;
14332: }
14333: #ifdef XPATH_STREAMING
14334: if (ctxt->comp->stream) {
14335: int res;
14336:
14337: if (toBool) {
14338: /*
14339: * Evaluation to boolean result.
14340: */
14341: res = xmlXPathRunStreamEval(ctxt->context,
14342: ctxt->comp->stream, NULL, 1);
14343: if (res != -1)
14344: return(res);
14345: } else {
14346: xmlXPathObjectPtr resObj = NULL;
14347:
14348: /*
14349: * Evaluation to a sequence.
14350: */
14351: res = xmlXPathRunStreamEval(ctxt->context,
14352: ctxt->comp->stream, &resObj, 0);
14353:
14354: if ((res != -1) && (resObj != NULL)) {
14355: valuePush(ctxt, resObj);
14356: return(0);
14357: }
14358: if (resObj != NULL)
14359: xmlXPathReleaseObject(ctxt->context, resObj);
14360: }
14361: /*
14362: * QUESTION TODO: This falls back to normal XPath evaluation
14363: * if res == -1. Is this intended?
14364: */
14365: }
14366: #endif
14367: comp = ctxt->comp;
14368: if (comp->last < 0) {
14369: xmlGenericError(xmlGenericErrorContext,
14370: "xmlXPathRunEval: last is less than zero\n");
14371: return(-1);
14372: }
14373: if (toBool)
14374: return(xmlXPathCompOpEvalToBoolean(ctxt,
14375: &comp->steps[comp->last], 0));
14376: else
14377: xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
14378:
14379: return(0);
14380: }
14381:
14382: /************************************************************************
14383: * *
14384: * Public interfaces *
14385: * *
14386: ************************************************************************/
14387:
14388: /**
14389: * xmlXPathEvalPredicate:
14390: * @ctxt: the XPath context
14391: * @res: the Predicate Expression evaluation result
14392: *
14393: * Evaluate a predicate result for the current node.
14394: * A PredicateExpr is evaluated by evaluating the Expr and converting
14395: * the result to a boolean. If the result is a number, the result will
14396: * be converted to true if the number is equal to the position of the
14397: * context node in the context node list (as returned by the position
14398: * function) and will be converted to false otherwise; if the result
14399: * is not a number, then the result will be converted as if by a call
14400: * to the boolean function.
14401: *
14402: * Returns 1 if predicate is true, 0 otherwise
14403: */
14404: int
14405: xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
14406: if ((ctxt == NULL) || (res == NULL)) return(0);
14407: switch (res->type) {
14408: case XPATH_BOOLEAN:
14409: return(res->boolval);
14410: case XPATH_NUMBER:
14411: return(res->floatval == ctxt->proximityPosition);
14412: case XPATH_NODESET:
14413: case XPATH_XSLT_TREE:
14414: if (res->nodesetval == NULL)
14415: return(0);
14416: return(res->nodesetval->nodeNr != 0);
14417: case XPATH_STRING:
14418: return((res->stringval != NULL) &&
14419: (xmlStrlen(res->stringval) != 0));
14420: default:
14421: STRANGE
14422: }
14423: return(0);
14424: }
14425:
14426: /**
14427: * xmlXPathEvaluatePredicateResult:
14428: * @ctxt: the XPath Parser context
14429: * @res: the Predicate Expression evaluation result
14430: *
14431: * Evaluate a predicate result for the current node.
14432: * A PredicateExpr is evaluated by evaluating the Expr and converting
14433: * the result to a boolean. If the result is a number, the result will
14434: * be converted to true if the number is equal to the position of the
14435: * context node in the context node list (as returned by the position
14436: * function) and will be converted to false otherwise; if the result
14437: * is not a number, then the result will be converted as if by a call
14438: * to the boolean function.
14439: *
14440: * Returns 1 if predicate is true, 0 otherwise
14441: */
14442: int
14443: xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
14444: xmlXPathObjectPtr res) {
14445: if ((ctxt == NULL) || (res == NULL)) return(0);
14446: switch (res->type) {
14447: case XPATH_BOOLEAN:
14448: return(res->boolval);
14449: case XPATH_NUMBER:
14450: #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
14451: return((res->floatval == ctxt->context->proximityPosition) &&
14452: (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
14453: #else
14454: return(res->floatval == ctxt->context->proximityPosition);
14455: #endif
14456: case XPATH_NODESET:
14457: case XPATH_XSLT_TREE:
14458: if (res->nodesetval == NULL)
14459: return(0);
14460: return(res->nodesetval->nodeNr != 0);
14461: case XPATH_STRING:
14462: return((res->stringval != NULL) && (res->stringval[0] != 0));
14463: #ifdef LIBXML_XPTR_ENABLED
14464: case XPATH_LOCATIONSET:{
14465: xmlLocationSetPtr ptr = res->user;
14466: if (ptr == NULL)
14467: return(0);
14468: return (ptr->locNr != 0);
14469: }
14470: #endif
14471: default:
14472: STRANGE
14473: }
14474: return(0);
14475: }
14476:
14477: #ifdef XPATH_STREAMING
14478: /**
14479: * xmlXPathTryStreamCompile:
14480: * @ctxt: an XPath context
14481: * @str: the XPath expression
14482: *
14483: * Try to compile the XPath expression as a streamable subset.
14484: *
14485: * Returns the compiled expression or NULL if failed to compile.
14486: */
14487: static xmlXPathCompExprPtr
14488: xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14489: /*
14490: * Optimization: use streaming patterns when the XPath expression can
14491: * be compiled to a stream lookup
14492: */
14493: xmlPatternPtr stream;
14494: xmlXPathCompExprPtr comp;
14495: xmlDictPtr dict = NULL;
14496: const xmlChar **namespaces = NULL;
14497: xmlNsPtr ns;
14498: int i, j;
14499:
14500: if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) &&
14501: (!xmlStrchr(str, '@'))) {
14502: const xmlChar *tmp;
14503:
14504: /*
14505: * We don't try to handle expressions using the verbose axis
14506: * specifiers ("::"), just the simplied form at this point.
14507: * Additionally, if there is no list of namespaces available and
14508: * there's a ":" in the expression, indicating a prefixed QName,
14509: * then we won't try to compile either. xmlPatterncompile() needs
14510: * to have a list of namespaces at compilation time in order to
14511: * compile prefixed name tests.
14512: */
14513: tmp = xmlStrchr(str, ':');
14514: if ((tmp != NULL) &&
14515: ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
14516: return(NULL);
14517:
14518: if (ctxt != NULL) {
14519: dict = ctxt->dict;
14520: if (ctxt->nsNr > 0) {
14521: namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*));
14522: if (namespaces == NULL) {
14523: xmlXPathErrMemory(ctxt, "allocating namespaces array\n");
14524: return(NULL);
14525: }
14526: for (i = 0, j = 0; (j < ctxt->nsNr); j++) {
14527: ns = ctxt->namespaces[j];
14528: namespaces[i++] = ns->href;
14529: namespaces[i++] = ns->prefix;
14530: }
14531: namespaces[i++] = NULL;
14532: namespaces[i] = NULL;
14533: }
14534: }
14535:
14536: stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH,
14537: &namespaces[0]);
14538: if (namespaces != NULL) {
14539: xmlFree((xmlChar **)namespaces);
14540: }
14541: if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
14542: comp = xmlXPathNewCompExpr();
14543: if (comp == NULL) {
14544: xmlXPathErrMemory(ctxt, "allocating streamable expression\n");
14545: return(NULL);
14546: }
14547: comp->stream = stream;
14548: comp->dict = dict;
14549: if (comp->dict)
14550: xmlDictReference(comp->dict);
14551: return(comp);
14552: }
14553: xmlFreePattern(stream);
14554: }
14555: return(NULL);
14556: }
14557: #endif /* XPATH_STREAMING */
14558:
14559: static int
14560: xmlXPathCanRewriteDosExpression(xmlChar *expr)
14561: {
14562: if (expr == NULL)
14563: return(0);
14564: do {
14565: if ((*expr == '/') && (*(++expr) == '/'))
14566: return(1);
14567: } while (*expr++);
14568: return(0);
14569: }
14570: static void
14571: xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
14572: {
14573: /*
14574: * Try to rewrite "descendant-or-self::node()/foo" to an optimized
14575: * internal representation.
14576: */
14577: if (op->ch1 != -1) {
14578: if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
14579: ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) &&
14580: ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) &&
14581: ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */))
14582: {
14583: /*
14584: * This is a "child::foo"
14585: */
14586: xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
14587:
14588: if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
14589: (prevop->ch1 != -1) &&
14590: ((xmlXPathAxisVal) prevop->value ==
14591: AXIS_DESCENDANT_OR_SELF) &&
14592: (prevop->ch2 == -1) &&
14593: ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
14594: ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) &&
14595: (comp->steps[prevop->ch1].op == XPATH_OP_ROOT))
14596: {
14597: /*
14598: * This is a "/descendant-or-self::node()" without predicates.
14599: * Eliminate it.
14600: */
14601: op->ch1 = prevop->ch1;
14602: op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM;
14603: }
14604: }
14605: if (op->ch1 != -1)
14606: xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]);
14607: }
14608: if (op->ch2 != -1)
14609: xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]);
14610: }
14611:
14612: /**
14613: * xmlXPathCtxtCompile:
14614: * @ctxt: an XPath context
14615: * @str: the XPath expression
14616: *
14617: * Compile an XPath expression
14618: *
14619: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14620: * the caller has to free the object.
14621: */
14622: xmlXPathCompExprPtr
14623: xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
14624: xmlXPathParserContextPtr pctxt;
14625: xmlXPathCompExprPtr comp;
14626:
14627: #ifdef XPATH_STREAMING
14628: comp = xmlXPathTryStreamCompile(ctxt, str);
14629: if (comp != NULL)
14630: return(comp);
14631: #endif
14632:
14633: xmlXPathInit();
14634:
14635: pctxt = xmlXPathNewParserContext(str, ctxt);
14636: if (pctxt == NULL)
14637: return NULL;
14638: xmlXPathCompileExpr(pctxt, 1);
14639:
14640: if( pctxt->error != XPATH_EXPRESSION_OK )
14641: {
14642: xmlXPathFreeParserContext(pctxt);
14643: return(NULL);
14644: }
14645:
14646: if (*pctxt->cur != 0) {
14647: /*
14648: * aleksey: in some cases this line prints *second* error message
14649: * (see bug #78858) and probably this should be fixed.
14650: * However, we are not sure that all error messages are printed
14651: * out in other places. It's not critical so we leave it as-is for now
14652: */
14653: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14654: comp = NULL;
14655: } else {
14656: comp = pctxt->comp;
14657: pctxt->comp = NULL;
14658: }
14659: xmlXPathFreeParserContext(pctxt);
14660:
14661: if (comp != NULL) {
14662: comp->expr = xmlStrdup(str);
14663: #ifdef DEBUG_EVAL_COUNTS
14664: comp->string = xmlStrdup(str);
14665: comp->nb = 0;
14666: #endif
14667: if ((comp->expr != NULL) &&
14668: (comp->nbStep > 2) &&
14669: (comp->last >= 0) &&
14670: (xmlXPathCanRewriteDosExpression(comp->expr) == 1))
14671: {
14672: xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]);
14673: }
14674: }
14675: return(comp);
14676: }
14677:
14678: /**
14679: * xmlXPathCompile:
14680: * @str: the XPath expression
14681: *
14682: * Compile an XPath expression
14683: *
14684: * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
14685: * the caller has to free the object.
14686: */
14687: xmlXPathCompExprPtr
14688: xmlXPathCompile(const xmlChar *str) {
14689: return(xmlXPathCtxtCompile(NULL, str));
14690: }
14691:
14692: /**
14693: * xmlXPathCompiledEvalInternal:
14694: * @comp: the compiled XPath expression
14695: * @ctxt: the XPath context
14696: * @resObj: the resulting XPath object or NULL
14697: * @toBool: 1 if only a boolean result is requested
14698: *
14699: * Evaluate the Precompiled XPath expression in the given context.
14700: * The caller has to free @resObj.
14701: *
14702: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14703: * the caller has to free the object.
14704: */
14705: static int
14706: xmlXPathCompiledEvalInternal(xmlXPathCompExprPtr comp,
14707: xmlXPathContextPtr ctxt,
14708: xmlXPathObjectPtr *resObj,
14709: int toBool)
14710: {
14711: xmlXPathParserContextPtr pctxt;
14712: #ifndef LIBXML_THREAD_ENABLED
14713: static int reentance = 0;
14714: #endif
14715: int res;
14716:
14717: CHECK_CTXT_NEG(ctxt)
14718:
14719: if (comp == NULL)
14720: return(-1);
14721: xmlXPathInit();
14722:
14723: #ifndef LIBXML_THREAD_ENABLED
14724: reentance++;
14725: if (reentance > 1)
14726: xmlXPathDisableOptimizer = 1;
14727: #endif
14728:
14729: #ifdef DEBUG_EVAL_COUNTS
14730: comp->nb++;
14731: if ((comp->string != NULL) && (comp->nb > 100)) {
14732: fprintf(stderr, "100 x %s\n", comp->string);
14733: comp->nb = 0;
14734: }
14735: #endif
14736: pctxt = xmlXPathCompParserContext(comp, ctxt);
14737: res = xmlXPathRunEval(pctxt, toBool);
14738:
14739: if (resObj) {
14740: if (pctxt->value == NULL) {
14741: xmlGenericError(xmlGenericErrorContext,
14742: "xmlXPathCompiledEval: evaluation failed\n");
14743: *resObj = NULL;
14744: } else {
14745: *resObj = valuePop(pctxt);
14746: }
14747: }
14748:
14749: /*
14750: * Pop all remaining objects from the stack.
14751: */
14752: if (pctxt->valueNr > 0) {
14753: xmlXPathObjectPtr tmp;
14754: int stack = 0;
14755:
14756: do {
14757: tmp = valuePop(pctxt);
14758: if (tmp != NULL) {
14759: stack++;
14760: xmlXPathReleaseObject(ctxt, tmp);
14761: }
14762: } while (tmp != NULL);
14763: if ((stack != 0) &&
14764: ((toBool) || ((resObj) && (*resObj))))
14765: {
14766: xmlGenericError(xmlGenericErrorContext,
14767: "xmlXPathCompiledEval: %d objects left on the stack.\n",
14768: stack);
14769: }
14770: }
14771:
14772: if ((pctxt->error != XPATH_EXPRESSION_OK) && (resObj) && (*resObj)) {
14773: xmlXPathFreeObject(*resObj);
14774: *resObj = NULL;
14775: }
14776: pctxt->comp = NULL;
14777: xmlXPathFreeParserContext(pctxt);
14778: #ifndef LIBXML_THREAD_ENABLED
14779: reentance--;
14780: #endif
14781:
14782: return(res);
14783: }
14784:
14785: /**
14786: * xmlXPathCompiledEval:
14787: * @comp: the compiled XPath expression
14788: * @ctx: the XPath context
14789: *
14790: * Evaluate the Precompiled XPath expression in the given context.
14791: *
14792: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14793: * the caller has to free the object.
14794: */
14795: xmlXPathObjectPtr
14796: xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx)
14797: {
14798: xmlXPathObjectPtr res = NULL;
14799:
14800: xmlXPathCompiledEvalInternal(comp, ctx, &res, 0);
14801: return(res);
14802: }
14803:
14804: /**
14805: * xmlXPathCompiledEvalToBoolean:
14806: * @comp: the compiled XPath expression
14807: * @ctxt: the XPath context
14808: *
14809: * Applies the XPath boolean() function on the result of the given
14810: * compiled expression.
14811: *
14812: * Returns 1 if the expression evaluated to true, 0 if to false and
14813: * -1 in API and internal errors.
14814: */
14815: int
14816: xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
14817: xmlXPathContextPtr ctxt)
14818: {
14819: return(xmlXPathCompiledEvalInternal(comp, ctxt, NULL, 1));
14820: }
14821:
14822: /**
14823: * xmlXPathEvalExpr:
14824: * @ctxt: the XPath Parser context
14825: *
14826: * Parse and evaluate an XPath expression in the given context,
14827: * then push the result on the context stack
14828: */
14829: void
14830: xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
14831: #ifdef XPATH_STREAMING
14832: xmlXPathCompExprPtr comp;
14833: #endif
14834:
14835: if (ctxt == NULL) return;
14836:
14837: #ifdef XPATH_STREAMING
14838: comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base);
14839: if (comp != NULL) {
14840: if (ctxt->comp != NULL)
14841: xmlXPathFreeCompExpr(ctxt->comp);
14842: ctxt->comp = comp;
14843: if (ctxt->cur != NULL)
14844: while (*ctxt->cur != 0) ctxt->cur++;
14845: } else
14846: #endif
14847: {
14848: xmlXPathCompileExpr(ctxt, 1);
14849: /*
14850: * In this scenario the expression string will sit in ctxt->base.
14851: */
14852: if ((ctxt->error == XPATH_EXPRESSION_OK) &&
14853: (ctxt->comp != NULL) &&
14854: (ctxt->base != NULL) &&
14855: (ctxt->comp->nbStep > 2) &&
14856: (ctxt->comp->last >= 0) &&
14857: (xmlXPathCanRewriteDosExpression((xmlChar *) ctxt->base) == 1))
14858: {
14859: xmlXPathRewriteDOSExpression(ctxt->comp,
14860: &ctxt->comp->steps[ctxt->comp->last]);
14861: }
14862: }
14863: CHECK_ERROR;
14864: xmlXPathRunEval(ctxt, 0);
14865: }
14866:
14867: /**
14868: * xmlXPathEval:
14869: * @str: the XPath expression
14870: * @ctx: the XPath context
14871: *
14872: * Evaluate the XPath Location Path in the given context.
14873: *
14874: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14875: * the caller has to free the object.
14876: */
14877: xmlXPathObjectPtr
14878: xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
14879: xmlXPathParserContextPtr ctxt;
14880: xmlXPathObjectPtr res, tmp, init = NULL;
14881: int stack = 0;
14882:
14883: CHECK_CTXT(ctx)
14884:
14885: xmlXPathInit();
14886:
14887: ctxt = xmlXPathNewParserContext(str, ctx);
14888: if (ctxt == NULL)
14889: return NULL;
14890: xmlXPathEvalExpr(ctxt);
14891:
14892: if (ctxt->value == NULL) {
14893: xmlGenericError(xmlGenericErrorContext,
14894: "xmlXPathEval: evaluation failed\n");
14895: res = NULL;
14896: } else if ((*ctxt->cur != 0) && (ctxt->comp != NULL)
14897: #ifdef XPATH_STREAMING
14898: && (ctxt->comp->stream == NULL)
14899: #endif
14900: ) {
14901: xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14902: res = NULL;
14903: } else {
14904: res = valuePop(ctxt);
14905: }
14906:
14907: do {
14908: tmp = valuePop(ctxt);
14909: if (tmp != NULL) {
14910: if (tmp != init)
14911: stack++;
14912: xmlXPathReleaseObject(ctx, tmp);
14913: }
14914: } while (tmp != NULL);
14915: if ((stack != 0) && (res != NULL)) {
14916: xmlGenericError(xmlGenericErrorContext,
14917: "xmlXPathEval: %d object left on the stack\n",
14918: stack);
14919: }
14920: if (ctxt->error != XPATH_EXPRESSION_OK) {
14921: xmlXPathFreeObject(res);
14922: res = NULL;
14923: }
14924:
14925: xmlXPathFreeParserContext(ctxt);
14926: return(res);
14927: }
14928:
14929: /**
14930: * xmlXPathEvalExpression:
14931: * @str: the XPath expression
14932: * @ctxt: the XPath context
14933: *
14934: * Evaluate the XPath expression in the given context.
14935: *
14936: * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
14937: * the caller has to free the object.
14938: */
14939: xmlXPathObjectPtr
14940: xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
14941: xmlXPathParserContextPtr pctxt;
14942: xmlXPathObjectPtr res, tmp;
14943: int stack = 0;
14944:
14945: CHECK_CTXT(ctxt)
14946:
14947: xmlXPathInit();
14948:
14949: pctxt = xmlXPathNewParserContext(str, ctxt);
14950: if (pctxt == NULL)
14951: return NULL;
14952: xmlXPathEvalExpr(pctxt);
14953:
14954: if ((*pctxt->cur != 0) || (pctxt->error != XPATH_EXPRESSION_OK)) {
14955: xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
14956: res = NULL;
14957: } else {
14958: res = valuePop(pctxt);
14959: }
14960: do {
14961: tmp = valuePop(pctxt);
14962: if (tmp != NULL) {
14963: xmlXPathReleaseObject(ctxt, tmp);
14964: stack++;
14965: }
14966: } while (tmp != NULL);
14967: if ((stack != 0) && (res != NULL)) {
14968: xmlGenericError(xmlGenericErrorContext,
14969: "xmlXPathEvalExpression: %d object left on the stack\n",
14970: stack);
14971: }
14972: xmlXPathFreeParserContext(pctxt);
14973: return(res);
14974: }
14975:
14976: /************************************************************************
14977: * *
14978: * Extra functions not pertaining to the XPath spec *
14979: * *
14980: ************************************************************************/
14981: /**
14982: * xmlXPathEscapeUriFunction:
14983: * @ctxt: the XPath Parser context
14984: * @nargs: the number of arguments
14985: *
14986: * Implement the escape-uri() XPath function
14987: * string escape-uri(string $str, bool $escape-reserved)
14988: *
14989: * This function applies the URI escaping rules defined in section 2 of [RFC
14990: * 2396] to the string supplied as $uri-part, which typically represents all
14991: * or part of a URI. The effect of the function is to replace any special
14992: * character in the string by an escape sequence of the form %xx%yy...,
14993: * where xxyy... is the hexadecimal representation of the octets used to
14994: * represent the character in UTF-8.
14995: *
14996: * The set of characters that are escaped depends on the setting of the
14997: * boolean argument $escape-reserved.
14998: *
14999: * If $escape-reserved is true, all characters are escaped other than lower
15000: * case letters a-z, upper case letters A-Z, digits 0-9, and the characters
15001: * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
15002: * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
15003: * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
15004: * A-F).
15005: *
15006: * If $escape-reserved is false, the behavior differs in that characters
15007: * referred to in [RFC 2396] as reserved characters are not escaped. These
15008: * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
15009: *
15010: * [RFC 2396] does not define whether escaped URIs should use lower case or
15011: * upper case for hexadecimal digits. To ensure that escaped URIs can be
15012: * compared using string comparison functions, this function must always use
15013: * the upper-case letters A-F.
15014: *
15015: * Generally, $escape-reserved should be set to true when escaping a string
15016: * that is to form a single part of a URI, and to false when escaping an
15017: * entire URI or URI reference.
15018: *
15019: * In the case of non-ascii characters, the string is encoded according to
15020: * utf-8 and then converted according to RFC 2396.
15021: *
15022: * Examples
15023: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
15024: * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
15025: * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
15026: * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
15027: *
15028: */
15029: static void
15030: xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
15031: xmlXPathObjectPtr str;
15032: int escape_reserved;
15033: xmlBufferPtr target;
15034: xmlChar *cptr;
15035: xmlChar escape[4];
15036:
15037: CHECK_ARITY(2);
15038:
15039: escape_reserved = xmlXPathPopBoolean(ctxt);
15040:
15041: CAST_TO_STRING;
15042: str = valuePop(ctxt);
15043:
15044: target = xmlBufferCreate();
15045:
15046: escape[0] = '%';
15047: escape[3] = 0;
15048:
15049: if (target) {
15050: for (cptr = str->stringval; *cptr; cptr++) {
15051: if ((*cptr >= 'A' && *cptr <= 'Z') ||
15052: (*cptr >= 'a' && *cptr <= 'z') ||
15053: (*cptr >= '0' && *cptr <= '9') ||
15054: *cptr == '-' || *cptr == '_' || *cptr == '.' ||
15055: *cptr == '!' || *cptr == '~' || *cptr == '*' ||
15056: *cptr == '\''|| *cptr == '(' || *cptr == ')' ||
15057: (*cptr == '%' &&
15058: ((cptr[1] >= 'A' && cptr[1] <= 'F') ||
15059: (cptr[1] >= 'a' && cptr[1] <= 'f') ||
15060: (cptr[1] >= '0' && cptr[1] <= '9')) &&
15061: ((cptr[2] >= 'A' && cptr[2] <= 'F') ||
15062: (cptr[2] >= 'a' && cptr[2] <= 'f') ||
15063: (cptr[2] >= '0' && cptr[2] <= '9'))) ||
15064: (!escape_reserved &&
15065: (*cptr == ';' || *cptr == '/' || *cptr == '?' ||
15066: *cptr == ':' || *cptr == '@' || *cptr == '&' ||
15067: *cptr == '=' || *cptr == '+' || *cptr == '$' ||
15068: *cptr == ','))) {
15069: xmlBufferAdd(target, cptr, 1);
15070: } else {
15071: if ((*cptr >> 4) < 10)
15072: escape[1] = '0' + (*cptr >> 4);
15073: else
15074: escape[1] = 'A' - 10 + (*cptr >> 4);
15075: if ((*cptr & 0xF) < 10)
15076: escape[2] = '0' + (*cptr & 0xF);
15077: else
15078: escape[2] = 'A' - 10 + (*cptr & 0xF);
15079:
15080: xmlBufferAdd(target, &escape[0], 3);
15081: }
15082: }
15083: }
15084: valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
15085: xmlBufferContent(target)));
15086: xmlBufferFree(target);
15087: xmlXPathReleaseObject(ctxt->context, str);
15088: }
15089:
15090: /**
15091: * xmlXPathRegisterAllFunctions:
15092: * @ctxt: the XPath context
15093: *
15094: * Registers all default XPath functions in this context
15095: */
15096: void
15097: xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
15098: {
15099: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
15100: xmlXPathBooleanFunction);
15101: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
15102: xmlXPathCeilingFunction);
15103: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
15104: xmlXPathCountFunction);
15105: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
15106: xmlXPathConcatFunction);
15107: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
15108: xmlXPathContainsFunction);
15109: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
15110: xmlXPathIdFunction);
15111: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
15112: xmlXPathFalseFunction);
15113: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
15114: xmlXPathFloorFunction);
15115: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
15116: xmlXPathLastFunction);
15117: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
15118: xmlXPathLangFunction);
15119: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
15120: xmlXPathLocalNameFunction);
15121: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
15122: xmlXPathNotFunction);
15123: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
15124: xmlXPathNameFunction);
15125: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
15126: xmlXPathNamespaceURIFunction);
15127: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
15128: xmlXPathNormalizeFunction);
15129: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
15130: xmlXPathNumberFunction);
15131: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
15132: xmlXPathPositionFunction);
15133: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
15134: xmlXPathRoundFunction);
15135: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
15136: xmlXPathStringFunction);
15137: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
15138: xmlXPathStringLengthFunction);
15139: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
15140: xmlXPathStartsWithFunction);
15141: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
15142: xmlXPathSubstringFunction);
15143: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
15144: xmlXPathSubstringBeforeFunction);
15145: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
15146: xmlXPathSubstringAfterFunction);
15147: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
15148: xmlXPathSumFunction);
15149: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
15150: xmlXPathTrueFunction);
15151: xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
15152: xmlXPathTranslateFunction);
15153:
15154: xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
15155: (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
15156: xmlXPathEscapeUriFunction);
15157: }
15158:
15159: #endif /* LIBXML_XPATH_ENABLED */
15160: #define bottom_xpath
15161: #include "elfgcchack.h"
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>