blob: 7d4fc328040acef453b15e5ccb5a54723d5b6301 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
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 *
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@w3.org
14 *
15 * 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
16 * for VMS
17 */
18
19#ifdef WIN32
20#include "win32config.h"
21#else
22#include "config.h"
23#endif
24
25#include <libxml/xmlversion.h>
26#ifdef LIBXML_XPATH_ENABLED
27
28#include <stdio.h>
29#include <string.h>
30
31#ifdef HAVE_SYS_TYPES_H
32#include <sys/types.h>
33#endif
34#ifdef HAVE_MATH_H
35#include <math.h>
36#endif
37#ifdef HAVE_FLOAT_H
38#include <float.h>
39#endif
40#ifdef HAVE_IEEEFP_H
41#include <ieeefp.h>
42#endif
43#ifdef HAVE_NAN_H
44#include <nan.h>
45#endif
46#ifdef HAVE_CTYPE_H
47#include <ctype.h>
48#endif
49
50#include <libxml/xmlmemory.h>
51#include <libxml/tree.h>
52#include <libxml/valid.h>
53#include <libxml/xpath.h>
54#include <libxml/xpathInternals.h>
55#include <libxml/parserInternals.h>
56#include <libxml/hash.h>
57#ifdef LIBXML_XPTR_ENABLED
58#include <libxml/xpointer.h>
59#endif
60#ifdef LIBXML_DEBUG_ENABLED
61#include <libxml/debugXML.h>
62#endif
63#include <libxml/xmlerror.h>
64
65/* #define DEBUG */
66/* #define DEBUG_STEP */
67/* #define DEBUG_EXPR */
68
69void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
70double xmlXPathStringEvalNumber(const xmlChar *str);
71
Daniel Veillard9e7160d2001-03-18 23:17:47 +000072/************************************************************************
73 * *
74 * Floating point stuff *
75 * *
76 ************************************************************************/
77
Owen Taylor3473f882001-02-23 17:55:21 +000078/*
Owen Taylor3473f882001-02-23 17:55:21 +000079 * The lack of portability of this section of the libc is annoying !
80 */
81double xmlXPathNAN = 0;
82double xmlXPathPINF = 1;
83double xmlXPathNINF = -1;
84
85#ifndef isinf
86#ifndef HAVE_ISINF
87
88#if HAVE_FPCLASS
89
90int isinf(double d) {
91 fpclass_t type = fpclass(d);
92 switch (type) {
93 case FP_NINF:
94 return(-1);
95 case FP_PINF:
96 return(1);
97 }
98 return(0);
99}
100
101#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
102
103#if HAVE_FP_CLASS_H
104#include <fp_class.h>
105#endif
106
107int isinf(double d) {
108#if HAVE_FP_CLASS
109 int fpclass = fp_class(d);
110#else
111 int fpclass = fp_class_d(d);
112#endif
113 if (fpclass == FP_POS_INF)
114 return(1);
115 if (fpclass == FP_NEG_INF)
116 return(-1);
117 return(0);
118}
119
120#elif defined(HAVE_CLASS)
121
122int isinf(double d) {
123 int fpclass = class(d);
124 if (fpclass == FP_PLUS_INF)
125 return(1);
126 if (fpclass == FP_MINUS_INF)
127 return(-1);
128 return(0);
129}
130#elif defined(finite) || defined(HAVE_FINITE)
131int isinf(double x) { return !finite(x) && x==x; }
132#elif defined(HUGE_VAL)
133int isinf(double x)
134{
135 if (x == HUGE_VAL)
136 return(1);
137 if (x == -HUGE_VAL)
138 return(-1);
139 return(0);
140}
141#endif
142
143#endif /* ! HAVE_ISINF */
144#endif /* ! defined(isinf) */
145
146#ifndef isnan
147#ifndef HAVE_ISNAN
148
149#ifdef HAVE_ISNAND
150#define isnan(f) isnand(f)
151#endif /* HAVE_iSNAND */
152
153#endif /* ! HAVE_iSNAN */
154#endif /* ! defined(isnan) */
155
156/**
157 * xmlXPathInit:
158 *
159 * Initialize the XPath environment
160 */
161void
162xmlXPathInit(void) {
163 static int initialized = 0;
164
165 if (initialized) return;
166
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000167 xmlXPathNAN = 0.0 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000168
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000169 xmlXPathPINF = 1 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000170
Daniel Veillard1731d6a2001-04-10 16:38:06 +0000171 xmlXPathNINF = -1 / 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +0000172
173 initialized = 1;
174}
175
176/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000177 * *
178 * Parser Types *
179 * *
180 ************************************************************************/
181
182/*
183 * Types are private:
184 */
185
186typedef enum {
187 XPATH_OP_END=0,
188 XPATH_OP_AND,
189 XPATH_OP_OR,
190 XPATH_OP_EQUAL,
191 XPATH_OP_CMP,
192 XPATH_OP_PLUS,
193 XPATH_OP_MULT,
194 XPATH_OP_UNION,
195 XPATH_OP_ROOT,
196 XPATH_OP_NODE,
197 XPATH_OP_RESET,
198 XPATH_OP_COLLECT,
199 XPATH_OP_VALUE,
200 XPATH_OP_VARIABLE,
201 XPATH_OP_FUNCTION,
202 XPATH_OP_ARG,
203 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000204 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000205 XPATH_OP_SORT
206#ifdef LIBXML_XPTR_ENABLED
207 ,XPATH_OP_RANGETO
208#endif
209} xmlXPathOp;
210
211typedef enum {
212 AXIS_ANCESTOR = 1,
213 AXIS_ANCESTOR_OR_SELF,
214 AXIS_ATTRIBUTE,
215 AXIS_CHILD,
216 AXIS_DESCENDANT,
217 AXIS_DESCENDANT_OR_SELF,
218 AXIS_FOLLOWING,
219 AXIS_FOLLOWING_SIBLING,
220 AXIS_NAMESPACE,
221 AXIS_PARENT,
222 AXIS_PRECEDING,
223 AXIS_PRECEDING_SIBLING,
224 AXIS_SELF
225} xmlXPathAxisVal;
226
227typedef enum {
228 NODE_TEST_NONE = 0,
229 NODE_TEST_TYPE = 1,
230 NODE_TEST_PI = 2,
231 NODE_TEST_ALL = 3,
232 NODE_TEST_NS = 4,
233 NODE_TEST_NAME = 5
234} xmlXPathTestVal;
235
236typedef enum {
237 NODE_TYPE_NODE = 0,
238 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
239 NODE_TYPE_TEXT = XML_TEXT_NODE,
240 NODE_TYPE_PI = XML_PI_NODE
241} xmlXPathTypeVal;
242
243
244typedef struct _xmlXPathStepOp xmlXPathStepOp;
245typedef xmlXPathStepOp *xmlXPathStepOpPtr;
246struct _xmlXPathStepOp {
247 xmlXPathOp op;
248 int ch1;
249 int ch2;
250 int value;
251 int value2;
252 int value3;
253 void *value4;
254 void *value5;
255};
256
257struct _xmlXPathCompExpr {
258 int nbStep;
259 int maxStep;
260 xmlXPathStepOp *steps; /* ops for computation */
261 int last;
262};
263
264/************************************************************************
265 * *
266 * Parser Type functions *
267 * *
268 ************************************************************************/
269
270/**
271 * xmlXPathNewCompExpr:
272 *
273 * Create a new Xpath component
274 *
275 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
276 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000277static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000278xmlXPathNewCompExpr(void) {
279 xmlXPathCompExprPtr cur;
280
281 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
282 if (cur == NULL) {
283 xmlGenericError(xmlGenericErrorContext,
284 "xmlXPathNewCompExpr : malloc failed\n");
285 return(NULL);
286 }
287 memset(cur, 0, sizeof(xmlXPathCompExpr));
288 cur->maxStep = 10;
289 cur->nbStep = 0;
290 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
291 sizeof(xmlXPathStepOp));
292 if (cur->steps == NULL) {
293 xmlGenericError(xmlGenericErrorContext,
294 "xmlXPathNewCompExpr : malloc failed\n");
295 xmlFree(cur);
296 return(NULL);
297 }
298 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
299 cur->last = -1;
300 return(cur);
301}
302
303/**
304 * xmlXPathFreeCompExpr:
305 * @comp: an XPATH comp
306 *
307 * Free up the memory allocated by @comp
308 */
309void
310xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) {
311 xmlXPathStepOpPtr op;
312 int i;
313
314 if (comp == NULL)
315 return;
316 for (i = 0;i < comp->nbStep;i++) {
317 op = &comp->steps[i];
318 if (op->value4 != NULL) {
319 if (op->op == XPATH_OP_VALUE)
320 xmlXPathFreeObject(op->value4);
321 else
322 xmlFree(op->value4);
323 }
324 if (op->value5 != NULL)
325 xmlFree(op->value5);
326 }
327 if (comp->steps != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000328 xmlFree(comp->steps);
329 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000330 xmlFree(comp);
331}
332
333/**
334 * xmlXPathCompExprAdd:
335 * @comp: the compiled expression
336 * @ch1: first child index
337 * @ch2: second child index
338 * @op: an op
339 * @value: the first int value
340 * @value2: the second int value
341 * @value3: the third int value
342 * @value4: the first string value
343 * @value5: the second string value
344 *
345 * Add an step to an XPath Compiled Expression
346 *
347 * Returns -1 in case of failure, the index otherwise
348 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000349static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000350xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
351 xmlXPathOp op, int value,
352 int value2, int value3, void *value4, void *value5) {
353 if (comp->nbStep >= comp->maxStep) {
354 xmlXPathStepOp *real;
355
356 comp->maxStep *= 2;
357 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
358 comp->maxStep * sizeof(xmlXPathStepOp));
359 if (real == NULL) {
360 comp->maxStep /= 2;
361 xmlGenericError(xmlGenericErrorContext,
362 "xmlXPathCompExprAdd : realloc failed\n");
363 return(-1);
364 }
365 comp->steps = real;
366 }
367 comp->last = comp->nbStep;
368 comp->steps[comp->nbStep].ch1 = ch1;
369 comp->steps[comp->nbStep].ch2 = ch2;
370 comp->steps[comp->nbStep].op = op;
371 comp->steps[comp->nbStep].value = value;
372 comp->steps[comp->nbStep].value2 = value2;
373 comp->steps[comp->nbStep].value3 = value3;
374 comp->steps[comp->nbStep].value4 = value4;
375 comp->steps[comp->nbStep].value5 = value5;
376 return(comp->nbStep++);
377}
378
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000379#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
380 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
381 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000382#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
383 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
384 (op), (val), (val2), (val3), (val4), (val5))
385
386#define PUSH_LEAVE_EXPR(op, val, val2) \
387xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
388
389#define PUSH_UNARY_EXPR(op, ch, val, val2) \
390xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
391
392#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
393xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
394
395/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000396 * *
397 * Debugging related functions *
398 * *
399 ************************************************************************/
400
401#define TODO \
402 xmlGenericError(xmlGenericErrorContext, \
403 "Unimplemented block at %s:%d\n", \
404 __FILE__, __LINE__);
405
406#define STRANGE \
407 xmlGenericError(xmlGenericErrorContext, \
408 "Internal error at %s:%d\n", \
409 __FILE__, __LINE__);
410
411#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000412static void
413xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000414 int i;
415 char shift[100];
416
417 for (i = 0;((i < depth) && (i < 25));i++)
418 shift[2 * i] = shift[2 * i + 1] = ' ';
419 shift[2 * i] = shift[2 * i + 1] = 0;
420 if (cur == NULL) {
421 fprintf(output, shift);
422 fprintf(output, "Node is NULL !\n");
423 return;
424
425 }
426
427 if ((cur->type == XML_DOCUMENT_NODE) ||
428 (cur->type == XML_HTML_DOCUMENT_NODE)) {
429 fprintf(output, shift);
430 fprintf(output, " /\n");
431 } else if (cur->type == XML_ATTRIBUTE_NODE)
432 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
433 else
434 xmlDebugDumpOneNode(output, cur, depth);
435}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000436static void
437xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000438 xmlNodePtr tmp;
439 int i;
440 char shift[100];
441
442 for (i = 0;((i < depth) && (i < 25));i++)
443 shift[2 * i] = shift[2 * i + 1] = ' ';
444 shift[2 * i] = shift[2 * i + 1] = 0;
445 if (cur == NULL) {
446 fprintf(output, shift);
447 fprintf(output, "Node is NULL !\n");
448 return;
449
450 }
451
452 while (cur != NULL) {
453 tmp = cur;
454 cur = cur->next;
455 xmlDebugDumpOneNode(output, tmp, depth);
456 }
457}
Owen Taylor3473f882001-02-23 17:55:21 +0000458
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000459static void
460xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000461 int i;
462 char shift[100];
463
464 for (i = 0;((i < depth) && (i < 25));i++)
465 shift[2 * i] = shift[2 * i + 1] = ' ';
466 shift[2 * i] = shift[2 * i + 1] = 0;
467
468 if (cur == NULL) {
469 fprintf(output, shift);
470 fprintf(output, "NodeSet is NULL !\n");
471 return;
472
473 }
474
Daniel Veillard911f49a2001-04-07 15:39:35 +0000475 if (cur != NULL) {
476 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
477 for (i = 0;i < cur->nodeNr;i++) {
478 fprintf(output, shift);
479 fprintf(output, "%d", i + 1);
480 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
481 }
Owen Taylor3473f882001-02-23 17:55:21 +0000482 }
483}
484
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000485static void
486xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000487 int i;
488 char shift[100];
489
490 for (i = 0;((i < depth) && (i < 25));i++)
491 shift[2 * i] = shift[2 * i + 1] = ' ';
492 shift[2 * i] = shift[2 * i + 1] = 0;
493
494 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
495 fprintf(output, shift);
496 fprintf(output, "Value Tree is NULL !\n");
497 return;
498
499 }
500
501 fprintf(output, shift);
502 fprintf(output, "%d", i + 1);
503 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
504}
Owen Taylor3473f882001-02-23 17:55:21 +0000505#if defined(LIBXML_XPTR_ENABLED)
506void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000507static void
508xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000509 int i;
510 char shift[100];
511
512 for (i = 0;((i < depth) && (i < 25));i++)
513 shift[2 * i] = shift[2 * i + 1] = ' ';
514 shift[2 * i] = shift[2 * i + 1] = 0;
515
516 if (cur == NULL) {
517 fprintf(output, shift);
518 fprintf(output, "LocationSet is NULL !\n");
519 return;
520
521 }
522
523 for (i = 0;i < cur->locNr;i++) {
524 fprintf(output, shift);
525 fprintf(output, "%d : ", i + 1);
526 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
527 }
528}
529#endif
530
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000531/**
532 * xmlXPathDebugDumpObject:
533 * @output: the FILE * to dump the output
534 * @cur: the object to inspect
535 * @depth: indentation level
536 *
537 * Dump the content of the object for debugging purposes
538 */
539void
540xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000541 int i;
542 char shift[100];
543
544 for (i = 0;((i < depth) && (i < 25));i++)
545 shift[2 * i] = shift[2 * i + 1] = ' ';
546 shift[2 * i] = shift[2 * i + 1] = 0;
547
548 fprintf(output, shift);
549
550 if (cur == NULL) {
551 fprintf(output, "Object is empty (NULL)\n");
552 return;
553 }
554 switch(cur->type) {
555 case XPATH_UNDEFINED:
556 fprintf(output, "Object is uninitialized\n");
557 break;
558 case XPATH_NODESET:
559 fprintf(output, "Object is a Node Set :\n");
560 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
561 break;
562 case XPATH_XSLT_TREE:
563 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000564 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000565 break;
566 case XPATH_BOOLEAN:
567 fprintf(output, "Object is a Boolean : ");
568 if (cur->boolval) fprintf(output, "true\n");
569 else fprintf(output, "false\n");
570 break;
571 case XPATH_NUMBER:
572 fprintf(output, "Object is a number : %0g\n", cur->floatval);
573 break;
574 case XPATH_STRING:
575 fprintf(output, "Object is a string : ");
576 xmlDebugDumpString(output, cur->stringval);
577 fprintf(output, "\n");
578 break;
579 case XPATH_POINT:
580 fprintf(output, "Object is a point : index %d in node", cur->index);
581 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
582 fprintf(output, "\n");
583 break;
584 case XPATH_RANGE:
585 if ((cur->user2 == NULL) ||
586 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
587 fprintf(output, "Object is a collapsed range :\n");
588 fprintf(output, shift);
589 if (cur->index >= 0)
590 fprintf(output, "index %d in ", cur->index);
591 fprintf(output, "node\n");
592 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
593 depth + 1);
594 } else {
595 fprintf(output, "Object is a range :\n");
596 fprintf(output, shift);
597 fprintf(output, "From ");
598 if (cur->index >= 0)
599 fprintf(output, "index %d in ", cur->index);
600 fprintf(output, "node\n");
601 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
602 depth + 1);
603 fprintf(output, shift);
604 fprintf(output, "To ");
605 if (cur->index2 >= 0)
606 fprintf(output, "index %d in ", cur->index2);
607 fprintf(output, "node\n");
608 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
609 depth + 1);
610 fprintf(output, "\n");
611 }
612 break;
613 case XPATH_LOCATIONSET:
614#if defined(LIBXML_XPTR_ENABLED)
615 fprintf(output, "Object is a Location Set:\n");
616 xmlXPathDebugDumpLocationSet(output,
617 (xmlLocationSetPtr) cur->user, depth);
618#endif
619 break;
620 case XPATH_USERS:
621 fprintf(output, "Object is user defined\n");
622 break;
623 }
624}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000625
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000626static void
627xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000628 xmlXPathStepOpPtr op, int depth) {
629 int i;
630 char shift[100];
631
632 for (i = 0;((i < depth) && (i < 25));i++)
633 shift[2 * i] = shift[2 * i + 1] = ' ';
634 shift[2 * i] = shift[2 * i + 1] = 0;
635
636 fprintf(output, shift);
637 if (op == NULL) {
638 fprintf(output, "Step is NULL\n");
639 return;
640 }
641 switch (op->op) {
642 case XPATH_OP_END:
643 fprintf(output, "END"); break;
644 case XPATH_OP_AND:
645 fprintf(output, "AND"); break;
646 case XPATH_OP_OR:
647 fprintf(output, "OR"); break;
648 case XPATH_OP_EQUAL:
649 if (op->value)
650 fprintf(output, "EQUAL =");
651 else
652 fprintf(output, "EQUAL !=");
653 break;
654 case XPATH_OP_CMP:
655 if (op->value)
656 fprintf(output, "CMP <");
657 else
658 fprintf(output, "CMP >");
659 if (!op->value2)
660 fprintf(output, "=");
661 break;
662 case XPATH_OP_PLUS:
663 if (op->value == 0)
664 fprintf(output, "PLUS -");
665 else if (op->value == 1)
666 fprintf(output, "PLUS +");
667 else if (op->value == 2)
668 fprintf(output, "PLUS unary -");
669 else if (op->value == 3)
670 fprintf(output, "PLUS unary - -");
671 break;
672 case XPATH_OP_MULT:
673 if (op->value == 0)
674 fprintf(output, "MULT *");
675 else if (op->value == 1)
676 fprintf(output, "MULT div");
677 else
678 fprintf(output, "MULT mod");
679 break;
680 case XPATH_OP_UNION:
681 fprintf(output, "UNION"); break;
682 case XPATH_OP_ROOT:
683 fprintf(output, "ROOT"); break;
684 case XPATH_OP_NODE:
685 fprintf(output, "NODE"); break;
686 case XPATH_OP_RESET:
687 fprintf(output, "RESET"); break;
688 case XPATH_OP_SORT:
689 fprintf(output, "SORT"); break;
690 case XPATH_OP_COLLECT: {
691 xmlXPathAxisVal axis = op->value;
692 xmlXPathTestVal test = op->value2;
693 xmlXPathTypeVal type = op->value3;
694 const xmlChar *prefix = op->value4;
695 const xmlChar *name = op->value5;
696
697 fprintf(output, "COLLECT ");
698 switch (axis) {
699 case AXIS_ANCESTOR:
700 fprintf(output, " 'ancestors' "); break;
701 case AXIS_ANCESTOR_OR_SELF:
702 fprintf(output, " 'ancestors-or-self' "); break;
703 case AXIS_ATTRIBUTE:
704 fprintf(output, " 'attributes' "); break;
705 case AXIS_CHILD:
706 fprintf(output, " 'child' "); break;
707 case AXIS_DESCENDANT:
708 fprintf(output, " 'descendant' "); break;
709 case AXIS_DESCENDANT_OR_SELF:
710 fprintf(output, " 'descendant-or-self' "); break;
711 case AXIS_FOLLOWING:
712 fprintf(output, " 'following' "); break;
713 case AXIS_FOLLOWING_SIBLING:
714 fprintf(output, " 'following-siblings' "); break;
715 case AXIS_NAMESPACE:
716 fprintf(output, " 'namespace' "); break;
717 case AXIS_PARENT:
718 fprintf(output, " 'parent' "); break;
719 case AXIS_PRECEDING:
720 fprintf(output, " 'preceding' "); break;
721 case AXIS_PRECEDING_SIBLING:
722 fprintf(output, " 'preceding-sibling' "); break;
723 case AXIS_SELF:
724 fprintf(output, " 'self' "); break;
725 }
726 switch (test) {
727 case NODE_TEST_NONE:
728 fprintf(output, "'none' "); break;
729 case NODE_TEST_TYPE:
730 fprintf(output, "'type' "); break;
731 case NODE_TEST_PI:
732 fprintf(output, "'PI' "); break;
733 case NODE_TEST_ALL:
734 fprintf(output, "'all' "); break;
735 case NODE_TEST_NS:
736 fprintf(output, "'namespace' "); break;
737 case NODE_TEST_NAME:
738 fprintf(output, "'name' "); break;
739 }
740 switch (type) {
741 case NODE_TYPE_NODE:
742 fprintf(output, "'node' "); break;
743 case NODE_TYPE_COMMENT:
744 fprintf(output, "'comment' "); break;
745 case NODE_TYPE_TEXT:
746 fprintf(output, "'text' "); break;
747 case NODE_TYPE_PI:
748 fprintf(output, "'PI' "); break;
749 }
750 if (prefix != NULL)
751 fprintf(output, "%s:", prefix);
752 if (name != NULL)
753 fprintf(output, "%s", name);
754 break;
755
756 }
757 case XPATH_OP_VALUE: {
758 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
759
760 fprintf(output, "ELEM ");
761 xmlXPathDebugDumpObject(output, object, 0);
762 goto finish;
763 }
764 case XPATH_OP_VARIABLE: {
765 const xmlChar *prefix = op->value5;
766 const xmlChar *name = op->value4;
767
768 if (prefix != NULL)
769 fprintf(output, "VARIABLE %s:%s", prefix, name);
770 else
771 fprintf(output, "VARIABLE %s", name);
772 break;
773 }
774 case XPATH_OP_FUNCTION: {
775 int nbargs = op->value;
776 const xmlChar *prefix = op->value5;
777 const xmlChar *name = op->value4;
778
779 if (prefix != NULL)
780 fprintf(output, "FUNCTION %s:%s(%d args)",
781 prefix, name, nbargs);
782 else
783 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
784 break;
785 }
786 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
787 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000788 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000789 default:
790 fprintf(output, "UNKNOWN %d\n", op->op); return;
791 }
792 fprintf(output, "\n");
793finish:
794 if (op->ch1 >= 0)
795 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
796 if (op->ch2 >= 0)
797 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
798}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000799
800void
801xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
802 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000803 int i;
804 char shift[100];
805
806 for (i = 0;((i < depth) && (i < 25));i++)
807 shift[2 * i] = shift[2 * i + 1] = ' ';
808 shift[2 * i] = shift[2 * i + 1] = 0;
809
810 fprintf(output, shift);
811
812 if (comp == NULL) {
813 fprintf(output, "Compiled Expression is NULL\n");
814 return;
815 }
816 fprintf(output, "Compiled Expression : %d elements\n",
817 comp->nbStep);
818 i = comp->last;
819 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
820}
Owen Taylor3473f882001-02-23 17:55:21 +0000821#endif
822
823/************************************************************************
824 * *
825 * Parser stacks related functions and macros *
826 * *
827 ************************************************************************/
828
829/*
830 * Generic function for accessing stacks in the Parser Context
831 */
832
833#define PUSH_AND_POP(type, name) \
834extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
835 if (ctxt->name##Nr >= ctxt->name##Max) { \
836 ctxt->name##Max *= 2; \
837 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
838 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
839 if (ctxt->name##Tab == NULL) { \
840 xmlGenericError(xmlGenericErrorContext, \
841 "realloc failed !\n"); \
842 return(0); \
843 } \
844 } \
845 ctxt->name##Tab[ctxt->name##Nr] = value; \
846 ctxt->name = value; \
847 return(ctxt->name##Nr++); \
848} \
849extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
850 type ret; \
851 if (ctxt->name##Nr <= 0) return(0); \
852 ctxt->name##Nr--; \
853 if (ctxt->name##Nr > 0) \
854 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
855 else \
856 ctxt->name = NULL; \
857 ret = ctxt->name##Tab[ctxt->name##Nr]; \
858 ctxt->name##Tab[ctxt->name##Nr] = 0; \
859 return(ret); \
860} \
861
862PUSH_AND_POP(xmlXPathObjectPtr, value)
863
864/*
865 * Macros for accessing the content. Those should be used only by the parser,
866 * and not exported.
867 *
868 * Dirty macros, i.e. one need to make assumption on the context to use them
869 *
870 * CUR_PTR return the current pointer to the xmlChar to be parsed.
871 * CUR returns the current xmlChar value, i.e. a 8 bit value
872 * in ISO-Latin or UTF-8.
873 * This should be used internally by the parser
874 * only to compare to ASCII values otherwise it would break when
875 * running with UTF-8 encoding.
876 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
877 * to compare on ASCII based substring.
878 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
879 * strings within the parser.
880 * CURRENT Returns the current char value, with the full decoding of
881 * UTF-8 if we are using this mode. It returns an int.
882 * NEXT Skip to the next character, this does the proper decoding
883 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
884 * It returns the pointer to the current xmlChar.
885 */
886
887#define CUR (*ctxt->cur)
888#define SKIP(val) ctxt->cur += (val)
889#define NXT(val) ctxt->cur[(val)]
890#define CUR_PTR ctxt->cur
891
892#define SKIP_BLANKS \
893 while (IS_BLANK(*(ctxt->cur))) NEXT
894
895#define CURRENT (*ctxt->cur)
896#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
897
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000898
899#ifndef DBL_DIG
900#define DBL_DIG 16
901#endif
902#ifndef DBL_EPSILON
903#define DBL_EPSILON 1E-9
904#endif
905
906#define UPPER_DOUBLE 1E9
907#define LOWER_DOUBLE 1E-5
908
909#define INTEGER_DIGITS DBL_DIG
910#define FRACTION_DIGITS (DBL_DIG + 1)
911#define EXPONENT_DIGITS (3 + 2)
912
913/**
914 * xmlXPathFormatNumber:
915 * @number: number to format
916 * @buffer: output buffer
917 * @buffersize: size of output buffer
918 *
919 * Convert the number into a string representation.
920 */
921static void
922xmlXPathFormatNumber(double number, char buffer[], int buffersize)
923{
924 switch (isinf(number)) {
925 case 1:
926 if (buffersize > (int)sizeof("+Infinity"))
927 sprintf(buffer, "+Infinity");
928 break;
929 case -1:
930 if (buffersize > (int)sizeof("-Infinity"))
931 sprintf(buffer, "-Infinity");
932 break;
933 default:
934 if (isnan(number)) {
935 if (buffersize > (int)sizeof("NaN"))
936 sprintf(buffer, "NaN");
937 } else {
938 char work[INTEGER_DIGITS + FRACTION_DIGITS + EXPONENT_DIGITS + 1];
939 char *pointer;
940 char *start;
941 int i;
942 int digits;
943 int is_negative;
944 int use_scientific;
945 int exponent;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000946 int indx;
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000947 int count;
948 double n;
949
950 i = digits = 0;
951 is_negative = (number < 0.0);
952 if (is_negative)
953 number = -number;
954
955 /* Scale number */
956 n = log10(number);
957 exponent = (isinf(n) == -1) ? 0 : (int)n;
958 use_scientific = (((number <= LOWER_DOUBLE) ||
959 (number > UPPER_DOUBLE)) &&
960 (number != 0));
961 if (use_scientific) {
962 number /= pow(10.0, (double)exponent);
963 while (number < 1.0) {
964 number *= 10.0;
965 exponent--;
966 }
967 }
968
969 /* Integer part is build from back */
970 pointer = &work[INTEGER_DIGITS + 1];
971 if (number < 1.0) {
972 *(--pointer) = '0';
973 digits++;
974 } else {
975 n = number;
976 for (i = 1; i < INTEGER_DIGITS - 1; i++) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000977 indx = (int)n % 10;
978 *(--pointer) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +0000979 n /= 10.0;
980 if (n < 1.0)
981 break;
982 }
983 digits += i;
984 }
985 if (is_negative) {
986 *(--pointer) = '-';
987 digits++;
988 }
989 start = pointer;
990
991 /* Fraction part is build from front */
992 i = 0;
993 pointer = &work[INTEGER_DIGITS + 1];
994 if (number - floor(number) > DBL_EPSILON) {
995 *(pointer++) = '.';
996 i++;
997 n = number;
998 count = 0;
999 while (i < FRACTION_DIGITS) {
1000 n -= floor(n);
1001 n *= 10.0;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001002 indx = (int)n % 10;
1003 *(pointer++) = "0123456789"[indx];
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001004 i++;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001005 if ((indx != 0) || (count > 0))
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001006 count++;
1007 if ((n > 10.0) || (count > FRACTION_DIGITS / 2))
1008 break;
1009 }
1010 }
1011 /* Remove trailing zeroes */
1012 while ((pointer[-1] == '0') && (i > 0)) {
1013 pointer--;
1014 i--;
1015 }
1016 digits += i;
1017
1018 if (use_scientific) {
1019 *(pointer++) = 'e';
1020 digits++;
1021 if (exponent < 0) {
1022 *(pointer++) = '-';
1023 exponent = -exponent;
1024 } else {
1025 *(pointer++) = '+';
1026 }
1027 digits++;
1028 if (exponent >= 100)
1029 pointer += 2;
1030 else if (exponent >= 10)
1031 pointer += 1;
1032 while (exponent >= 1) {
1033 *(pointer--) = "0123456789"[exponent % 10];
1034 exponent /= 10;
1035 digits++;
1036 }
1037 }
1038
1039 if (digits >= buffersize)
1040 digits = buffersize - 1;
1041
1042 memcpy(buffer, start, digits);
1043 buffer[digits] = 0;
1044 }
1045 break;
1046 }
1047}
1048
Owen Taylor3473f882001-02-23 17:55:21 +00001049/************************************************************************
1050 * *
1051 * Error handling routines *
1052 * *
1053 ************************************************************************/
1054
1055
1056const char *xmlXPathErrorMessages[] = {
1057 "Ok",
1058 "Number encoding",
1059 "Unfinished litteral",
1060 "Start of litteral",
1061 "Expected $ for variable reference",
1062 "Undefined variable",
1063 "Invalid predicate",
1064 "Invalid expression",
1065 "Missing closing curly brace",
1066 "Unregistered function",
1067 "Invalid operand",
1068 "Invalid type",
1069 "Invalid number of arguments",
1070 "Invalid context size",
1071 "Invalid context position",
1072 "Memory allocation error",
1073 "Syntax error",
1074 "Resource error",
1075 "Sub resource error",
1076 "Undefined namespace prefix"
1077};
1078
1079/**
1080 * xmlXPathError:
1081 * @ctxt: the XPath Parser context
1082 * @file: the file name
1083 * @line: the line number
1084 * @no: the error number
1085 *
1086 * Create a new xmlNodeSetPtr of type double and of value @val
1087 *
1088 * Returns the newly created object.
1089 */
1090void
1091xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1092 int line, int no) {
1093 int n;
1094 const xmlChar *cur;
1095 const xmlChar *base;
1096
1097 xmlGenericError(xmlGenericErrorContext,
1098 "Error %s:%d: %s\n", file, line,
1099 xmlXPathErrorMessages[no]);
1100
1101 cur = ctxt->cur;
1102 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001103 if ((cur == NULL) || (base == NULL))
1104 return;
1105
Owen Taylor3473f882001-02-23 17:55:21 +00001106 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1107 cur--;
1108 }
1109 n = 0;
1110 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1111 cur--;
1112 if ((*cur == '\n') || (*cur == '\r')) cur++;
1113 base = cur;
1114 n = 0;
1115 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1116 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1117 n++;
1118 }
1119 xmlGenericError(xmlGenericErrorContext, "\n");
1120 cur = ctxt->cur;
1121 while ((*cur == '\n') || (*cur == '\r'))
1122 cur--;
1123 n = 0;
1124 while ((cur != base) && (n++ < 80)) {
1125 xmlGenericError(xmlGenericErrorContext, " ");
1126 base++;
1127 }
1128 xmlGenericError(xmlGenericErrorContext,"^\n");
1129}
1130
1131
1132/************************************************************************
1133 * *
1134 * Routines to handle NodeSets *
1135 * *
1136 ************************************************************************/
1137
1138/**
1139 * xmlXPathCmpNodes:
1140 * @node1: the first node
1141 * @node2: the second node
1142 *
1143 * Compare two nodes w.r.t document order
1144 *
1145 * Returns -2 in case of error 1 if first point < second point, 0 if
1146 * that's the same node, -1 otherwise
1147 */
1148int
1149xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1150 int depth1, depth2;
1151 xmlNodePtr cur, root;
1152
1153 if ((node1 == NULL) || (node2 == NULL))
1154 return(-2);
1155 /*
1156 * a couple of optimizations which will avoid computations in most cases
1157 */
1158 if (node1 == node2)
1159 return(0);
1160 if (node1 == node2->prev)
1161 return(1);
1162 if (node1 == node2->next)
1163 return(-1);
1164
1165 /*
1166 * compute depth to root
1167 */
1168 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1169 if (cur == node1)
1170 return(1);
1171 depth2++;
1172 }
1173 root = cur;
1174 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1175 if (cur == node2)
1176 return(-1);
1177 depth1++;
1178 }
1179 /*
1180 * Distinct document (or distinct entities :-( ) case.
1181 */
1182 if (root != cur) {
1183 return(-2);
1184 }
1185 /*
1186 * get the nearest common ancestor.
1187 */
1188 while (depth1 > depth2) {
1189 depth1--;
1190 node1 = node1->parent;
1191 }
1192 while (depth2 > depth1) {
1193 depth2--;
1194 node2 = node2->parent;
1195 }
1196 while (node1->parent != node2->parent) {
1197 node1 = node1->parent;
1198 node2 = node2->parent;
1199 /* should not happen but just in case ... */
1200 if ((node1 == NULL) || (node2 == NULL))
1201 return(-2);
1202 }
1203 /*
1204 * Find who's first.
1205 */
1206 if (node1 == node2->next)
1207 return(-1);
1208 for (cur = node1->next;cur != NULL;cur = cur->next)
1209 if (cur == node2)
1210 return(1);
1211 return(-1); /* assume there is no sibling list corruption */
1212}
1213
1214/**
1215 * xmlXPathNodeSetSort:
1216 * @set: the node set
1217 *
1218 * Sort the node set in document order
1219 */
1220void
1221xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001222 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001223 xmlNodePtr tmp;
1224
1225 if (set == NULL)
1226 return;
1227
1228 /* Use Shell's sort to sort the node-set */
1229 len = set->nodeNr;
1230 for (incr = len / 2; incr > 0; incr /= 2) {
1231 for (i = incr; i < len; i++) {
1232 j = i - incr;
1233 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001234 if (xmlXPathCmpNodes(set->nodeTab[j],
1235 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001236 tmp = set->nodeTab[j];
1237 set->nodeTab[j] = set->nodeTab[j + incr];
1238 set->nodeTab[j + incr] = tmp;
1239 j -= incr;
1240 } else
1241 break;
1242 }
1243 }
1244 }
1245}
1246
1247#define XML_NODESET_DEFAULT 10
1248/**
1249 * xmlXPathNodeSetCreate:
1250 * @val: an initial xmlNodePtr, or NULL
1251 *
1252 * Create a new xmlNodeSetPtr of type double and of value @val
1253 *
1254 * Returns the newly created object.
1255 */
1256xmlNodeSetPtr
1257xmlXPathNodeSetCreate(xmlNodePtr val) {
1258 xmlNodeSetPtr ret;
1259
1260 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1261 if (ret == NULL) {
1262 xmlGenericError(xmlGenericErrorContext,
1263 "xmlXPathNewNodeSet: out of memory\n");
1264 return(NULL);
1265 }
1266 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1267 if (val != NULL) {
1268 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1269 sizeof(xmlNodePtr));
1270 if (ret->nodeTab == NULL) {
1271 xmlGenericError(xmlGenericErrorContext,
1272 "xmlXPathNewNodeSet: out of memory\n");
1273 return(NULL);
1274 }
1275 memset(ret->nodeTab, 0 ,
1276 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1277 ret->nodeMax = XML_NODESET_DEFAULT;
1278 ret->nodeTab[ret->nodeNr++] = val;
1279 }
1280 return(ret);
1281}
1282
1283/**
1284 * xmlXPathNodeSetAdd:
1285 * @cur: the initial node set
1286 * @val: a new xmlNodePtr
1287 *
1288 * add a new xmlNodePtr ot an existing NodeSet
1289 */
1290void
1291xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1292 int i;
1293
1294 if (val == NULL) return;
1295
1296 /*
1297 * check against doublons
1298 */
1299 for (i = 0;i < cur->nodeNr;i++)
1300 if (cur->nodeTab[i] == val) return;
1301
1302 /*
1303 * grow the nodeTab if needed
1304 */
1305 if (cur->nodeMax == 0) {
1306 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1307 sizeof(xmlNodePtr));
1308 if (cur->nodeTab == NULL) {
1309 xmlGenericError(xmlGenericErrorContext,
1310 "xmlXPathNodeSetAdd: out of memory\n");
1311 return;
1312 }
1313 memset(cur->nodeTab, 0 ,
1314 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1315 cur->nodeMax = XML_NODESET_DEFAULT;
1316 } else if (cur->nodeNr == cur->nodeMax) {
1317 xmlNodePtr *temp;
1318
1319 cur->nodeMax *= 2;
1320 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1321 sizeof(xmlNodePtr));
1322 if (temp == NULL) {
1323 xmlGenericError(xmlGenericErrorContext,
1324 "xmlXPathNodeSetAdd: out of memory\n");
1325 return;
1326 }
1327 cur->nodeTab = temp;
1328 }
1329 cur->nodeTab[cur->nodeNr++] = val;
1330}
1331
1332/**
1333 * xmlXPathNodeSetAddUnique:
1334 * @cur: the initial node set
1335 * @val: a new xmlNodePtr
1336 *
1337 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1338 * when we are sure the node is not already in the set.
1339 */
1340void
1341xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1342 if (val == NULL) return;
1343
1344 /*
1345 * grow the nodeTab if needed
1346 */
1347 if (cur->nodeMax == 0) {
1348 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1349 sizeof(xmlNodePtr));
1350 if (cur->nodeTab == NULL) {
1351 xmlGenericError(xmlGenericErrorContext,
1352 "xmlXPathNodeSetAddUnique: out of memory\n");
1353 return;
1354 }
1355 memset(cur->nodeTab, 0 ,
1356 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1357 cur->nodeMax = XML_NODESET_DEFAULT;
1358 } else if (cur->nodeNr == cur->nodeMax) {
1359 xmlNodePtr *temp;
1360
1361 cur->nodeMax *= 2;
1362 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1363 sizeof(xmlNodePtr));
1364 if (temp == NULL) {
1365 xmlGenericError(xmlGenericErrorContext,
1366 "xmlXPathNodeSetAddUnique: out of memory\n");
1367 return;
1368 }
1369 cur->nodeTab = temp;
1370 }
1371 cur->nodeTab[cur->nodeNr++] = val;
1372}
1373
1374/**
1375 * xmlXPathNodeSetMerge:
1376 * @val1: the first NodeSet or NULL
1377 * @val2: the second NodeSet
1378 *
1379 * Merges two nodesets, all nodes from @val2 are added to @val1
1380 * if @val1 is NULL, a new set is created and copied from @val2
1381 *
1382 * Returns val1 once extended or NULL in case of error.
1383 */
1384xmlNodeSetPtr
1385xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001386 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001387
1388 if (val2 == NULL) return(val1);
1389 if (val1 == NULL) {
1390 val1 = xmlXPathNodeSetCreate(NULL);
1391 }
1392
1393 initNr = val1->nodeNr;
1394
1395 for (i = 0;i < val2->nodeNr;i++) {
1396 /*
1397 * check against doublons
1398 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001399 skip = 0;
1400 for (j = 0; j < initNr; j++) {
1401 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1402 skip = 1;
1403 break;
1404 }
1405 }
1406 if (skip)
1407 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001408
1409 /*
1410 * grow the nodeTab if needed
1411 */
1412 if (val1->nodeMax == 0) {
1413 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1414 sizeof(xmlNodePtr));
1415 if (val1->nodeTab == NULL) {
1416 xmlGenericError(xmlGenericErrorContext,
1417 "xmlXPathNodeSetMerge: out of memory\n");
1418 return(NULL);
1419 }
1420 memset(val1->nodeTab, 0 ,
1421 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1422 val1->nodeMax = XML_NODESET_DEFAULT;
1423 } else if (val1->nodeNr == val1->nodeMax) {
1424 xmlNodePtr *temp;
1425
1426 val1->nodeMax *= 2;
1427 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1428 sizeof(xmlNodePtr));
1429 if (temp == NULL) {
1430 xmlGenericError(xmlGenericErrorContext,
1431 "xmlXPathNodeSetMerge: out of memory\n");
1432 return(NULL);
1433 }
1434 val1->nodeTab = temp;
1435 }
1436 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1437 }
1438
1439 return(val1);
1440}
1441
1442/**
1443 * xmlXPathNodeSetDel:
1444 * @cur: the initial node set
1445 * @val: an xmlNodePtr
1446 *
1447 * Removes an xmlNodePtr from an existing NodeSet
1448 */
1449void
1450xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1451 int i;
1452
1453 if (cur == NULL) return;
1454 if (val == NULL) return;
1455
1456 /*
1457 * check against doublons
1458 */
1459 for (i = 0;i < cur->nodeNr;i++)
1460 if (cur->nodeTab[i] == val) break;
1461
1462 if (i >= cur->nodeNr) {
1463#ifdef DEBUG
1464 xmlGenericError(xmlGenericErrorContext,
1465 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1466 val->name);
1467#endif
1468 return;
1469 }
1470 cur->nodeNr--;
1471 for (;i < cur->nodeNr;i++)
1472 cur->nodeTab[i] = cur->nodeTab[i + 1];
1473 cur->nodeTab[cur->nodeNr] = NULL;
1474}
1475
1476/**
1477 * xmlXPathNodeSetRemove:
1478 * @cur: the initial node set
1479 * @val: the index to remove
1480 *
1481 * Removes an entry from an existing NodeSet list.
1482 */
1483void
1484xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1485 if (cur == NULL) return;
1486 if (val >= cur->nodeNr) return;
1487 cur->nodeNr--;
1488 for (;val < cur->nodeNr;val++)
1489 cur->nodeTab[val] = cur->nodeTab[val + 1];
1490 cur->nodeTab[cur->nodeNr] = NULL;
1491}
1492
1493/**
1494 * xmlXPathFreeNodeSet:
1495 * @obj: the xmlNodeSetPtr to free
1496 *
1497 * Free the NodeSet compound (not the actual nodes !).
1498 */
1499void
1500xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1501 if (obj == NULL) return;
1502 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001503 xmlFree(obj->nodeTab);
1504 }
Owen Taylor3473f882001-02-23 17:55:21 +00001505 xmlFree(obj);
1506}
1507
1508/**
1509 * xmlXPathFreeValueTree:
1510 * @obj: the xmlNodeSetPtr to free
1511 *
1512 * Free the NodeSet compound and the actual tree, this is different
1513 * from xmlXPathFreeNodeSet()
1514 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001515static void
Owen Taylor3473f882001-02-23 17:55:21 +00001516xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1517 int i;
1518
1519 if (obj == NULL) return;
1520 for (i = 0;i < obj->nodeNr;i++)
1521 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001522 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001523
1524 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001525 xmlFree(obj->nodeTab);
1526 }
Owen Taylor3473f882001-02-23 17:55:21 +00001527 xmlFree(obj);
1528}
1529
1530#if defined(DEBUG) || defined(DEBUG_STEP)
1531/**
1532 * xmlGenericErrorContextNodeSet:
1533 * @output: a FILE * for the output
1534 * @obj: the xmlNodeSetPtr to free
1535 *
1536 * Quick display of a NodeSet
1537 */
1538void
1539xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1540 int i;
1541
1542 if (output == NULL) output = xmlGenericErrorContext;
1543 if (obj == NULL) {
1544 fprintf(output, "NodeSet == NULL !\n");
1545 return;
1546 }
1547 if (obj->nodeNr == 0) {
1548 fprintf(output, "NodeSet is empty\n");
1549 return;
1550 }
1551 if (obj->nodeTab == NULL) {
1552 fprintf(output, " nodeTab == NULL !\n");
1553 return;
1554 }
1555 for (i = 0; i < obj->nodeNr; i++) {
1556 if (obj->nodeTab[i] == NULL) {
1557 fprintf(output, " NULL !\n");
1558 return;
1559 }
1560 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1561 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1562 fprintf(output, " /");
1563 else if (obj->nodeTab[i]->name == NULL)
1564 fprintf(output, " noname!");
1565 else fprintf(output, " %s", obj->nodeTab[i]->name);
1566 }
1567 fprintf(output, "\n");
1568}
1569#endif
1570
1571/**
1572 * xmlXPathNewNodeSet:
1573 * @val: the NodePtr value
1574 *
1575 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1576 * it with the single Node @val
1577 *
1578 * Returns the newly created object.
1579 */
1580xmlXPathObjectPtr
1581xmlXPathNewNodeSet(xmlNodePtr val) {
1582 xmlXPathObjectPtr ret;
1583
1584 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1585 if (ret == NULL) {
1586 xmlGenericError(xmlGenericErrorContext,
1587 "xmlXPathNewNodeSet: out of memory\n");
1588 return(NULL);
1589 }
1590 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1591 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001592 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001593 ret->nodesetval = xmlXPathNodeSetCreate(val);
1594 return(ret);
1595}
1596
1597/**
1598 * xmlXPathNewValueTree:
1599 * @val: the NodePtr value
1600 *
1601 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1602 * it with the tree root @val
1603 *
1604 * Returns the newly created object.
1605 */
1606xmlXPathObjectPtr
1607xmlXPathNewValueTree(xmlNodePtr val) {
1608 xmlXPathObjectPtr ret;
1609
1610 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1611 if (ret == NULL) {
1612 xmlGenericError(xmlGenericErrorContext,
1613 "xmlXPathNewNodeSet: out of memory\n");
1614 return(NULL);
1615 }
1616 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1617 ret->type = XPATH_XSLT_TREE;
1618 ret->nodesetval = xmlXPathNodeSetCreate(val);
1619 return(ret);
1620}
1621
1622/**
1623 * xmlXPathNewNodeSetList:
1624 * @val: an existing NodeSet
1625 *
1626 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1627 * it with the Nodeset @val
1628 *
1629 * Returns the newly created object.
1630 */
1631xmlXPathObjectPtr
1632xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1633 xmlXPathObjectPtr ret;
1634 int i;
1635
1636 if (val == NULL)
1637 ret = NULL;
1638 else if (val->nodeTab == NULL)
1639 ret = xmlXPathNewNodeSet(NULL);
1640 else
1641 {
1642 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1643 for (i = 1; i < val->nodeNr; ++i)
1644 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1645 }
1646
1647 return(ret);
1648}
1649
1650/**
1651 * xmlXPathWrapNodeSet:
1652 * @val: the NodePtr value
1653 *
1654 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1655 *
1656 * Returns the newly created object.
1657 */
1658xmlXPathObjectPtr
1659xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1660 xmlXPathObjectPtr ret;
1661
1662 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1663 if (ret == NULL) {
1664 xmlGenericError(xmlGenericErrorContext,
1665 "xmlXPathWrapNodeSet: out of memory\n");
1666 return(NULL);
1667 }
1668 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1669 ret->type = XPATH_NODESET;
1670 ret->nodesetval = val;
1671 return(ret);
1672}
1673
1674/**
1675 * xmlXPathFreeNodeSetList:
1676 * @obj: an existing NodeSetList object
1677 *
1678 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1679 * the list contrary to xmlXPathFreeObject().
1680 */
1681void
1682xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1683 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001684 xmlFree(obj);
1685}
1686
1687/************************************************************************
1688 * *
1689 * Routines to handle extra functions *
1690 * *
1691 ************************************************************************/
1692
1693/**
1694 * xmlXPathRegisterFunc:
1695 * @ctxt: the XPath context
1696 * @name: the function name
1697 * @f: the function implementation or NULL
1698 *
1699 * Register a new function. If @f is NULL it unregisters the function
1700 *
1701 * Returns 0 in case of success, -1 in case of error
1702 */
1703int
1704xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1705 xmlXPathFunction f) {
1706 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1707}
1708
1709/**
1710 * xmlXPathRegisterFuncNS:
1711 * @ctxt: the XPath context
1712 * @name: the function name
1713 * @ns_uri: the function namespace URI
1714 * @f: the function implementation or NULL
1715 *
1716 * Register a new function. If @f is NULL it unregisters the function
1717 *
1718 * Returns 0 in case of success, -1 in case of error
1719 */
1720int
1721xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1722 const xmlChar *ns_uri, xmlXPathFunction f) {
1723 if (ctxt == NULL)
1724 return(-1);
1725 if (name == NULL)
1726 return(-1);
1727
1728 if (ctxt->funcHash == NULL)
1729 ctxt->funcHash = xmlHashCreate(0);
1730 if (ctxt->funcHash == NULL)
1731 return(-1);
1732 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1733}
1734
1735/**
1736 * xmlXPathFunctionLookup:
1737 * @ctxt: the XPath context
1738 * @name: the function name
1739 *
1740 * Search in the Function array of the context for the given
1741 * function.
1742 *
1743 * Returns the xmlXPathFunction or NULL if not found
1744 */
1745xmlXPathFunction
1746xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1747 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1748}
1749
1750/**
1751 * xmlXPathFunctionLookupNS:
1752 * @ctxt: the XPath context
1753 * @name: the function name
1754 * @ns_uri: the function namespace URI
1755 *
1756 * Search in the Function array of the context for the given
1757 * function.
1758 *
1759 * Returns the xmlXPathFunction or NULL if not found
1760 */
1761xmlXPathFunction
1762xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1763 const xmlChar *ns_uri) {
1764 if (ctxt == NULL)
1765 return(NULL);
1766 if (ctxt->funcHash == NULL)
1767 return(NULL);
1768 if (name == NULL)
1769 return(NULL);
1770
1771 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1772}
1773
1774/**
1775 * xmlXPathRegisteredFuncsCleanup:
1776 * @ctxt: the XPath context
1777 *
1778 * Cleanup the XPath context data associated to registered functions
1779 */
1780void
1781xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1782 if (ctxt == NULL)
1783 return;
1784
1785 xmlHashFree(ctxt->funcHash, NULL);
1786 ctxt->funcHash = NULL;
1787}
1788
1789/************************************************************************
1790 * *
1791 * Routines to handle Variable *
1792 * *
1793 ************************************************************************/
1794
1795/**
1796 * xmlXPathRegisterVariable:
1797 * @ctxt: the XPath context
1798 * @name: the variable name
1799 * @value: the variable value or NULL
1800 *
1801 * Register a new variable value. If @value is NULL it unregisters
1802 * the variable
1803 *
1804 * Returns 0 in case of success, -1 in case of error
1805 */
1806int
1807xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1808 xmlXPathObjectPtr value) {
1809 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1810}
1811
1812/**
1813 * xmlXPathRegisterVariableNS:
1814 * @ctxt: the XPath context
1815 * @name: the variable name
1816 * @ns_uri: the variable namespace URI
1817 * @value: the variable value or NULL
1818 *
1819 * Register a new variable value. If @value is NULL it unregisters
1820 * the variable
1821 *
1822 * Returns 0 in case of success, -1 in case of error
1823 */
1824int
1825xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1826 const xmlChar *ns_uri,
1827 xmlXPathObjectPtr value) {
1828 if (ctxt == NULL)
1829 return(-1);
1830 if (name == NULL)
1831 return(-1);
1832
1833 if (ctxt->varHash == NULL)
1834 ctxt->varHash = xmlHashCreate(0);
1835 if (ctxt->varHash == NULL)
1836 return(-1);
1837 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1838 (void *) value,
1839 (xmlHashDeallocator)xmlXPathFreeObject));
1840}
1841
1842/**
1843 * xmlXPathRegisterVariableLookup:
1844 * @ctxt: the XPath context
1845 * @f: the lookup function
1846 * @data: the lookup data
1847 *
1848 * register an external mechanism to do variable lookup
1849 */
1850void
1851xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1852 xmlXPathVariableLookupFunc f, void *data) {
1853 if (ctxt == NULL)
1854 return;
1855 ctxt->varLookupFunc = (void *) f;
1856 ctxt->varLookupData = data;
1857}
1858
1859/**
1860 * xmlXPathVariableLookup:
1861 * @ctxt: the XPath context
1862 * @name: the variable name
1863 *
1864 * Search in the Variable array of the context for the given
1865 * variable value.
1866 *
1867 * Returns the value or NULL if not found
1868 */
1869xmlXPathObjectPtr
1870xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1871 if (ctxt == NULL)
1872 return(NULL);
1873
1874 if (ctxt->varLookupFunc != NULL) {
1875 xmlXPathObjectPtr ret;
1876
1877 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1878 (ctxt->varLookupData, name, NULL);
1879 if (ret != NULL) return(ret);
1880 }
1881 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1882}
1883
1884/**
1885 * xmlXPathVariableLookupNS:
1886 * @ctxt: the XPath context
1887 * @name: the variable name
1888 * @ns_uri: the variable namespace URI
1889 *
1890 * Search in the Variable array of the context for the given
1891 * variable value.
1892 *
1893 * Returns the value or NULL if not found
1894 */
1895xmlXPathObjectPtr
1896xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1897 const xmlChar *ns_uri) {
1898 if (ctxt == NULL)
1899 return(NULL);
1900
1901 if (ctxt->varLookupFunc != NULL) {
1902 xmlXPathObjectPtr ret;
1903
1904 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1905 (ctxt->varLookupData, name, ns_uri);
1906 if (ret != NULL) return(ret);
1907 }
1908
1909 if (ctxt->varHash == NULL)
1910 return(NULL);
1911 if (name == NULL)
1912 return(NULL);
1913
1914 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1915}
1916
1917/**
1918 * xmlXPathRegisteredVariablesCleanup:
1919 * @ctxt: the XPath context
1920 *
1921 * Cleanup the XPath context data associated to registered variables
1922 */
1923void
1924xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1925 if (ctxt == NULL)
1926 return;
1927
1928 xmlHashFree(ctxt->varHash, NULL);
1929 ctxt->varHash = NULL;
1930}
1931
1932/**
1933 * xmlXPathRegisterNs:
1934 * @ctxt: the XPath context
1935 * @prefix: the namespace prefix
1936 * @ns_uri: the namespace name
1937 *
1938 * Register a new namespace. If @ns_uri is NULL it unregisters
1939 * the namespace
1940 *
1941 * Returns 0 in case of success, -1 in case of error
1942 */
1943int
1944xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1945 const xmlChar *ns_uri) {
1946 if (ctxt == NULL)
1947 return(-1);
1948 if (prefix == NULL)
1949 return(-1);
1950
1951 if (ctxt->nsHash == NULL)
1952 ctxt->nsHash = xmlHashCreate(10);
1953 if (ctxt->nsHash == NULL)
1954 return(-1);
1955 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1956 (xmlHashDeallocator)xmlFree));
1957}
1958
1959/**
1960 * xmlXPathNsLookup:
1961 * @ctxt: the XPath context
1962 * @prefix: the namespace prefix value
1963 *
1964 * Search in the namespace declaration array of the context for the given
1965 * namespace name associated to the given prefix
1966 *
1967 * Returns the value or NULL if not found
1968 */
1969const xmlChar *
1970xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1971 if (ctxt == NULL)
1972 return(NULL);
1973 if (prefix == NULL)
1974 return(NULL);
1975
1976#ifdef XML_XML_NAMESPACE
1977 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1978 return(XML_XML_NAMESPACE);
1979#endif
1980
1981 if (ctxt->nsHash == NULL)
1982 return(NULL);
1983
1984 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1985}
1986
1987/**
1988 * xmlXPathRegisteredVariablesCleanup:
1989 * @ctxt: the XPath context
1990 *
1991 * Cleanup the XPath context data associated to registered variables
1992 */
1993void
1994xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1995 if (ctxt == NULL)
1996 return;
1997
1998 xmlHashFree(ctxt->nsHash, NULL);
1999 ctxt->nsHash = NULL;
2000}
2001
2002/************************************************************************
2003 * *
2004 * Routines to handle Values *
2005 * *
2006 ************************************************************************/
2007
2008/* Allocations are terrible, one need to optimize all this !!! */
2009
2010/**
2011 * xmlXPathNewFloat:
2012 * @val: the double value
2013 *
2014 * Create a new xmlXPathObjectPtr of type double and of value @val
2015 *
2016 * Returns the newly created object.
2017 */
2018xmlXPathObjectPtr
2019xmlXPathNewFloat(double val) {
2020 xmlXPathObjectPtr ret;
2021
2022 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2023 if (ret == NULL) {
2024 xmlGenericError(xmlGenericErrorContext,
2025 "xmlXPathNewFloat: out of memory\n");
2026 return(NULL);
2027 }
2028 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2029 ret->type = XPATH_NUMBER;
2030 ret->floatval = val;
2031 return(ret);
2032}
2033
2034/**
2035 * xmlXPathNewBoolean:
2036 * @val: the boolean value
2037 *
2038 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2039 *
2040 * Returns the newly created object.
2041 */
2042xmlXPathObjectPtr
2043xmlXPathNewBoolean(int val) {
2044 xmlXPathObjectPtr ret;
2045
2046 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2047 if (ret == NULL) {
2048 xmlGenericError(xmlGenericErrorContext,
2049 "xmlXPathNewBoolean: out of memory\n");
2050 return(NULL);
2051 }
2052 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2053 ret->type = XPATH_BOOLEAN;
2054 ret->boolval = (val != 0);
2055 return(ret);
2056}
2057
2058/**
2059 * xmlXPathNewString:
2060 * @val: the xmlChar * value
2061 *
2062 * Create a new xmlXPathObjectPtr of type string and of value @val
2063 *
2064 * Returns the newly created object.
2065 */
2066xmlXPathObjectPtr
2067xmlXPathNewString(const xmlChar *val) {
2068 xmlXPathObjectPtr ret;
2069
2070 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2071 if (ret == NULL) {
2072 xmlGenericError(xmlGenericErrorContext,
2073 "xmlXPathNewString: out of memory\n");
2074 return(NULL);
2075 }
2076 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2077 ret->type = XPATH_STRING;
2078 if (val != NULL)
2079 ret->stringval = xmlStrdup(val);
2080 else
2081 ret->stringval = xmlStrdup((const xmlChar *)"");
2082 return(ret);
2083}
2084
2085/**
2086 * xmlXPathNewCString:
2087 * @val: the char * value
2088 *
2089 * Create a new xmlXPathObjectPtr of type string and of value @val
2090 *
2091 * Returns the newly created object.
2092 */
2093xmlXPathObjectPtr
2094xmlXPathNewCString(const char *val) {
2095 xmlXPathObjectPtr ret;
2096
2097 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2098 if (ret == NULL) {
2099 xmlGenericError(xmlGenericErrorContext,
2100 "xmlXPathNewCString: out of memory\n");
2101 return(NULL);
2102 }
2103 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2104 ret->type = XPATH_STRING;
2105 ret->stringval = xmlStrdup(BAD_CAST val);
2106 return(ret);
2107}
2108
2109/**
2110 * xmlXPathObjectCopy:
2111 * @val: the original object
2112 *
2113 * allocate a new copy of a given object
2114 *
2115 * Returns the newly created object.
2116 */
2117xmlXPathObjectPtr
2118xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2119 xmlXPathObjectPtr ret;
2120
2121 if (val == NULL)
2122 return(NULL);
2123
2124 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2125 if (ret == NULL) {
2126 xmlGenericError(xmlGenericErrorContext,
2127 "xmlXPathObjectCopy: out of memory\n");
2128 return(NULL);
2129 }
2130 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2131 switch (val->type) {
2132 case XPATH_BOOLEAN:
2133 case XPATH_NUMBER:
2134 case XPATH_POINT:
2135 case XPATH_RANGE:
2136 break;
2137 case XPATH_STRING:
2138 ret->stringval = xmlStrdup(val->stringval);
2139 break;
2140 case XPATH_XSLT_TREE:
2141 if ((val->nodesetval != NULL) &&
2142 (val->nodesetval->nodeTab != NULL))
2143 ret->nodesetval = xmlXPathNodeSetCreate(
2144 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
2145 else
2146 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
2147 break;
2148 case XPATH_NODESET:
2149 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
2150 break;
2151 case XPATH_LOCATIONSET:
2152#ifdef LIBXML_XPTR_ENABLED
2153 {
2154 xmlLocationSetPtr loc = val->user;
2155 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2156 break;
2157 }
2158#endif
2159 case XPATH_UNDEFINED:
2160 case XPATH_USERS:
2161 xmlGenericError(xmlGenericErrorContext,
2162 "xmlXPathObjectCopy: unsupported type %d\n",
2163 val->type);
2164 break;
2165 }
2166 return(ret);
2167}
2168
2169/**
2170 * xmlXPathFreeObject:
2171 * @obj: the object to free
2172 *
2173 * Free up an xmlXPathObjectPtr object.
2174 */
2175void
2176xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2177 if (obj == NULL) return;
2178 if (obj->type == XPATH_NODESET) {
Daniel Veillard77851712001-02-27 21:54:07 +00002179 if (obj->boolval) {
2180 obj->type = XPATH_XSLT_TREE;
2181 if (obj->nodesetval != NULL)
2182 xmlXPathFreeValueTree(obj->nodesetval);
2183 } else {
2184 if (obj->nodesetval != NULL)
2185 xmlXPathFreeNodeSet(obj->nodesetval);
2186 }
Owen Taylor3473f882001-02-23 17:55:21 +00002187#ifdef LIBXML_XPTR_ENABLED
2188 } else if (obj->type == XPATH_LOCATIONSET) {
2189 if (obj->user != NULL)
2190 xmlXPtrFreeLocationSet(obj->user);
2191#endif
2192 } else if (obj->type == XPATH_STRING) {
2193 if (obj->stringval != NULL)
2194 xmlFree(obj->stringval);
2195 } else if (obj->type == XPATH_XSLT_TREE) {
2196 if (obj->nodesetval != NULL)
2197 xmlXPathFreeValueTree(obj->nodesetval);
2198 }
2199
Owen Taylor3473f882001-02-23 17:55:21 +00002200 xmlFree(obj);
2201}
2202
2203/************************************************************************
2204 * *
2205 * Routines to handle XPath contexts *
2206 * *
2207 ************************************************************************/
2208
2209/**
2210 * xmlXPathNewContext:
2211 * @doc: the XML document
2212 *
2213 * Create a new xmlXPathContext
2214 *
2215 * Returns the xmlXPathContext just allocated.
2216 */
2217xmlXPathContextPtr
2218xmlXPathNewContext(xmlDocPtr doc) {
2219 xmlXPathContextPtr ret;
2220
2221 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
2222 if (ret == NULL) {
2223 xmlGenericError(xmlGenericErrorContext,
2224 "xmlXPathNewContext: out of memory\n");
2225 return(NULL);
2226 }
2227 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
2228 ret->doc = doc;
2229 ret->node = NULL;
2230
2231 ret->varHash = NULL;
2232
2233 ret->nb_types = 0;
2234 ret->max_types = 0;
2235 ret->types = NULL;
2236
2237 ret->funcHash = xmlHashCreate(0);
2238
2239 ret->nb_axis = 0;
2240 ret->max_axis = 0;
2241 ret->axis = NULL;
2242
2243 ret->nsHash = NULL;
2244 ret->user = NULL;
2245
2246 ret->contextSize = -1;
2247 ret->proximityPosition = -1;
2248
2249 xmlXPathRegisterAllFunctions(ret);
2250
2251 return(ret);
2252}
2253
2254/**
2255 * xmlXPathFreeContext:
2256 * @ctxt: the context to free
2257 *
2258 * Free up an xmlXPathContext
2259 */
2260void
2261xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
2262 xmlXPathRegisteredNsCleanup(ctxt);
2263 xmlXPathRegisteredFuncsCleanup(ctxt);
2264 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00002265 xmlFree(ctxt);
2266}
2267
2268/************************************************************************
2269 * *
2270 * Routines to handle XPath parser contexts *
2271 * *
2272 ************************************************************************/
2273
2274#define CHECK_CTXT(ctxt) \
2275 if (ctxt == NULL) { \
2276 xmlGenericError(xmlGenericErrorContext, \
2277 "%s:%d Internal error: ctxt == NULL\n", \
2278 __FILE__, __LINE__); \
2279 } \
2280
2281
2282#define CHECK_CONTEXT(ctxt) \
2283 if (ctxt == NULL) { \
2284 xmlGenericError(xmlGenericErrorContext, \
2285 "%s:%d Internal error: no context\n", \
2286 __FILE__, __LINE__); \
2287 } \
2288 else if (ctxt->doc == NULL) { \
2289 xmlGenericError(xmlGenericErrorContext, \
2290 "%s:%d Internal error: no document\n", \
2291 __FILE__, __LINE__); \
2292 } \
2293 else if (ctxt->doc->children == NULL) { \
2294 xmlGenericError(xmlGenericErrorContext, \
2295 "%s:%d Internal error: document without root\n", \
2296 __FILE__, __LINE__); \
2297 } \
2298
2299
2300/**
2301 * xmlXPathNewParserContext:
2302 * @str: the XPath expression
2303 * @ctxt: the XPath context
2304 *
2305 * Create a new xmlXPathParserContext
2306 *
2307 * Returns the xmlXPathParserContext just allocated.
2308 */
2309xmlXPathParserContextPtr
2310xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
2311 xmlXPathParserContextPtr ret;
2312
2313 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2314 if (ret == NULL) {
2315 xmlGenericError(xmlGenericErrorContext,
2316 "xmlXPathNewParserContext: out of memory\n");
2317 return(NULL);
2318 }
2319 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2320 ret->cur = ret->base = str;
2321 ret->context = ctxt;
2322
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002323 ret->comp = xmlXPathNewCompExpr();
2324 if (ret->comp == NULL) {
2325 xmlFree(ret->valueTab);
2326 xmlFree(ret);
2327 return(NULL);
2328 }
2329
2330 return(ret);
2331}
2332
2333/**
2334 * xmlXPathCompParserContext:
2335 * @comp: the XPath compiled expression
2336 * @ctxt: the XPath context
2337 *
2338 * Create a new xmlXPathParserContext when processing a compiled expression
2339 *
2340 * Returns the xmlXPathParserContext just allocated.
2341 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002342static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002343xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
2344 xmlXPathParserContextPtr ret;
2345
2346 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
2347 if (ret == NULL) {
2348 xmlGenericError(xmlGenericErrorContext,
2349 "xmlXPathNewParserContext: out of memory\n");
2350 return(NULL);
2351 }
2352 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
2353
Owen Taylor3473f882001-02-23 17:55:21 +00002354 /* Allocate the value stack */
2355 ret->valueTab = (xmlXPathObjectPtr *)
2356 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002357 if (ret->valueTab == NULL) {
2358 xmlFree(ret);
2359 xmlGenericError(xmlGenericErrorContext,
2360 "xmlXPathNewParserContext: out of memory\n");
2361 return(NULL);
2362 }
Owen Taylor3473f882001-02-23 17:55:21 +00002363 ret->valueNr = 0;
2364 ret->valueMax = 10;
2365 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002366
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00002367 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002368 ret->comp = comp;
2369
Owen Taylor3473f882001-02-23 17:55:21 +00002370 return(ret);
2371}
2372
2373/**
2374 * xmlXPathFreeParserContext:
2375 * @ctxt: the context to free
2376 *
2377 * Free up an xmlXPathParserContext
2378 */
2379void
2380xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
2381 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002382 xmlFree(ctxt->valueTab);
2383 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00002384 if (ctxt->comp)
2385 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00002386 xmlFree(ctxt);
2387}
2388
2389/************************************************************************
2390 * *
2391 * The implicit core function library *
2392 * *
2393 ************************************************************************/
2394
2395/*
2396 * Auto-pop and cast to a number
2397 */
2398void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
2399
2400
2401#define POP_FLOAT \
2402 arg = valuePop(ctxt); \
2403 if (arg == NULL) { \
2404 XP_ERROR(XPATH_INVALID_OPERAND); \
2405 } \
2406 if (arg->type != XPATH_NUMBER) { \
2407 valuePush(ctxt, arg); \
2408 xmlXPathNumberFunction(ctxt, 1); \
2409 arg = valuePop(ctxt); \
2410 }
2411
2412/**
2413 * xmlXPathCompareNodeSetFloat:
2414 * @ctxt: the XPath Parser context
2415 * @inf: less than (1) or greater than (0)
2416 * @strict: is the comparison strict
2417 * @arg: the node set
2418 * @f: the value
2419 *
2420 * Implement the compare operation between a nodeset and a number
2421 * @ns < @val (1, 1, ...
2422 * @ns <= @val (1, 0, ...
2423 * @ns > @val (0, 1, ...
2424 * @ns >= @val (0, 0, ...
2425 *
2426 * If one object to be compared is a node-set and the other is a number,
2427 * then the comparison will be true if and only if there is a node in the
2428 * node-set such that the result of performing the comparison on the number
2429 * to be compared and on the result of converting the string-value of that
2430 * node to a number using the number function is true.
2431 *
2432 * Returns 0 or 1 depending on the results of the test.
2433 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002434static int
Owen Taylor3473f882001-02-23 17:55:21 +00002435xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
2436 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
2437 int i, ret = 0;
2438 xmlNodeSetPtr ns;
2439 xmlChar *str2;
2440
2441 if ((f == NULL) || (arg == NULL) ||
2442 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2443 xmlXPathFreeObject(arg);
2444 xmlXPathFreeObject(f);
2445 return(0);
2446 }
2447 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002448 if (ns != NULL) {
2449 for (i = 0;i < ns->nodeNr;i++) {
2450 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2451 if (str2 != NULL) {
2452 valuePush(ctxt,
2453 xmlXPathNewString(str2));
2454 xmlFree(str2);
2455 xmlXPathNumberFunction(ctxt, 1);
2456 valuePush(ctxt, xmlXPathObjectCopy(f));
2457 ret = xmlXPathCompareValues(ctxt, inf, strict);
2458 if (ret)
2459 break;
2460 }
2461 }
Owen Taylor3473f882001-02-23 17:55:21 +00002462 }
2463 xmlXPathFreeObject(arg);
2464 xmlXPathFreeObject(f);
2465 return(ret);
2466}
2467
2468/**
2469 * xmlXPathCompareNodeSetString:
2470 * @ctxt: the XPath Parser context
2471 * @inf: less than (1) or greater than (0)
2472 * @strict: is the comparison strict
2473 * @arg: the node set
2474 * @s: the value
2475 *
2476 * Implement the compare operation between a nodeset and a string
2477 * @ns < @val (1, 1, ...
2478 * @ns <= @val (1, 0, ...
2479 * @ns > @val (0, 1, ...
2480 * @ns >= @val (0, 0, ...
2481 *
2482 * If one object to be compared is a node-set and the other is a string,
2483 * then the comparison will be true if and only if there is a node in
2484 * the node-set such that the result of performing the comparison on the
2485 * string-value of the node and the other string is true.
2486 *
2487 * Returns 0 or 1 depending on the results of the test.
2488 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002489static int
Owen Taylor3473f882001-02-23 17:55:21 +00002490xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
2491 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
2492 int i, ret = 0;
2493 xmlNodeSetPtr ns;
2494 xmlChar *str2;
2495
2496 if ((s == NULL) || (arg == NULL) ||
2497 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
2498 xmlXPathFreeObject(arg);
2499 xmlXPathFreeObject(s);
2500 return(0);
2501 }
2502 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00002503 if (ns != NULL) {
2504 for (i = 0;i < ns->nodeNr;i++) {
2505 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2506 if (str2 != NULL) {
2507 valuePush(ctxt,
2508 xmlXPathNewString(str2));
2509 xmlFree(str2);
2510 valuePush(ctxt, xmlXPathObjectCopy(s));
2511 ret = xmlXPathCompareValues(ctxt, inf, strict);
2512 if (ret)
2513 break;
2514 }
2515 }
Owen Taylor3473f882001-02-23 17:55:21 +00002516 }
2517 xmlXPathFreeObject(arg);
2518 xmlXPathFreeObject(s);
2519 return(ret);
2520}
2521
2522/**
2523 * xmlXPathCompareNodeSets:
Owen Taylor3473f882001-02-23 17:55:21 +00002524 * @op: less than (-1), equal (0) or greater than (1)
2525 * @strict: is the comparison strict
2526 * @arg1: the fist node set object
2527 * @arg2: the second node set object
2528 *
2529 * Implement the compare operation on nodesets:
2530 *
2531 * If both objects to be compared are node-sets, then the comparison
2532 * will be true if and only if there is a node in the first node-set
2533 * and a node in the second node-set such that the result of performing
2534 * the comparison on the string-values of the two nodes is true.
2535 * ....
2536 * When neither object to be compared is a node-set and the operator
2537 * is <=, <, >= or >, then the objects are compared by converting both
2538 * objects to numbers and comparing the numbers according to IEEE 754.
2539 * ....
2540 * The number function converts its argument to a number as follows:
2541 * - a string that consists of optional whitespace followed by an
2542 * optional minus sign followed by a Number followed by whitespace
2543 * is converted to the IEEE 754 number that is nearest (according
2544 * to the IEEE 754 round-to-nearest rule) to the mathematical value
2545 * represented by the string; any other string is converted to NaN
2546 *
2547 * Conclusion all nodes need to be converted first to their string value
2548 * and then the comparison must be done when possible
2549 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002550static int
2551xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00002552 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2553 int i, j, init = 0;
2554 double val1;
2555 double *values2;
2556 int ret = 0;
2557 xmlChar *str;
2558 xmlNodeSetPtr ns1;
2559 xmlNodeSetPtr ns2;
2560
2561 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002562 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
2563 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002564 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002565 }
Owen Taylor3473f882001-02-23 17:55:21 +00002566 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00002567 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
2568 xmlXPathFreeObject(arg1);
2569 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002570 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002571 }
Owen Taylor3473f882001-02-23 17:55:21 +00002572
2573 ns1 = arg1->nodesetval;
2574 ns2 = arg2->nodesetval;
2575
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002576 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002577 xmlXPathFreeObject(arg1);
2578 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002579 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002580 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002581 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002582 xmlXPathFreeObject(arg1);
2583 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002584 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002585 }
Owen Taylor3473f882001-02-23 17:55:21 +00002586
2587 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
2588 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00002589 xmlXPathFreeObject(arg1);
2590 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002591 return(0);
2592 }
2593 for (i = 0;i < ns1->nodeNr;i++) {
2594 str = xmlNodeGetContent(ns1->nodeTab[i]);
2595 if (str == NULL)
2596 continue;
2597 val1 = xmlXPathStringEvalNumber(str);
2598 xmlFree(str);
2599 if (isnan(val1))
2600 continue;
2601 for (j = 0;j < ns2->nodeNr;j++) {
2602 if (init == 0) {
2603 str = xmlNodeGetContent(ns2->nodeTab[j]);
2604 if (str == NULL) {
2605 values2[j] = xmlXPathNAN;
2606 } else {
2607 values2[j] = xmlXPathStringEvalNumber(str);
2608 xmlFree(str);
2609 }
2610 }
2611 if (isnan(values2[j]))
2612 continue;
2613 if (inf && strict)
2614 ret = (val1 < values2[j]);
2615 else if (inf && !strict)
2616 ret = (val1 <= values2[j]);
2617 else if (!inf && strict)
2618 ret = (val1 > values2[j]);
2619 else if (!inf && !strict)
2620 ret = (val1 >= values2[j]);
2621 if (ret)
2622 break;
2623 }
2624 if (ret)
2625 break;
2626 init = 1;
2627 }
2628 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00002629 xmlXPathFreeObject(arg1);
2630 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00002631 return(ret);
2632 return(0);
2633}
2634
2635/**
2636 * xmlXPathCompareNodeSetValue:
2637 * @ctxt: the XPath Parser context
2638 * @inf: less than (1) or greater than (0)
2639 * @strict: is the comparison strict
2640 * @arg: the node set
2641 * @val: the value
2642 *
2643 * Implement the compare operation between a nodeset and a value
2644 * @ns < @val (1, 1, ...
2645 * @ns <= @val (1, 0, ...
2646 * @ns > @val (0, 1, ...
2647 * @ns >= @val (0, 0, ...
2648 *
2649 * If one object to be compared is a node-set and the other is a boolean,
2650 * then the comparison will be true if and only if the result of performing
2651 * the comparison on the boolean and on the result of converting
2652 * the node-set to a boolean using the boolean function is true.
2653 *
2654 * Returns 0 or 1 depending on the results of the test.
2655 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002656static int
Owen Taylor3473f882001-02-23 17:55:21 +00002657xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2658 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2659 if ((val == NULL) || (arg == NULL) ||
2660 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2661 return(0);
2662
2663 switch(val->type) {
2664 case XPATH_NUMBER:
2665 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2666 case XPATH_NODESET:
2667 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002668 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00002669 case XPATH_STRING:
2670 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2671 case XPATH_BOOLEAN:
2672 valuePush(ctxt, arg);
2673 xmlXPathBooleanFunction(ctxt, 1);
2674 valuePush(ctxt, val);
2675 return(xmlXPathCompareValues(ctxt, inf, strict));
2676 default:
2677 TODO
2678 return(0);
2679 }
2680 return(0);
2681}
2682
2683/**
2684 * xmlXPathEqualNodeSetString
2685 * @arg: the nodeset object argument
2686 * @str: the string to compare to.
2687 *
2688 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2689 * If one object to be compared is a node-set and the other is a string,
2690 * then the comparison will be true if and only if there is a node in
2691 * the node-set such that the result of performing the comparison on the
2692 * string-value of the node and the other string is true.
2693 *
2694 * Returns 0 or 1 depending on the results of the test.
2695 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002696static int
Owen Taylor3473f882001-02-23 17:55:21 +00002697xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2698 int i;
2699 xmlNodeSetPtr ns;
2700 xmlChar *str2;
2701
2702 if ((str == NULL) || (arg == NULL) ||
2703 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2704 return(0);
2705 ns = arg->nodesetval;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00002706 if (ns == NULL)
2707 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002708 if (ns->nodeNr <= 0)
2709 return(0);
2710 for (i = 0;i < ns->nodeNr;i++) {
2711 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2712 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2713 xmlFree(str2);
2714 return(1);
2715 }
2716 if (str2 != NULL)
2717 xmlFree(str2);
2718 }
2719 return(0);
2720}
2721
2722/**
2723 * xmlXPathEqualNodeSetFloat
2724 * @arg: the nodeset object argument
2725 * @f: the float to compare to
2726 *
2727 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2728 * If one object to be compared is a node-set and the other is a number,
2729 * then the comparison will be true if and only if there is a node in
2730 * the node-set such that the result of performing the comparison on the
2731 * number to be compared and on the result of converting the string-value
2732 * of that node to a number using the number function is true.
2733 *
2734 * Returns 0 or 1 depending on the results of the test.
2735 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002736static int
Owen Taylor3473f882001-02-23 17:55:21 +00002737xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2738 char buf[100] = "";
2739
2740 if ((arg == NULL) ||
2741 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2742 return(0);
2743
Bjorn Reesee1dc0112001-03-03 12:09:03 +00002744 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00002745 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2746}
2747
2748
2749/**
2750 * xmlXPathEqualNodeSets
2751 * @arg1: first nodeset object argument
2752 * @arg2: second nodeset object argument
2753 *
2754 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2755 * If both objects to be compared are node-sets, then the comparison
2756 * will be true if and only if there is a node in the first node-set and
2757 * a node in the second node-set such that the result of performing the
2758 * comparison on the string-values of the two nodes is true.
2759 *
2760 * (needless to say, this is a costly operation)
2761 *
2762 * Returns 0 or 1 depending on the results of the test.
2763 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002764static int
Owen Taylor3473f882001-02-23 17:55:21 +00002765xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2766 int i, j;
2767 xmlChar **values1;
2768 xmlChar **values2;
2769 int ret = 0;
2770 xmlNodeSetPtr ns1;
2771 xmlNodeSetPtr ns2;
2772
2773 if ((arg1 == NULL) ||
2774 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2775 return(0);
2776 if ((arg2 == NULL) ||
2777 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2778 return(0);
2779
2780 ns1 = arg1->nodesetval;
2781 ns2 = arg2->nodesetval;
2782
Daniel Veillard911f49a2001-04-07 15:39:35 +00002783 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002784 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00002785 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00002786 return(0);
2787
2788 /*
2789 * check if there is a node pertaining to both sets
2790 */
2791 for (i = 0;i < ns1->nodeNr;i++)
2792 for (j = 0;j < ns2->nodeNr;j++)
2793 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2794 return(1);
2795
2796 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2797 if (values1 == NULL)
2798 return(0);
2799 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2800 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2801 if (values2 == NULL) {
2802 xmlFree(values1);
2803 return(0);
2804 }
2805 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2806 for (i = 0;i < ns1->nodeNr;i++) {
2807 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2808 for (j = 0;j < ns2->nodeNr;j++) {
2809 if (i == 0)
2810 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2811 ret = xmlStrEqual(values1[i], values2[j]);
2812 if (ret)
2813 break;
2814 }
2815 if (ret)
2816 break;
2817 }
2818 for (i = 0;i < ns1->nodeNr;i++)
2819 if (values1[i] != NULL)
2820 xmlFree(values1[i]);
2821 for (j = 0;j < ns2->nodeNr;j++)
2822 if (values2[j] != NULL)
2823 xmlFree(values2[j]);
2824 xmlFree(values1);
2825 xmlFree(values2);
2826 return(ret);
2827}
2828
2829/**
2830 * xmlXPathEqualValues:
2831 * @ctxt: the XPath Parser context
2832 *
2833 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2834 *
2835 * Returns 0 or 1 depending on the results of the test.
2836 */
2837int
2838xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2839 xmlXPathObjectPtr arg1, arg2;
2840 int ret = 0;
2841
2842 arg1 = valuePop(ctxt);
2843 if (arg1 == NULL)
2844 XP_ERROR0(XPATH_INVALID_OPERAND);
2845
2846 arg2 = valuePop(ctxt);
2847 if (arg2 == NULL) {
2848 xmlXPathFreeObject(arg1);
2849 XP_ERROR0(XPATH_INVALID_OPERAND);
2850 }
2851
2852 if (arg1 == arg2) {
2853#ifdef DEBUG_EXPR
2854 xmlGenericError(xmlGenericErrorContext,
2855 "Equal: by pointer\n");
2856#endif
2857 return(1);
2858 }
2859
2860 switch (arg1->type) {
2861 case XPATH_UNDEFINED:
2862#ifdef DEBUG_EXPR
2863 xmlGenericError(xmlGenericErrorContext,
2864 "Equal: undefined\n");
2865#endif
2866 break;
2867 case XPATH_XSLT_TREE:
2868 case XPATH_NODESET:
2869 switch (arg2->type) {
2870 case XPATH_UNDEFINED:
2871#ifdef DEBUG_EXPR
2872 xmlGenericError(xmlGenericErrorContext,
2873 "Equal: undefined\n");
2874#endif
2875 break;
2876 case XPATH_XSLT_TREE:
2877 case XPATH_NODESET:
2878 ret = xmlXPathEqualNodeSets(arg1, arg2);
2879 break;
2880 case XPATH_BOOLEAN:
2881 if ((arg1->nodesetval == NULL) ||
2882 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2883 else
2884 ret = 1;
2885 ret = (ret == arg2->boolval);
2886 break;
2887 case XPATH_NUMBER:
2888 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2889 break;
2890 case XPATH_STRING:
2891 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2892 break;
2893 case XPATH_USERS:
2894 case XPATH_POINT:
2895 case XPATH_RANGE:
2896 case XPATH_LOCATIONSET:
2897 TODO
2898 break;
2899 }
2900 break;
2901 case XPATH_BOOLEAN:
2902 switch (arg2->type) {
2903 case XPATH_UNDEFINED:
2904#ifdef DEBUG_EXPR
2905 xmlGenericError(xmlGenericErrorContext,
2906 "Equal: undefined\n");
2907#endif
2908 break;
2909 case XPATH_NODESET:
2910 case XPATH_XSLT_TREE:
2911 if ((arg2->nodesetval == NULL) ||
2912 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2913 else
2914 ret = 1;
2915 break;
2916 case XPATH_BOOLEAN:
2917#ifdef DEBUG_EXPR
2918 xmlGenericError(xmlGenericErrorContext,
2919 "Equal: %d boolean %d \n",
2920 arg1->boolval, arg2->boolval);
2921#endif
2922 ret = (arg1->boolval == arg2->boolval);
2923 break;
2924 case XPATH_NUMBER:
2925 if (arg2->floatval) ret = 1;
2926 else ret = 0;
2927 ret = (arg1->boolval == ret);
2928 break;
2929 case XPATH_STRING:
2930 if ((arg2->stringval == NULL) ||
2931 (arg2->stringval[0] == 0)) ret = 0;
2932 else
2933 ret = 1;
2934 ret = (arg1->boolval == ret);
2935 break;
2936 case XPATH_USERS:
2937 case XPATH_POINT:
2938 case XPATH_RANGE:
2939 case XPATH_LOCATIONSET:
2940 TODO
2941 break;
2942 }
2943 break;
2944 case XPATH_NUMBER:
2945 switch (arg2->type) {
2946 case XPATH_UNDEFINED:
2947#ifdef DEBUG_EXPR
2948 xmlGenericError(xmlGenericErrorContext,
2949 "Equal: undefined\n");
2950#endif
2951 break;
2952 case XPATH_NODESET:
2953 case XPATH_XSLT_TREE:
2954 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2955 break;
2956 case XPATH_BOOLEAN:
2957 if (arg1->floatval) ret = 1;
2958 else ret = 0;
2959 ret = (arg2->boolval == ret);
2960 break;
2961 case XPATH_STRING:
2962 valuePush(ctxt, arg2);
2963 xmlXPathNumberFunction(ctxt, 1);
2964 arg2 = valuePop(ctxt);
2965 /* no break on purpose */
2966 case XPATH_NUMBER:
2967 ret = (arg1->floatval == arg2->floatval);
2968 break;
2969 case XPATH_USERS:
2970 case XPATH_POINT:
2971 case XPATH_RANGE:
2972 case XPATH_LOCATIONSET:
2973 TODO
2974 break;
2975 }
2976 break;
2977 case XPATH_STRING:
2978 switch (arg2->type) {
2979 case XPATH_UNDEFINED:
2980#ifdef DEBUG_EXPR
2981 xmlGenericError(xmlGenericErrorContext,
2982 "Equal: undefined\n");
2983#endif
2984 break;
2985 case XPATH_NODESET:
2986 case XPATH_XSLT_TREE:
2987 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2988 break;
2989 case XPATH_BOOLEAN:
2990 if ((arg1->stringval == NULL) ||
2991 (arg1->stringval[0] == 0)) ret = 0;
2992 else
2993 ret = 1;
2994 ret = (arg2->boolval == ret);
2995 break;
2996 case XPATH_STRING:
2997 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2998 break;
2999 case XPATH_NUMBER:
3000 valuePush(ctxt, arg1);
3001 xmlXPathNumberFunction(ctxt, 1);
3002 arg1 = valuePop(ctxt);
3003 ret = (arg1->floatval == arg2->floatval);
3004 break;
3005 case XPATH_USERS:
3006 case XPATH_POINT:
3007 case XPATH_RANGE:
3008 case XPATH_LOCATIONSET:
3009 TODO
3010 break;
3011 }
3012 break;
3013 case XPATH_USERS:
3014 case XPATH_POINT:
3015 case XPATH_RANGE:
3016 case XPATH_LOCATIONSET:
3017 TODO
3018 break;
3019 }
3020 xmlXPathFreeObject(arg1);
3021 xmlXPathFreeObject(arg2);
3022 return(ret);
3023}
3024
3025
3026/**
3027 * xmlXPathCompareValues:
3028 * @ctxt: the XPath Parser context
3029 * @inf: less than (1) or greater than (0)
3030 * @strict: is the comparison strict
3031 *
3032 * Implement the compare operation on XPath objects:
3033 * @arg1 < @arg2 (1, 1, ...
3034 * @arg1 <= @arg2 (1, 0, ...
3035 * @arg1 > @arg2 (0, 1, ...
3036 * @arg1 >= @arg2 (0, 0, ...
3037 *
3038 * When neither object to be compared is a node-set and the operator is
3039 * <=, <, >=, >, then the objects are compared by converted both objects
3040 * to numbers and comparing the numbers according to IEEE 754. The <
3041 * comparison will be true if and only if the first number is less than the
3042 * second number. The <= comparison will be true if and only if the first
3043 * number is less than or equal to the second number. The > comparison
3044 * will be true if and only if the first number is greater than the second
3045 * number. The >= comparison will be true if and only if the first number
3046 * is greater than or equal to the second number.
3047 *
3048 * Returns 1 if the comparaison succeeded, 0 if it failed
3049 */
3050int
3051xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
3052 int ret = 0;
3053 xmlXPathObjectPtr arg1, arg2;
3054
3055 arg2 = valuePop(ctxt);
3056 if (arg2 == NULL) {
3057 XP_ERROR0(XPATH_INVALID_OPERAND);
3058 }
3059
3060 arg1 = valuePop(ctxt);
3061 if (arg1 == NULL) {
3062 xmlXPathFreeObject(arg2);
3063 XP_ERROR0(XPATH_INVALID_OPERAND);
3064 }
3065
3066 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
3067 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003068 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003069 } else {
3070 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003071 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
3072 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003073 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00003074 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
3075 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00003076 }
3077 }
3078 return(ret);
3079 }
3080
3081 if (arg1->type != XPATH_NUMBER) {
3082 valuePush(ctxt, arg1);
3083 xmlXPathNumberFunction(ctxt, 1);
3084 arg1 = valuePop(ctxt);
3085 }
3086 if (arg1->type != XPATH_NUMBER) {
3087 xmlXPathFreeObject(arg1);
3088 xmlXPathFreeObject(arg2);
3089 XP_ERROR0(XPATH_INVALID_OPERAND);
3090 }
3091 if (arg2->type != XPATH_NUMBER) {
3092 valuePush(ctxt, arg2);
3093 xmlXPathNumberFunction(ctxt, 1);
3094 arg2 = valuePop(ctxt);
3095 }
3096 if (arg2->type != XPATH_NUMBER) {
3097 xmlXPathFreeObject(arg1);
3098 xmlXPathFreeObject(arg2);
3099 XP_ERROR0(XPATH_INVALID_OPERAND);
3100 }
3101 /*
3102 * Add tests for infinity and nan
3103 * => feedback on 3.4 for Inf and NaN
3104 */
3105 if (inf && strict)
3106 ret = (arg1->floatval < arg2->floatval);
3107 else if (inf && !strict)
3108 ret = (arg1->floatval <= arg2->floatval);
3109 else if (!inf && strict)
3110 ret = (arg1->floatval > arg2->floatval);
3111 else if (!inf && !strict)
3112 ret = (arg1->floatval >= arg2->floatval);
3113 xmlXPathFreeObject(arg1);
3114 xmlXPathFreeObject(arg2);
3115 return(ret);
3116}
3117
3118/**
3119 * xmlXPathValueFlipSign:
3120 * @ctxt: the XPath Parser context
3121 *
3122 * Implement the unary - operation on an XPath object
3123 * The numeric operators convert their operands to numbers as if
3124 * by calling the number function.
3125 */
3126void
3127xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
3128 xmlXPathObjectPtr arg;
3129
3130 POP_FLOAT
3131 arg->floatval = -arg->floatval;
3132 valuePush(ctxt, arg);
3133}
3134
3135/**
3136 * xmlXPathAddValues:
3137 * @ctxt: the XPath Parser context
3138 *
3139 * Implement the add operation on XPath objects:
3140 * The numeric operators convert their operands to numbers as if
3141 * by calling the number function.
3142 */
3143void
3144xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
3145 xmlXPathObjectPtr arg;
3146 double val;
3147
3148 POP_FLOAT
3149 val = arg->floatval;
3150 xmlXPathFreeObject(arg);
3151
3152 POP_FLOAT
3153 arg->floatval += val;
3154 valuePush(ctxt, arg);
3155}
3156
3157/**
3158 * xmlXPathSubValues:
3159 * @ctxt: the XPath Parser context
3160 *
3161 * Implement the substraction operation on XPath objects:
3162 * The numeric operators convert their operands to numbers as if
3163 * by calling the number function.
3164 */
3165void
3166xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
3167 xmlXPathObjectPtr arg;
3168 double val;
3169
3170 POP_FLOAT
3171 val = arg->floatval;
3172 xmlXPathFreeObject(arg);
3173
3174 POP_FLOAT
3175 arg->floatval -= val;
3176 valuePush(ctxt, arg);
3177}
3178
3179/**
3180 * xmlXPathMultValues:
3181 * @ctxt: the XPath Parser context
3182 *
3183 * Implement the multiply operation on XPath objects:
3184 * The numeric operators convert their operands to numbers as if
3185 * by calling the number function.
3186 */
3187void
3188xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
3189 xmlXPathObjectPtr arg;
3190 double val;
3191
3192 POP_FLOAT
3193 val = arg->floatval;
3194 xmlXPathFreeObject(arg);
3195
3196 POP_FLOAT
3197 arg->floatval *= val;
3198 valuePush(ctxt, arg);
3199}
3200
3201/**
3202 * xmlXPathDivValues:
3203 * @ctxt: the XPath Parser context
3204 *
3205 * Implement the div operation on XPath objects @arg1 / @arg2:
3206 * The numeric operators convert their operands to numbers as if
3207 * by calling the number function.
3208 */
3209void
3210xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
3211 xmlXPathObjectPtr arg;
3212 double val;
3213
3214 POP_FLOAT
3215 val = arg->floatval;
3216 xmlXPathFreeObject(arg);
3217
3218 POP_FLOAT
3219 arg->floatval /= val;
3220 valuePush(ctxt, arg);
3221}
3222
3223/**
3224 * xmlXPathModValues:
3225 * @ctxt: the XPath Parser context
3226 *
3227 * Implement the mod operation on XPath objects: @arg1 / @arg2
3228 * The numeric operators convert their operands to numbers as if
3229 * by calling the number function.
3230 */
3231void
3232xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
3233 xmlXPathObjectPtr arg;
3234 int arg1, arg2;
3235
3236 POP_FLOAT
3237 arg2 = (int) arg->floatval;
3238 xmlXPathFreeObject(arg);
3239
3240 POP_FLOAT
3241 arg1 = (int) arg->floatval;
3242 arg->floatval = arg1 % arg2;
3243 valuePush(ctxt, arg);
3244}
3245
3246/************************************************************************
3247 * *
3248 * The traversal functions *
3249 * *
3250 ************************************************************************/
3251
Owen Taylor3473f882001-02-23 17:55:21 +00003252/*
3253 * A traversal function enumerates nodes along an axis.
3254 * Initially it must be called with NULL, and it indicates
3255 * termination on the axis by returning NULL.
3256 */
3257typedef xmlNodePtr (*xmlXPathTraversalFunction)
3258 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
3259
3260/**
3261 * xmlXPathNextSelf:
3262 * @ctxt: the XPath Parser context
3263 * @cur: the current node in the traversal
3264 *
3265 * Traversal function for the "self" direction
3266 * The self axis contains just the context node itself
3267 *
3268 * Returns the next element following that axis
3269 */
3270xmlNodePtr
3271xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3272 if (cur == NULL)
3273 return(ctxt->context->node);
3274 return(NULL);
3275}
3276
3277/**
3278 * xmlXPathNextChild:
3279 * @ctxt: the XPath Parser context
3280 * @cur: the current node in the traversal
3281 *
3282 * Traversal function for the "child" direction
3283 * The child axis contains the children of the context node in document order.
3284 *
3285 * Returns the next element following that axis
3286 */
3287xmlNodePtr
3288xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3289 if (cur == NULL) {
3290 if (ctxt->context->node == NULL) return(NULL);
3291 switch (ctxt->context->node->type) {
3292 case XML_ELEMENT_NODE:
3293 case XML_TEXT_NODE:
3294 case XML_CDATA_SECTION_NODE:
3295 case XML_ENTITY_REF_NODE:
3296 case XML_ENTITY_NODE:
3297 case XML_PI_NODE:
3298 case XML_COMMENT_NODE:
3299 case XML_NOTATION_NODE:
3300 case XML_DTD_NODE:
3301 return(ctxt->context->node->children);
3302 case XML_DOCUMENT_NODE:
3303 case XML_DOCUMENT_TYPE_NODE:
3304 case XML_DOCUMENT_FRAG_NODE:
3305 case XML_HTML_DOCUMENT_NODE:
3306#ifdef LIBXML_SGML_ENABLED
3307 case XML_SGML_DOCUMENT_NODE:
3308#endif
3309 return(((xmlDocPtr) ctxt->context->node)->children);
3310 case XML_ELEMENT_DECL:
3311 case XML_ATTRIBUTE_DECL:
3312 case XML_ENTITY_DECL:
3313 case XML_ATTRIBUTE_NODE:
3314 case XML_NAMESPACE_DECL:
3315 case XML_XINCLUDE_START:
3316 case XML_XINCLUDE_END:
3317 return(NULL);
3318 }
3319 return(NULL);
3320 }
3321 if ((cur->type == XML_DOCUMENT_NODE) ||
3322 (cur->type == XML_HTML_DOCUMENT_NODE))
3323 return(NULL);
3324 return(cur->next);
3325}
3326
3327/**
3328 * xmlXPathNextDescendant:
3329 * @ctxt: the XPath Parser context
3330 * @cur: the current node in the traversal
3331 *
3332 * Traversal function for the "descendant" direction
3333 * the descendant axis contains the descendants of the context node in document
3334 * order; a descendant is a child or a child of a child and so on.
3335 *
3336 * Returns the next element following that axis
3337 */
3338xmlNodePtr
3339xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3340 if (cur == NULL) {
3341 if (ctxt->context->node == NULL)
3342 return(NULL);
3343 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3344 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3345 return(NULL);
3346
3347 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3348 return(ctxt->context->doc->children);
3349 return(ctxt->context->node->children);
3350 }
3351
3352 if (cur->children != NULL)
3353 {
3354 if (cur->children->type != XML_ENTITY_DECL)
3355 return(cur->children);
3356 }
3357 if (cur->next != NULL) return(cur->next);
3358
3359 do {
3360 cur = cur->parent;
3361 if (cur == NULL) return(NULL);
3362 if (cur == ctxt->context->node) return(NULL);
3363 if (cur->next != NULL) {
3364 cur = cur->next;
3365 return(cur);
3366 }
3367 } while (cur != NULL);
3368 return(cur);
3369}
3370
3371/**
3372 * xmlXPathNextDescendantOrSelf:
3373 * @ctxt: the XPath Parser context
3374 * @cur: the current node in the traversal
3375 *
3376 * Traversal function for the "descendant-or-self" direction
3377 * the descendant-or-self axis contains the context node and the descendants
3378 * of the context node in document order; thus the context node is the first
3379 * node on the axis, and the first child of the context node is the second node
3380 * on the axis
3381 *
3382 * Returns the next element following that axis
3383 */
3384xmlNodePtr
3385xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3386 if (cur == NULL) {
3387 if (ctxt->context->node == NULL)
3388 return(NULL);
3389 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3390 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3391 return(NULL);
3392 return(ctxt->context->node);
3393 }
3394
3395 return(xmlXPathNextDescendant(ctxt, cur));
3396}
3397
3398/**
3399 * xmlXPathNextParent:
3400 * @ctxt: the XPath Parser context
3401 * @cur: the current node in the traversal
3402 *
3403 * Traversal function for the "parent" direction
3404 * The parent axis contains the parent of the context node, if there is one.
3405 *
3406 * Returns the next element following that axis
3407 */
3408xmlNodePtr
3409xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3410 /*
3411 * the parent of an attribute or namespace node is the element
3412 * to which the attribute or namespace node is attached
3413 * Namespace handling !!!
3414 */
3415 if (cur == NULL) {
3416 if (ctxt->context->node == NULL) return(NULL);
3417 switch (ctxt->context->node->type) {
3418 case XML_ELEMENT_NODE:
3419 case XML_TEXT_NODE:
3420 case XML_CDATA_SECTION_NODE:
3421 case XML_ENTITY_REF_NODE:
3422 case XML_ENTITY_NODE:
3423 case XML_PI_NODE:
3424 case XML_COMMENT_NODE:
3425 case XML_NOTATION_NODE:
3426 case XML_DTD_NODE:
3427 case XML_ELEMENT_DECL:
3428 case XML_ATTRIBUTE_DECL:
3429 case XML_XINCLUDE_START:
3430 case XML_XINCLUDE_END:
3431 case XML_ENTITY_DECL:
3432 if (ctxt->context->node->parent == NULL)
3433 return((xmlNodePtr) ctxt->context->doc);
3434 return(ctxt->context->node->parent);
3435 case XML_ATTRIBUTE_NODE: {
3436 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3437
3438 return(att->parent);
3439 }
3440 case XML_DOCUMENT_NODE:
3441 case XML_DOCUMENT_TYPE_NODE:
3442 case XML_DOCUMENT_FRAG_NODE:
3443 case XML_HTML_DOCUMENT_NODE:
3444#ifdef LIBXML_SGML_ENABLED
3445 case XML_SGML_DOCUMENT_NODE:
3446#endif
3447 return(NULL);
3448 case XML_NAMESPACE_DECL:
3449 /*
3450 * TODO !!! may require extending struct _xmlNs with
3451 * parent field
3452 * C.f. Infoset case...
3453 */
3454 return(NULL);
3455 }
3456 }
3457 return(NULL);
3458}
3459
3460/**
3461 * xmlXPathNextAncestor:
3462 * @ctxt: the XPath Parser context
3463 * @cur: the current node in the traversal
3464 *
3465 * Traversal function for the "ancestor" direction
3466 * the ancestor axis contains the ancestors of the context node; the ancestors
3467 * of the context node consist of the parent of context node and the parent's
3468 * parent and so on; the nodes are ordered in reverse document order; thus the
3469 * parent is the first node on the axis, and the parent's parent is the second
3470 * node on the axis
3471 *
3472 * Returns the next element following that axis
3473 */
3474xmlNodePtr
3475xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3476 /*
3477 * the parent of an attribute or namespace node is the element
3478 * to which the attribute or namespace node is attached
3479 * !!!!!!!!!!!!!
3480 */
3481 if (cur == NULL) {
3482 if (ctxt->context->node == NULL) return(NULL);
3483 switch (ctxt->context->node->type) {
3484 case XML_ELEMENT_NODE:
3485 case XML_TEXT_NODE:
3486 case XML_CDATA_SECTION_NODE:
3487 case XML_ENTITY_REF_NODE:
3488 case XML_ENTITY_NODE:
3489 case XML_PI_NODE:
3490 case XML_COMMENT_NODE:
3491 case XML_DTD_NODE:
3492 case XML_ELEMENT_DECL:
3493 case XML_ATTRIBUTE_DECL:
3494 case XML_ENTITY_DECL:
3495 case XML_NOTATION_NODE:
3496 case XML_XINCLUDE_START:
3497 case XML_XINCLUDE_END:
3498 if (ctxt->context->node->parent == NULL)
3499 return((xmlNodePtr) ctxt->context->doc);
3500 return(ctxt->context->node->parent);
3501 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003502 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003503
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003504 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00003505 }
3506 case XML_DOCUMENT_NODE:
3507 case XML_DOCUMENT_TYPE_NODE:
3508 case XML_DOCUMENT_FRAG_NODE:
3509 case XML_HTML_DOCUMENT_NODE:
3510#ifdef LIBXML_SGML_ENABLED
3511 case XML_SGML_DOCUMENT_NODE:
3512#endif
3513 return(NULL);
3514 case XML_NAMESPACE_DECL:
3515 /*
3516 * TODO !!! may require extending struct _xmlNs with
3517 * parent field
3518 * C.f. Infoset case...
3519 */
3520 return(NULL);
3521 }
3522 return(NULL);
3523 }
3524 if (cur == ctxt->context->doc->children)
3525 return((xmlNodePtr) ctxt->context->doc);
3526 if (cur == (xmlNodePtr) ctxt->context->doc)
3527 return(NULL);
3528 switch (cur->type) {
3529 case XML_ELEMENT_NODE:
3530 case XML_TEXT_NODE:
3531 case XML_CDATA_SECTION_NODE:
3532 case XML_ENTITY_REF_NODE:
3533 case XML_ENTITY_NODE:
3534 case XML_PI_NODE:
3535 case XML_COMMENT_NODE:
3536 case XML_NOTATION_NODE:
3537 case XML_DTD_NODE:
3538 case XML_ELEMENT_DECL:
3539 case XML_ATTRIBUTE_DECL:
3540 case XML_ENTITY_DECL:
3541 case XML_XINCLUDE_START:
3542 case XML_XINCLUDE_END:
3543 return(cur->parent);
3544 case XML_ATTRIBUTE_NODE: {
3545 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3546
3547 return(att->parent);
3548 }
3549 case XML_DOCUMENT_NODE:
3550 case XML_DOCUMENT_TYPE_NODE:
3551 case XML_DOCUMENT_FRAG_NODE:
3552 case XML_HTML_DOCUMENT_NODE:
3553#ifdef LIBXML_SGML_ENABLED
3554 case XML_SGML_DOCUMENT_NODE:
3555#endif
3556 return(NULL);
3557 case XML_NAMESPACE_DECL:
3558 /*
3559 * TODO !!! may require extending struct _xmlNs with
3560 * parent field
3561 * C.f. Infoset case...
3562 */
3563 return(NULL);
3564 }
3565 return(NULL);
3566}
3567
3568/**
3569 * xmlXPathNextAncestorOrSelf:
3570 * @ctxt: the XPath Parser context
3571 * @cur: the current node in the traversal
3572 *
3573 * Traversal function for the "ancestor-or-self" direction
3574 * he ancestor-or-self axis contains the context node and ancestors of
3575 * the context node in reverse document order; thus the context node is
3576 * the first node on the axis, and the context node's parent the second;
3577 * parent here is defined the same as with the parent axis.
3578 *
3579 * Returns the next element following that axis
3580 */
3581xmlNodePtr
3582xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3583 if (cur == NULL)
3584 return(ctxt->context->node);
3585 return(xmlXPathNextAncestor(ctxt, cur));
3586}
3587
3588/**
3589 * xmlXPathNextFollowingSibling:
3590 * @ctxt: the XPath Parser context
3591 * @cur: the current node in the traversal
3592 *
3593 * Traversal function for the "following-sibling" direction
3594 * The following-sibling axis contains the following siblings of the context
3595 * node in document order.
3596 *
3597 * Returns the next element following that axis
3598 */
3599xmlNodePtr
3600xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3601 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3602 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3603 return(NULL);
3604 if (cur == (xmlNodePtr) ctxt->context->doc)
3605 return(NULL);
3606 if (cur == NULL)
3607 return(ctxt->context->node->next);
3608 return(cur->next);
3609}
3610
3611/**
3612 * xmlXPathNextPrecedingSibling:
3613 * @ctxt: the XPath Parser context
3614 * @cur: the current node in the traversal
3615 *
3616 * Traversal function for the "preceding-sibling" direction
3617 * The preceding-sibling axis contains the preceding siblings of the context
3618 * node in reverse document order; the first preceding sibling is first on the
3619 * axis; the sibling preceding that node is the second on the axis and so on.
3620 *
3621 * Returns the next element following that axis
3622 */
3623xmlNodePtr
3624xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3625 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3626 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3627 return(NULL);
3628 if (cur == (xmlNodePtr) ctxt->context->doc)
3629 return(NULL);
3630 if (cur == NULL)
3631 return(ctxt->context->node->prev);
3632 return(cur->prev);
3633}
3634
3635/**
3636 * xmlXPathNextFollowing:
3637 * @ctxt: the XPath Parser context
3638 * @cur: the current node in the traversal
3639 *
3640 * Traversal function for the "following" direction
3641 * The following axis contains all nodes in the same document as the context
3642 * node that are after the context node in document order, excluding any
3643 * descendants and excluding attribute nodes and namespace nodes; the nodes
3644 * are ordered in document order
3645 *
3646 * Returns the next element following that axis
3647 */
3648xmlNodePtr
3649xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3650 if (cur != NULL && cur->children != NULL)
3651 return cur->children ;
3652 if (cur == NULL) cur = ctxt->context->node;
3653 if (cur == NULL) return(NULL) ; /* ERROR */
3654 if (cur->next != NULL) return(cur->next) ;
3655 do {
3656 cur = cur->parent;
3657 if (cur == NULL) return(NULL);
3658 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3659 if (cur->next != NULL) return(cur->next);
3660 } while (cur != NULL);
3661 return(cur);
3662}
3663
3664/*
3665 * xmlXPathIsAncestor:
3666 * @ancestor: the ancestor node
3667 * @node: the current node
3668 *
3669 * Check that @ancestor is a @node's ancestor
3670 *
3671 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3672 */
3673static int
3674xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3675 if ((ancestor == NULL) || (node == NULL)) return(0);
3676 /* nodes need to be in the same document */
3677 if (ancestor->doc != node->doc) return(0);
3678 /* avoid searching if ancestor or node is the root node */
3679 if (ancestor == (xmlNodePtr) node->doc) return(1);
3680 if (node == (xmlNodePtr) ancestor->doc) return(0);
3681 while (node->parent != NULL) {
3682 if (node->parent == ancestor)
3683 return(1);
3684 node = node->parent;
3685 }
3686 return(0);
3687}
3688
3689/**
3690 * xmlXPathNextPreceding:
3691 * @ctxt: the XPath Parser context
3692 * @cur: the current node in the traversal
3693 *
3694 * Traversal function for the "preceding" direction
3695 * the preceding axis contains all nodes in the same document as the context
3696 * node that are before the context node in document order, excluding any
3697 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3698 * ordered in reverse document order
3699 *
3700 * Returns the next element following that axis
3701 */
3702xmlNodePtr
3703xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3704 if (cur == NULL)
3705 cur = ctxt->context->node ;
3706 do {
3707 if (cur->prev != NULL) {
3708 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3709 ;
3710 return(cur) ;
3711 }
3712
3713 cur = cur->parent;
3714 if (cur == NULL) return(NULL);
3715 if (cur == ctxt->context->doc->children) return(NULL);
3716 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3717 return(cur);
3718}
3719
3720/**
3721 * xmlXPathNextNamespace:
3722 * @ctxt: the XPath Parser context
3723 * @cur: the current attribute in the traversal
3724 *
3725 * Traversal function for the "namespace" direction
3726 * the namespace axis contains the namespace nodes of the context node;
3727 * the order of nodes on this axis is implementation-defined; the axis will
3728 * be empty unless the context node is an element
3729 *
3730 * Returns the next element following that axis
3731 */
3732xmlNodePtr
3733xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3734 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3735 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3736 if (ctxt->context->namespaces != NULL)
3737 xmlFree(ctxt->context->namespaces);
3738 ctxt->context->namespaces =
3739 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3740 if (ctxt->context->namespaces == NULL) return(NULL);
3741 ctxt->context->nsNr = 0;
3742 }
3743 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3744}
3745
3746/**
3747 * xmlXPathNextAttribute:
3748 * @ctxt: the XPath Parser context
3749 * @cur: the current attribute in the traversal
3750 *
3751 * Traversal function for the "attribute" direction
3752 * TODO: support DTD inherited default attributes
3753 *
3754 * Returns the next element following that axis
3755 */
3756xmlNodePtr
3757xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00003758 if (ctxt->context->node == NULL)
3759 return(NULL);
3760 if (ctxt->context->node->type != XML_ELEMENT_NODE)
3761 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003762 if (cur == NULL) {
3763 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3764 return(NULL);
3765 return((xmlNodePtr)ctxt->context->node->properties);
3766 }
3767 return((xmlNodePtr)cur->next);
3768}
3769
3770/************************************************************************
3771 * *
3772 * NodeTest Functions *
3773 * *
3774 ************************************************************************/
3775
Owen Taylor3473f882001-02-23 17:55:21 +00003776#define IS_FUNCTION 200
3777
Owen Taylor3473f882001-02-23 17:55:21 +00003778
3779/************************************************************************
3780 * *
3781 * Implicit tree core function library *
3782 * *
3783 ************************************************************************/
3784
3785/**
3786 * xmlXPathRoot:
3787 * @ctxt: the XPath Parser context
3788 *
3789 * Initialize the context to the root of the document
3790 */
3791void
3792xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3793 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3794 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3795}
3796
3797/************************************************************************
3798 * *
3799 * The explicit core function library *
3800 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3801 * *
3802 ************************************************************************/
3803
3804
3805/**
3806 * xmlXPathLastFunction:
3807 * @ctxt: the XPath Parser context
3808 * @nargs: the number of arguments
3809 *
3810 * Implement the last() XPath function
3811 * number last()
3812 * The last function returns the number of nodes in the context node list.
3813 */
3814void
3815xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3816 CHECK_ARITY(0);
3817 if (ctxt->context->contextSize >= 0) {
3818 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3819#ifdef DEBUG_EXPR
3820 xmlGenericError(xmlGenericErrorContext,
3821 "last() : %d\n", ctxt->context->contextSize);
3822#endif
3823 } else {
3824 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3825 }
3826}
3827
3828/**
3829 * xmlXPathPositionFunction:
3830 * @ctxt: the XPath Parser context
3831 * @nargs: the number of arguments
3832 *
3833 * Implement the position() XPath function
3834 * number position()
3835 * The position function returns the position of the context node in the
3836 * context node list. The first position is 1, and so the last positionr
3837 * will be equal to last().
3838 */
3839void
3840xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3841 CHECK_ARITY(0);
3842 if (ctxt->context->proximityPosition >= 0) {
3843 valuePush(ctxt,
3844 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3845#ifdef DEBUG_EXPR
3846 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3847 ctxt->context->proximityPosition);
3848#endif
3849 } else {
3850 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3851 }
3852}
3853
3854/**
3855 * xmlXPathCountFunction:
3856 * @ctxt: the XPath Parser context
3857 * @nargs: the number of arguments
3858 *
3859 * Implement the count() XPath function
3860 * number count(node-set)
3861 */
3862void
3863xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3864 xmlXPathObjectPtr cur;
3865
3866 CHECK_ARITY(1);
3867 if ((ctxt->value == NULL) ||
3868 ((ctxt->value->type != XPATH_NODESET) &&
3869 (ctxt->value->type != XPATH_XSLT_TREE)))
3870 XP_ERROR(XPATH_INVALID_TYPE);
3871 cur = valuePop(ctxt);
3872
Daniel Veillard911f49a2001-04-07 15:39:35 +00003873 if ((cur == NULL) || (cur->nodesetval == NULL))
3874 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3875 else
3876 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003877 xmlXPathFreeObject(cur);
3878}
3879
3880/**
3881 * xmlXPathIdFunction:
3882 * @ctxt: the XPath Parser context
3883 * @nargs: the number of arguments
3884 *
3885 * Implement the id() XPath function
3886 * node-set id(object)
3887 * The id function selects elements by their unique ID
3888 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3889 * then the result is the union of the result of applying id to the
3890 * string value of each of the nodes in the argument node-set. When the
3891 * argument to id is of any other type, the argument is converted to a
3892 * string as if by a call to the string function; the string is split
3893 * into a whitespace-separated list of tokens (whitespace is any sequence
3894 * of characters matching the production S); the result is a node-set
3895 * containing the elements in the same document as the context node that
3896 * have a unique ID equal to any of the tokens in the list.
3897 */
3898void
3899xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3900 const xmlChar *tokens;
3901 const xmlChar *cur;
3902 xmlChar *ID;
3903 xmlAttrPtr attr;
3904 xmlNodePtr elem = NULL;
3905 xmlXPathObjectPtr ret, obj;
3906
3907 CHECK_ARITY(1);
3908 obj = valuePop(ctxt);
3909 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3910 if (obj->type == XPATH_NODESET) {
3911 xmlXPathObjectPtr newobj;
3912 int i;
3913
3914 ret = xmlXPathNewNodeSet(NULL);
3915
Daniel Veillard911f49a2001-04-07 15:39:35 +00003916 if (obj->nodesetval != NULL) {
3917 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3918 valuePush(ctxt,
3919 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3920 xmlXPathStringFunction(ctxt, 1);
3921 xmlXPathIdFunction(ctxt, 1);
3922 newobj = valuePop(ctxt);
3923 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3924 newobj->nodesetval);
3925 xmlXPathFreeObject(newobj);
3926 }
Owen Taylor3473f882001-02-23 17:55:21 +00003927 }
3928
3929 xmlXPathFreeObject(obj);
3930 valuePush(ctxt, ret);
3931 return;
3932 }
3933 if (obj->type != XPATH_STRING) {
3934 valuePush(ctxt, obj);
3935 xmlXPathStringFunction(ctxt, 1);
3936 obj = valuePop(ctxt);
3937 if (obj->type != XPATH_STRING) {
3938 xmlXPathFreeObject(obj);
3939 return;
3940 }
3941 }
3942 tokens = obj->stringval;
3943
3944 ret = xmlXPathNewNodeSet(NULL);
3945 valuePush(ctxt, ret);
3946 if (tokens == NULL) {
3947 xmlXPathFreeObject(obj);
3948 return;
3949 }
3950
3951 cur = tokens;
3952
3953 while (IS_BLANK(*cur)) cur++;
3954 while (*cur != 0) {
3955 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3956 (*cur == '.') || (*cur == '-') ||
3957 (*cur == '_') || (*cur == ':') ||
3958 (IS_COMBINING(*cur)) ||
3959 (IS_EXTENDER(*cur)))
3960 cur++;
3961
3962 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3963
3964 ID = xmlStrndup(tokens, cur - tokens);
3965 attr = xmlGetID(ctxt->context->doc, ID);
3966 if (attr != NULL) {
3967 elem = attr->parent;
3968 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3969 }
3970 if (ID != NULL)
3971 xmlFree(ID);
3972
3973 while (IS_BLANK(*cur)) cur++;
3974 tokens = cur;
3975 }
3976 xmlXPathFreeObject(obj);
3977 return;
3978}
3979
3980/**
3981 * xmlXPathLocalNameFunction:
3982 * @ctxt: the XPath Parser context
3983 * @nargs: the number of arguments
3984 *
3985 * Implement the local-name() XPath function
3986 * string local-name(node-set?)
3987 * The local-name function returns a string containing the local part
3988 * of the name of the node in the argument node-set that is first in
3989 * document order. If the node-set is empty or the first node has no
3990 * name, an empty string is returned. If the argument is omitted it
3991 * defaults to the context node.
3992 */
3993void
3994xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3995 xmlXPathObjectPtr cur;
3996
3997 if (nargs == 0) {
3998 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3999 nargs = 1;
4000 }
4001
4002 CHECK_ARITY(1);
4003 if ((ctxt->value == NULL) ||
4004 ((ctxt->value->type != XPATH_NODESET) &&
4005 (ctxt->value->type != XPATH_XSLT_TREE)))
4006 XP_ERROR(XPATH_INVALID_TYPE);
4007 cur = valuePop(ctxt);
4008
Daniel Veillard911f49a2001-04-07 15:39:35 +00004009 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004010 valuePush(ctxt, xmlXPathNewCString(""));
4011 } else {
4012 int i = 0; /* Should be first in document order !!!!! */
4013 switch (cur->nodesetval->nodeTab[i]->type) {
4014 case XML_ELEMENT_NODE:
4015 case XML_ATTRIBUTE_NODE:
4016 case XML_PI_NODE:
4017 valuePush(ctxt,
4018 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4019 break;
4020 case XML_NAMESPACE_DECL:
4021 valuePush(ctxt, xmlXPathNewString(
4022 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4023 break;
4024 default:
4025 valuePush(ctxt, xmlXPathNewCString(""));
4026 }
4027 }
4028 xmlXPathFreeObject(cur);
4029}
4030
4031/**
4032 * xmlXPathNamespaceURIFunction:
4033 * @ctxt: the XPath Parser context
4034 * @nargs: the number of arguments
4035 *
4036 * Implement the namespace-uri() XPath function
4037 * string namespace-uri(node-set?)
4038 * The namespace-uri function returns a string containing the
4039 * namespace URI of the expanded name of the node in the argument
4040 * node-set that is first in document order. If the node-set is empty,
4041 * the first node has no name, or the expanded name has no namespace
4042 * URI, an empty string is returned. If the argument is omitted it
4043 * defaults to the context node.
4044 */
4045void
4046xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4047 xmlXPathObjectPtr cur;
4048
4049 if (nargs == 0) {
4050 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4051 nargs = 1;
4052 }
4053 CHECK_ARITY(1);
4054 if ((ctxt->value == NULL) ||
4055 ((ctxt->value->type != XPATH_NODESET) &&
4056 (ctxt->value->type != XPATH_XSLT_TREE)))
4057 XP_ERROR(XPATH_INVALID_TYPE);
4058 cur = valuePop(ctxt);
4059
Daniel Veillard911f49a2001-04-07 15:39:35 +00004060 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004061 valuePush(ctxt, xmlXPathNewCString(""));
4062 } else {
4063 int i = 0; /* Should be first in document order !!!!! */
4064 switch (cur->nodesetval->nodeTab[i]->type) {
4065 case XML_ELEMENT_NODE:
4066 case XML_ATTRIBUTE_NODE:
4067 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4068 valuePush(ctxt, xmlXPathNewCString(""));
4069 else
4070 valuePush(ctxt, xmlXPathNewString(
4071 cur->nodesetval->nodeTab[i]->ns->href));
4072 break;
4073 default:
4074 valuePush(ctxt, xmlXPathNewCString(""));
4075 }
4076 }
4077 xmlXPathFreeObject(cur);
4078}
4079
4080/**
4081 * xmlXPathNameFunction:
4082 * @ctxt: the XPath Parser context
4083 * @nargs: the number of arguments
4084 *
4085 * Implement the name() XPath function
4086 * string name(node-set?)
4087 * The name function returns a string containing a QName representing
4088 * the name of the node in the argument node-set that is first in documenti
4089 * order. The QName must represent the name with respect to the namespace
4090 * declarations in effect on the node whose name is being represented.
4091 * Typically, this will be the form in which the name occurred in the XML
4092 * source. This need not be the case if there are namespace declarations
4093 * in effect on the node that associate multiple prefixes with the same
4094 * namespace. However, an implementation may include information about
4095 * the original prefix in its representation of nodes; in this case, an
4096 * implementation can ensure that the returned string is always the same
4097 * as the QName used in the XML source. If the argument it omitted it
4098 * defaults to the context node.
4099 * Libxml keep the original prefix so the "real qualified name" used is
4100 * returned.
4101 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004102static void
Owen Taylor3473f882001-02-23 17:55:21 +00004103xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4104 xmlXPathObjectPtr cur;
4105
4106 if (nargs == 0) {
4107 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4108 nargs = 1;
4109 }
4110
4111 CHECK_ARITY(1);
4112 if ((ctxt->value == NULL) ||
4113 ((ctxt->value->type != XPATH_NODESET) &&
4114 (ctxt->value->type != XPATH_XSLT_TREE)))
4115 XP_ERROR(XPATH_INVALID_TYPE);
4116 cur = valuePop(ctxt);
4117
Daniel Veillard911f49a2001-04-07 15:39:35 +00004118 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004119 valuePush(ctxt, xmlXPathNewCString(""));
4120 } else {
4121 int i = 0; /* Should be first in document order !!!!! */
4122
4123 switch (cur->nodesetval->nodeTab[i]->type) {
4124 case XML_ELEMENT_NODE:
4125 case XML_ATTRIBUTE_NODE:
4126 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4127 valuePush(ctxt, xmlXPathNewString(
4128 cur->nodesetval->nodeTab[i]->name));
4129
4130 else {
4131 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004132 snprintf(name, sizeof(name), "%s:%s",
4133 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4134 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004135 name[sizeof(name) - 1] = 0;
4136 valuePush(ctxt, xmlXPathNewCString(name));
4137 }
4138 break;
4139 default:
4140 valuePush(ctxt,
4141 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4142 xmlXPathLocalNameFunction(ctxt, 1);
4143 }
4144 }
4145 xmlXPathFreeObject(cur);
4146}
4147
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004148
4149/**
4150 * xmlXPathConvertString:
4151 * @val: an XPath object
4152 *
4153 * Converts an existing object to its string() equivalent
4154 *
4155 * Returns the new object, the old one is freed (or the operation
4156 * is done directly on @val)
4157 */
4158xmlXPathObjectPtr
4159xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004160 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004161
4162 if (val == NULL)
4163 return(xmlXPathNewCString(""));
4164 switch (val->type) {
4165 case XPATH_UNDEFINED:
4166#ifdef DEBUG_EXPR
4167 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4168#endif
4169 ret = xmlXPathNewCString("");
4170 break;
4171 case XPATH_XSLT_TREE:
4172 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004173 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004174 ret = xmlXPathNewCString("");
4175 } else {
4176 xmlChar *res;
4177
4178 xmlXPathNodeSetSort(val->nodesetval);
4179 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4180 /* TODO: avoid allocating res to free it */
4181 ret = xmlXPathNewString(res);
4182 if (res != NULL)
4183 xmlFree(res);
4184 }
4185 break;
4186 case XPATH_STRING:
4187 return(val);
4188 case XPATH_BOOLEAN:
4189 if (val->boolval) ret = xmlXPathNewCString("true");
4190 else ret = xmlXPathNewCString("false");
4191 break;
4192 case XPATH_NUMBER: {
4193 char buf[100];
4194
4195 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4196 ret = xmlXPathNewCString(buf);
4197 break;
4198 }
4199 case XPATH_USERS:
4200 case XPATH_POINT:
4201 case XPATH_RANGE:
4202 case XPATH_LOCATIONSET:
4203 TODO
4204 ret = xmlXPathNewCString("");
4205 break;
4206 }
4207 xmlXPathFreeObject(val);
4208 return(ret);
4209}
4210
Owen Taylor3473f882001-02-23 17:55:21 +00004211/**
4212 * xmlXPathStringFunction:
4213 * @ctxt: the XPath Parser context
4214 * @nargs: the number of arguments
4215 *
4216 * Implement the string() XPath function
4217 * string string(object?)
4218 * he string function converts an object to a string as follows:
4219 * - A node-set is converted to a string by returning the value of
4220 * the node in the node-set that is first in document order.
4221 * If the node-set is empty, an empty string is returned.
4222 * - A number is converted to a string as follows
4223 * + NaN is converted to the string NaN
4224 * + positive zero is converted to the string 0
4225 * + negative zero is converted to the string 0
4226 * + positive infinity is converted to the string Infinity
4227 * + negative infinity is converted to the string -Infinity
4228 * + if the number is an integer, the number is represented in
4229 * decimal form as a Number with no decimal point and no leading
4230 * zeros, preceded by a minus sign (-) if the number is negative
4231 * + otherwise, the number is represented in decimal form as a
4232 * Number including a decimal point with at least one digit
4233 * before the decimal point and at least one digit after the
4234 * decimal point, preceded by a minus sign (-) if the number
4235 * is negative; there must be no leading zeros before the decimal
4236 * point apart possibly from the one required digit immediatelyi
4237 * before the decimal point; beyond the one required digit
4238 * after the decimal point there must be as many, but only as
4239 * many, more digits as are needed to uniquely distinguish the
4240 * number from all other IEEE 754 numeric values.
4241 * - The boolean false value is converted to the string false.
4242 * The boolean true value is converted to the string true.
4243 *
4244 * If the argument is omitted, it defaults to a node-set with the
4245 * context node as its only member.
4246 */
4247void
4248xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4249 xmlXPathObjectPtr cur;
4250
4251 if (nargs == 0) {
4252 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4253 nargs = 1;
4254 }
4255
4256 CHECK_ARITY(1);
4257 cur = valuePop(ctxt);
4258 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004259 cur = xmlXPathConvertString(cur);
4260 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004261}
4262
4263/**
4264 * xmlXPathStringLengthFunction:
4265 * @ctxt: the XPath Parser context
4266 * @nargs: the number of arguments
4267 *
4268 * Implement the string-length() XPath function
4269 * number string-length(string?)
4270 * The string-length returns the number of characters in the string
4271 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4272 * the context node converted to a string, in other words the value
4273 * of the context node.
4274 */
4275void
4276xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4277 xmlXPathObjectPtr cur;
4278
4279 if (nargs == 0) {
4280 if (ctxt->context->node == NULL) {
4281 valuePush(ctxt, xmlXPathNewFloat(0));
4282 } else {
4283 xmlChar *content;
4284
4285 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004286 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004287 xmlFree(content);
4288 }
4289 return;
4290 }
4291 CHECK_ARITY(1);
4292 CAST_TO_STRING;
4293 CHECK_TYPE(XPATH_STRING);
4294 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004295 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004296 xmlXPathFreeObject(cur);
4297}
4298
4299/**
4300 * xmlXPathConcatFunction:
4301 * @ctxt: the XPath Parser context
4302 * @nargs: the number of arguments
4303 *
4304 * Implement the concat() XPath function
4305 * string concat(string, string, string*)
4306 * The concat function returns the concatenation of its arguments.
4307 */
4308void
4309xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4310 xmlXPathObjectPtr cur, newobj;
4311 xmlChar *tmp;
4312
4313 if (nargs < 2) {
4314 CHECK_ARITY(2);
4315 }
4316
4317 CAST_TO_STRING;
4318 cur = valuePop(ctxt);
4319 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4320 xmlXPathFreeObject(cur);
4321 return;
4322 }
4323 nargs--;
4324
4325 while (nargs > 0) {
4326 CAST_TO_STRING;
4327 newobj = valuePop(ctxt);
4328 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4329 xmlXPathFreeObject(newobj);
4330 xmlXPathFreeObject(cur);
4331 XP_ERROR(XPATH_INVALID_TYPE);
4332 }
4333 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4334 newobj->stringval = cur->stringval;
4335 cur->stringval = tmp;
4336
4337 xmlXPathFreeObject(newobj);
4338 nargs--;
4339 }
4340 valuePush(ctxt, cur);
4341}
4342
4343/**
4344 * xmlXPathContainsFunction:
4345 * @ctxt: the XPath Parser context
4346 * @nargs: the number of arguments
4347 *
4348 * Implement the contains() XPath function
4349 * boolean contains(string, string)
4350 * The contains function returns true if the first argument string
4351 * contains the second argument string, and otherwise returns false.
4352 */
4353void
4354xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4355 xmlXPathObjectPtr hay, needle;
4356
4357 CHECK_ARITY(2);
4358 CAST_TO_STRING;
4359 CHECK_TYPE(XPATH_STRING);
4360 needle = valuePop(ctxt);
4361 CAST_TO_STRING;
4362 hay = valuePop(ctxt);
4363 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4364 xmlXPathFreeObject(hay);
4365 xmlXPathFreeObject(needle);
4366 XP_ERROR(XPATH_INVALID_TYPE);
4367 }
4368 if (xmlStrstr(hay->stringval, needle->stringval))
4369 valuePush(ctxt, xmlXPathNewBoolean(1));
4370 else
4371 valuePush(ctxt, xmlXPathNewBoolean(0));
4372 xmlXPathFreeObject(hay);
4373 xmlXPathFreeObject(needle);
4374}
4375
4376/**
4377 * xmlXPathStartsWithFunction:
4378 * @ctxt: the XPath Parser context
4379 * @nargs: the number of arguments
4380 *
4381 * Implement the starts-with() XPath function
4382 * boolean starts-with(string, string)
4383 * The starts-with function returns true if the first argument string
4384 * starts with the second argument string, and otherwise returns false.
4385 */
4386void
4387xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4388 xmlXPathObjectPtr hay, needle;
4389 int n;
4390
4391 CHECK_ARITY(2);
4392 CAST_TO_STRING;
4393 CHECK_TYPE(XPATH_STRING);
4394 needle = valuePop(ctxt);
4395 CAST_TO_STRING;
4396 hay = valuePop(ctxt);
4397 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4398 xmlXPathFreeObject(hay);
4399 xmlXPathFreeObject(needle);
4400 XP_ERROR(XPATH_INVALID_TYPE);
4401 }
4402 n = xmlStrlen(needle->stringval);
4403 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4404 valuePush(ctxt, xmlXPathNewBoolean(0));
4405 else
4406 valuePush(ctxt, xmlXPathNewBoolean(1));
4407 xmlXPathFreeObject(hay);
4408 xmlXPathFreeObject(needle);
4409}
4410
4411/**
4412 * xmlXPathSubstringFunction:
4413 * @ctxt: the XPath Parser context
4414 * @nargs: the number of arguments
4415 *
4416 * Implement the substring() XPath function
4417 * string substring(string, number, number?)
4418 * The substring function returns the substring of the first argument
4419 * starting at the position specified in the second argument with
4420 * length specified in the third argument. For example,
4421 * substring("12345",2,3) returns "234". If the third argument is not
4422 * specified, it returns the substring starting at the position specified
4423 * in the second argument and continuing to the end of the string. For
4424 * example, substring("12345",2) returns "2345". More precisely, each
4425 * character in the string (see [3.6 Strings]) is considered to have a
4426 * numeric position: the position of the first character is 1, the position
4427 * of the second character is 2 and so on. The returned substring contains
4428 * those characters for which the position of the character is greater than
4429 * or equal to the second argument and, if the third argument is specified,
4430 * less than the sum of the second and third arguments; the comparisons
4431 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4432 * - substring("12345", 1.5, 2.6) returns "234"
4433 * - substring("12345", 0, 3) returns "12"
4434 * - substring("12345", 0 div 0, 3) returns ""
4435 * - substring("12345", 1, 0 div 0) returns ""
4436 * - substring("12345", -42, 1 div 0) returns "12345"
4437 * - substring("12345", -1 div 0, 1 div 0) returns ""
4438 */
4439void
4440xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4441 xmlXPathObjectPtr str, start, len;
4442 double le, in;
4443 int i, l;
4444 xmlChar *ret;
4445
4446 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004447 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004448 */
4449 if (nargs < 2) {
4450 CHECK_ARITY(2);
4451 }
4452 if (nargs > 3) {
4453 CHECK_ARITY(3);
4454 }
4455 if (nargs == 3) {
4456 CAST_TO_NUMBER;
4457 CHECK_TYPE(XPATH_NUMBER);
4458 len = valuePop(ctxt);
4459 le = len->floatval;
4460 xmlXPathFreeObject(len);
4461 } else {
4462 le = 2000000000;
4463 }
4464 CAST_TO_NUMBER;
4465 CHECK_TYPE(XPATH_NUMBER);
4466 start = valuePop(ctxt);
4467 in = start->floatval;
4468 xmlXPathFreeObject(start);
4469 CAST_TO_STRING;
4470 CHECK_TYPE(XPATH_STRING);
4471 str = valuePop(ctxt);
4472 le += in;
4473
4474 /* integer index of the first char */
4475 i = (int) in;
4476 if (((double)i) != in) i++;
4477
4478 /* integer index of the last char */
4479 l = (int) le;
4480 if (((double)l) != le) l++;
4481
4482 /* back to a zero based len */
4483 i--;
4484 l--;
4485
4486 /* check against the string len */
4487 if (l > 1024) {
4488 l = xmlStrlen(str->stringval);
4489 }
4490 if (i < 0) {
4491 i = 0;
4492 }
4493
4494 /* number of chars to copy */
4495 l -= i;
4496
4497 ret = xmlStrsub(str->stringval, i, l);
4498 if (ret == NULL)
4499 valuePush(ctxt, xmlXPathNewCString(""));
4500 else {
4501 valuePush(ctxt, xmlXPathNewString(ret));
4502 xmlFree(ret);
4503 }
4504 xmlXPathFreeObject(str);
4505}
4506
4507/**
4508 * xmlXPathSubstringBeforeFunction:
4509 * @ctxt: the XPath Parser context
4510 * @nargs: the number of arguments
4511 *
4512 * Implement the substring-before() XPath function
4513 * string substring-before(string, string)
4514 * The substring-before function returns the substring of the first
4515 * argument string that precedes the first occurrence of the second
4516 * argument string in the first argument string, or the empty string
4517 * if the first argument string does not contain the second argument
4518 * string. For example, substring-before("1999/04/01","/") returns 1999.
4519 */
4520void
4521xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4522 xmlXPathObjectPtr str;
4523 xmlXPathObjectPtr find;
4524 xmlBufferPtr target;
4525 const xmlChar *point;
4526 int offset;
4527
4528 CHECK_ARITY(2);
4529 CAST_TO_STRING;
4530 find = valuePop(ctxt);
4531 CAST_TO_STRING;
4532 str = valuePop(ctxt);
4533
4534 target = xmlBufferCreate();
4535 if (target) {
4536 point = xmlStrstr(str->stringval, find->stringval);
4537 if (point) {
4538 offset = (int)(point - str->stringval);
4539 xmlBufferAdd(target, str->stringval, offset);
4540 }
4541 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4542 xmlBufferFree(target);
4543 }
4544
4545 xmlXPathFreeObject(str);
4546 xmlXPathFreeObject(find);
4547}
4548
4549/**
4550 * xmlXPathSubstringAfterFunction:
4551 * @ctxt: the XPath Parser context
4552 * @nargs: the number of arguments
4553 *
4554 * Implement the substring-after() XPath function
4555 * string substring-after(string, string)
4556 * The substring-after function returns the substring of the first
4557 * argument string that follows the first occurrence of the second
4558 * argument string in the first argument string, or the empty stringi
4559 * if the first argument string does not contain the second argument
4560 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4561 * and substring-after("1999/04/01","19") returns 99/04/01.
4562 */
4563void
4564xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4565 xmlXPathObjectPtr str;
4566 xmlXPathObjectPtr find;
4567 xmlBufferPtr target;
4568 const xmlChar *point;
4569 int offset;
4570
4571 CHECK_ARITY(2);
4572 CAST_TO_STRING;
4573 find = valuePop(ctxt);
4574 CAST_TO_STRING;
4575 str = valuePop(ctxt);
4576
4577 target = xmlBufferCreate();
4578 if (target) {
4579 point = xmlStrstr(str->stringval, find->stringval);
4580 if (point) {
4581 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4582 xmlBufferAdd(target, &str->stringval[offset],
4583 xmlStrlen(str->stringval) - offset);
4584 }
4585 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4586 xmlBufferFree(target);
4587 }
4588
4589 xmlXPathFreeObject(str);
4590 xmlXPathFreeObject(find);
4591}
4592
4593/**
4594 * xmlXPathNormalizeFunction:
4595 * @ctxt: the XPath Parser context
4596 * @nargs: the number of arguments
4597 *
4598 * Implement the normalize-space() XPath function
4599 * string normalize-space(string?)
4600 * The normalize-space function returns the argument string with white
4601 * space normalized by stripping leading and trailing whitespace
4602 * and replacing sequences of whitespace characters by a single
4603 * space. Whitespace characters are the same allowed by the S production
4604 * in XML. If the argument is omitted, it defaults to the context
4605 * node converted to a string, in other words the value of the context node.
4606 */
4607void
4608xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4609 xmlXPathObjectPtr obj = NULL;
4610 xmlChar *source = NULL;
4611 xmlBufferPtr target;
4612 xmlChar blank;
4613
4614 if (nargs == 0) {
4615 /* Use current context node */
4616 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4617 xmlXPathStringFunction(ctxt, 1);
4618 nargs = 1;
4619 }
4620
4621 CHECK_ARITY(1);
4622 CAST_TO_STRING;
4623 CHECK_TYPE(XPATH_STRING);
4624 obj = valuePop(ctxt);
4625 source = obj->stringval;
4626
4627 target = xmlBufferCreate();
4628 if (target && source) {
4629
4630 /* Skip leading whitespaces */
4631 while (IS_BLANK(*source))
4632 source++;
4633
4634 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4635 blank = 0;
4636 while (*source) {
4637 if (IS_BLANK(*source)) {
4638 blank = *source;
4639 } else {
4640 if (blank) {
4641 xmlBufferAdd(target, &blank, 1);
4642 blank = 0;
4643 }
4644 xmlBufferAdd(target, source, 1);
4645 }
4646 source++;
4647 }
4648
4649 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4650 xmlBufferFree(target);
4651 }
4652 xmlXPathFreeObject(obj);
4653}
4654
4655/**
4656 * xmlXPathTranslateFunction:
4657 * @ctxt: the XPath Parser context
4658 * @nargs: the number of arguments
4659 *
4660 * Implement the translate() XPath function
4661 * string translate(string, string, string)
4662 * The translate function returns the first argument string with
4663 * occurrences of characters in the second argument string replaced
4664 * by the character at the corresponding position in the third argument
4665 * string. For example, translate("bar","abc","ABC") returns the string
4666 * BAr. If there is a character in the second argument string with no
4667 * character at a corresponding position in the third argument string
4668 * (because the second argument string is longer than the third argument
4669 * string), then occurrences of that character in the first argument
4670 * string are removed. For example, translate("--aaa--","abc-","ABC")
4671 * returns "AAA". If a character occurs more than once in second
4672 * argument string, then the first occurrence determines the replacement
4673 * character. If the third argument string is longer than the second
4674 * argument string, then excess characters are ignored.
4675 */
4676void
4677xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004678 xmlXPathObjectPtr str;
4679 xmlXPathObjectPtr from;
4680 xmlXPathObjectPtr to;
4681 xmlBufferPtr target;
4682 int i, offset, max;
4683 xmlChar ch;
4684 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004685
Daniel Veillarde043ee12001-04-16 14:08:07 +00004686 /*
4687 * TODO: need to be converted to UTF8 strings
4688 */
4689 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004690
Daniel Veillarde043ee12001-04-16 14:08:07 +00004691 CAST_TO_STRING;
4692 to = valuePop(ctxt);
4693 CAST_TO_STRING;
4694 from = valuePop(ctxt);
4695 CAST_TO_STRING;
4696 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004697
Daniel Veillarde043ee12001-04-16 14:08:07 +00004698 target = xmlBufferCreate();
4699 if (target) {
4700 max = xmlStrlen(to->stringval);
4701 for (i = 0; (ch = str->stringval[i]); i++) {
4702 point = xmlStrchr(from->stringval, ch);
4703 if (point) {
4704 offset = (int)(point - from->stringval);
4705 if (offset < max)
4706 xmlBufferAdd(target, &to->stringval[offset], 1);
4707 } else
4708 xmlBufferAdd(target, &ch, 1);
4709 }
Owen Taylor3473f882001-02-23 17:55:21 +00004710 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004711 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4712 xmlBufferFree(target);
4713 xmlXPathFreeObject(str);
4714 xmlXPathFreeObject(from);
4715 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004716}
4717
4718/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004719 * xmlXPathConvertBoolean:
4720 * @val: an XPath object
4721 *
4722 * Converts an existing object to its boolean() equivalent
4723 *
4724 * Returns the new object, the old one is freed (or the operation
4725 * is done directly on @val)
4726 */
4727xmlXPathObjectPtr
4728xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4729 int res = 0;
4730
4731 if (val == NULL)
4732 return(NULL);
4733 switch (val->type) {
4734 case XPATH_NODESET:
4735 case XPATH_XSLT_TREE:
4736 if ((val->nodesetval == NULL) ||
4737 (val->nodesetval->nodeNr == 0)) res = 0;
4738 else
4739 res = 1;
4740 break;
4741 case XPATH_STRING:
4742 if ((val->stringval == NULL) ||
4743 (val->stringval[0] == 0)) res = 0;
4744 else
4745 res = 1;
4746 break;
4747 case XPATH_BOOLEAN:
4748 return(val);
4749 case XPATH_NUMBER:
4750 if (val->floatval) res = 1;
4751 break;
4752 default:
4753 STRANGE
4754 }
4755 xmlXPathFreeObject(val);
4756 return(xmlXPathNewBoolean(res));
4757}
4758
4759/**
Owen Taylor3473f882001-02-23 17:55:21 +00004760 * xmlXPathBooleanFunction:
4761 * @ctxt: the XPath Parser context
4762 * @nargs: the number of arguments
4763 *
4764 * Implement the boolean() XPath function
4765 * boolean boolean(object)
4766 * he boolean function converts its argument to a boolean as follows:
4767 * - a number is true if and only if it is neither positive or
4768 * negative zero nor NaN
4769 * - a node-set is true if and only if it is non-empty
4770 * - a string is true if and only if its length is non-zero
4771 */
4772void
4773xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4774 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004775
4776 CHECK_ARITY(1);
4777 cur = valuePop(ctxt);
4778 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004779 cur = xmlXPathConvertBoolean(cur);
4780 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004781}
4782
4783/**
4784 * xmlXPathNotFunction:
4785 * @ctxt: the XPath Parser context
4786 * @nargs: the number of arguments
4787 *
4788 * Implement the not() XPath function
4789 * boolean not(boolean)
4790 * The not function returns true if its argument is false,
4791 * and false otherwise.
4792 */
4793void
4794xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4795 CHECK_ARITY(1);
4796 CAST_TO_BOOLEAN;
4797 CHECK_TYPE(XPATH_BOOLEAN);
4798 ctxt->value->boolval = ! ctxt->value->boolval;
4799}
4800
4801/**
4802 * xmlXPathTrueFunction:
4803 * @ctxt: the XPath Parser context
4804 * @nargs: the number of arguments
4805 *
4806 * Implement the true() XPath function
4807 * boolean true()
4808 */
4809void
4810xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4811 CHECK_ARITY(0);
4812 valuePush(ctxt, xmlXPathNewBoolean(1));
4813}
4814
4815/**
4816 * xmlXPathFalseFunction:
4817 * @ctxt: the XPath Parser context
4818 * @nargs: the number of arguments
4819 *
4820 * Implement the false() XPath function
4821 * boolean false()
4822 */
4823void
4824xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4825 CHECK_ARITY(0);
4826 valuePush(ctxt, xmlXPathNewBoolean(0));
4827}
4828
4829/**
4830 * xmlXPathLangFunction:
4831 * @ctxt: the XPath Parser context
4832 * @nargs: the number of arguments
4833 *
4834 * Implement the lang() XPath function
4835 * boolean lang(string)
4836 * The lang function returns true or false depending on whether the
4837 * language of the context node as specified by xml:lang attributes
4838 * is the same as or is a sublanguage of the language specified by
4839 * the argument string. The language of the context node is determined
4840 * by the value of the xml:lang attribute on the context node, or, if
4841 * the context node has no xml:lang attribute, by the value of the
4842 * xml:lang attribute on the nearest ancestor of the context node that
4843 * has an xml:lang attribute. If there is no such attribute, then lang
4844 * returns false. If there is such an attribute, then lang returns
4845 * true if the attribute value is equal to the argument ignoring case,
4846 * or if there is some suffix starting with - such that the attribute
4847 * value is equal to the argument ignoring that suffix of the attribute
4848 * value and ignoring case.
4849 */
4850void
4851xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4852 xmlXPathObjectPtr val;
4853 const xmlChar *theLang;
4854 const xmlChar *lang;
4855 int ret = 0;
4856 int i;
4857
4858 CHECK_ARITY(1);
4859 CAST_TO_STRING;
4860 CHECK_TYPE(XPATH_STRING);
4861 val = valuePop(ctxt);
4862 lang = val->stringval;
4863 theLang = xmlNodeGetLang(ctxt->context->node);
4864 if ((theLang != NULL) && (lang != NULL)) {
4865 for (i = 0;lang[i] != 0;i++)
4866 if (toupper(lang[i]) != toupper(theLang[i]))
4867 goto not_equal;
4868 ret = 1;
4869 }
4870not_equal:
4871 xmlXPathFreeObject(val);
4872 valuePush(ctxt, xmlXPathNewBoolean(ret));
4873}
4874
4875/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004876 * xmlXPathConvertNumber:
4877 * @val: an XPath object
4878 *
4879 * Converts an existing object to its number() equivalent
4880 *
4881 * Returns the new object, the old one is freed (or the operation
4882 * is done directly on @val)
4883 */
4884xmlXPathObjectPtr
4885xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004886 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004887 double res;
4888
4889 if (val == NULL)
4890 return(xmlXPathNewFloat(0.0));
4891 switch (val->type) {
4892 case XPATH_UNDEFINED:
4893#ifdef DEBUG_EXPR
4894 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4895#endif
4896 ret = xmlXPathNewFloat(0.0);
4897 break;
4898 case XPATH_XSLT_TREE:
4899 case XPATH_NODESET:
4900 val = xmlXPathConvertString(val);
4901 /* no break on purpose */
4902 case XPATH_STRING:
4903 res = xmlXPathStringEvalNumber(val->stringval);
4904 ret = xmlXPathNewFloat(res);
4905 break;
4906 case XPATH_BOOLEAN:
4907 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4908 else ret = xmlXPathNewFloat(0.0);
4909 break;
4910 case XPATH_NUMBER:
4911 return(val);
4912 case XPATH_USERS:
4913 case XPATH_POINT:
4914 case XPATH_RANGE:
4915 case XPATH_LOCATIONSET:
4916 TODO
4917 ret = xmlXPathNewFloat(0.0);
4918 break;
4919 }
4920 xmlXPathFreeObject(val);
4921 return(ret);
4922}
4923
4924/**
Owen Taylor3473f882001-02-23 17:55:21 +00004925 * xmlXPathNumberFunction:
4926 * @ctxt: the XPath Parser context
4927 * @nargs: the number of arguments
4928 *
4929 * Implement the number() XPath function
4930 * number number(object?)
4931 */
4932void
4933xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4934 xmlXPathObjectPtr cur;
4935 double res;
4936
4937 if (nargs == 0) {
4938 if (ctxt->context->node == NULL) {
4939 valuePush(ctxt, xmlXPathNewFloat(0.0));
4940 } else {
4941 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4942
4943 res = xmlXPathStringEvalNumber(content);
4944 valuePush(ctxt, xmlXPathNewFloat(res));
4945 xmlFree(content);
4946 }
4947 return;
4948 }
4949
4950 CHECK_ARITY(1);
4951 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004952 cur = xmlXPathConvertNumber(cur);
4953 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004954}
4955
4956/**
4957 * xmlXPathSumFunction:
4958 * @ctxt: the XPath Parser context
4959 * @nargs: the number of arguments
4960 *
4961 * Implement the sum() XPath function
4962 * number sum(node-set)
4963 * The sum function returns the sum of the values of the nodes in
4964 * the argument node-set.
4965 */
4966void
4967xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4968 xmlXPathObjectPtr cur;
4969 int i;
4970
4971 CHECK_ARITY(1);
4972 if ((ctxt->value == NULL) ||
4973 ((ctxt->value->type != XPATH_NODESET) &&
4974 (ctxt->value->type != XPATH_XSLT_TREE)))
4975 XP_ERROR(XPATH_INVALID_TYPE);
4976 cur = valuePop(ctxt);
4977
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004978 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004979 valuePush(ctxt, xmlXPathNewFloat(0.0));
4980 } else {
4981 valuePush(ctxt,
4982 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4983 xmlXPathNumberFunction(ctxt, 1);
4984 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4985 valuePush(ctxt,
4986 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4987 xmlXPathAddValues(ctxt);
4988 }
4989 }
4990 xmlXPathFreeObject(cur);
4991}
4992
4993/**
4994 * xmlXPathFloorFunction:
4995 * @ctxt: the XPath Parser context
4996 * @nargs: the number of arguments
4997 *
4998 * Implement the floor() XPath function
4999 * number floor(number)
5000 * The floor function returns the largest (closest to positive infinity)
5001 * number that is not greater than the argument and that is an integer.
5002 */
5003void
5004xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5005 CHECK_ARITY(1);
5006 CAST_TO_NUMBER;
5007 CHECK_TYPE(XPATH_NUMBER);
5008#if 0
5009 ctxt->value->floatval = floor(ctxt->value->floatval);
5010#else
5011 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5012 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5013#endif
5014}
5015
5016/**
5017 * xmlXPathCeilingFunction:
5018 * @ctxt: the XPath Parser context
5019 * @nargs: the number of arguments
5020 *
5021 * Implement the ceiling() XPath function
5022 * number ceiling(number)
5023 * The ceiling function returns the smallest (closest to negative infinity)
5024 * number that is not less than the argument and that is an integer.
5025 */
5026void
5027xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5028 double f;
5029
5030 CHECK_ARITY(1);
5031 CAST_TO_NUMBER;
5032 CHECK_TYPE(XPATH_NUMBER);
5033
5034#if 0
5035 ctxt->value->floatval = ceil(ctxt->value->floatval);
5036#else
5037 f = (double)((int) ctxt->value->floatval);
5038 if (f != ctxt->value->floatval)
5039 ctxt->value->floatval = f + 1;
5040#endif
5041}
5042
5043/**
5044 * xmlXPathRoundFunction:
5045 * @ctxt: the XPath Parser context
5046 * @nargs: the number of arguments
5047 *
5048 * Implement the round() XPath function
5049 * number round(number)
5050 * The round function returns the number that is closest to the
5051 * argument and that is an integer. If there are two such numbers,
5052 * then the one that is even is returned.
5053 */
5054void
5055xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5056 double f;
5057
5058 CHECK_ARITY(1);
5059 CAST_TO_NUMBER;
5060 CHECK_TYPE(XPATH_NUMBER);
5061
5062 if ((ctxt->value->floatval == xmlXPathNAN) ||
5063 (ctxt->value->floatval == xmlXPathPINF) ||
5064 (ctxt->value->floatval == xmlXPathNINF) ||
5065 (ctxt->value->floatval == 0.0))
5066 return;
5067
5068#if 0
5069 f = floor(ctxt->value->floatval);
5070#else
5071 f = (double)((int) ctxt->value->floatval);
5072#endif
5073 if (ctxt->value->floatval < f + 0.5)
5074 ctxt->value->floatval = f;
5075 else
5076 ctxt->value->floatval = f + 1;
5077}
5078
5079/************************************************************************
5080 * *
5081 * The Parser *
5082 * *
5083 ************************************************************************/
5084
5085/*
5086 * a couple of forward declarations since we use a recursive call based
5087 * implementation.
5088 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005089static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005090static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005091static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005092#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005093static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5094#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005095#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005096static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005097#endif
5098
5099/**
5100 * xmlXPathParseNCName:
5101 * @ctxt: the XPath Parser context
5102 *
5103 * parse an XML namespace non qualified name.
5104 *
5105 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5106 *
5107 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5108 * CombiningChar | Extender
5109 *
5110 * Returns the namespace name or NULL
5111 */
5112
5113xmlChar *
5114xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5115 const xmlChar *q;
5116 xmlChar *ret = NULL;
5117
5118 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5119 q = NEXT;
5120
5121 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5122 (CUR == '.') || (CUR == '-') ||
5123 (CUR == '_') ||
5124 (IS_COMBINING(CUR)) ||
5125 (IS_EXTENDER(CUR)))
5126 NEXT;
5127
5128 ret = xmlStrndup(q, CUR_PTR - q);
5129
5130 return(ret);
5131}
5132
5133/**
5134 * xmlXPathParseQName:
5135 * @ctxt: the XPath Parser context
5136 * @prefix: a xmlChar **
5137 *
5138 * parse an XML qualified name
5139 *
5140 * [NS 5] QName ::= (Prefix ':')? LocalPart
5141 *
5142 * [NS 6] Prefix ::= NCName
5143 *
5144 * [NS 7] LocalPart ::= NCName
5145 *
5146 * Returns the function returns the local part, and prefix is updated
5147 * to get the Prefix if any.
5148 */
5149
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005150static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005151xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5152 xmlChar *ret = NULL;
5153
5154 *prefix = NULL;
5155 ret = xmlXPathParseNCName(ctxt);
5156 if (CUR == ':') {
5157 *prefix = ret;
5158 NEXT;
5159 ret = xmlXPathParseNCName(ctxt);
5160 }
5161 return(ret);
5162}
5163
5164/**
5165 * xmlXPathParseName:
5166 * @ctxt: the XPath Parser context
5167 *
5168 * parse an XML name
5169 *
5170 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5171 * CombiningChar | Extender
5172 *
5173 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5174 *
5175 * Returns the namespace name or NULL
5176 */
5177
5178xmlChar *
5179xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5180 const xmlChar *q;
5181 xmlChar *ret = NULL;
5182
5183 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5184 q = NEXT;
5185
5186 /* TODO Make this UTF8 compliant !!! */
5187 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5188 (CUR == '.') || (CUR == '-') ||
5189 (CUR == '_') || (CUR == ':') ||
5190 (IS_COMBINING(CUR)) ||
5191 (IS_EXTENDER(CUR)))
5192 NEXT;
5193
5194 ret = xmlStrndup(q, CUR_PTR - q);
5195
5196 return(ret);
5197}
5198
5199/**
5200 * xmlXPathStringEvalNumber:
5201 * @str: A string to scan
5202 *
5203 * [30] Number ::= Digits ('.' Digits?)?
5204 * | '.' Digits
5205 * [31] Digits ::= [0-9]+
5206 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005207 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005208 * In complement of the Number expression, this function also handles
5209 * negative values : '-' Number.
5210 *
5211 * Returns the double value.
5212 */
5213double
5214xmlXPathStringEvalNumber(const xmlChar *str) {
5215 const xmlChar *cur = str;
5216 double ret = 0.0;
5217 double mult = 1;
5218 int ok = 0;
5219 int isneg = 0;
5220
5221 while (IS_BLANK(*cur)) cur++;
5222 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5223 return(xmlXPathNAN);
5224 }
5225 if (*cur == '-') {
5226 isneg = 1;
5227 cur++;
5228 }
5229 while ((*cur >= '0') && (*cur <= '9')) {
5230 ret = ret * 10 + (*cur - '0');
5231 ok = 1;
5232 cur++;
5233 }
5234 if (*cur == '.') {
5235 cur++;
5236 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5237 return(xmlXPathNAN);
5238 }
5239 while ((*cur >= '0') && (*cur <= '9')) {
5240 mult /= 10;
5241 ret = ret + (*cur - '0') * mult;
5242 cur++;
5243 }
5244 }
5245 while (IS_BLANK(*cur)) cur++;
5246 if (*cur != 0) return(xmlXPathNAN);
5247 if (isneg) ret = -ret;
5248 return(ret);
5249}
5250
5251/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005252 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005253 * @ctxt: the XPath Parser context
5254 *
5255 * [30] Number ::= Digits ('.' Digits?)?
5256 * | '.' Digits
5257 * [31] Digits ::= [0-9]+
5258 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005259 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005260 *
5261 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005262static void
5263xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005264 double ret = 0.0;
5265 double mult = 1;
5266 int ok = 0;
5267
5268 CHECK_ERROR;
5269 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5270 XP_ERROR(XPATH_NUMBER_ERROR);
5271 }
5272 while ((CUR >= '0') && (CUR <= '9')) {
5273 ret = ret * 10 + (CUR - '0');
5274 ok = 1;
5275 NEXT;
5276 }
5277 if (CUR == '.') {
5278 NEXT;
5279 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5280 XP_ERROR(XPATH_NUMBER_ERROR);
5281 }
5282 while ((CUR >= '0') && (CUR <= '9')) {
5283 mult /= 10;
5284 ret = ret + (CUR - '0') * mult;
5285 NEXT;
5286 }
5287 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005288 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5289 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005290}
5291
5292/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005293 * xmlXPathParseLiteral:
5294 * @ctxt: the XPath Parser context
5295 *
5296 * Parse a Literal
5297 *
5298 * [29] Literal ::= '"' [^"]* '"'
5299 * | "'" [^']* "'"
5300 *
5301 * Returns the value found or NULL in case of error
5302 */
5303static xmlChar *
5304xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5305 const xmlChar *q;
5306 xmlChar *ret = NULL;
5307
5308 if (CUR == '"') {
5309 NEXT;
5310 q = CUR_PTR;
5311 while ((IS_CHAR(CUR)) && (CUR != '"'))
5312 NEXT;
5313 if (!IS_CHAR(CUR)) {
5314 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5315 } else {
5316 ret = xmlStrndup(q, CUR_PTR - q);
5317 NEXT;
5318 }
5319 } else if (CUR == '\'') {
5320 NEXT;
5321 q = CUR_PTR;
5322 while ((IS_CHAR(CUR)) && (CUR != '\''))
5323 NEXT;
5324 if (!IS_CHAR(CUR)) {
5325 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5326 } else {
5327 ret = xmlStrndup(q, CUR_PTR - q);
5328 NEXT;
5329 }
5330 } else {
5331 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5332 }
5333 return(ret);
5334}
5335
5336/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005337 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005338 * @ctxt: the XPath Parser context
5339 *
5340 * Parse a Literal and push it on the stack.
5341 *
5342 * [29] Literal ::= '"' [^"]* '"'
5343 * | "'" [^']* "'"
5344 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005345 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005346 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005347static void
5348xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005349 const xmlChar *q;
5350 xmlChar *ret = NULL;
5351
5352 if (CUR == '"') {
5353 NEXT;
5354 q = CUR_PTR;
5355 while ((IS_CHAR(CUR)) && (CUR != '"'))
5356 NEXT;
5357 if (!IS_CHAR(CUR)) {
5358 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5359 } else {
5360 ret = xmlStrndup(q, CUR_PTR - q);
5361 NEXT;
5362 }
5363 } else if (CUR == '\'') {
5364 NEXT;
5365 q = CUR_PTR;
5366 while ((IS_CHAR(CUR)) && (CUR != '\''))
5367 NEXT;
5368 if (!IS_CHAR(CUR)) {
5369 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5370 } else {
5371 ret = xmlStrndup(q, CUR_PTR - q);
5372 NEXT;
5373 }
5374 } else {
5375 XP_ERROR(XPATH_START_LITERAL_ERROR);
5376 }
5377 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005378 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5379 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005380 xmlFree(ret);
5381}
5382
5383/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005384 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005385 * @ctxt: the XPath Parser context
5386 *
5387 * Parse a VariableReference, evaluate it and push it on the stack.
5388 *
5389 * The variable bindings consist of a mapping from variable names
5390 * to variable values. The value of a variable is an object, which
5391 * of any of the types that are possible for the value of an expression,
5392 * and may also be of additional types not specified here.
5393 *
5394 * Early evaluation is possible since:
5395 * The variable bindings [...] used to evaluate a subexpression are
5396 * always the same as those used to evaluate the containing expression.
5397 *
5398 * [36] VariableReference ::= '$' QName
5399 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005400static void
5401xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005402 xmlChar *name;
5403 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005404
5405 SKIP_BLANKS;
5406 if (CUR != '$') {
5407 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5408 }
5409 NEXT;
5410 name = xmlXPathParseQName(ctxt, &prefix);
5411 if (name == NULL) {
5412 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5413 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005414 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005415 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5416 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005417 SKIP_BLANKS;
5418}
5419
5420/**
5421 * xmlXPathIsNodeType:
5422 * @ctxt: the XPath Parser context
5423 * @name: a name string
5424 *
5425 * Is the name given a NodeType one.
5426 *
5427 * [38] NodeType ::= 'comment'
5428 * | 'text'
5429 * | 'processing-instruction'
5430 * | 'node'
5431 *
5432 * Returns 1 if true 0 otherwise
5433 */
5434int
5435xmlXPathIsNodeType(const xmlChar *name) {
5436 if (name == NULL)
5437 return(0);
5438
5439 if (xmlStrEqual(name, BAD_CAST "comment"))
5440 return(1);
5441 if (xmlStrEqual(name, BAD_CAST "text"))
5442 return(1);
5443 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5444 return(1);
5445 if (xmlStrEqual(name, BAD_CAST "node"))
5446 return(1);
5447 return(0);
5448}
5449
5450/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005451 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005452 * @ctxt: the XPath Parser context
5453 *
5454 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5455 * [17] Argument ::= Expr
5456 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005457 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005458 * pushed on the stack
5459 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005460static void
5461xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005462 xmlChar *name;
5463 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005464 int nbargs = 0;
5465
5466 name = xmlXPathParseQName(ctxt, &prefix);
5467 if (name == NULL) {
5468 XP_ERROR(XPATH_EXPR_ERROR);
5469 }
5470 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005471#ifdef DEBUG_EXPR
5472 if (prefix == NULL)
5473 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5474 name);
5475 else
5476 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5477 prefix, name);
5478#endif
5479
Owen Taylor3473f882001-02-23 17:55:21 +00005480 if (CUR != '(') {
5481 XP_ERROR(XPATH_EXPR_ERROR);
5482 }
5483 NEXT;
5484 SKIP_BLANKS;
5485
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005486 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005487 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005488 int op1 = ctxt->comp->last;
5489 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005490 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005491 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005492 nbargs++;
5493 if (CUR == ')') break;
5494 if (CUR != ',') {
5495 XP_ERROR(XPATH_EXPR_ERROR);
5496 }
5497 NEXT;
5498 SKIP_BLANKS;
5499 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005500 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5501 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005502 NEXT;
5503 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005504}
5505
5506/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005507 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005508 * @ctxt: the XPath Parser context
5509 *
5510 * [15] PrimaryExpr ::= VariableReference
5511 * | '(' Expr ')'
5512 * | Literal
5513 * | Number
5514 * | FunctionCall
5515 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005516 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005517 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005518static void
5519xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005520 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005521 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005522 else if (CUR == '(') {
5523 NEXT;
5524 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005525 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005526 if (CUR != ')') {
5527 XP_ERROR(XPATH_EXPR_ERROR);
5528 }
5529 NEXT;
5530 SKIP_BLANKS;
5531 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005532 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005533 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005534 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005535 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005536 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005537 }
5538 SKIP_BLANKS;
5539}
5540
5541/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005542 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005543 * @ctxt: the XPath Parser context
5544 *
5545 * [20] FilterExpr ::= PrimaryExpr
5546 * | FilterExpr Predicate
5547 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005548 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005549 * Square brackets are used to filter expressions in the same way that
5550 * they are used in location paths. It is an error if the expression to
5551 * be filtered does not evaluate to a node-set. The context node list
5552 * used for evaluating the expression in square brackets is the node-set
5553 * to be filtered listed in document order.
5554 */
5555
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005556static void
5557xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5558 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005559 CHECK_ERROR;
5560 SKIP_BLANKS;
5561
5562 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005563 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005564 SKIP_BLANKS;
5565 }
5566
5567
5568}
5569
5570/**
5571 * xmlXPathScanName:
5572 * @ctxt: the XPath Parser context
5573 *
5574 * Trickery: parse an XML name but without consuming the input flow
5575 * Needed to avoid insanity in the parser state.
5576 *
5577 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5578 * CombiningChar | Extender
5579 *
5580 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5581 *
5582 * [6] Names ::= Name (S Name)*
5583 *
5584 * Returns the Name parsed or NULL
5585 */
5586
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005587static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005588xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5589 xmlChar buf[XML_MAX_NAMELEN];
5590 int len = 0;
5591
5592 SKIP_BLANKS;
5593 if (!IS_LETTER(CUR) && (CUR != '_') &&
5594 (CUR != ':')) {
5595 return(NULL);
5596 }
5597
5598 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5599 (NXT(len) == '.') || (NXT(len) == '-') ||
5600 (NXT(len) == '_') || (NXT(len) == ':') ||
5601 (IS_COMBINING(NXT(len))) ||
5602 (IS_EXTENDER(NXT(len)))) {
5603 buf[len] = NXT(len);
5604 len++;
5605 if (len >= XML_MAX_NAMELEN) {
5606 xmlGenericError(xmlGenericErrorContext,
5607 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5608 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5609 (NXT(len) == '.') || (NXT(len) == '-') ||
5610 (NXT(len) == '_') || (NXT(len) == ':') ||
5611 (IS_COMBINING(NXT(len))) ||
5612 (IS_EXTENDER(NXT(len))))
5613 len++;
5614 break;
5615 }
5616 }
5617 return(xmlStrndup(buf, len));
5618}
5619
5620/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005621 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005622 * @ctxt: the XPath Parser context
5623 *
5624 * [19] PathExpr ::= LocationPath
5625 * | FilterExpr
5626 * | FilterExpr '/' RelativeLocationPath
5627 * | FilterExpr '//' RelativeLocationPath
5628 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005629 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005630 * The / operator and // operators combine an arbitrary expression
5631 * and a relative location path. It is an error if the expression
5632 * does not evaluate to a node-set.
5633 * The / operator does composition in the same way as when / is
5634 * used in a location path. As in location paths, // is short for
5635 * /descendant-or-self::node()/.
5636 */
5637
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005638static void
5639xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005640 int lc = 1; /* Should we branch to LocationPath ? */
5641 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5642
5643 SKIP_BLANKS;
5644 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5645 (CUR == '\'') || (CUR == '"')) {
5646 lc = 0;
5647 } else if (CUR == '*') {
5648 /* relative or absolute location path */
5649 lc = 1;
5650 } else if (CUR == '/') {
5651 /* relative or absolute location path */
5652 lc = 1;
5653 } else if (CUR == '@') {
5654 /* relative abbreviated attribute location path */
5655 lc = 1;
5656 } else if (CUR == '.') {
5657 /* relative abbreviated attribute location path */
5658 lc = 1;
5659 } else {
5660 /*
5661 * Problem is finding if we have a name here whether it's:
5662 * - a nodetype
5663 * - a function call in which case it's followed by '('
5664 * - an axis in which case it's followed by ':'
5665 * - a element name
5666 * We do an a priori analysis here rather than having to
5667 * maintain parsed token content through the recursive function
5668 * calls. This looks uglier but makes the code quite easier to
5669 * read/write/debug.
5670 */
5671 SKIP_BLANKS;
5672 name = xmlXPathScanName(ctxt);
5673 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5674#ifdef DEBUG_STEP
5675 xmlGenericError(xmlGenericErrorContext,
5676 "PathExpr: Axis\n");
5677#endif
5678 lc = 1;
5679 xmlFree(name);
5680 } else if (name != NULL) {
5681 int len =xmlStrlen(name);
5682 int blank = 0;
5683
5684
5685 while (NXT(len) != 0) {
5686 if (NXT(len) == '/') {
5687 /* element name */
5688#ifdef DEBUG_STEP
5689 xmlGenericError(xmlGenericErrorContext,
5690 "PathExpr: AbbrRelLocation\n");
5691#endif
5692 lc = 1;
5693 break;
5694 } else if (IS_BLANK(NXT(len))) {
5695 /* skip to next */
5696 blank = 1;
5697 } else if (NXT(len) == ':') {
5698#ifdef DEBUG_STEP
5699 xmlGenericError(xmlGenericErrorContext,
5700 "PathExpr: AbbrRelLocation\n");
5701#endif
5702 lc = 1;
5703 break;
5704 } else if ((NXT(len) == '(')) {
5705 /* Note Type or Function */
5706 if (xmlXPathIsNodeType(name)) {
5707#ifdef DEBUG_STEP
5708 xmlGenericError(xmlGenericErrorContext,
5709 "PathExpr: Type search\n");
5710#endif
5711 lc = 1;
5712 } else {
5713#ifdef DEBUG_STEP
5714 xmlGenericError(xmlGenericErrorContext,
5715 "PathExpr: function call\n");
5716#endif
5717 lc = 0;
5718 }
5719 break;
5720 } else if ((NXT(len) == '[')) {
5721 /* element name */
5722#ifdef DEBUG_STEP
5723 xmlGenericError(xmlGenericErrorContext,
5724 "PathExpr: AbbrRelLocation\n");
5725#endif
5726 lc = 1;
5727 break;
5728 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5729 (NXT(len) == '=')) {
5730 lc = 1;
5731 break;
5732 } else {
5733 lc = 1;
5734 break;
5735 }
5736 len++;
5737 }
5738 if (NXT(len) == 0) {
5739#ifdef DEBUG_STEP
5740 xmlGenericError(xmlGenericErrorContext,
5741 "PathExpr: AbbrRelLocation\n");
5742#endif
5743 /* element name */
5744 lc = 1;
5745 }
5746 xmlFree(name);
5747 } else {
5748 /* make sure all cases are covered explicitely */
5749 XP_ERROR(XPATH_EXPR_ERROR);
5750 }
5751 }
5752
5753 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005754 if (CUR == '/') {
5755 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5756 } else {
5757 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005758 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005759 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005760 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005761 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005762 CHECK_ERROR;
5763 if ((CUR == '/') && (NXT(1) == '/')) {
5764 SKIP(2);
5765 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005766
5767 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5768 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5769 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5770
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005771 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005772 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005773 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005774 }
5775 }
5776 SKIP_BLANKS;
5777}
5778
5779/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005780 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005781 * @ctxt: the XPath Parser context
5782 *
5783 * [18] UnionExpr ::= PathExpr
5784 * | UnionExpr '|' PathExpr
5785 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005786 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005787 */
5788
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005789static void
5790xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5791 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005792 CHECK_ERROR;
5793 SKIP_BLANKS;
5794 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005795 int op1 = ctxt->comp->last;
5796 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005797
5798 NEXT;
5799 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005800 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005801
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005802 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5803
Owen Taylor3473f882001-02-23 17:55:21 +00005804 SKIP_BLANKS;
5805 }
Owen Taylor3473f882001-02-23 17:55:21 +00005806}
5807
5808/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005809 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005810 * @ctxt: the XPath Parser context
5811 *
5812 * [27] UnaryExpr ::= UnionExpr
5813 * | '-' UnaryExpr
5814 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005815 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005816 */
5817
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005818static void
5819xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005820 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005821 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005822
5823 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005824 while (CUR == '-') {
5825 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005826 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005827 NEXT;
5828 SKIP_BLANKS;
5829 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005830
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005831 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005832 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005833 if (found) {
5834 if (minus)
5835 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
5836 else
5837 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005838 }
5839}
5840
5841/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005842 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005843 * @ctxt: the XPath Parser context
5844 *
5845 * [26] MultiplicativeExpr ::= UnaryExpr
5846 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5847 * | MultiplicativeExpr 'div' UnaryExpr
5848 * | MultiplicativeExpr 'mod' UnaryExpr
5849 * [34] MultiplyOperator ::= '*'
5850 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005851 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005852 */
5853
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005854static void
5855xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5856 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005857 CHECK_ERROR;
5858 SKIP_BLANKS;
5859 while ((CUR == '*') ||
5860 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5861 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5862 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005863 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005864
5865 if (CUR == '*') {
5866 op = 0;
5867 NEXT;
5868 } else if (CUR == 'd') {
5869 op = 1;
5870 SKIP(3);
5871 } else if (CUR == 'm') {
5872 op = 2;
5873 SKIP(3);
5874 }
5875 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005876 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005877 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005878 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005879 SKIP_BLANKS;
5880 }
5881}
5882
5883/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005884 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005885 * @ctxt: the XPath Parser context
5886 *
5887 * [25] AdditiveExpr ::= MultiplicativeExpr
5888 * | AdditiveExpr '+' MultiplicativeExpr
5889 * | AdditiveExpr '-' MultiplicativeExpr
5890 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005891 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005892 */
5893
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005894static void
5895xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005896
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005897 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005898 CHECK_ERROR;
5899 SKIP_BLANKS;
5900 while ((CUR == '+') || (CUR == '-')) {
5901 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005902 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005903
5904 if (CUR == '+') plus = 1;
5905 else plus = 0;
5906 NEXT;
5907 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005908 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005909 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005910 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005911 SKIP_BLANKS;
5912 }
5913}
5914
5915/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005916 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005917 * @ctxt: the XPath Parser context
5918 *
5919 * [24] RelationalExpr ::= AdditiveExpr
5920 * | RelationalExpr '<' AdditiveExpr
5921 * | RelationalExpr '>' AdditiveExpr
5922 * | RelationalExpr '<=' AdditiveExpr
5923 * | RelationalExpr '>=' AdditiveExpr
5924 *
5925 * A <= B > C is allowed ? Answer from James, yes with
5926 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5927 * which is basically what got implemented.
5928 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005929 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00005930 * on the stack
5931 */
5932
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005933static void
5934xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
5935 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005936 CHECK_ERROR;
5937 SKIP_BLANKS;
5938 while ((CUR == '<') ||
5939 (CUR == '>') ||
5940 ((CUR == '<') && (NXT(1) == '=')) ||
5941 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005942 int inf, strict;
5943 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005944
5945 if (CUR == '<') inf = 1;
5946 else inf = 0;
5947 if (NXT(1) == '=') strict = 0;
5948 else strict = 1;
5949 NEXT;
5950 if (!strict) NEXT;
5951 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005952 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005953 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005954 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00005955 SKIP_BLANKS;
5956 }
5957}
5958
5959/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005960 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005961 * @ctxt: the XPath Parser context
5962 *
5963 * [23] EqualityExpr ::= RelationalExpr
5964 * | EqualityExpr '=' RelationalExpr
5965 * | EqualityExpr '!=' RelationalExpr
5966 *
5967 * A != B != C is allowed ? Answer from James, yes with
5968 * (RelationalExpr = RelationalExpr) = RelationalExpr
5969 * (RelationalExpr != RelationalExpr) != RelationalExpr
5970 * which is basically what got implemented.
5971 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005972 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005973 *
5974 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005975static void
5976xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
5977 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005978 CHECK_ERROR;
5979 SKIP_BLANKS;
5980 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005981 int eq;
5982 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005983
5984 if (CUR == '=') eq = 1;
5985 else eq = 0;
5986 NEXT;
5987 if (!eq) NEXT;
5988 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005989 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005990 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005991 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005992 SKIP_BLANKS;
5993 }
5994}
5995
5996/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005997 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005998 * @ctxt: the XPath Parser context
5999 *
6000 * [22] AndExpr ::= EqualityExpr
6001 * | AndExpr 'and' EqualityExpr
6002 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006003 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006004 *
6005 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006006static void
6007xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6008 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006009 CHECK_ERROR;
6010 SKIP_BLANKS;
6011 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006012 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006013 SKIP(3);
6014 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006015 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006016 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006017 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006018 SKIP_BLANKS;
6019 }
6020}
6021
6022/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006023 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006024 * @ctxt: the XPath Parser context
6025 *
6026 * [14] Expr ::= OrExpr
6027 * [21] OrExpr ::= AndExpr
6028 * | OrExpr 'or' AndExpr
6029 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006030 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006031 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006032static void
6033xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6034 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006035 CHECK_ERROR;
6036 SKIP_BLANKS;
6037 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006038 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006039 SKIP(2);
6040 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006041 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006042 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006043 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6044 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006045 SKIP_BLANKS;
6046 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006047 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6048 /* more ops could be optimized too */
6049 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6050 }
Owen Taylor3473f882001-02-23 17:55:21 +00006051}
6052
6053/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006054 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006055 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006056 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006057 *
6058 * [8] Predicate ::= '[' PredicateExpr ']'
6059 * [9] PredicateExpr ::= Expr
6060 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006061 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006062 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006063static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006064xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006065 int op1 = ctxt->comp->last;
6066
6067 SKIP_BLANKS;
6068 if (CUR != '[') {
6069 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6070 }
6071 NEXT;
6072 SKIP_BLANKS;
6073
6074 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006075 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006076 CHECK_ERROR;
6077
6078 if (CUR != ']') {
6079 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6080 }
6081
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006082 if (filter)
6083 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6084 else
6085 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006086
6087 NEXT;
6088 SKIP_BLANKS;
6089}
6090
6091/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006092 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006093 * @ctxt: the XPath Parser context
6094 * @test: pointer to a xmlXPathTestVal
6095 * @type: pointer to a xmlXPathTypeVal
6096 * @prefix: placeholder for a possible name prefix
6097 *
6098 * [7] NodeTest ::= NameTest
6099 * | NodeType '(' ')'
6100 * | 'processing-instruction' '(' Literal ')'
6101 *
6102 * [37] NameTest ::= '*'
6103 * | NCName ':' '*'
6104 * | QName
6105 * [38] NodeType ::= 'comment'
6106 * | 'text'
6107 * | 'processing-instruction'
6108 * | 'node'
6109 *
6110 * Returns the name found and update @test, @type and @prefix appropriately
6111 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006112static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006113xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6114 xmlXPathTypeVal *type, const xmlChar **prefix,
6115 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006116 int blanks;
6117
6118 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6119 STRANGE;
6120 return(NULL);
6121 }
6122 *type = 0;
6123 *test = 0;
6124 *prefix = NULL;
6125 SKIP_BLANKS;
6126
6127 if ((name == NULL) && (CUR == '*')) {
6128 /*
6129 * All elements
6130 */
6131 NEXT;
6132 *test = NODE_TEST_ALL;
6133 return(NULL);
6134 }
6135
6136 if (name == NULL)
6137 name = xmlXPathParseNCName(ctxt);
6138 if (name == NULL) {
6139 XP_ERROR0(XPATH_EXPR_ERROR);
6140 }
6141
6142 blanks = IS_BLANK(CUR);
6143 SKIP_BLANKS;
6144 if (CUR == '(') {
6145 NEXT;
6146 /*
6147 * NodeType or PI search
6148 */
6149 if (xmlStrEqual(name, BAD_CAST "comment"))
6150 *type = NODE_TYPE_COMMENT;
6151 else if (xmlStrEqual(name, BAD_CAST "node"))
6152 *type = NODE_TYPE_NODE;
6153 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6154 *type = NODE_TYPE_PI;
6155 else if (xmlStrEqual(name, BAD_CAST "text"))
6156 *type = NODE_TYPE_TEXT;
6157 else {
6158 if (name != NULL)
6159 xmlFree(name);
6160 XP_ERROR0(XPATH_EXPR_ERROR);
6161 }
6162
6163 *test = NODE_TEST_TYPE;
6164
6165 SKIP_BLANKS;
6166 if (*type == NODE_TYPE_PI) {
6167 /*
6168 * Specific case: search a PI by name.
6169 */
Owen Taylor3473f882001-02-23 17:55:21 +00006170 if (name != NULL)
6171 xmlFree(name);
6172
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006173 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006174 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006175 SKIP_BLANKS;
6176 }
6177 if (CUR != ')') {
6178 if (name != NULL)
6179 xmlFree(name);
6180 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6181 }
6182 NEXT;
6183 return(name);
6184 }
6185 *test = NODE_TEST_NAME;
6186 if ((!blanks) && (CUR == ':')) {
6187 NEXT;
6188
6189 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006190 * Since currently the parser context don't have a
6191 * namespace list associated:
6192 * The namespace name for this prefix can be computed
6193 * only at evaluation time. The compilation is done
6194 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006195 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006196#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006197 *prefix = xmlXPathNsLookup(ctxt->context, name);
6198 if (name != NULL)
6199 xmlFree(name);
6200 if (*prefix == NULL) {
6201 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6202 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006203#else
6204 *prefix = name;
6205#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006206
6207 if (CUR == '*') {
6208 /*
6209 * All elements
6210 */
6211 NEXT;
6212 *test = NODE_TEST_ALL;
6213 return(NULL);
6214 }
6215
6216 name = xmlXPathParseNCName(ctxt);
6217 if (name == NULL) {
6218 XP_ERROR0(XPATH_EXPR_ERROR);
6219 }
6220 }
6221 return(name);
6222}
6223
6224/**
6225 * xmlXPathIsAxisName:
6226 * @name: a preparsed name token
6227 *
6228 * [6] AxisName ::= 'ancestor'
6229 * | 'ancestor-or-self'
6230 * | 'attribute'
6231 * | 'child'
6232 * | 'descendant'
6233 * | 'descendant-or-self'
6234 * | 'following'
6235 * | 'following-sibling'
6236 * | 'namespace'
6237 * | 'parent'
6238 * | 'preceding'
6239 * | 'preceding-sibling'
6240 * | 'self'
6241 *
6242 * Returns the axis or 0
6243 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006244static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006245xmlXPathIsAxisName(const xmlChar *name) {
6246 xmlXPathAxisVal ret = 0;
6247 switch (name[0]) {
6248 case 'a':
6249 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6250 ret = AXIS_ANCESTOR;
6251 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6252 ret = AXIS_ANCESTOR_OR_SELF;
6253 if (xmlStrEqual(name, BAD_CAST "attribute"))
6254 ret = AXIS_ATTRIBUTE;
6255 break;
6256 case 'c':
6257 if (xmlStrEqual(name, BAD_CAST "child"))
6258 ret = AXIS_CHILD;
6259 break;
6260 case 'd':
6261 if (xmlStrEqual(name, BAD_CAST "descendant"))
6262 ret = AXIS_DESCENDANT;
6263 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6264 ret = AXIS_DESCENDANT_OR_SELF;
6265 break;
6266 case 'f':
6267 if (xmlStrEqual(name, BAD_CAST "following"))
6268 ret = AXIS_FOLLOWING;
6269 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6270 ret = AXIS_FOLLOWING_SIBLING;
6271 break;
6272 case 'n':
6273 if (xmlStrEqual(name, BAD_CAST "namespace"))
6274 ret = AXIS_NAMESPACE;
6275 break;
6276 case 'p':
6277 if (xmlStrEqual(name, BAD_CAST "parent"))
6278 ret = AXIS_PARENT;
6279 if (xmlStrEqual(name, BAD_CAST "preceding"))
6280 ret = AXIS_PRECEDING;
6281 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6282 ret = AXIS_PRECEDING_SIBLING;
6283 break;
6284 case 's':
6285 if (xmlStrEqual(name, BAD_CAST "self"))
6286 ret = AXIS_SELF;
6287 break;
6288 }
6289 return(ret);
6290}
6291
6292/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006293 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006294 * @ctxt: the XPath Parser context
6295 *
6296 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6297 * | AbbreviatedStep
6298 *
6299 * [12] AbbreviatedStep ::= '.' | '..'
6300 *
6301 * [5] AxisSpecifier ::= AxisName '::'
6302 * | AbbreviatedAxisSpecifier
6303 *
6304 * [13] AbbreviatedAxisSpecifier ::= '@'?
6305 *
6306 * Modified for XPtr range support as:
6307 *
6308 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6309 * | AbbreviatedStep
6310 * | 'range-to' '(' Expr ')' Predicate*
6311 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006312 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006313 * A location step of . is short for self::node(). This is
6314 * particularly useful in conjunction with //. For example, the
6315 * location path .//para is short for
6316 * self::node()/descendant-or-self::node()/child::para
6317 * and so will select all para descendant elements of the context
6318 * node.
6319 * Similarly, a location step of .. is short for parent::node().
6320 * For example, ../title is short for parent::node()/child::title
6321 * and so will select the title children of the parent of the context
6322 * node.
6323 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006324static void
6325xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006326 SKIP_BLANKS;
6327 if ((CUR == '.') && (NXT(1) == '.')) {
6328 SKIP(2);
6329 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006330 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6331 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006332 } else if (CUR == '.') {
6333 NEXT;
6334 SKIP_BLANKS;
6335 } else {
6336 xmlChar *name = NULL;
6337 const xmlChar *prefix = NULL;
6338 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006339 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006340 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006341 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006342
6343 /*
6344 * The modification needed for XPointer change to the production
6345 */
6346#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006347 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006348 name = xmlXPathParseNCName(ctxt);
6349 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006350 int op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006351 xmlFree(name);
6352 SKIP_BLANKS;
6353 if (CUR != '(') {
6354 XP_ERROR(XPATH_EXPR_ERROR);
6355 }
6356 NEXT;
6357 SKIP_BLANKS;
6358
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006359 xmlXPathCompileExpr(ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006360 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006361 CHECK_ERROR;
6362
6363 SKIP_BLANKS;
6364 if (CUR != ')') {
6365 XP_ERROR(XPATH_EXPR_ERROR);
6366 }
6367 NEXT;
6368 goto eval_predicates;
6369 }
6370 }
6371#endif
6372 if (name == NULL)
6373 name = xmlXPathParseNCName(ctxt);
6374 if (name != NULL) {
6375 axis = xmlXPathIsAxisName(name);
6376 if (axis != 0) {
6377 SKIP_BLANKS;
6378 if ((CUR == ':') && (NXT(1) == ':')) {
6379 SKIP(2);
6380 xmlFree(name);
6381 name = NULL;
6382 } else {
6383 /* an element name can conflict with an axis one :-\ */
6384 axis = AXIS_CHILD;
6385 }
6386 } else {
6387 axis = AXIS_CHILD;
6388 }
6389 } else if (CUR == '@') {
6390 NEXT;
6391 axis = AXIS_ATTRIBUTE;
6392 } else {
6393 axis = AXIS_CHILD;
6394 }
6395
6396 CHECK_ERROR;
6397
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006398 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006399 if (test == 0)
6400 return;
6401
6402#ifdef DEBUG_STEP
6403 xmlGenericError(xmlGenericErrorContext,
6404 "Basis : computing new set\n");
6405#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006406
Owen Taylor3473f882001-02-23 17:55:21 +00006407#ifdef DEBUG_STEP
6408 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6409 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6410#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006411
6412eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006413 op1 = ctxt->comp->last;
6414 ctxt->comp->last = -1;
6415
Owen Taylor3473f882001-02-23 17:55:21 +00006416 SKIP_BLANKS;
6417 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006418 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006419 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006420
6421 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6422 test, type, (void *)prefix, (void *)name);
6423
Owen Taylor3473f882001-02-23 17:55:21 +00006424 }
6425#ifdef DEBUG_STEP
6426 xmlGenericError(xmlGenericErrorContext, "Step : ");
6427 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6428 ctxt->value->nodesetval);
6429#endif
6430}
6431
6432/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006433 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006434 * @ctxt: the XPath Parser context
6435 *
6436 * [3] RelativeLocationPath ::= Step
6437 * | RelativeLocationPath '/' Step
6438 * | AbbreviatedRelativeLocationPath
6439 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6440 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006441 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006442 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006443static void
Owen Taylor3473f882001-02-23 17:55:21 +00006444#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006445xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006446#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006447xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006448#endif
6449(xmlXPathParserContextPtr ctxt) {
6450 SKIP_BLANKS;
6451 if ((CUR == '/') && (NXT(1) == '/')) {
6452 SKIP(2);
6453 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006454 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6455 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006456 } else if (CUR == '/') {
6457 NEXT;
6458 SKIP_BLANKS;
6459 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006460 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006461 SKIP_BLANKS;
6462 while (CUR == '/') {
6463 if ((CUR == '/') && (NXT(1) == '/')) {
6464 SKIP(2);
6465 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006466 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006467 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006468 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006469 } else if (CUR == '/') {
6470 NEXT;
6471 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006472 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006473 }
6474 SKIP_BLANKS;
6475 }
6476}
6477
6478/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006479 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006480 * @ctxt: the XPath Parser context
6481 *
6482 * [1] LocationPath ::= RelativeLocationPath
6483 * | AbsoluteLocationPath
6484 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6485 * | AbbreviatedAbsoluteLocationPath
6486 * [10] AbbreviatedAbsoluteLocationPath ::=
6487 * '//' RelativeLocationPath
6488 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006489 * Compile a location path
6490 *
Owen Taylor3473f882001-02-23 17:55:21 +00006491 * // is short for /descendant-or-self::node()/. For example,
6492 * //para is short for /descendant-or-self::node()/child::para and
6493 * so will select any para element in the document (even a para element
6494 * that is a document element will be selected by //para since the
6495 * document element node is a child of the root node); div//para is
6496 * short for div/descendant-or-self::node()/child::para and so will
6497 * select all para descendants of div children.
6498 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006499static void
6500xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006501 SKIP_BLANKS;
6502 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006503 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006504 } else {
6505 while (CUR == '/') {
6506 if ((CUR == '/') && (NXT(1) == '/')) {
6507 SKIP(2);
6508 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006509 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6510 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006511 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006512 } else if (CUR == '/') {
6513 NEXT;
6514 SKIP_BLANKS;
6515 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006516 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006517 }
6518 }
6519 }
6520}
6521
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006522/************************************************************************
6523 * *
6524 * XPath precompiled expression evaluation *
6525 * *
6526 ************************************************************************/
6527
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006528static void
6529xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6530
6531/**
6532 * xmlXPathNodeCollectAndTest:
6533 * @ctxt: the XPath Parser context
6534 * @op: the XPath precompiled step operation
6535 *
6536 * This is the function implementing a step: based on the current list
6537 * of nodes, it builds up a new list, looking at all nodes under that
6538 * axis and selecting them it also do the predicate filtering
6539 *
6540 * Pushes the new NodeSet resulting from the search.
6541 */
6542static void
6543xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6544 xmlXPathStepOpPtr op) {
6545 xmlXPathAxisVal axis = op->value;
6546 xmlXPathTestVal test = op->value2;
6547 xmlXPathTypeVal type = op->value3;
6548 const xmlChar *prefix = op->value4;
6549 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006550 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006551
6552#ifdef DEBUG_STEP
6553 int n = 0, t = 0;
6554#endif
6555 int i;
6556 xmlNodeSetPtr ret, list;
6557 xmlXPathTraversalFunction next = NULL;
6558 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6559 xmlNodePtr cur = NULL;
6560 xmlXPathObjectPtr obj;
6561 xmlNodeSetPtr nodelist;
6562 xmlNodePtr tmp;
6563
6564 CHECK_TYPE(XPATH_NODESET);
6565 obj = valuePop(ctxt);
6566 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006567 if (prefix != NULL) {
6568 URI = xmlXPathNsLookup(ctxt->context, prefix);
6569 if (URI == NULL)
6570 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6571 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006572
6573#ifdef DEBUG_STEP
6574 xmlGenericError(xmlGenericErrorContext,
6575 "new step : ");
6576#endif
6577 switch (axis) {
6578 case AXIS_ANCESTOR:
6579#ifdef DEBUG_STEP
6580 xmlGenericError(xmlGenericErrorContext,
6581 "axis 'ancestors' ");
6582#endif
6583 next = xmlXPathNextAncestor; break;
6584 case AXIS_ANCESTOR_OR_SELF:
6585#ifdef DEBUG_STEP
6586 xmlGenericError(xmlGenericErrorContext,
6587 "axis 'ancestors-or-self' ");
6588#endif
6589 next = xmlXPathNextAncestorOrSelf; break;
6590 case AXIS_ATTRIBUTE:
6591#ifdef DEBUG_STEP
6592 xmlGenericError(xmlGenericErrorContext,
6593 "axis 'attributes' ");
6594#endif
6595 next = xmlXPathNextAttribute; break;
6596 break;
6597 case AXIS_CHILD:
6598#ifdef DEBUG_STEP
6599 xmlGenericError(xmlGenericErrorContext,
6600 "axis 'child' ");
6601#endif
6602 next = xmlXPathNextChild; break;
6603 case AXIS_DESCENDANT:
6604#ifdef DEBUG_STEP
6605 xmlGenericError(xmlGenericErrorContext,
6606 "axis 'descendant' ");
6607#endif
6608 next = xmlXPathNextDescendant; break;
6609 case AXIS_DESCENDANT_OR_SELF:
6610#ifdef DEBUG_STEP
6611 xmlGenericError(xmlGenericErrorContext,
6612 "axis 'descendant-or-self' ");
6613#endif
6614 next = xmlXPathNextDescendantOrSelf; break;
6615 case AXIS_FOLLOWING:
6616#ifdef DEBUG_STEP
6617 xmlGenericError(xmlGenericErrorContext,
6618 "axis 'following' ");
6619#endif
6620 next = xmlXPathNextFollowing; break;
6621 case AXIS_FOLLOWING_SIBLING:
6622#ifdef DEBUG_STEP
6623 xmlGenericError(xmlGenericErrorContext,
6624 "axis 'following-siblings' ");
6625#endif
6626 next = xmlXPathNextFollowingSibling; break;
6627 case AXIS_NAMESPACE:
6628#ifdef DEBUG_STEP
6629 xmlGenericError(xmlGenericErrorContext,
6630 "axis 'namespace' ");
6631#endif
6632 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6633 break;
6634 case AXIS_PARENT:
6635#ifdef DEBUG_STEP
6636 xmlGenericError(xmlGenericErrorContext,
6637 "axis 'parent' ");
6638#endif
6639 next = xmlXPathNextParent; break;
6640 case AXIS_PRECEDING:
6641#ifdef DEBUG_STEP
6642 xmlGenericError(xmlGenericErrorContext,
6643 "axis 'preceding' ");
6644#endif
6645 next = xmlXPathNextPreceding; break;
6646 case AXIS_PRECEDING_SIBLING:
6647#ifdef DEBUG_STEP
6648 xmlGenericError(xmlGenericErrorContext,
6649 "axis 'preceding-sibling' ");
6650#endif
6651 next = xmlXPathNextPrecedingSibling; break;
6652 case AXIS_SELF:
6653#ifdef DEBUG_STEP
6654 xmlGenericError(xmlGenericErrorContext,
6655 "axis 'self' ");
6656#endif
6657 next = xmlXPathNextSelf; break;
6658 }
6659 if (next == NULL)
6660 return;
6661
6662 nodelist = obj->nodesetval;
6663 if (nodelist == NULL) {
6664 xmlXPathFreeObject(obj);
6665 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6666 return;
6667 }
6668 addNode = xmlXPathNodeSetAddUnique;
6669 ret = NULL;
6670#ifdef DEBUG_STEP
6671 xmlGenericError(xmlGenericErrorContext,
6672 " context contains %d nodes\n",
6673 nodelist->nodeNr);
6674 switch (test) {
6675 case NODE_TEST_NODE:
6676 xmlGenericError(xmlGenericErrorContext,
6677 " searching all nodes\n");
6678 break;
6679 case NODE_TEST_NONE:
6680 xmlGenericError(xmlGenericErrorContext,
6681 " searching for none !!!\n");
6682 break;
6683 case NODE_TEST_TYPE:
6684 xmlGenericError(xmlGenericErrorContext,
6685 " searching for type %d\n", type);
6686 break;
6687 case NODE_TEST_PI:
6688 xmlGenericError(xmlGenericErrorContext,
6689 " searching for PI !!!\n");
6690 break;
6691 case NODE_TEST_ALL:
6692 xmlGenericError(xmlGenericErrorContext,
6693 " searching for *\n");
6694 break;
6695 case NODE_TEST_NS:
6696 xmlGenericError(xmlGenericErrorContext,
6697 " searching for namespace %s\n",
6698 prefix);
6699 break;
6700 case NODE_TEST_NAME:
6701 xmlGenericError(xmlGenericErrorContext,
6702 " searching for name %s\n", name);
6703 if (prefix != NULL)
6704 xmlGenericError(xmlGenericErrorContext,
6705 " with namespace %s\n",
6706 prefix);
6707 break;
6708 }
6709 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6710#endif
6711 /*
6712 * 2.3 Node Tests
6713 * - For the attribute axis, the principal node type is attribute.
6714 * - For the namespace axis, the principal node type is namespace.
6715 * - For other axes, the principal node type is element.
6716 *
6717 * A node test * is true for any node of the
6718 * principal node type. For example, child::* willi
6719 * select all element children of the context node
6720 */
6721 tmp = ctxt->context->node;
6722 for (i = 0;i < nodelist->nodeNr; i++) {
6723 ctxt->context->node = nodelist->nodeTab[i];
6724
6725 cur = NULL;
6726 list = xmlXPathNodeSetCreate(NULL);
6727 do {
6728 cur = next(ctxt, cur);
6729 if (cur == NULL) break;
6730#ifdef DEBUG_STEP
6731 t++;
6732 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6733#endif
6734 switch (test) {
6735 case NODE_TEST_NONE:
6736 ctxt->context->node = tmp;
6737 STRANGE
6738 return;
6739 case NODE_TEST_TYPE:
6740 if ((cur->type == type) ||
6741 ((type == NODE_TYPE_NODE) &&
6742 ((cur->type == XML_DOCUMENT_NODE) ||
6743 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6744 (cur->type == XML_ELEMENT_NODE) ||
6745 (cur->type == XML_PI_NODE) ||
6746 (cur->type == XML_COMMENT_NODE) ||
6747 (cur->type == XML_CDATA_SECTION_NODE) ||
6748 (cur->type == XML_TEXT_NODE)))) {
6749#ifdef DEBUG_STEP
6750 n++;
6751#endif
6752 addNode(list, cur);
6753 }
6754 break;
6755 case NODE_TEST_PI:
6756 if (cur->type == XML_PI_NODE) {
6757 if ((name != NULL) &&
6758 (!xmlStrEqual(name, cur->name)))
6759 break;
6760#ifdef DEBUG_STEP
6761 n++;
6762#endif
6763 addNode(list, cur);
6764 }
6765 break;
6766 case NODE_TEST_ALL:
6767 if (axis == AXIS_ATTRIBUTE) {
6768 if (cur->type == XML_ATTRIBUTE_NODE) {
6769#ifdef DEBUG_STEP
6770 n++;
6771#endif
6772 addNode(list, cur);
6773 }
6774 } else if (axis == AXIS_NAMESPACE) {
6775 if (cur->type == XML_NAMESPACE_DECL) {
6776#ifdef DEBUG_STEP
6777 n++;
6778#endif
6779 addNode(list, cur);
6780 }
6781 } else {
6782 if ((cur->type == XML_ELEMENT_NODE) ||
6783 (cur->type == XML_DOCUMENT_NODE) ||
6784 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6785 if (prefix == NULL) {
6786#ifdef DEBUG_STEP
6787 n++;
6788#endif
6789 addNode(list, cur);
6790 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006791 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006792 cur->ns->href))) {
6793#ifdef DEBUG_STEP
6794 n++;
6795#endif
6796 addNode(list, cur);
6797 }
6798 }
6799 }
6800 break;
6801 case NODE_TEST_NS: {
6802 TODO;
6803 break;
6804 }
6805 case NODE_TEST_NAME:
6806 switch (cur->type) {
6807 case XML_ELEMENT_NODE:
6808 if (xmlStrEqual(name, cur->name)) {
6809 if (prefix == NULL) {
6810 if ((cur->ns == NULL) ||
6811 (cur->ns->prefix == NULL)) {
6812#ifdef DEBUG_STEP
6813 n++;
6814#endif
6815 addNode(list, cur);
6816 }
6817 } else {
6818 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006819 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006820 cur->ns->href))) {
6821#ifdef DEBUG_STEP
6822 n++;
6823#endif
6824 addNode(list, cur);
6825 }
6826 }
6827 }
6828 break;
6829 case XML_ATTRIBUTE_NODE: {
6830 xmlAttrPtr attr = (xmlAttrPtr) cur;
6831 if (xmlStrEqual(name, attr->name)) {
6832 if (prefix == NULL) {
6833 if ((attr->ns == NULL) ||
6834 (attr->ns->prefix == NULL)) {
6835#ifdef DEBUG_STEP
6836 n++;
6837#endif
6838 addNode(list, (xmlNodePtr) attr);
6839 }
6840 } else {
6841 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006842 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006843 attr->ns->href))) {
6844#ifdef DEBUG_STEP
6845 n++;
6846#endif
6847 addNode(list, (xmlNodePtr) attr);
6848 }
6849 }
6850 }
6851 break;
6852 }
6853 case XML_NAMESPACE_DECL: {
6854 TODO;
6855 break;
6856 }
6857 default:
6858 break;
6859 }
6860 break;
6861 }
6862 } while (cur != NULL);
6863
6864 /*
6865 * If there is some predicate filtering do it now
6866 */
6867 if (op->ch2 != -1) {
6868 xmlXPathObjectPtr obj2;
6869
6870 valuePush(ctxt, xmlXPathWrapNodeSet(list));
6871 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
6872 CHECK_TYPE(XPATH_NODESET);
6873 obj2 = valuePop(ctxt);
6874 list = obj2->nodesetval;
6875 obj2->nodesetval = NULL;
6876 xmlXPathFreeObject(obj2);
6877 }
6878 if (ret == NULL) {
6879 ret = list;
6880 } else {
6881 ret = xmlXPathNodeSetMerge(ret, list);
6882 xmlXPathFreeNodeSet(list);
6883 }
6884 }
6885 ctxt->context->node = tmp;
6886#ifdef DEBUG_STEP
6887 xmlGenericError(xmlGenericErrorContext,
6888 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
6889#endif
6890 xmlXPathFreeObject(obj);
6891 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
6892}
6893
Owen Taylor3473f882001-02-23 17:55:21 +00006894/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006895 * xmlXPathCompOpEval:
6896 * @ctxt: the XPath parser context with the compiled expression
6897 * @op: an XPath compiled operation
6898 *
6899 * Evaluate the Precompiled XPath operation
6900 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006901static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006902xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6903 int equal, ret;
6904 xmlXPathCompExprPtr comp;
6905 xmlXPathObjectPtr arg1, arg2;
6906
6907 comp = ctxt->comp;
6908 switch (op->op) {
6909 case XPATH_OP_END:
6910 return;
6911 case XPATH_OP_AND:
6912 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6913 xmlXPathBooleanFunction(ctxt, 1);
6914 if (ctxt->value->boolval == 0)
6915 return;
6916 arg2 = valuePop(ctxt);
6917 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6918 xmlXPathBooleanFunction(ctxt, 1);
6919 arg1 = valuePop(ctxt);
6920 arg1->boolval &= arg2->boolval;
6921 valuePush(ctxt, arg1);
6922 xmlXPathFreeObject(arg2);
6923 return;
6924 case XPATH_OP_OR:
6925 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6926 xmlXPathBooleanFunction(ctxt, 1);
6927 if (ctxt->value->boolval == 1)
6928 return;
6929 arg2 = valuePop(ctxt);
6930 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6931 xmlXPathBooleanFunction(ctxt, 1);
6932 arg1 = valuePop(ctxt);
6933 arg1->boolval |= arg2->boolval;
6934 valuePush(ctxt, arg1);
6935 xmlXPathFreeObject(arg2);
6936 return;
6937 case XPATH_OP_EQUAL:
6938 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6939 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6940 equal = xmlXPathEqualValues(ctxt);
6941 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6942 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6943 return;
6944 case XPATH_OP_CMP:
6945 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6946 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6947 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6948 valuePush(ctxt, xmlXPathNewBoolean(ret));
6949 return;
6950 case XPATH_OP_PLUS:
6951 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6952 if (op->ch2 != -1)
6953 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6954 if (op->value == 0) xmlXPathSubValues(ctxt);
6955 else if (op->value == 1) xmlXPathAddValues(ctxt);
6956 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6957 else if (op->value == 3) {
6958 xmlXPathObjectPtr arg;
6959
6960 POP_FLOAT
6961 valuePush(ctxt, arg);
6962 }
6963 return;
6964 case XPATH_OP_MULT:
6965 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6966 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6967 if (op->value == 0) xmlXPathMultValues(ctxt);
6968 else if (op->value == 1) xmlXPathDivValues(ctxt);
6969 else if (op->value == 2) xmlXPathModValues(ctxt);
6970 return;
6971 case XPATH_OP_UNION:
6972 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6973 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6974 CHECK_TYPE(XPATH_NODESET);
6975 arg2 = valuePop(ctxt);
6976
6977 CHECK_TYPE(XPATH_NODESET);
6978 arg1 = valuePop(ctxt);
6979
6980 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6981 arg2->nodesetval);
6982 valuePush(ctxt, arg1);
6983 xmlXPathFreeObject(arg2);
6984 return;
6985 case XPATH_OP_ROOT:
6986 xmlXPathRoot(ctxt);
6987 return;
6988 case XPATH_OP_NODE:
6989 if (op->ch1 != -1)
6990 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6991 if (op->ch2 != -1)
6992 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6993 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6994 return;
6995 case XPATH_OP_RESET:
6996 if (op->ch1 != -1)
6997 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6998 if (op->ch2 != -1)
6999 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7000 ctxt->context->node = NULL;
7001 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007002 case XPATH_OP_COLLECT: {
7003 if (op->ch1 == -1)
7004 return;
7005
7006 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7007 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007008 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007009 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007010 case XPATH_OP_VALUE:
7011 valuePush(ctxt,
7012 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7013 return;
7014 case XPATH_OP_VARIABLE: {
7015 if (op->ch1 != -1)
7016 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7017 if (op->value5 == NULL)
7018 valuePush(ctxt,
7019 xmlXPathVariableLookup(ctxt->context, op->value4));
7020 else {
7021 const xmlChar *URI;
7022 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7023 if (URI == NULL) {
7024 xmlGenericError(xmlGenericErrorContext,
7025 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7026 op->value4, op->value5);
7027 return;
7028 }
7029 valuePush(ctxt,
7030 xmlXPathVariableLookupNS(ctxt->context,
7031 op->value4, URI));
7032 }
7033 return;
7034 }
7035 case XPATH_OP_FUNCTION: {
7036 xmlXPathFunction func;
7037
7038 if (op->ch1 != -1)
7039 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7040 if (op->value5 == NULL)
7041 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7042 else {
7043 const xmlChar *URI;
7044 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7045 if (URI == NULL) {
7046 xmlGenericError(xmlGenericErrorContext,
7047 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7048 op->value4, op->value5);
7049 return;
7050 }
7051 func = xmlXPathFunctionLookupNS(ctxt->context,
7052 op->value4, URI);
7053 }
7054 if (func == NULL) {
7055 xmlGenericError(xmlGenericErrorContext,
7056 "xmlXPathRunEval: function %s not found\n",
7057 op->value4);
7058 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7059 return;
7060 }
7061 func(ctxt, op->value);
7062 return;
7063 }
7064 case XPATH_OP_ARG:
7065 if (op->ch1 != -1)
7066 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7067 if (op->ch2 != -1)
7068 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7069 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007070 case XPATH_OP_PREDICATE:
7071 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007072 xmlXPathObjectPtr res;
7073 xmlXPathObjectPtr obj, tmp;
7074 xmlNodeSetPtr newset = NULL;
7075 xmlNodeSetPtr oldset;
7076 xmlNodePtr oldnode;
7077 int i;
7078
7079 if (op->ch1 != -1)
7080 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7081 if (op->ch2 == -1)
7082 return;
7083
7084 oldnode = ctxt->context->node;
7085
7086#ifdef LIBXML_XPTR_ENABLED
7087 /*
7088 * Hum are we filtering the result of an XPointer expression
7089 */
7090 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007091 xmlLocationSetPtr newlocset = NULL;
7092 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007093
7094 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007095 * Extract the old locset, and then evaluate the result of the
7096 * expression for all the element in the locset. use it to grow
7097 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007098 */
7099 CHECK_TYPE(XPATH_LOCATIONSET);
7100 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007101 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007102 ctxt->context->node = NULL;
7103
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007104 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007105 ctxt->context->contextSize = 0;
7106 ctxt->context->proximityPosition = 0;
7107 if (op->ch2 != -1)
7108 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7109 res = valuePop(ctxt);
7110 if (res != NULL)
7111 xmlXPathFreeObject(res);
7112 valuePush(ctxt, obj);
7113 CHECK_ERROR;
7114 return;
7115 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007116 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007117
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007118 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007119 /*
7120 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007121 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007122 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007123 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007124 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7125 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007126 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007127 ctxt->context->proximityPosition = i + 1;
7128
7129 if (op->ch2 != -1)
7130 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7131 CHECK_ERROR;
7132
7133 /*
7134 * The result of the evaluation need to be tested to
7135 * decided whether the filter succeeded or not
7136 */
7137 res = valuePop(ctxt);
7138 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007139 xmlXPtrLocationSetAdd(newlocset,
7140 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007141 }
7142
7143 /*
7144 * Cleanup
7145 */
7146 if (res != NULL)
7147 xmlXPathFreeObject(res);
7148 if (ctxt->value == tmp) {
7149 res = valuePop(ctxt);
7150 xmlXPathFreeObject(res);
7151 }
7152
7153 ctxt->context->node = NULL;
7154 }
7155
7156 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007157 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007158 */
7159 xmlXPathFreeObject(obj);
7160 ctxt->context->node = NULL;
7161 ctxt->context->contextSize = -1;
7162 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007163 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007164 ctxt->context->node = oldnode;
7165 return;
7166 }
7167#endif /* LIBXML_XPTR_ENABLED */
7168
7169 /*
7170 * Extract the old set, and then evaluate the result of the
7171 * expression for all the element in the set. use it to grow
7172 * up a new set.
7173 */
7174 CHECK_TYPE(XPATH_NODESET);
7175 obj = valuePop(ctxt);
7176 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007177
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007178 oldnode = ctxt->context->node;
7179 ctxt->context->node = NULL;
7180
7181 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7182 ctxt->context->contextSize = 0;
7183 ctxt->context->proximityPosition = 0;
7184 if (op->ch2 != -1)
7185 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7186 res = valuePop(ctxt);
7187 if (res != NULL)
7188 xmlXPathFreeObject(res);
7189 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007190 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007191 CHECK_ERROR;
7192 } else {
7193 /*
7194 * Initialize the new set.
7195 */
7196 newset = xmlXPathNodeSetCreate(NULL);
7197
7198 for (i = 0; i < oldset->nodeNr; i++) {
7199 /*
7200 * Run the evaluation with a node list made of
7201 * a single item in the nodeset.
7202 */
7203 ctxt->context->node = oldset->nodeTab[i];
7204 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7205 valuePush(ctxt, tmp);
7206 ctxt->context->contextSize = oldset->nodeNr;
7207 ctxt->context->proximityPosition = i + 1;
7208
7209 if (op->ch2 != -1)
7210 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7211 CHECK_ERROR;
7212
7213 /*
7214 * The result of the evaluation need to be tested to
7215 * decided whether the filter succeeded or not
7216 */
7217 res = valuePop(ctxt);
7218 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7219 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7220 }
7221
7222 /*
7223 * Cleanup
7224 */
7225 if (res != NULL)
7226 xmlXPathFreeObject(res);
7227 if (ctxt->value == tmp) {
7228 res = valuePop(ctxt);
7229 xmlXPathFreeObject(res);
7230 }
7231
7232 ctxt->context->node = NULL;
7233 }
7234
7235 /*
7236 * The result is used as the new evaluation set.
7237 */
7238 xmlXPathFreeObject(obj);
7239 ctxt->context->node = NULL;
7240 ctxt->context->contextSize = -1;
7241 ctxt->context->proximityPosition = -1;
7242 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7243 }
7244 ctxt->context->node = oldnode;
7245 return;
7246 }
7247 case XPATH_OP_SORT:
7248 if (op->ch1 != -1)
7249 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7250 if ((ctxt->value != NULL) &&
7251 (ctxt->value->type == XPATH_NODESET) &&
7252 (ctxt->value->nodesetval != NULL))
7253 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7254 return;
7255#ifdef LIBXML_XPTR_ENABLED
7256 case XPATH_OP_RANGETO: {
7257 xmlXPathObjectPtr range;
7258 xmlXPathObjectPtr res, obj;
7259 xmlXPathObjectPtr tmp;
7260 xmlLocationSetPtr newset = NULL;
7261 xmlNodeSetPtr oldset;
7262 int i;
7263
7264 if (op->ch1 != -1)
7265 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7266 if (op->ch2 == -1)
7267 return;
7268
7269 CHECK_TYPE(XPATH_NODESET);
7270 obj = valuePop(ctxt);
7271 oldset = obj->nodesetval;
7272 ctxt->context->node = NULL;
7273
7274 newset = xmlXPtrLocationSetCreate(NULL);
7275
Daniel Veillard911f49a2001-04-07 15:39:35 +00007276 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007277 for (i = 0; i < oldset->nodeNr; i++) {
7278 /*
7279 * Run the evaluation with a node list made of a single item
7280 * in the nodeset.
7281 */
7282 ctxt->context->node = oldset->nodeTab[i];
7283 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7284 valuePush(ctxt, tmp);
7285
7286 if (op->ch2 != -1)
7287 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7288 CHECK_ERROR;
7289
7290 /*
7291 * The result of the evaluation need to be tested to
7292 * decided whether the filter succeeded or not
7293 */
7294 res = valuePop(ctxt);
7295 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7296 if (range != NULL) {
7297 xmlXPtrLocationSetAdd(newset, range);
7298 }
7299
7300 /*
7301 * Cleanup
7302 */
7303 if (res != NULL)
7304 xmlXPathFreeObject(res);
7305 if (ctxt->value == tmp) {
7306 res = valuePop(ctxt);
7307 xmlXPathFreeObject(res);
7308 }
7309
7310 ctxt->context->node = NULL;
7311 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007312 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007313
7314 /*
7315 * The result is used as the new evaluation set.
7316 */
7317 xmlXPathFreeObject(obj);
7318 ctxt->context->node = NULL;
7319 ctxt->context->contextSize = -1;
7320 ctxt->context->proximityPosition = -1;
7321 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7322 return;
7323 }
7324#endif /* LIBXML_XPTR_ENABLED */
7325 }
7326 xmlGenericError(xmlGenericErrorContext,
7327 "XPath: unknown precompiled operation %d\n",
7328 op->op);
7329 return;
7330}
7331
7332/**
7333 * xmlXPathRunEval:
7334 * @ctxt: the XPath parser context with the compiled expression
7335 *
7336 * Evaluate the Precompiled XPath expression in the given context.
7337 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007338static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007339xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7340 xmlXPathCompExprPtr comp;
7341
7342 if ((ctxt == NULL) || (ctxt->comp == NULL))
7343 return;
7344
7345 if (ctxt->valueTab == NULL) {
7346 /* Allocate the value stack */
7347 ctxt->valueTab = (xmlXPathObjectPtr *)
7348 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7349 if (ctxt->valueTab == NULL) {
7350 xmlFree(ctxt);
7351 xmlGenericError(xmlGenericErrorContext,
7352 "xmlXPathRunEval: out of memory\n");
7353 return;
7354 }
7355 ctxt->valueNr = 0;
7356 ctxt->valueMax = 10;
7357 ctxt->value = NULL;
7358 }
7359 comp = ctxt->comp;
7360 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7361}
7362
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007363/************************************************************************
7364 * *
7365 * Public interfaces *
7366 * *
7367 ************************************************************************/
7368
7369/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007370 * xmlXPathEvalPredicate:
7371 * @ctxt: the XPath context
7372 * @res: the Predicate Expression evaluation result
7373 *
7374 * Evaluate a predicate result for the current node.
7375 * A PredicateExpr is evaluated by evaluating the Expr and converting
7376 * the result to a boolean. If the result is a number, the result will
7377 * be converted to true if the number is equal to the position of the
7378 * context node in the context node list (as returned by the position
7379 * function) and will be converted to false otherwise; if the result
7380 * is not a number, then the result will be converted as if by a call
7381 * to the boolean function.
7382 *
7383 * Return 1 if predicate is true, 0 otherwise
7384 */
7385int
7386xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7387 if (res == NULL) return(0);
7388 switch (res->type) {
7389 case XPATH_BOOLEAN:
7390 return(res->boolval);
7391 case XPATH_NUMBER:
7392 return(res->floatval == ctxt->proximityPosition);
7393 case XPATH_NODESET:
7394 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007395 if (res->nodesetval == NULL)
7396 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007397 return(res->nodesetval->nodeNr != 0);
7398 case XPATH_STRING:
7399 return((res->stringval != NULL) &&
7400 (xmlStrlen(res->stringval) != 0));
7401 default:
7402 STRANGE
7403 }
7404 return(0);
7405}
7406
7407/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007408 * xmlXPathEvaluatePredicateResult:
7409 * @ctxt: the XPath Parser context
7410 * @res: the Predicate Expression evaluation result
7411 *
7412 * Evaluate a predicate result for the current node.
7413 * A PredicateExpr is evaluated by evaluating the Expr and converting
7414 * the result to a boolean. If the result is a number, the result will
7415 * be converted to true if the number is equal to the position of the
7416 * context node in the context node list (as returned by the position
7417 * function) and will be converted to false otherwise; if the result
7418 * is not a number, then the result will be converted as if by a call
7419 * to the boolean function.
7420 *
7421 * Return 1 if predicate is true, 0 otherwise
7422 */
7423int
7424xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7425 xmlXPathObjectPtr res) {
7426 if (res == NULL) return(0);
7427 switch (res->type) {
7428 case XPATH_BOOLEAN:
7429 return(res->boolval);
7430 case XPATH_NUMBER:
7431 return(res->floatval == ctxt->context->proximityPosition);
7432 case XPATH_NODESET:
7433 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007434 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007435 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007436 return(res->nodesetval->nodeNr != 0);
7437 case XPATH_STRING:
7438 return((res->stringval != NULL) &&
7439 (xmlStrlen(res->stringval) != 0));
7440 default:
7441 STRANGE
7442 }
7443 return(0);
7444}
7445
7446/**
7447 * xmlXPathCompile:
7448 * @str: the XPath expression
7449 *
7450 * Compile an XPath expression
7451 *
7452 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7453 * the caller has to free the object.
7454 */
7455xmlXPathCompExprPtr
7456xmlXPathCompile(const xmlChar *str) {
7457 xmlXPathParserContextPtr ctxt;
7458 xmlXPathCompExprPtr comp;
7459
7460 xmlXPathInit();
7461
7462 ctxt = xmlXPathNewParserContext(str, NULL);
7463 xmlXPathCompileExpr(ctxt);
7464
7465 comp = ctxt->comp;
7466 ctxt->comp = NULL;
7467 xmlXPathFreeParserContext(ctxt);
7468 return(comp);
7469}
7470
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007471/**
7472 * xmlXPathCompiledEval:
7473 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007474 * @ctx: the XPath context
7475 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007476 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007477 *
7478 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7479 * the caller has to free the object.
7480 */
7481xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007482xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007483 xmlXPathParserContextPtr ctxt;
7484 xmlXPathObjectPtr res, tmp, init = NULL;
7485 int stack = 0;
7486
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007487 if ((comp == NULL) || (ctx == NULL))
7488 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007489 xmlXPathInit();
7490
7491 CHECK_CONTEXT(ctx)
7492
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007493 ctxt = xmlXPathCompParserContext(comp, ctx);
7494 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007495
7496 if (ctxt->value == NULL) {
7497 xmlGenericError(xmlGenericErrorContext,
7498 "xmlXPathEval: evaluation failed\n");
7499 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007500 } else {
7501 res = valuePop(ctxt);
7502 }
7503
7504 do {
7505 tmp = valuePop(ctxt);
7506 if (tmp != NULL) {
7507 if (tmp != init)
7508 stack++;
7509 xmlXPathFreeObject(tmp);
7510 }
7511 } while (tmp != NULL);
7512 if ((stack != 0) && (res != NULL)) {
7513 xmlGenericError(xmlGenericErrorContext,
7514 "xmlXPathEval: %d object left on the stack\n",
7515 stack);
7516 }
7517 if (ctxt->error != XPATH_EXPRESSION_OK) {
7518 xmlXPathFreeObject(res);
7519 res = NULL;
7520 }
7521
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007522
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007523 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007524 xmlXPathFreeParserContext(ctxt);
7525 return(res);
7526}
7527
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007528/**
7529 * xmlXPathEvalExpr:
7530 * @ctxt: the XPath Parser context
7531 *
7532 * Parse and evaluate an XPath expression in the given context,
7533 * then push the result on the context stack
7534 */
7535void
7536xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7537 xmlXPathCompileExpr(ctxt);
7538 xmlXPathRunEval(ctxt);
7539}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007540
7541/**
7542 * xmlXPathEval:
7543 * @str: the XPath expression
7544 * @ctx: the XPath context
7545 *
7546 * Evaluate the XPath Location Path in the given context.
7547 *
7548 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7549 * the caller has to free the object.
7550 */
7551xmlXPathObjectPtr
7552xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7553 xmlXPathParserContextPtr ctxt;
7554 xmlXPathObjectPtr res, tmp, init = NULL;
7555 int stack = 0;
7556
7557 xmlXPathInit();
7558
7559 CHECK_CONTEXT(ctx)
7560
7561 ctxt = xmlXPathNewParserContext(str, ctx);
7562 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007563
7564 if (ctxt->value == NULL) {
7565 xmlGenericError(xmlGenericErrorContext,
7566 "xmlXPathEval: evaluation failed\n");
7567 res = NULL;
7568 } else if (*ctxt->cur != 0) {
7569 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7570 res = NULL;
7571 } else {
7572 res = valuePop(ctxt);
7573 }
7574
7575 do {
7576 tmp = valuePop(ctxt);
7577 if (tmp != NULL) {
7578 if (tmp != init)
7579 stack++;
7580 xmlXPathFreeObject(tmp);
7581 }
7582 } while (tmp != NULL);
7583 if ((stack != 0) && (res != NULL)) {
7584 xmlGenericError(xmlGenericErrorContext,
7585 "xmlXPathEval: %d object left on the stack\n",
7586 stack);
7587 }
7588 if (ctxt->error != XPATH_EXPRESSION_OK) {
7589 xmlXPathFreeObject(res);
7590 res = NULL;
7591 }
7592
Owen Taylor3473f882001-02-23 17:55:21 +00007593 xmlXPathFreeParserContext(ctxt);
7594 return(res);
7595}
7596
7597/**
7598 * xmlXPathEvalExpression:
7599 * @str: the XPath expression
7600 * @ctxt: the XPath context
7601 *
7602 * Evaluate the XPath expression in the given context.
7603 *
7604 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7605 * the caller has to free the object.
7606 */
7607xmlXPathObjectPtr
7608xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7609 xmlXPathParserContextPtr pctxt;
7610 xmlXPathObjectPtr res, tmp;
7611 int stack = 0;
7612
7613 xmlXPathInit();
7614
7615 CHECK_CONTEXT(ctxt)
7616
7617 pctxt = xmlXPathNewParserContext(str, ctxt);
7618 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007619
7620 if (*pctxt->cur != 0) {
7621 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7622 res = NULL;
7623 } else {
7624 res = valuePop(pctxt);
7625 }
7626 do {
7627 tmp = valuePop(pctxt);
7628 if (tmp != NULL) {
7629 xmlXPathFreeObject(tmp);
7630 stack++;
7631 }
7632 } while (tmp != NULL);
7633 if ((stack != 0) && (res != NULL)) {
7634 xmlGenericError(xmlGenericErrorContext,
7635 "xmlXPathEvalExpression: %d object left on the stack\n",
7636 stack);
7637 }
7638 xmlXPathFreeParserContext(pctxt);
7639 return(res);
7640}
7641
7642/**
7643 * xmlXPathRegisterAllFunctions:
7644 * @ctxt: the XPath context
7645 *
7646 * Registers all default XPath functions in this context
7647 */
7648void
7649xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7650{
7651 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7652 xmlXPathBooleanFunction);
7653 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7654 xmlXPathCeilingFunction);
7655 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7656 xmlXPathCountFunction);
7657 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7658 xmlXPathConcatFunction);
7659 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7660 xmlXPathContainsFunction);
7661 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7662 xmlXPathIdFunction);
7663 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7664 xmlXPathFalseFunction);
7665 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7666 xmlXPathFloorFunction);
7667 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7668 xmlXPathLastFunction);
7669 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7670 xmlXPathLangFunction);
7671 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7672 xmlXPathLocalNameFunction);
7673 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7674 xmlXPathNotFunction);
7675 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7676 xmlXPathNameFunction);
7677 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7678 xmlXPathNamespaceURIFunction);
7679 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7680 xmlXPathNormalizeFunction);
7681 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7682 xmlXPathNumberFunction);
7683 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7684 xmlXPathPositionFunction);
7685 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7686 xmlXPathRoundFunction);
7687 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7688 xmlXPathStringFunction);
7689 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7690 xmlXPathStringLengthFunction);
7691 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7692 xmlXPathStartsWithFunction);
7693 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7694 xmlXPathSubstringFunction);
7695 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7696 xmlXPathSubstringBeforeFunction);
7697 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7698 xmlXPathSubstringAfterFunction);
7699 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7700 xmlXPathSumFunction);
7701 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7702 xmlXPathTrueFunction);
7703 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7704 xmlXPathTranslateFunction);
7705}
7706
7707#endif /* LIBXML_XPATH_ENABLED */