blob: 48ddc75b7c4304cb2aa37d156d21af2924a95c86 [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) {
3758 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3759 if (cur == NULL) {
3760 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3761 return(NULL);
3762 return((xmlNodePtr)ctxt->context->node->properties);
3763 }
3764 return((xmlNodePtr)cur->next);
3765}
3766
3767/************************************************************************
3768 * *
3769 * NodeTest Functions *
3770 * *
3771 ************************************************************************/
3772
Owen Taylor3473f882001-02-23 17:55:21 +00003773#define IS_FUNCTION 200
3774
Owen Taylor3473f882001-02-23 17:55:21 +00003775
3776/************************************************************************
3777 * *
3778 * Implicit tree core function library *
3779 * *
3780 ************************************************************************/
3781
3782/**
3783 * xmlXPathRoot:
3784 * @ctxt: the XPath Parser context
3785 *
3786 * Initialize the context to the root of the document
3787 */
3788void
3789xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3790 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3791 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3792}
3793
3794/************************************************************************
3795 * *
3796 * The explicit core function library *
3797 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3798 * *
3799 ************************************************************************/
3800
3801
3802/**
3803 * xmlXPathLastFunction:
3804 * @ctxt: the XPath Parser context
3805 * @nargs: the number of arguments
3806 *
3807 * Implement the last() XPath function
3808 * number last()
3809 * The last function returns the number of nodes in the context node list.
3810 */
3811void
3812xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3813 CHECK_ARITY(0);
3814 if (ctxt->context->contextSize >= 0) {
3815 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3816#ifdef DEBUG_EXPR
3817 xmlGenericError(xmlGenericErrorContext,
3818 "last() : %d\n", ctxt->context->contextSize);
3819#endif
3820 } else {
3821 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3822 }
3823}
3824
3825/**
3826 * xmlXPathPositionFunction:
3827 * @ctxt: the XPath Parser context
3828 * @nargs: the number of arguments
3829 *
3830 * Implement the position() XPath function
3831 * number position()
3832 * The position function returns the position of the context node in the
3833 * context node list. The first position is 1, and so the last positionr
3834 * will be equal to last().
3835 */
3836void
3837xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3838 CHECK_ARITY(0);
3839 if (ctxt->context->proximityPosition >= 0) {
3840 valuePush(ctxt,
3841 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3842#ifdef DEBUG_EXPR
3843 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3844 ctxt->context->proximityPosition);
3845#endif
3846 } else {
3847 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3848 }
3849}
3850
3851/**
3852 * xmlXPathCountFunction:
3853 * @ctxt: the XPath Parser context
3854 * @nargs: the number of arguments
3855 *
3856 * Implement the count() XPath function
3857 * number count(node-set)
3858 */
3859void
3860xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3861 xmlXPathObjectPtr cur;
3862
3863 CHECK_ARITY(1);
3864 if ((ctxt->value == NULL) ||
3865 ((ctxt->value->type != XPATH_NODESET) &&
3866 (ctxt->value->type != XPATH_XSLT_TREE)))
3867 XP_ERROR(XPATH_INVALID_TYPE);
3868 cur = valuePop(ctxt);
3869
Daniel Veillard911f49a2001-04-07 15:39:35 +00003870 if ((cur == NULL) || (cur->nodesetval == NULL))
3871 valuePush(ctxt, xmlXPathNewFloat((double) 0));
3872 else
3873 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Owen Taylor3473f882001-02-23 17:55:21 +00003874 xmlXPathFreeObject(cur);
3875}
3876
3877/**
3878 * xmlXPathIdFunction:
3879 * @ctxt: the XPath Parser context
3880 * @nargs: the number of arguments
3881 *
3882 * Implement the id() XPath function
3883 * node-set id(object)
3884 * The id function selects elements by their unique ID
3885 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3886 * then the result is the union of the result of applying id to the
3887 * string value of each of the nodes in the argument node-set. When the
3888 * argument to id is of any other type, the argument is converted to a
3889 * string as if by a call to the string function; the string is split
3890 * into a whitespace-separated list of tokens (whitespace is any sequence
3891 * of characters matching the production S); the result is a node-set
3892 * containing the elements in the same document as the context node that
3893 * have a unique ID equal to any of the tokens in the list.
3894 */
3895void
3896xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3897 const xmlChar *tokens;
3898 const xmlChar *cur;
3899 xmlChar *ID;
3900 xmlAttrPtr attr;
3901 xmlNodePtr elem = NULL;
3902 xmlXPathObjectPtr ret, obj;
3903
3904 CHECK_ARITY(1);
3905 obj = valuePop(ctxt);
3906 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3907 if (obj->type == XPATH_NODESET) {
3908 xmlXPathObjectPtr newobj;
3909 int i;
3910
3911 ret = xmlXPathNewNodeSet(NULL);
3912
Daniel Veillard911f49a2001-04-07 15:39:35 +00003913 if (obj->nodesetval != NULL) {
3914 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3915 valuePush(ctxt,
3916 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3917 xmlXPathStringFunction(ctxt, 1);
3918 xmlXPathIdFunction(ctxt, 1);
3919 newobj = valuePop(ctxt);
3920 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3921 newobj->nodesetval);
3922 xmlXPathFreeObject(newobj);
3923 }
Owen Taylor3473f882001-02-23 17:55:21 +00003924 }
3925
3926 xmlXPathFreeObject(obj);
3927 valuePush(ctxt, ret);
3928 return;
3929 }
3930 if (obj->type != XPATH_STRING) {
3931 valuePush(ctxt, obj);
3932 xmlXPathStringFunction(ctxt, 1);
3933 obj = valuePop(ctxt);
3934 if (obj->type != XPATH_STRING) {
3935 xmlXPathFreeObject(obj);
3936 return;
3937 }
3938 }
3939 tokens = obj->stringval;
3940
3941 ret = xmlXPathNewNodeSet(NULL);
3942 valuePush(ctxt, ret);
3943 if (tokens == NULL) {
3944 xmlXPathFreeObject(obj);
3945 return;
3946 }
3947
3948 cur = tokens;
3949
3950 while (IS_BLANK(*cur)) cur++;
3951 while (*cur != 0) {
3952 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3953 (*cur == '.') || (*cur == '-') ||
3954 (*cur == '_') || (*cur == ':') ||
3955 (IS_COMBINING(*cur)) ||
3956 (IS_EXTENDER(*cur)))
3957 cur++;
3958
3959 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3960
3961 ID = xmlStrndup(tokens, cur - tokens);
3962 attr = xmlGetID(ctxt->context->doc, ID);
3963 if (attr != NULL) {
3964 elem = attr->parent;
3965 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3966 }
3967 if (ID != NULL)
3968 xmlFree(ID);
3969
3970 while (IS_BLANK(*cur)) cur++;
3971 tokens = cur;
3972 }
3973 xmlXPathFreeObject(obj);
3974 return;
3975}
3976
3977/**
3978 * xmlXPathLocalNameFunction:
3979 * @ctxt: the XPath Parser context
3980 * @nargs: the number of arguments
3981 *
3982 * Implement the local-name() XPath function
3983 * string local-name(node-set?)
3984 * The local-name function returns a string containing the local part
3985 * of the name of the node in the argument node-set that is first in
3986 * document order. If the node-set is empty or the first node has no
3987 * name, an empty string is returned. If the argument is omitted it
3988 * defaults to the context node.
3989 */
3990void
3991xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3992 xmlXPathObjectPtr cur;
3993
3994 if (nargs == 0) {
3995 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3996 nargs = 1;
3997 }
3998
3999 CHECK_ARITY(1);
4000 if ((ctxt->value == NULL) ||
4001 ((ctxt->value->type != XPATH_NODESET) &&
4002 (ctxt->value->type != XPATH_XSLT_TREE)))
4003 XP_ERROR(XPATH_INVALID_TYPE);
4004 cur = valuePop(ctxt);
4005
Daniel Veillard911f49a2001-04-07 15:39:35 +00004006 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004007 valuePush(ctxt, xmlXPathNewCString(""));
4008 } else {
4009 int i = 0; /* Should be first in document order !!!!! */
4010 switch (cur->nodesetval->nodeTab[i]->type) {
4011 case XML_ELEMENT_NODE:
4012 case XML_ATTRIBUTE_NODE:
4013 case XML_PI_NODE:
4014 valuePush(ctxt,
4015 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
4016 break;
4017 case XML_NAMESPACE_DECL:
4018 valuePush(ctxt, xmlXPathNewString(
4019 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
4020 break;
4021 default:
4022 valuePush(ctxt, xmlXPathNewCString(""));
4023 }
4024 }
4025 xmlXPathFreeObject(cur);
4026}
4027
4028/**
4029 * xmlXPathNamespaceURIFunction:
4030 * @ctxt: the XPath Parser context
4031 * @nargs: the number of arguments
4032 *
4033 * Implement the namespace-uri() XPath function
4034 * string namespace-uri(node-set?)
4035 * The namespace-uri function returns a string containing the
4036 * namespace URI of the expanded name of the node in the argument
4037 * node-set that is first in document order. If the node-set is empty,
4038 * the first node has no name, or the expanded name has no namespace
4039 * URI, an empty string is returned. If the argument is omitted it
4040 * defaults to the context node.
4041 */
4042void
4043xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4044 xmlXPathObjectPtr cur;
4045
4046 if (nargs == 0) {
4047 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4048 nargs = 1;
4049 }
4050 CHECK_ARITY(1);
4051 if ((ctxt->value == NULL) ||
4052 ((ctxt->value->type != XPATH_NODESET) &&
4053 (ctxt->value->type != XPATH_XSLT_TREE)))
4054 XP_ERROR(XPATH_INVALID_TYPE);
4055 cur = valuePop(ctxt);
4056
Daniel Veillard911f49a2001-04-07 15:39:35 +00004057 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004058 valuePush(ctxt, xmlXPathNewCString(""));
4059 } else {
4060 int i = 0; /* Should be first in document order !!!!! */
4061 switch (cur->nodesetval->nodeTab[i]->type) {
4062 case XML_ELEMENT_NODE:
4063 case XML_ATTRIBUTE_NODE:
4064 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4065 valuePush(ctxt, xmlXPathNewCString(""));
4066 else
4067 valuePush(ctxt, xmlXPathNewString(
4068 cur->nodesetval->nodeTab[i]->ns->href));
4069 break;
4070 default:
4071 valuePush(ctxt, xmlXPathNewCString(""));
4072 }
4073 }
4074 xmlXPathFreeObject(cur);
4075}
4076
4077/**
4078 * xmlXPathNameFunction:
4079 * @ctxt: the XPath Parser context
4080 * @nargs: the number of arguments
4081 *
4082 * Implement the name() XPath function
4083 * string name(node-set?)
4084 * The name function returns a string containing a QName representing
4085 * the name of the node in the argument node-set that is first in documenti
4086 * order. The QName must represent the name with respect to the namespace
4087 * declarations in effect on the node whose name is being represented.
4088 * Typically, this will be the form in which the name occurred in the XML
4089 * source. This need not be the case if there are namespace declarations
4090 * in effect on the node that associate multiple prefixes with the same
4091 * namespace. However, an implementation may include information about
4092 * the original prefix in its representation of nodes; in this case, an
4093 * implementation can ensure that the returned string is always the same
4094 * as the QName used in the XML source. If the argument it omitted it
4095 * defaults to the context node.
4096 * Libxml keep the original prefix so the "real qualified name" used is
4097 * returned.
4098 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004099static void
Owen Taylor3473f882001-02-23 17:55:21 +00004100xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4101 xmlXPathObjectPtr cur;
4102
4103 if (nargs == 0) {
4104 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4105 nargs = 1;
4106 }
4107
4108 CHECK_ARITY(1);
4109 if ((ctxt->value == NULL) ||
4110 ((ctxt->value->type != XPATH_NODESET) &&
4111 (ctxt->value->type != XPATH_XSLT_TREE)))
4112 XP_ERROR(XPATH_INVALID_TYPE);
4113 cur = valuePop(ctxt);
4114
Daniel Veillard911f49a2001-04-07 15:39:35 +00004115 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004116 valuePush(ctxt, xmlXPathNewCString(""));
4117 } else {
4118 int i = 0; /* Should be first in document order !!!!! */
4119
4120 switch (cur->nodesetval->nodeTab[i]->type) {
4121 case XML_ELEMENT_NODE:
4122 case XML_ATTRIBUTE_NODE:
4123 if (cur->nodesetval->nodeTab[i]->ns == NULL)
4124 valuePush(ctxt, xmlXPathNewString(
4125 cur->nodesetval->nodeTab[i]->name));
4126
4127 else {
4128 char name[2000];
Owen Taylor3473f882001-02-23 17:55:21 +00004129 snprintf(name, sizeof(name), "%s:%s",
4130 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
4131 (char *) cur->nodesetval->nodeTab[i]->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004132 name[sizeof(name) - 1] = 0;
4133 valuePush(ctxt, xmlXPathNewCString(name));
4134 }
4135 break;
4136 default:
4137 valuePush(ctxt,
4138 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4139 xmlXPathLocalNameFunction(ctxt, 1);
4140 }
4141 }
4142 xmlXPathFreeObject(cur);
4143}
4144
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004145
4146/**
4147 * xmlXPathConvertString:
4148 * @val: an XPath object
4149 *
4150 * Converts an existing object to its string() equivalent
4151 *
4152 * Returns the new object, the old one is freed (or the operation
4153 * is done directly on @val)
4154 */
4155xmlXPathObjectPtr
4156xmlXPathConvertString(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004157 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004158
4159 if (val == NULL)
4160 return(xmlXPathNewCString(""));
4161 switch (val->type) {
4162 case XPATH_UNDEFINED:
4163#ifdef DEBUG_EXPR
4164 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4165#endif
4166 ret = xmlXPathNewCString("");
4167 break;
4168 case XPATH_XSLT_TREE:
4169 case XPATH_NODESET:
Daniel Veillard911f49a2001-04-07 15:39:35 +00004170 if ((val->nodesetval == NULL) || (val->nodesetval->nodeNr == 0)) {
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004171 ret = xmlXPathNewCString("");
4172 } else {
4173 xmlChar *res;
4174
4175 xmlXPathNodeSetSort(val->nodesetval);
4176 res = xmlNodeGetContent(val->nodesetval->nodeTab[0]);
4177 /* TODO: avoid allocating res to free it */
4178 ret = xmlXPathNewString(res);
4179 if (res != NULL)
4180 xmlFree(res);
4181 }
4182 break;
4183 case XPATH_STRING:
4184 return(val);
4185 case XPATH_BOOLEAN:
4186 if (val->boolval) ret = xmlXPathNewCString("true");
4187 else ret = xmlXPathNewCString("false");
4188 break;
4189 case XPATH_NUMBER: {
4190 char buf[100];
4191
4192 xmlXPathFormatNumber(val->floatval, buf, sizeof(buf));
4193 ret = xmlXPathNewCString(buf);
4194 break;
4195 }
4196 case XPATH_USERS:
4197 case XPATH_POINT:
4198 case XPATH_RANGE:
4199 case XPATH_LOCATIONSET:
4200 TODO
4201 ret = xmlXPathNewCString("");
4202 break;
4203 }
4204 xmlXPathFreeObject(val);
4205 return(ret);
4206}
4207
Owen Taylor3473f882001-02-23 17:55:21 +00004208/**
4209 * xmlXPathStringFunction:
4210 * @ctxt: the XPath Parser context
4211 * @nargs: the number of arguments
4212 *
4213 * Implement the string() XPath function
4214 * string string(object?)
4215 * he string function converts an object to a string as follows:
4216 * - A node-set is converted to a string by returning the value of
4217 * the node in the node-set that is first in document order.
4218 * If the node-set is empty, an empty string is returned.
4219 * - A number is converted to a string as follows
4220 * + NaN is converted to the string NaN
4221 * + positive zero is converted to the string 0
4222 * + negative zero is converted to the string 0
4223 * + positive infinity is converted to the string Infinity
4224 * + negative infinity is converted to the string -Infinity
4225 * + if the number is an integer, the number is represented in
4226 * decimal form as a Number with no decimal point and no leading
4227 * zeros, preceded by a minus sign (-) if the number is negative
4228 * + otherwise, the number is represented in decimal form as a
4229 * Number including a decimal point with at least one digit
4230 * before the decimal point and at least one digit after the
4231 * decimal point, preceded by a minus sign (-) if the number
4232 * is negative; there must be no leading zeros before the decimal
4233 * point apart possibly from the one required digit immediatelyi
4234 * before the decimal point; beyond the one required digit
4235 * after the decimal point there must be as many, but only as
4236 * many, more digits as are needed to uniquely distinguish the
4237 * number from all other IEEE 754 numeric values.
4238 * - The boolean false value is converted to the string false.
4239 * The boolean true value is converted to the string true.
4240 *
4241 * If the argument is omitted, it defaults to a node-set with the
4242 * context node as its only member.
4243 */
4244void
4245xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4246 xmlXPathObjectPtr cur;
4247
4248 if (nargs == 0) {
4249 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4250 nargs = 1;
4251 }
4252
4253 CHECK_ARITY(1);
4254 cur = valuePop(ctxt);
4255 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004256 cur = xmlXPathConvertString(cur);
4257 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004258}
4259
4260/**
4261 * xmlXPathStringLengthFunction:
4262 * @ctxt: the XPath Parser context
4263 * @nargs: the number of arguments
4264 *
4265 * Implement the string-length() XPath function
4266 * number string-length(string?)
4267 * The string-length returns the number of characters in the string
4268 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4269 * the context node converted to a string, in other words the value
4270 * of the context node.
4271 */
4272void
4273xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4274 xmlXPathObjectPtr cur;
4275
4276 if (nargs == 0) {
4277 if (ctxt->context->node == NULL) {
4278 valuePush(ctxt, xmlXPathNewFloat(0));
4279 } else {
4280 xmlChar *content;
4281
4282 content = xmlNodeGetContent(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004283 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00004284 xmlFree(content);
4285 }
4286 return;
4287 }
4288 CHECK_ARITY(1);
4289 CAST_TO_STRING;
4290 CHECK_TYPE(XPATH_STRING);
4291 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00004292 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00004293 xmlXPathFreeObject(cur);
4294}
4295
4296/**
4297 * xmlXPathConcatFunction:
4298 * @ctxt: the XPath Parser context
4299 * @nargs: the number of arguments
4300 *
4301 * Implement the concat() XPath function
4302 * string concat(string, string, string*)
4303 * The concat function returns the concatenation of its arguments.
4304 */
4305void
4306xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4307 xmlXPathObjectPtr cur, newobj;
4308 xmlChar *tmp;
4309
4310 if (nargs < 2) {
4311 CHECK_ARITY(2);
4312 }
4313
4314 CAST_TO_STRING;
4315 cur = valuePop(ctxt);
4316 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4317 xmlXPathFreeObject(cur);
4318 return;
4319 }
4320 nargs--;
4321
4322 while (nargs > 0) {
4323 CAST_TO_STRING;
4324 newobj = valuePop(ctxt);
4325 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4326 xmlXPathFreeObject(newobj);
4327 xmlXPathFreeObject(cur);
4328 XP_ERROR(XPATH_INVALID_TYPE);
4329 }
4330 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4331 newobj->stringval = cur->stringval;
4332 cur->stringval = tmp;
4333
4334 xmlXPathFreeObject(newobj);
4335 nargs--;
4336 }
4337 valuePush(ctxt, cur);
4338}
4339
4340/**
4341 * xmlXPathContainsFunction:
4342 * @ctxt: the XPath Parser context
4343 * @nargs: the number of arguments
4344 *
4345 * Implement the contains() XPath function
4346 * boolean contains(string, string)
4347 * The contains function returns true if the first argument string
4348 * contains the second argument string, and otherwise returns false.
4349 */
4350void
4351xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4352 xmlXPathObjectPtr hay, needle;
4353
4354 CHECK_ARITY(2);
4355 CAST_TO_STRING;
4356 CHECK_TYPE(XPATH_STRING);
4357 needle = valuePop(ctxt);
4358 CAST_TO_STRING;
4359 hay = valuePop(ctxt);
4360 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4361 xmlXPathFreeObject(hay);
4362 xmlXPathFreeObject(needle);
4363 XP_ERROR(XPATH_INVALID_TYPE);
4364 }
4365 if (xmlStrstr(hay->stringval, needle->stringval))
4366 valuePush(ctxt, xmlXPathNewBoolean(1));
4367 else
4368 valuePush(ctxt, xmlXPathNewBoolean(0));
4369 xmlXPathFreeObject(hay);
4370 xmlXPathFreeObject(needle);
4371}
4372
4373/**
4374 * xmlXPathStartsWithFunction:
4375 * @ctxt: the XPath Parser context
4376 * @nargs: the number of arguments
4377 *
4378 * Implement the starts-with() XPath function
4379 * boolean starts-with(string, string)
4380 * The starts-with function returns true if the first argument string
4381 * starts with the second argument string, and otherwise returns false.
4382 */
4383void
4384xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4385 xmlXPathObjectPtr hay, needle;
4386 int n;
4387
4388 CHECK_ARITY(2);
4389 CAST_TO_STRING;
4390 CHECK_TYPE(XPATH_STRING);
4391 needle = valuePop(ctxt);
4392 CAST_TO_STRING;
4393 hay = valuePop(ctxt);
4394 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4395 xmlXPathFreeObject(hay);
4396 xmlXPathFreeObject(needle);
4397 XP_ERROR(XPATH_INVALID_TYPE);
4398 }
4399 n = xmlStrlen(needle->stringval);
4400 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4401 valuePush(ctxt, xmlXPathNewBoolean(0));
4402 else
4403 valuePush(ctxt, xmlXPathNewBoolean(1));
4404 xmlXPathFreeObject(hay);
4405 xmlXPathFreeObject(needle);
4406}
4407
4408/**
4409 * xmlXPathSubstringFunction:
4410 * @ctxt: the XPath Parser context
4411 * @nargs: the number of arguments
4412 *
4413 * Implement the substring() XPath function
4414 * string substring(string, number, number?)
4415 * The substring function returns the substring of the first argument
4416 * starting at the position specified in the second argument with
4417 * length specified in the third argument. For example,
4418 * substring("12345",2,3) returns "234". If the third argument is not
4419 * specified, it returns the substring starting at the position specified
4420 * in the second argument and continuing to the end of the string. For
4421 * example, substring("12345",2) returns "2345". More precisely, each
4422 * character in the string (see [3.6 Strings]) is considered to have a
4423 * numeric position: the position of the first character is 1, the position
4424 * of the second character is 2 and so on. The returned substring contains
4425 * those characters for which the position of the character is greater than
4426 * or equal to the second argument and, if the third argument is specified,
4427 * less than the sum of the second and third arguments; the comparisons
4428 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4429 * - substring("12345", 1.5, 2.6) returns "234"
4430 * - substring("12345", 0, 3) returns "12"
4431 * - substring("12345", 0 div 0, 3) returns ""
4432 * - substring("12345", 1, 0 div 0) returns ""
4433 * - substring("12345", -42, 1 div 0) returns "12345"
4434 * - substring("12345", -1 div 0, 1 div 0) returns ""
4435 */
4436void
4437xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4438 xmlXPathObjectPtr str, start, len;
4439 double le, in;
4440 int i, l;
4441 xmlChar *ret;
4442
4443 /*
Daniel Veillarde043ee12001-04-16 14:08:07 +00004444 * TODO: need to be converted to UTF8 strings
Owen Taylor3473f882001-02-23 17:55:21 +00004445 */
4446 if (nargs < 2) {
4447 CHECK_ARITY(2);
4448 }
4449 if (nargs > 3) {
4450 CHECK_ARITY(3);
4451 }
4452 if (nargs == 3) {
4453 CAST_TO_NUMBER;
4454 CHECK_TYPE(XPATH_NUMBER);
4455 len = valuePop(ctxt);
4456 le = len->floatval;
4457 xmlXPathFreeObject(len);
4458 } else {
4459 le = 2000000000;
4460 }
4461 CAST_TO_NUMBER;
4462 CHECK_TYPE(XPATH_NUMBER);
4463 start = valuePop(ctxt);
4464 in = start->floatval;
4465 xmlXPathFreeObject(start);
4466 CAST_TO_STRING;
4467 CHECK_TYPE(XPATH_STRING);
4468 str = valuePop(ctxt);
4469 le += in;
4470
4471 /* integer index of the first char */
4472 i = (int) in;
4473 if (((double)i) != in) i++;
4474
4475 /* integer index of the last char */
4476 l = (int) le;
4477 if (((double)l) != le) l++;
4478
4479 /* back to a zero based len */
4480 i--;
4481 l--;
4482
4483 /* check against the string len */
4484 if (l > 1024) {
4485 l = xmlStrlen(str->stringval);
4486 }
4487 if (i < 0) {
4488 i = 0;
4489 }
4490
4491 /* number of chars to copy */
4492 l -= i;
4493
4494 ret = xmlStrsub(str->stringval, i, l);
4495 if (ret == NULL)
4496 valuePush(ctxt, xmlXPathNewCString(""));
4497 else {
4498 valuePush(ctxt, xmlXPathNewString(ret));
4499 xmlFree(ret);
4500 }
4501 xmlXPathFreeObject(str);
4502}
4503
4504/**
4505 * xmlXPathSubstringBeforeFunction:
4506 * @ctxt: the XPath Parser context
4507 * @nargs: the number of arguments
4508 *
4509 * Implement the substring-before() XPath function
4510 * string substring-before(string, string)
4511 * The substring-before function returns the substring of the first
4512 * argument string that precedes the first occurrence of the second
4513 * argument string in the first argument string, or the empty string
4514 * if the first argument string does not contain the second argument
4515 * string. For example, substring-before("1999/04/01","/") returns 1999.
4516 */
4517void
4518xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4519 xmlXPathObjectPtr str;
4520 xmlXPathObjectPtr find;
4521 xmlBufferPtr target;
4522 const xmlChar *point;
4523 int offset;
4524
4525 CHECK_ARITY(2);
4526 CAST_TO_STRING;
4527 find = valuePop(ctxt);
4528 CAST_TO_STRING;
4529 str = valuePop(ctxt);
4530
4531 target = xmlBufferCreate();
4532 if (target) {
4533 point = xmlStrstr(str->stringval, find->stringval);
4534 if (point) {
4535 offset = (int)(point - str->stringval);
4536 xmlBufferAdd(target, str->stringval, offset);
4537 }
4538 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4539 xmlBufferFree(target);
4540 }
4541
4542 xmlXPathFreeObject(str);
4543 xmlXPathFreeObject(find);
4544}
4545
4546/**
4547 * xmlXPathSubstringAfterFunction:
4548 * @ctxt: the XPath Parser context
4549 * @nargs: the number of arguments
4550 *
4551 * Implement the substring-after() XPath function
4552 * string substring-after(string, string)
4553 * The substring-after function returns the substring of the first
4554 * argument string that follows the first occurrence of the second
4555 * argument string in the first argument string, or the empty stringi
4556 * if the first argument string does not contain the second argument
4557 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4558 * and substring-after("1999/04/01","19") returns 99/04/01.
4559 */
4560void
4561xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4562 xmlXPathObjectPtr str;
4563 xmlXPathObjectPtr find;
4564 xmlBufferPtr target;
4565 const xmlChar *point;
4566 int offset;
4567
4568 CHECK_ARITY(2);
4569 CAST_TO_STRING;
4570 find = valuePop(ctxt);
4571 CAST_TO_STRING;
4572 str = valuePop(ctxt);
4573
4574 target = xmlBufferCreate();
4575 if (target) {
4576 point = xmlStrstr(str->stringval, find->stringval);
4577 if (point) {
4578 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4579 xmlBufferAdd(target, &str->stringval[offset],
4580 xmlStrlen(str->stringval) - offset);
4581 }
4582 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4583 xmlBufferFree(target);
4584 }
4585
4586 xmlXPathFreeObject(str);
4587 xmlXPathFreeObject(find);
4588}
4589
4590/**
4591 * xmlXPathNormalizeFunction:
4592 * @ctxt: the XPath Parser context
4593 * @nargs: the number of arguments
4594 *
4595 * Implement the normalize-space() XPath function
4596 * string normalize-space(string?)
4597 * The normalize-space function returns the argument string with white
4598 * space normalized by stripping leading and trailing whitespace
4599 * and replacing sequences of whitespace characters by a single
4600 * space. Whitespace characters are the same allowed by the S production
4601 * in XML. If the argument is omitted, it defaults to the context
4602 * node converted to a string, in other words the value of the context node.
4603 */
4604void
4605xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4606 xmlXPathObjectPtr obj = NULL;
4607 xmlChar *source = NULL;
4608 xmlBufferPtr target;
4609 xmlChar blank;
4610
4611 if (nargs == 0) {
4612 /* Use current context node */
4613 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4614 xmlXPathStringFunction(ctxt, 1);
4615 nargs = 1;
4616 }
4617
4618 CHECK_ARITY(1);
4619 CAST_TO_STRING;
4620 CHECK_TYPE(XPATH_STRING);
4621 obj = valuePop(ctxt);
4622 source = obj->stringval;
4623
4624 target = xmlBufferCreate();
4625 if (target && source) {
4626
4627 /* Skip leading whitespaces */
4628 while (IS_BLANK(*source))
4629 source++;
4630
4631 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4632 blank = 0;
4633 while (*source) {
4634 if (IS_BLANK(*source)) {
4635 blank = *source;
4636 } else {
4637 if (blank) {
4638 xmlBufferAdd(target, &blank, 1);
4639 blank = 0;
4640 }
4641 xmlBufferAdd(target, source, 1);
4642 }
4643 source++;
4644 }
4645
4646 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4647 xmlBufferFree(target);
4648 }
4649 xmlXPathFreeObject(obj);
4650}
4651
4652/**
4653 * xmlXPathTranslateFunction:
4654 * @ctxt: the XPath Parser context
4655 * @nargs: the number of arguments
4656 *
4657 * Implement the translate() XPath function
4658 * string translate(string, string, string)
4659 * The translate function returns the first argument string with
4660 * occurrences of characters in the second argument string replaced
4661 * by the character at the corresponding position in the third argument
4662 * string. For example, translate("bar","abc","ABC") returns the string
4663 * BAr. If there is a character in the second argument string with no
4664 * character at a corresponding position in the third argument string
4665 * (because the second argument string is longer than the third argument
4666 * string), then occurrences of that character in the first argument
4667 * string are removed. For example, translate("--aaa--","abc-","ABC")
4668 * returns "AAA". If a character occurs more than once in second
4669 * argument string, then the first occurrence determines the replacement
4670 * character. If the third argument string is longer than the second
4671 * argument string, then excess characters are ignored.
4672 */
4673void
4674xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00004675 xmlXPathObjectPtr str;
4676 xmlXPathObjectPtr from;
4677 xmlXPathObjectPtr to;
4678 xmlBufferPtr target;
4679 int i, offset, max;
4680 xmlChar ch;
4681 const xmlChar *point;
Owen Taylor3473f882001-02-23 17:55:21 +00004682
Daniel Veillarde043ee12001-04-16 14:08:07 +00004683 /*
4684 * TODO: need to be converted to UTF8 strings
4685 */
4686 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00004687
Daniel Veillarde043ee12001-04-16 14:08:07 +00004688 CAST_TO_STRING;
4689 to = valuePop(ctxt);
4690 CAST_TO_STRING;
4691 from = valuePop(ctxt);
4692 CAST_TO_STRING;
4693 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00004694
Daniel Veillarde043ee12001-04-16 14:08:07 +00004695 target = xmlBufferCreate();
4696 if (target) {
4697 max = xmlStrlen(to->stringval);
4698 for (i = 0; (ch = str->stringval[i]); i++) {
4699 point = xmlStrchr(from->stringval, ch);
4700 if (point) {
4701 offset = (int)(point - from->stringval);
4702 if (offset < max)
4703 xmlBufferAdd(target, &to->stringval[offset], 1);
4704 } else
4705 xmlBufferAdd(target, &ch, 1);
4706 }
Owen Taylor3473f882001-02-23 17:55:21 +00004707 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00004708 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4709 xmlBufferFree(target);
4710 xmlXPathFreeObject(str);
4711 xmlXPathFreeObject(from);
4712 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00004713}
4714
4715/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004716 * xmlXPathConvertBoolean:
4717 * @val: an XPath object
4718 *
4719 * Converts an existing object to its boolean() equivalent
4720 *
4721 * Returns the new object, the old one is freed (or the operation
4722 * is done directly on @val)
4723 */
4724xmlXPathObjectPtr
4725xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
4726 int res = 0;
4727
4728 if (val == NULL)
4729 return(NULL);
4730 switch (val->type) {
4731 case XPATH_NODESET:
4732 case XPATH_XSLT_TREE:
4733 if ((val->nodesetval == NULL) ||
4734 (val->nodesetval->nodeNr == 0)) res = 0;
4735 else
4736 res = 1;
4737 break;
4738 case XPATH_STRING:
4739 if ((val->stringval == NULL) ||
4740 (val->stringval[0] == 0)) res = 0;
4741 else
4742 res = 1;
4743 break;
4744 case XPATH_BOOLEAN:
4745 return(val);
4746 case XPATH_NUMBER:
4747 if (val->floatval) res = 1;
4748 break;
4749 default:
4750 STRANGE
4751 }
4752 xmlXPathFreeObject(val);
4753 return(xmlXPathNewBoolean(res));
4754}
4755
4756/**
Owen Taylor3473f882001-02-23 17:55:21 +00004757 * xmlXPathBooleanFunction:
4758 * @ctxt: the XPath Parser context
4759 * @nargs: the number of arguments
4760 *
4761 * Implement the boolean() XPath function
4762 * boolean boolean(object)
4763 * he boolean function converts its argument to a boolean as follows:
4764 * - a number is true if and only if it is neither positive or
4765 * negative zero nor NaN
4766 * - a node-set is true if and only if it is non-empty
4767 * - a string is true if and only if its length is non-zero
4768 */
4769void
4770xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4771 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00004772
4773 CHECK_ARITY(1);
4774 cur = valuePop(ctxt);
4775 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004776 cur = xmlXPathConvertBoolean(cur);
4777 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004778}
4779
4780/**
4781 * xmlXPathNotFunction:
4782 * @ctxt: the XPath Parser context
4783 * @nargs: the number of arguments
4784 *
4785 * Implement the not() XPath function
4786 * boolean not(boolean)
4787 * The not function returns true if its argument is false,
4788 * and false otherwise.
4789 */
4790void
4791xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4792 CHECK_ARITY(1);
4793 CAST_TO_BOOLEAN;
4794 CHECK_TYPE(XPATH_BOOLEAN);
4795 ctxt->value->boolval = ! ctxt->value->boolval;
4796}
4797
4798/**
4799 * xmlXPathTrueFunction:
4800 * @ctxt: the XPath Parser context
4801 * @nargs: the number of arguments
4802 *
4803 * Implement the true() XPath function
4804 * boolean true()
4805 */
4806void
4807xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4808 CHECK_ARITY(0);
4809 valuePush(ctxt, xmlXPathNewBoolean(1));
4810}
4811
4812/**
4813 * xmlXPathFalseFunction:
4814 * @ctxt: the XPath Parser context
4815 * @nargs: the number of arguments
4816 *
4817 * Implement the false() XPath function
4818 * boolean false()
4819 */
4820void
4821xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4822 CHECK_ARITY(0);
4823 valuePush(ctxt, xmlXPathNewBoolean(0));
4824}
4825
4826/**
4827 * xmlXPathLangFunction:
4828 * @ctxt: the XPath Parser context
4829 * @nargs: the number of arguments
4830 *
4831 * Implement the lang() XPath function
4832 * boolean lang(string)
4833 * The lang function returns true or false depending on whether the
4834 * language of the context node as specified by xml:lang attributes
4835 * is the same as or is a sublanguage of the language specified by
4836 * the argument string. The language of the context node is determined
4837 * by the value of the xml:lang attribute on the context node, or, if
4838 * the context node has no xml:lang attribute, by the value of the
4839 * xml:lang attribute on the nearest ancestor of the context node that
4840 * has an xml:lang attribute. If there is no such attribute, then lang
4841 * returns false. If there is such an attribute, then lang returns
4842 * true if the attribute value is equal to the argument ignoring case,
4843 * or if there is some suffix starting with - such that the attribute
4844 * value is equal to the argument ignoring that suffix of the attribute
4845 * value and ignoring case.
4846 */
4847void
4848xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4849 xmlXPathObjectPtr val;
4850 const xmlChar *theLang;
4851 const xmlChar *lang;
4852 int ret = 0;
4853 int i;
4854
4855 CHECK_ARITY(1);
4856 CAST_TO_STRING;
4857 CHECK_TYPE(XPATH_STRING);
4858 val = valuePop(ctxt);
4859 lang = val->stringval;
4860 theLang = xmlNodeGetLang(ctxt->context->node);
4861 if ((theLang != NULL) && (lang != NULL)) {
4862 for (i = 0;lang[i] != 0;i++)
4863 if (toupper(lang[i]) != toupper(theLang[i]))
4864 goto not_equal;
4865 ret = 1;
4866 }
4867not_equal:
4868 xmlXPathFreeObject(val);
4869 valuePush(ctxt, xmlXPathNewBoolean(ret));
4870}
4871
4872/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004873 * xmlXPathConvertNumber:
4874 * @val: an XPath object
4875 *
4876 * Converts an existing object to its number() equivalent
4877 *
4878 * Returns the new object, the old one is freed (or the operation
4879 * is done directly on @val)
4880 */
4881xmlXPathObjectPtr
4882xmlXPathConvertNumber(xmlXPathObjectPtr val) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004883 xmlXPathObjectPtr ret = NULL;
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004884 double res;
4885
4886 if (val == NULL)
4887 return(xmlXPathNewFloat(0.0));
4888 switch (val->type) {
4889 case XPATH_UNDEFINED:
4890#ifdef DEBUG_EXPR
4891 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4892#endif
4893 ret = xmlXPathNewFloat(0.0);
4894 break;
4895 case XPATH_XSLT_TREE:
4896 case XPATH_NODESET:
4897 val = xmlXPathConvertString(val);
4898 /* no break on purpose */
4899 case XPATH_STRING:
4900 res = xmlXPathStringEvalNumber(val->stringval);
4901 ret = xmlXPathNewFloat(res);
4902 break;
4903 case XPATH_BOOLEAN:
4904 if (val->boolval) ret = xmlXPathNewFloat(1.0);
4905 else ret = xmlXPathNewFloat(0.0);
4906 break;
4907 case XPATH_NUMBER:
4908 return(val);
4909 case XPATH_USERS:
4910 case XPATH_POINT:
4911 case XPATH_RANGE:
4912 case XPATH_LOCATIONSET:
4913 TODO
4914 ret = xmlXPathNewFloat(0.0);
4915 break;
4916 }
4917 xmlXPathFreeObject(val);
4918 return(ret);
4919}
4920
4921/**
Owen Taylor3473f882001-02-23 17:55:21 +00004922 * xmlXPathNumberFunction:
4923 * @ctxt: the XPath Parser context
4924 * @nargs: the number of arguments
4925 *
4926 * Implement the number() XPath function
4927 * number number(object?)
4928 */
4929void
4930xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4931 xmlXPathObjectPtr cur;
4932 double res;
4933
4934 if (nargs == 0) {
4935 if (ctxt->context->node == NULL) {
4936 valuePush(ctxt, xmlXPathNewFloat(0.0));
4937 } else {
4938 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4939
4940 res = xmlXPathStringEvalNumber(content);
4941 valuePush(ctxt, xmlXPathNewFloat(res));
4942 xmlFree(content);
4943 }
4944 return;
4945 }
4946
4947 CHECK_ARITY(1);
4948 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00004949 cur = xmlXPathConvertNumber(cur);
4950 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004951}
4952
4953/**
4954 * xmlXPathSumFunction:
4955 * @ctxt: the XPath Parser context
4956 * @nargs: the number of arguments
4957 *
4958 * Implement the sum() XPath function
4959 * number sum(node-set)
4960 * The sum function returns the sum of the values of the nodes in
4961 * the argument node-set.
4962 */
4963void
4964xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4965 xmlXPathObjectPtr cur;
4966 int i;
4967
4968 CHECK_ARITY(1);
4969 if ((ctxt->value == NULL) ||
4970 ((ctxt->value->type != XPATH_NODESET) &&
4971 (ctxt->value->type != XPATH_XSLT_TREE)))
4972 XP_ERROR(XPATH_INVALID_TYPE);
4973 cur = valuePop(ctxt);
4974
Daniel Veillardd8df6c02001-04-05 16:54:14 +00004975 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004976 valuePush(ctxt, xmlXPathNewFloat(0.0));
4977 } else {
4978 valuePush(ctxt,
4979 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4980 xmlXPathNumberFunction(ctxt, 1);
4981 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4982 valuePush(ctxt,
4983 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4984 xmlXPathAddValues(ctxt);
4985 }
4986 }
4987 xmlXPathFreeObject(cur);
4988}
4989
4990/**
4991 * xmlXPathFloorFunction:
4992 * @ctxt: the XPath Parser context
4993 * @nargs: the number of arguments
4994 *
4995 * Implement the floor() XPath function
4996 * number floor(number)
4997 * The floor function returns the largest (closest to positive infinity)
4998 * number that is not greater than the argument and that is an integer.
4999 */
5000void
5001xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5002 CHECK_ARITY(1);
5003 CAST_TO_NUMBER;
5004 CHECK_TYPE(XPATH_NUMBER);
5005#if 0
5006 ctxt->value->floatval = floor(ctxt->value->floatval);
5007#else
5008 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
5009 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
5010#endif
5011}
5012
5013/**
5014 * xmlXPathCeilingFunction:
5015 * @ctxt: the XPath Parser context
5016 * @nargs: the number of arguments
5017 *
5018 * Implement the ceiling() XPath function
5019 * number ceiling(number)
5020 * The ceiling function returns the smallest (closest to negative infinity)
5021 * number that is not less than the argument and that is an integer.
5022 */
5023void
5024xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5025 double f;
5026
5027 CHECK_ARITY(1);
5028 CAST_TO_NUMBER;
5029 CHECK_TYPE(XPATH_NUMBER);
5030
5031#if 0
5032 ctxt->value->floatval = ceil(ctxt->value->floatval);
5033#else
5034 f = (double)((int) ctxt->value->floatval);
5035 if (f != ctxt->value->floatval)
5036 ctxt->value->floatval = f + 1;
5037#endif
5038}
5039
5040/**
5041 * xmlXPathRoundFunction:
5042 * @ctxt: the XPath Parser context
5043 * @nargs: the number of arguments
5044 *
5045 * Implement the round() XPath function
5046 * number round(number)
5047 * The round function returns the number that is closest to the
5048 * argument and that is an integer. If there are two such numbers,
5049 * then the one that is even is returned.
5050 */
5051void
5052xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5053 double f;
5054
5055 CHECK_ARITY(1);
5056 CAST_TO_NUMBER;
5057 CHECK_TYPE(XPATH_NUMBER);
5058
5059 if ((ctxt->value->floatval == xmlXPathNAN) ||
5060 (ctxt->value->floatval == xmlXPathPINF) ||
5061 (ctxt->value->floatval == xmlXPathNINF) ||
5062 (ctxt->value->floatval == 0.0))
5063 return;
5064
5065#if 0
5066 f = floor(ctxt->value->floatval);
5067#else
5068 f = (double)((int) ctxt->value->floatval);
5069#endif
5070 if (ctxt->value->floatval < f + 0.5)
5071 ctxt->value->floatval = f;
5072 else
5073 ctxt->value->floatval = f + 1;
5074}
5075
5076/************************************************************************
5077 * *
5078 * The Parser *
5079 * *
5080 ************************************************************************/
5081
5082/*
5083 * a couple of forward declarations since we use a recursive call based
5084 * implementation.
5085 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005086static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005087static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005088static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005089#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005090static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
5091#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00005092#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005093static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005094#endif
5095
5096/**
5097 * xmlXPathParseNCName:
5098 * @ctxt: the XPath Parser context
5099 *
5100 * parse an XML namespace non qualified name.
5101 *
5102 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
5103 *
5104 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
5105 * CombiningChar | Extender
5106 *
5107 * Returns the namespace name or NULL
5108 */
5109
5110xmlChar *
5111xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
5112 const xmlChar *q;
5113 xmlChar *ret = NULL;
5114
5115 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5116 q = NEXT;
5117
5118 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5119 (CUR == '.') || (CUR == '-') ||
5120 (CUR == '_') ||
5121 (IS_COMBINING(CUR)) ||
5122 (IS_EXTENDER(CUR)))
5123 NEXT;
5124
5125 ret = xmlStrndup(q, CUR_PTR - q);
5126
5127 return(ret);
5128}
5129
5130/**
5131 * xmlXPathParseQName:
5132 * @ctxt: the XPath Parser context
5133 * @prefix: a xmlChar **
5134 *
5135 * parse an XML qualified name
5136 *
5137 * [NS 5] QName ::= (Prefix ':')? LocalPart
5138 *
5139 * [NS 6] Prefix ::= NCName
5140 *
5141 * [NS 7] LocalPart ::= NCName
5142 *
5143 * Returns the function returns the local part, and prefix is updated
5144 * to get the Prefix if any.
5145 */
5146
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005147static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005148xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
5149 xmlChar *ret = NULL;
5150
5151 *prefix = NULL;
5152 ret = xmlXPathParseNCName(ctxt);
5153 if (CUR == ':') {
5154 *prefix = ret;
5155 NEXT;
5156 ret = xmlXPathParseNCName(ctxt);
5157 }
5158 return(ret);
5159}
5160
5161/**
5162 * xmlXPathParseName:
5163 * @ctxt: the XPath Parser context
5164 *
5165 * parse an XML name
5166 *
5167 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5168 * CombiningChar | Extender
5169 *
5170 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5171 *
5172 * Returns the namespace name or NULL
5173 */
5174
5175xmlChar *
5176xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
5177 const xmlChar *q;
5178 xmlChar *ret = NULL;
5179
5180 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
5181 q = NEXT;
5182
5183 /* TODO Make this UTF8 compliant !!! */
5184 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
5185 (CUR == '.') || (CUR == '-') ||
5186 (CUR == '_') || (CUR == ':') ||
5187 (IS_COMBINING(CUR)) ||
5188 (IS_EXTENDER(CUR)))
5189 NEXT;
5190
5191 ret = xmlStrndup(q, CUR_PTR - q);
5192
5193 return(ret);
5194}
5195
5196/**
5197 * xmlXPathStringEvalNumber:
5198 * @str: A string to scan
5199 *
5200 * [30] Number ::= Digits ('.' Digits?)?
5201 * | '.' Digits
5202 * [31] Digits ::= [0-9]+
5203 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005204 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00005205 * In complement of the Number expression, this function also handles
5206 * negative values : '-' Number.
5207 *
5208 * Returns the double value.
5209 */
5210double
5211xmlXPathStringEvalNumber(const xmlChar *str) {
5212 const xmlChar *cur = str;
5213 double ret = 0.0;
5214 double mult = 1;
5215 int ok = 0;
5216 int isneg = 0;
5217
5218 while (IS_BLANK(*cur)) cur++;
5219 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5220 return(xmlXPathNAN);
5221 }
5222 if (*cur == '-') {
5223 isneg = 1;
5224 cur++;
5225 }
5226 while ((*cur >= '0') && (*cur <= '9')) {
5227 ret = ret * 10 + (*cur - '0');
5228 ok = 1;
5229 cur++;
5230 }
5231 if (*cur == '.') {
5232 cur++;
5233 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5234 return(xmlXPathNAN);
5235 }
5236 while ((*cur >= '0') && (*cur <= '9')) {
5237 mult /= 10;
5238 ret = ret + (*cur - '0') * mult;
5239 cur++;
5240 }
5241 }
5242 while (IS_BLANK(*cur)) cur++;
5243 if (*cur != 0) return(xmlXPathNAN);
5244 if (isneg) ret = -ret;
5245 return(ret);
5246}
5247
5248/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005249 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00005250 * @ctxt: the XPath Parser context
5251 *
5252 * [30] Number ::= Digits ('.' Digits?)?
5253 * | '.' Digits
5254 * [31] Digits ::= [0-9]+
5255 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005256 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00005257 *
5258 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005259static void
5260xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005261 double ret = 0.0;
5262 double mult = 1;
5263 int ok = 0;
5264
5265 CHECK_ERROR;
5266 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5267 XP_ERROR(XPATH_NUMBER_ERROR);
5268 }
5269 while ((CUR >= '0') && (CUR <= '9')) {
5270 ret = ret * 10 + (CUR - '0');
5271 ok = 1;
5272 NEXT;
5273 }
5274 if (CUR == '.') {
5275 NEXT;
5276 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5277 XP_ERROR(XPATH_NUMBER_ERROR);
5278 }
5279 while ((CUR >= '0') && (CUR <= '9')) {
5280 mult /= 10;
5281 ret = ret + (CUR - '0') * mult;
5282 NEXT;
5283 }
5284 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005285 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
5286 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005287}
5288
5289/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005290 * xmlXPathParseLiteral:
5291 * @ctxt: the XPath Parser context
5292 *
5293 * Parse a Literal
5294 *
5295 * [29] Literal ::= '"' [^"]* '"'
5296 * | "'" [^']* "'"
5297 *
5298 * Returns the value found or NULL in case of error
5299 */
5300static xmlChar *
5301xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
5302 const xmlChar *q;
5303 xmlChar *ret = NULL;
5304
5305 if (CUR == '"') {
5306 NEXT;
5307 q = CUR_PTR;
5308 while ((IS_CHAR(CUR)) && (CUR != '"'))
5309 NEXT;
5310 if (!IS_CHAR(CUR)) {
5311 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5312 } else {
5313 ret = xmlStrndup(q, CUR_PTR - q);
5314 NEXT;
5315 }
5316 } else if (CUR == '\'') {
5317 NEXT;
5318 q = CUR_PTR;
5319 while ((IS_CHAR(CUR)) && (CUR != '\''))
5320 NEXT;
5321 if (!IS_CHAR(CUR)) {
5322 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
5323 } else {
5324 ret = xmlStrndup(q, CUR_PTR - q);
5325 NEXT;
5326 }
5327 } else {
5328 XP_ERROR0(XPATH_START_LITERAL_ERROR);
5329 }
5330 return(ret);
5331}
5332
5333/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005334 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00005335 * @ctxt: the XPath Parser context
5336 *
5337 * Parse a Literal and push it on the stack.
5338 *
5339 * [29] Literal ::= '"' [^"]* '"'
5340 * | "'" [^']* "'"
5341 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005342 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00005343 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005344static void
5345xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005346 const xmlChar *q;
5347 xmlChar *ret = NULL;
5348
5349 if (CUR == '"') {
5350 NEXT;
5351 q = CUR_PTR;
5352 while ((IS_CHAR(CUR)) && (CUR != '"'))
5353 NEXT;
5354 if (!IS_CHAR(CUR)) {
5355 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5356 } else {
5357 ret = xmlStrndup(q, CUR_PTR - q);
5358 NEXT;
5359 }
5360 } else if (CUR == '\'') {
5361 NEXT;
5362 q = CUR_PTR;
5363 while ((IS_CHAR(CUR)) && (CUR != '\''))
5364 NEXT;
5365 if (!IS_CHAR(CUR)) {
5366 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5367 } else {
5368 ret = xmlStrndup(q, CUR_PTR - q);
5369 NEXT;
5370 }
5371 } else {
5372 XP_ERROR(XPATH_START_LITERAL_ERROR);
5373 }
5374 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005375 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
5376 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005377 xmlFree(ret);
5378}
5379
5380/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005381 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00005382 * @ctxt: the XPath Parser context
5383 *
5384 * Parse a VariableReference, evaluate it and push it on the stack.
5385 *
5386 * The variable bindings consist of a mapping from variable names
5387 * to variable values. The value of a variable is an object, which
5388 * of any of the types that are possible for the value of an expression,
5389 * and may also be of additional types not specified here.
5390 *
5391 * Early evaluation is possible since:
5392 * The variable bindings [...] used to evaluate a subexpression are
5393 * always the same as those used to evaluate the containing expression.
5394 *
5395 * [36] VariableReference ::= '$' QName
5396 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005397static void
5398xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005399 xmlChar *name;
5400 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005401
5402 SKIP_BLANKS;
5403 if (CUR != '$') {
5404 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5405 }
5406 NEXT;
5407 name = xmlXPathParseQName(ctxt, &prefix);
5408 if (name == NULL) {
5409 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5410 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005411 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005412 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
5413 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005414 SKIP_BLANKS;
5415}
5416
5417/**
5418 * xmlXPathIsNodeType:
5419 * @ctxt: the XPath Parser context
5420 * @name: a name string
5421 *
5422 * Is the name given a NodeType one.
5423 *
5424 * [38] NodeType ::= 'comment'
5425 * | 'text'
5426 * | 'processing-instruction'
5427 * | 'node'
5428 *
5429 * Returns 1 if true 0 otherwise
5430 */
5431int
5432xmlXPathIsNodeType(const xmlChar *name) {
5433 if (name == NULL)
5434 return(0);
5435
5436 if (xmlStrEqual(name, BAD_CAST "comment"))
5437 return(1);
5438 if (xmlStrEqual(name, BAD_CAST "text"))
5439 return(1);
5440 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5441 return(1);
5442 if (xmlStrEqual(name, BAD_CAST "node"))
5443 return(1);
5444 return(0);
5445}
5446
5447/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005448 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00005449 * @ctxt: the XPath Parser context
5450 *
5451 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5452 * [17] Argument ::= Expr
5453 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005454 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00005455 * pushed on the stack
5456 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005457static void
5458xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005459 xmlChar *name;
5460 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00005461 int nbargs = 0;
5462
5463 name = xmlXPathParseQName(ctxt, &prefix);
5464 if (name == NULL) {
5465 XP_ERROR(XPATH_EXPR_ERROR);
5466 }
5467 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005468#ifdef DEBUG_EXPR
5469 if (prefix == NULL)
5470 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5471 name);
5472 else
5473 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5474 prefix, name);
5475#endif
5476
Owen Taylor3473f882001-02-23 17:55:21 +00005477 if (CUR != '(') {
5478 XP_ERROR(XPATH_EXPR_ERROR);
5479 }
5480 NEXT;
5481 SKIP_BLANKS;
5482
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005483 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005484 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005485 int op1 = ctxt->comp->last;
5486 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005487 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005488 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005489 nbargs++;
5490 if (CUR == ')') break;
5491 if (CUR != ',') {
5492 XP_ERROR(XPATH_EXPR_ERROR);
5493 }
5494 NEXT;
5495 SKIP_BLANKS;
5496 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005497 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
5498 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00005499 NEXT;
5500 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00005501}
5502
5503/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005504 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005505 * @ctxt: the XPath Parser context
5506 *
5507 * [15] PrimaryExpr ::= VariableReference
5508 * | '(' Expr ')'
5509 * | Literal
5510 * | Number
5511 * | FunctionCall
5512 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005513 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005514 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005515static void
5516xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005517 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005518 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005519 else if (CUR == '(') {
5520 NEXT;
5521 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005522 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005523 if (CUR != ')') {
5524 XP_ERROR(XPATH_EXPR_ERROR);
5525 }
5526 NEXT;
5527 SKIP_BLANKS;
5528 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005529 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005530 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005531 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005532 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005533 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005534 }
5535 SKIP_BLANKS;
5536}
5537
5538/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005539 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005540 * @ctxt: the XPath Parser context
5541 *
5542 * [20] FilterExpr ::= PrimaryExpr
5543 * | FilterExpr Predicate
5544 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005545 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005546 * Square brackets are used to filter expressions in the same way that
5547 * they are used in location paths. It is an error if the expression to
5548 * be filtered does not evaluate to a node-set. The context node list
5549 * used for evaluating the expression in square brackets is the node-set
5550 * to be filtered listed in document order.
5551 */
5552
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005553static void
5554xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
5555 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005556 CHECK_ERROR;
5557 SKIP_BLANKS;
5558
5559 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00005560 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00005561 SKIP_BLANKS;
5562 }
5563
5564
5565}
5566
5567/**
5568 * xmlXPathScanName:
5569 * @ctxt: the XPath Parser context
5570 *
5571 * Trickery: parse an XML name but without consuming the input flow
5572 * Needed to avoid insanity in the parser state.
5573 *
5574 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5575 * CombiningChar | Extender
5576 *
5577 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5578 *
5579 * [6] Names ::= Name (S Name)*
5580 *
5581 * Returns the Name parsed or NULL
5582 */
5583
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005584static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00005585xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5586 xmlChar buf[XML_MAX_NAMELEN];
5587 int len = 0;
5588
5589 SKIP_BLANKS;
5590 if (!IS_LETTER(CUR) && (CUR != '_') &&
5591 (CUR != ':')) {
5592 return(NULL);
5593 }
5594
5595 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5596 (NXT(len) == '.') || (NXT(len) == '-') ||
5597 (NXT(len) == '_') || (NXT(len) == ':') ||
5598 (IS_COMBINING(NXT(len))) ||
5599 (IS_EXTENDER(NXT(len)))) {
5600 buf[len] = NXT(len);
5601 len++;
5602 if (len >= XML_MAX_NAMELEN) {
5603 xmlGenericError(xmlGenericErrorContext,
5604 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5605 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5606 (NXT(len) == '.') || (NXT(len) == '-') ||
5607 (NXT(len) == '_') || (NXT(len) == ':') ||
5608 (IS_COMBINING(NXT(len))) ||
5609 (IS_EXTENDER(NXT(len))))
5610 len++;
5611 break;
5612 }
5613 }
5614 return(xmlStrndup(buf, len));
5615}
5616
5617/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005618 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005619 * @ctxt: the XPath Parser context
5620 *
5621 * [19] PathExpr ::= LocationPath
5622 * | FilterExpr
5623 * | FilterExpr '/' RelativeLocationPath
5624 * | FilterExpr '//' RelativeLocationPath
5625 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005626 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005627 * The / operator and // operators combine an arbitrary expression
5628 * and a relative location path. It is an error if the expression
5629 * does not evaluate to a node-set.
5630 * The / operator does composition in the same way as when / is
5631 * used in a location path. As in location paths, // is short for
5632 * /descendant-or-self::node()/.
5633 */
5634
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005635static void
5636xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005637 int lc = 1; /* Should we branch to LocationPath ? */
5638 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5639
5640 SKIP_BLANKS;
5641 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5642 (CUR == '\'') || (CUR == '"')) {
5643 lc = 0;
5644 } else if (CUR == '*') {
5645 /* relative or absolute location path */
5646 lc = 1;
5647 } else if (CUR == '/') {
5648 /* relative or absolute location path */
5649 lc = 1;
5650 } else if (CUR == '@') {
5651 /* relative abbreviated attribute location path */
5652 lc = 1;
5653 } else if (CUR == '.') {
5654 /* relative abbreviated attribute location path */
5655 lc = 1;
5656 } else {
5657 /*
5658 * Problem is finding if we have a name here whether it's:
5659 * - a nodetype
5660 * - a function call in which case it's followed by '('
5661 * - an axis in which case it's followed by ':'
5662 * - a element name
5663 * We do an a priori analysis here rather than having to
5664 * maintain parsed token content through the recursive function
5665 * calls. This looks uglier but makes the code quite easier to
5666 * read/write/debug.
5667 */
5668 SKIP_BLANKS;
5669 name = xmlXPathScanName(ctxt);
5670 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5671#ifdef DEBUG_STEP
5672 xmlGenericError(xmlGenericErrorContext,
5673 "PathExpr: Axis\n");
5674#endif
5675 lc = 1;
5676 xmlFree(name);
5677 } else if (name != NULL) {
5678 int len =xmlStrlen(name);
5679 int blank = 0;
5680
5681
5682 while (NXT(len) != 0) {
5683 if (NXT(len) == '/') {
5684 /* element name */
5685#ifdef DEBUG_STEP
5686 xmlGenericError(xmlGenericErrorContext,
5687 "PathExpr: AbbrRelLocation\n");
5688#endif
5689 lc = 1;
5690 break;
5691 } else if (IS_BLANK(NXT(len))) {
5692 /* skip to next */
5693 blank = 1;
5694 } else if (NXT(len) == ':') {
5695#ifdef DEBUG_STEP
5696 xmlGenericError(xmlGenericErrorContext,
5697 "PathExpr: AbbrRelLocation\n");
5698#endif
5699 lc = 1;
5700 break;
5701 } else if ((NXT(len) == '(')) {
5702 /* Note Type or Function */
5703 if (xmlXPathIsNodeType(name)) {
5704#ifdef DEBUG_STEP
5705 xmlGenericError(xmlGenericErrorContext,
5706 "PathExpr: Type search\n");
5707#endif
5708 lc = 1;
5709 } else {
5710#ifdef DEBUG_STEP
5711 xmlGenericError(xmlGenericErrorContext,
5712 "PathExpr: function call\n");
5713#endif
5714 lc = 0;
5715 }
5716 break;
5717 } else if ((NXT(len) == '[')) {
5718 /* element name */
5719#ifdef DEBUG_STEP
5720 xmlGenericError(xmlGenericErrorContext,
5721 "PathExpr: AbbrRelLocation\n");
5722#endif
5723 lc = 1;
5724 break;
5725 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5726 (NXT(len) == '=')) {
5727 lc = 1;
5728 break;
5729 } else {
5730 lc = 1;
5731 break;
5732 }
5733 len++;
5734 }
5735 if (NXT(len) == 0) {
5736#ifdef DEBUG_STEP
5737 xmlGenericError(xmlGenericErrorContext,
5738 "PathExpr: AbbrRelLocation\n");
5739#endif
5740 /* element name */
5741 lc = 1;
5742 }
5743 xmlFree(name);
5744 } else {
5745 /* make sure all cases are covered explicitely */
5746 XP_ERROR(XPATH_EXPR_ERROR);
5747 }
5748 }
5749
5750 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005751 if (CUR == '/') {
5752 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
5753 } else {
5754 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005755 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005756 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005757 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005758 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005759 CHECK_ERROR;
5760 if ((CUR == '/') && (NXT(1) == '/')) {
5761 SKIP(2);
5762 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005763
5764 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
5765 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5766 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
5767
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005768 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005769 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005770 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005771 }
5772 }
5773 SKIP_BLANKS;
5774}
5775
5776/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005777 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005778 * @ctxt: the XPath Parser context
5779 *
5780 * [18] UnionExpr ::= PathExpr
5781 * | UnionExpr '|' PathExpr
5782 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005783 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005784 */
5785
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005786static void
5787xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
5788 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005789 CHECK_ERROR;
5790 SKIP_BLANKS;
5791 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005792 int op1 = ctxt->comp->last;
5793 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005794
5795 NEXT;
5796 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005797 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005798
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005799 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
5800
Owen Taylor3473f882001-02-23 17:55:21 +00005801 SKIP_BLANKS;
5802 }
Owen Taylor3473f882001-02-23 17:55:21 +00005803}
5804
5805/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005806 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005807 * @ctxt: the XPath Parser context
5808 *
5809 * [27] UnaryExpr ::= UnionExpr
5810 * | '-' UnaryExpr
5811 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005812 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005813 */
5814
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005815static void
5816xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00005817 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005818 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005819
5820 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00005821 while (CUR == '-') {
5822 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005823 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005824 NEXT;
5825 SKIP_BLANKS;
5826 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005827
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005828 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005829 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005830 if (found) {
5831 if (minus)
5832 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
5833 else
5834 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005835 }
5836}
5837
5838/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005839 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005840 * @ctxt: the XPath Parser context
5841 *
5842 * [26] MultiplicativeExpr ::= UnaryExpr
5843 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5844 * | MultiplicativeExpr 'div' UnaryExpr
5845 * | MultiplicativeExpr 'mod' UnaryExpr
5846 * [34] MultiplyOperator ::= '*'
5847 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005848 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005849 */
5850
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005851static void
5852xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5853 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005854 CHECK_ERROR;
5855 SKIP_BLANKS;
5856 while ((CUR == '*') ||
5857 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5858 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5859 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005860 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005861
5862 if (CUR == '*') {
5863 op = 0;
5864 NEXT;
5865 } else if (CUR == 'd') {
5866 op = 1;
5867 SKIP(3);
5868 } else if (CUR == 'm') {
5869 op = 2;
5870 SKIP(3);
5871 }
5872 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005873 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005874 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005875 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005876 SKIP_BLANKS;
5877 }
5878}
5879
5880/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005881 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005882 * @ctxt: the XPath Parser context
5883 *
5884 * [25] AdditiveExpr ::= MultiplicativeExpr
5885 * | AdditiveExpr '+' MultiplicativeExpr
5886 * | AdditiveExpr '-' MultiplicativeExpr
5887 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005888 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005889 */
5890
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005891static void
5892xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005893
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005894 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005895 CHECK_ERROR;
5896 SKIP_BLANKS;
5897 while ((CUR == '+') || (CUR == '-')) {
5898 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005899 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005900
5901 if (CUR == '+') plus = 1;
5902 else plus = 0;
5903 NEXT;
5904 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005905 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005906 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005907 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005908 SKIP_BLANKS;
5909 }
5910}
5911
5912/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005913 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005914 * @ctxt: the XPath Parser context
5915 *
5916 * [24] RelationalExpr ::= AdditiveExpr
5917 * | RelationalExpr '<' AdditiveExpr
5918 * | RelationalExpr '>' AdditiveExpr
5919 * | RelationalExpr '<=' AdditiveExpr
5920 * | RelationalExpr '>=' AdditiveExpr
5921 *
5922 * A <= B > C is allowed ? Answer from James, yes with
5923 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5924 * which is basically what got implemented.
5925 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005926 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00005927 * on the stack
5928 */
5929
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005930static void
5931xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
5932 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005933 CHECK_ERROR;
5934 SKIP_BLANKS;
5935 while ((CUR == '<') ||
5936 (CUR == '>') ||
5937 ((CUR == '<') && (NXT(1) == '=')) ||
5938 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005939 int inf, strict;
5940 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005941
5942 if (CUR == '<') inf = 1;
5943 else inf = 0;
5944 if (NXT(1) == '=') strict = 0;
5945 else strict = 1;
5946 NEXT;
5947 if (!strict) NEXT;
5948 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005949 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005950 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005951 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00005952 SKIP_BLANKS;
5953 }
5954}
5955
5956/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005957 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005958 * @ctxt: the XPath Parser context
5959 *
5960 * [23] EqualityExpr ::= RelationalExpr
5961 * | EqualityExpr '=' RelationalExpr
5962 * | EqualityExpr '!=' RelationalExpr
5963 *
5964 * A != B != C is allowed ? Answer from James, yes with
5965 * (RelationalExpr = RelationalExpr) = RelationalExpr
5966 * (RelationalExpr != RelationalExpr) != RelationalExpr
5967 * which is basically what got implemented.
5968 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005969 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00005970 *
5971 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005972static void
5973xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
5974 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005975 CHECK_ERROR;
5976 SKIP_BLANKS;
5977 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005978 int eq;
5979 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00005980
5981 if (CUR == '=') eq = 1;
5982 else eq = 0;
5983 NEXT;
5984 if (!eq) NEXT;
5985 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005986 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005987 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00005988 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00005989 SKIP_BLANKS;
5990 }
5991}
5992
5993/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00005994 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00005995 * @ctxt: the XPath Parser context
5996 *
5997 * [22] AndExpr ::= EqualityExpr
5998 * | AndExpr 'and' EqualityExpr
5999 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006000 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006001 *
6002 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006003static void
6004xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
6005 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006006 CHECK_ERROR;
6007 SKIP_BLANKS;
6008 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006009 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006010 SKIP(3);
6011 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006012 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006013 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006014 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006015 SKIP_BLANKS;
6016 }
6017}
6018
6019/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006020 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006021 * @ctxt: the XPath Parser context
6022 *
6023 * [14] Expr ::= OrExpr
6024 * [21] OrExpr ::= AndExpr
6025 * | OrExpr 'or' AndExpr
6026 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006027 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00006028 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006029static void
6030xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
6031 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006032 CHECK_ERROR;
6033 SKIP_BLANKS;
6034 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006035 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006036 SKIP(2);
6037 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006038 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006039 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006040 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
6041 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00006042 SKIP_BLANKS;
6043 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006044 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
6045 /* more ops could be optimized too */
6046 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
6047 }
Owen Taylor3473f882001-02-23 17:55:21 +00006048}
6049
6050/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006051 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00006052 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006053 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00006054 *
6055 * [8] Predicate ::= '[' PredicateExpr ']'
6056 * [9] PredicateExpr ::= Expr
6057 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006058 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00006059 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006060static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006061xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006062 int op1 = ctxt->comp->last;
6063
6064 SKIP_BLANKS;
6065 if (CUR != '[') {
6066 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6067 }
6068 NEXT;
6069 SKIP_BLANKS;
6070
6071 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006072 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006073 CHECK_ERROR;
6074
6075 if (CUR != ']') {
6076 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6077 }
6078
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006079 if (filter)
6080 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
6081 else
6082 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006083
6084 NEXT;
6085 SKIP_BLANKS;
6086}
6087
6088/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006089 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00006090 * @ctxt: the XPath Parser context
6091 * @test: pointer to a xmlXPathTestVal
6092 * @type: pointer to a xmlXPathTypeVal
6093 * @prefix: placeholder for a possible name prefix
6094 *
6095 * [7] NodeTest ::= NameTest
6096 * | NodeType '(' ')'
6097 * | 'processing-instruction' '(' Literal ')'
6098 *
6099 * [37] NameTest ::= '*'
6100 * | NCName ':' '*'
6101 * | QName
6102 * [38] NodeType ::= 'comment'
6103 * | 'text'
6104 * | 'processing-instruction'
6105 * | 'node'
6106 *
6107 * Returns the name found and update @test, @type and @prefix appropriately
6108 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006109static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006110xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6111 xmlXPathTypeVal *type, const xmlChar **prefix,
6112 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00006113 int blanks;
6114
6115 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6116 STRANGE;
6117 return(NULL);
6118 }
6119 *type = 0;
6120 *test = 0;
6121 *prefix = NULL;
6122 SKIP_BLANKS;
6123
6124 if ((name == NULL) && (CUR == '*')) {
6125 /*
6126 * All elements
6127 */
6128 NEXT;
6129 *test = NODE_TEST_ALL;
6130 return(NULL);
6131 }
6132
6133 if (name == NULL)
6134 name = xmlXPathParseNCName(ctxt);
6135 if (name == NULL) {
6136 XP_ERROR0(XPATH_EXPR_ERROR);
6137 }
6138
6139 blanks = IS_BLANK(CUR);
6140 SKIP_BLANKS;
6141 if (CUR == '(') {
6142 NEXT;
6143 /*
6144 * NodeType or PI search
6145 */
6146 if (xmlStrEqual(name, BAD_CAST "comment"))
6147 *type = NODE_TYPE_COMMENT;
6148 else if (xmlStrEqual(name, BAD_CAST "node"))
6149 *type = NODE_TYPE_NODE;
6150 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6151 *type = NODE_TYPE_PI;
6152 else if (xmlStrEqual(name, BAD_CAST "text"))
6153 *type = NODE_TYPE_TEXT;
6154 else {
6155 if (name != NULL)
6156 xmlFree(name);
6157 XP_ERROR0(XPATH_EXPR_ERROR);
6158 }
6159
6160 *test = NODE_TEST_TYPE;
6161
6162 SKIP_BLANKS;
6163 if (*type == NODE_TYPE_PI) {
6164 /*
6165 * Specific case: search a PI by name.
6166 */
Owen Taylor3473f882001-02-23 17:55:21 +00006167 if (name != NULL)
6168 xmlFree(name);
6169
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006170 name = xmlXPathParseLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006171 CHECK_ERROR 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006172 SKIP_BLANKS;
6173 }
6174 if (CUR != ')') {
6175 if (name != NULL)
6176 xmlFree(name);
6177 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6178 }
6179 NEXT;
6180 return(name);
6181 }
6182 *test = NODE_TEST_NAME;
6183 if ((!blanks) && (CUR == ':')) {
6184 NEXT;
6185
6186 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006187 * Since currently the parser context don't have a
6188 * namespace list associated:
6189 * The namespace name for this prefix can be computed
6190 * only at evaluation time. The compilation is done
6191 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00006192 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006193#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00006194 *prefix = xmlXPathNsLookup(ctxt->context, name);
6195 if (name != NULL)
6196 xmlFree(name);
6197 if (*prefix == NULL) {
6198 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6199 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006200#else
6201 *prefix = name;
6202#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006203
6204 if (CUR == '*') {
6205 /*
6206 * All elements
6207 */
6208 NEXT;
6209 *test = NODE_TEST_ALL;
6210 return(NULL);
6211 }
6212
6213 name = xmlXPathParseNCName(ctxt);
6214 if (name == NULL) {
6215 XP_ERROR0(XPATH_EXPR_ERROR);
6216 }
6217 }
6218 return(name);
6219}
6220
6221/**
6222 * xmlXPathIsAxisName:
6223 * @name: a preparsed name token
6224 *
6225 * [6] AxisName ::= 'ancestor'
6226 * | 'ancestor-or-self'
6227 * | 'attribute'
6228 * | 'child'
6229 * | 'descendant'
6230 * | 'descendant-or-self'
6231 * | 'following'
6232 * | 'following-sibling'
6233 * | 'namespace'
6234 * | 'parent'
6235 * | 'preceding'
6236 * | 'preceding-sibling'
6237 * | 'self'
6238 *
6239 * Returns the axis or 0
6240 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006241static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00006242xmlXPathIsAxisName(const xmlChar *name) {
6243 xmlXPathAxisVal ret = 0;
6244 switch (name[0]) {
6245 case 'a':
6246 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6247 ret = AXIS_ANCESTOR;
6248 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6249 ret = AXIS_ANCESTOR_OR_SELF;
6250 if (xmlStrEqual(name, BAD_CAST "attribute"))
6251 ret = AXIS_ATTRIBUTE;
6252 break;
6253 case 'c':
6254 if (xmlStrEqual(name, BAD_CAST "child"))
6255 ret = AXIS_CHILD;
6256 break;
6257 case 'd':
6258 if (xmlStrEqual(name, BAD_CAST "descendant"))
6259 ret = AXIS_DESCENDANT;
6260 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6261 ret = AXIS_DESCENDANT_OR_SELF;
6262 break;
6263 case 'f':
6264 if (xmlStrEqual(name, BAD_CAST "following"))
6265 ret = AXIS_FOLLOWING;
6266 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6267 ret = AXIS_FOLLOWING_SIBLING;
6268 break;
6269 case 'n':
6270 if (xmlStrEqual(name, BAD_CAST "namespace"))
6271 ret = AXIS_NAMESPACE;
6272 break;
6273 case 'p':
6274 if (xmlStrEqual(name, BAD_CAST "parent"))
6275 ret = AXIS_PARENT;
6276 if (xmlStrEqual(name, BAD_CAST "preceding"))
6277 ret = AXIS_PRECEDING;
6278 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6279 ret = AXIS_PRECEDING_SIBLING;
6280 break;
6281 case 's':
6282 if (xmlStrEqual(name, BAD_CAST "self"))
6283 ret = AXIS_SELF;
6284 break;
6285 }
6286 return(ret);
6287}
6288
6289/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006290 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00006291 * @ctxt: the XPath Parser context
6292 *
6293 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6294 * | AbbreviatedStep
6295 *
6296 * [12] AbbreviatedStep ::= '.' | '..'
6297 *
6298 * [5] AxisSpecifier ::= AxisName '::'
6299 * | AbbreviatedAxisSpecifier
6300 *
6301 * [13] AbbreviatedAxisSpecifier ::= '@'?
6302 *
6303 * Modified for XPtr range support as:
6304 *
6305 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6306 * | AbbreviatedStep
6307 * | 'range-to' '(' Expr ')' Predicate*
6308 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006309 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00006310 * A location step of . is short for self::node(). This is
6311 * particularly useful in conjunction with //. For example, the
6312 * location path .//para is short for
6313 * self::node()/descendant-or-self::node()/child::para
6314 * and so will select all para descendant elements of the context
6315 * node.
6316 * Similarly, a location step of .. is short for parent::node().
6317 * For example, ../title is short for parent::node()/child::title
6318 * and so will select the title children of the parent of the context
6319 * node.
6320 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006321static void
6322xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006323 SKIP_BLANKS;
6324 if ((CUR == '.') && (NXT(1) == '.')) {
6325 SKIP(2);
6326 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006327 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
6328 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006329 } else if (CUR == '.') {
6330 NEXT;
6331 SKIP_BLANKS;
6332 } else {
6333 xmlChar *name = NULL;
6334 const xmlChar *prefix = NULL;
6335 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006336 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006337 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006338 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00006339
6340 /*
6341 * The modification needed for XPointer change to the production
6342 */
6343#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006344 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00006345 name = xmlXPathParseNCName(ctxt);
6346 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006347 int op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00006348 xmlFree(name);
6349 SKIP_BLANKS;
6350 if (CUR != '(') {
6351 XP_ERROR(XPATH_EXPR_ERROR);
6352 }
6353 NEXT;
6354 SKIP_BLANKS;
6355
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006356 xmlXPathCompileExpr(ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006357 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006358 CHECK_ERROR;
6359
6360 SKIP_BLANKS;
6361 if (CUR != ')') {
6362 XP_ERROR(XPATH_EXPR_ERROR);
6363 }
6364 NEXT;
6365 goto eval_predicates;
6366 }
6367 }
6368#endif
6369 if (name == NULL)
6370 name = xmlXPathParseNCName(ctxt);
6371 if (name != NULL) {
6372 axis = xmlXPathIsAxisName(name);
6373 if (axis != 0) {
6374 SKIP_BLANKS;
6375 if ((CUR == ':') && (NXT(1) == ':')) {
6376 SKIP(2);
6377 xmlFree(name);
6378 name = NULL;
6379 } else {
6380 /* an element name can conflict with an axis one :-\ */
6381 axis = AXIS_CHILD;
6382 }
6383 } else {
6384 axis = AXIS_CHILD;
6385 }
6386 } else if (CUR == '@') {
6387 NEXT;
6388 axis = AXIS_ATTRIBUTE;
6389 } else {
6390 axis = AXIS_CHILD;
6391 }
6392
6393 CHECK_ERROR;
6394
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006395 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00006396 if (test == 0)
6397 return;
6398
6399#ifdef DEBUG_STEP
6400 xmlGenericError(xmlGenericErrorContext,
6401 "Basis : computing new set\n");
6402#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006403
Owen Taylor3473f882001-02-23 17:55:21 +00006404#ifdef DEBUG_STEP
6405 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6406 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6407#endif
Owen Taylor3473f882001-02-23 17:55:21 +00006408
6409eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006410 op1 = ctxt->comp->last;
6411 ctxt->comp->last = -1;
6412
Owen Taylor3473f882001-02-23 17:55:21 +00006413 SKIP_BLANKS;
6414 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006415 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006416 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006417
6418 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
6419 test, type, (void *)prefix, (void *)name);
6420
Owen Taylor3473f882001-02-23 17:55:21 +00006421 }
6422#ifdef DEBUG_STEP
6423 xmlGenericError(xmlGenericErrorContext, "Step : ");
6424 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6425 ctxt->value->nodesetval);
6426#endif
6427}
6428
6429/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006430 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006431 * @ctxt: the XPath Parser context
6432 *
6433 * [3] RelativeLocationPath ::= Step
6434 * | RelativeLocationPath '/' Step
6435 * | AbbreviatedRelativeLocationPath
6436 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6437 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006438 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00006439 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006440static void
Owen Taylor3473f882001-02-23 17:55:21 +00006441#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006442xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006443#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006444xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006445#endif
6446(xmlXPathParserContextPtr ctxt) {
6447 SKIP_BLANKS;
6448 if ((CUR == '/') && (NXT(1) == '/')) {
6449 SKIP(2);
6450 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006451 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6452 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006453 } else if (CUR == '/') {
6454 NEXT;
6455 SKIP_BLANKS;
6456 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006457 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006458 SKIP_BLANKS;
6459 while (CUR == '/') {
6460 if ((CUR == '/') && (NXT(1) == '/')) {
6461 SKIP(2);
6462 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006463 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00006464 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006465 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006466 } else if (CUR == '/') {
6467 NEXT;
6468 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006469 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006470 }
6471 SKIP_BLANKS;
6472 }
6473}
6474
6475/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006476 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00006477 * @ctxt: the XPath Parser context
6478 *
6479 * [1] LocationPath ::= RelativeLocationPath
6480 * | AbsoluteLocationPath
6481 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6482 * | AbbreviatedAbsoluteLocationPath
6483 * [10] AbbreviatedAbsoluteLocationPath ::=
6484 * '//' RelativeLocationPath
6485 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006486 * Compile a location path
6487 *
Owen Taylor3473f882001-02-23 17:55:21 +00006488 * // is short for /descendant-or-self::node()/. For example,
6489 * //para is short for /descendant-or-self::node()/child::para and
6490 * so will select any para element in the document (even a para element
6491 * that is a document element will be selected by //para since the
6492 * document element node is a child of the root node); div//para is
6493 * short for div/descendant-or-self::node()/child::para and so will
6494 * select all para descendants of div children.
6495 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006496static void
6497xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006498 SKIP_BLANKS;
6499 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006500 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006501 } else {
6502 while (CUR == '/') {
6503 if ((CUR == '/') && (NXT(1) == '/')) {
6504 SKIP(2);
6505 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006506 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
6507 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006508 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006509 } else if (CUR == '/') {
6510 NEXT;
6511 SKIP_BLANKS;
6512 if (CUR != 0)
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006513 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006514 }
6515 }
6516 }
6517}
6518
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006519/************************************************************************
6520 * *
6521 * XPath precompiled expression evaluation *
6522 * *
6523 ************************************************************************/
6524
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006525static void
6526xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
6527
6528/**
6529 * xmlXPathNodeCollectAndTest:
6530 * @ctxt: the XPath Parser context
6531 * @op: the XPath precompiled step operation
6532 *
6533 * This is the function implementing a step: based on the current list
6534 * of nodes, it builds up a new list, looking at all nodes under that
6535 * axis and selecting them it also do the predicate filtering
6536 *
6537 * Pushes the new NodeSet resulting from the search.
6538 */
6539static void
6540xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
6541 xmlXPathStepOpPtr op) {
6542 xmlXPathAxisVal axis = op->value;
6543 xmlXPathTestVal test = op->value2;
6544 xmlXPathTypeVal type = op->value3;
6545 const xmlChar *prefix = op->value4;
6546 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006547 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006548
6549#ifdef DEBUG_STEP
6550 int n = 0, t = 0;
6551#endif
6552 int i;
6553 xmlNodeSetPtr ret, list;
6554 xmlXPathTraversalFunction next = NULL;
6555 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
6556 xmlNodePtr cur = NULL;
6557 xmlXPathObjectPtr obj;
6558 xmlNodeSetPtr nodelist;
6559 xmlNodePtr tmp;
6560
6561 CHECK_TYPE(XPATH_NODESET);
6562 obj = valuePop(ctxt);
6563 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00006564 if (prefix != NULL) {
6565 URI = xmlXPathNsLookup(ctxt->context, prefix);
6566 if (URI == NULL)
6567 XP_ERROR(XPATH_UNDEF_PREFIX_ERROR);
6568 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006569
6570#ifdef DEBUG_STEP
6571 xmlGenericError(xmlGenericErrorContext,
6572 "new step : ");
6573#endif
6574 switch (axis) {
6575 case AXIS_ANCESTOR:
6576#ifdef DEBUG_STEP
6577 xmlGenericError(xmlGenericErrorContext,
6578 "axis 'ancestors' ");
6579#endif
6580 next = xmlXPathNextAncestor; break;
6581 case AXIS_ANCESTOR_OR_SELF:
6582#ifdef DEBUG_STEP
6583 xmlGenericError(xmlGenericErrorContext,
6584 "axis 'ancestors-or-self' ");
6585#endif
6586 next = xmlXPathNextAncestorOrSelf; break;
6587 case AXIS_ATTRIBUTE:
6588#ifdef DEBUG_STEP
6589 xmlGenericError(xmlGenericErrorContext,
6590 "axis 'attributes' ");
6591#endif
6592 next = xmlXPathNextAttribute; break;
6593 break;
6594 case AXIS_CHILD:
6595#ifdef DEBUG_STEP
6596 xmlGenericError(xmlGenericErrorContext,
6597 "axis 'child' ");
6598#endif
6599 next = xmlXPathNextChild; break;
6600 case AXIS_DESCENDANT:
6601#ifdef DEBUG_STEP
6602 xmlGenericError(xmlGenericErrorContext,
6603 "axis 'descendant' ");
6604#endif
6605 next = xmlXPathNextDescendant; break;
6606 case AXIS_DESCENDANT_OR_SELF:
6607#ifdef DEBUG_STEP
6608 xmlGenericError(xmlGenericErrorContext,
6609 "axis 'descendant-or-self' ");
6610#endif
6611 next = xmlXPathNextDescendantOrSelf; break;
6612 case AXIS_FOLLOWING:
6613#ifdef DEBUG_STEP
6614 xmlGenericError(xmlGenericErrorContext,
6615 "axis 'following' ");
6616#endif
6617 next = xmlXPathNextFollowing; break;
6618 case AXIS_FOLLOWING_SIBLING:
6619#ifdef DEBUG_STEP
6620 xmlGenericError(xmlGenericErrorContext,
6621 "axis 'following-siblings' ");
6622#endif
6623 next = xmlXPathNextFollowingSibling; break;
6624 case AXIS_NAMESPACE:
6625#ifdef DEBUG_STEP
6626 xmlGenericError(xmlGenericErrorContext,
6627 "axis 'namespace' ");
6628#endif
6629 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
6630 break;
6631 case AXIS_PARENT:
6632#ifdef DEBUG_STEP
6633 xmlGenericError(xmlGenericErrorContext,
6634 "axis 'parent' ");
6635#endif
6636 next = xmlXPathNextParent; break;
6637 case AXIS_PRECEDING:
6638#ifdef DEBUG_STEP
6639 xmlGenericError(xmlGenericErrorContext,
6640 "axis 'preceding' ");
6641#endif
6642 next = xmlXPathNextPreceding; break;
6643 case AXIS_PRECEDING_SIBLING:
6644#ifdef DEBUG_STEP
6645 xmlGenericError(xmlGenericErrorContext,
6646 "axis 'preceding-sibling' ");
6647#endif
6648 next = xmlXPathNextPrecedingSibling; break;
6649 case AXIS_SELF:
6650#ifdef DEBUG_STEP
6651 xmlGenericError(xmlGenericErrorContext,
6652 "axis 'self' ");
6653#endif
6654 next = xmlXPathNextSelf; break;
6655 }
6656 if (next == NULL)
6657 return;
6658
6659 nodelist = obj->nodesetval;
6660 if (nodelist == NULL) {
6661 xmlXPathFreeObject(obj);
6662 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
6663 return;
6664 }
6665 addNode = xmlXPathNodeSetAddUnique;
6666 ret = NULL;
6667#ifdef DEBUG_STEP
6668 xmlGenericError(xmlGenericErrorContext,
6669 " context contains %d nodes\n",
6670 nodelist->nodeNr);
6671 switch (test) {
6672 case NODE_TEST_NODE:
6673 xmlGenericError(xmlGenericErrorContext,
6674 " searching all nodes\n");
6675 break;
6676 case NODE_TEST_NONE:
6677 xmlGenericError(xmlGenericErrorContext,
6678 " searching for none !!!\n");
6679 break;
6680 case NODE_TEST_TYPE:
6681 xmlGenericError(xmlGenericErrorContext,
6682 " searching for type %d\n", type);
6683 break;
6684 case NODE_TEST_PI:
6685 xmlGenericError(xmlGenericErrorContext,
6686 " searching for PI !!!\n");
6687 break;
6688 case NODE_TEST_ALL:
6689 xmlGenericError(xmlGenericErrorContext,
6690 " searching for *\n");
6691 break;
6692 case NODE_TEST_NS:
6693 xmlGenericError(xmlGenericErrorContext,
6694 " searching for namespace %s\n",
6695 prefix);
6696 break;
6697 case NODE_TEST_NAME:
6698 xmlGenericError(xmlGenericErrorContext,
6699 " searching for name %s\n", name);
6700 if (prefix != NULL)
6701 xmlGenericError(xmlGenericErrorContext,
6702 " with namespace %s\n",
6703 prefix);
6704 break;
6705 }
6706 xmlGenericError(xmlGenericErrorContext, "Testing : ");
6707#endif
6708 /*
6709 * 2.3 Node Tests
6710 * - For the attribute axis, the principal node type is attribute.
6711 * - For the namespace axis, the principal node type is namespace.
6712 * - For other axes, the principal node type is element.
6713 *
6714 * A node test * is true for any node of the
6715 * principal node type. For example, child::* willi
6716 * select all element children of the context node
6717 */
6718 tmp = ctxt->context->node;
6719 for (i = 0;i < nodelist->nodeNr; i++) {
6720 ctxt->context->node = nodelist->nodeTab[i];
6721
6722 cur = NULL;
6723 list = xmlXPathNodeSetCreate(NULL);
6724 do {
6725 cur = next(ctxt, cur);
6726 if (cur == NULL) break;
6727#ifdef DEBUG_STEP
6728 t++;
6729 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
6730#endif
6731 switch (test) {
6732 case NODE_TEST_NONE:
6733 ctxt->context->node = tmp;
6734 STRANGE
6735 return;
6736 case NODE_TEST_TYPE:
6737 if ((cur->type == type) ||
6738 ((type == NODE_TYPE_NODE) &&
6739 ((cur->type == XML_DOCUMENT_NODE) ||
6740 (cur->type == XML_HTML_DOCUMENT_NODE) ||
6741 (cur->type == XML_ELEMENT_NODE) ||
6742 (cur->type == XML_PI_NODE) ||
6743 (cur->type == XML_COMMENT_NODE) ||
6744 (cur->type == XML_CDATA_SECTION_NODE) ||
6745 (cur->type == XML_TEXT_NODE)))) {
6746#ifdef DEBUG_STEP
6747 n++;
6748#endif
6749 addNode(list, cur);
6750 }
6751 break;
6752 case NODE_TEST_PI:
6753 if (cur->type == XML_PI_NODE) {
6754 if ((name != NULL) &&
6755 (!xmlStrEqual(name, cur->name)))
6756 break;
6757#ifdef DEBUG_STEP
6758 n++;
6759#endif
6760 addNode(list, cur);
6761 }
6762 break;
6763 case NODE_TEST_ALL:
6764 if (axis == AXIS_ATTRIBUTE) {
6765 if (cur->type == XML_ATTRIBUTE_NODE) {
6766#ifdef DEBUG_STEP
6767 n++;
6768#endif
6769 addNode(list, cur);
6770 }
6771 } else if (axis == AXIS_NAMESPACE) {
6772 if (cur->type == XML_NAMESPACE_DECL) {
6773#ifdef DEBUG_STEP
6774 n++;
6775#endif
6776 addNode(list, cur);
6777 }
6778 } else {
6779 if ((cur->type == XML_ELEMENT_NODE) ||
6780 (cur->type == XML_DOCUMENT_NODE) ||
6781 (cur->type == XML_HTML_DOCUMENT_NODE)) {
6782 if (prefix == NULL) {
6783#ifdef DEBUG_STEP
6784 n++;
6785#endif
6786 addNode(list, cur);
6787 } else if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006788 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006789 cur->ns->href))) {
6790#ifdef DEBUG_STEP
6791 n++;
6792#endif
6793 addNode(list, cur);
6794 }
6795 }
6796 }
6797 break;
6798 case NODE_TEST_NS: {
6799 TODO;
6800 break;
6801 }
6802 case NODE_TEST_NAME:
6803 switch (cur->type) {
6804 case XML_ELEMENT_NODE:
6805 if (xmlStrEqual(name, cur->name)) {
6806 if (prefix == NULL) {
6807 if ((cur->ns == NULL) ||
6808 (cur->ns->prefix == NULL)) {
6809#ifdef DEBUG_STEP
6810 n++;
6811#endif
6812 addNode(list, cur);
6813 }
6814 } else {
6815 if ((cur->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006816 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006817 cur->ns->href))) {
6818#ifdef DEBUG_STEP
6819 n++;
6820#endif
6821 addNode(list, cur);
6822 }
6823 }
6824 }
6825 break;
6826 case XML_ATTRIBUTE_NODE: {
6827 xmlAttrPtr attr = (xmlAttrPtr) cur;
6828 if (xmlStrEqual(name, attr->name)) {
6829 if (prefix == NULL) {
6830 if ((attr->ns == NULL) ||
6831 (attr->ns->prefix == NULL)) {
6832#ifdef DEBUG_STEP
6833 n++;
6834#endif
6835 addNode(list, (xmlNodePtr) attr);
6836 }
6837 } else {
6838 if ((attr->ns != NULL) &&
Daniel Veillarde043ee12001-04-16 14:08:07 +00006839 (xmlStrEqual(URI,
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006840 attr->ns->href))) {
6841#ifdef DEBUG_STEP
6842 n++;
6843#endif
6844 addNode(list, (xmlNodePtr) attr);
6845 }
6846 }
6847 }
6848 break;
6849 }
6850 case XML_NAMESPACE_DECL: {
6851 TODO;
6852 break;
6853 }
6854 default:
6855 break;
6856 }
6857 break;
6858 }
6859 } while (cur != NULL);
6860
6861 /*
6862 * If there is some predicate filtering do it now
6863 */
6864 if (op->ch2 != -1) {
6865 xmlXPathObjectPtr obj2;
6866
6867 valuePush(ctxt, xmlXPathWrapNodeSet(list));
6868 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
6869 CHECK_TYPE(XPATH_NODESET);
6870 obj2 = valuePop(ctxt);
6871 list = obj2->nodesetval;
6872 obj2->nodesetval = NULL;
6873 xmlXPathFreeObject(obj2);
6874 }
6875 if (ret == NULL) {
6876 ret = list;
6877 } else {
6878 ret = xmlXPathNodeSetMerge(ret, list);
6879 xmlXPathFreeNodeSet(list);
6880 }
6881 }
6882 ctxt->context->node = tmp;
6883#ifdef DEBUG_STEP
6884 xmlGenericError(xmlGenericErrorContext,
6885 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
6886#endif
6887 xmlXPathFreeObject(obj);
6888 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
6889}
6890
Owen Taylor3473f882001-02-23 17:55:21 +00006891/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006892 * xmlXPathCompOpEval:
6893 * @ctxt: the XPath parser context with the compiled expression
6894 * @op: an XPath compiled operation
6895 *
6896 * Evaluate the Precompiled XPath operation
6897 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006898static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006899xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) {
6900 int equal, ret;
6901 xmlXPathCompExprPtr comp;
6902 xmlXPathObjectPtr arg1, arg2;
6903
6904 comp = ctxt->comp;
6905 switch (op->op) {
6906 case XPATH_OP_END:
6907 return;
6908 case XPATH_OP_AND:
6909 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6910 xmlXPathBooleanFunction(ctxt, 1);
6911 if (ctxt->value->boolval == 0)
6912 return;
6913 arg2 = valuePop(ctxt);
6914 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6915 xmlXPathBooleanFunction(ctxt, 1);
6916 arg1 = valuePop(ctxt);
6917 arg1->boolval &= arg2->boolval;
6918 valuePush(ctxt, arg1);
6919 xmlXPathFreeObject(arg2);
6920 return;
6921 case XPATH_OP_OR:
6922 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6923 xmlXPathBooleanFunction(ctxt, 1);
6924 if (ctxt->value->boolval == 1)
6925 return;
6926 arg2 = valuePop(ctxt);
6927 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6928 xmlXPathBooleanFunction(ctxt, 1);
6929 arg1 = valuePop(ctxt);
6930 arg1->boolval |= arg2->boolval;
6931 valuePush(ctxt, arg1);
6932 xmlXPathFreeObject(arg2);
6933 return;
6934 case XPATH_OP_EQUAL:
6935 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6936 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6937 equal = xmlXPathEqualValues(ctxt);
6938 if (op->value) valuePush(ctxt, xmlXPathNewBoolean(equal));
6939 else valuePush(ctxt, xmlXPathNewBoolean(!equal));
6940 return;
6941 case XPATH_OP_CMP:
6942 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6943 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6944 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
6945 valuePush(ctxt, xmlXPathNewBoolean(ret));
6946 return;
6947 case XPATH_OP_PLUS:
6948 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6949 if (op->ch2 != -1)
6950 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6951 if (op->value == 0) xmlXPathSubValues(ctxt);
6952 else if (op->value == 1) xmlXPathAddValues(ctxt);
6953 else if (op->value == 2) xmlXPathValueFlipSign(ctxt);
6954 else if (op->value == 3) {
6955 xmlXPathObjectPtr arg;
6956
6957 POP_FLOAT
6958 valuePush(ctxt, arg);
6959 }
6960 return;
6961 case XPATH_OP_MULT:
6962 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6963 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6964 if (op->value == 0) xmlXPathMultValues(ctxt);
6965 else if (op->value == 1) xmlXPathDivValues(ctxt);
6966 else if (op->value == 2) xmlXPathModValues(ctxt);
6967 return;
6968 case XPATH_OP_UNION:
6969 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6970 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6971 CHECK_TYPE(XPATH_NODESET);
6972 arg2 = valuePop(ctxt);
6973
6974 CHECK_TYPE(XPATH_NODESET);
6975 arg1 = valuePop(ctxt);
6976
6977 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
6978 arg2->nodesetval);
6979 valuePush(ctxt, arg1);
6980 xmlXPathFreeObject(arg2);
6981 return;
6982 case XPATH_OP_ROOT:
6983 xmlXPathRoot(ctxt);
6984 return;
6985 case XPATH_OP_NODE:
6986 if (op->ch1 != -1)
6987 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6988 if (op->ch2 != -1)
6989 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6990 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
6991 return;
6992 case XPATH_OP_RESET:
6993 if (op->ch1 != -1)
6994 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
6995 if (op->ch2 != -1)
6996 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
6997 ctxt->context->node = NULL;
6998 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006999 case XPATH_OP_COLLECT: {
7000 if (op->ch1 == -1)
7001 return;
7002
7003 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7004 xmlXPathNodeCollectAndTest(ctxt, op);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007005 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007006 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007007 case XPATH_OP_VALUE:
7008 valuePush(ctxt,
7009 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
7010 return;
7011 case XPATH_OP_VARIABLE: {
7012 if (op->ch1 != -1)
7013 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7014 if (op->value5 == NULL)
7015 valuePush(ctxt,
7016 xmlXPathVariableLookup(ctxt->context, op->value4));
7017 else {
7018 const xmlChar *URI;
7019 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7020 if (URI == NULL) {
7021 xmlGenericError(xmlGenericErrorContext,
7022 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
7023 op->value4, op->value5);
7024 return;
7025 }
7026 valuePush(ctxt,
7027 xmlXPathVariableLookupNS(ctxt->context,
7028 op->value4, URI));
7029 }
7030 return;
7031 }
7032 case XPATH_OP_FUNCTION: {
7033 xmlXPathFunction func;
7034
7035 if (op->ch1 != -1)
7036 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7037 if (op->value5 == NULL)
7038 func = xmlXPathFunctionLookup(ctxt->context, op->value4);
7039 else {
7040 const xmlChar *URI;
7041 URI = xmlXPathNsLookup(ctxt->context, op->value5);
7042 if (URI == NULL) {
7043 xmlGenericError(xmlGenericErrorContext,
7044 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
7045 op->value4, op->value5);
7046 return;
7047 }
7048 func = xmlXPathFunctionLookupNS(ctxt->context,
7049 op->value4, URI);
7050 }
7051 if (func == NULL) {
7052 xmlGenericError(xmlGenericErrorContext,
7053 "xmlXPathRunEval: function %s not found\n",
7054 op->value4);
7055 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
7056 return;
7057 }
7058 func(ctxt, op->value);
7059 return;
7060 }
7061 case XPATH_OP_ARG:
7062 if (op->ch1 != -1)
7063 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7064 if (op->ch2 != -1)
7065 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7066 return;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007067 case XPATH_OP_PREDICATE:
7068 case XPATH_OP_FILTER: {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007069 xmlXPathObjectPtr res;
7070 xmlXPathObjectPtr obj, tmp;
7071 xmlNodeSetPtr newset = NULL;
7072 xmlNodeSetPtr oldset;
7073 xmlNodePtr oldnode;
7074 int i;
7075
7076 if (op->ch1 != -1)
7077 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7078 if (op->ch2 == -1)
7079 return;
7080
7081 oldnode = ctxt->context->node;
7082
7083#ifdef LIBXML_XPTR_ENABLED
7084 /*
7085 * Hum are we filtering the result of an XPointer expression
7086 */
7087 if (ctxt->value->type == XPATH_LOCATIONSET) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007088 xmlLocationSetPtr newlocset = NULL;
7089 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007090
7091 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007092 * Extract the old locset, and then evaluate the result of the
7093 * expression for all the element in the locset. use it to grow
7094 * up a new locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007095 */
7096 CHECK_TYPE(XPATH_LOCATIONSET);
7097 obj = valuePop(ctxt);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007098 oldlocset = obj->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007099 ctxt->context->node = NULL;
7100
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007101 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007102 ctxt->context->contextSize = 0;
7103 ctxt->context->proximityPosition = 0;
7104 if (op->ch2 != -1)
7105 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7106 res = valuePop(ctxt);
7107 if (res != NULL)
7108 xmlXPathFreeObject(res);
7109 valuePush(ctxt, obj);
7110 CHECK_ERROR;
7111 return;
7112 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007113 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007114
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007115 for (i = 0; i < oldlocset->locNr; i++) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007116 /*
7117 * Run the evaluation with a node list made of a
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007118 * single item in the nodelocset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007119 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007120 ctxt->context->node = oldlocset->locTab[i]->user;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007121 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7122 valuePush(ctxt, tmp);
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007123 ctxt->context->contextSize = oldlocset->locNr;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007124 ctxt->context->proximityPosition = i + 1;
7125
7126 if (op->ch2 != -1)
7127 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7128 CHECK_ERROR;
7129
7130 /*
7131 * The result of the evaluation need to be tested to
7132 * decided whether the filter succeeded or not
7133 */
7134 res = valuePop(ctxt);
7135 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007136 xmlXPtrLocationSetAdd(newlocset,
7137 xmlXPathObjectCopy(oldlocset->locTab[i]));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007138 }
7139
7140 /*
7141 * Cleanup
7142 */
7143 if (res != NULL)
7144 xmlXPathFreeObject(res);
7145 if (ctxt->value == tmp) {
7146 res = valuePop(ctxt);
7147 xmlXPathFreeObject(res);
7148 }
7149
7150 ctxt->context->node = NULL;
7151 }
7152
7153 /*
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007154 * The result is used as the new evaluation locset.
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007155 */
7156 xmlXPathFreeObject(obj);
7157 ctxt->context->node = NULL;
7158 ctxt->context->contextSize = -1;
7159 ctxt->context->proximityPosition = -1;
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007160 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007161 ctxt->context->node = oldnode;
7162 return;
7163 }
7164#endif /* LIBXML_XPTR_ENABLED */
7165
7166 /*
7167 * Extract the old set, and then evaluate the result of the
7168 * expression for all the element in the set. use it to grow
7169 * up a new set.
7170 */
7171 CHECK_TYPE(XPATH_NODESET);
7172 obj = valuePop(ctxt);
7173 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00007174
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007175 oldnode = ctxt->context->node;
7176 ctxt->context->node = NULL;
7177
7178 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
7179 ctxt->context->contextSize = 0;
7180 ctxt->context->proximityPosition = 0;
7181 if (op->ch2 != -1)
7182 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7183 res = valuePop(ctxt);
7184 if (res != NULL)
7185 xmlXPathFreeObject(res);
7186 valuePush(ctxt, obj);
Daniel Veillard911f49a2001-04-07 15:39:35 +00007187 ctxt->context->node = oldnode;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007188 CHECK_ERROR;
7189 } else {
7190 /*
7191 * Initialize the new set.
7192 */
7193 newset = xmlXPathNodeSetCreate(NULL);
7194
7195 for (i = 0; i < oldset->nodeNr; i++) {
7196 /*
7197 * Run the evaluation with a node list made of
7198 * a single item in the nodeset.
7199 */
7200 ctxt->context->node = oldset->nodeTab[i];
7201 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7202 valuePush(ctxt, tmp);
7203 ctxt->context->contextSize = oldset->nodeNr;
7204 ctxt->context->proximityPosition = i + 1;
7205
7206 if (op->ch2 != -1)
7207 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7208 CHECK_ERROR;
7209
7210 /*
7211 * The result of the evaluation need to be tested to
7212 * decided whether the filter succeeded or not
7213 */
7214 res = valuePop(ctxt);
7215 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
7216 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
7217 }
7218
7219 /*
7220 * Cleanup
7221 */
7222 if (res != NULL)
7223 xmlXPathFreeObject(res);
7224 if (ctxt->value == tmp) {
7225 res = valuePop(ctxt);
7226 xmlXPathFreeObject(res);
7227 }
7228
7229 ctxt->context->node = NULL;
7230 }
7231
7232 /*
7233 * The result is used as the new evaluation set.
7234 */
7235 xmlXPathFreeObject(obj);
7236 ctxt->context->node = NULL;
7237 ctxt->context->contextSize = -1;
7238 ctxt->context->proximityPosition = -1;
7239 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
7240 }
7241 ctxt->context->node = oldnode;
7242 return;
7243 }
7244 case XPATH_OP_SORT:
7245 if (op->ch1 != -1)
7246 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7247 if ((ctxt->value != NULL) &&
7248 (ctxt->value->type == XPATH_NODESET) &&
7249 (ctxt->value->nodesetval != NULL))
7250 xmlXPathNodeSetSort(ctxt->value->nodesetval);
7251 return;
7252#ifdef LIBXML_XPTR_ENABLED
7253 case XPATH_OP_RANGETO: {
7254 xmlXPathObjectPtr range;
7255 xmlXPathObjectPtr res, obj;
7256 xmlXPathObjectPtr tmp;
7257 xmlLocationSetPtr newset = NULL;
7258 xmlNodeSetPtr oldset;
7259 int i;
7260
7261 if (op->ch1 != -1)
7262 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
7263 if (op->ch2 == -1)
7264 return;
7265
7266 CHECK_TYPE(XPATH_NODESET);
7267 obj = valuePop(ctxt);
7268 oldset = obj->nodesetval;
7269 ctxt->context->node = NULL;
7270
7271 newset = xmlXPtrLocationSetCreate(NULL);
7272
Daniel Veillard911f49a2001-04-07 15:39:35 +00007273 if (oldset != NULL) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007274 for (i = 0; i < oldset->nodeNr; i++) {
7275 /*
7276 * Run the evaluation with a node list made of a single item
7277 * in the nodeset.
7278 */
7279 ctxt->context->node = oldset->nodeTab[i];
7280 tmp = xmlXPathNewNodeSet(ctxt->context->node);
7281 valuePush(ctxt, tmp);
7282
7283 if (op->ch2 != -1)
7284 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
7285 CHECK_ERROR;
7286
7287 /*
7288 * The result of the evaluation need to be tested to
7289 * decided whether the filter succeeded or not
7290 */
7291 res = valuePop(ctxt);
7292 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
7293 if (range != NULL) {
7294 xmlXPtrLocationSetAdd(newset, range);
7295 }
7296
7297 /*
7298 * Cleanup
7299 */
7300 if (res != NULL)
7301 xmlXPathFreeObject(res);
7302 if (ctxt->value == tmp) {
7303 res = valuePop(ctxt);
7304 xmlXPathFreeObject(res);
7305 }
7306
7307 ctxt->context->node = NULL;
7308 }
Daniel Veillard911f49a2001-04-07 15:39:35 +00007309 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007310
7311 /*
7312 * The result is used as the new evaluation set.
7313 */
7314 xmlXPathFreeObject(obj);
7315 ctxt->context->node = NULL;
7316 ctxt->context->contextSize = -1;
7317 ctxt->context->proximityPosition = -1;
7318 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
7319 return;
7320 }
7321#endif /* LIBXML_XPTR_ENABLED */
7322 }
7323 xmlGenericError(xmlGenericErrorContext,
7324 "XPath: unknown precompiled operation %d\n",
7325 op->op);
7326 return;
7327}
7328
7329/**
7330 * xmlXPathRunEval:
7331 * @ctxt: the XPath parser context with the compiled expression
7332 *
7333 * Evaluate the Precompiled XPath expression in the given context.
7334 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007335static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007336xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
7337 xmlXPathCompExprPtr comp;
7338
7339 if ((ctxt == NULL) || (ctxt->comp == NULL))
7340 return;
7341
7342 if (ctxt->valueTab == NULL) {
7343 /* Allocate the value stack */
7344 ctxt->valueTab = (xmlXPathObjectPtr *)
7345 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
7346 if (ctxt->valueTab == NULL) {
7347 xmlFree(ctxt);
7348 xmlGenericError(xmlGenericErrorContext,
7349 "xmlXPathRunEval: out of memory\n");
7350 return;
7351 }
7352 ctxt->valueNr = 0;
7353 ctxt->valueMax = 10;
7354 ctxt->value = NULL;
7355 }
7356 comp = ctxt->comp;
7357 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
7358}
7359
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007360/************************************************************************
7361 * *
7362 * Public interfaces *
7363 * *
7364 ************************************************************************/
7365
7366/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007367 * xmlXPathEvalPredicate:
7368 * @ctxt: the XPath context
7369 * @res: the Predicate Expression evaluation result
7370 *
7371 * Evaluate a predicate result for the current node.
7372 * A PredicateExpr is evaluated by evaluating the Expr and converting
7373 * the result to a boolean. If the result is a number, the result will
7374 * be converted to true if the number is equal to the position of the
7375 * context node in the context node list (as returned by the position
7376 * function) and will be converted to false otherwise; if the result
7377 * is not a number, then the result will be converted as if by a call
7378 * to the boolean function.
7379 *
7380 * Return 1 if predicate is true, 0 otherwise
7381 */
7382int
7383xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
7384 if (res == NULL) return(0);
7385 switch (res->type) {
7386 case XPATH_BOOLEAN:
7387 return(res->boolval);
7388 case XPATH_NUMBER:
7389 return(res->floatval == ctxt->proximityPosition);
7390 case XPATH_NODESET:
7391 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007392 if (res->nodesetval == NULL)
7393 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007394 return(res->nodesetval->nodeNr != 0);
7395 case XPATH_STRING:
7396 return((res->stringval != NULL) &&
7397 (xmlStrlen(res->stringval) != 0));
7398 default:
7399 STRANGE
7400 }
7401 return(0);
7402}
7403
7404/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007405 * xmlXPathEvaluatePredicateResult:
7406 * @ctxt: the XPath Parser context
7407 * @res: the Predicate Expression evaluation result
7408 *
7409 * Evaluate a predicate result for the current node.
7410 * A PredicateExpr is evaluated by evaluating the Expr and converting
7411 * the result to a boolean. If the result is a number, the result will
7412 * be converted to true if the number is equal to the position of the
7413 * context node in the context node list (as returned by the position
7414 * function) and will be converted to false otherwise; if the result
7415 * is not a number, then the result will be converted as if by a call
7416 * to the boolean function.
7417 *
7418 * Return 1 if predicate is true, 0 otherwise
7419 */
7420int
7421xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
7422 xmlXPathObjectPtr res) {
7423 if (res == NULL) return(0);
7424 switch (res->type) {
7425 case XPATH_BOOLEAN:
7426 return(res->boolval);
7427 case XPATH_NUMBER:
7428 return(res->floatval == ctxt->context->proximityPosition);
7429 case XPATH_NODESET:
7430 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00007431 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00007432 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007433 return(res->nodesetval->nodeNr != 0);
7434 case XPATH_STRING:
7435 return((res->stringval != NULL) &&
7436 (xmlStrlen(res->stringval) != 0));
7437 default:
7438 STRANGE
7439 }
7440 return(0);
7441}
7442
7443/**
7444 * xmlXPathCompile:
7445 * @str: the XPath expression
7446 *
7447 * Compile an XPath expression
7448 *
7449 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7450 * the caller has to free the object.
7451 */
7452xmlXPathCompExprPtr
7453xmlXPathCompile(const xmlChar *str) {
7454 xmlXPathParserContextPtr ctxt;
7455 xmlXPathCompExprPtr comp;
7456
7457 xmlXPathInit();
7458
7459 ctxt = xmlXPathNewParserContext(str, NULL);
7460 xmlXPathCompileExpr(ctxt);
7461
7462 comp = ctxt->comp;
7463 ctxt->comp = NULL;
7464 xmlXPathFreeParserContext(ctxt);
7465 return(comp);
7466}
7467
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007468/**
7469 * xmlXPathCompiledEval:
7470 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00007471 * @ctx: the XPath context
7472 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007473 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00007474 *
7475 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7476 * the caller has to free the object.
7477 */
7478xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007479xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00007480 xmlXPathParserContextPtr ctxt;
7481 xmlXPathObjectPtr res, tmp, init = NULL;
7482 int stack = 0;
7483
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007484 if ((comp == NULL) || (ctx == NULL))
7485 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007486 xmlXPathInit();
7487
7488 CHECK_CONTEXT(ctx)
7489
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007490 ctxt = xmlXPathCompParserContext(comp, ctx);
7491 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007492
7493 if (ctxt->value == NULL) {
7494 xmlGenericError(xmlGenericErrorContext,
7495 "xmlXPathEval: evaluation failed\n");
7496 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00007497 } else {
7498 res = valuePop(ctxt);
7499 }
7500
7501 do {
7502 tmp = valuePop(ctxt);
7503 if (tmp != NULL) {
7504 if (tmp != init)
7505 stack++;
7506 xmlXPathFreeObject(tmp);
7507 }
7508 } while (tmp != NULL);
7509 if ((stack != 0) && (res != NULL)) {
7510 xmlGenericError(xmlGenericErrorContext,
7511 "xmlXPathEval: %d object left on the stack\n",
7512 stack);
7513 }
7514 if (ctxt->error != XPATH_EXPRESSION_OK) {
7515 xmlXPathFreeObject(res);
7516 res = NULL;
7517 }
7518
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007519
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007520 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007521 xmlXPathFreeParserContext(ctxt);
7522 return(res);
7523}
7524
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007525/**
7526 * xmlXPathEvalExpr:
7527 * @ctxt: the XPath Parser context
7528 *
7529 * Parse and evaluate an XPath expression in the given context,
7530 * then push the result on the context stack
7531 */
7532void
7533xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
7534 xmlXPathCompileExpr(ctxt);
7535 xmlXPathRunEval(ctxt);
7536}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007537
7538/**
7539 * xmlXPathEval:
7540 * @str: the XPath expression
7541 * @ctx: the XPath context
7542 *
7543 * Evaluate the XPath Location Path in the given context.
7544 *
7545 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
7546 * the caller has to free the object.
7547 */
7548xmlXPathObjectPtr
7549xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
7550 xmlXPathParserContextPtr ctxt;
7551 xmlXPathObjectPtr res, tmp, init = NULL;
7552 int stack = 0;
7553
7554 xmlXPathInit();
7555
7556 CHECK_CONTEXT(ctx)
7557
7558 ctxt = xmlXPathNewParserContext(str, ctx);
7559 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007560
7561 if (ctxt->value == NULL) {
7562 xmlGenericError(xmlGenericErrorContext,
7563 "xmlXPathEval: evaluation failed\n");
7564 res = NULL;
7565 } else if (*ctxt->cur != 0) {
7566 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7567 res = NULL;
7568 } else {
7569 res = valuePop(ctxt);
7570 }
7571
7572 do {
7573 tmp = valuePop(ctxt);
7574 if (tmp != NULL) {
7575 if (tmp != init)
7576 stack++;
7577 xmlXPathFreeObject(tmp);
7578 }
7579 } while (tmp != NULL);
7580 if ((stack != 0) && (res != NULL)) {
7581 xmlGenericError(xmlGenericErrorContext,
7582 "xmlXPathEval: %d object left on the stack\n",
7583 stack);
7584 }
7585 if (ctxt->error != XPATH_EXPRESSION_OK) {
7586 xmlXPathFreeObject(res);
7587 res = NULL;
7588 }
7589
Owen Taylor3473f882001-02-23 17:55:21 +00007590 xmlXPathFreeParserContext(ctxt);
7591 return(res);
7592}
7593
7594/**
7595 * xmlXPathEvalExpression:
7596 * @str: the XPath expression
7597 * @ctxt: the XPath context
7598 *
7599 * Evaluate the XPath expression in the given context.
7600 *
7601 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
7602 * the caller has to free the object.
7603 */
7604xmlXPathObjectPtr
7605xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
7606 xmlXPathParserContextPtr pctxt;
7607 xmlXPathObjectPtr res, tmp;
7608 int stack = 0;
7609
7610 xmlXPathInit();
7611
7612 CHECK_CONTEXT(ctxt)
7613
7614 pctxt = xmlXPathNewParserContext(str, ctxt);
7615 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007616
7617 if (*pctxt->cur != 0) {
7618 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
7619 res = NULL;
7620 } else {
7621 res = valuePop(pctxt);
7622 }
7623 do {
7624 tmp = valuePop(pctxt);
7625 if (tmp != NULL) {
7626 xmlXPathFreeObject(tmp);
7627 stack++;
7628 }
7629 } while (tmp != NULL);
7630 if ((stack != 0) && (res != NULL)) {
7631 xmlGenericError(xmlGenericErrorContext,
7632 "xmlXPathEvalExpression: %d object left on the stack\n",
7633 stack);
7634 }
7635 xmlXPathFreeParserContext(pctxt);
7636 return(res);
7637}
7638
7639/**
7640 * xmlXPathRegisterAllFunctions:
7641 * @ctxt: the XPath context
7642 *
7643 * Registers all default XPath functions in this context
7644 */
7645void
7646xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
7647{
7648 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
7649 xmlXPathBooleanFunction);
7650 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
7651 xmlXPathCeilingFunction);
7652 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
7653 xmlXPathCountFunction);
7654 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
7655 xmlXPathConcatFunction);
7656 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
7657 xmlXPathContainsFunction);
7658 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
7659 xmlXPathIdFunction);
7660 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
7661 xmlXPathFalseFunction);
7662 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
7663 xmlXPathFloorFunction);
7664 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
7665 xmlXPathLastFunction);
7666 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
7667 xmlXPathLangFunction);
7668 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
7669 xmlXPathLocalNameFunction);
7670 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
7671 xmlXPathNotFunction);
7672 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
7673 xmlXPathNameFunction);
7674 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
7675 xmlXPathNamespaceURIFunction);
7676 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
7677 xmlXPathNormalizeFunction);
7678 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
7679 xmlXPathNumberFunction);
7680 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
7681 xmlXPathPositionFunction);
7682 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
7683 xmlXPathRoundFunction);
7684 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
7685 xmlXPathStringFunction);
7686 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
7687 xmlXPathStringLengthFunction);
7688 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
7689 xmlXPathStartsWithFunction);
7690 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
7691 xmlXPathSubstringFunction);
7692 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
7693 xmlXPathSubstringBeforeFunction);
7694 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
7695 xmlXPathSubstringAfterFunction);
7696 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
7697 xmlXPathSumFunction);
7698 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
7699 xmlXPathTrueFunction);
7700 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
7701 xmlXPathTranslateFunction);
7702}
7703
7704#endif /* LIBXML_XPATH_ENABLED */